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