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.8 (Berkeley) 05/14/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 vdp->v_count++; 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 vdp->v_count++; 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 vdp->v_count++; /* 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 error = rdwri(UIO_WRITE, dp, (caddr_t)&ndp->ni_dent, 548 newentrysize, ndp->ni_offset, UIO_SYSSPACE, ndp->ni_cred, 549 (int *)0); 550 if (DIRBLKSIZ > dp->i_fs->fs_fsize) 551 panic("wdir: blksize"); /* XXX - should grow w/balloc */ 552 else 553 dp->i_size = roundup(dp->i_size, DIRBLKSIZ); 554 iput(dp); 555 return (error); 556 } 557 558 /* 559 * If ndp->ni_count is non-zero, then namei found space for the new 560 * entry in the range ndp->ni_offset to ndp->ni_offset + ndp->ni_count. 561 * in the directory. To use this space, we may have to compact 562 * the entries located there, by copying them together towards 563 * the beginning of the block, leaving the free space in 564 * one usable chunk at the end. 565 */ 566 567 /* 568 * Increase size of directory if entry eats into new space. 569 * This should never push the size past a new multiple of 570 * DIRBLKSIZE. 571 * 572 * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN. 573 */ 574 if (ndp->ni_offset + ndp->ni_count > dp->i_size) 575 dp->i_size = ndp->ni_offset + ndp->ni_count; 576 /* 577 * Get the block containing the space for the new directory entry. 578 */ 579 if (error = blkatoff(dp, ndp->ni_offset, (char **)&dirbuf, &bp)) { 580 iput(dp); 581 return (error); 582 } 583 /* 584 * Find space for the new entry. In the simple case, the 585 * entry at offset base will have the space. If it does 586 * not, then namei arranged that compacting the region 587 * ndp->ni_offset to ndp->ni_offset+ndp->ni_count would yield the space. 588 */ 589 ep = (struct direct *)dirbuf; 590 dsize = DIRSIZ(ep); 591 spacefree = ep->d_reclen - dsize; 592 for (loc = ep->d_reclen; loc < ndp->ni_count; ) { 593 nep = (struct direct *)(dirbuf + loc); 594 if (ep->d_ino) { 595 /* trim the existing slot */ 596 ep->d_reclen = dsize; 597 ep = (struct direct *)((char *)ep + dsize); 598 } else { 599 /* overwrite; nothing there; header is ours */ 600 spacefree += dsize; 601 } 602 dsize = DIRSIZ(nep); 603 spacefree += nep->d_reclen - dsize; 604 loc += nep->d_reclen; 605 bcopy((caddr_t)nep, (caddr_t)ep, dsize); 606 } 607 /* 608 * Update the pointer fields in the previous entry (if any), 609 * copy in the new entry, and write out the block. 610 */ 611 if (ep->d_ino == 0) { 612 if (spacefree + dsize < newentrysize) 613 panic("wdir: compact1"); 614 ndp->ni_dent.d_reclen = spacefree + dsize; 615 } else { 616 if (spacefree < newentrysize) 617 panic("wdir: compact2"); 618 ndp->ni_dent.d_reclen = spacefree; 619 ep->d_reclen = dsize; 620 ep = (struct direct *)((char *)ep + dsize); 621 } 622 bcopy((caddr_t)&ndp->ni_dent, (caddr_t)ep, (u_int)newentrysize); 623 error = bwrite(bp); 624 dp->i_flag |= IUPD|ICHG; 625 if (ndp->ni_endoff && ndp->ni_endoff < dp->i_size) 626 error = itrunc(dp, (u_long)ndp->ni_endoff); 627 iput(dp); 628 return (error); 629 } 630 631 /* 632 * Remove a directory entry after a call to namei, using the 633 * parameters which it left in the u. area. The u. entry 634 * ni_offset contains the offset into the directory of the 635 * entry to be eliminated. The ni_count field contains the 636 * size of the previous record in the directory. If this 637 * is 0, the first entry is being deleted, so we need only 638 * zero the inode number to mark the entry as free. If the 639 * entry isn't the first in the directory, we must reclaim 640 * the space of the now empty record by adding the record size 641 * to the size of the previous entry. 642 */ 643 dirremove(ndp) 644 register struct nameidata *ndp; 645 { 646 register struct inode *dp = VTOI(ndp->ni_dvp); 647 struct direct *ep; 648 struct buf *bp; 649 int error; 650 651 if (ndp->ni_count == 0) { 652 /* 653 * First entry in block: set d_ino to zero. 654 */ 655 ndp->ni_dent.d_ino = 0; 656 error = rdwri(UIO_WRITE, dp, (caddr_t)&ndp->ni_dent, 657 (int)DIRSIZ(&ndp->ni_dent), ndp->ni_offset, UIO_SYSSPACE, 658 ndp->ni_cred, (int *)0); 659 } else { 660 /* 661 * Collapse new free space into previous entry. 662 */ 663 if (error = blkatoff(dp, ndp->ni_offset - ndp->ni_count, 664 (char **)&ep, &bp)) { 665 return (error); 666 } 667 ep->d_reclen += ndp->ni_dent.d_reclen; 668 error = bwrite(bp); 669 dp->i_flag |= IUPD|ICHG; 670 } 671 return (error); 672 } 673 674 /* 675 * Rewrite an existing directory entry to point at the inode 676 * supplied. The parameters describing the directory entry are 677 * set up by a call to namei. 678 */ 679 dirrewrite(dp, ip, ndp) 680 struct inode *dp, *ip; 681 struct nameidata *ndp; 682 { 683 684 ndp->ni_dent.d_ino = ip->i_number; 685 return (rdwri(UIO_WRITE, dp, (caddr_t)&ndp->ni_dent, 686 (int)DIRSIZ(&ndp->ni_dent), ndp->ni_offset, UIO_SYSSPACE, 687 ndp->ni_cred, (int *)0)); 688 } 689 690 /* 691 * Return buffer with contents of block "offset" 692 * from the beginning of directory "ip". If "res" 693 * is non-zero, fill it in with a pointer to the 694 * remaining space in the directory. 695 */ 696 blkatoff(ip, offset, res, bpp) 697 struct inode *ip; 698 off_t offset; 699 char **res; 700 struct buf **bpp; 701 { 702 register struct fs *fs = ip->i_fs; 703 daddr_t lbn = lblkno(fs, offset); 704 int bsize = blksize(fs, ip, lbn); 705 struct buf *bp; 706 daddr_t bn; 707 int error; 708 709 *bpp = 0; 710 if (error = bmap(ip, lbn, &bn, (daddr_t *)0, (int *)0)) 711 return (error); 712 if (bn == (daddr_t)-1) { 713 dirbad(ip, offset, "hole in dir"); 714 return (EIO); 715 } 716 error = bread(ip->i_devvp, bn, bsize, &bp); 717 if (error) { 718 brelse(bp); 719 return (error); 720 } 721 if (res) 722 *res = bp->b_un.b_addr + blkoff(fs, offset); 723 *bpp = bp; 724 return (0); 725 } 726 727 /* 728 * Check if a directory is empty or not. 729 * Inode supplied must be locked. 730 * 731 * Using a struct dirtemplate here is not precisely 732 * what we want, but better than using a struct direct. 733 * 734 * NB: does not handle corrupted directories. 735 */ 736 dirempty(ip, parentino, cred) 737 register struct inode *ip; 738 ino_t parentino; 739 struct ucred *cred; 740 { 741 register off_t off; 742 struct dirtemplate dbuf; 743 register struct direct *dp = (struct direct *)&dbuf; 744 int error, count; 745 #define MINDIRSIZ (sizeof (struct dirtemplate) / 2) 746 747 for (off = 0; off < ip->i_size; off += dp->d_reclen) { 748 error = rdwri(UIO_READ, ip, (caddr_t)dp, MINDIRSIZ, off, 749 UIO_SYSSPACE, cred, &count); 750 /* 751 * Since we read MINDIRSIZ, residual must 752 * be 0 unless we're at end of file. 753 */ 754 if (error || count != 0) 755 return (0); 756 /* avoid infinite loops */ 757 if (dp->d_reclen == 0) 758 return (0); 759 /* skip empty entries */ 760 if (dp->d_ino == 0) 761 continue; 762 /* accept only "." and ".." */ 763 if (dp->d_namlen > 2) 764 return (0); 765 if (dp->d_name[0] != '.') 766 return (0); 767 /* 768 * At this point d_namlen must be 1 or 2. 769 * 1 implies ".", 2 implies ".." if second 770 * char is also "." 771 */ 772 if (dp->d_namlen == 1) 773 continue; 774 if (dp->d_name[1] == '.' && dp->d_ino == parentino) 775 continue; 776 return (0); 777 } 778 return (1); 779 } 780 781 /* 782 * Check if source directory is in the path of the target directory. 783 * Target is supplied locked, source is unlocked. 784 * The target is always iput() before returning. 785 */ 786 checkpath(source, target, cred) 787 struct inode *source, *target; 788 struct ucred *cred; 789 { 790 struct dirtemplate dirbuf; 791 struct inode *ip; 792 int error = 0; 793 794 ip = target; 795 if (ip->i_number == source->i_number) { 796 error = EEXIST; 797 goto out; 798 } 799 if (ip->i_number == ROOTINO) 800 goto out; 801 802 for (;;) { 803 if ((ip->i_mode&IFMT) != IFDIR) { 804 error = ENOTDIR; 805 break; 806 } 807 error = rdwri(UIO_READ, ip, (caddr_t)&dirbuf, 808 sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE, 809 cred, (int *)0); 810 if (error != 0) 811 break; 812 if (dirbuf.dotdot_namlen != 2 || 813 dirbuf.dotdot_name[0] != '.' || 814 dirbuf.dotdot_name[1] != '.') { 815 error = ENOTDIR; 816 break; 817 } 818 if (dirbuf.dotdot_ino == source->i_number) { 819 error = EINVAL; 820 break; 821 } 822 if (dirbuf.dotdot_ino == ROOTINO) 823 break; 824 iput(ip); 825 if (error = iget(ip, dirbuf.dotdot_ino, &ip)) 826 break; 827 } 828 829 out: 830 if (error == ENOTDIR) 831 printf("checkpath: .. not a directory\n"); 832 if (ip != NULL) 833 iput(ip); 834 return (error); 835 } 836