1 /* ufs_lookup.c 4.31 82/11/17 */ 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) { 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 432 * to massage the name before we continue translation. 433 * To avoid deadlock have to unlock the current directory, 434 * but don't iput it because we may need it again (if 435 * the symbolic link is relative to .). Instead save 436 * it (unlocked) as pdp. 437 */ 438 pdp = dp; 439 iunlock(pdp); 440 dp = iget(dp->i_dev, fs, u.u_dent.d_ino); 441 if (dp == NULL) 442 goto bad2; 443 fs = dp->i_fs; 444 445 /* 446 * Check for symbolic link 447 */ 448 if ((dp->i_mode & IFMT) == IFLNK && (follow || *cp == '/')) { 449 u_int pathlen = strlen(cp) + 1; 450 451 if (dp->i_size + pathlen >= MAXPATHLEN - 1 || 452 ++nlink > MAXSYMLINKS) { 453 u.u_error = ELOOP; 454 goto bad2; 455 } 456 ovbcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen); 457 u.u_error = 458 rdwri(UIO_READ, dp, nbp->b_un.b_addr, (int)dp->i_size, 459 0, 1, (int *)0); 460 if (u.u_error) 461 goto bad2; 462 cp = nbp->b_un.b_addr; 463 iput(dp); 464 if (*cp == '/') { 465 irele(pdp); 466 while (*cp == '/') 467 cp++; 468 if ((dp = u.u_rdir) == NULL) 469 dp = rootdir; 470 ilock(dp); 471 dp->i_count++; 472 } else { 473 dp = pdp; 474 ilock(dp); 475 } 476 fs = dp->i_fs; 477 goto dirloop; 478 } 479 480 /* 481 * Not a symbolic link. If more pathname, 482 * continue at next component, else return. 483 */ 484 if (*cp == '/') { 485 while (*cp == '/') 486 cp++; 487 irele(pdp); 488 goto dirloop; 489 } 490 brelse(nbp); 491 if (lockparent) 492 u.u_pdir = pdp; 493 else 494 irele(pdp); 495 return (dp); 496 bad2: 497 irele(pdp); 498 bad: 499 if (bp) 500 brelse(bp); 501 if (dp) 502 iput(dp); 503 brelse(nbp); 504 return (NULL); 505 } 506 507 dirbad(ip, how) 508 struct inode *ip; 509 char *how; 510 { 511 512 printf("%s: bad dir ino %d at offset %d: %s\n", 513 ip->i_fs->fs_fsmnt, ip->i_number, u.u_offset, how); 514 } 515 516 dirbadname(ep) 517 register struct direct *ep; 518 { 519 register int i; 520 521 for (i = 0; i < ep->d_namlen; i++) 522 if (ep->d_name[i] == 0) 523 return (1); 524 return (ep->d_name[i]); 525 } 526 527 /* 528 * Write a directory entry after a call to namei, using the parameters 529 * which it left in the u. area. The argument ip is the inode which 530 * the new directory entry will refer to. The u. area field u.u_pdir is 531 * a pointer to the directory to be written, which was left locked by 532 * namei. Remaining parameters (u.u_offset, u.u_count) indicate 533 * how the space for the new entry is to be gotten. 534 */ 535 direnter(ip) 536 struct inode *ip; 537 { 538 register struct direct *ep, *nep; 539 struct buf *bp; 540 int loc, freespace; 541 u_int dsize; 542 int newentrysize; 543 char *dirbuf; 544 545 u.u_dent.d_ino = ip->i_number; 546 u.u_segflg = 1; 547 newentrysize = DIRSIZ(&u.u_dent); 548 if (u.u_count == 0) { 549 /* 550 * If u.u_count is 0, then namei could find no space in the 551 * directory. In this case u.u_offset will be on a directory 552 * block boundary and we will write the new entry into a fresh 553 * block. 554 */ 555 if (u.u_offset&(DIRBLKSIZ-1)) 556 panic("wdir: newblk"); 557 u.u_dent.d_reclen = DIRBLKSIZ; 558 (void) rdwri(UIO_WRITE, u.u_pdir, (caddr_t)&u.u_dent, 559 newentrysize, u.u_offset, 1, (int *)0); 560 iput(u.u_pdir); 561 return; 562 } 563 564 /* 565 * If u.u_count is non-zero, then namei found space for the 566 * new entry in the range u.u_offset to u.u_offset+u.u_count. 567 * in the directory. To use this space, we may have to compact 568 * the entries located there, by copying them together towards 569 * the beginning of the block, leaving the free space in 570 * one usable chunk at the end. 571 */ 572 573 /* 574 * Increase size of directory if entry eats into new space. 575 * This should never push the size past a new multiple of 576 * DIRBLKSIZE. 577 */ 578 if (u.u_offset + u.u_count > u.u_pdir->i_size) 579 u.u_pdir->i_size = u.u_offset + u.u_count; 580 581 /* 582 * Get the block containing the space for the new directory 583 * entry. 584 */ 585 bp = blkatoff(u.u_pdir, u.u_offset, (char **)&dirbuf); 586 if (bp == 0) { 587 iput(u.u_pdir); 588 return; 589 } 590 591 /* 592 * Find space for the new entry. In the simple case, the 593 * entry at offset base will have the space. If it does 594 * not, then namei arranged that compacting the region 595 * u.u_offset to u.u_offset+u.u_count would yield the space. 596 */ 597 ep = (struct direct *)dirbuf; 598 dsize = DIRSIZ(ep); 599 freespace = ep->d_reclen - dsize; 600 for (loc = ep->d_reclen; loc < u.u_count; ) { 601 nep = (struct direct *)(dirbuf + loc); 602 if (ep->d_ino) { 603 /* trim the existing slot */ 604 ep->d_reclen = dsize; 605 ep = (struct direct *)((char *)ep + dsize); 606 } else { 607 /* overwrite; nothing there; header is ours */ 608 freespace += dsize; 609 } 610 dsize = DIRSIZ(nep); 611 freespace += nep->d_reclen - dsize; 612 loc += nep->d_reclen; 613 bcopy((caddr_t)nep, (caddr_t)ep, dsize); 614 } 615 /* 616 * Update the pointer fields in the previous entry (if any), 617 * copy in the new entry, and write out the block. 618 */ 619 if (ep->d_ino == 0) { 620 if (freespace + dsize < newentrysize) 621 panic("wdir: compact1"); 622 u.u_dent.d_reclen = freespace + dsize; 623 } else { 624 if (freespace < newentrysize) 625 panic("wdir: compact2"); 626 u.u_dent.d_reclen = freespace; 627 ep->d_reclen = dsize; 628 ep = (struct direct *)((char *)ep + dsize); 629 } 630 bcopy((caddr_t)&u.u_dent, (caddr_t)ep, (u_int)newentrysize); 631 bwrite(bp); 632 u.u_pdir->i_flag |= IUPD|ICHG; 633 iput(u.u_pdir); 634 } 635 636 /* 637 * Remove a directory entry after a call to namei, using the 638 * parameters which it left in the u. area. The u. entry 639 * u_offset contains the offset into the directory of the 640 * entry to be eliminated. The u_count field contains the 641 * size of the previous record in the directory. If this 642 * is 0, the first entry is being deleted, so we need only 643 * zero the inode number to mark the entry as free. If the 644 * entry isn't the first in the directory, we must reclaim 645 * the space of the now empty record by adding the record size 646 * to the size of the previous entry. 647 */ 648 dirremove() 649 { 650 register struct inode *dp = u.u_pdir; 651 register struct buf *bp; 652 struct direct *ep; 653 654 if (u.u_count == 0) { 655 /* 656 * First entry in block: set d_ino to zero. 657 */ 658 u.u_dent.d_ino = 0; 659 (void) rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent, 660 (int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0); 661 } else { 662 /* 663 * Collapse new free space into previous entry. 664 */ 665 bp = blkatoff(dp, (int)(u.u_offset - u.u_count), (char **)&ep); 666 if (bp == 0) 667 return (0); 668 ep->d_reclen += u.u_dent.d_reclen; 669 bwrite(bp); 670 dp->i_flag |= IUPD|ICHG; 671 } 672 return (1); 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) 681 struct inode *dp, *ip; 682 { 683 684 u.u_dent.d_ino = ip->i_number; 685 u.u_error = rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent, 686 (int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0); 687 iput(dp); 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 struct buf * 697 blkatoff(ip, offset, res) 698 struct inode *ip; 699 off_t offset; 700 char **res; 701 { 702 register struct fs *fs = ip->i_fs; 703 daddr_t lbn = lblkno(fs, offset); 704 int base = blkoff(fs, offset); 705 int bsize = blksize(fs, ip, lbn); 706 daddr_t bn = fsbtodb(fs, bmap(ip, lbn, B_WRITE, base, bsize)); 707 register struct buf *bp; 708 709 if (u.u_error) 710 return (0); 711 bp = bread(ip->i_dev, bn, bsize); 712 if (bp->b_flags & B_ERROR) { 713 brelse(bp); 714 return (0); 715 } 716 if (res) 717 *res = bp->b_un.b_addr + base; 718 return (bp); 719 } 720 721 /* 722 * Check if a directory is empty or not. 723 * Inode supplied must be locked. 724 */ 725 dirempty(ip) 726 struct inode *ip; 727 { 728 register off_t off; 729 struct direct dbuf; 730 register struct direct *dp = &dbuf; 731 int error; 732 733 for (off = 0; off < ip->i_size; off += dp->d_reclen) { 734 error = rdwri(UIO_READ, ip, (caddr_t)dp, 735 sizeof (struct direct), off, 1, (int *)0); 736 if (error) 737 return (0); 738 if (dp->d_ino == 0) 739 continue; 740 if (dp->d_name[0] != '.') 741 return (0); 742 if (dp->d_namlen == 1 || 743 (dp->d_namlen == 2 && dp->d_name[1] == '.')) 744 continue; 745 return (0); 746 } 747 return (1); 748 } 749