1 /* ufs_lookup.c 4.37 83/04/22 */ 2 3 #include "../h/param.h" 4 #include "../h/systm.h" 5 #include "../h/inode.h" 6 #include "../h/fs.h" 7 #include "../h/mount.h" 8 #include "../h/dir.h" 9 #include "../h/user.h" 10 #include "../h/buf.h" 11 #include "../h/conf.h" 12 #include "../h/uio.h" 13 #include "../h/nami.h" 14 15 struct buf *blkatoff(); 16 int dirchk = 0; 17 /* 18 * Convert a pathname into a pointer to a locked inode, 19 * with side effects usable in creating and removing files. 20 * This is a very central and rather complicated routine. 21 * 22 * The func argument gives the routine which returns successive 23 * characters of the name to be translated. 24 * 25 * The flag argument is (LOOKUP, CREATE, DELETE) depending on whether 26 * the name is to be (looked up, created, deleted). If flag has 27 * LOCKPARENT or'ed into it and the target of the pathname exists, 28 * namei returns both the target and its parent directory locked. 29 * If the file system is not maintained in a strict tree hierarchy, 30 * this can result in a deadlock situation. When creating and 31 * LOCKPARENT is specified, the target may not be ".". When deleting 32 * and LOCKPARENT is specified, the target may be ".", but the caller 33 * must check to insure it does an irele and iput instead of two iputs. 34 * 35 * The follow argument is 1 when symbolic links are to be followed 36 * when they occur at the end of the name translation process. 37 * 38 * Overall outline: 39 * 40 * copy in name 41 * get starting directory 42 * dirloop: 43 * check accessibility of directory 44 * dirloop2: 45 * copy next component of name to u.u_dent 46 * handle degenerate case where name is null string 47 * search for name in directory, to found or notfound 48 * notfound: 49 * if creating, return locked directory, leaving info on avail. slots 50 * else return error 51 * found: 52 * if at end of path and deleting, return information to allow delete 53 * if at end of path and rewriting (create and LOCKPARENT), lock targe 54 * inode and return info to allow rewrite 55 * if .. and on mounted filesys, look in mount table for parent 56 * if symbolic link, massage name in buffer and continue at dirloop 57 * if more components of name, do next level at dirloop 58 * return the answer as locked inode 59 * 60 * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode, 61 * but unlocked. 62 */ 63 struct inode * 64 namei(func, flag, follow) 65 int (*func)(), flag, follow; 66 { 67 register char *cp; /* pointer into pathname argument */ 68 /* these variables refer to things which must be freed or unlocked */ 69 register struct inode *dp = 0; /* the directory we are searching */ 70 register struct fs *fs; /* file system that directory is in */ 71 register struct buf *bp = 0; /* a buffer of directory entries */ 72 register struct direct *ep; /* the current directory entry */ 73 int entryoffsetinblock; /* offset of ep in bp's buffer */ 74 register struct buf *nbp; /* buffer storing path name argument */ 75 /* these variables hold information about the search for a slot */ 76 enum {NONE, COMPACT, FOUND} slotstatus; 77 int slotoffset = -1; /* 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 /* */ 82 int dirsize; 83 int prevoff; /* u.u_offset of previous entry */ 84 int nlink = 0; /* number of symbolic links taken */ 85 struct inode *pdp; /* saved dp during symlink work */ 86 int i; 87 int lockparent; 88 89 lockparent = flag & LOCKPARENT; 90 flag &= ~LOCKPARENT; 91 /* 92 * Get a buffer for the name to be translated, and copy the 93 * name into the buffer. 94 */ 95 nbp = geteblk(MAXPATHLEN); 96 for (cp = nbp->b_un.b_addr; *cp = (*func)(); ) { 97 if ((*cp&0377) == ('/'|0200) || (*cp&0200) && flag != 2) { 98 u.u_error = EPERM; 99 goto bad; 100 } 101 cp++; 102 if (cp >= nbp->b_un.b_addr + MAXPATHLEN) { 103 u.u_error = ENOENT; 104 goto bad; 105 } 106 } 107 if (u.u_error) 108 goto bad; 109 110 /* 111 * Get starting directory. 112 */ 113 cp = nbp->b_un.b_addr; 114 if (*cp == '/') { 115 while (*cp == '/') 116 cp++; 117 if ((dp = u.u_rdir) == NULL) 118 dp = rootdir; 119 } else 120 dp = u.u_cdir; 121 fs = dp->i_fs; 122 ilock(dp); 123 dp->i_count++; 124 u.u_pdir = (struct inode *)0xc0000000; /* illegal */ 125 126 /* 127 * We come to dirloop to search a new directory. 128 * The directory must be locked so that it can be 129 * iput, and fs must be already set to dp->i_fs. 130 */ 131 dirloop: 132 /* 133 * Check accessiblity of directory. 134 */ 135 if ((dp->i_mode&IFMT) != IFDIR) { 136 u.u_error = ENOTDIR; 137 goto bad; 138 } 139 if (access(dp, IEXEC)) 140 goto bad; 141 142 dirloop2: 143 /* 144 * Copy next component of name to u.u_dent. 145 */ 146 for (i = 0; *cp != 0 && *cp != '/'; cp++) { 147 if (i >= MAXNAMLEN) { 148 u.u_error = ENOENT; 149 goto bad; 150 } 151 u.u_dent.d_name[i++] = *cp; 152 } 153 u.u_dent.d_namlen = i; 154 u.u_dent.d_name[i] = 0; 155 156 /* 157 * Check for degenerate name (e.g. / or "") 158 * which is a way of talking about a directory, 159 * e.g. like "/." or ".". 160 */ 161 if (u.u_dent.d_name[0] == 0) { 162 if (flag || lockparent) { 163 u.u_error = ENOENT; 164 goto bad; 165 } 166 brelse(nbp); 167 return (dp); 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 if (flag == CREATE && *cp == 0) { 178 slotstatus = NONE; 179 slotfreespace = 0; 180 slotneeded = DIRSIZ(&u.u_dent); 181 } 182 183 dirsize = roundup(dp->i_size, DIRBLKSIZ); 184 u.u_offset = 0; 185 while (u.u_offset < dirsize) { 186 /* 187 * If offset is on a block boundary, 188 * read the next directory block. 189 * Release previous if it exists. 190 */ 191 if (blkoff(fs, u.u_offset) == 0) { 192 if (bp != NULL) 193 brelse(bp); 194 bp = blkatoff(dp, u.u_offset, (char **)0); 195 if (bp == 0) 196 goto bad; 197 entryoffsetinblock = 0; 198 } 199 200 /* 201 * If still looking for a slot, and at a DIRBLKSIZE 202 * boundary, have to start looking for free space 203 * again. 204 */ 205 if (slotstatus == NONE && 206 (entryoffsetinblock&(DIRBLKSIZ-1)) == 0) { 207 slotoffset = -1; 208 slotfreespace = 0; 209 } 210 211 /* 212 * Get pointer to next entry, and do consistency checking: 213 * record length must be multiple of 4 214 * record length must not be zero 215 * entry must fit in rest of this DIRBLKSIZ block 216 * record must be large enough to contain name 217 * When dirchk is set we also check: 218 * name is not longer than MAXNAMLEN 219 * name must be as long as advertised, and null terminated 220 * Checking last two conditions is done only when dirchk is 221 * set, to save time. 222 */ 223 ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock); 224 i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)); 225 if ((ep->d_reclen & 0x3) || ep->d_reclen == 0 || 226 ep->d_reclen > i || DIRSIZ(ep) > ep->d_reclen || 227 dirchk && (ep->d_namlen > MAXNAMLEN || dirbadname(ep))) { 228 dirbad(dp, "mangled entry"); 229 u.u_offset += i; 230 entryoffsetinblock += i; 231 continue; 232 } 233 234 /* 235 * If an appropriate sized slot has not yet been found, 236 * check to see if one is available. Also accumulate space 237 * in the current block so that we can determine if 238 * compaction is viable. 239 */ 240 if (slotstatus != FOUND) { 241 int size = ep->d_reclen; 242 243 if (ep->d_ino != 0) 244 size -= DIRSIZ(ep); 245 if (size > 0) { 246 if (size >= slotneeded) { 247 slotstatus = FOUND; 248 slotoffset = u.u_offset; 249 slotsize = ep->d_reclen; 250 } else if (slotstatus == NONE) { 251 slotfreespace += size; 252 if (slotoffset == -1) 253 slotoffset = u.u_offset; 254 if (slotfreespace >= slotneeded) { 255 slotstatus = COMPACT; 256 slotsize = 257 u.u_offset+ep->d_reclen - 258 slotoffset; 259 } 260 } 261 } 262 } 263 264 /* 265 * Check for a name match. 266 */ 267 if (ep->d_ino) { 268 if (ep->d_namlen == u.u_dent.d_namlen && 269 !bcmp(u.u_dent.d_name, ep->d_name, ep->d_namlen)) 270 goto found; 271 } 272 prevoff = u.u_offset; 273 u.u_offset += ep->d_reclen; 274 entryoffsetinblock += ep->d_reclen; 275 } 276 /* notfound: */ 277 /* 278 * If creating, and at end of pathname and current 279 * directory has not been removed, then can consider 280 * allowing file to be created. 281 */ 282 if (flag == CREATE && *cp == 0 && dp->i_nlink != 0) { 283 /* 284 * Access for write is interpreted as allowing 285 * creation of files in the directory. 286 */ 287 if (access(dp, IWRITE)) 288 goto bad; 289 /* 290 * Return an indication of where the new directory 291 * entry should be put. If we didn't find a slot, 292 * then set u.u_count to 0 indicating that the 293 * new slot belongs at the end of the directory. 294 * If we found a slot, then the new entry can be 295 * put in the range [u.u_offset..u.u_offset+u.u_count) 296 */ 297 if (slotstatus == NONE) 298 u.u_count = 0; 299 else { 300 u.u_offset = slotoffset; 301 u.u_count = slotsize; 302 } 303 dp->i_flag |= IUPD|ICHG; 304 if (bp) 305 brelse(bp); 306 brelse(nbp); 307 /* 308 * We return with the directory locked, so that 309 * the parameters we set up above will still be 310 * valid if we actually decide to do a direnter(). 311 * We return NULL to indicate that the entry doesn't 312 * currently exist, leaving a pointer to the (locked) 313 * directory inode in u.u_pdir. 314 */ 315 u.u_pdir = dp; 316 return (NULL); 317 } 318 u.u_error = ENOENT; 319 goto bad; 320 found: 321 /* 322 * Check that directory length properly reflects presence 323 * of this entry. 324 */ 325 if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) { 326 dirbad(dp, "i_size too small"); 327 dp->i_size = entryoffsetinblock + DIRSIZ(ep); 328 dp->i_flag |= IUPD|ICHG; 329 } 330 331 /* 332 * Found component in pathname; save directory 333 * entry in u.u_dent, and release directory buffer. 334 */ 335 bcopy((caddr_t)ep, (caddr_t)&u.u_dent, (u_int)DIRSIZ(ep)); 336 brelse(bp); 337 bp = NULL; 338 339 /* 340 * If deleting, and at end of pathname, return 341 * parameters which can be used to remove file. 342 * If the lockparent flag isn't set, we return only 343 * the directory (in u.u_pdir), otherwise we go 344 * on and lock the inode, being careful with ".". 345 */ 346 if (flag == DELETE && *cp == 0) { 347 /* 348 * Write access to directory required to delete files. 349 */ 350 if (access(dp, IWRITE)) 351 goto bad; 352 u.u_pdir = dp; /* for dirremove() */ 353 /* 354 * Return pointer to current entry in u.u_offset, 355 * and distance past previous entry (if there 356 * is a previous entry in this block) in u.u_count. 357 * Save directory inode pointer in u.u_pdir for dirremove(). 358 */ 359 if ((u.u_offset&(DIRBLKSIZ-1)) == 0) 360 u.u_count = 0; 361 else 362 u.u_count = u.u_offset - prevoff; 363 if (lockparent) { 364 if (dp->i_number == u.u_dent.d_ino) 365 dp->i_count++; 366 else { 367 dp = iget(dp->i_dev, fs, u.u_dent.d_ino); 368 if (dp == NULL) { 369 iput(u.u_pdir); 370 goto bad; 371 } 372 } 373 } 374 brelse(nbp); 375 return (dp); 376 } 377 378 /* 379 * Special handling for ".." allowing chdir out of mounted 380 * file system: indirect .. in root inode to reevaluate 381 * in directory file system was mounted on. 382 */ 383 if (u.u_dent.d_name[0] == '.' && u.u_dent.d_name[1] == '.' && 384 u.u_dent.d_name[2] == '\0') { 385 if (dp == u.u_rdir) 386 u.u_dent.d_ino = dp->i_number; 387 else if (u.u_dent.d_ino == ROOTINO && 388 dp->i_number == ROOTINO) { 389 for (i = 1; i < NMOUNT; i++) 390 if (mount[i].m_bufp != NULL && 391 mount[i].m_dev == dp->i_dev) { 392 iput(dp); 393 dp = mount[i].m_inodp; 394 ilock(dp); 395 dp->i_count++; 396 fs = dp->i_fs; 397 cp -= 2; /* back over .. */ 398 goto dirloop2; 399 } 400 } 401 } 402 403 /* 404 * If rewriting (rename), return the inode and the 405 * information required to rewrite the present directory 406 * Must get inode of directory entry to verify it's a 407 * regular file, or empty directory. 408 */ 409 if ((flag == CREATE && lockparent) && *cp == 0) { 410 if (access(dp, IWRITE)) 411 goto bad; 412 u.u_pdir = dp; /* for dirrewrite() */ 413 /* 414 * Careful about locking second inode. 415 * This can only occur if the target is ".". 416 */ 417 if (dp->i_number == u.u_dent.d_ino) { 418 u.u_error = EISDIR; /* XXX */ 419 goto bad; 420 } 421 dp = iget(dp->i_dev, fs, u.u_dent.d_ino); 422 if (dp == NULL) { 423 iput(u.u_pdir); 424 goto bad; 425 } 426 brelse(nbp); 427 return (dp); 428 } 429 430 /* 431 * Check for symbolic link, which may require us to massage the 432 * name before we continue translation. We do not `iput' the 433 * directory because we may need it again if the 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 sophistocated 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 (bcmp(u.u_dent.d_name, "..", 3) == 0) { 452 iunlock(pdp); /* race to get the inode */ 453 dp = iget(dp->i_dev, fs, u.u_dent.d_ino); 454 if (dp == NULL) 455 goto bad2; 456 } else if (dp->i_number == u.u_dent.d_ino) { 457 dp->i_count++; /* we want ourself, ie "." */ 458 } else { 459 dp = iget(dp->i_dev, fs, u.u_dent.d_ino); 460 iunlock(pdp); 461 if (dp == NULL) 462 goto bad2; 463 } 464 fs = dp->i_fs; 465 466 /* 467 * Check for symbolic link 468 */ 469 if ((dp->i_mode & IFMT) == IFLNK && (follow || *cp == '/')) { 470 u_int pathlen = strlen(cp) + 1; 471 472 if (dp->i_size + pathlen >= MAXPATHLEN - 1 || 473 ++nlink > MAXSYMLINKS) { 474 u.u_error = ELOOP; 475 goto bad2; 476 } 477 ovbcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen); 478 u.u_error = 479 rdwri(UIO_READ, dp, nbp->b_un.b_addr, (int)dp->i_size, 480 0, 1, (int *)0); 481 if (u.u_error) 482 goto bad2; 483 cp = nbp->b_un.b_addr; 484 iput(dp); 485 if (*cp == '/') { 486 irele(pdp); 487 while (*cp == '/') 488 cp++; 489 if ((dp = u.u_rdir) == NULL) 490 dp = rootdir; 491 ilock(dp); 492 dp->i_count++; 493 } else { 494 dp = pdp; 495 ilock(dp); 496 } 497 fs = dp->i_fs; 498 goto dirloop; 499 } 500 501 /* 502 * Not a symbolic link. If more pathname, 503 * continue at next component, else return. 504 */ 505 if (*cp == '/') { 506 while (*cp == '/') 507 cp++; 508 irele(pdp); 509 goto dirloop; 510 } 511 brelse(nbp); 512 if (lockparent) 513 u.u_pdir = pdp; 514 else 515 irele(pdp); 516 return (dp); 517 bad2: 518 irele(pdp); 519 bad: 520 if (bp) 521 brelse(bp); 522 if (dp) 523 iput(dp); 524 brelse(nbp); 525 return (NULL); 526 } 527 528 dirbad(ip, how) 529 struct inode *ip; 530 char *how; 531 { 532 533 printf("%s: bad dir ino %d at offset %d: %s\n", 534 ip->i_fs->fs_fsmnt, ip->i_number, u.u_offset, how); 535 } 536 537 dirbadname(ep) 538 register struct direct *ep; 539 { 540 register int i; 541 542 for (i = 0; i < ep->d_namlen; i++) 543 if (ep->d_name[i] == 0) 544 return (1); 545 return (ep->d_name[i]); 546 } 547 548 /* 549 * Write a directory entry after a call to namei, using the parameters 550 * which it left in the u. area. The argument ip is the inode which 551 * the new directory entry will refer to. The u. area field u.u_pdir is 552 * a pointer to the directory to be written, which was left locked by 553 * namei. Remaining parameters (u.u_offset, u.u_count) indicate 554 * how the space for the new entry is to be gotten. 555 */ 556 direnter(ip) 557 struct inode *ip; 558 { 559 register struct direct *ep, *nep; 560 struct buf *bp; 561 int loc, spacefree, error = 0; 562 u_int dsize; 563 int newentrysize; 564 char *dirbuf; 565 566 u.u_dent.d_ino = ip->i_number; 567 u.u_segflg = 1; 568 newentrysize = DIRSIZ(&u.u_dent); 569 if (u.u_count == 0) { 570 /* 571 * If u.u_count is 0, then namei could find no space in the 572 * directory. In this case u.u_offset will be on a directory 573 * block boundary and we will write the new entry into a fresh 574 * block. 575 */ 576 if (u.u_offset&(DIRBLKSIZ-1)) 577 panic("wdir: newblk"); 578 u.u_dent.d_reclen = DIRBLKSIZ; 579 error = rdwri(UIO_WRITE, u.u_pdir, (caddr_t)&u.u_dent, 580 newentrysize, u.u_offset, 1, (int *)0); 581 iput(u.u_pdir); 582 return (error); 583 } 584 585 /* 586 * If u.u_count is non-zero, then namei found space for the 587 * new entry in the range u.u_offset to u.u_offset+u.u_count. 588 * in the directory. To use this space, we may have to compact 589 * the entries located there, by copying them together towards 590 * the beginning of the block, leaving the free space in 591 * one usable chunk at the end. 592 */ 593 594 /* 595 * Increase size of directory if entry eats into new space. 596 * This should never push the size past a new multiple of 597 * DIRBLKSIZE. 598 */ 599 if (u.u_offset + u.u_count > u.u_pdir->i_size) 600 u.u_pdir->i_size = u.u_offset + u.u_count; 601 602 /* 603 * Get the block containing the space for the new directory 604 * entry. Should return error by result instead of u.u_error. 605 */ 606 bp = blkatoff(u.u_pdir, u.u_offset, (char **)&dirbuf); 607 if (bp == 0) { 608 iput(u.u_pdir); 609 return (u.u_error); 610 } 611 612 /* 613 * Find space for the new entry. In the simple case, the 614 * entry at offset base will have the space. If it does 615 * not, then namei arranged that compacting the region 616 * u.u_offset to u.u_offset+u.u_count would yield the space. 617 */ 618 ep = (struct direct *)dirbuf; 619 dsize = DIRSIZ(ep); 620 spacefree = ep->d_reclen - dsize; 621 for (loc = ep->d_reclen; loc < u.u_count; ) { 622 nep = (struct direct *)(dirbuf + loc); 623 if (ep->d_ino) { 624 /* trim the existing slot */ 625 ep->d_reclen = dsize; 626 ep = (struct direct *)((char *)ep + dsize); 627 } else { 628 /* overwrite; nothing there; header is ours */ 629 spacefree += dsize; 630 } 631 dsize = DIRSIZ(nep); 632 spacefree += nep->d_reclen - dsize; 633 loc += nep->d_reclen; 634 bcopy((caddr_t)nep, (caddr_t)ep, dsize); 635 } 636 /* 637 * Update the pointer fields in the previous entry (if any), 638 * copy in the new entry, and write out the block. 639 */ 640 if (ep->d_ino == 0) { 641 if (spacefree + dsize < newentrysize) 642 panic("wdir: compact1"); 643 u.u_dent.d_reclen = spacefree + dsize; 644 } else { 645 if (spacefree < newentrysize) 646 panic("wdir: compact2"); 647 u.u_dent.d_reclen = spacefree; 648 ep->d_reclen = dsize; 649 ep = (struct direct *)((char *)ep + dsize); 650 } 651 bcopy((caddr_t)&u.u_dent, (caddr_t)ep, (u_int)newentrysize); 652 bwrite(bp); 653 u.u_pdir->i_flag |= IUPD|ICHG; 654 iput(u.u_pdir); 655 return (error); 656 } 657 658 /* 659 * Remove a directory entry after a call to namei, using the 660 * parameters which it left in the u. area. The u. entry 661 * u_offset contains the offset into the directory of the 662 * entry to be eliminated. The u_count field contains the 663 * size of the previous record in the directory. If this 664 * is 0, the first entry is being deleted, so we need only 665 * zero the inode number to mark the entry as free. If the 666 * entry isn't the first in the directory, we must reclaim 667 * the space of the now empty record by adding the record size 668 * to the size of the previous entry. 669 */ 670 dirremove() 671 { 672 register struct inode *dp = u.u_pdir; 673 register struct buf *bp; 674 struct direct *ep; 675 676 if (u.u_count == 0) { 677 /* 678 * First entry in block: set d_ino to zero. 679 */ 680 u.u_dent.d_ino = 0; 681 (void) rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent, 682 (int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0); 683 } else { 684 /* 685 * Collapse new free space into previous entry. 686 */ 687 bp = blkatoff(dp, (int)(u.u_offset - u.u_count), (char **)&ep); 688 if (bp == 0) 689 return (0); 690 ep->d_reclen += u.u_dent.d_reclen; 691 bwrite(bp); 692 dp->i_flag |= IUPD|ICHG; 693 } 694 return (1); 695 } 696 697 /* 698 * Rewrite an existing directory entry to point at the inode 699 * supplied. The parameters describing the directory entry are 700 * set up by a call to namei. 701 */ 702 dirrewrite(dp, ip) 703 struct inode *dp, *ip; 704 { 705 706 u.u_dent.d_ino = ip->i_number; 707 u.u_error = rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent, 708 (int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0); 709 iput(dp); 710 } 711 712 /* 713 * Return buffer with contents of block "offset" 714 * from the beginning of directory "ip". If "res" 715 * is non-zero, fill it in with a pointer to the 716 * remaining space in the directory. 717 */ 718 struct buf * 719 blkatoff(ip, offset, res) 720 struct inode *ip; 721 off_t offset; 722 char **res; 723 { 724 register struct fs *fs = ip->i_fs; 725 daddr_t lbn = lblkno(fs, offset); 726 int base = blkoff(fs, offset); 727 int bsize = blksize(fs, ip, lbn); 728 daddr_t bn = fsbtodb(fs, bmap(ip, lbn, B_WRITE, base, bsize)); 729 register struct buf *bp; 730 731 if (u.u_error) 732 return (0); 733 bp = bread(ip->i_dev, bn, bsize); 734 if (bp->b_flags & B_ERROR) { 735 brelse(bp); 736 return (0); 737 } 738 if (res) 739 *res = bp->b_un.b_addr + base; 740 return (bp); 741 } 742 743 /* 744 * Check if a directory is empty or not. 745 * Inode supplied must be locked. 746 */ 747 dirempty(ip) 748 register struct inode *ip; 749 { 750 register off_t off; 751 struct direct dbuf; 752 register struct direct *dp = &dbuf; 753 int error, count; 754 755 for (off = 0; off < ip->i_size; off += dp->d_reclen) { 756 error = rdwri(UIO_READ, ip, (caddr_t)dp, 757 sizeof (struct direct), off, 1, &count); 758 count = sizeof (struct direct) - count; 759 #define MINDIRSIZ (sizeof (struct direct) - (MAXNAMLEN + 1)) 760 if (error || count < MINDIRSIZ || count < DIRSIZ(dp)) 761 return (0); 762 if (dp->d_ino == 0) 763 continue; 764 if (dp->d_name[0] != '.') 765 return (0); 766 if (dp->d_namlen == 1 || 767 (dp->d_namlen == 2 && dp->d_name[1] == '.')) 768 continue; 769 return (0); 770 } 771 return (1); 772 } 773