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