1 /* $OpenBSD: ext2fs_lookup.c,v 1.48 2024/09/20 02:00:46 jsg Exp $ */ 2 /* $NetBSD: ext2fs_lookup.c,v 1.16 2000/08/03 20:29:26 thorpej Exp $ */ 3 4 /* 5 * Modified for NetBSD 1.2E 6 * May 1997, Manuel Bouyer 7 * Laboratoire d'informatique de Paris VI 8 */ 9 /* 10 * modified for Lites 1.1 11 * 12 * Aug 1995, Godmar Back (gback@cs.utah.edu) 13 * University of Utah, Department of Computer Science 14 */ 15 /* 16 * Copyright (c) 1989, 1993 17 * The Regents of the University of California. All rights reserved. 18 * (c) UNIX System Laboratories, Inc. 19 * All or some portions of this file are derived from material licensed 20 * to the University of California by American Telephone and Telegraph 21 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 22 * the permission of UNIX System Laboratories, Inc. 23 * 24 * Redistribution and use in source and binary forms, with or without 25 * modification, are permitted provided that the following conditions 26 * are met: 27 * 1. Redistributions of source code must retain the above copyright 28 * notice, this list of conditions and the following disclaimer. 29 * 2. Redistributions in binary form must reproduce the above copyright 30 * notice, this list of conditions and the following disclaimer in the 31 * documentation and/or other materials provided with the distribution. 32 * 3. Neither the name of the University nor the names of its contributors 33 * may be used to endorse or promote products derived from this software 34 * without specific prior written permission. 35 * 36 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 37 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 38 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 39 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 40 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 41 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 42 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 43 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 44 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 45 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 46 * SUCH DAMAGE. 47 * 48 * @(#)ufs_lookup.c 8.6 (Berkeley) 4/1/94 49 */ 50 51 #include <sys/param.h> 52 #include <sys/systm.h> 53 #include <sys/namei.h> 54 #include <sys/buf.h> 55 #include <sys/mount.h> 56 #include <sys/vnode.h> 57 #include <sys/malloc.h> 58 #include <sys/dirent.h> 59 60 #include <ufs/ufs/quota.h> 61 #include <ufs/ufs/inode.h> 62 #include <ufs/ufs/ufsmount.h> 63 #include <ufs/ufs/ufs_extern.h> 64 65 #include <ufs/ext2fs/ext2fs_extern.h> 66 #include <ufs/ext2fs/ext2fs_dir.h> 67 #include <ufs/ext2fs/ext2fs.h> 68 69 extern int dirchk; 70 71 static void ext2fs_dirconv2ffs(struct ext2fs_direct *e2dir, 72 struct dirent *ffsdir); 73 static int ext2fs_dirbadentry(struct vnode *dp, struct ext2fs_direct *de, 74 int entryoffsetinblock); 75 static int ext2fs_search_dirblock(struct inode *, void *, int *, 76 struct componentname *, int *, doff_t *, doff_t *, 77 struct ext2fs_searchslot *); 78 79 /* 80 * the problem that is tackled below is the fact that FFS 81 * includes the terminating zero on disk while EXT2FS doesn't 82 * this implies that we need to introduce some padding. 83 * For instance, a filename "sbin" has normally a reclen 12 84 * in EXT2, but 16 in FFS. 85 * This reminds me of that Pepsi commercial: 'Kid saved a lousy nine cents...' 86 * If it wasn't for that, the complete ufs code for directories would 87 * have worked w/o changes (except for the difference in DIRBLKSIZ) 88 */ 89 static void 90 ext2fs_dirconv2ffs(struct ext2fs_direct *e2dir, struct dirent *ffsdir) 91 { 92 memset(ffsdir, 0, sizeof(struct dirent)); 93 ffsdir->d_fileno = letoh32(e2dir->e2d_ino); 94 ffsdir->d_namlen = e2dir->e2d_namlen; 95 96 ffsdir->d_type = DT_UNKNOWN; /* don't know more here */ 97 #ifdef DIAGNOSTIC 98 /* 99 * XXX Right now this can't happen, but if one day 100 * MAXNAMLEN != E2FS_MAXNAMLEN we should handle this more gracefully ! 101 */ 102 /* XXX: e2d_namlen is to small for such comparison 103 if (e2dir->e2d_namlen > MAXNAMLEN) 104 panic("ext2fs: e2dir->e2d_namlen"); 105 */ 106 #endif 107 strncpy(ffsdir->d_name, e2dir->e2d_name, ffsdir->d_namlen); 108 109 /* Godmar thinks: since e2dir->e2d_reclen can be big and means 110 nothing anyway, we compute our own reclen according to what 111 we think is right 112 */ 113 ffsdir->d_reclen = DIRENT_SIZE(ffsdir); 114 } 115 116 /* 117 * Vnode op for reading directories. 118 * 119 * Convert the on-disk entries to <sys/dirent.h> entries. 120 * the problem is that the conversion will blow up some entries by four bytes, 121 * so it can't be done in place. This is too bad. Right now the conversion is 122 * done entry by entry, the converted entry is sent via uiomove. 123 * 124 * XXX allocate a buffer, convert as many entries as possible, then send 125 * the whole buffer to uiomove 126 */ 127 int 128 ext2fs_readdir(void *v) 129 { 130 struct vop_readdir_args *ap = v; 131 struct uio *uio = ap->a_uio; 132 int error; 133 size_t e2fs_count, readcnt, entries; 134 struct vnode *vp = ap->a_vp; 135 struct m_ext2fs *fs = VTOI(vp)->i_e2fs; 136 137 struct ext2fs_direct *dp; 138 struct dirent dstd; 139 struct uio auio; 140 struct iovec aiov; 141 caddr_t dirbuf; 142 off_t off = uio->uio_offset; 143 int e2d_reclen; 144 145 if (vp->v_type != VDIR) 146 return (ENOTDIR); 147 148 e2fs_count = uio->uio_resid; 149 entries = (uio->uio_offset + e2fs_count) & (fs->e2fs_bsize - 1); 150 151 /* Make sure we don't return partial entries. */ 152 if (e2fs_count <= entries) 153 return (EINVAL); 154 155 e2fs_count -= entries; 156 auio = *uio; 157 auio.uio_iov = &aiov; 158 auio.uio_iovcnt = 1; 159 auio.uio_segflg = UIO_SYSSPACE; 160 aiov.iov_len = e2fs_count; 161 auio.uio_resid = e2fs_count; 162 dirbuf = malloc(e2fs_count, M_TEMP, M_WAITOK | M_ZERO); 163 aiov.iov_base = dirbuf; 164 165 error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred); 166 if (error == 0) { 167 readcnt = e2fs_count - auio.uio_resid; 168 dp = (struct ext2fs_direct *) dirbuf; 169 while ((char *) dp < (char *) dirbuf + readcnt) { 170 e2d_reclen = letoh16(dp->e2d_reclen); 171 if (e2d_reclen == 0) { 172 error = EIO; 173 break; 174 } 175 ext2fs_dirconv2ffs(dp, &dstd); 176 if (memchr(dstd.d_name, '/', dstd.d_namlen) != NULL) { 177 error = EINVAL; 178 break; 179 } 180 if (dstd.d_reclen > uio->uio_resid) { 181 break; 182 } 183 dstd.d_off = off + e2d_reclen; 184 if ((error = uiomove((caddr_t)&dstd, dstd.d_reclen, uio)) != 0) { 185 break; 186 } 187 off = off + e2d_reclen; 188 /* advance dp */ 189 dp = (struct ext2fs_direct *) ((char *)dp + e2d_reclen); 190 } 191 /* we need to correct uio_offset */ 192 uio->uio_offset = off; 193 } 194 free(dirbuf, M_TEMP, e2fs_count); 195 *ap->a_eofflag = ext2fs_size(VTOI(ap->a_vp)) <= uio->uio_offset; 196 return (error); 197 } 198 199 /* 200 * Convert a component of a pathname into a pointer to a locked inode. 201 * This is a very central and rather complicated routine. 202 * If the file system is not maintained in a strict tree hierarchy, 203 * this can result in a deadlock situation (see comments in code below). 204 * 205 * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending 206 * on whether the name is to be looked up, created, renamed, or deleted. 207 * When CREATE, RENAME, or DELETE is specified, information usable in 208 * creating, renaming, or deleting a directory entry may be calculated. 209 * If flag has LOCKPARENT or'ed into it and the target of the pathname 210 * exists, lookup returns both the target and its parent directory locked. 211 * When creating or renaming and LOCKPARENT is specified, the target may 212 * not be ".". When deleting and LOCKPARENT is specified, the target may 213 * be "."., but the caller must check to ensure it does an vrele and vput 214 * instead of two vputs. 215 * 216 * Overall outline of ext2fs_lookup: 217 * 218 * check accessibility of directory 219 * look for name in cache, if found, then if at end of path 220 * and deleting or creating, drop it, else return name 221 * search for name in directory, to found or notfound 222 * notfound: 223 * if creating, return locked directory, leaving info on available slots 224 * else return error 225 * found: 226 * if at end of path and deleting, return information to allow delete 227 * if at end of path and rewriting (RENAME and LOCKPARENT), lock target 228 * inode and return info to allow rewrite 229 * if not at end, add name to cache; if at end and neither creating 230 * nor deleting, add name to cache 231 */ 232 int 233 ext2fs_lookup(void *v) 234 { 235 struct vop_lookup_args *ap = v; 236 struct vnode *vdp; /* vnode for directory being searched */ 237 struct inode *dp; /* inode for directory being searched */ 238 struct buf *bp; /* a buffer of directory entries */ 239 struct ext2fs_direct *ep; /* the current directory entry */ 240 int entryoffsetinblock; /* offset of ep in bp's buffer */ 241 struct ext2fs_searchslot ss; 242 int numdirpasses; /* strategy for directory search */ 243 doff_t endsearch; /* offset to end directory search */ 244 doff_t prevoff; /* prev entry dp->i_offset */ 245 struct vnode *pdp; /* saved dp during symlink work */ 246 struct vnode *tdp; /* returned by VFS_VGET */ 247 doff_t enduseful; /* pointer past last used dir slot */ 248 u_long bmask; /* block offset mask */ 249 int lockparent; /* 1 => lockparent flag is set */ 250 int wantparent; /* 1 => wantparent or lockparent flag */ 251 struct vnode **vpp = ap->a_vpp; 252 struct componentname *cnp = ap->a_cnp; 253 struct ucred *cred = cnp->cn_cred; 254 int flags = cnp->cn_flags; 255 int nameiop = cnp->cn_nameiop; 256 int dirblksize, entry_found = 0, error; 257 258 ss.slotstatus = FOUND; 259 ss.slotoffset = -1; 260 ss.slotfreespace = ss.slotsize = ss.slotneeded = 0; 261 262 bp = NULL; 263 *vpp = NULL; 264 vdp = ap->a_dvp; 265 dp = VTOI(vdp); 266 dirblksize = dp->i_e2fs->e2fs_bsize; 267 lockparent = flags & LOCKPARENT; 268 wantparent = flags & (LOCKPARENT|WANTPARENT); 269 270 /* 271 * Check accessibility of directory. 272 */ 273 if ((error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc)) != 0) 274 return (error); 275 276 if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) && 277 (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) 278 return (EROFS); 279 280 /* 281 * We now have a segment name to search for, and a directory to search. 282 * 283 * Before tediously performing a linear scan of the directory, 284 * check the name cache to see if the directory/name pair 285 * we are looking for is known already. 286 */ 287 if ((error = cache_lookup(vdp, vpp, cnp)) >= 0) 288 return (error); 289 290 /* 291 * Suppress search for slots unless creating 292 * file and at end of pathname, in which case 293 * we watch for a place to put the new file in 294 * case it doesn't already exist. 295 */ 296 if ((nameiop == CREATE || nameiop == RENAME) && (flags & ISLASTCN)) { 297 ss.slotstatus = NONE; 298 ss.slotneeded = EXT2FS_DIRSIZ(cnp->cn_namelen); 299 } 300 301 /* 302 * If there is cached information on a previous search of 303 * this directory, pick up where we last left off. 304 * We cache only lookups as these are the most common 305 * and have the greatest payoff. Caching CREATE has little 306 * benefit as it usually must search the entire directory 307 * to determine that the entry does not exist. Caching the 308 * location of the last DELETE or RENAME has not reduced 309 * profiling time and hence has been removed in the interest 310 * of simplicity. 311 */ 312 bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1; 313 if (nameiop != LOOKUP || dp->i_diroff == 0 || 314 dp->i_diroff > ext2fs_size(dp)) { 315 entryoffsetinblock = 0; 316 dp->i_offset = 0; 317 numdirpasses = 1; 318 } else { 319 dp->i_offset = dp->i_diroff; 320 if ((entryoffsetinblock = dp->i_offset & bmask) && 321 (error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, 322 NULL, &bp))) 323 return (error); 324 numdirpasses = 2; 325 } 326 prevoff = dp->i_offset; 327 endsearch = roundup(ext2fs_size(dp), dirblksize); 328 enduseful = 0; 329 330 searchloop: 331 while (dp->i_offset < endsearch) { 332 /* 333 * If necessary, get the next directory block. 334 */ 335 if (bp != NULL) 336 brelse(bp); 337 338 error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, NULL, &bp); 339 if (error != 0) 340 return (error); 341 entryoffsetinblock = 0; 342 343 /* 344 * If still looking for a slot, and at a dirblksize 345 * boundary, have to start looking for free space again. 346 */ 347 if (ss.slotstatus == NONE) { 348 ss.slotoffset = -1; 349 ss.slotfreespace = 0; 350 } 351 352 error = ext2fs_search_dirblock(dp, bp->b_data, &entry_found, 353 cnp, &entryoffsetinblock, &prevoff, &enduseful, &ss); 354 if (error) { 355 brelse(bp); 356 return (error); 357 } 358 if (entry_found) { 359 ep = (struct ext2fs_direct *) 360 ((char *)bp->b_data + (entryoffsetinblock & bmask)); 361 /* foundentry: */ 362 dp->i_ino = letoh32(ep->e2d_ino); 363 dp->i_reclen = letoh16(ep->e2d_reclen); 364 goto found; 365 } 366 } 367 /* notfound: */ 368 /* 369 * If we started in the middle of the directory and failed 370 * to find our target, we must check the beginning as well. 371 */ 372 if (numdirpasses == 2) { 373 numdirpasses--; 374 dp->i_offset = 0; 375 endsearch = dp->i_diroff; 376 goto searchloop; 377 } 378 if (bp != NULL) 379 brelse(bp); 380 /* 381 * If creating, and at end of pathname and current 382 * directory has not been removed, then can consider 383 * allowing file to be created. 384 */ 385 if ((nameiop == CREATE || nameiop == RENAME) && 386 (flags & ISLASTCN) && dp->i_e2fs_nlink != 0) { 387 /* 388 * Creation of files on a read-only mounted file system 389 * is pointless, so don't proceed any further. 390 */ 391 if (vdp->v_mount->mnt_flag & MNT_RDONLY) 392 return (EROFS); 393 /* 394 * Access for write is interpreted as allowing 395 * creation of files in the directory. 396 */ 397 if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0) 398 return (error); 399 /* 400 * Return an indication of where the new directory 401 * entry should be put. If we didn't find a slot, 402 * then set dp->i_count to 0 indicating 403 * that the new slot belongs at the end of the 404 * directory. If we found a slot, then the new entry 405 * can be put in the range from dp->i_offset to 406 * dp->i_offset + dp->i_count. 407 */ 408 if (ss.slotstatus == NONE) { 409 dp->i_offset = roundup(ext2fs_size(dp), dirblksize); 410 dp->i_count = 0; 411 enduseful = dp->i_offset; 412 } else { 413 dp->i_offset = ss.slotoffset; 414 dp->i_count = ss.slotsize; 415 if (enduseful < ss.slotoffset + ss.slotsize) 416 enduseful = ss.slotoffset + ss.slotsize; 417 } 418 dp->i_endoff = roundup(enduseful, dirblksize); 419 dp->i_flag |= IN_CHANGE | IN_UPDATE; 420 /* 421 * We return with the directory locked, so that 422 * the parameters we set up above will still be 423 * valid if we actually decide to do a direnter(). 424 * We return ni_vp == NULL to indicate that the entry 425 * does not currently exist; we leave a pointer to 426 * the (locked) directory inode in ndp->ni_dvp. 427 * The pathname buffer is saved so that the name 428 * can be obtained later. 429 * 430 * NB - if the directory is unlocked, then this 431 * information cannot be used. 432 */ 433 cnp->cn_flags |= SAVENAME; 434 if (!lockparent) { 435 VOP_UNLOCK(vdp); 436 cnp->cn_flags |= PDIRUNLOCK; 437 } 438 return (EJUSTRETURN); 439 } 440 /* 441 * Insert name into cache (as non-existent) if appropriate. 442 */ 443 if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) 444 cache_enter(vdp, *vpp, cnp); 445 return (ENOENT); 446 447 found: 448 /* 449 * Check that directory length properly reflects presence 450 * of this entry. 451 */ 452 if (entryoffsetinblock + EXT2FS_DIRSIZ(ep->e2d_namlen) 453 > ext2fs_size(dp)) { 454 ufs_dirbad(dp, dp->i_offset, "i_size too small"); 455 error = ext2fs_setsize(dp, 456 entryoffsetinblock + EXT2FS_DIRSIZ(ep->e2d_namlen)); 457 if (error) { 458 brelse(bp); 459 return(error); 460 } 461 dp->i_flag |= IN_CHANGE | IN_UPDATE; 462 } 463 brelse(bp); 464 465 /* 466 * Found component in pathname. 467 * If the final component of path name, save information 468 * in the cache as to where the entry was found. 469 */ 470 if ((flags & ISLASTCN) && nameiop == LOOKUP) 471 dp->i_diroff = dp->i_offset &~ (dirblksize - 1); 472 473 /* 474 * If deleting, and at end of pathname, return 475 * parameters which can be used to remove file. 476 * If the wantparent flag isn't set, we return only 477 * the directory (in ndp->ni_dvp), otherwise we go 478 * on and lock the inode, being careful with ".". 479 */ 480 if (nameiop == DELETE && (flags & ISLASTCN)) { 481 /* 482 * Write access to directory required to delete files. 483 */ 484 if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0) 485 return (error); 486 /* 487 * Return pointer to current entry in dp->i_offset, 488 * and distance past previous entry (if there 489 * is a previous entry in this block) in dp->i_count. 490 * Save directory inode pointer in ndp->ni_dvp for dirremove(). 491 */ 492 if ((dp->i_offset & (dirblksize - 1)) == 0) 493 dp->i_count = 0; 494 else 495 dp->i_count = dp->i_offset - prevoff; 496 if (dp->i_number == dp->i_ino) { 497 vref(vdp); 498 *vpp = vdp; 499 return (0); 500 } 501 if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) 502 return (error); 503 /* 504 * If directory is "sticky", then user must own 505 * the directory, or the file in it, else she 506 * may not delete it (unless she's root). This 507 * implements append-only directories. 508 */ 509 if ((dp->i_e2fs_mode & ISVTX) && 510 cred->cr_uid != 0 && 511 cred->cr_uid != dp->i_e2fs_uid && 512 VTOI(tdp)->i_e2fs_uid != cred->cr_uid) { 513 vput(tdp); 514 return (EPERM); 515 } 516 *vpp = tdp; 517 if (!lockparent) { 518 VOP_UNLOCK(vdp); 519 cnp->cn_flags |= PDIRUNLOCK; 520 } 521 return (0); 522 } 523 524 /* 525 * If rewriting (RENAME), return the inode and the 526 * information required to rewrite the present directory 527 * Must get inode of directory entry to verify it's a 528 * regular file, or empty directory. 529 */ 530 if (nameiop == RENAME && wantparent && (flags & ISLASTCN)) { 531 if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0) 532 return (error); 533 /* 534 * Careful about locking second inode. 535 * This can only occur if the target is ".". 536 */ 537 if (dp->i_number == dp->i_ino) 538 return (EISDIR); 539 if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) 540 return (error); 541 *vpp = tdp; 542 cnp->cn_flags |= SAVENAME; 543 if (!lockparent) { 544 VOP_UNLOCK(vdp); 545 cnp->cn_flags |= PDIRUNLOCK; 546 } 547 return (0); 548 } 549 550 /* 551 * Step through the translation in the name. We do not `vput' the 552 * directory because we may need it again if a symbolic link 553 * is relative to the current directory. Instead we save it 554 * unlocked as "pdp". We must get the target inode before unlocking 555 * the directory to insure that the inode will not be removed 556 * before we get it. We prevent deadlock by always fetching 557 * inodes from the root, moving down the directory tree. Thus 558 * when following backward pointers ".." we must unlock the 559 * parent directory before getting the requested directory. 560 * There is a potential race condition here if both the current 561 * and parent directories are removed before the VFS_VGET for the 562 * inode associated with ".." returns. We hope that this occurs 563 * infrequently since we cannot avoid this race condition without 564 * implementing a sophisticated deadlock detection algorithm. 565 * Note also that this simple deadlock detection scheme will not 566 * work if the file system has any hard links other than ".." 567 * that point backwards in the directory structure. 568 */ 569 pdp = vdp; 570 if (flags & ISDOTDOT) { 571 VOP_UNLOCK(pdp); /* race to get the inode */ 572 cnp->cn_flags |= PDIRUNLOCK; 573 if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) { 574 if (vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY) == 0) 575 cnp->cn_flags &= ~PDIRUNLOCK; 576 return (error); 577 } 578 if (lockparent && (flags & ISLASTCN)) { 579 if ((error = vn_lock(pdp, LK_EXCLUSIVE)) != 0) { 580 vput(tdp); 581 return (error); 582 } 583 cnp->cn_flags &= ~PDIRUNLOCK; 584 } 585 *vpp = tdp; 586 } else if (dp->i_number == dp->i_ino) { 587 vref(vdp); /* we want ourself, ie "." */ 588 *vpp = vdp; 589 } else { 590 if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) 591 return (error); 592 if (!lockparent || !(flags & ISLASTCN)) { 593 VOP_UNLOCK(pdp); 594 cnp->cn_flags |= PDIRUNLOCK; 595 } 596 *vpp = tdp; 597 } 598 599 /* 600 * Insert name into cache if appropriate. 601 */ 602 if (cnp->cn_flags & MAKEENTRY) 603 cache_enter(vdp, *vpp, cnp); 604 return (0); 605 } 606 607 int 608 ext2fs_search_dirblock(struct inode *ip, void *data, int *foundp, 609 struct componentname *cnp, int *entryoffsetinblockp, 610 doff_t *prevoffp, doff_t *endusefulp, struct ext2fs_searchslot *ssp) 611 { 612 struct ext2fs_direct *ep, *lim; 613 struct vnode *vdp; 614 int offset = *entryoffsetinblockp; 615 int dirblksize = ip->i_e2fs->e2fs_bsize; 616 size_t namlen; 617 618 vdp = ITOV(ip); 619 620 lim = (struct ext2fs_direct *) 621 ((char *)data + dirblksize - EXT2FS_DIRSIZ(0)); 622 ep = (struct ext2fs_direct *) ((char *)data + offset); 623 624 while (ep < lim) { 625 /* 626 * Full validation checks are slow, so we only check 627 * enough to insure forward progress through the 628 * directory. Complete checks can be run by patching 629 * "dirchk" to be true. 630 */ 631 if (ep->e2d_reclen == 0 || 632 (dirchk && ext2fs_dirbadentry(vdp, ep, offset))) { 633 int i; 634 ufs_dirbad(ip, ip->i_offset, "mangled entry"); 635 i = dirblksize - (offset & (dirblksize - 1)); 636 ip->i_offset += i; 637 offset += i; 638 continue; 639 } 640 641 /* 642 * If an appropriate sized slot has not yet been found, 643 * check to see if one is available. Also accumulate space 644 * in the current block so that we can determine if 645 * compaction is viable. 646 */ 647 if (ssp->slotstatus != FOUND) { 648 int size = letoh16(ep->e2d_reclen); 649 650 if (ep->e2d_ino != 0) 651 size -= EXT2FS_DIRSIZ(ep->e2d_namlen); 652 if (size > 0) { 653 if (size >= ssp->slotneeded) { 654 ssp->slotstatus = FOUND; 655 ssp->slotoffset = ip->i_offset; 656 ssp->slotsize = letoh16(ep->e2d_reclen); 657 } else if (ssp->slotstatus == NONE) { 658 ssp->slotfreespace += size; 659 if (ssp->slotoffset == -1) 660 ssp->slotoffset = ip->i_offset; 661 if (ssp->slotfreespace >= ssp->slotneeded) { 662 ssp->slotstatus = COMPACT; 663 ssp->slotsize = ip->i_offset + 664 letoh16(ep->e2d_reclen) - ssp->slotoffset; 665 } 666 } 667 } 668 } 669 670 /* 671 * Check for a name match. 672 */ 673 if (ep->e2d_ino) { 674 namlen = ep->e2d_namlen; 675 if (namlen == cnp->cn_namelen && 676 !memcmp(cnp->cn_nameptr, ep->e2d_name, namlen)) { 677 /* 678 * Save directory entry's inode number and 679 * reclen in ndp->ni_ufs area, and release 680 * directory buffer. 681 */ 682 *foundp = 1; 683 return (0); 684 } 685 } 686 *prevoffp = ip->i_offset; 687 ip->i_offset += letoh16(ep->e2d_reclen); 688 offset += letoh16(ep->e2d_reclen); 689 *entryoffsetinblockp = offset; 690 if (ep->e2d_ino) 691 *endusefulp = ip->i_offset; 692 693 /* 694 * Get pointer to the next entry. 695 */ 696 ep = (struct ext2fs_direct *) ((char *)data + offset); 697 } 698 699 return (0); 700 } 701 702 /* 703 * Do consistency checking on a directory entry: 704 * record length must be multiple of 4 705 * entry must fit in rest of its dirblksize block 706 * record must be large enough to contain entry 707 * name is not longer than MAXNAMLEN 708 * name must be as long as advertised, and null terminated 709 */ 710 /* 711 * changed so that it confirms to ext2fs_check_dir_entry 712 */ 713 static int 714 ext2fs_dirbadentry(struct vnode *dp, struct ext2fs_direct *de, 715 int entryoffsetinblock) 716 { 717 int dirblksize = VTOI(dp)->i_e2fs->e2fs_bsize; 718 char *error_msg = NULL; 719 int reclen = letoh16(de->e2d_reclen); 720 int namlen = de->e2d_namlen; 721 722 if (reclen < EXT2FS_DIRSIZ(1)) /* e2d_namlen = 1 */ 723 error_msg = "rec_len is smaller than minimal"; 724 else if (reclen % 4 != 0) 725 error_msg = "rec_len % 4 != 0"; 726 else if (reclen < EXT2FS_DIRSIZ(namlen)) 727 error_msg = "reclen is too small for name_len"; 728 else if (entryoffsetinblock + reclen > dirblksize) 729 error_msg = "directory entry across blocks"; 730 else if (letoh32(de->e2d_ino) > VTOI(dp)->i_e2fs->e2fs.e2fs_icount) 731 error_msg = "inode out of bounds"; 732 733 if (error_msg != NULL) { 734 printf("bad directory entry: %s\n" 735 "offset=%d, inode=%u, rec_len=%d, name_len=%d \n", 736 error_msg, entryoffsetinblock, letoh32(de->e2d_ino), 737 reclen, namlen); 738 panic(__func__); 739 } 740 return (0); 741 } 742 743 /* 744 * Write a directory entry after a call to namei, using the parameters 745 * that it left in nameidata. The argument ip is the inode which the new 746 * directory entry will refer to. Dvp is a pointer to the directory to 747 * be written, which was left locked by namei. Remaining parameters 748 * (dp->i_offset, dp->i_count) indicate how the space for the new 749 * entry is to be obtained. 750 */ 751 int 752 ext2fs_direnter(struct inode *ip, struct vnode *dvp, 753 struct componentname *cnp) 754 { 755 struct ext2fs_direct *ep, *nep; 756 struct inode *dp; 757 struct buf *bp; 758 struct ext2fs_direct newdir; 759 struct iovec aiov; 760 struct uio auio; 761 u_int dsize; 762 int error, loc, newentrysize, spacefree; 763 char *dirbuf; 764 int dirblksize = ip->i_e2fs->e2fs_bsize; 765 766 767 #ifdef DIAGNOSTIC 768 if ((cnp->cn_flags & SAVENAME) == 0) 769 panic("direnter: missing name"); 770 #endif 771 dp = VTOI(dvp); 772 newdir.e2d_ino = htole32(ip->i_number); 773 newdir.e2d_namlen = cnp->cn_namelen; 774 if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 && 775 (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) { 776 newdir.e2d_type = inot2ext2dt(IFTODT(ip->i_e2fs_mode)); 777 } else { 778 newdir.e2d_type = 0; 779 } 780 memcpy(newdir.e2d_name, cnp->cn_nameptr, (unsigned)cnp->cn_namelen + 1); 781 newentrysize = EXT2FS_DIRSIZ(cnp->cn_namelen); 782 if (dp->i_count == 0) { 783 /* 784 * If dp->i_count is 0, then namei could find no 785 * space in the directory. Here, dp->i_offset will 786 * be on a directory block boundary and we will write the 787 * new entry into a fresh block. 788 */ 789 if (dp->i_offset & (dirblksize - 1)) 790 panic("ext2fs_direnter: newblk"); 791 auio.uio_offset = dp->i_offset; 792 newdir.e2d_reclen = htole16(dirblksize); 793 auio.uio_resid = newentrysize; 794 aiov.iov_len = newentrysize; 795 aiov.iov_base = (caddr_t)&newdir; 796 auio.uio_iov = &aiov; 797 auio.uio_iovcnt = 1; 798 auio.uio_rw = UIO_WRITE; 799 auio.uio_segflg = UIO_SYSSPACE; 800 auio.uio_procp = NULL; 801 error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred); 802 if (dirblksize > 803 VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize) 804 /* XXX should grow with balloc() */ 805 panic("ext2fs_direnter: frag size"); 806 else if (!error) { 807 error = ext2fs_setsize(dp, 808 roundup(ext2fs_size(dp), dirblksize)); 809 if (error) 810 return (error); 811 dp->i_flag |= IN_CHANGE; 812 } 813 return (error); 814 } 815 816 /* 817 * If dp->i_count is non-zero, then namei found space 818 * for the new entry in the range dp->i_offset to 819 * dp->i_offset + dp->i_count in the directory. 820 * To use this space, we may have to compact the entries located 821 * there, by copying them together towards the beginning of the 822 * block, leaving the free space in one usable chunk at the end. 823 */ 824 825 /* 826 * Get the block containing the space for the new directory entry. 827 */ 828 if ((error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, &dirbuf, &bp)) 829 != 0) 830 return (error); 831 /* 832 * Find space for the new entry. In the simple case, the entry at 833 * offset base will have the space. If it does not, then namei 834 * arranged that compacting the region dp->i_offset to 835 * dp->i_offset + dp->i_count would yield the 836 * space. 837 */ 838 ep = (struct ext2fs_direct *)dirbuf; 839 dsize = EXT2FS_DIRSIZ(ep->e2d_namlen); 840 spacefree = letoh16(ep->e2d_reclen) - dsize; 841 for (loc = letoh16(ep->e2d_reclen); loc < dp->i_count; ) { 842 nep = (struct ext2fs_direct *)(dirbuf + loc); 843 if (ep->e2d_ino) { 844 /* trim the existing slot */ 845 ep->e2d_reclen = htole16(dsize); 846 ep = (struct ext2fs_direct *)((char *)ep + dsize); 847 } else { 848 /* overwrite; nothing there; header is ours */ 849 spacefree += dsize; 850 } 851 dsize = EXT2FS_DIRSIZ(nep->e2d_namlen); 852 spacefree += letoh16(nep->e2d_reclen) - dsize; 853 loc += letoh16(nep->e2d_reclen); 854 memcpy(ep, nep, dsize); 855 } 856 /* 857 * Update the pointer fields in the previous entry (if any), 858 * copy in the new entry, and write out the block. 859 */ 860 if (ep->e2d_ino == 0) { 861 #ifdef DIAGNOSTIC 862 if (spacefree + dsize < newentrysize) 863 panic("ext2fs_direnter: compact1"); 864 #endif 865 newdir.e2d_reclen = htole16(spacefree + dsize); 866 } else { 867 #ifdef DIAGNOSTIC 868 if (spacefree < newentrysize) { 869 printf("ext2fs_direnter: compact2 %u %u", 870 (u_int)spacefree, (u_int)newentrysize); 871 panic("ext2fs_direnter: compact2"); 872 } 873 #endif 874 newdir.e2d_reclen = htole16(spacefree); 875 ep->e2d_reclen = htole16(dsize); 876 ep = (struct ext2fs_direct *)((char *)ep + dsize); 877 } 878 memcpy(ep, &newdir, newentrysize); 879 error = VOP_BWRITE(bp); 880 dp->i_flag |= IN_CHANGE | IN_UPDATE; 881 if (!error && dp->i_endoff && dp->i_endoff < ext2fs_size(dp)) 882 error = ext2fs_truncate(dp, (off_t)dp->i_endoff, IO_SYNC, 883 cnp->cn_cred); 884 return (error); 885 } 886 887 /* 888 * Remove a directory entry after a call to namei, using 889 * the parameters which it left in nameidata. The entry 890 * dp->i_offset contains the offset into the directory of the 891 * entry to be eliminated. The dp->i_count field contains the 892 * size of the previous record in the directory. If this 893 * is 0, the first entry is being deleted, so we need only 894 * zero the inode number to mark the entry as free. If the 895 * entry is not the first in the directory, we must reclaim 896 * the space of the now empty record by adding the record size 897 * to the size of the previous entry. 898 */ 899 int 900 ext2fs_dirremove(struct vnode *dvp, struct componentname *cnp) 901 { 902 struct inode *dp; 903 struct ext2fs_direct *ep; 904 struct buf *bp; 905 int error; 906 907 dp = VTOI(dvp); 908 if (dp->i_count == 0) { 909 /* 910 * First entry in block: set d_ino to zero. 911 */ 912 error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, (char **)&ep, 913 &bp); 914 if (error != 0) 915 return (error); 916 ep->e2d_ino = 0; 917 error = VOP_BWRITE(bp); 918 dp->i_flag |= IN_CHANGE | IN_UPDATE; 919 return (error); 920 } 921 /* 922 * Collapse new free space into previous entry. 923 */ 924 error = ext2fs_bufatoff(dp, (off_t)(dp->i_offset - dp->i_count), 925 (char **)&ep, &bp); 926 if (error != 0) 927 return (error); 928 ep->e2d_reclen = htole16(letoh16(ep->e2d_reclen) + dp->i_reclen); 929 error = VOP_BWRITE(bp); 930 dp->i_flag |= IN_CHANGE | IN_UPDATE; 931 return (error); 932 } 933 934 /* 935 * Rewrite an existing directory entry to point at the inode 936 * supplied. The parameters describing the directory entry are 937 * set up by a call to namei. 938 */ 939 int 940 ext2fs_dirrewrite(struct inode *dp, struct inode *ip, 941 struct componentname *cnp) 942 { 943 struct buf *bp; 944 struct ext2fs_direct *ep; 945 int error; 946 947 error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, (char **)&ep, &bp); 948 if (error != 0) 949 return (error); 950 ep->e2d_ino = htole32(ip->i_number); 951 if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 && 952 (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) { 953 ep->e2d_type = inot2ext2dt(IFTODT(ip->i_e2fs_mode)); 954 } else { 955 ep->e2d_type = 0; 956 } 957 error = VOP_BWRITE(bp); 958 dp->i_flag |= IN_CHANGE | IN_UPDATE; 959 return (error); 960 } 961 962 /* 963 * Check if a directory is empty or not. 964 * Inode supplied must be locked. 965 * 966 * Using a struct dirtemplate here is not precisely 967 * what we want, but better than using a struct ext2fs_direct. 968 * 969 * NB: does not handle corrupted directories. 970 */ 971 int 972 ext2fs_dirempty(struct inode *ip, ufsino_t parentino, struct ucred *cred) 973 { 974 off_t off; 975 struct ext2fs_dirtemplate dbuf; 976 struct ext2fs_direct *dp = (struct ext2fs_direct *)&dbuf; 977 int error, namlen; 978 size_t count; 979 980 #define MINDIRSIZ (sizeof (struct ext2fs_dirtemplate) / 2) 981 982 for (off = 0; off < ext2fs_size(ip); off += letoh16(dp->e2d_reclen)) { 983 error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off, 984 UIO_SYSSPACE, IO_NODELOCKED, cred, &count, curproc); 985 /* 986 * Since we read MINDIRSIZ, residual must 987 * be 0 unless we're at end of file. 988 */ 989 if (error || count != 0) 990 return (0); 991 /* avoid infinite loops */ 992 if (dp->e2d_reclen == 0) 993 return (0); 994 /* skip empty entries */ 995 if (dp->e2d_ino == 0) 996 continue; 997 /* accept only "." and ".." */ 998 namlen = dp->e2d_namlen; 999 if (namlen > 2) 1000 return (0); 1001 if (dp->e2d_name[0] != '.') 1002 return (0); 1003 /* 1004 * At this point namlen must be 1 or 2. 1005 * 1 implies ".", 2 implies ".." if second 1006 * char is also "." 1007 */ 1008 if (namlen == 1) 1009 continue; 1010 if (dp->e2d_name[1] == '.' && letoh32(dp->e2d_ino) == parentino) 1011 continue; 1012 return (0); 1013 } 1014 return (1); 1015 } 1016 1017 /* 1018 * Check if source directory is in the path of the target directory. 1019 * Target is supplied locked, source is unlocked. 1020 * The target is always vput before returning. 1021 */ 1022 int 1023 ext2fs_checkpath(struct inode *source, struct inode *target, 1024 struct ucred *cred) 1025 { 1026 struct vnode *vp; 1027 int error, rootino, namlen; 1028 struct ext2fs_dirtemplate dirbuf; 1029 u_int32_t ino; 1030 1031 vp = ITOV(target); 1032 if (target->i_number == source->i_number) { 1033 error = EEXIST; 1034 goto out; 1035 } 1036 rootino = ROOTINO; 1037 error = 0; 1038 if (target->i_number == rootino) 1039 goto out; 1040 1041 for (;;) { 1042 if (vp->v_type != VDIR) { 1043 error = ENOTDIR; 1044 break; 1045 } 1046 error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf, 1047 sizeof (struct ext2fs_dirtemplate), (off_t)0, 1048 UIO_SYSSPACE, IO_NODELOCKED, cred, NULL, 1049 curproc); 1050 if (error != 0) 1051 break; 1052 namlen = dirbuf.dotdot_namlen; 1053 if (namlen != 2 || 1054 dirbuf.dotdot_name[0] != '.' || 1055 dirbuf.dotdot_name[1] != '.') { 1056 error = ENOTDIR; 1057 break; 1058 } 1059 ino = letoh32(dirbuf.dotdot_ino); 1060 if (ino == source->i_number) { 1061 error = EINVAL; 1062 break; 1063 } 1064 if (ino == rootino) 1065 break; 1066 vput(vp); 1067 error = VFS_VGET(vp->v_mount, ino, &vp); 1068 if (error != 0) { 1069 vp = NULL; 1070 break; 1071 } 1072 } 1073 1074 out: 1075 if (error == ENOTDIR) { 1076 printf("checkpath: .. not a directory\n"); 1077 panic("checkpath"); 1078 } 1079 if (vp != NULL) 1080 vput(vp); 1081 return (error); 1082 } 1083