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