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