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