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