1 /*- 2 * modified for Lites 1.1 3 * 4 * Aug 1995, Godmar Back (gback@cs.utah.edu) 5 * University of Utah, Department of Computer Science 6 */ 7 /*- 8 * SPDX-License-Identifier: BSD-3-Clause 9 * 10 * Copyright (c) 1989, 1993 11 * The Regents of the University of California. All rights reserved. 12 * (c) UNIX System Laboratories, Inc. 13 * All or some portions of this file are derived from material licensed 14 * to the University of California by American Telephone and Telegraph 15 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 16 * the permission of UNIX System Laboratories, Inc. 17 * 18 * Redistribution and use in source and binary forms, with or without 19 * modification, are permitted provided that the following conditions 20 * are met: 21 * 1. Redistributions of source code must retain the above copyright 22 * notice, this list of conditions and the following disclaimer. 23 * 2. Redistributions in binary form must reproduce the above copyright 24 * notice, this list of conditions and the following disclaimer in the 25 * documentation and/or other materials provided with the distribution. 26 * 3. Neither the name of the University nor the names of its contributors 27 * may be used to endorse or promote products derived from this software 28 * without specific prior written permission. 29 * 30 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 * SUCH DAMAGE. 41 * 42 * @(#)ufs_lookup.c 8.6 (Berkeley) 4/1/94 43 */ 44 45 #include <sys/param.h> 46 #include <sys/systm.h> 47 #include <sys/namei.h> 48 #include <sys/bio.h> 49 #include <sys/buf.h> 50 #include <sys/endian.h> 51 #include <sys/mount.h> 52 #include <sys/vnode.h> 53 #include <sys/malloc.h> 54 #include <sys/dirent.h> 55 #include <sys/sdt.h> 56 #include <sys/sysctl.h> 57 58 #include <ufs/ufs/dir.h> 59 60 #include <fs/ext2fs/fs.h> 61 #include <fs/ext2fs/inode.h> 62 #include <fs/ext2fs/ext2_mount.h> 63 #include <fs/ext2fs/ext2fs.h> 64 #include <fs/ext2fs/ext2_dinode.h> 65 #include <fs/ext2fs/ext2_dir.h> 66 #include <fs/ext2fs/ext2_extern.h> 67 #include <fs/ext2fs/fs.h> 68 69 SDT_PROVIDER_DECLARE(ext2fs); 70 /* 71 * ext2fs trace probe: 72 * arg0: verbosity. Higher numbers give more verbose messages 73 * arg1: Textual message 74 */ 75 SDT_PROBE_DEFINE2(ext2fs, , lookup, trace, "int", "char*"); 76 SDT_PROBE_DEFINE4(ext2fs, , trace, ext2_dirbad_error, 77 "char*", "ino_t", "doff_t", "char*"); 78 SDT_PROBE_DEFINE5(ext2fs, , trace, ext2_dirbadentry_error, 79 "char*", "int", "uint32_t", "uint16_t", "uint8_t"); 80 81 static SYSCTL_NODE(_vfs, OID_AUTO, e2fs, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 82 "EXT2FS filesystem"); 83 84 /* 85 DIRBLKSIZE in ffs is DEV_BSIZE (in most cases 512) 86 while it is the native blocksize in ext2fs - thus, a #define 87 is no longer appropriate 88 */ 89 #undef DIRBLKSIZ 90 91 static u_char ext2_ft_to_dt[] = { 92 DT_UNKNOWN, /* EXT2_FT_UNKNOWN */ 93 DT_REG, /* EXT2_FT_REG_FILE */ 94 DT_DIR, /* EXT2_FT_DIR */ 95 DT_CHR, /* EXT2_FT_CHRDEV */ 96 DT_BLK, /* EXT2_FT_BLKDEV */ 97 DT_FIFO, /* EXT2_FT_FIFO */ 98 DT_SOCK, /* EXT2_FT_SOCK */ 99 DT_LNK, /* EXT2_FT_SYMLINK */ 100 }; 101 #define FTTODT(ft) \ 102 ((ft) < nitems(ext2_ft_to_dt) ? ext2_ft_to_dt[(ft)] : DT_UNKNOWN) 103 104 static u_char dt_to_ext2_ft[] = { 105 EXT2_FT_UNKNOWN, /* DT_UNKNOWN */ 106 EXT2_FT_FIFO, /* DT_FIFO */ 107 EXT2_FT_CHRDEV, /* DT_CHR */ 108 EXT2_FT_UNKNOWN, /* unused */ 109 EXT2_FT_DIR, /* DT_DIR */ 110 EXT2_FT_UNKNOWN, /* unused */ 111 EXT2_FT_BLKDEV, /* DT_BLK */ 112 EXT2_FT_UNKNOWN, /* unused */ 113 EXT2_FT_REG_FILE, /* DT_REG */ 114 EXT2_FT_UNKNOWN, /* unused */ 115 EXT2_FT_SYMLINK, /* DT_LNK */ 116 EXT2_FT_UNKNOWN, /* unused */ 117 EXT2_FT_SOCK, /* DT_SOCK */ 118 EXT2_FT_UNKNOWN, /* unused */ 119 EXT2_FT_UNKNOWN, /* DT_WHT */ 120 }; 121 #define DTTOFT(dt) \ 122 ((dt) < nitems(dt_to_ext2_ft) ? dt_to_ext2_ft[(dt)] : EXT2_FT_UNKNOWN) 123 124 static int ext2_check_direntry(struct vnode *dp, 125 struct ext2fs_direct_2 *de, int entryoffsetinblock); 126 static int ext2_is_dot_entry(struct componentname *cnp); 127 static int ext2_lookup_ino(struct vnode *vdp, struct vnode **vpp, 128 struct componentname *cnp, ino_t *dd_ino); 129 130 static int 131 ext2_is_dot_entry(struct componentname *cnp) 132 { 133 if (cnp->cn_namelen <= 2 && cnp->cn_nameptr[0] == '.' && 134 (cnp->cn_nameptr[1] == '.' || cnp->cn_nameptr[1] == '\0')) 135 return (1); 136 return (0); 137 } 138 139 /* 140 * Vnode op for reading directories. 141 */ 142 int 143 ext2_readdir(struct vop_readdir_args *ap) 144 { 145 struct vnode *vp = ap->a_vp; 146 struct uio *uio = ap->a_uio; 147 struct buf *bp; 148 struct inode *ip; 149 struct ext2fs_direct_2 *dp, *edp; 150 uint64_t *cookies; 151 struct dirent dstdp; 152 off_t offset, startoffset; 153 size_t readcnt, skipcnt; 154 ssize_t startresid; 155 u_int ncookies; 156 int DIRBLKSIZ = VTOI(ap->a_vp)->i_e2fs->e2fs_bsize; 157 int error; 158 159 if (uio->uio_offset < 0) 160 return (EINVAL); 161 ip = VTOI(vp); 162 if (ap->a_ncookies != NULL) { 163 if (uio->uio_resid < 0) 164 ncookies = 0; 165 else 166 ncookies = uio->uio_resid; 167 if (uio->uio_offset >= ip->i_size) 168 ncookies = 0; 169 else if (ip->i_size - uio->uio_offset < ncookies) 170 ncookies = ip->i_size - uio->uio_offset; 171 ncookies = ncookies / (offsetof(struct ext2fs_direct_2, 172 e2d_namlen) + 4) + 1; 173 cookies = malloc(ncookies * sizeof(*cookies), M_TEMP, M_WAITOK); 174 *ap->a_ncookies = ncookies; 175 *ap->a_cookies = cookies; 176 } else { 177 ncookies = 0; 178 cookies = NULL; 179 } 180 offset = startoffset = uio->uio_offset; 181 startresid = uio->uio_resid; 182 error = 0; 183 while (error == 0 && uio->uio_resid > 0 && 184 uio->uio_offset < ip->i_size) { 185 error = ext2_blkatoff(vp, uio->uio_offset, NULL, &bp); 186 if (error) 187 break; 188 if (bp->b_offset + bp->b_bcount > ip->i_size) 189 readcnt = ip->i_size - bp->b_offset; 190 else 191 readcnt = bp->b_bcount; 192 skipcnt = (size_t)(uio->uio_offset - bp->b_offset) & 193 ~(size_t)(DIRBLKSIZ - 1); 194 offset = bp->b_offset + skipcnt; 195 dp = (struct ext2fs_direct_2 *)&bp->b_data[skipcnt]; 196 edp = (struct ext2fs_direct_2 *)&bp->b_data[readcnt]; 197 while (error == 0 && uio->uio_resid > 0 && dp < edp) { 198 if (le16toh(dp->e2d_reclen) <= offsetof(struct ext2fs_direct_2, 199 e2d_namlen) || (caddr_t)dp + le16toh(dp->e2d_reclen) > 200 (caddr_t)edp) { 201 error = EIO; 202 break; 203 } 204 /*- 205 * "New" ext2fs directory entries differ in 3 ways 206 * from ufs on-disk ones: 207 * - the name is not necessarily NUL-terminated. 208 * - the file type field always exists and always 209 * follows the name length field. 210 * - the file type is encoded in a different way. 211 * 212 * "Old" ext2fs directory entries need no special 213 * conversions, since they are binary compatible 214 * with "new" entries having a file type of 0 (i.e., 215 * EXT2_FT_UNKNOWN). Splitting the old name length 216 * field didn't make a mess like it did in ufs, 217 * because ext2fs uses a machine-independent disk 218 * layout. 219 */ 220 dstdp.d_namlen = dp->e2d_namlen; 221 dstdp.d_type = FTTODT(dp->e2d_type); 222 if (offsetof(struct ext2fs_direct_2, e2d_namlen) + 223 dstdp.d_namlen > le16toh(dp->e2d_reclen)) { 224 error = EIO; 225 break; 226 } 227 if (offset < startoffset || le32toh(dp->e2d_ino) == 0) 228 goto nextentry; 229 dstdp.d_fileno = le32toh(dp->e2d_ino); 230 dstdp.d_reclen = GENERIC_DIRSIZ(&dstdp); 231 bcopy(dp->e2d_name, dstdp.d_name, dstdp.d_namlen); 232 /* NOTE: d_off is the offset of the *next* entry. */ 233 dstdp.d_off = offset + le16toh(dp->e2d_reclen); 234 dirent_terminate(&dstdp); 235 if (dstdp.d_reclen > uio->uio_resid) { 236 if (uio->uio_resid == startresid) 237 error = EINVAL; 238 else 239 error = EJUSTRETURN; 240 break; 241 } 242 /* Advance dp. */ 243 error = uiomove((caddr_t)&dstdp, dstdp.d_reclen, uio); 244 if (error) 245 break; 246 if (cookies != NULL) { 247 KASSERT(ncookies > 0, 248 ("ext2_readdir: cookies buffer too small")); 249 *cookies = offset + le16toh(dp->e2d_reclen); 250 cookies++; 251 ncookies--; 252 } 253 nextentry: 254 offset += le16toh(dp->e2d_reclen); 255 dp = (struct ext2fs_direct_2 *)((caddr_t)dp + 256 le16toh(dp->e2d_reclen)); 257 } 258 bqrelse(bp); 259 uio->uio_offset = offset; 260 } 261 /* We need to correct uio_offset. */ 262 uio->uio_offset = offset; 263 if (error == EJUSTRETURN) 264 error = 0; 265 if (ap->a_ncookies != NULL) { 266 if (error == 0) { 267 *ap->a_ncookies -= ncookies; 268 } else { 269 free(*ap->a_cookies, M_TEMP); 270 *ap->a_ncookies = 0; 271 *ap->a_cookies = NULL; 272 } 273 } 274 if (error == 0 && ap->a_eofflag) 275 *ap->a_eofflag = ip->i_size <= uio->uio_offset; 276 return (error); 277 } 278 279 /* 280 * Convert a component of a pathname into a pointer to a locked inode. 281 * This is a very central and rather complicated routine. 282 * If the file system is not maintained in a strict tree hierarchy, 283 * this can result in a deadlock situation (see comments in code below). 284 * 285 * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending 286 * on whether the name is to be looked up, created, renamed, or deleted. 287 * When CREATE, RENAME, or DELETE is specified, information usable in 288 * creating, renaming, or deleting a directory entry may be calculated. 289 * If flag has LOCKPARENT or'ed into it and the target of the pathname 290 * exists, lookup returns both the target and its parent directory locked. 291 * When creating or renaming and LOCKPARENT is specified, the target may 292 * not be ".". When deleting and LOCKPARENT is specified, the target may 293 * be "."., but the caller must check to ensure it does an vrele and vput 294 * instead of two vputs. 295 * 296 * Overall outline of ext2_lookup: 297 * 298 * search for name in directory, to found or notfound 299 * notfound: 300 * if creating, return locked directory, leaving info on available slots 301 * else return error 302 * found: 303 * if at end of path and deleting, return information to allow delete 304 * if at end of path and rewriting (RENAME and LOCKPARENT), lock target 305 * inode and return info to allow rewrite 306 * if not at end, add name to cache; if at end and neither creating 307 * nor deleting, add name to cache 308 */ 309 int 310 ext2_lookup(struct vop_cachedlookup_args *ap) 311 { 312 313 return (ext2_lookup_ino(ap->a_dvp, ap->a_vpp, ap->a_cnp, NULL)); 314 } 315 316 static int 317 ext2_lookup_ino(struct vnode *vdp, struct vnode **vpp, struct componentname *cnp, 318 ino_t *dd_ino) 319 { 320 struct inode *dp; /* inode for directory being searched */ 321 struct buf *bp; /* a buffer of directory entries */ 322 struct ext2fs_direct_2 *ep; /* the current directory entry */ 323 int entryoffsetinblock; /* offset of ep in bp's buffer */ 324 struct ext2fs_searchslot ss; 325 doff_t i_diroff; /* cached i_diroff value */ 326 doff_t i_offset; /* cached i_offset value */ 327 int numdirpasses; /* strategy for directory search */ 328 doff_t endsearch; /* offset to end directory search */ 329 doff_t prevoff; /* prev entry dp->i_offset */ 330 struct vnode *pdp; /* saved dp during symlink work */ 331 struct vnode *tdp; /* returned by VFS_VGET */ 332 doff_t enduseful; /* pointer past last used dir slot */ 333 u_long bmask; /* block offset mask */ 334 int error; 335 struct ucred *cred = cnp->cn_cred; 336 int flags = cnp->cn_flags; 337 int nameiop = cnp->cn_nameiop; 338 ino_t ino, ino1; 339 int ltype; 340 int entry_found = 0; 341 342 int DIRBLKSIZ = VTOI(vdp)->i_e2fs->e2fs_bsize; 343 344 if (vpp != NULL) 345 *vpp = NULL; 346 347 dp = VTOI(vdp); 348 bmask = VFSTOEXT2(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1; 349 restart: 350 bp = NULL; 351 ss.slotoffset = -1; 352 353 /* 354 * We now have a segment name to search for, and a directory to search. 355 * 356 * Suppress search for slots unless creating 357 * file and at end of pathname, in which case 358 * we watch for a place to put the new file in 359 * case it doesn't already exist. 360 */ 361 i_diroff = dp->i_diroff; 362 ss.slotstatus = FOUND; 363 ss.slotfreespace = ss.slotsize = ss.slotneeded = 0; 364 if ((nameiop == CREATE || nameiop == RENAME) && 365 (flags & ISLASTCN)) { 366 ss.slotstatus = NONE; 367 ss.slotneeded = EXT2_DIR_REC_LEN(cnp->cn_namelen); 368 /* 369 * was ss.slotneeded = (sizeof(struct direct) - MAXNAMLEN + 370 * cnp->cn_namelen + 3) &~ 3; 371 */ 372 } 373 /* 374 * Try to lookup dir entry using htree directory index. 375 * 376 * If we got an error or we want to find '.' or '..' entry, 377 * we will fall back to linear search. 378 */ 379 if (!ext2_is_dot_entry(cnp) && ext2_htree_has_idx(dp)) { 380 numdirpasses = 1; 381 entryoffsetinblock = 0; 382 switch (ext2_htree_lookup(dp, cnp->cn_nameptr, cnp->cn_namelen, 383 &bp, &entryoffsetinblock, &i_offset, &prevoff, 384 &enduseful, &ss)) { 385 case 0: 386 ep = (struct ext2fs_direct_2 *)((char *)bp->b_data + 387 (i_offset & bmask)); 388 goto foundentry; 389 case ENOENT: 390 i_offset = roundup2(dp->i_size, DIRBLKSIZ); 391 goto notfound; 392 default: 393 /* 394 * Something failed; just fallback to do a linear 395 * search. 396 */ 397 break; 398 } 399 } 400 401 /* 402 * If there is cached information on a previous search of 403 * this directory, pick up where we last left off. 404 * We cache only lookups as these are the most common 405 * and have the greatest payoff. Caching CREATE has little 406 * benefit as it usually must search the entire directory 407 * to determine that the entry does not exist. Caching the 408 * location of the last DELETE or RENAME has not reduced 409 * profiling time and hence has been removed in the interest 410 * of simplicity. 411 */ 412 if (nameiop != LOOKUP || i_diroff == 0 || 413 i_diroff > dp->i_size) { 414 entryoffsetinblock = 0; 415 i_offset = 0; 416 numdirpasses = 1; 417 } else { 418 i_offset = i_diroff; 419 if ((entryoffsetinblock = i_offset & bmask) && 420 (error = ext2_blkatoff(vdp, (off_t)i_offset, NULL, 421 &bp))) 422 return (error); 423 numdirpasses = 2; 424 nchstats.ncs_2passes++; 425 } 426 prevoff = i_offset; 427 endsearch = roundup2(dp->i_size, DIRBLKSIZ); 428 enduseful = 0; 429 430 searchloop: 431 while (i_offset < endsearch) { 432 /* 433 * If necessary, get the next directory block. 434 */ 435 if (bp != NULL) 436 brelse(bp); 437 error = ext2_blkatoff(vdp, (off_t)i_offset, NULL, &bp); 438 if (error != 0) 439 return (error); 440 441 entryoffsetinblock = 0; 442 if (ss.slotstatus == NONE) { 443 ss.slotoffset = -1; 444 ss.slotfreespace = 0; 445 } 446 447 error = ext2_search_dirblock(dp, bp->b_data, &entry_found, 448 cnp->cn_nameptr, cnp->cn_namelen, 449 &entryoffsetinblock, &i_offset, &prevoff, 450 &enduseful, &ss); 451 if (error != 0) { 452 brelse(bp); 453 return (error); 454 } 455 if (entry_found) { 456 ep = (struct ext2fs_direct_2 *)((char *)bp->b_data + 457 (entryoffsetinblock & bmask)); 458 foundentry: 459 ino = le32toh(ep->e2d_ino); 460 goto found; 461 } 462 } 463 notfound: 464 /* 465 * If we started in the middle of the directory and failed 466 * to find our target, we must check the beginning as well. 467 */ 468 if (numdirpasses == 2) { 469 numdirpasses--; 470 i_offset = 0; 471 endsearch = i_diroff; 472 goto searchloop; 473 } 474 if (bp != NULL) 475 brelse(bp); 476 /* 477 * If creating, and at end of pathname and current 478 * directory has not been removed, then can consider 479 * allowing file to be created. 480 */ 481 if ((nameiop == CREATE || nameiop == RENAME) && 482 (flags & ISLASTCN) && dp->i_nlink != 0) { 483 /* 484 * Access for write is interpreted as allowing 485 * creation of files in the directory. 486 */ 487 if ((error = VOP_ACCESS(vdp, VWRITE, cred, curthread)) != 0) 488 return (error); 489 /* 490 * Return an indication of where the new directory 491 * entry should be put. If we didn't find a slot, 492 * then set dp->i_count to 0 indicating 493 * that the new slot belongs at the end of the 494 * directory. If we found a slot, then the new entry 495 * can be put in the range from dp->i_offset to 496 * dp->i_offset + dp->i_count. 497 */ 498 if (ss.slotstatus == NONE) { 499 dp->i_offset = roundup2(dp->i_size, DIRBLKSIZ); 500 dp->i_count = 0; 501 enduseful = dp->i_offset; 502 } else { 503 dp->i_offset = ss.slotoffset; 504 dp->i_count = ss.slotsize; 505 if (enduseful < ss.slotoffset + ss.slotsize) 506 enduseful = ss.slotoffset + ss.slotsize; 507 } 508 dp->i_endoff = roundup2(enduseful, DIRBLKSIZ); 509 /* 510 * We return with the directory locked, so that 511 * the parameters we set up above will still be 512 * valid if we actually decide to do a direnter(). 513 * We return ni_vp == NULL to indicate that the entry 514 * does not currently exist; we leave a pointer to 515 * the (locked) directory inode in ndp->ni_dvp. 516 * 517 * NB - if the directory is unlocked, then this 518 * information cannot be used. 519 */ 520 return (EJUSTRETURN); 521 } 522 /* 523 * Insert name into cache (as non-existent) if appropriate. 524 */ 525 if ((cnp->cn_flags & MAKEENTRY) != 0) 526 cache_enter(vdp, NULL, cnp); 527 return (ENOENT); 528 529 found: 530 if (dd_ino != NULL) 531 *dd_ino = ino; 532 if (numdirpasses == 2) 533 nchstats.ncs_pass2++; 534 /* 535 * Check that directory length properly reflects presence 536 * of this entry. 537 */ 538 if (entryoffsetinblock + EXT2_DIR_REC_LEN(ep->e2d_namlen) > 539 dp->i_size) { 540 ext2_dirbad(dp, i_offset, "i_size too small"); 541 brelse(bp); 542 return (EIO); 543 } 544 brelse(bp); 545 546 /* 547 * Found component in pathname. 548 * If the final component of path name, save information 549 * in the cache as to where the entry was found. 550 */ 551 if ((flags & ISLASTCN) && nameiop == LOOKUP) 552 dp->i_diroff = rounddown2(i_offset, DIRBLKSIZ); 553 /* 554 * If deleting, and at end of pathname, return 555 * parameters which can be used to remove file. 556 */ 557 if (nameiop == DELETE && (flags & ISLASTCN)) { 558 if (flags & LOCKPARENT) 559 ASSERT_VOP_ELOCKED(vdp, __FUNCTION__); 560 /* 561 * Write access to directory required to delete files. 562 */ 563 if ((error = VOP_ACCESS(vdp, VWRITE, cred, curthread)) != 0) 564 return (error); 565 /* 566 * Return pointer to current entry in dp->i_offset, 567 * and distance past previous entry (if there 568 * is a previous entry in this block) in dp->i_count. 569 * Save directory inode pointer in ndp->ni_dvp for dirremove(). 570 * 571 * Technically we shouldn't be setting these in the 572 * WANTPARENT case (first lookup in rename()), but any 573 * lookups that will result in directory changes will 574 * overwrite these. 575 */ 576 dp->i_offset = i_offset; 577 if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0) 578 dp->i_count = 0; 579 else 580 dp->i_count = dp->i_offset - prevoff; 581 if (dd_ino != NULL) 582 return (0); 583 if (dp->i_number == ino) { 584 VREF(vdp); 585 *vpp = vdp; 586 return (0); 587 } 588 if ((error = VFS_VGET(vdp->v_mount, ino, LK_EXCLUSIVE, 589 &tdp)) != 0) 590 return (error); 591 /* 592 * If directory is "sticky", then user must own 593 * the directory, or the file in it, else she 594 * may not delete it (unless she's root). This 595 * implements append-only directories. 596 */ 597 if ((dp->i_mode & ISVTX) && 598 cred->cr_uid != 0 && 599 cred->cr_uid != dp->i_uid && 600 VTOI(tdp)->i_uid != cred->cr_uid) { 601 vput(tdp); 602 return (EPERM); 603 } 604 *vpp = tdp; 605 return (0); 606 } 607 608 /* 609 * If rewriting (RENAME), return the inode and the 610 * information required to rewrite the present directory 611 * Must get inode of directory entry to verify it's a 612 * regular file, or empty directory. 613 */ 614 if (nameiop == RENAME && (flags & ISLASTCN)) { 615 if ((error = VOP_ACCESS(vdp, VWRITE, cred, curthread)) != 0) 616 return (error); 617 /* 618 * Careful about locking second inode. 619 * This can only occur if the target is ".". 620 */ 621 dp->i_offset = i_offset; 622 if (dp->i_number == ino) 623 return (EISDIR); 624 if (dd_ino != NULL) 625 return (0); 626 if ((error = VFS_VGET(vdp->v_mount, ino, LK_EXCLUSIVE, 627 &tdp)) != 0) 628 return (error); 629 *vpp = tdp; 630 return (0); 631 } 632 if (dd_ino != NULL) 633 return (0); 634 635 /* 636 * Step through the translation in the name. We do not `vput' the 637 * directory because we may need it again if a symbolic link 638 * is relative to the current directory. Instead we save it 639 * unlocked as "pdp". We must get the target inode before unlocking 640 * the directory to insure that the inode will not be removed 641 * before we get it. We prevent deadlock by always fetching 642 * inodes from the root, moving down the directory tree. Thus 643 * when following backward pointers ".." we must unlock the 644 * parent directory before getting the requested directory. 645 * There is a potential race condition here if both the current 646 * and parent directories are removed before the VFS_VGET for the 647 * inode associated with ".." returns. We hope that this occurs 648 * infrequently since we cannot avoid this race condition without 649 * implementing a sophisticated deadlock detection algorithm. 650 * Note also that this simple deadlock detection scheme will not 651 * work if the file system has any hard links other than ".." 652 * that point backwards in the directory structure. 653 */ 654 pdp = vdp; 655 if (flags & ISDOTDOT) { 656 error = vn_vget_ino(pdp, ino, cnp->cn_lkflags, &tdp); 657 if (VN_IS_DOOMED(pdp)) { 658 if (error == 0) 659 vput(tdp); 660 error = ENOENT; 661 } 662 if (error) 663 return (error); 664 /* 665 * Recheck that ".." entry in the vdp directory points 666 * to the inode we looked up before vdp lock was 667 * dropped. 668 */ 669 error = ext2_lookup_ino(pdp, NULL, cnp, &ino1); 670 if (error) { 671 vput(tdp); 672 return (error); 673 } 674 if (ino1 != ino) { 675 vput(tdp); 676 goto restart; 677 } 678 *vpp = tdp; 679 } else if (dp->i_number == ino) { 680 VREF(vdp); /* we want ourself, ie "." */ 681 /* 682 * When we lookup "." we still can be asked to lock it 683 * differently. 684 */ 685 ltype = cnp->cn_lkflags & LK_TYPE_MASK; 686 if (ltype != VOP_ISLOCKED(vdp)) { 687 if (ltype == LK_EXCLUSIVE) 688 vn_lock(vdp, LK_UPGRADE | LK_RETRY); 689 else /* if (ltype == LK_SHARED) */ 690 vn_lock(vdp, LK_DOWNGRADE | LK_RETRY); 691 } 692 *vpp = vdp; 693 } else { 694 if ((error = VFS_VGET(vdp->v_mount, ino, cnp->cn_lkflags, 695 &tdp)) != 0) 696 return (error); 697 *vpp = tdp; 698 } 699 700 /* 701 * Insert name into cache if appropriate. 702 */ 703 if (cnp->cn_flags & MAKEENTRY) 704 cache_enter(vdp, *vpp, cnp); 705 return (0); 706 } 707 708 int 709 ext2_search_dirblock(struct inode *ip, void *data, int *foundp, 710 const char *name, int namelen, int *entryoffsetinblockp, 711 doff_t *offp, doff_t *prevoffp, doff_t *endusefulp, 712 struct ext2fs_searchslot *ssp) 713 { 714 struct vnode *vdp; 715 struct ext2fs_direct_2 *ep, *top; 716 uint32_t bsize = ip->i_e2fs->e2fs_bsize; 717 int offset = *entryoffsetinblockp; 718 int namlen; 719 720 vdp = ITOV(ip); 721 722 ep = (struct ext2fs_direct_2 *)((char *)data + offset); 723 top = (struct ext2fs_direct_2 *)((char *)data + bsize); 724 while (ep < top) { 725 if (ext2_check_direntry(vdp, ep, offset)) { 726 int i; 727 728 ext2_dirbad(ip, *offp, "mangled entry"); 729 i = bsize - (offset & (bsize - 1)); 730 *offp += i; 731 offset += i; 732 ep = (struct ext2fs_direct_2 *)((char *)data + offset); 733 continue; 734 } 735 736 /* 737 * If an appropriate sized slot has not yet been found, 738 * check to see if one is available. Also accumulate space 739 * in the current block so that we can determine if 740 * compaction is viable. 741 */ 742 if (ssp->slotstatus != FOUND) { 743 int size = le16toh(ep->e2d_reclen); 744 745 if (ep->e2d_ino != 0) 746 size -= EXT2_DIR_REC_LEN(ep->e2d_namlen); 747 else if (ext2_is_dirent_tail(ip, ep)) 748 size -= sizeof(struct ext2fs_direct_tail); 749 if (size > 0) { 750 if (size >= ssp->slotneeded) { 751 ssp->slotstatus = FOUND; 752 ssp->slotoffset = *offp; 753 ssp->slotsize = le16toh(ep->e2d_reclen); 754 } else if (ssp->slotstatus == NONE) { 755 ssp->slotfreespace += size; 756 if (ssp->slotoffset == -1) 757 ssp->slotoffset = *offp; 758 if (ssp->slotfreespace >= ssp->slotneeded) { 759 ssp->slotstatus = COMPACT; 760 ssp->slotsize = *offp + 761 le16toh(ep->e2d_reclen) - 762 ssp->slotoffset; 763 } 764 } 765 } 766 } 767 /* 768 * Check for a name match. 769 */ 770 if (ep->e2d_ino != 0) { 771 namlen = ep->e2d_namlen; 772 if (namlen == namelen && 773 !bcmp(name, ep->e2d_name, (unsigned)namlen)) { 774 /* 775 * Save directory entry's inode number and 776 * reclen in ndp->ni_ufs area, and release 777 * directory buffer. 778 */ 779 *foundp = 1; 780 return (0); 781 } 782 } 783 *prevoffp = *offp; 784 *offp += le16toh(ep->e2d_reclen); 785 offset += le16toh(ep->e2d_reclen); 786 *entryoffsetinblockp = offset; 787 if (ep->e2d_ino != 0) 788 *endusefulp = *offp; 789 /* 790 * Get pointer to the next entry. 791 */ 792 ep = (struct ext2fs_direct_2 *)((char *)data + offset); 793 } 794 795 return (0); 796 } 797 798 void 799 ext2_dirbad(struct inode *ip, doff_t offset, char *how) 800 { 801 802 SDT_PROBE4(ext2fs, , trace, ext2_dirbad_error, 803 ITOV(ip)->v_mount->mnt_stat.f_mntonname, ip->i_number, offset, how); 804 } 805 806 /* 807 * Do consistency checking on a directory entry: 808 * record length must be multiple of 4 809 * entry must fit in rest of its DIRBLKSIZ block 810 * record must be large enough to contain entry 811 * name is not longer than MAXNAMLEN 812 * name must be as long as advertised, and null terminated 813 * inode number less then inode count 814 * if root inode entry, it have correct name 815 */ 816 static int 817 ext2_check_direntry(struct vnode *dp, struct ext2fs_direct_2 *de, 818 int entryoffsetinblock) 819 { 820 struct m_ext2fs *fs = VTOI(dp)->i_e2fs; 821 char *error_msg = NULL; 822 823 if (le16toh(de->e2d_reclen) < EXT2_DIR_REC_LEN(1)) 824 error_msg = "rec_len is smaller than minimal"; 825 else if (le16toh(de->e2d_reclen) % 4 != 0) 826 error_msg = "rec_len % 4 != 0"; 827 else if (le16toh(de->e2d_reclen) < EXT2_DIR_REC_LEN(de->e2d_namlen)) 828 error_msg = "reclen is too small for name_len"; 829 else if (entryoffsetinblock + le16toh(de->e2d_reclen)> fs->e2fs_bsize) 830 error_msg = "directory entry across blocks"; 831 else if (le32toh(de->e2d_ino) > fs->e2fs->e2fs_icount) 832 error_msg = "directory entry inode out of bounds"; 833 else if (le32toh(de->e2d_ino) == EXT2_ROOTINO && 834 ((de->e2d_namlen != 1 && de->e2d_namlen != 2) || 835 (de->e2d_name[0] != '.') || 836 (de->e2d_namlen == 2 && de->e2d_name[1] != '.'))) 837 error_msg = "bad root directory entry"; 838 839 if (error_msg != NULL) { 840 SDT_PROBE5(ext2fs, , trace, ext2_dirbadentry_error, 841 error_msg, entryoffsetinblock, 842 le32toh(de->e2d_ino), le16toh(de->e2d_reclen), 843 de->e2d_namlen); 844 } 845 return (error_msg == NULL ? 0 : EINVAL); 846 } 847 848 /* 849 * Insert an entry into the fresh directory block. 850 * Initialize entry tail if the metadata_csum feature is turned on. 851 */ 852 static int 853 ext2_add_first_entry(struct vnode *dvp, struct ext2fs_direct_2 *entry, 854 struct componentname *cnp) 855 { 856 struct inode *dp; 857 struct iovec aiov; 858 struct uio auio; 859 char* buf = NULL; 860 int dirblksize, error; 861 862 dp = VTOI(dvp); 863 dirblksize = dp->i_e2fs->e2fs_bsize; 864 865 if (dp->i_offset & (dirblksize - 1)) 866 panic("ext2_add_first_entry: bad directory offset"); 867 868 if (EXT2_HAS_RO_COMPAT_FEATURE(dp->i_e2fs, 869 EXT2F_ROCOMPAT_METADATA_CKSUM)) { 870 entry->e2d_reclen = htole16(dirblksize - 871 sizeof(struct ext2fs_direct_tail)); 872 buf = malloc(dirblksize, M_TEMP, M_WAITOK); 873 memcpy(buf, entry, EXT2_DIR_REC_LEN(entry->e2d_namlen)); 874 ext2_init_dirent_tail(EXT2_DIRENT_TAIL(buf, dirblksize)); 875 ext2_dirent_csum_set(dp, (struct ext2fs_direct_2 *)buf); 876 877 auio.uio_offset = dp->i_offset; 878 auio.uio_resid = dirblksize; 879 aiov.iov_len = auio.uio_resid; 880 aiov.iov_base = (caddr_t)buf; 881 } else { 882 entry->e2d_reclen = htole16(dirblksize); 883 auio.uio_offset = dp->i_offset; 884 auio.uio_resid = EXT2_DIR_REC_LEN(entry->e2d_namlen); 885 aiov.iov_len = auio.uio_resid; 886 aiov.iov_base = (caddr_t)entry; 887 } 888 889 auio.uio_iov = &aiov; 890 auio.uio_iovcnt = 1; 891 auio.uio_rw = UIO_WRITE; 892 auio.uio_segflg = UIO_SYSSPACE; 893 auio.uio_td = (struct thread *)0; 894 error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred); 895 if (error) 896 goto out; 897 898 dp->i_size = roundup2(dp->i_size, dirblksize); 899 dp->i_flag |= IN_CHANGE; 900 901 out: 902 free(buf, M_TEMP); 903 return (error); 904 905 } 906 907 /* 908 * Write a directory entry after a call to namei, using the parameters 909 * that it left in nameidata. The argument ip is the inode which the new 910 * directory entry will refer to. Dvp is a pointer to the directory to 911 * be written, which was left locked by namei. Remaining parameters 912 * (dp->i_offset, dp->i_count) indicate how the space for the new 913 * entry is to be obtained. 914 */ 915 int 916 ext2_direnter(struct inode *ip, struct vnode *dvp, struct componentname *cnp) 917 { 918 struct inode *dp; 919 struct ext2fs_direct_2 newdir; 920 int DIRBLKSIZ = ip->i_e2fs->e2fs_bsize; 921 int error; 922 923 dp = VTOI(dvp); 924 newdir.e2d_ino = htole32(ip->i_number); 925 if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs, 926 EXT2F_INCOMPAT_FTYPE)) { 927 newdir.e2d_namlen = cnp->cn_namelen; 928 newdir.e2d_type = DTTOFT(IFTODT(ip->i_mode)); 929 } else 930 newdir.e2d_namlen = htole16(cnp->cn_namelen); 931 932 bcopy(cnp->cn_nameptr, newdir.e2d_name, (unsigned)cnp->cn_namelen + 1); 933 934 if (ext2_htree_has_idx(dp)) { 935 error = ext2_htree_add_entry(dvp, &newdir, cnp); 936 if (error) { 937 dp->i_flag &= ~IN_E3INDEX; 938 dp->i_flag |= IN_CHANGE | IN_UPDATE; 939 } 940 return (error); 941 } 942 943 if (EXT2_HAS_COMPAT_FEATURE(ip->i_e2fs, EXT2F_COMPAT_DIRHASHINDEX) && 944 !ext2_htree_has_idx(dp)) { 945 if ((dp->i_size / DIRBLKSIZ) == 1 && 946 dp->i_offset == DIRBLKSIZ) { 947 /* 948 * Making indexed directory when one block is not 949 * enough to save all entries. 950 */ 951 return ext2_htree_create_index(dvp, cnp, &newdir); 952 } 953 } 954 955 /* 956 * If dp->i_count is 0, then namei could find no 957 * space in the directory. Here, dp->i_offset will 958 * be on a directory block boundary and we will write the 959 * new entry into a fresh block. 960 */ 961 if (dp->i_count == 0) 962 return ext2_add_first_entry(dvp, &newdir, cnp); 963 964 error = ext2_add_entry(dvp, &newdir); 965 if (!error && dp->i_endoff && dp->i_endoff < dp->i_size) 966 error = ext2_truncate(dvp, (off_t)dp->i_endoff, IO_SYNC, 967 cnp->cn_cred, curthread); 968 return (error); 969 } 970 971 /* 972 * Insert an entry into the directory block. 973 * Compact the contents. 974 */ 975 int 976 ext2_add_entry(struct vnode *dvp, struct ext2fs_direct_2 *entry) 977 { 978 struct ext2fs_direct_2 *ep, *nep; 979 struct inode *dp; 980 struct buf *bp; 981 u_int dsize; 982 int error, loc, newentrysize, spacefree; 983 char *dirbuf; 984 985 dp = VTOI(dvp); 986 987 /* 988 * If dp->i_count is non-zero, then namei found space 989 * for the new entry in the range dp->i_offset to 990 * dp->i_offset + dp->i_count in the directory. 991 * To use this space, we may have to compact the entries located 992 * there, by copying them together towards the beginning of the 993 * block, leaving the free space in one usable chunk at the end. 994 */ 995 996 /* 997 * Increase size of directory if entry eats into new space. 998 * This should never push the size past a new multiple of 999 * DIRBLKSIZE. 1000 * 1001 * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN. 1002 */ 1003 if (dp->i_offset + dp->i_count > dp->i_size) 1004 dp->i_size = dp->i_offset + dp->i_count; 1005 /* 1006 * Get the block containing the space for the new directory entry. 1007 */ 1008 if ((error = ext2_blkatoff(dvp, (off_t)dp->i_offset, &dirbuf, 1009 &bp)) != 0) 1010 return (error); 1011 /* 1012 * Find space for the new entry. In the simple case, the entry at 1013 * offset base will have the space. If it does not, then namei 1014 * arranged that compacting the region dp->i_offset to 1015 * dp->i_offset + dp->i_count would yield the 1016 * space. 1017 */ 1018 newentrysize = EXT2_DIR_REC_LEN(entry->e2d_namlen); 1019 ep = (struct ext2fs_direct_2 *)dirbuf; 1020 dsize = EXT2_DIR_REC_LEN(ep->e2d_namlen); 1021 spacefree = le16toh(ep->e2d_reclen) - dsize; 1022 for (loc = le16toh(ep->e2d_reclen); loc < dp->i_count; ) { 1023 nep = (struct ext2fs_direct_2 *)(dirbuf + loc); 1024 if (le32toh(ep->e2d_ino)) { 1025 /* trim the existing slot */ 1026 ep->e2d_reclen = htole16(dsize); 1027 ep = (struct ext2fs_direct_2 *)((char *)ep + dsize); 1028 } else { 1029 /* overwrite; nothing there; header is ours */ 1030 spacefree += dsize; 1031 } 1032 dsize = EXT2_DIR_REC_LEN(nep->e2d_namlen); 1033 spacefree += le16toh(nep->e2d_reclen) - dsize; 1034 loc += le16toh(nep->e2d_reclen); 1035 bcopy((caddr_t)nep, (caddr_t)ep, dsize); 1036 } 1037 /* 1038 * Update the pointer fields in the previous entry (if any), 1039 * copy in the new entry, and write out the block. 1040 */ 1041 if (ep->e2d_ino == 0) { 1042 if (spacefree + dsize < newentrysize) 1043 panic("ext2_direnter: compact1"); 1044 entry->e2d_reclen = htole16(spacefree + dsize); 1045 } else { 1046 if (spacefree < newentrysize) 1047 panic("ext2_direnter: compact2"); 1048 entry->e2d_reclen = htole16(spacefree); 1049 ep->e2d_reclen = htole16(dsize); 1050 ep = (struct ext2fs_direct_2 *)((char *)ep + dsize); 1051 } 1052 bcopy((caddr_t)entry, (caddr_t)ep, (u_int)newentrysize); 1053 ext2_dirent_csum_set(dp, (struct ext2fs_direct_2 *)bp->b_data); 1054 if (DOINGASYNC(dvp)) { 1055 bdwrite(bp); 1056 error = 0; 1057 } else { 1058 error = bwrite(bp); 1059 } 1060 dp->i_flag |= IN_CHANGE | IN_UPDATE; 1061 return (error); 1062 } 1063 1064 /* 1065 * Remove a directory entry after a call to namei, using 1066 * the parameters which it left in nameidata. The entry 1067 * dp->i_offset contains the offset into the directory of the 1068 * entry to be eliminated. The dp->i_count field contains the 1069 * size of the previous record in the directory. If this 1070 * is 0, the first entry is being deleted, so we need only 1071 * zero the inode number to mark the entry as free. If the 1072 * entry is not the first in the directory, we must reclaim 1073 * the space of the now empty record by adding the record size 1074 * to the size of the previous entry. 1075 */ 1076 int 1077 ext2_dirremove(struct vnode *dvp, struct componentname *cnp) 1078 { 1079 struct inode *dp; 1080 struct ext2fs_direct_2 *ep, *rep; 1081 struct buf *bp; 1082 int error; 1083 1084 dp = VTOI(dvp); 1085 if (dp->i_count == 0) { 1086 /* 1087 * First entry in block: set d_ino to zero. 1088 */ 1089 if ((error = 1090 ext2_blkatoff(dvp, (off_t)dp->i_offset, (char **)&ep, 1091 &bp)) != 0) 1092 return (error); 1093 ep->e2d_ino = 0; 1094 ext2_dirent_csum_set(dp, (struct ext2fs_direct_2 *)bp->b_data); 1095 error = bwrite(bp); 1096 dp->i_flag |= IN_CHANGE | IN_UPDATE; 1097 return (error); 1098 } 1099 /* 1100 * Collapse new free space into previous entry. 1101 */ 1102 if ((error = ext2_blkatoff(dvp, (off_t)(dp->i_offset - dp->i_count), 1103 (char **)&ep, &bp)) != 0) 1104 return (error); 1105 1106 /* Set 'rep' to the entry being removed. */ 1107 if (dp->i_count == 0) 1108 rep = ep; 1109 else 1110 rep = (struct ext2fs_direct_2 *)((char *)ep + 1111 le16toh(ep->e2d_reclen)); 1112 ep->e2d_reclen += rep->e2d_reclen; 1113 ext2_dirent_csum_set(dp, (struct ext2fs_direct_2 *)bp->b_data); 1114 if (DOINGASYNC(dvp) && dp->i_count != 0) 1115 bdwrite(bp); 1116 else 1117 error = bwrite(bp); 1118 dp->i_flag |= IN_CHANGE | IN_UPDATE; 1119 return (error); 1120 } 1121 1122 /* 1123 * Rewrite an existing directory entry to point at the inode 1124 * supplied. The parameters describing the directory entry are 1125 * set up by a call to namei. 1126 */ 1127 int 1128 ext2_dirrewrite(struct inode *dp, struct inode *ip, struct componentname *cnp) 1129 { 1130 struct buf *bp; 1131 struct ext2fs_direct_2 *ep; 1132 struct vnode *vdp = ITOV(dp); 1133 int error; 1134 1135 if ((error = ext2_blkatoff(vdp, (off_t)dp->i_offset, (char **)&ep, 1136 &bp)) != 0) 1137 return (error); 1138 ep->e2d_ino = htole32(ip->i_number); 1139 if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs, 1140 EXT2F_INCOMPAT_FTYPE)) 1141 ep->e2d_type = DTTOFT(IFTODT(ip->i_mode)); 1142 else 1143 ep->e2d_type = EXT2_FT_UNKNOWN; 1144 ext2_dirent_csum_set(dp, (struct ext2fs_direct_2 *)bp->b_data); 1145 error = bwrite(bp); 1146 dp->i_flag |= IN_CHANGE | IN_UPDATE; 1147 return (error); 1148 } 1149 1150 /* 1151 * Check if a directory is empty or not. 1152 * Inode supplied must be locked. 1153 * 1154 * Using a struct dirtemplate here is not precisely 1155 * what we want, but better than using a struct direct. 1156 * 1157 * NB: does not handle corrupted directories. 1158 */ 1159 int 1160 ext2_dirempty(struct inode *ip, ino_t parentino, struct ucred *cred) 1161 { 1162 off_t off; 1163 struct dirtemplate dbuf; 1164 struct ext2fs_direct_2 *dp = (struct ext2fs_direct_2 *)&dbuf; 1165 int error, namlen; 1166 ssize_t count; 1167 #define MINDIRSIZ (sizeof(struct dirtemplate) / 2) 1168 1169 for (off = 0; off < ip->i_size; off += le16toh(dp->e2d_reclen)) { 1170 error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, 1171 off, UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK, cred, 1172 NOCRED, &count, (struct thread *)0); 1173 /* 1174 * Since we read MINDIRSIZ, residual must 1175 * be 0 unless we're at end of file. 1176 */ 1177 if (error || count != 0) 1178 return (0); 1179 /* avoid infinite loops */ 1180 if (dp->e2d_reclen == 0) 1181 return (0); 1182 /* skip empty entries */ 1183 if (dp->e2d_ino == 0) 1184 continue; 1185 /* accept only "." and ".." */ 1186 namlen = dp->e2d_namlen; 1187 if (namlen > 2) 1188 return (0); 1189 if (dp->e2d_name[0] != '.') 1190 return (0); 1191 /* 1192 * At this point namlen must be 1 or 2. 1193 * 1 implies ".", 2 implies ".." if second 1194 * char is also "." 1195 */ 1196 if (namlen == 1) 1197 continue; 1198 if (dp->e2d_name[1] == '.' && le32toh(dp->e2d_ino) == parentino) 1199 continue; 1200 return (0); 1201 } 1202 return (1); 1203 } 1204 1205 /* 1206 * Check if source directory is in the path of the target directory. 1207 * Target is supplied locked, source is unlocked. 1208 * The target is always vput before returning. 1209 */ 1210 int 1211 ext2_checkpath(struct inode *source, struct inode *target, struct ucred *cred) 1212 { 1213 struct vnode *vp; 1214 int error, namlen; 1215 struct dirtemplate dirbuf; 1216 1217 vp = ITOV(target); 1218 if (target->i_number == source->i_number) { 1219 error = EEXIST; 1220 goto out; 1221 } 1222 if (target->i_number == EXT2_ROOTINO) { 1223 error = 0; 1224 goto out; 1225 } 1226 1227 for (;;) { 1228 if (vp->v_type != VDIR) { 1229 error = ENOTDIR; 1230 break; 1231 } 1232 error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf, 1233 sizeof(struct dirtemplate), (off_t)0, UIO_SYSSPACE, 1234 IO_NODELOCKED | IO_NOMACCHECK, cred, NOCRED, NULL, 1235 NULL); 1236 if (error != 0) 1237 break; 1238 namlen = dirbuf.dotdot_type; /* like ufs little-endian */ 1239 if (namlen != 2 || 1240 dirbuf.dotdot_name[0] != '.' || 1241 dirbuf.dotdot_name[1] != '.') { 1242 error = ENOTDIR; 1243 break; 1244 } 1245 if (le32toh(dirbuf.dotdot_ino) == source->i_number) { 1246 error = EINVAL; 1247 break; 1248 } 1249 if (le32toh(dirbuf.dotdot_ino) == EXT2_ROOTINO) 1250 break; 1251 vput(vp); 1252 if ((error = VFS_VGET(vp->v_mount, le32toh(dirbuf.dotdot_ino), 1253 LK_EXCLUSIVE, &vp)) != 0) { 1254 vp = NULL; 1255 break; 1256 } 1257 } 1258 1259 out: 1260 if (error == ENOTDIR) 1261 SDT_PROBE2(ext2fs, , lookup, trace, 1, 1262 "checkpath: .. not a directory"); 1263 if (vp != NULL) 1264 vput(vp); 1265 return (error); 1266 } 1267