1 /* $OpenBSD: ext2fs_lookup.c,v 1.45 2021/03/11 13:31:35 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(dstd.d_reclen > uio->uio_resid) { 177 break; 178 } 179 dstd.d_off = off + e2d_reclen; 180 if ((error = uiomove((caddr_t)&dstd, dstd.d_reclen, uio)) != 0) { 181 break; 182 } 183 off = off + e2d_reclen; 184 /* advance dp */ 185 dp = (struct ext2fs_direct *) ((char *)dp + e2d_reclen); 186 } 187 /* we need to correct uio_offset */ 188 uio->uio_offset = off; 189 } 190 free(dirbuf, M_TEMP, e2fs_count); 191 *ap->a_eofflag = ext2fs_size(VTOI(ap->a_vp)) <= uio->uio_offset; 192 return (error); 193 } 194 195 /* 196 * Convert a component of a pathname into a pointer to a locked inode. 197 * This is a very central and rather complicated routine. 198 * If the file system is not maintained in a strict tree hierarchy, 199 * this can result in a deadlock situation (see comments in code below). 200 * 201 * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending 202 * on whether the name is to be looked up, created, renamed, or deleted. 203 * When CREATE, RENAME, or DELETE is specified, information usable in 204 * creating, renaming, or deleting a directory entry may be calculated. 205 * If flag has LOCKPARENT or'ed into it and the target of the pathname 206 * exists, lookup returns both the target and its parent directory locked. 207 * When creating or renaming and LOCKPARENT is specified, the target may 208 * not be ".". When deleting and LOCKPARENT is specified, the target may 209 * be "."., but the caller must check to ensure it does an vrele and vput 210 * instead of two vputs. 211 * 212 * Overall outline of ext2fs_lookup: 213 * 214 * check accessibility of directory 215 * look for name in cache, if found, then if at end of path 216 * and deleting or creating, drop it, else return name 217 * search for name in directory, to found or notfound 218 * notfound: 219 * if creating, return locked directory, leaving info on available slots 220 * else return error 221 * found: 222 * if at end of path and deleting, return information to allow delete 223 * if at end of path and rewriting (RENAME and LOCKPARENT), lock target 224 * inode and return info to allow rewrite 225 * if not at end, add name to cache; if at end and neither creating 226 * nor deleting, add name to cache 227 */ 228 int 229 ext2fs_lookup(void *v) 230 { 231 struct vop_lookup_args *ap = v; 232 struct vnode *vdp; /* vnode for directory being searched */ 233 struct inode *dp; /* inode for directory being searched */ 234 struct buf *bp; /* a buffer of directory entries */ 235 struct ext2fs_direct *ep; /* the current directory entry */ 236 int entryoffsetinblock; /* offset of ep in bp's buffer */ 237 struct ext2fs_searchslot ss; 238 int numdirpasses; /* strategy for directory search */ 239 doff_t endsearch; /* offset to end directory search */ 240 doff_t prevoff; /* prev entry dp->i_offset */ 241 struct vnode *pdp; /* saved dp during symlink work */ 242 struct vnode *tdp; /* returned by VFS_VGET */ 243 doff_t enduseful; /* pointer past last used dir slot */ 244 u_long bmask; /* block offset mask */ 245 int lockparent; /* 1 => lockparent flag is set */ 246 int wantparent; /* 1 => wantparent or lockparent flag */ 247 struct vnode **vpp = ap->a_vpp; 248 struct componentname *cnp = ap->a_cnp; 249 struct ucred *cred = cnp->cn_cred; 250 int flags = cnp->cn_flags; 251 int nameiop = cnp->cn_nameiop; 252 int dirblksize, entry_found = 0, error; 253 254 ss.slotstatus = FOUND; 255 ss.slotoffset = -1; 256 ss.slotfreespace = ss.slotsize = ss.slotneeded = 0; 257 258 bp = NULL; 259 *vpp = NULL; 260 vdp = ap->a_dvp; 261 dp = VTOI(vdp); 262 dirblksize = dp->i_e2fs->e2fs_bsize; 263 lockparent = flags & LOCKPARENT; 264 wantparent = flags & (LOCKPARENT|WANTPARENT); 265 266 /* 267 * Check accessiblity of directory. 268 */ 269 if ((error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc)) != 0) 270 return (error); 271 272 if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) && 273 (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) 274 return (EROFS); 275 276 /* 277 * We now have a segment name to search for, and a directory to search. 278 * 279 * Before tediously performing a linear scan of the directory, 280 * check the name cache to see if the directory/name pair 281 * we are looking for is known already. 282 */ 283 if ((error = cache_lookup(vdp, vpp, cnp)) >= 0) 284 return (error); 285 286 /* 287 * Suppress search for slots unless creating 288 * file and at end of pathname, in which case 289 * we watch for a place to put the new file in 290 * case it doesn't already exist. 291 */ 292 if ((nameiop == CREATE || nameiop == RENAME) && (flags & ISLASTCN)) { 293 ss.slotstatus = NONE; 294 ss.slotneeded = EXT2FS_DIRSIZ(cnp->cn_namelen); 295 } 296 297 /* 298 * If there is cached information on a previous search of 299 * this directory, pick up where we last left off. 300 * We cache only lookups as these are the most common 301 * and have the greatest payoff. Caching CREATE has little 302 * benefit as it usually must search the entire directory 303 * to determine that the entry does not exist. Caching the 304 * location of the last DELETE or RENAME has not reduced 305 * profiling time and hence has been removed in the interest 306 * of simplicity. 307 */ 308 bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1; 309 if (nameiop != LOOKUP || dp->i_diroff == 0 || 310 dp->i_diroff > ext2fs_size(dp)) { 311 entryoffsetinblock = 0; 312 dp->i_offset = 0; 313 numdirpasses = 1; 314 } else { 315 dp->i_offset = dp->i_diroff; 316 if ((entryoffsetinblock = dp->i_offset & bmask) && 317 (error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, 318 NULL, &bp))) 319 return (error); 320 numdirpasses = 2; 321 } 322 prevoff = dp->i_offset; 323 endsearch = roundup(ext2fs_size(dp), dirblksize); 324 enduseful = 0; 325 326 searchloop: 327 while (dp->i_offset < endsearch) { 328 /* 329 * If necessary, get the next directory block. 330 */ 331 if (bp != NULL) 332 brelse(bp); 333 334 error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, NULL, &bp); 335 if (error != 0) 336 return (error); 337 entryoffsetinblock = 0; 338 339 /* 340 * If still looking for a slot, and at a dirblksize 341 * boundary, have to start looking for free space again. 342 */ 343 if (ss.slotstatus == NONE) { 344 ss.slotoffset = -1; 345 ss.slotfreespace = 0; 346 } 347 348 error = ext2fs_search_dirblock(dp, bp->b_data, &entry_found, 349 cnp, &entryoffsetinblock, &prevoff, &enduseful, &ss); 350 if (error) { 351 brelse(bp); 352 return (error); 353 } 354 if (entry_found) { 355 ep = (struct ext2fs_direct *) 356 ((char *)bp->b_data + (entryoffsetinblock & bmask)); 357 /* foundentry: */ 358 dp->i_ino = letoh32(ep->e2d_ino); 359 dp->i_reclen = letoh16(ep->e2d_reclen); 360 goto found; 361 } 362 } 363 /* notfound: */ 364 /* 365 * If we started in the middle of the directory and failed 366 * to find our target, we must check the beginning as well. 367 */ 368 if (numdirpasses == 2) { 369 numdirpasses--; 370 dp->i_offset = 0; 371 endsearch = dp->i_diroff; 372 goto searchloop; 373 } 374 if (bp != NULL) 375 brelse(bp); 376 /* 377 * If creating, and at end of pathname and current 378 * directory has not been removed, then can consider 379 * allowing file to be created. 380 */ 381 if ((nameiop == CREATE || nameiop == RENAME) && 382 (flags & ISLASTCN) && dp->i_e2fs_nlink != 0) { 383 /* 384 * Creation of files on a read-only mounted file system 385 * is pointless, so don't proceed any further. 386 */ 387 if (vdp->v_mount->mnt_flag & MNT_RDONLY) 388 return (EROFS); 389 /* 390 * Access for write is interpreted as allowing 391 * creation of files in the directory. 392 */ 393 if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0) 394 return (error); 395 /* 396 * Return an indication of where the new directory 397 * entry should be put. If we didn't find a slot, 398 * then set dp->i_count to 0 indicating 399 * that the new slot belongs at the end of the 400 * directory. If we found a slot, then the new entry 401 * can be put in the range from dp->i_offset to 402 * dp->i_offset + dp->i_count. 403 */ 404 if (ss.slotstatus == NONE) { 405 dp->i_offset = roundup(ext2fs_size(dp), dirblksize); 406 dp->i_count = 0; 407 enduseful = dp->i_offset; 408 } else { 409 dp->i_offset = ss.slotoffset; 410 dp->i_count = ss.slotsize; 411 if (enduseful < ss.slotoffset + ss.slotsize) 412 enduseful = ss.slotoffset + ss.slotsize; 413 } 414 dp->i_endoff = roundup(enduseful, dirblksize); 415 dp->i_flag |= IN_CHANGE | IN_UPDATE; 416 /* 417 * We return with the directory locked, so that 418 * the parameters we set up above will still be 419 * valid if we actually decide to do a direnter(). 420 * We return ni_vp == NULL to indicate that the entry 421 * does not currently exist; we leave a pointer to 422 * the (locked) directory inode in ndp->ni_dvp. 423 * The pathname buffer is saved so that the name 424 * can be obtained later. 425 * 426 * NB - if the directory is unlocked, then this 427 * information cannot be used. 428 */ 429 cnp->cn_flags |= SAVENAME; 430 if (!lockparent) { 431 VOP_UNLOCK(vdp); 432 cnp->cn_flags |= PDIRUNLOCK; 433 } 434 return (EJUSTRETURN); 435 } 436 /* 437 * Insert name into cache (as non-existent) if appropriate. 438 */ 439 if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) 440 cache_enter(vdp, *vpp, cnp); 441 return (ENOENT); 442 443 found: 444 /* 445 * Check that directory length properly reflects presence 446 * of this entry. 447 */ 448 if (entryoffsetinblock + EXT2FS_DIRSIZ(ep->e2d_namlen) 449 > ext2fs_size(dp)) { 450 ufs_dirbad(dp, dp->i_offset, "i_size too small"); 451 error = ext2fs_setsize(dp, 452 entryoffsetinblock + EXT2FS_DIRSIZ(ep->e2d_namlen)); 453 if (error) { 454 brelse(bp); 455 return(error); 456 } 457 dp->i_flag |= IN_CHANGE | IN_UPDATE; 458 } 459 brelse(bp); 460 461 /* 462 * Found component in pathname. 463 * If the final component of path name, save information 464 * in the cache as to where the entry was found. 465 */ 466 if ((flags & ISLASTCN) && nameiop == LOOKUP) 467 dp->i_diroff = dp->i_offset &~ (dirblksize - 1); 468 469 /* 470 * If deleting, and at end of pathname, return 471 * parameters which can be used to remove file. 472 * If the wantparent flag isn't set, we return only 473 * the directory (in ndp->ni_dvp), otherwise we go 474 * on and lock the inode, being careful with ".". 475 */ 476 if (nameiop == DELETE && (flags & ISLASTCN)) { 477 /* 478 * Write access to directory required to delete files. 479 */ 480 if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0) 481 return (error); 482 /* 483 * Return pointer to current entry in dp->i_offset, 484 * and distance past previous entry (if there 485 * is a previous entry in this block) in dp->i_count. 486 * Save directory inode pointer in ndp->ni_dvp for dirremove(). 487 */ 488 if ((dp->i_offset & (dirblksize - 1)) == 0) 489 dp->i_count = 0; 490 else 491 dp->i_count = dp->i_offset - prevoff; 492 if (dp->i_number == dp->i_ino) { 493 vref(vdp); 494 *vpp = vdp; 495 return (0); 496 } 497 if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) 498 return (error); 499 /* 500 * If directory is "sticky", then user must own 501 * the directory, or the file in it, else she 502 * may not delete it (unless she's root). This 503 * implements append-only directories. 504 */ 505 if ((dp->i_e2fs_mode & ISVTX) && 506 cred->cr_uid != 0 && 507 cred->cr_uid != dp->i_e2fs_uid && 508 VTOI(tdp)->i_e2fs_uid != cred->cr_uid) { 509 vput(tdp); 510 return (EPERM); 511 } 512 *vpp = tdp; 513 if (!lockparent) { 514 VOP_UNLOCK(vdp); 515 cnp->cn_flags |= PDIRUNLOCK; 516 } 517 return (0); 518 } 519 520 /* 521 * If rewriting (RENAME), return the inode and the 522 * information required to rewrite the present directory 523 * Must get inode of directory entry to verify it's a 524 * regular file, or empty directory. 525 */ 526 if (nameiop == RENAME && wantparent && (flags & ISLASTCN)) { 527 if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0) 528 return (error); 529 /* 530 * Careful about locking second inode. 531 * This can only occur if the target is ".". 532 */ 533 if (dp->i_number == dp->i_ino) 534 return (EISDIR); 535 if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) 536 return (error); 537 *vpp = tdp; 538 cnp->cn_flags |= SAVENAME; 539 if (!lockparent) { 540 VOP_UNLOCK(vdp); 541 cnp->cn_flags |= PDIRUNLOCK; 542 } 543 return (0); 544 } 545 546 /* 547 * Step through the translation in the name. We do not `vput' the 548 * directory because we may need it again if a symbolic link 549 * is relative to the current directory. Instead we save it 550 * unlocked as "pdp". We must get the target inode before unlocking 551 * the directory to insure that the inode will not be removed 552 * before we get it. We prevent deadlock by always fetching 553 * inodes from the root, moving down the directory tree. Thus 554 * when following backward pointers ".." we must unlock the 555 * parent directory before getting the requested directory. 556 * There is a potential race condition here if both the current 557 * and parent directories are removed before the VFS_VGET for the 558 * inode associated with ".." returns. We hope that this occurs 559 * infrequently since we cannot avoid this race condition without 560 * implementing a sophisticated deadlock detection algorithm. 561 * Note also that this simple deadlock detection scheme will not 562 * work if the file system has any hard links other than ".." 563 * that point backwards in the directory structure. 564 */ 565 pdp = vdp; 566 if (flags & ISDOTDOT) { 567 VOP_UNLOCK(pdp); /* race to get the inode */ 568 cnp->cn_flags |= PDIRUNLOCK; 569 if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) { 570 if (vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY) == 0) 571 cnp->cn_flags &= ~PDIRUNLOCK; 572 return (error); 573 } 574 if (lockparent && (flags & ISLASTCN)) { 575 if ((error = vn_lock(pdp, LK_EXCLUSIVE)) != 0) { 576 vput(tdp); 577 return (error); 578 } 579 cnp->cn_flags &= ~PDIRUNLOCK; 580 } 581 *vpp = tdp; 582 } else if (dp->i_number == dp->i_ino) { 583 vref(vdp); /* we want ourself, ie "." */ 584 *vpp = vdp; 585 } else { 586 if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) 587 return (error); 588 if (!lockparent || !(flags & ISLASTCN)) { 589 VOP_UNLOCK(pdp); 590 cnp->cn_flags |= PDIRUNLOCK; 591 } 592 *vpp = tdp; 593 } 594 595 /* 596 * Insert name into cache if appropriate. 597 */ 598 if (cnp->cn_flags & MAKEENTRY) 599 cache_enter(vdp, *vpp, cnp); 600 return (0); 601 } 602 603 int 604 ext2fs_search_dirblock(struct inode *ip, void *data, int *foundp, 605 struct componentname *cnp, int *entryoffsetinblockp, 606 doff_t *prevoffp, doff_t *endusefulp, struct ext2fs_searchslot *ssp) 607 { 608 struct ext2fs_direct *ep, *lim; 609 struct vnode *vdp; 610 int offset = *entryoffsetinblockp; 611 int dirblksize = ip->i_e2fs->e2fs_bsize; 612 size_t namlen; 613 614 vdp = ITOV(ip); 615 616 lim = (struct ext2fs_direct *) 617 ((char *)data + dirblksize - EXT2FS_DIRSIZ(0)); 618 ep = (struct ext2fs_direct *) ((char *)data + offset); 619 620 while (ep < lim) { 621 /* 622 * Full validation checks are slow, so we only check 623 * enough to insure forward progress through the 624 * directory. Complete checks can be run by patching 625 * "dirchk" to be true. 626 */ 627 if (ep->e2d_reclen == 0 || 628 (dirchk && ext2fs_dirbadentry(vdp, ep, offset))) { 629 int i; 630 ufs_dirbad(ip, ip->i_offset, "mangled entry"); 631 i = dirblksize - (offset & (dirblksize - 1)); 632 ip->i_offset += i; 633 offset += i; 634 continue; 635 } 636 637 /* 638 * If an appropriate sized slot has not yet been found, 639 * check to see if one is available. Also accumulate space 640 * in the current block so that we can determine if 641 * compaction is viable. 642 */ 643 if (ssp->slotstatus != FOUND) { 644 int size = letoh16(ep->e2d_reclen); 645 646 if (ep->e2d_ino != 0) 647 size -= EXT2FS_DIRSIZ(ep->e2d_namlen); 648 if (size > 0) { 649 if (size >= ssp->slotneeded) { 650 ssp->slotstatus = FOUND; 651 ssp->slotoffset = ip->i_offset; 652 ssp->slotsize = letoh16(ep->e2d_reclen); 653 } else if (ssp->slotstatus == NONE) { 654 ssp->slotfreespace += size; 655 if (ssp->slotoffset == -1) 656 ssp->slotoffset = ip->i_offset; 657 if (ssp->slotfreespace >= ssp->slotneeded) { 658 ssp->slotstatus = COMPACT; 659 ssp->slotsize = ip->i_offset + 660 letoh16(ep->e2d_reclen) - ssp->slotoffset; 661 } 662 } 663 } 664 } 665 666 /* 667 * Check for a name match. 668 */ 669 if (ep->e2d_ino) { 670 namlen = ep->e2d_namlen; 671 if (namlen == cnp->cn_namelen && 672 !memcmp(cnp->cn_nameptr, ep->e2d_name, namlen)) { 673 /* 674 * Save directory entry's inode number and 675 * reclen in ndp->ni_ufs area, and release 676 * directory buffer. 677 */ 678 *foundp = 1; 679 return (0); 680 } 681 } 682 *prevoffp = ip->i_offset; 683 ip->i_offset += letoh16(ep->e2d_reclen); 684 offset += letoh16(ep->e2d_reclen); 685 *entryoffsetinblockp = offset; 686 if (ep->e2d_ino) 687 *endusefulp = ip->i_offset; 688 689 /* 690 * Get pointer to the next entry. 691 */ 692 ep = (struct ext2fs_direct *) ((char *)data + offset); 693 } 694 695 return (0); 696 } 697 698 /* 699 * Do consistency checking on a directory entry: 700 * record length must be multiple of 4 701 * entry must fit in rest of its dirblksize block 702 * record must be large enough to contain entry 703 * name is not longer than MAXNAMLEN 704 * name must be as long as advertised, and null terminated 705 */ 706 /* 707 * changed so that it confirms to ext2fs_check_dir_entry 708 */ 709 static int 710 ext2fs_dirbadentry(struct vnode *dp, struct ext2fs_direct *de, 711 int entryoffsetinblock) 712 { 713 int dirblksize = VTOI(dp)->i_e2fs->e2fs_bsize; 714 char *error_msg = NULL; 715 int reclen = letoh16(de->e2d_reclen); 716 int namlen = de->e2d_namlen; 717 718 if (reclen < EXT2FS_DIRSIZ(1)) /* e2d_namlen = 1 */ 719 error_msg = "rec_len is smaller than minimal"; 720 else if (reclen % 4 != 0) 721 error_msg = "rec_len % 4 != 0"; 722 else if (reclen < EXT2FS_DIRSIZ(namlen)) 723 error_msg = "reclen is too small for name_len"; 724 else if (entryoffsetinblock + reclen > dirblksize) 725 error_msg = "directory entry across blocks"; 726 else if (letoh32(de->e2d_ino) > VTOI(dp)->i_e2fs->e2fs.e2fs_icount) 727 error_msg = "inode out of bounds"; 728 729 if (error_msg != NULL) { 730 printf("bad directory entry: %s\n" 731 "offset=%d, inode=%u, rec_len=%d, name_len=%d \n", 732 error_msg, entryoffsetinblock, letoh32(de->e2d_ino), 733 reclen, namlen); 734 panic(__func__); 735 } 736 return (0); 737 } 738 739 /* 740 * Write a directory entry after a call to namei, using the parameters 741 * that it left in nameidata. The argument ip is the inode which the new 742 * directory entry will refer to. Dvp is a pointer to the directory to 743 * be written, which was left locked by namei. Remaining parameters 744 * (dp->i_offset, dp->i_count) indicate how the space for the new 745 * entry is to be obtained. 746 */ 747 int 748 ext2fs_direnter(struct inode *ip, struct vnode *dvp, 749 struct componentname *cnp) 750 { 751 struct ext2fs_direct *ep, *nep; 752 struct inode *dp; 753 struct buf *bp; 754 struct ext2fs_direct newdir; 755 struct iovec aiov; 756 struct uio auio; 757 u_int dsize; 758 int error, loc, newentrysize, spacefree; 759 char *dirbuf; 760 int dirblksize = ip->i_e2fs->e2fs_bsize; 761 762 763 #ifdef DIAGNOSTIC 764 if ((cnp->cn_flags & SAVENAME) == 0) 765 panic("direnter: missing name"); 766 #endif 767 dp = VTOI(dvp); 768 newdir.e2d_ino = htole32(ip->i_number); 769 newdir.e2d_namlen = cnp->cn_namelen; 770 if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 && 771 (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) { 772 newdir.e2d_type = inot2ext2dt(IFTODT(ip->i_e2fs_mode)); 773 } else { 774 newdir.e2d_type = 0; 775 }; 776 memcpy(newdir.e2d_name, cnp->cn_nameptr, (unsigned)cnp->cn_namelen + 1); 777 newentrysize = EXT2FS_DIRSIZ(cnp->cn_namelen); 778 if (dp->i_count == 0) { 779 /* 780 * If dp->i_count is 0, then namei could find no 781 * space in the directory. Here, dp->i_offset will 782 * be on a directory block boundary and we will write the 783 * new entry into a fresh block. 784 */ 785 if (dp->i_offset & (dirblksize - 1)) 786 panic("ext2fs_direnter: newblk"); 787 auio.uio_offset = dp->i_offset; 788 newdir.e2d_reclen = htole16(dirblksize); 789 auio.uio_resid = newentrysize; 790 aiov.iov_len = newentrysize; 791 aiov.iov_base = (caddr_t)&newdir; 792 auio.uio_iov = &aiov; 793 auio.uio_iovcnt = 1; 794 auio.uio_rw = UIO_WRITE; 795 auio.uio_segflg = UIO_SYSSPACE; 796 auio.uio_procp = NULL; 797 error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred); 798 if (dirblksize > 799 VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize) 800 /* XXX should grow with balloc() */ 801 panic("ext2fs_direnter: frag size"); 802 else if (!error) { 803 error = ext2fs_setsize(dp, 804 roundup(ext2fs_size(dp), dirblksize)); 805 if (error) 806 return (error); 807 dp->i_flag |= IN_CHANGE; 808 } 809 return (error); 810 } 811 812 /* 813 * If dp->i_count is non-zero, then namei found space 814 * for the new entry in the range dp->i_offset to 815 * dp->i_offset + dp->i_count in the directory. 816 * To use this space, we may have to compact the entries located 817 * there, by copying them together towards the beginning of the 818 * block, leaving the free space in one usable chunk at the end. 819 */ 820 821 /* 822 * Get the block containing the space for the new directory entry. 823 */ 824 if ((error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, &dirbuf, &bp)) 825 != 0) 826 return (error); 827 /* 828 * Find space for the new entry. In the simple case, the entry at 829 * offset base will have the space. If it does not, then namei 830 * arranged that compacting the region dp->i_offset to 831 * dp->i_offset + dp->i_count would yield the 832 * space. 833 */ 834 ep = (struct ext2fs_direct *)dirbuf; 835 dsize = EXT2FS_DIRSIZ(ep->e2d_namlen); 836 spacefree = letoh16(ep->e2d_reclen) - dsize; 837 for (loc = letoh16(ep->e2d_reclen); loc < dp->i_count; ) { 838 nep = (struct ext2fs_direct *)(dirbuf + loc); 839 if (ep->e2d_ino) { 840 /* trim the existing slot */ 841 ep->e2d_reclen = htole16(dsize); 842 ep = (struct ext2fs_direct *)((char *)ep + dsize); 843 } else { 844 /* overwrite; nothing there; header is ours */ 845 spacefree += dsize; 846 } 847 dsize = EXT2FS_DIRSIZ(nep->e2d_namlen); 848 spacefree += letoh16(nep->e2d_reclen) - dsize; 849 loc += letoh16(nep->e2d_reclen); 850 memcpy(ep, nep, dsize); 851 } 852 /* 853 * Update the pointer fields in the previous entry (if any), 854 * copy in the new entry, and write out the block. 855 */ 856 if (ep->e2d_ino == 0) { 857 #ifdef DIAGNOSTIC 858 if (spacefree + dsize < newentrysize) 859 panic("ext2fs_direnter: compact1"); 860 #endif 861 newdir.e2d_reclen = htole16(spacefree + dsize); 862 } else { 863 #ifdef DIAGNOSTIC 864 if (spacefree < newentrysize) { 865 printf("ext2fs_direnter: compact2 %u %u", 866 (u_int)spacefree, (u_int)newentrysize); 867 panic("ext2fs_direnter: compact2"); 868 } 869 #endif 870 newdir.e2d_reclen = htole16(spacefree); 871 ep->e2d_reclen = htole16(dsize); 872 ep = (struct ext2fs_direct *)((char *)ep + dsize); 873 } 874 memcpy(ep, &newdir, newentrysize); 875 error = VOP_BWRITE(bp); 876 dp->i_flag |= IN_CHANGE | IN_UPDATE; 877 if (!error && dp->i_endoff && dp->i_endoff < ext2fs_size(dp)) 878 error = ext2fs_truncate(dp, (off_t)dp->i_endoff, IO_SYNC, 879 cnp->cn_cred); 880 return (error); 881 } 882 883 /* 884 * Remove a directory entry after a call to namei, using 885 * the parameters which it left in nameidata. The entry 886 * dp->i_offset contains the offset into the directory of the 887 * entry to be eliminated. The dp->i_count field contains the 888 * size of the previous record in the directory. If this 889 * is 0, the first entry is being deleted, so we need only 890 * zero the inode number to mark the entry as free. If the 891 * entry is not the first in the directory, we must reclaim 892 * the space of the now empty record by adding the record size 893 * to the size of the previous entry. 894 */ 895 int 896 ext2fs_dirremove(struct vnode *dvp, struct componentname *cnp) 897 { 898 struct inode *dp; 899 struct ext2fs_direct *ep; 900 struct buf *bp; 901 int error; 902 903 dp = VTOI(dvp); 904 if (dp->i_count == 0) { 905 /* 906 * First entry in block: set d_ino to zero. 907 */ 908 error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, (char **)&ep, 909 &bp); 910 if (error != 0) 911 return (error); 912 ep->e2d_ino = 0; 913 error = VOP_BWRITE(bp); 914 dp->i_flag |= IN_CHANGE | IN_UPDATE; 915 return (error); 916 } 917 /* 918 * Collapse new free space into previous entry. 919 */ 920 error = ext2fs_bufatoff(dp, (off_t)(dp->i_offset - dp->i_count), 921 (char **)&ep, &bp); 922 if (error != 0) 923 return (error); 924 ep->e2d_reclen = htole16(letoh16(ep->e2d_reclen) + dp->i_reclen); 925 error = VOP_BWRITE(bp); 926 dp->i_flag |= IN_CHANGE | IN_UPDATE; 927 return (error); 928 } 929 930 /* 931 * Rewrite an existing directory entry to point at the inode 932 * supplied. The parameters describing the directory entry are 933 * set up by a call to namei. 934 */ 935 int 936 ext2fs_dirrewrite(struct inode *dp, struct inode *ip, 937 struct componentname *cnp) 938 { 939 struct buf *bp; 940 struct ext2fs_direct *ep; 941 int error; 942 943 error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, (char **)&ep, &bp); 944 if (error != 0) 945 return (error); 946 ep->e2d_ino = htole32(ip->i_number); 947 if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 && 948 (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) { 949 ep->e2d_type = inot2ext2dt(IFTODT(ip->i_e2fs_mode)); 950 } else { 951 ep->e2d_type = 0; 952 } 953 error = VOP_BWRITE(bp); 954 dp->i_flag |= IN_CHANGE | IN_UPDATE; 955 return (error); 956 } 957 958 /* 959 * Check if a directory is empty or not. 960 * Inode supplied must be locked. 961 * 962 * Using a struct dirtemplate here is not precisely 963 * what we want, but better than using a struct ext2fs_direct. 964 * 965 * NB: does not handle corrupted directories. 966 */ 967 int 968 ext2fs_dirempty(struct inode *ip, ufsino_t parentino, struct ucred *cred) 969 { 970 off_t off; 971 struct ext2fs_dirtemplate dbuf; 972 struct ext2fs_direct *dp = (struct ext2fs_direct *)&dbuf; 973 int error, namlen; 974 size_t count; 975 976 #define MINDIRSIZ (sizeof (struct ext2fs_dirtemplate) / 2) 977 978 for (off = 0; off < ext2fs_size(ip); off += letoh16(dp->e2d_reclen)) { 979 error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off, 980 UIO_SYSSPACE, IO_NODELOCKED, cred, &count, curproc); 981 /* 982 * Since we read MINDIRSIZ, residual must 983 * be 0 unless we're at end of file. 984 */ 985 if (error || count != 0) 986 return (0); 987 /* avoid infinite loops */ 988 if (dp->e2d_reclen == 0) 989 return (0); 990 /* skip empty entries */ 991 if (dp->e2d_ino == 0) 992 continue; 993 /* accept only "." and ".." */ 994 namlen = dp->e2d_namlen; 995 if (namlen > 2) 996 return (0); 997 if (dp->e2d_name[0] != '.') 998 return (0); 999 /* 1000 * At this point namlen must be 1 or 2. 1001 * 1 implies ".", 2 implies ".." if second 1002 * char is also "." 1003 */ 1004 if (namlen == 1) 1005 continue; 1006 if (dp->e2d_name[1] == '.' && letoh32(dp->e2d_ino) == parentino) 1007 continue; 1008 return (0); 1009 } 1010 return (1); 1011 } 1012 1013 /* 1014 * Check if source directory is in the path of the target directory. 1015 * Target is supplied locked, source is unlocked. 1016 * The target is always vput before returning. 1017 */ 1018 int 1019 ext2fs_checkpath(struct inode *source, struct inode *target, 1020 struct ucred *cred) 1021 { 1022 struct vnode *vp; 1023 int error, rootino, namlen; 1024 struct ext2fs_dirtemplate dirbuf; 1025 u_int32_t ino; 1026 1027 vp = ITOV(target); 1028 if (target->i_number == source->i_number) { 1029 error = EEXIST; 1030 goto out; 1031 } 1032 rootino = ROOTINO; 1033 error = 0; 1034 if (target->i_number == rootino) 1035 goto out; 1036 1037 for (;;) { 1038 if (vp->v_type != VDIR) { 1039 error = ENOTDIR; 1040 break; 1041 } 1042 error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf, 1043 sizeof (struct ext2fs_dirtemplate), (off_t)0, 1044 UIO_SYSSPACE, IO_NODELOCKED, cred, NULL, 1045 curproc); 1046 if (error != 0) 1047 break; 1048 namlen = dirbuf.dotdot_namlen; 1049 if (namlen != 2 || 1050 dirbuf.dotdot_name[0] != '.' || 1051 dirbuf.dotdot_name[1] != '.') { 1052 error = ENOTDIR; 1053 break; 1054 } 1055 ino = letoh32(dirbuf.dotdot_ino); 1056 if (ino == source->i_number) { 1057 error = EINVAL; 1058 break; 1059 } 1060 if (ino == rootino) 1061 break; 1062 vput(vp); 1063 error = VFS_VGET(vp->v_mount, ino, &vp); 1064 if (error != 0) { 1065 vp = NULL; 1066 break; 1067 } 1068 } 1069 1070 out: 1071 if (error == ENOTDIR) { 1072 printf("checkpath: .. not a directory\n"); 1073 panic("checkpath"); 1074 } 1075 if (vp != NULL) 1076 vput(vp); 1077 return (error); 1078 } 1079