1 /* 2 * Copyright (c) 1989, 1993 3 * The Regents of the University of California. All rights reserved. 4 * (c) UNIX System Laboratories, Inc. 5 * All or some portions of this file are derived from material licensed 6 * to the University of California by American Telephone and Telegraph 7 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 8 * the permission of UNIX System Laboratories, Inc. 9 * 10 * %sccs.include.redist.c% 11 * 12 * @(#)ufs_lookup.c 8.14 (Berkeley) 05/27/95 13 */ 14 15 #include <sys/param.h> 16 #include <sys/namei.h> 17 #include <sys/buf.h> 18 #include <sys/file.h> 19 #include <sys/mount.h> 20 #include <sys/vnode.h> 21 22 #include <ufs/ufs/quota.h> 23 #include <ufs/ufs/inode.h> 24 #include <ufs/ufs/dir.h> 25 #include <ufs/ufs/ufsmount.h> 26 #include <ufs/ufs/ufs_extern.h> 27 28 struct nchstats nchstats; 29 #ifdef DIAGNOSTIC 30 int dirchk = 1; 31 #else 32 int dirchk = 0; 33 #endif 34 35 #define FSFMT(vp) ((vp)->v_mount->mnt_maxsymlinklen <= 0) 36 37 /* 38 * Convert a component of a pathname into a pointer to a locked inode. 39 * This is a very central and rather complicated routine. 40 * If the file system is not maintained in a strict tree hierarchy, 41 * this can result in a deadlock situation (see comments in code below). 42 * 43 * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending 44 * on whether the name is to be looked up, created, renamed, or deleted. 45 * When CREATE, RENAME, or DELETE is specified, information usable in 46 * creating, renaming, or deleting a directory entry may be calculated. 47 * If flag has LOCKPARENT or'ed into it and the target of the pathname 48 * exists, lookup returns both the target and its parent directory locked. 49 * When creating or renaming and LOCKPARENT is specified, the target may 50 * not be ".". When deleting and LOCKPARENT is specified, the target may 51 * be "."., but the caller must check to ensure it does an vrele and vput 52 * instead of two vputs. 53 * 54 * Overall outline of ufs_lookup: 55 * 56 * check accessibility of directory 57 * look for name in cache, if found, then if at end of path 58 * and deleting or creating, drop it, else return name 59 * search for name in directory, to found or notfound 60 * notfound: 61 * if creating, return locked directory, leaving info on available slots 62 * else return error 63 * found: 64 * if at end of path and deleting, return information to allow delete 65 * if at end of path and rewriting (RENAME and LOCKPARENT), lock target 66 * inode and return info to allow rewrite 67 * if not at end, add name to cache; if at end and neither creating 68 * nor deleting, add name to cache 69 */ 70 int 71 ufs_lookup(ap) 72 struct vop_lookup_args /* { 73 struct vnode *a_dvp; 74 struct vnode **a_vpp; 75 struct componentname *a_cnp; 76 } */ *ap; 77 { 78 register struct vnode *vdp; /* vnode for directory being searched */ 79 register struct inode *dp; /* inode for directory being searched */ 80 struct buf *bp; /* a buffer of directory entries */ 81 register struct direct *ep; /* the current directory entry */ 82 int entryoffsetinblock; /* offset of ep in bp's buffer */ 83 enum {NONE, COMPACT, FOUND} slotstatus; 84 doff_t slotoffset; /* offset of area with free space */ 85 int slotsize; /* size of area at slotoffset */ 86 int slotfreespace; /* amount of space free in slot */ 87 int slotneeded; /* size of the entry we're seeking */ 88 int numdirpasses; /* strategy for directory search */ 89 doff_t endsearch; /* offset to end directory search */ 90 doff_t prevoff; /* prev entry dp->i_offset */ 91 struct vnode *pdp; /* saved dp during symlink work */ 92 struct vnode *tdp; /* returned by VFS_VGET */ 93 doff_t enduseful; /* pointer past last used dir slot */ 94 u_long bmask; /* block offset mask */ 95 int lockparent; /* 1 => lockparent flag is set */ 96 int wantparent; /* 1 => wantparent or lockparent flag */ 97 int namlen, error; 98 struct vnode **vpp = ap->a_vpp; 99 struct componentname *cnp = ap->a_cnp; 100 struct ucred *cred = cnp->cn_cred; 101 int flags = cnp->cn_flags; 102 int nameiop = cnp->cn_nameiop; 103 struct proc *p = cnp->cn_proc; 104 105 bp = NULL; 106 slotoffset = -1; 107 *vpp = NULL; 108 vdp = ap->a_dvp; 109 dp = VTOI(vdp); 110 lockparent = flags & LOCKPARENT; 111 wantparent = flags & (LOCKPARENT|WANTPARENT); 112 113 /* 114 * Check accessiblity of directory. 115 */ 116 if ((dp->i_mode & IFMT) != IFDIR) 117 return (ENOTDIR); 118 if (error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc)) 119 return (error); 120 if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) && 121 (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) 122 return (EROFS); 123 124 /* 125 * We now have a segment name to search for, and a directory to search. 126 * 127 * Before tediously performing a linear scan of the directory, 128 * check the name cache to see if the directory/name pair 129 * we are looking for is known already. 130 */ 131 if (error = cache_lookup(vdp, vpp, cnp)) { 132 int vpid; /* capability number of vnode */ 133 134 if (error == ENOENT) 135 return (error); 136 /* 137 * Get the next vnode in the path. 138 * See comment below starting `Step through' for 139 * an explaination of the locking protocol. 140 */ 141 pdp = vdp; 142 dp = VTOI(*vpp); 143 vdp = *vpp; 144 vpid = vdp->v_id; 145 if (pdp == vdp) { /* lookup on "." */ 146 VREF(vdp); 147 error = 0; 148 } else if (flags & ISDOTDOT) { 149 VOP_UNLOCK(pdp, 0, p); 150 error = vget(vdp, LK_EXCLUSIVE, p); 151 if (!error && lockparent && (flags & ISLASTCN)) 152 error = vn_lock(pdp, LK_EXCLUSIVE, p); 153 } else { 154 error = vget(vdp, LK_EXCLUSIVE, p); 155 if (!lockparent || error || !(flags & ISLASTCN)) 156 VOP_UNLOCK(pdp, 0, p); 157 } 158 /* 159 * Check that the capability number did not change 160 * while we were waiting for the lock. 161 */ 162 if (!error) { 163 if (vpid == vdp->v_id) 164 return (0); 165 vput(vdp); 166 if (lockparent && pdp != vdp && (flags & ISLASTCN)) 167 VOP_UNLOCK(pdp, 0, p); 168 } 169 if (error = vn_lock(pdp, LK_EXCLUSIVE, p)) 170 return (error); 171 vdp = pdp; 172 dp = VTOI(pdp); 173 *vpp = NULL; 174 } 175 176 /* 177 * Suppress search for slots unless creating 178 * file and at end of pathname, in which case 179 * we watch for a place to put the new file in 180 * case it doesn't already exist. 181 */ 182 slotstatus = FOUND; 183 slotfreespace = slotsize = slotneeded = 0; 184 if ((nameiop == CREATE || nameiop == RENAME) && 185 (flags & ISLASTCN)) { 186 slotstatus = NONE; 187 slotneeded = (sizeof(struct direct) - MAXNAMLEN + 188 cnp->cn_namelen + 3) &~ 3; 189 } 190 191 /* 192 * If there is cached information on a previous search of 193 * this directory, pick up where we last left off. 194 * We cache only lookups as these are the most common 195 * and have the greatest payoff. Caching CREATE has little 196 * benefit as it usually must search the entire directory 197 * to determine that the entry does not exist. Caching the 198 * location of the last DELETE or RENAME has not reduced 199 * profiling time and hence has been removed in the interest 200 * of simplicity. 201 */ 202 bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1; 203 if (nameiop != LOOKUP || dp->i_diroff == 0 || 204 dp->i_diroff > dp->i_size) { 205 entryoffsetinblock = 0; 206 dp->i_offset = 0; 207 numdirpasses = 1; 208 } else { 209 dp->i_offset = dp->i_diroff; 210 if ((entryoffsetinblock = dp->i_offset & bmask) && 211 (error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp))) 212 return (error); 213 numdirpasses = 2; 214 nchstats.ncs_2passes++; 215 } 216 prevoff = dp->i_offset; 217 endsearch = roundup(dp->i_size, DIRBLKSIZ); 218 enduseful = 0; 219 220 searchloop: 221 while (dp->i_offset < endsearch) { 222 /* 223 * If necessary, get the next directory block. 224 */ 225 if ((dp->i_offset & bmask) == 0) { 226 if (bp != NULL) 227 brelse(bp); 228 if (error = 229 VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp)) 230 return (error); 231 entryoffsetinblock = 0; 232 } 233 /* 234 * If still looking for a slot, and at a DIRBLKSIZE 235 * boundary, have to start looking for free space again. 236 */ 237 if (slotstatus == NONE && 238 (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) { 239 slotoffset = -1; 240 slotfreespace = 0; 241 } 242 /* 243 * Get pointer to next entry. 244 * Full validation checks are slow, so we only check 245 * enough to insure forward progress through the 246 * directory. Complete checks can be run by patching 247 * "dirchk" to be true. 248 */ 249 ep = (struct direct *)((char *)bp->b_data + entryoffsetinblock); 250 if (ep->d_reclen == 0 || 251 dirchk && ufs_dirbadentry(vdp, ep, entryoffsetinblock)) { 252 int i; 253 254 ufs_dirbad(dp, dp->i_offset, "mangled entry"); 255 i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)); 256 dp->i_offset += i; 257 entryoffsetinblock += i; 258 continue; 259 } 260 261 /* 262 * If an appropriate sized slot has not yet been found, 263 * check to see if one is available. Also accumulate space 264 * in the current block so that we can determine if 265 * compaction is viable. 266 */ 267 if (slotstatus != FOUND) { 268 int size = ep->d_reclen; 269 270 if (ep->d_ino != 0) 271 size -= DIRSIZ(FSFMT(vdp), ep); 272 if (size > 0) { 273 if (size >= slotneeded) { 274 slotstatus = FOUND; 275 slotoffset = dp->i_offset; 276 slotsize = ep->d_reclen; 277 } else if (slotstatus == NONE) { 278 slotfreespace += size; 279 if (slotoffset == -1) 280 slotoffset = dp->i_offset; 281 if (slotfreespace >= slotneeded) { 282 slotstatus = COMPACT; 283 slotsize = dp->i_offset + 284 ep->d_reclen - slotoffset; 285 } 286 } 287 } 288 } 289 290 /* 291 * Check for a name match. 292 */ 293 if (ep->d_ino) { 294 # if (BYTE_ORDER == LITTLE_ENDIAN) 295 if (vdp->v_mount->mnt_maxsymlinklen > 0) 296 namlen = ep->d_namlen; 297 else 298 namlen = ep->d_type; 299 # else 300 namlen = ep->d_namlen; 301 # endif 302 if (namlen == cnp->cn_namelen && 303 !bcmp(cnp->cn_nameptr, ep->d_name, 304 (unsigned)namlen)) { 305 /* 306 * Save directory entry's inode number and 307 * reclen in ndp->ni_ufs area, and release 308 * directory buffer. 309 */ 310 if (vdp->v_mount->mnt_maxsymlinklen > 0 && 311 ep->d_type == DT_WHT) { 312 slotstatus = FOUND; 313 slotoffset = dp->i_offset; 314 slotsize = ep->d_reclen; 315 dp->i_reclen = slotsize; 316 enduseful = slotoffset + slotsize; 317 ap->a_cnp->cn_flags |= ISWHITEOUT; 318 numdirpasses--; 319 goto notfound; 320 } 321 dp->i_ino = ep->d_ino; 322 dp->i_reclen = ep->d_reclen; 323 brelse(bp); 324 goto found; 325 } 326 } 327 prevoff = dp->i_offset; 328 dp->i_offset += ep->d_reclen; 329 entryoffsetinblock += ep->d_reclen; 330 if (ep->d_ino) 331 enduseful = dp->i_offset; 332 } 333 notfound: 334 /* 335 * If we started in the middle of the directory and failed 336 * to find our target, we must check the beginning as well. 337 */ 338 if (numdirpasses == 2) { 339 numdirpasses--; 340 dp->i_offset = 0; 341 endsearch = dp->i_diroff; 342 goto searchloop; 343 } 344 if (bp != NULL) 345 brelse(bp); 346 /* 347 * If creating, and at end of pathname and current 348 * directory has not been removed, then can consider 349 * allowing file to be created. 350 */ 351 if ((nameiop == CREATE || nameiop == RENAME || 352 (nameiop == DELETE && 353 (ap->a_cnp->cn_flags & DOWHITEOUT) && 354 (ap->a_cnp->cn_flags & ISWHITEOUT))) && 355 (flags & ISLASTCN) && dp->i_nlink != 0) { 356 /* 357 * Access for write is interpreted as allowing 358 * creation of files in the directory. 359 */ 360 if (error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) 361 return (error); 362 /* 363 * Return an indication of where the new directory 364 * entry should be put. If we didn't find a slot, 365 * then set dp->i_count to 0 indicating 366 * that the new slot belongs at the end of the 367 * directory. If we found a slot, then the new entry 368 * can be put in the range from dp->i_offset to 369 * dp->i_offset + dp->i_count. 370 */ 371 if (slotstatus == NONE) { 372 dp->i_offset = roundup(dp->i_size, DIRBLKSIZ); 373 dp->i_count = 0; 374 enduseful = dp->i_offset; 375 } else if (nameiop == DELETE) { 376 dp->i_offset = slotoffset; 377 if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0) 378 dp->i_count = 0; 379 else 380 dp->i_count = dp->i_offset - prevoff; 381 } else { 382 dp->i_offset = slotoffset; 383 dp->i_count = slotsize; 384 if (enduseful < slotoffset + slotsize) 385 enduseful = slotoffset + slotsize; 386 } 387 dp->i_endoff = roundup(enduseful, DIRBLKSIZ); 388 dp->i_flag |= IN_CHANGE | IN_UPDATE; 389 /* 390 * We return with the directory locked, so that 391 * the parameters we set up above will still be 392 * valid if we actually decide to do a direnter(). 393 * We return ni_vp == NULL to indicate that the entry 394 * does not currently exist; we leave a pointer to 395 * the (locked) directory inode in ndp->ni_dvp. 396 * The pathname buffer is saved so that the name 397 * can be obtained later. 398 * 399 * NB - if the directory is unlocked, then this 400 * information cannot be used. 401 */ 402 cnp->cn_flags |= SAVENAME; 403 if (!lockparent) 404 VOP_UNLOCK(vdp, 0, p); 405 return (EJUSTRETURN); 406 } 407 /* 408 * Insert name into cache (as non-existent) if appropriate. 409 */ 410 if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) 411 cache_enter(vdp, *vpp, cnp); 412 return (ENOENT); 413 414 found: 415 if (numdirpasses == 2) 416 nchstats.ncs_pass2++; 417 /* 418 * Check that directory length properly reflects presence 419 * of this entry. 420 */ 421 if (entryoffsetinblock + DIRSIZ(FSFMT(vdp), ep) > dp->i_size) { 422 ufs_dirbad(dp, dp->i_offset, "i_size too small"); 423 dp->i_size = entryoffsetinblock + DIRSIZ(FSFMT(vdp), ep); 424 dp->i_flag |= IN_CHANGE | IN_UPDATE; 425 } 426 427 /* 428 * Found component in pathname. 429 * If the final component of path name, save information 430 * in the cache as to where the entry was found. 431 */ 432 if ((flags & ISLASTCN) && nameiop == LOOKUP) 433 dp->i_diroff = dp->i_offset &~ (DIRBLKSIZ - 1); 434 435 /* 436 * If deleting, and at end of pathname, return 437 * parameters which can be used to remove file. 438 * If the wantparent flag isn't set, we return only 439 * the directory (in ndp->ni_dvp), otherwise we go 440 * on and lock the inode, being careful with ".". 441 */ 442 if (nameiop == DELETE && (flags & ISLASTCN)) { 443 /* 444 * Write access to directory required to delete files. 445 */ 446 if (error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) 447 return (error); 448 /* 449 * Return pointer to current entry in dp->i_offset, 450 * and distance past previous entry (if there 451 * is a previous entry in this block) in dp->i_count. 452 * Save directory inode pointer in ndp->ni_dvp for dirremove(). 453 */ 454 if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0) 455 dp->i_count = 0; 456 else 457 dp->i_count = dp->i_offset - prevoff; 458 if (dp->i_number == dp->i_ino) { 459 VREF(vdp); 460 *vpp = vdp; 461 return (0); 462 } 463 if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) 464 return (error); 465 /* 466 * If directory is "sticky", then user must own 467 * the directory, or the file in it, else she 468 * may not delete it (unless she's root). This 469 * implements append-only directories. 470 */ 471 if ((dp->i_mode & ISVTX) && 472 cred->cr_uid != 0 && 473 cred->cr_uid != dp->i_uid && 474 tdp->v_type != VLNK && 475 VTOI(tdp)->i_uid != cred->cr_uid) { 476 vput(tdp); 477 return (EPERM); 478 } 479 *vpp = tdp; 480 if (!lockparent) 481 VOP_UNLOCK(vdp, 0, p); 482 return (0); 483 } 484 485 /* 486 * If rewriting (RENAME), return the inode and the 487 * information required to rewrite the present directory 488 * Must get inode of directory entry to verify it's a 489 * regular file, or empty directory. 490 */ 491 if (nameiop == RENAME && wantparent && (flags & ISLASTCN)) { 492 if (error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) 493 return (error); 494 /* 495 * Careful about locking second inode. 496 * This can only occur if the target is ".". 497 */ 498 if (dp->i_number == dp->i_ino) 499 return (EISDIR); 500 if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) 501 return (error); 502 *vpp = tdp; 503 cnp->cn_flags |= SAVENAME; 504 if (!lockparent) 505 VOP_UNLOCK(vdp, 0, p); 506 return (0); 507 } 508 509 /* 510 * Step through the translation in the name. We do not `vput' the 511 * directory because we may need it again if a symbolic link 512 * is relative to the current directory. Instead we save it 513 * unlocked as "pdp". We must get the target inode before unlocking 514 * the directory to insure that the inode will not be removed 515 * before we get it. We prevent deadlock by always fetching 516 * inodes from the root, moving down the directory tree. Thus 517 * when following backward pointers ".." we must unlock the 518 * parent directory before getting the requested directory. 519 * There is a potential race condition here if both the current 520 * and parent directories are removed before the VFS_VGET for the 521 * inode associated with ".." returns. We hope that this occurs 522 * infrequently since we cannot avoid this race condition without 523 * implementing a sophisticated deadlock detection algorithm. 524 * Note also that this simple deadlock detection scheme will not 525 * work if the file system has any hard links other than ".." 526 * that point backwards in the directory structure. 527 */ 528 pdp = vdp; 529 if (flags & ISDOTDOT) { 530 VOP_UNLOCK(pdp, 0, p); /* race to get the inode */ 531 if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) { 532 vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p); 533 return (error); 534 } 535 if (lockparent && (flags & ISLASTCN) && 536 (error = vn_lock(pdp, LK_EXCLUSIVE, p))) { 537 vput(tdp); 538 return (error); 539 } 540 *vpp = tdp; 541 } else if (dp->i_number == dp->i_ino) { 542 VREF(vdp); /* we want ourself, ie "." */ 543 *vpp = vdp; 544 } else { 545 if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) 546 return (error); 547 if (!lockparent || !(flags & ISLASTCN)) 548 VOP_UNLOCK(pdp, 0, p); 549 *vpp = tdp; 550 } 551 552 /* 553 * Insert name into cache if appropriate. 554 */ 555 if (cnp->cn_flags & MAKEENTRY) 556 cache_enter(vdp, *vpp, cnp); 557 return (0); 558 } 559 560 void 561 ufs_dirbad(ip, offset, how) 562 struct inode *ip; 563 doff_t offset; 564 char *how; 565 { 566 struct mount *mp; 567 568 mp = ITOV(ip)->v_mount; 569 (void)printf("%s: bad dir ino %d at offset %d: %s\n", 570 mp->mnt_stat.f_mntonname, ip->i_number, offset, how); 571 if ((mp->mnt_stat.f_flags & MNT_RDONLY) == 0) 572 panic("bad dir"); 573 } 574 575 /* 576 * Do consistency checking on a directory entry: 577 * record length must be multiple of 4 578 * entry must fit in rest of its DIRBLKSIZ block 579 * record must be large enough to contain entry 580 * name is not longer than MAXNAMLEN 581 * name must be as long as advertised, and null terminated 582 */ 583 int 584 ufs_dirbadentry(dp, ep, entryoffsetinblock) 585 struct vnode *dp; 586 register struct direct *ep; 587 int entryoffsetinblock; 588 { 589 register int i; 590 int namlen; 591 592 # if (BYTE_ORDER == LITTLE_ENDIAN) 593 if (dp->v_mount->mnt_maxsymlinklen > 0) 594 namlen = ep->d_namlen; 595 else 596 namlen = ep->d_type; 597 # else 598 namlen = ep->d_namlen; 599 # endif 600 if ((ep->d_reclen & 0x3) != 0 || 601 ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) || 602 ep->d_reclen < DIRSIZ(FSFMT(dp), ep) || namlen > MAXNAMLEN) { 603 /*return (1); */ 604 printf("First bad\n"); 605 goto bad; 606 } 607 if (ep->d_ino == 0) 608 return (0); 609 for (i = 0; i < namlen; i++) 610 if (ep->d_name[i] == '\0') { 611 /*return (1); */ 612 printf("Second bad\n"); 613 goto bad; 614 } 615 if (ep->d_name[i]) 616 goto bad; 617 return (0); 618 bad: 619 return (1); 620 } 621 622 /* 623 * Write a directory entry after a call to namei, using the parameters 624 * that it left in nameidata. The argument ip is the inode which the new 625 * directory entry will refer to. Dvp is a pointer to the directory to 626 * be written, which was left locked by namei. Remaining parameters 627 * (dp->i_offset, dp->i_count) indicate how the space for the new 628 * entry is to be obtained. 629 */ 630 int 631 ufs_direnter(ip, dvp, cnp) 632 struct inode *ip; 633 struct vnode *dvp; 634 register struct componentname *cnp; 635 { 636 register struct inode *dp; 637 struct direct newdir; 638 639 #ifdef DIAGNOSTIC 640 if ((cnp->cn_flags & SAVENAME) == 0) 641 panic("direnter: missing name"); 642 #endif 643 dp = VTOI(dvp); 644 newdir.d_ino = ip->i_number; 645 newdir.d_namlen = cnp->cn_namelen; 646 bcopy(cnp->cn_nameptr, newdir.d_name, (unsigned)cnp->cn_namelen + 1); 647 if (dvp->v_mount->mnt_maxsymlinklen > 0) 648 newdir.d_type = IFTODT(ip->i_mode); 649 else { 650 newdir.d_type = 0; 651 # if (BYTE_ORDER == LITTLE_ENDIAN) 652 { u_char tmp = newdir.d_namlen; 653 newdir.d_namlen = newdir.d_type; 654 newdir.d_type = tmp; } 655 # endif 656 } 657 return (ufs_direnter2(dvp, &newdir, cnp->cn_cred, cnp->cn_proc)); 658 } 659 660 /* 661 * Common entry point for directory entry removal used by ufs_direnter 662 * and ufs_whiteout 663 */ 664 ufs_direnter2(dvp, dirp, cr, p) 665 struct vnode *dvp; 666 struct direct *dirp; 667 struct ucred *cr; 668 struct proc *p; 669 { 670 int newentrysize; 671 struct inode *dp; 672 struct buf *bp; 673 struct iovec aiov; 674 struct uio auio; 675 u_int dsize; 676 struct direct *ep, *nep; 677 int error, loc, spacefree; 678 char *dirbuf; 679 680 dp = VTOI(dvp); 681 newentrysize = DIRSIZ(FSFMT(dvp), dirp); 682 683 if (dp->i_count == 0) { 684 /* 685 * If dp->i_count is 0, then namei could find no 686 * space in the directory. Here, dp->i_offset will 687 * be on a directory block boundary and we will write the 688 * new entry into a fresh block. 689 */ 690 if (dp->i_offset & (DIRBLKSIZ - 1)) 691 panic("ufs_direnter2: newblk"); 692 auio.uio_offset = dp->i_offset; 693 dirp->d_reclen = DIRBLKSIZ; 694 auio.uio_resid = newentrysize; 695 aiov.iov_len = newentrysize; 696 aiov.iov_base = (caddr_t)dirp; 697 auio.uio_iov = &aiov; 698 auio.uio_iovcnt = 1; 699 auio.uio_rw = UIO_WRITE; 700 auio.uio_segflg = UIO_SYSSPACE; 701 auio.uio_procp = (struct proc *)0; 702 error = VOP_WRITE(dvp, &auio, IO_SYNC, cr); 703 if (DIRBLKSIZ > 704 VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize) 705 /* XXX should grow with balloc() */ 706 panic("ufs_direnter2: frag size"); 707 else if (!error) { 708 dp->i_size = roundup(dp->i_size, DIRBLKSIZ); 709 dp->i_flag |= IN_CHANGE; 710 } 711 return (error); 712 } 713 714 /* 715 * If dp->i_count is non-zero, then namei found space 716 * for the new entry in the range dp->i_offset to 717 * dp->i_offset + dp->i_count in the directory. 718 * To use this space, we may have to compact the entries located 719 * there, by copying them together towards the beginning of the 720 * block, leaving the free space in one usable chunk at the end. 721 */ 722 723 /* 724 * Increase size of directory if entry eats into new space. 725 * This should never push the size past a new multiple of 726 * DIRBLKSIZE. 727 * 728 * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN. 729 */ 730 if (dp->i_offset + dp->i_count > dp->i_size) 731 dp->i_size = dp->i_offset + dp->i_count; 732 /* 733 * Get the block containing the space for the new directory entry. 734 */ 735 if (error = VOP_BLKATOFF(dvp, (off_t)dp->i_offset, &dirbuf, &bp)) 736 return (error); 737 /* 738 * Find space for the new entry. In the simple case, the entry at 739 * offset base will have the space. If it does not, then namei 740 * arranged that compacting the region dp->i_offset to 741 * dp->i_offset + dp->i_count would yield the 742 * space. 743 */ 744 ep = (struct direct *)dirbuf; 745 dsize = DIRSIZ(FSFMT(dvp), ep); 746 spacefree = ep->d_reclen - dsize; 747 for (loc = ep->d_reclen; loc < dp->i_count; ) { 748 nep = (struct direct *)(dirbuf + loc); 749 if (ep->d_ino) { 750 /* trim the existing slot */ 751 ep->d_reclen = dsize; 752 ep = (struct direct *)((char *)ep + dsize); 753 } else { 754 /* overwrite; nothing there; header is ours */ 755 spacefree += dsize; 756 } 757 dsize = DIRSIZ(FSFMT(dvp), nep); 758 spacefree += nep->d_reclen - dsize; 759 loc += nep->d_reclen; 760 bcopy((caddr_t)nep, (caddr_t)ep, dsize); 761 } 762 /* 763 * Update the pointer fields in the previous entry (if any), 764 * copy in the new entry, and write out the block. 765 */ 766 if (ep->d_ino == 0 || 767 (ep->d_ino == WINO && 768 bcmp(ep->d_name, dirp->d_name, dirp->d_namlen) == 0)) { 769 if (spacefree + dsize < newentrysize) 770 panic("ufs_direnter2: compact1"); 771 dirp->d_reclen = spacefree + dsize; 772 } else { 773 if (spacefree < newentrysize) 774 panic("ufs_direnter2: compact2"); 775 dirp->d_reclen = spacefree; 776 ep->d_reclen = dsize; 777 ep = (struct direct *)((char *)ep + dsize); 778 } 779 bcopy((caddr_t)dirp, (caddr_t)ep, (u_int)newentrysize); 780 error = VOP_BWRITE(bp); 781 dp->i_flag |= IN_CHANGE | IN_UPDATE; 782 if (!error && dp->i_endoff && dp->i_endoff < dp->i_size) 783 error = VOP_TRUNCATE(dvp, (off_t)dp->i_endoff, IO_SYNC, cr, p); 784 return (error); 785 } 786 787 /* 788 * Remove a directory entry after a call to namei, using 789 * the parameters which it left in nameidata. The entry 790 * dp->i_offset contains the offset into the directory of the 791 * entry to be eliminated. The dp->i_count field contains the 792 * size of the previous record in the directory. If this 793 * is 0, the first entry is being deleted, so we need only 794 * zero the inode number to mark the entry as free. If the 795 * entry is not the first in the directory, we must reclaim 796 * the space of the now empty record by adding the record size 797 * to the size of the previous entry. 798 */ 799 int 800 ufs_dirremove(dvp, cnp) 801 struct vnode *dvp; 802 struct componentname *cnp; 803 { 804 register struct inode *dp; 805 struct direct *ep; 806 struct buf *bp; 807 int error; 808 809 dp = VTOI(dvp); 810 811 if (cnp->cn_flags & DOWHITEOUT) { 812 /* 813 * Whiteout entry: set d_ino to WINO. 814 */ 815 if (error = 816 VOP_BLKATOFF(dvp, (off_t)dp->i_offset, (char **)&ep, &bp)) 817 return (error); 818 ep->d_ino = WINO; 819 ep->d_type = DT_WHT; 820 error = VOP_BWRITE(bp); 821 dp->i_flag |= IN_CHANGE | IN_UPDATE; 822 return (error); 823 } 824 825 if (dp->i_count == 0) { 826 /* 827 * First entry in block: set d_ino to zero. 828 */ 829 if (error = 830 VOP_BLKATOFF(dvp, (off_t)dp->i_offset, (char **)&ep, &bp)) 831 return (error); 832 ep->d_ino = 0; 833 error = VOP_BWRITE(bp); 834 dp->i_flag |= IN_CHANGE | IN_UPDATE; 835 return (error); 836 } 837 /* 838 * Collapse new free space into previous entry. 839 */ 840 if (error = VOP_BLKATOFF(dvp, (off_t)(dp->i_offset - dp->i_count), 841 (char **)&ep, &bp)) 842 return (error); 843 ep->d_reclen += dp->i_reclen; 844 error = VOP_BWRITE(bp); 845 dp->i_flag |= IN_CHANGE | IN_UPDATE; 846 return (error); 847 } 848 849 /* 850 * Rewrite an existing directory entry to point at the inode 851 * supplied. The parameters describing the directory entry are 852 * set up by a call to namei. 853 */ 854 int 855 ufs_dirrewrite(dp, ip, cnp) 856 struct inode *dp, *ip; 857 struct componentname *cnp; 858 { 859 struct buf *bp; 860 struct direct *ep; 861 struct vnode *vdp = ITOV(dp); 862 int error; 863 864 if (error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, (char **)&ep, &bp)) 865 return (error); 866 ep->d_ino = ip->i_number; 867 if (vdp->v_mount->mnt_maxsymlinklen > 0) 868 ep->d_type = IFTODT(ip->i_mode); 869 error = VOP_BWRITE(bp); 870 dp->i_flag |= IN_CHANGE | IN_UPDATE; 871 return (error); 872 } 873 874 /* 875 * Check if a directory is empty or not. 876 * Inode supplied must be locked. 877 * 878 * Using a struct dirtemplate here is not precisely 879 * what we want, but better than using a struct direct. 880 * 881 * NB: does not handle corrupted directories. 882 */ 883 int 884 ufs_dirempty(ip, parentino, cred) 885 register struct inode *ip; 886 ino_t parentino; 887 struct ucred *cred; 888 { 889 register off_t off; 890 struct dirtemplate dbuf; 891 register struct direct *dp = (struct direct *)&dbuf; 892 int error, count, namlen; 893 #define MINDIRSIZ (sizeof (struct dirtemplate) / 2) 894 895 for (off = 0; off < ip->i_size; off += dp->d_reclen) { 896 error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off, 897 UIO_SYSSPACE, IO_NODELOCKED, cred, &count, (struct proc *)0); 898 /* 899 * Since we read MINDIRSIZ, residual must 900 * be 0 unless we're at end of file. 901 */ 902 if (error || count != 0) 903 return (0); 904 /* avoid infinite loops */ 905 if (dp->d_reclen == 0) 906 return (0); 907 /* skip empty entries */ 908 if (dp->d_ino == 0 || dp->d_ino == WINO) 909 continue; 910 /* accept only "." and ".." */ 911 # if (BYTE_ORDER == LITTLE_ENDIAN) 912 if (ITOV(ip)->v_mount->mnt_maxsymlinklen > 0) 913 namlen = dp->d_namlen; 914 else 915 namlen = dp->d_type; 916 # else 917 namlen = dp->d_namlen; 918 # endif 919 if (namlen > 2) 920 return (0); 921 if (dp->d_name[0] != '.') 922 return (0); 923 /* 924 * At this point namlen must be 1 or 2. 925 * 1 implies ".", 2 implies ".." if second 926 * char is also "." 927 */ 928 if (namlen == 1) 929 continue; 930 if (dp->d_name[1] == '.' && dp->d_ino == parentino) 931 continue; 932 return (0); 933 } 934 return (1); 935 } 936 937 /* 938 * Check if source directory is in the path of the target directory. 939 * Target is supplied locked, source is unlocked. 940 * The target is always vput before returning. 941 */ 942 int 943 ufs_checkpath(source, target, cred) 944 struct inode *source, *target; 945 struct ucred *cred; 946 { 947 struct vnode *vp; 948 int error, rootino, namlen; 949 struct dirtemplate dirbuf; 950 951 vp = ITOV(target); 952 if (target->i_number == source->i_number) { 953 error = EEXIST; 954 goto out; 955 } 956 rootino = ROOTINO; 957 error = 0; 958 if (target->i_number == rootino) 959 goto out; 960 961 for (;;) { 962 if (vp->v_type != VDIR) { 963 error = ENOTDIR; 964 break; 965 } 966 error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf, 967 sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE, 968 IO_NODELOCKED, cred, (int *)0, (struct proc *)0); 969 if (error != 0) 970 break; 971 # if (BYTE_ORDER == LITTLE_ENDIAN) 972 if (vp->v_mount->mnt_maxsymlinklen > 0) 973 namlen = dirbuf.dotdot_namlen; 974 else 975 namlen = dirbuf.dotdot_type; 976 # else 977 namlen = dirbuf.dotdot_namlen; 978 # endif 979 if (namlen != 2 || 980 dirbuf.dotdot_name[0] != '.' || 981 dirbuf.dotdot_name[1] != '.') { 982 error = ENOTDIR; 983 break; 984 } 985 if (dirbuf.dotdot_ino == source->i_number) { 986 error = EINVAL; 987 break; 988 } 989 if (dirbuf.dotdot_ino == rootino) 990 break; 991 vput(vp); 992 if (error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino, &vp)) { 993 vp = NULL; 994 break; 995 } 996 } 997 998 out: 999 if (error == ENOTDIR) 1000 printf("checkpath: .. not a directory\n"); 1001 if (vp != NULL) 1002 vput(vp); 1003 return (error); 1004 } 1005