1 /* ufs_lookup.c 4.22 82/08/14 */ 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 13 struct buf *blkatoff(); 14 int dirchk = 1; 15 /* 16 * Convert a pathname into a pointer to a locked inode, 17 * with side effects usable in creating and removing files. 18 * This is a very central and rather complicated routine. 19 * 20 * The func argument gives the routine which returns successive 21 * characters of the name to be translated. The flag 22 * argument is (0, 1, 2) depending on whether the name is to be 23 * (looked up, created, deleted). The follow argument is 1 when 24 * symbolic links are to be followed when they occur at the end of 25 * the name translation process. 26 * 27 * Overall outline: 28 * 29 * copy in name 30 * get starting directory 31 * dirloop: 32 * check accessibility of directory 33 * dirloop2: 34 * copy next component of name to u.u_dent 35 * handle degenerate case where name is null string 36 * search for name in directory, to found or notfound 37 * notfound: 38 * if creating, return locked inode, leaving information on avail. slots 39 * else return error 40 * found: 41 * if at end of path and deleting, return information to allow delete 42 * if .. and on mounted filesys, look in mount table for parent 43 * if symbolic link, massage name in buffer and continue at dirloop 44 * if more components of name, do next level at dirloop 45 * return the answer as locked inode 46 */ 47 struct inode * 48 namei(func, flag, follow) 49 int (*func)(), flag, follow; 50 { 51 register char *cp; /* pointer into pathname argument */ 52 /* these variables refer to things which must be freed or unlocked */ 53 register struct inode *dp = 0; /* the directory we are searching */ 54 register struct fs *fs; /* file system that directory is in */ 55 register struct buf *bp = 0; /* a buffer of directory entries */ 56 register struct direct *ep; /* the current directory entry */ 57 int entryoffsetinblock; /* offset of ep in bp's buffer */ 58 register struct buf *nbp; /* buffer storing path name argument */ 59 /* these variables hold information about the search for a slot */ 60 enum {NONE, COMPACT, FOUND} slotstatus; 61 int slotoffset = -1; /* offset of area with free space */ 62 int slotsize; /* size of area at slotoffset */ 63 int slotfreespace; /* amount of space free in slot */ 64 int slotneeded; /* size of the entry we're seeking */ 65 /* */ 66 int dirsize; 67 int prevoff; /* u.u_offset of previous entry */ 68 int nlink = 0; /* number of symbolic links taken */ 69 struct inode *pdp; /* saved dp during symlink work */ 70 int i; 71 72 /* 73 * Get a buffer for the name to be translated, and copy the 74 * name into the buffer. 75 */ 76 nbp = geteblk(MAXPATHLEN); 77 for (cp = nbp->b_un.b_addr; *cp = (*func)(); ) { 78 if ((*cp&0377) == ('/'|0200) || (*cp&0200) && flag != 2) { 79 u.u_error = EPERM; 80 goto bad; 81 } 82 cp++; 83 if (cp >= nbp->b_un.b_addr + MAXPATHLEN) { 84 u.u_error = ENOENT; 85 goto bad; 86 } 87 } 88 if (u.u_error) 89 goto bad; 90 91 /* 92 * Get starting directory. 93 */ 94 cp = nbp->b_un.b_addr; 95 if (*cp == '/') { 96 while (*cp == '/') 97 cp++; 98 if ((dp = u.u_rdir) == NULL) 99 dp = rootdir; 100 } else 101 dp = u.u_cdir; 102 fs = dp->i_fs; 103 ilock(dp); 104 dp->i_count++; 105 u.u_pdir = (struct inode *)0xc0000000; /* illegal */ 106 107 /* 108 * We come to dirloop to search a new directory. 109 * The directory must be locked so that it can be 110 * iput, and fs must be already set to dp->i_fs. 111 */ 112 dirloop: 113 /* 114 * Check accessiblity of directory. 115 */ 116 if ((dp->i_mode&IFMT) != IFDIR) { 117 u.u_error = ENOTDIR; 118 goto bad; 119 } 120 if (access(dp, IEXEC)) 121 goto bad; 122 123 dirloop2: 124 /* 125 * Copy next component of name to u.u_dent. 126 */ 127 for (i = 0; *cp != 0 && *cp != '/'; cp++) { 128 if (i >= MAXNAMLEN) { 129 u.u_error = ENOENT; 130 goto bad; 131 } 132 u.u_dent.d_name[i++] = *cp; 133 } 134 u.u_dent.d_namlen = i; 135 u.u_dent.d_name[i] = 0; 136 137 /* 138 * Check for degenerate name (e.g. / or "") 139 * which is a way of talking about a directory, 140 * e.g. like "/." or ".". 141 */ 142 if (u.u_dent.d_name[0] == 0) { 143 if (flag) { 144 u.u_error = ENOENT; 145 goto bad; 146 } 147 brelse(nbp); 148 return (dp); 149 } 150 151 /* 152 * Suppress search for slots unless creating 153 * file and at end of pathname, in which case 154 * we watch for a place to put the new file in 155 * case it doesn't already exist. 156 */ 157 slotstatus = FOUND; 158 if (flag == 1 && *cp == 0) { 159 slotstatus = NONE; 160 slotfreespace = 0; 161 slotneeded = DIRSIZ(&u.u_dent); 162 } 163 164 dirsize = roundup(dp->i_size, DIRBLKSIZ); 165 u.u_offset = 0; 166 while (u.u_offset < dirsize) { 167 /* 168 * If offset is on a block boundary, 169 * read the next directory block. 170 * Release previous if it exists. 171 */ 172 if (blkoff(fs, u.u_offset) == 0) { 173 if (bp != NULL) 174 brelse(bp); 175 bp = blkatoff(dp, u.u_offset, (char **)0); 176 if (bp == 0) 177 goto bad; 178 entryoffsetinblock = 0; 179 } 180 181 /* 182 * If still looking for a slot, and at a DIRBLKSIZE 183 * boundary, have to start looking for free space 184 * again. 185 */ 186 if (slotstatus == NONE && 187 (entryoffsetinblock&(DIRBLKSIZ-1)) == 0) { 188 slotoffset = -1; 189 slotfreespace = 0; 190 } 191 192 /* 193 * Get pointer to next entry, and do consistency checking: 194 * record length must be multiple of 4 195 * record length must not be zero 196 * entry must fit in rest of this DIRBLKSIZ block 197 * record must be large enough to contain name 198 * When dirchk is set we also check: 199 * name is not longer than MAXNAMLEN 200 * name must be as long as advertised, and null terminated 201 * Checking last two conditions is done only when dirchk is 202 * set, to save time. 203 */ 204 ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock); 205 i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)); 206 if ((ep->d_reclen & 0x3) || ep->d_reclen == 0 || 207 ep->d_reclen > i || DIRSIZ(ep) > ep->d_reclen || 208 dirchk && (ep->d_namlen > MAXNAMLEN || dirbadname(ep))) { 209 dirbad(dp, "mangled entry"); 210 u.u_offset += i; 211 entryoffsetinblock += i; 212 continue; 213 } 214 215 /* 216 * If an appropriate sized slot has not yet been found, 217 * check to see if one is available. Also accumulate space 218 * in the current block so that we can determine if 219 * compaction is viable. 220 */ 221 if (slotstatus != FOUND) { 222 int size = ep->d_reclen; 223 224 if (ep->d_ino != 0) 225 size -= DIRSIZ(ep); 226 if (size > 0) { 227 if (size >= slotneeded) { 228 slotstatus = FOUND; 229 slotoffset = u.u_offset; 230 slotsize = ep->d_reclen; 231 } else if (slotstatus == NONE) { 232 slotfreespace += size; 233 if (slotoffset == -1) 234 slotoffset = u.u_offset; 235 if (slotfreespace >= slotneeded) { 236 slotstatus = COMPACT; 237 slotsize = 238 u.u_offset+ep->d_reclen - 239 slotoffset; 240 } 241 } 242 } 243 } 244 245 /* 246 * Check for a name match. 247 */ 248 if (ep->d_ino) { 249 if (ep->d_namlen == u.u_dent.d_namlen && 250 !bcmp(u.u_dent.d_name, ep->d_name, ep->d_namlen)) 251 goto found; 252 } 253 prevoff = u.u_offset; 254 u.u_offset += ep->d_reclen; 255 entryoffsetinblock += ep->d_reclen; 256 } 257 /* notfound: */ 258 /* 259 * If creating, and at end of pathname and current 260 * directory has not been removed, then can consider allowing 261 * file to be created. 262 */ 263 if (flag == 1 && *cp == 0 && dp->i_nlink != 0) { 264 /* 265 * Access for write is interpreted as allowing 266 * creation of files in the directory. 267 */ 268 if (access(dp, IWRITE)) 269 goto bad; 270 /* 271 * Return an indication of where the new directory 272 * entry should be put. If we didn't find a slot, 273 * then set u.u_count to 0 indicating that the 274 * new slot belongs at the end of the directory. 275 * If we found a slot, then the new entry can be 276 * put in the range [u.u_offset..u.u_offset+u.u_count) 277 */ 278 if (slotstatus == NONE) 279 u.u_count = 0; 280 else { 281 u.u_offset = slotoffset; 282 u.u_count = slotsize; 283 } 284 dp->i_flag |= IUPD|ICHG; 285 if (bp) 286 brelse(bp); 287 brelse(nbp); 288 /* 289 * We return with the directory locked, so that 290 * the parameters we set up above will still be 291 * valid if we actually decide to do a direnter(). 292 * We return NULL to indicate that the entry doesn't 293 * currently exist, leaving a pointer to the (locked) 294 * directory inode in u.u_pdir. 295 */ 296 u.u_pdir = dp; 297 return (NULL); 298 } 299 u.u_error = ENOENT; 300 goto bad; 301 found: 302 /* 303 * Check that directory length properly reflects presence 304 * of this entry. 305 */ 306 if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) { 307 dirbad(dp, "i_size too small"); 308 dp->i_size = entryoffsetinblock + DIRSIZ(ep); 309 dp->i_flag |= IUPD|ICHG; 310 } 311 312 /* 313 * Found component in pathname; save directory 314 * entry in u.u_dent, and release directory buffer. 315 */ 316 bcopy((caddr_t)ep, (caddr_t)&u.u_dent, DIRSIZ(ep)); 317 brelse(bp); 318 bp = NULL; 319 320 /* 321 * If deleting, and at end of pathname, return 322 * parameters which can be used to remove file. 323 * Note that in this case we return the directory 324 * inode, not the inode of the file being deleted. 325 */ 326 if (flag == 2 && *cp == 0) { 327 /* 328 * Write access to directory required to delete files. 329 */ 330 if (access(dp, IWRITE)) 331 goto bad; 332 /* 333 * Return pointer to current entry in u.u_offset, 334 * and distance past previous entry (if there 335 * is a previous entry in this block) in u.u_count. 336 * Save directory inode pointer in u.u_pdir for dirremove(). 337 */ 338 if ((u.u_offset&(DIRBLKSIZ-1)) == 0) 339 u.u_count = 0; 340 else 341 u.u_count = u.u_offset - prevoff; 342 brelse(nbp); 343 u.u_pdir = dp; /* for dirremove() */ 344 return (dp); 345 } 346 347 /* 348 * Special handling for ".." allowing chdir out of mounted 349 * file system: indirect .. in root inode to reevaluate 350 * in directory file system was mounted on. 351 */ 352 if (u.u_dent.d_name[0] == '.' && u.u_dent.d_name[1] == '.' && 353 u.u_dent.d_name[2] == '\0') { 354 if (dp == u.u_rdir) 355 u.u_dent.d_ino = dp->i_number; 356 else if (u.u_dent.d_ino == ROOTINO && 357 dp->i_number == ROOTINO) { 358 for (i = 1; i < NMOUNT; i++) 359 if (mount[i].m_bufp != NULL && 360 mount[i].m_dev == dp->i_dev) { 361 iput(dp); 362 dp = mount[i].m_inodp; 363 ilock(dp); 364 dp->i_count++; 365 fs = dp->i_fs; 366 cp -= 2; /* back over .. */ 367 goto dirloop2; 368 } 369 } 370 } 371 372 /* 373 * Check for symbolic link, which may require us 374 * to massage the name before we continue translation. 375 * To avoid deadlock have to unlock the current directory, 376 * but don't iput it because we may need it again (if 377 * the symbolic link is relative to .). Instead save 378 * it (unlocked) as pdp. 379 */ 380 pdp = dp; 381 iunlock(pdp); 382 dp = iget(dp->i_dev, fs, u.u_dent.d_ino); 383 if (dp == NULL) 384 goto bad2; 385 fs = dp->i_fs; 386 387 /* 388 * Check for symbolic link 389 */ 390 if ((dp->i_mode & IFMT) == IFLNK && (follow || *cp == '/')) { 391 int pathlen = strlen(cp) + 1; 392 int bn; 393 394 if (dp->i_size + pathlen >= MAXPATHLEN - 1 || 395 ++nlink > MAXSYMLINKS) { 396 u.u_error = ELOOP; 397 goto bad2; 398 } 399 bcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen); 400 u.u_error = 401 readip1(dp, nbp->b_un.b_addr, dp->i_size, 0, 1, 0); 402 if (u.u_error) 403 goto bad2; 404 cp = nbp->b_un.b_addr; 405 iput(dp); 406 if (*cp == '/') { 407 irele(pdp); 408 while (*cp == '/') 409 cp++; 410 if ((dp = u.u_rdir) == NULL) 411 dp = rootdir; 412 ilock(dp); 413 dp->i_count++; 414 } else { 415 dp = pdp; 416 ilock(dp); 417 } 418 fs = dp->i_fs; 419 goto dirloop; 420 } 421 irele(pdp); 422 423 /* 424 * Not a symbolic link. If more pathname, 425 * continue at next component, else return. 426 */ 427 if (*cp == '/') { 428 while (*cp == '/') 429 cp++; 430 goto dirloop; 431 } 432 brelse(nbp); 433 return (dp); 434 bad2: 435 irele(pdp); 436 bad: 437 if (bp) 438 brelse(bp); 439 if (dp) 440 iput(dp); 441 brelse(nbp); 442 return (NULL); 443 } 444 445 dirbad(ip, how) 446 struct inode *ip; 447 char *how; 448 { 449 450 printf("%s: bad dir ino %d at offset %d: %s\n", 451 ip->i_fs->fs_fsmnt, ip->i_number, u.u_offset, how); 452 } 453 454 dirbadname(ep) 455 register struct direct *ep; 456 { 457 register char *cp; 458 register int i; 459 460 for (i = 0; i < ep->d_namlen; i++) 461 if (ep->d_name[i] == 0) 462 return (1); 463 return (ep->d_name[i]); 464 } 465 466 /* 467 * Write a directory entry after a call to namei, using the parameters 468 * which it left in the u. area. The argument ip is the inode which 469 * the new directory entry will refer to. The u. area field u.u_pdir is 470 * a pointer to the directory to be written, which was left locked by 471 * namei. Remaining parameters (u.u_offset, u.u_count) indicate 472 * how the space for the new entry is to be gotten. 473 */ 474 direnter(ip) 475 struct inode *ip; 476 { 477 register struct direct *ep, *nep; 478 struct fs *fs; 479 struct buf *bp; 480 int loc, dsize, freespace, newentrysize; 481 char *dirbuf; 482 483 u.u_dent.d_ino = ip->i_number; 484 u.u_segflg = 1; 485 newentrysize = DIRSIZ(&u.u_dent); 486 if (u.u_count == 0) { 487 /* 488 * If u.u_count is 0, then namei could find no space in the 489 * directory. In this case u.u_offset will be on a directory 490 * block boundary and we will write the new entry into a fresh 491 * block. 492 */ 493 if (u.u_offset&(DIRBLKSIZ-1)) 494 panic("wdir: newblk"); 495 u.u_dent.d_reclen = DIRBLKSIZ; 496 u.u_count = newentrysize; 497 u.u_base = (caddr_t)&u.u_dent; 498 u.u_segflg = 1; 499 writei(u.u_pdir); 500 iput(u.u_pdir); 501 return; 502 } 503 504 /* 505 * If u.u_count is non-zero, then namei found space for the 506 * new entry in the range u.u_offset to u.u_offset+u.u_count. 507 * in the directory. To use this space, we may have to compact 508 * the entries located there, by copying them together towards 509 * the beginning of the block, leaving the free space in 510 * one usable chunk at the end. 511 */ 512 513 /* 514 * Increase size of directory if entry eats into new space. 515 * This should never push the size past a new multiple of 516 * DIRBLKSIZE. 517 */ 518 if (u.u_offset+u.u_count > u.u_pdir->i_size) { 519 /*ZZ*/ if (((u.u_offset+u.u_count-1)&~(DIRBLKSIZ-1)) != 520 /*ZZ*/ ((u.u_pdir->i_size-1)&~(DIRBLKSIZ-1))) { 521 /*ZZ*/ panic("wdir: span"); 522 /*ZZ*/ } 523 u.u_pdir->i_size = u.u_offset + u.u_count; 524 } 525 526 /* 527 * Get the block containing the space for the new directory 528 * entry. 529 */ 530 bp = blkatoff(u.u_pdir, u.u_offset, (char **)&dirbuf); 531 if (bp == 0) 532 return; 533 534 /* 535 * Find space for the new entry. In the simple case, the 536 * entry at offset base will have the space. If it does 537 * not, then namei arranged that compacting the region 538 * u.u_offset to u.u_offset+u.u_count would yield the space. 539 */ 540 ep = (struct direct *)dirbuf; 541 dsize = DIRSIZ(ep); 542 freespace = ep->d_reclen - dsize; 543 for (loc = ep->d_reclen; loc < u.u_count; ) { 544 nep = (struct direct *)(dirbuf + loc); 545 if (ep->d_ino) { 546 /* trim the existing slot */ 547 ep->d_reclen = dsize; 548 ep = (struct direct *)((char *)ep + dsize); 549 } else { 550 /* overwrite; nothing there; header is ours */ 551 freespace += dsize; 552 } 553 dsize = DIRSIZ(nep); 554 freespace += nep->d_reclen - dsize; 555 loc += nep->d_reclen; 556 /*ZZ*/if((loc&~0x1ff)!=(loc+nep->d_reclen-1&~0x1ff)) 557 /*ZZ*/printf("wdir: compact loc %d reclen %d (dir %s/%d)\n",loc,nep->d_reclen, 558 /*ZZ*/u.u_pdir->i_fs->fs_fsmnt,u.u_pdir->i_number); 559 bcopy(nep, ep, dsize); 560 } 561 /* 562 * Update the pointer fields in the previous entry (if any), 563 * copy in the new entry, and write out the block. 564 */ 565 if (ep->d_ino == 0) { 566 if (freespace + dsize < newentrysize) 567 panic("wdir: compact1"); 568 /*ZZ*/if(freespace+dsize>512)panic("wdir: compact screwup"); 569 u.u_dent.d_reclen = freespace + dsize; 570 } else { 571 if (freespace < newentrysize) 572 panic("wdir: compact2"); 573 u.u_dent.d_reclen = freespace; 574 /*ZZ*/if ((((char *)ep-bp->b_un.b_addr)&0x1ff)+dsize>512) panic("wdir: reclen"); 575 ep->d_reclen = dsize; 576 ep = (struct direct *)((char *)ep + dsize); 577 } 578 /*ZZ*/if((((char*)ep-bp->b_un.b_addr)&0x1ff)+u.u_dent.d_reclen>512)panic("wdir: botch"); 579 bcopy(&u.u_dent, ep, newentrysize); 580 bwrite(bp); 581 u.u_pdir->i_flag |= IUPD|ICHG; 582 iput(u.u_pdir); 583 } 584 585 dirremove() 586 { 587 register struct inode *dp = u.u_pdir; 588 register struct fs *fs = dp->i_fs; 589 register struct buf *bp; 590 struct direct *ep; 591 592 if (u.u_count == 0) { 593 /* 594 * First entry in block: set d_ino to zero. 595 */ 596 /*ZZ*/if(u.u_offset&0x1ff)printf("missed dir compact dir %s/%d off %d file %s\n" 597 /*ZZ*/,dp->i_fs->fs_fsmnt,dp->i_number,u.u_offset,u.u_dent.d_name); 598 u.u_base = (caddr_t)&u.u_dent; 599 u.u_count = DIRSIZ(&u.u_dent); 600 u.u_segflg = 1; 601 u.u_dent.d_ino = 0; 602 writei(dp); 603 } else { 604 /* 605 * Collapse new free space into previous entry. 606 */ 607 bp = blkatoff(dp, u.u_offset - u.u_count, (char **)&ep); 608 if (bp == 0) 609 return (0); 610 ep->d_reclen += u.u_dent.d_reclen; 611 /*ZZ*/if((((char *)ep - bp->b_un.b_addr)&0x1ff)+u.u_dent.d_reclen > 512) 612 /*ZZ*/ panic("unlink: reclen"); 613 bwrite(bp); 614 dp->i_flag |= IUPD|ICHG; 615 } 616 return (1); 617 } 618 619 /* 620 * Return buffer with contents of block "offset" 621 * from the beginning of directory "ip". If "res" 622 * is non-zero, fill it in with a pointer to the 623 * remaining space in the directory. 624 */ 625 struct buf * 626 blkatoff(ip, offset, res) 627 struct inode *ip; 628 off_t offset; 629 char **res; 630 { 631 register struct fs *fs = ip->i_fs; 632 int lbn = lblkno(fs, offset); 633 int base = blkoff(fs, offset); 634 int bsize = blksize(fs, ip, lbn); 635 int bn = fsbtodb(fs, bmap(ip, lbn, B_WRITE, base, bsize)); 636 register struct buf *bp; 637 638 if (u.u_error) 639 return (0); 640 bp = bread(ip->i_dev, bn, bsize); 641 if (bp->b_flags & B_ERROR) { 642 brelse(bp); 643 return (0); 644 } 645 if (res) 646 *res = bp->b_un.b_addr + base; 647 return (bp); 648 } 649