1*479c151dSjsg /* $OpenBSD: ext2fs_lookup.c,v 1.48 2024/09/20 02:00:46 jsg Exp $ */ 27f1901c5Sart /* $NetBSD: ext2fs_lookup.c,v 1.16 2000/08/03 20:29:26 thorpej Exp $ */ 35ac2d602Sdownsj 41f3ff51cSdownsj /* 51f3ff51cSdownsj * Modified for NetBSD 1.2E 61f3ff51cSdownsj * May 1997, Manuel Bouyer 71f3ff51cSdownsj * Laboratoire d'informatique de Paris VI 81f3ff51cSdownsj */ 95ac2d602Sdownsj /* 105ac2d602Sdownsj * modified for Lites 1.1 115ac2d602Sdownsj * 125ac2d602Sdownsj * Aug 1995, Godmar Back (gback@cs.utah.edu) 135ac2d602Sdownsj * University of Utah, Department of Computer Science 145ac2d602Sdownsj */ 155ac2d602Sdownsj /* 165ac2d602Sdownsj * Copyright (c) 1989, 1993 175ac2d602Sdownsj * The Regents of the University of California. All rights reserved. 185ac2d602Sdownsj * (c) UNIX System Laboratories, Inc. 195ac2d602Sdownsj * All or some portions of this file are derived from material licensed 205ac2d602Sdownsj * to the University of California by American Telephone and Telegraph 215ac2d602Sdownsj * Co. or Unix System Laboratories, Inc. and are reproduced herein with 225ac2d602Sdownsj * the permission of UNIX System Laboratories, Inc. 235ac2d602Sdownsj * 245ac2d602Sdownsj * Redistribution and use in source and binary forms, with or without 255ac2d602Sdownsj * modification, are permitted provided that the following conditions 265ac2d602Sdownsj * are met: 275ac2d602Sdownsj * 1. Redistributions of source code must retain the above copyright 285ac2d602Sdownsj * notice, this list of conditions and the following disclaimer. 295ac2d602Sdownsj * 2. Redistributions in binary form must reproduce the above copyright 305ac2d602Sdownsj * notice, this list of conditions and the following disclaimer in the 315ac2d602Sdownsj * documentation and/or other materials provided with the distribution. 3229295d1cSmillert * 3. Neither the name of the University nor the names of its contributors 335ac2d602Sdownsj * may be used to endorse or promote products derived from this software 345ac2d602Sdownsj * without specific prior written permission. 355ac2d602Sdownsj * 365ac2d602Sdownsj * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 375ac2d602Sdownsj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 385ac2d602Sdownsj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 395ac2d602Sdownsj * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 405ac2d602Sdownsj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 415ac2d602Sdownsj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 425ac2d602Sdownsj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 435ac2d602Sdownsj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 445ac2d602Sdownsj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 455ac2d602Sdownsj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 465ac2d602Sdownsj * SUCH DAMAGE. 475ac2d602Sdownsj * 485ac2d602Sdownsj * @(#)ufs_lookup.c 8.6 (Berkeley) 4/1/94 495ac2d602Sdownsj */ 505ac2d602Sdownsj 515ac2d602Sdownsj #include <sys/param.h> 525ac2d602Sdownsj #include <sys/systm.h> 535ac2d602Sdownsj #include <sys/namei.h> 545ac2d602Sdownsj #include <sys/buf.h> 555ac2d602Sdownsj #include <sys/mount.h> 565ac2d602Sdownsj #include <sys/vnode.h> 575ac2d602Sdownsj #include <sys/malloc.h> 585ac2d602Sdownsj #include <sys/dirent.h> 595ac2d602Sdownsj 605ac2d602Sdownsj #include <ufs/ufs/quota.h> 615ac2d602Sdownsj #include <ufs/ufs/inode.h> 625ac2d602Sdownsj #include <ufs/ufs/ufsmount.h> 635ac2d602Sdownsj #include <ufs/ufs/ufs_extern.h> 645ac2d602Sdownsj 655ac2d602Sdownsj #include <ufs/ext2fs/ext2fs_extern.h> 665ac2d602Sdownsj #include <ufs/ext2fs/ext2fs_dir.h> 675ac2d602Sdownsj #include <ufs/ext2fs/ext2fs.h> 685ac2d602Sdownsj 695ac2d602Sdownsj extern int dirchk; 705ac2d602Sdownsj 71c4071fd1Smillert static void ext2fs_dirconv2ffs(struct ext2fs_direct *e2dir, 72c4071fd1Smillert struct dirent *ffsdir); 73f92a609fSpelikan static int ext2fs_dirbadentry(struct vnode *dp, struct ext2fs_direct *de, 74c4071fd1Smillert int entryoffsetinblock); 75f92a609fSpelikan static int ext2fs_search_dirblock(struct inode *, void *, int *, 76f92a609fSpelikan struct componentname *, int *, doff_t *, doff_t *, 77f92a609fSpelikan struct ext2fs_searchslot *); 785ac2d602Sdownsj 795ac2d602Sdownsj /* 805ac2d602Sdownsj * the problem that is tackled below is the fact that FFS 815ac2d602Sdownsj * includes the terminating zero on disk while EXT2FS doesn't 825ac2d602Sdownsj * this implies that we need to introduce some padding. 835ac2d602Sdownsj * For instance, a filename "sbin" has normally a reclen 12 845ac2d602Sdownsj * in EXT2, but 16 in FFS. 855ac2d602Sdownsj * This reminds me of that Pepsi commercial: 'Kid saved a lousy nine cents...' 865ac2d602Sdownsj * If it wasn't for that, the complete ufs code for directories would 875ac2d602Sdownsj * have worked w/o changes (except for the difference in DIRBLKSIZ) 885ac2d602Sdownsj */ 895ac2d602Sdownsj static void 905f64cd9cSjasper ext2fs_dirconv2ffs(struct ext2fs_direct *e2dir, struct dirent *ffsdir) 915ac2d602Sdownsj { 927f1901c5Sart memset(ffsdir, 0, sizeof(struct dirent)); 93f7dbefaaSpelikan ffsdir->d_fileno = letoh32(e2dir->e2d_ino); 945ac2d602Sdownsj ffsdir->d_namlen = e2dir->e2d_namlen; 955ac2d602Sdownsj 965ac2d602Sdownsj ffsdir->d_type = DT_UNKNOWN; /* don't know more here */ 975ac2d602Sdownsj #ifdef DIAGNOSTIC 985ac2d602Sdownsj /* 99b66b9ef8Sjsg * XXX Right now this can't happen, but if one day 1005ac2d602Sdownsj * MAXNAMLEN != E2FS_MAXNAMLEN we should handle this more gracefully ! 1015ac2d602Sdownsj */ 10235986c15Sgrange /* XXX: e2d_namlen is to small for such comparison 1037f1901c5Sart if (e2dir->e2d_namlen > MAXNAMLEN) 104bc84bce2Skrw panic("ext2fs: e2dir->e2d_namlen"); 10535986c15Sgrange */ 1065ac2d602Sdownsj #endif 1075ac2d602Sdownsj strncpy(ffsdir->d_name, e2dir->e2d_name, ffsdir->d_namlen); 1085ac2d602Sdownsj 1095ac2d602Sdownsj /* Godmar thinks: since e2dir->e2d_reclen can be big and means 1105ac2d602Sdownsj nothing anyway, we compute our own reclen according to what 1115ac2d602Sdownsj we think is right 1125ac2d602Sdownsj */ 1135ac2d602Sdownsj ffsdir->d_reclen = DIRENT_SIZE(ffsdir); 1145ac2d602Sdownsj } 1155ac2d602Sdownsj 1165ac2d602Sdownsj /* 1175ac2d602Sdownsj * Vnode op for reading directories. 1185ac2d602Sdownsj * 1195ac2d602Sdownsj * Convert the on-disk entries to <sys/dirent.h> entries. 1205ac2d602Sdownsj * the problem is that the conversion will blow up some entries by four bytes, 1215ac2d602Sdownsj * so it can't be done in place. This is too bad. Right now the conversion is 1225ac2d602Sdownsj * done entry by entry, the converted entry is sent via uiomove. 1235ac2d602Sdownsj * 1245ac2d602Sdownsj * XXX allocate a buffer, convert as many entries as possible, then send 1255ac2d602Sdownsj * the whole buffer to uiomove 1265ac2d602Sdownsj */ 1275ac2d602Sdownsj int 1285f64cd9cSjasper ext2fs_readdir(void *v) 1295ac2d602Sdownsj { 13099bc9d31Sderaadt struct vop_readdir_args *ap = v; 1317f1901c5Sart struct uio *uio = ap->a_uio; 1325ac2d602Sdownsj int error; 1333faca824Spedro size_t e2fs_count, readcnt, entries; 1347f1901c5Sart struct vnode *vp = ap->a_vp; 1357f1901c5Sart struct m_ext2fs *fs = VTOI(vp)->i_e2fs; 1365ac2d602Sdownsj 1375ac2d602Sdownsj struct ext2fs_direct *dp; 1385ac2d602Sdownsj struct dirent dstd; 1395ac2d602Sdownsj struct uio auio; 1405ac2d602Sdownsj struct iovec aiov; 1415ac2d602Sdownsj caddr_t dirbuf; 1425ac2d602Sdownsj off_t off = uio->uio_offset; 1437f1901c5Sart int e2d_reclen; 1447f1901c5Sart 1457f1901c5Sart if (vp->v_type != VDIR) 1467f1901c5Sart return (ENOTDIR); 1477f1901c5Sart 1487f1901c5Sart e2fs_count = uio->uio_resid; 1493faca824Spedro entries = (uio->uio_offset + e2fs_count) & (fs->e2fs_bsize - 1); 1503faca824Spedro 1515ac2d602Sdownsj /* Make sure we don't return partial entries. */ 1523faca824Spedro if (e2fs_count <= entries) 1535ac2d602Sdownsj return (EINVAL); 1545ac2d602Sdownsj 1553faca824Spedro e2fs_count -= entries; 1565ac2d602Sdownsj auio = *uio; 1575ac2d602Sdownsj auio.uio_iov = &aiov; 1585ac2d602Sdownsj auio.uio_iovcnt = 1; 1595ac2d602Sdownsj auio.uio_segflg = UIO_SYSSPACE; 1607f1901c5Sart aiov.iov_len = e2fs_count; 1617f1901c5Sart auio.uio_resid = e2fs_count; 162f18006a9Skrw dirbuf = malloc(e2fs_count, M_TEMP, M_WAITOK | M_ZERO); 1635ac2d602Sdownsj aiov.iov_base = dirbuf; 1645ac2d602Sdownsj 1655ac2d602Sdownsj error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred); 1667f1901c5Sart if (error == 0) { 1677f1901c5Sart readcnt = e2fs_count - auio.uio_resid; 1682d7df345Spelikan dp = (struct ext2fs_direct *) dirbuf; 1692d7df345Spelikan while ((char *) dp < (char *) dirbuf + readcnt) { 170f7dbefaaSpelikan e2d_reclen = letoh16(dp->e2d_reclen); 1717f1901c5Sart if (e2d_reclen == 0) { 1725ac2d602Sdownsj error = EIO; 1737f1901c5Sart break; 1745ac2d602Sdownsj } 1755ac2d602Sdownsj ext2fs_dirconv2ffs(dp, &dstd); 17646f7109aSclaudio if (memchr(dstd.d_name, '/', dstd.d_namlen) != NULL) { 17746f7109aSclaudio error = EINVAL; 17846f7109aSclaudio break; 17946f7109aSclaudio } 1805ac2d602Sdownsj if (dstd.d_reclen > uio->uio_resid) { 1815ac2d602Sdownsj break; 1825ac2d602Sdownsj } 18391a535ffSguenther dstd.d_off = off + e2d_reclen; 184dc30eddbSstefan if ((error = uiomove((caddr_t)&dstd, dstd.d_reclen, uio)) != 0) { 1857f1901c5Sart break; 1865ac2d602Sdownsj } 1877f1901c5Sart off = off + e2d_reclen; 1887f1901c5Sart /* advance dp */ 1897f1901c5Sart dp = (struct ext2fs_direct *) ((char *)dp + e2d_reclen); 1905ac2d602Sdownsj } 1915ac2d602Sdownsj /* we need to correct uio_offset */ 1925ac2d602Sdownsj uio->uio_offset = off; 193d145b341Scsapuntz } 194c9a9cb3fSpelikan free(dirbuf, M_TEMP, e2fs_count); 1954dcbbbb0Sniallo *ap->a_eofflag = ext2fs_size(VTOI(ap->a_vp)) <= uio->uio_offset; 1965ac2d602Sdownsj return (error); 1975ac2d602Sdownsj } 1985ac2d602Sdownsj 1995ac2d602Sdownsj /* 2005ac2d602Sdownsj * Convert a component of a pathname into a pointer to a locked inode. 2015ac2d602Sdownsj * This is a very central and rather complicated routine. 2025ac2d602Sdownsj * If the file system is not maintained in a strict tree hierarchy, 2035ac2d602Sdownsj * this can result in a deadlock situation (see comments in code below). 2045ac2d602Sdownsj * 2055ac2d602Sdownsj * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending 2065ac2d602Sdownsj * on whether the name is to be looked up, created, renamed, or deleted. 2075ac2d602Sdownsj * When CREATE, RENAME, or DELETE is specified, information usable in 2085ac2d602Sdownsj * creating, renaming, or deleting a directory entry may be calculated. 2095ac2d602Sdownsj * If flag has LOCKPARENT or'ed into it and the target of the pathname 2105ac2d602Sdownsj * exists, lookup returns both the target and its parent directory locked. 2115ac2d602Sdownsj * When creating or renaming and LOCKPARENT is specified, the target may 2125ac2d602Sdownsj * not be ".". When deleting and LOCKPARENT is specified, the target may 2135ac2d602Sdownsj * be "."., but the caller must check to ensure it does an vrele and vput 2145ac2d602Sdownsj * instead of two vputs. 2155ac2d602Sdownsj * 2165ac2d602Sdownsj * Overall outline of ext2fs_lookup: 2175ac2d602Sdownsj * 2185ac2d602Sdownsj * check accessibility of directory 2195ac2d602Sdownsj * look for name in cache, if found, then if at end of path 2205ac2d602Sdownsj * and deleting or creating, drop it, else return name 2215ac2d602Sdownsj * search for name in directory, to found or notfound 2225ac2d602Sdownsj * notfound: 2235ac2d602Sdownsj * if creating, return locked directory, leaving info on available slots 2245ac2d602Sdownsj * else return error 2255ac2d602Sdownsj * found: 2265ac2d602Sdownsj * if at end of path and deleting, return information to allow delete 2275ac2d602Sdownsj * if at end of path and rewriting (RENAME and LOCKPARENT), lock target 2285ac2d602Sdownsj * inode and return info to allow rewrite 2295ac2d602Sdownsj * if not at end, add name to cache; if at end and neither creating 2305ac2d602Sdownsj * nor deleting, add name to cache 2315ac2d602Sdownsj */ 2325ac2d602Sdownsj int 2335f64cd9cSjasper ext2fs_lookup(void *v) 2345ac2d602Sdownsj { 23599bc9d31Sderaadt struct vop_lookup_args *ap = v; 2367f1901c5Sart struct vnode *vdp; /* vnode for directory being searched */ 2377f1901c5Sart struct inode *dp; /* inode for directory being searched */ 2385ac2d602Sdownsj struct buf *bp; /* a buffer of directory entries */ 2397f1901c5Sart struct ext2fs_direct *ep; /* the current directory entry */ 2405ac2d602Sdownsj int entryoffsetinblock; /* offset of ep in bp's buffer */ 2413e537b2cSpelikan struct ext2fs_searchslot ss; 2425ac2d602Sdownsj int numdirpasses; /* strategy for directory search */ 2435ac2d602Sdownsj doff_t endsearch; /* offset to end directory search */ 2445ac2d602Sdownsj doff_t prevoff; /* prev entry dp->i_offset */ 2455ac2d602Sdownsj struct vnode *pdp; /* saved dp during symlink work */ 2465ac2d602Sdownsj struct vnode *tdp; /* returned by VFS_VGET */ 2475ac2d602Sdownsj doff_t enduseful; /* pointer past last used dir slot */ 2485ac2d602Sdownsj u_long bmask; /* block offset mask */ 2495ac2d602Sdownsj int lockparent; /* 1 => lockparent flag is set */ 2505ac2d602Sdownsj int wantparent; /* 1 => wantparent or lockparent flag */ 2515ac2d602Sdownsj struct vnode **vpp = ap->a_vpp; 2525ac2d602Sdownsj struct componentname *cnp = ap->a_cnp; 2535ac2d602Sdownsj struct ucred *cred = cnp->cn_cred; 2545ac2d602Sdownsj int flags = cnp->cn_flags; 2555ac2d602Sdownsj int nameiop = cnp->cn_nameiop; 256f92a609fSpelikan int dirblksize, entry_found = 0, error; 2575ac2d602Sdownsj 2583e537b2cSpelikan ss.slotstatus = FOUND; 2593e537b2cSpelikan ss.slotoffset = -1; 2603e537b2cSpelikan ss.slotfreespace = ss.slotsize = ss.slotneeded = 0; 2613e537b2cSpelikan 2625ac2d602Sdownsj bp = NULL; 2635ac2d602Sdownsj *vpp = NULL; 2645ac2d602Sdownsj vdp = ap->a_dvp; 2655ac2d602Sdownsj dp = VTOI(vdp); 266f92a609fSpelikan dirblksize = dp->i_e2fs->e2fs_bsize; 2675ac2d602Sdownsj lockparent = flags & LOCKPARENT; 2685ac2d602Sdownsj wantparent = flags & (LOCKPARENT|WANTPARENT); 2692d7df345Spelikan 2705ac2d602Sdownsj /* 2710d297f47Sjsg * Check accessibility of directory. 2725ac2d602Sdownsj */ 2735ac2d602Sdownsj if ((error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc)) != 0) 2745ac2d602Sdownsj return (error); 2755ac2d602Sdownsj 2767f1901c5Sart if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) && 2777f1901c5Sart (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) 2787f1901c5Sart return (EROFS); 2797f1901c5Sart 2805ac2d602Sdownsj /* 2815ac2d602Sdownsj * We now have a segment name to search for, and a directory to search. 2825ac2d602Sdownsj * 2835ac2d602Sdownsj * Before tediously performing a linear scan of the directory, 2845ac2d602Sdownsj * check the name cache to see if the directory/name pair 2855ac2d602Sdownsj * we are looking for is known already. 2865ac2d602Sdownsj */ 28709308f32Sart if ((error = cache_lookup(vdp, vpp, cnp)) >= 0) 2885ac2d602Sdownsj return (error); 2895ac2d602Sdownsj 2905ac2d602Sdownsj /* 2915ac2d602Sdownsj * Suppress search for slots unless creating 2925ac2d602Sdownsj * file and at end of pathname, in which case 2935ac2d602Sdownsj * we watch for a place to put the new file in 2945ac2d602Sdownsj * case it doesn't already exist. 2955ac2d602Sdownsj */ 2963e537b2cSpelikan if ((nameiop == CREATE || nameiop == RENAME) && (flags & ISLASTCN)) { 2973e537b2cSpelikan ss.slotstatus = NONE; 2983e537b2cSpelikan ss.slotneeded = EXT2FS_DIRSIZ(cnp->cn_namelen); 2995ac2d602Sdownsj } 3005ac2d602Sdownsj 3015ac2d602Sdownsj /* 3025ac2d602Sdownsj * If there is cached information on a previous search of 3035ac2d602Sdownsj * this directory, pick up where we last left off. 3045ac2d602Sdownsj * We cache only lookups as these are the most common 3055ac2d602Sdownsj * and have the greatest payoff. Caching CREATE has little 3065ac2d602Sdownsj * benefit as it usually must search the entire directory 3075ac2d602Sdownsj * to determine that the entry does not exist. Caching the 3085ac2d602Sdownsj * location of the last DELETE or RENAME has not reduced 3095ac2d602Sdownsj * profiling time and hence has been removed in the interest 3105ac2d602Sdownsj * of simplicity. 3115ac2d602Sdownsj */ 3125ac2d602Sdownsj bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1; 3135ac2d602Sdownsj if (nameiop != LOOKUP || dp->i_diroff == 0 || 3144dcbbbb0Sniallo dp->i_diroff > ext2fs_size(dp)) { 3155ac2d602Sdownsj entryoffsetinblock = 0; 3165ac2d602Sdownsj dp->i_offset = 0; 3175ac2d602Sdownsj numdirpasses = 1; 3185ac2d602Sdownsj } else { 3195ac2d602Sdownsj dp->i_offset = dp->i_diroff; 3205ac2d602Sdownsj if ((entryoffsetinblock = dp->i_offset & bmask) && 321b080ad39Scsapuntz (error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, 322b080ad39Scsapuntz NULL, &bp))) 3235ac2d602Sdownsj return (error); 3245ac2d602Sdownsj numdirpasses = 2; 3255ac2d602Sdownsj } 3265ac2d602Sdownsj prevoff = dp->i_offset; 3274dcbbbb0Sniallo endsearch = roundup(ext2fs_size(dp), dirblksize); 3285ac2d602Sdownsj enduseful = 0; 3295ac2d602Sdownsj 3305ac2d602Sdownsj searchloop: 3315ac2d602Sdownsj while (dp->i_offset < endsearch) { 3325ac2d602Sdownsj /* 3335ac2d602Sdownsj * If necessary, get the next directory block. 3345ac2d602Sdownsj */ 3355ac2d602Sdownsj if (bp != NULL) 3365ac2d602Sdownsj brelse(bp); 3372d7df345Spelikan 338f92a609fSpelikan error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, NULL, &bp); 3395ac2d602Sdownsj if (error != 0) 3405ac2d602Sdownsj return (error); 3415ac2d602Sdownsj entryoffsetinblock = 0; 342f92a609fSpelikan 3435ac2d602Sdownsj /* 3445ac2d602Sdownsj * If still looking for a slot, and at a dirblksize 3455ac2d602Sdownsj * boundary, have to start looking for free space again. 3465ac2d602Sdownsj */ 347f92a609fSpelikan if (ss.slotstatus == NONE) { 3483e537b2cSpelikan ss.slotoffset = -1; 3493e537b2cSpelikan ss.slotfreespace = 0; 3505ac2d602Sdownsj } 351f92a609fSpelikan 352f92a609fSpelikan error = ext2fs_search_dirblock(dp, bp->b_data, &entry_found, 353f92a609fSpelikan cnp, &entryoffsetinblock, &prevoff, &enduseful, &ss); 354f92a609fSpelikan if (error) { 355f92a609fSpelikan brelse(bp); 356f92a609fSpelikan return (error); 357f92a609fSpelikan } 358f92a609fSpelikan if (entry_found) { 3595ac2d602Sdownsj ep = (struct ext2fs_direct *) 360f92a609fSpelikan ((char *)bp->b_data + (entryoffsetinblock & bmask)); 361f92a609fSpelikan /* foundentry: */ 362f7dbefaaSpelikan dp->i_ino = letoh32(ep->e2d_ino); 363f7dbefaaSpelikan dp->i_reclen = letoh16(ep->e2d_reclen); 3645ac2d602Sdownsj goto found; 3655ac2d602Sdownsj } 3665ac2d602Sdownsj } 3675ac2d602Sdownsj /* notfound: */ 3685ac2d602Sdownsj /* 3695ac2d602Sdownsj * If we started in the middle of the directory and failed 3705ac2d602Sdownsj * to find our target, we must check the beginning as well. 3715ac2d602Sdownsj */ 3725ac2d602Sdownsj if (numdirpasses == 2) { 3735ac2d602Sdownsj numdirpasses--; 3745ac2d602Sdownsj dp->i_offset = 0; 3755ac2d602Sdownsj endsearch = dp->i_diroff; 3765ac2d602Sdownsj goto searchloop; 3775ac2d602Sdownsj } 3785ac2d602Sdownsj if (bp != NULL) 3795ac2d602Sdownsj brelse(bp); 3805ac2d602Sdownsj /* 3815ac2d602Sdownsj * If creating, and at end of pathname and current 3825ac2d602Sdownsj * directory has not been removed, then can consider 3835ac2d602Sdownsj * allowing file to be created. 3845ac2d602Sdownsj */ 3855ac2d602Sdownsj if ((nameiop == CREATE || nameiop == RENAME) && 3865ac2d602Sdownsj (flags & ISLASTCN) && dp->i_e2fs_nlink != 0) { 3875ac2d602Sdownsj /* 3881f3ff51cSdownsj * Creation of files on a read-only mounted file system 3891f3ff51cSdownsj * is pointless, so don't proceed any further. 3901f3ff51cSdownsj */ 3911f3ff51cSdownsj if (vdp->v_mount->mnt_flag & MNT_RDONLY) 3921f3ff51cSdownsj return (EROFS); 3931f3ff51cSdownsj /* 3945ac2d602Sdownsj * Access for write is interpreted as allowing 3955ac2d602Sdownsj * creation of files in the directory. 3965ac2d602Sdownsj */ 3975ac2d602Sdownsj if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0) 3985ac2d602Sdownsj return (error); 3995ac2d602Sdownsj /* 4005ac2d602Sdownsj * Return an indication of where the new directory 4015ac2d602Sdownsj * entry should be put. If we didn't find a slot, 4025ac2d602Sdownsj * then set dp->i_count to 0 indicating 4035ac2d602Sdownsj * that the new slot belongs at the end of the 4045ac2d602Sdownsj * directory. If we found a slot, then the new entry 4055ac2d602Sdownsj * can be put in the range from dp->i_offset to 4065ac2d602Sdownsj * dp->i_offset + dp->i_count. 4075ac2d602Sdownsj */ 4083e537b2cSpelikan if (ss.slotstatus == NONE) { 4094dcbbbb0Sniallo dp->i_offset = roundup(ext2fs_size(dp), dirblksize); 4105ac2d602Sdownsj dp->i_count = 0; 4115ac2d602Sdownsj enduseful = dp->i_offset; 4125ac2d602Sdownsj } else { 4133e537b2cSpelikan dp->i_offset = ss.slotoffset; 4143e537b2cSpelikan dp->i_count = ss.slotsize; 4153e537b2cSpelikan if (enduseful < ss.slotoffset + ss.slotsize) 4163e537b2cSpelikan enduseful = ss.slotoffset + ss.slotsize; 4175ac2d602Sdownsj } 4185ac2d602Sdownsj dp->i_endoff = roundup(enduseful, dirblksize); 4195ac2d602Sdownsj dp->i_flag |= IN_CHANGE | IN_UPDATE; 4205ac2d602Sdownsj /* 4215ac2d602Sdownsj * We return with the directory locked, so that 4225ac2d602Sdownsj * the parameters we set up above will still be 4235ac2d602Sdownsj * valid if we actually decide to do a direnter(). 4245ac2d602Sdownsj * We return ni_vp == NULL to indicate that the entry 4255ac2d602Sdownsj * does not currently exist; we leave a pointer to 4265ac2d602Sdownsj * the (locked) directory inode in ndp->ni_dvp. 4275ac2d602Sdownsj * The pathname buffer is saved so that the name 4285ac2d602Sdownsj * can be obtained later. 4295ac2d602Sdownsj * 4305ac2d602Sdownsj * NB - if the directory is unlocked, then this 4315ac2d602Sdownsj * information cannot be used. 4325ac2d602Sdownsj */ 4335ac2d602Sdownsj cnp->cn_flags |= SAVENAME; 43409308f32Sart if (!lockparent) { 43536bb23f1Svisa VOP_UNLOCK(vdp); 43609308f32Sart cnp->cn_flags |= PDIRUNLOCK; 43709308f32Sart } 4385ac2d602Sdownsj return (EJUSTRETURN); 4395ac2d602Sdownsj } 4405ac2d602Sdownsj /* 4415ac2d602Sdownsj * Insert name into cache (as non-existent) if appropriate. 4425ac2d602Sdownsj */ 4435ac2d602Sdownsj if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) 4445ac2d602Sdownsj cache_enter(vdp, *vpp, cnp); 4455ac2d602Sdownsj return (ENOENT); 4465ac2d602Sdownsj 4475ac2d602Sdownsj found: 4485ac2d602Sdownsj /* 4495ac2d602Sdownsj * Check that directory length properly reflects presence 4505ac2d602Sdownsj * of this entry. 4515ac2d602Sdownsj */ 4525ac2d602Sdownsj if (entryoffsetinblock + EXT2FS_DIRSIZ(ep->e2d_namlen) 4534dcbbbb0Sniallo > ext2fs_size(dp)) { 4545ac2d602Sdownsj ufs_dirbad(dp, dp->i_offset, "i_size too small"); 4554dcbbbb0Sniallo error = ext2fs_setsize(dp, 4564dcbbbb0Sniallo entryoffsetinblock + EXT2FS_DIRSIZ(ep->e2d_namlen)); 4574dcbbbb0Sniallo if (error) { 4584dcbbbb0Sniallo brelse(bp); 4594dcbbbb0Sniallo return(error); 4604dcbbbb0Sniallo } 4615ac2d602Sdownsj dp->i_flag |= IN_CHANGE | IN_UPDATE; 4625ac2d602Sdownsj } 463f92a609fSpelikan brelse(bp); 4645ac2d602Sdownsj 4655ac2d602Sdownsj /* 4665ac2d602Sdownsj * Found component in pathname. 4675ac2d602Sdownsj * If the final component of path name, save information 4685ac2d602Sdownsj * in the cache as to where the entry was found. 4695ac2d602Sdownsj */ 4705ac2d602Sdownsj if ((flags & ISLASTCN) && nameiop == LOOKUP) 4715ac2d602Sdownsj dp->i_diroff = dp->i_offset &~ (dirblksize - 1); 4725ac2d602Sdownsj 4735ac2d602Sdownsj /* 4745ac2d602Sdownsj * If deleting, and at end of pathname, return 4755ac2d602Sdownsj * parameters which can be used to remove file. 4765ac2d602Sdownsj * If the wantparent flag isn't set, we return only 4775ac2d602Sdownsj * the directory (in ndp->ni_dvp), otherwise we go 4785ac2d602Sdownsj * on and lock the inode, being careful with ".". 4795ac2d602Sdownsj */ 4805ac2d602Sdownsj if (nameiop == DELETE && (flags & ISLASTCN)) { 4815ac2d602Sdownsj /* 4825ac2d602Sdownsj * Write access to directory required to delete files. 4835ac2d602Sdownsj */ 4845ac2d602Sdownsj if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0) 4855ac2d602Sdownsj return (error); 4865ac2d602Sdownsj /* 4875ac2d602Sdownsj * Return pointer to current entry in dp->i_offset, 4885ac2d602Sdownsj * and distance past previous entry (if there 4895ac2d602Sdownsj * is a previous entry in this block) in dp->i_count. 4905ac2d602Sdownsj * Save directory inode pointer in ndp->ni_dvp for dirremove(). 4915ac2d602Sdownsj */ 4925ac2d602Sdownsj if ((dp->i_offset & (dirblksize - 1)) == 0) 4935ac2d602Sdownsj dp->i_count = 0; 4945ac2d602Sdownsj else 4955ac2d602Sdownsj dp->i_count = dp->i_offset - prevoff; 4965ac2d602Sdownsj if (dp->i_number == dp->i_ino) { 497627b2c48Sthib vref(vdp); 4985ac2d602Sdownsj *vpp = vdp; 4995ac2d602Sdownsj return (0); 5005ac2d602Sdownsj } 5015ac2d602Sdownsj if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) 5025ac2d602Sdownsj return (error); 5035ac2d602Sdownsj /* 5045ac2d602Sdownsj * If directory is "sticky", then user must own 5055ac2d602Sdownsj * the directory, or the file in it, else she 5065ac2d602Sdownsj * may not delete it (unless she's root). This 5075ac2d602Sdownsj * implements append-only directories. 5085ac2d602Sdownsj */ 5095ac2d602Sdownsj if ((dp->i_e2fs_mode & ISVTX) && 5105ac2d602Sdownsj cred->cr_uid != 0 && 5115ac2d602Sdownsj cred->cr_uid != dp->i_e2fs_uid && 5125ac2d602Sdownsj VTOI(tdp)->i_e2fs_uid != cred->cr_uid) { 5135ac2d602Sdownsj vput(tdp); 5145ac2d602Sdownsj return (EPERM); 5155ac2d602Sdownsj } 5165ac2d602Sdownsj *vpp = tdp; 51709308f32Sart if (!lockparent) { 51836bb23f1Svisa VOP_UNLOCK(vdp); 51909308f32Sart cnp->cn_flags |= PDIRUNLOCK; 52009308f32Sart } 5215ac2d602Sdownsj return (0); 5225ac2d602Sdownsj } 5235ac2d602Sdownsj 5245ac2d602Sdownsj /* 5255ac2d602Sdownsj * If rewriting (RENAME), return the inode and the 5265ac2d602Sdownsj * information required to rewrite the present directory 5275ac2d602Sdownsj * Must get inode of directory entry to verify it's a 5285ac2d602Sdownsj * regular file, or empty directory. 5295ac2d602Sdownsj */ 5303e537b2cSpelikan if (nameiop == RENAME && wantparent && (flags & ISLASTCN)) { 5315ac2d602Sdownsj if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0) 5325ac2d602Sdownsj return (error); 5335ac2d602Sdownsj /* 5345ac2d602Sdownsj * Careful about locking second inode. 5355ac2d602Sdownsj * This can only occur if the target is ".". 5365ac2d602Sdownsj */ 5375ac2d602Sdownsj if (dp->i_number == dp->i_ino) 5385ac2d602Sdownsj return (EISDIR); 5395ac2d602Sdownsj if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) 5405ac2d602Sdownsj return (error); 5415ac2d602Sdownsj *vpp = tdp; 5425ac2d602Sdownsj cnp->cn_flags |= SAVENAME; 54309308f32Sart if (!lockparent) { 54436bb23f1Svisa VOP_UNLOCK(vdp); 54509308f32Sart cnp->cn_flags |= PDIRUNLOCK; 54609308f32Sart } 5475ac2d602Sdownsj return (0); 5485ac2d602Sdownsj } 5495ac2d602Sdownsj 5505ac2d602Sdownsj /* 5515ac2d602Sdownsj * Step through the translation in the name. We do not `vput' the 5525ac2d602Sdownsj * directory because we may need it again if a symbolic link 5535ac2d602Sdownsj * is relative to the current directory. Instead we save it 5545ac2d602Sdownsj * unlocked as "pdp". We must get the target inode before unlocking 5555ac2d602Sdownsj * the directory to insure that the inode will not be removed 5565ac2d602Sdownsj * before we get it. We prevent deadlock by always fetching 5575ac2d602Sdownsj * inodes from the root, moving down the directory tree. Thus 5585ac2d602Sdownsj * when following backward pointers ".." we must unlock the 5595ac2d602Sdownsj * parent directory before getting the requested directory. 5605ac2d602Sdownsj * There is a potential race condition here if both the current 5615ac2d602Sdownsj * and parent directories are removed before the VFS_VGET for the 5625ac2d602Sdownsj * inode associated with ".." returns. We hope that this occurs 5635ac2d602Sdownsj * infrequently since we cannot avoid this race condition without 5645ac2d602Sdownsj * implementing a sophisticated deadlock detection algorithm. 5655ac2d602Sdownsj * Note also that this simple deadlock detection scheme will not 5665ac2d602Sdownsj * work if the file system has any hard links other than ".." 5675ac2d602Sdownsj * that point backwards in the directory structure. 5685ac2d602Sdownsj */ 5695ac2d602Sdownsj pdp = vdp; 5705ac2d602Sdownsj if (flags & ISDOTDOT) { 57136bb23f1Svisa VOP_UNLOCK(pdp); /* race to get the inode */ 57209308f32Sart cnp->cn_flags |= PDIRUNLOCK; 5735ac2d602Sdownsj if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) { 5746e880534Svisa if (vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY) == 0) 57509308f32Sart cnp->cn_flags &= ~PDIRUNLOCK; 5765ac2d602Sdownsj return (error); 5775ac2d602Sdownsj } 57809308f32Sart if (lockparent && (flags & ISLASTCN)) { 5796e880534Svisa if ((error = vn_lock(pdp, LK_EXCLUSIVE)) != 0) { 5805ac2d602Sdownsj vput(tdp); 5815ac2d602Sdownsj return (error); 5825ac2d602Sdownsj } 58309308f32Sart cnp->cn_flags &= ~PDIRUNLOCK; 58409308f32Sart } 5855ac2d602Sdownsj *vpp = tdp; 5865ac2d602Sdownsj } else if (dp->i_number == dp->i_ino) { 587627b2c48Sthib vref(vdp); /* we want ourself, ie "." */ 5885ac2d602Sdownsj *vpp = vdp; 5895ac2d602Sdownsj } else { 5905ac2d602Sdownsj if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) 5915ac2d602Sdownsj return (error); 59209308f32Sart if (!lockparent || !(flags & ISLASTCN)) { 59336bb23f1Svisa VOP_UNLOCK(pdp); 59409308f32Sart cnp->cn_flags |= PDIRUNLOCK; 59509308f32Sart } 5965ac2d602Sdownsj *vpp = tdp; 5975ac2d602Sdownsj } 5985ac2d602Sdownsj 5995ac2d602Sdownsj /* 6005ac2d602Sdownsj * Insert name into cache if appropriate. 6015ac2d602Sdownsj */ 6025ac2d602Sdownsj if (cnp->cn_flags & MAKEENTRY) 6035ac2d602Sdownsj cache_enter(vdp, *vpp, cnp); 6045ac2d602Sdownsj return (0); 6055ac2d602Sdownsj } 6065ac2d602Sdownsj 607f92a609fSpelikan int 608f92a609fSpelikan ext2fs_search_dirblock(struct inode *ip, void *data, int *foundp, 609f92a609fSpelikan struct componentname *cnp, int *entryoffsetinblockp, 610f92a609fSpelikan doff_t *prevoffp, doff_t *endusefulp, struct ext2fs_searchslot *ssp) 611f92a609fSpelikan { 612f92a609fSpelikan struct ext2fs_direct *ep, *lim; 613f92a609fSpelikan struct vnode *vdp; 614f92a609fSpelikan int offset = *entryoffsetinblockp; 615f92a609fSpelikan int dirblksize = ip->i_e2fs->e2fs_bsize; 616f92a609fSpelikan size_t namlen; 617f92a609fSpelikan 618f92a609fSpelikan vdp = ITOV(ip); 619f92a609fSpelikan 620f92a609fSpelikan lim = (struct ext2fs_direct *) 621f92a609fSpelikan ((char *)data + dirblksize - EXT2FS_DIRSIZ(0)); 622f92a609fSpelikan ep = (struct ext2fs_direct *) ((char *)data + offset); 623f92a609fSpelikan 624f92a609fSpelikan while (ep < lim) { 625f92a609fSpelikan /* 626f92a609fSpelikan * Full validation checks are slow, so we only check 627f92a609fSpelikan * enough to insure forward progress through the 628f92a609fSpelikan * directory. Complete checks can be run by patching 629f92a609fSpelikan * "dirchk" to be true. 630f92a609fSpelikan */ 631f92a609fSpelikan if (ep->e2d_reclen == 0 || 632f92a609fSpelikan (dirchk && ext2fs_dirbadentry(vdp, ep, offset))) { 633f92a609fSpelikan int i; 634f92a609fSpelikan ufs_dirbad(ip, ip->i_offset, "mangled entry"); 635f92a609fSpelikan i = dirblksize - (offset & (dirblksize - 1)); 636f92a609fSpelikan ip->i_offset += i; 637f92a609fSpelikan offset += i; 638f92a609fSpelikan continue; 639f92a609fSpelikan } 640f92a609fSpelikan 641f92a609fSpelikan /* 642f92a609fSpelikan * If an appropriate sized slot has not yet been found, 643f92a609fSpelikan * check to see if one is available. Also accumulate space 644f92a609fSpelikan * in the current block so that we can determine if 645f92a609fSpelikan * compaction is viable. 646f92a609fSpelikan */ 647f92a609fSpelikan if (ssp->slotstatus != FOUND) { 648f7dbefaaSpelikan int size = letoh16(ep->e2d_reclen); 649f92a609fSpelikan 650f92a609fSpelikan if (ep->e2d_ino != 0) 651f92a609fSpelikan size -= EXT2FS_DIRSIZ(ep->e2d_namlen); 652f92a609fSpelikan if (size > 0) { 653f92a609fSpelikan if (size >= ssp->slotneeded) { 654f92a609fSpelikan ssp->slotstatus = FOUND; 655f92a609fSpelikan ssp->slotoffset = ip->i_offset; 656f7dbefaaSpelikan ssp->slotsize = letoh16(ep->e2d_reclen); 657f92a609fSpelikan } else if (ssp->slotstatus == NONE) { 658f92a609fSpelikan ssp->slotfreespace += size; 659f92a609fSpelikan if (ssp->slotoffset == -1) 660f92a609fSpelikan ssp->slotoffset = ip->i_offset; 661f92a609fSpelikan if (ssp->slotfreespace >= ssp->slotneeded) { 662f92a609fSpelikan ssp->slotstatus = COMPACT; 663f92a609fSpelikan ssp->slotsize = ip->i_offset + 664f7dbefaaSpelikan letoh16(ep->e2d_reclen) - ssp->slotoffset; 665f92a609fSpelikan } 666f92a609fSpelikan } 667f92a609fSpelikan } 668f92a609fSpelikan } 669f92a609fSpelikan 670f92a609fSpelikan /* 671f92a609fSpelikan * Check for a name match. 672f92a609fSpelikan */ 673f92a609fSpelikan if (ep->e2d_ino) { 674f92a609fSpelikan namlen = ep->e2d_namlen; 675f92a609fSpelikan if (namlen == cnp->cn_namelen && 676f92a609fSpelikan !memcmp(cnp->cn_nameptr, ep->e2d_name, namlen)) { 677f92a609fSpelikan /* 678f92a609fSpelikan * Save directory entry's inode number and 679f92a609fSpelikan * reclen in ndp->ni_ufs area, and release 680f92a609fSpelikan * directory buffer. 681f92a609fSpelikan */ 682f92a609fSpelikan *foundp = 1; 683f92a609fSpelikan return (0); 684f92a609fSpelikan } 685f92a609fSpelikan } 686f92a609fSpelikan *prevoffp = ip->i_offset; 687f7dbefaaSpelikan ip->i_offset += letoh16(ep->e2d_reclen); 688f7dbefaaSpelikan offset += letoh16(ep->e2d_reclen); 689f92a609fSpelikan *entryoffsetinblockp = offset; 690f92a609fSpelikan if (ep->e2d_ino) 691f92a609fSpelikan *endusefulp = ip->i_offset; 692f92a609fSpelikan 693f92a609fSpelikan /* 694f92a609fSpelikan * Get pointer to the next entry. 695f92a609fSpelikan */ 696f92a609fSpelikan ep = (struct ext2fs_direct *) ((char *)data + offset); 697f92a609fSpelikan } 698f92a609fSpelikan 699f92a609fSpelikan return (0); 700f92a609fSpelikan } 701f92a609fSpelikan 7025ac2d602Sdownsj /* 7035ac2d602Sdownsj * Do consistency checking on a directory entry: 7045ac2d602Sdownsj * record length must be multiple of 4 7055ac2d602Sdownsj * entry must fit in rest of its dirblksize block 7065ac2d602Sdownsj * record must be large enough to contain entry 7075ac2d602Sdownsj * name is not longer than MAXNAMLEN 7085ac2d602Sdownsj * name must be as long as advertised, and null terminated 7095ac2d602Sdownsj */ 7105ac2d602Sdownsj /* 7115ac2d602Sdownsj * changed so that it confirms to ext2fs_check_dir_entry 7125ac2d602Sdownsj */ 7135ac2d602Sdownsj static int 7145f64cd9cSjasper ext2fs_dirbadentry(struct vnode *dp, struct ext2fs_direct *de, 7155f64cd9cSjasper int entryoffsetinblock) 7165ac2d602Sdownsj { 7175ac2d602Sdownsj int dirblksize = VTOI(dp)->i_e2fs->e2fs_bsize; 7185ac2d602Sdownsj char *error_msg = NULL; 719f7dbefaaSpelikan int reclen = letoh16(de->e2d_reclen); 7207f1901c5Sart int namlen = de->e2d_namlen; 7215ac2d602Sdownsj 7227f1901c5Sart if (reclen < EXT2FS_DIRSIZ(1)) /* e2d_namlen = 1 */ 7235ac2d602Sdownsj error_msg = "rec_len is smaller than minimal"; 7247f1901c5Sart else if (reclen % 4 != 0) 7255ac2d602Sdownsj error_msg = "rec_len % 4 != 0"; 7267f1901c5Sart else if (reclen < EXT2FS_DIRSIZ(namlen)) 7275ac2d602Sdownsj error_msg = "reclen is too small for name_len"; 7287f1901c5Sart else if (entryoffsetinblock + reclen > dirblksize) 7295ac2d602Sdownsj error_msg = "directory entry across blocks"; 730f7dbefaaSpelikan else if (letoh32(de->e2d_ino) > VTOI(dp)->i_e2fs->e2fs.e2fs_icount) 7315ac2d602Sdownsj error_msg = "inode out of bounds"; 7325ac2d602Sdownsj 7335ac2d602Sdownsj if (error_msg != NULL) { 7345ac2d602Sdownsj printf("bad directory entry: %s\n" 735f7dbefaaSpelikan "offset=%d, inode=%u, rec_len=%d, name_len=%d \n", 736f7dbefaaSpelikan error_msg, entryoffsetinblock, letoh32(de->e2d_ino), 7377f1901c5Sart reclen, namlen); 738f7dbefaaSpelikan panic(__func__); 7395ac2d602Sdownsj } 740f7dbefaaSpelikan return (0); 7415ac2d602Sdownsj } 7425ac2d602Sdownsj 7435ac2d602Sdownsj /* 7445ac2d602Sdownsj * Write a directory entry after a call to namei, using the parameters 7455ac2d602Sdownsj * that it left in nameidata. The argument ip is the inode which the new 7465ac2d602Sdownsj * directory entry will refer to. Dvp is a pointer to the directory to 7475ac2d602Sdownsj * be written, which was left locked by namei. Remaining parameters 7485ac2d602Sdownsj * (dp->i_offset, dp->i_count) indicate how the space for the new 7495ac2d602Sdownsj * entry is to be obtained. 7505ac2d602Sdownsj */ 7515ac2d602Sdownsj int 7525f64cd9cSjasper ext2fs_direnter(struct inode *ip, struct vnode *dvp, 7535f64cd9cSjasper struct componentname *cnp) 7545ac2d602Sdownsj { 7557f1901c5Sart struct ext2fs_direct *ep, *nep; 7567f1901c5Sart struct inode *dp; 7575ac2d602Sdownsj struct buf *bp; 7585ac2d602Sdownsj struct ext2fs_direct newdir; 7595ac2d602Sdownsj struct iovec aiov; 7605ac2d602Sdownsj struct uio auio; 7615ac2d602Sdownsj u_int dsize; 7625ac2d602Sdownsj int error, loc, newentrysize, spacefree; 7635ac2d602Sdownsj char *dirbuf; 7645ac2d602Sdownsj int dirblksize = ip->i_e2fs->e2fs_bsize; 7655ac2d602Sdownsj 7665ac2d602Sdownsj 7675ac2d602Sdownsj #ifdef DIAGNOSTIC 7685ac2d602Sdownsj if ((cnp->cn_flags & SAVENAME) == 0) 7695ac2d602Sdownsj panic("direnter: missing name"); 7705ac2d602Sdownsj #endif 7715ac2d602Sdownsj dp = VTOI(dvp); 772f7dbefaaSpelikan newdir.e2d_ino = htole32(ip->i_number); 7735ac2d602Sdownsj newdir.e2d_namlen = cnp->cn_namelen; 774f5ee6277Sjasoni if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 && 775f5ee6277Sjasoni (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) { 776fb844963Spedro newdir.e2d_type = inot2ext2dt(IFTODT(ip->i_e2fs_mode)); 777f5ee6277Sjasoni } else { 778f5ee6277Sjasoni newdir.e2d_type = 0; 779*479c151dSjsg } 7807f1901c5Sart memcpy(newdir.e2d_name, cnp->cn_nameptr, (unsigned)cnp->cn_namelen + 1); 7817f1901c5Sart newentrysize = EXT2FS_DIRSIZ(cnp->cn_namelen); 7825ac2d602Sdownsj if (dp->i_count == 0) { 7835ac2d602Sdownsj /* 7845ac2d602Sdownsj * If dp->i_count is 0, then namei could find no 7855ac2d602Sdownsj * space in the directory. Here, dp->i_offset will 7865ac2d602Sdownsj * be on a directory block boundary and we will write the 7875ac2d602Sdownsj * new entry into a fresh block. 7885ac2d602Sdownsj */ 7895ac2d602Sdownsj if (dp->i_offset & (dirblksize - 1)) 7905ac2d602Sdownsj panic("ext2fs_direnter: newblk"); 7915ac2d602Sdownsj auio.uio_offset = dp->i_offset; 792f7dbefaaSpelikan newdir.e2d_reclen = htole16(dirblksize); 7935ac2d602Sdownsj auio.uio_resid = newentrysize; 7945ac2d602Sdownsj aiov.iov_len = newentrysize; 7955ac2d602Sdownsj aiov.iov_base = (caddr_t)&newdir; 7965ac2d602Sdownsj auio.uio_iov = &aiov; 7975ac2d602Sdownsj auio.uio_iovcnt = 1; 7985ac2d602Sdownsj auio.uio_rw = UIO_WRITE; 7995ac2d602Sdownsj auio.uio_segflg = UIO_SYSSPACE; 80014bf419fSkrw auio.uio_procp = NULL; 8015ac2d602Sdownsj error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred); 8025ac2d602Sdownsj if (dirblksize > 8035ac2d602Sdownsj VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize) 8045ac2d602Sdownsj /* XXX should grow with balloc() */ 8055ac2d602Sdownsj panic("ext2fs_direnter: frag size"); 8065ac2d602Sdownsj else if (!error) { 8074dcbbbb0Sniallo error = ext2fs_setsize(dp, 8084dcbbbb0Sniallo roundup(ext2fs_size(dp), dirblksize)); 8094dcbbbb0Sniallo if (error) 8104dcbbbb0Sniallo return (error); 8115ac2d602Sdownsj dp->i_flag |= IN_CHANGE; 8125ac2d602Sdownsj } 8135ac2d602Sdownsj return (error); 8145ac2d602Sdownsj } 8155ac2d602Sdownsj 8165ac2d602Sdownsj /* 8175ac2d602Sdownsj * If dp->i_count is non-zero, then namei found space 8185ac2d602Sdownsj * for the new entry in the range dp->i_offset to 8195ac2d602Sdownsj * dp->i_offset + dp->i_count in the directory. 8205ac2d602Sdownsj * To use this space, we may have to compact the entries located 8215ac2d602Sdownsj * there, by copying them together towards the beginning of the 8225ac2d602Sdownsj * block, leaving the free space in one usable chunk at the end. 8235ac2d602Sdownsj */ 8245ac2d602Sdownsj 8255ac2d602Sdownsj /* 8265ac2d602Sdownsj * Get the block containing the space for the new directory entry. 8275ac2d602Sdownsj */ 828b080ad39Scsapuntz if ((error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, &dirbuf, &bp)) 829b080ad39Scsapuntz != 0) 8305ac2d602Sdownsj return (error); 8315ac2d602Sdownsj /* 8325ac2d602Sdownsj * Find space for the new entry. In the simple case, the entry at 8335ac2d602Sdownsj * offset base will have the space. If it does not, then namei 8345ac2d602Sdownsj * arranged that compacting the region dp->i_offset to 8355ac2d602Sdownsj * dp->i_offset + dp->i_count would yield the 8365ac2d602Sdownsj * space. 8375ac2d602Sdownsj */ 8385ac2d602Sdownsj ep = (struct ext2fs_direct *)dirbuf; 8395ac2d602Sdownsj dsize = EXT2FS_DIRSIZ(ep->e2d_namlen); 840f7dbefaaSpelikan spacefree = letoh16(ep->e2d_reclen) - dsize; 841f7dbefaaSpelikan for (loc = letoh16(ep->e2d_reclen); loc < dp->i_count; ) { 8425ac2d602Sdownsj nep = (struct ext2fs_direct *)(dirbuf + loc); 8435ac2d602Sdownsj if (ep->e2d_ino) { 8445ac2d602Sdownsj /* trim the existing slot */ 845f7dbefaaSpelikan ep->e2d_reclen = htole16(dsize); 8465ac2d602Sdownsj ep = (struct ext2fs_direct *)((char *)ep + dsize); 8475ac2d602Sdownsj } else { 8485ac2d602Sdownsj /* overwrite; nothing there; header is ours */ 8495ac2d602Sdownsj spacefree += dsize; 8505ac2d602Sdownsj } 8515ac2d602Sdownsj dsize = EXT2FS_DIRSIZ(nep->e2d_namlen); 852f7dbefaaSpelikan spacefree += letoh16(nep->e2d_reclen) - dsize; 853f7dbefaaSpelikan loc += letoh16(nep->e2d_reclen); 8540f5c6c8bStedu memcpy(ep, nep, dsize); 8555ac2d602Sdownsj } 8565ac2d602Sdownsj /* 8575ac2d602Sdownsj * Update the pointer fields in the previous entry (if any), 8585ac2d602Sdownsj * copy in the new entry, and write out the block. 8595ac2d602Sdownsj */ 8605ac2d602Sdownsj if (ep->e2d_ino == 0) { 8615ac2d602Sdownsj #ifdef DIAGNOSTIC 8625ac2d602Sdownsj if (spacefree + dsize < newentrysize) 8635ac2d602Sdownsj panic("ext2fs_direnter: compact1"); 8645ac2d602Sdownsj #endif 865f7dbefaaSpelikan newdir.e2d_reclen = htole16(spacefree + dsize); 8665ac2d602Sdownsj } else { 8675ac2d602Sdownsj #ifdef DIAGNOSTIC 8685ac2d602Sdownsj if (spacefree < newentrysize) { 8695ac2d602Sdownsj printf("ext2fs_direnter: compact2 %u %u", 8705ac2d602Sdownsj (u_int)spacefree, (u_int)newentrysize); 8715ac2d602Sdownsj panic("ext2fs_direnter: compact2"); 8725ac2d602Sdownsj } 8735ac2d602Sdownsj #endif 874f7dbefaaSpelikan newdir.e2d_reclen = htole16(spacefree); 875f7dbefaaSpelikan ep->e2d_reclen = htole16(dsize); 8765ac2d602Sdownsj ep = (struct ext2fs_direct *)((char *)ep + dsize); 8775ac2d602Sdownsj } 8780f5c6c8bStedu memcpy(ep, &newdir, newentrysize); 8795ac2d602Sdownsj error = VOP_BWRITE(bp); 8805ac2d602Sdownsj dp->i_flag |= IN_CHANGE | IN_UPDATE; 8814dcbbbb0Sniallo if (!error && dp->i_endoff && dp->i_endoff < ext2fs_size(dp)) 882b080ad39Scsapuntz error = ext2fs_truncate(dp, (off_t)dp->i_endoff, IO_SYNC, 883b080ad39Scsapuntz cnp->cn_cred); 8845ac2d602Sdownsj return (error); 8855ac2d602Sdownsj } 8865ac2d602Sdownsj 8875ac2d602Sdownsj /* 8885ac2d602Sdownsj * Remove a directory entry after a call to namei, using 8895ac2d602Sdownsj * the parameters which it left in nameidata. The entry 8905ac2d602Sdownsj * dp->i_offset contains the offset into the directory of the 8915ac2d602Sdownsj * entry to be eliminated. The dp->i_count field contains the 8925ac2d602Sdownsj * size of the previous record in the directory. If this 8935ac2d602Sdownsj * is 0, the first entry is being deleted, so we need only 8945ac2d602Sdownsj * zero the inode number to mark the entry as free. If the 8955ac2d602Sdownsj * entry is not the first in the directory, we must reclaim 8965ac2d602Sdownsj * the space of the now empty record by adding the record size 8975ac2d602Sdownsj * to the size of the previous entry. 8985ac2d602Sdownsj */ 8995ac2d602Sdownsj int 9005f64cd9cSjasper ext2fs_dirremove(struct vnode *dvp, struct componentname *cnp) 9015ac2d602Sdownsj { 9027f1901c5Sart struct inode *dp; 9035ac2d602Sdownsj struct ext2fs_direct *ep; 9045ac2d602Sdownsj struct buf *bp; 9055ac2d602Sdownsj int error; 9065ac2d602Sdownsj 9075ac2d602Sdownsj dp = VTOI(dvp); 9085ac2d602Sdownsj if (dp->i_count == 0) { 9095ac2d602Sdownsj /* 9105ac2d602Sdownsj * First entry in block: set d_ino to zero. 9115ac2d602Sdownsj */ 912b080ad39Scsapuntz error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, (char **)&ep, 913b080ad39Scsapuntz &bp); 9145ac2d602Sdownsj if (error != 0) 9155ac2d602Sdownsj return (error); 9165ac2d602Sdownsj ep->e2d_ino = 0; 9175ac2d602Sdownsj error = VOP_BWRITE(bp); 9185ac2d602Sdownsj dp->i_flag |= IN_CHANGE | IN_UPDATE; 9195ac2d602Sdownsj return (error); 9205ac2d602Sdownsj } 9215ac2d602Sdownsj /* 9225ac2d602Sdownsj * Collapse new free space into previous entry. 9235ac2d602Sdownsj */ 924b080ad39Scsapuntz error = ext2fs_bufatoff(dp, (off_t)(dp->i_offset - dp->i_count), 9255ac2d602Sdownsj (char **)&ep, &bp); 9265ac2d602Sdownsj if (error != 0) 9275ac2d602Sdownsj return (error); 928f7dbefaaSpelikan ep->e2d_reclen = htole16(letoh16(ep->e2d_reclen) + dp->i_reclen); 9295ac2d602Sdownsj error = VOP_BWRITE(bp); 9305ac2d602Sdownsj dp->i_flag |= IN_CHANGE | IN_UPDATE; 9315ac2d602Sdownsj return (error); 9325ac2d602Sdownsj } 9335ac2d602Sdownsj 9345ac2d602Sdownsj /* 9355ac2d602Sdownsj * Rewrite an existing directory entry to point at the inode 9365ac2d602Sdownsj * supplied. The parameters describing the directory entry are 9375ac2d602Sdownsj * set up by a call to namei. 9385ac2d602Sdownsj */ 9395ac2d602Sdownsj int 9405f64cd9cSjasper ext2fs_dirrewrite(struct inode *dp, struct inode *ip, 9415f64cd9cSjasper struct componentname *cnp) 9425ac2d602Sdownsj { 9435ac2d602Sdownsj struct buf *bp; 9445ac2d602Sdownsj struct ext2fs_direct *ep; 9455ac2d602Sdownsj int error; 9465ac2d602Sdownsj 947b080ad39Scsapuntz error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, (char **)&ep, &bp); 9485ac2d602Sdownsj if (error != 0) 9495ac2d602Sdownsj return (error); 950f7dbefaaSpelikan ep->e2d_ino = htole32(ip->i_number); 951f5ee6277Sjasoni if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 && 952f5ee6277Sjasoni (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) { 953fb844963Spedro ep->e2d_type = inot2ext2dt(IFTODT(ip->i_e2fs_mode)); 954f5ee6277Sjasoni } else { 955f5ee6277Sjasoni ep->e2d_type = 0; 956f5ee6277Sjasoni } 9575ac2d602Sdownsj error = VOP_BWRITE(bp); 9585ac2d602Sdownsj dp->i_flag |= IN_CHANGE | IN_UPDATE; 9595ac2d602Sdownsj return (error); 9605ac2d602Sdownsj } 9615ac2d602Sdownsj 9625ac2d602Sdownsj /* 9635ac2d602Sdownsj * Check if a directory is empty or not. 9645ac2d602Sdownsj * Inode supplied must be locked. 9655ac2d602Sdownsj * 9665ac2d602Sdownsj * Using a struct dirtemplate here is not precisely 9675ac2d602Sdownsj * what we want, but better than using a struct ext2fs_direct. 9685ac2d602Sdownsj * 9695ac2d602Sdownsj * NB: does not handle corrupted directories. 9705ac2d602Sdownsj */ 9715ac2d602Sdownsj int 972e012d6d3Sguenther ext2fs_dirempty(struct inode *ip, ufsino_t parentino, struct ucred *cred) 9735ac2d602Sdownsj { 9747f1901c5Sart off_t off; 9755ac2d602Sdownsj struct ext2fs_dirtemplate dbuf; 9767f1901c5Sart struct ext2fs_direct *dp = (struct ext2fs_direct *)&dbuf; 9779b355cb2Smillert int error, namlen; 9789b355cb2Smillert size_t count; 9795ac2d602Sdownsj 9805ac2d602Sdownsj #define MINDIRSIZ (sizeof (struct ext2fs_dirtemplate) / 2) 9815ac2d602Sdownsj 982f7dbefaaSpelikan for (off = 0; off < ext2fs_size(ip); off += letoh16(dp->e2d_reclen)) { 9835ac2d602Sdownsj error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off, 984717ea6e5Soga UIO_SYSSPACE, IO_NODELOCKED, cred, &count, curproc); 9855ac2d602Sdownsj /* 9865ac2d602Sdownsj * Since we read MINDIRSIZ, residual must 9875ac2d602Sdownsj * be 0 unless we're at end of file. 9885ac2d602Sdownsj */ 9895ac2d602Sdownsj if (error || count != 0) 9905ac2d602Sdownsj return (0); 9915ac2d602Sdownsj /* avoid infinite loops */ 9925ac2d602Sdownsj if (dp->e2d_reclen == 0) 9935ac2d602Sdownsj return (0); 9945ac2d602Sdownsj /* skip empty entries */ 9955ac2d602Sdownsj if (dp->e2d_ino == 0) 9965ac2d602Sdownsj continue; 9975ac2d602Sdownsj /* accept only "." and ".." */ 9985ac2d602Sdownsj namlen = dp->e2d_namlen; 9995ac2d602Sdownsj if (namlen > 2) 10005ac2d602Sdownsj return (0); 10015ac2d602Sdownsj if (dp->e2d_name[0] != '.') 10025ac2d602Sdownsj return (0); 10035ac2d602Sdownsj /* 10045ac2d602Sdownsj * At this point namlen must be 1 or 2. 10055ac2d602Sdownsj * 1 implies ".", 2 implies ".." if second 10065ac2d602Sdownsj * char is also "." 10075ac2d602Sdownsj */ 10085ac2d602Sdownsj if (namlen == 1) 10095ac2d602Sdownsj continue; 1010f7dbefaaSpelikan if (dp->e2d_name[1] == '.' && letoh32(dp->e2d_ino) == parentino) 10115ac2d602Sdownsj continue; 10125ac2d602Sdownsj return (0); 10135ac2d602Sdownsj } 10145ac2d602Sdownsj return (1); 10155ac2d602Sdownsj } 10165ac2d602Sdownsj 10175ac2d602Sdownsj /* 10185ac2d602Sdownsj * Check if source directory is in the path of the target directory. 10195ac2d602Sdownsj * Target is supplied locked, source is unlocked. 10205ac2d602Sdownsj * The target is always vput before returning. 10215ac2d602Sdownsj */ 10225ac2d602Sdownsj int 10235f64cd9cSjasper ext2fs_checkpath(struct inode *source, struct inode *target, 10245f64cd9cSjasper struct ucred *cred) 10255ac2d602Sdownsj { 10265ac2d602Sdownsj struct vnode *vp; 10275ac2d602Sdownsj int error, rootino, namlen; 10285ac2d602Sdownsj struct ext2fs_dirtemplate dirbuf; 10297f1901c5Sart u_int32_t ino; 10305ac2d602Sdownsj 10315ac2d602Sdownsj vp = ITOV(target); 10325ac2d602Sdownsj if (target->i_number == source->i_number) { 10335ac2d602Sdownsj error = EEXIST; 10345ac2d602Sdownsj goto out; 10355ac2d602Sdownsj } 10365ac2d602Sdownsj rootino = ROOTINO; 10375ac2d602Sdownsj error = 0; 10385ac2d602Sdownsj if (target->i_number == rootino) 10395ac2d602Sdownsj goto out; 10405ac2d602Sdownsj 10415ac2d602Sdownsj for (;;) { 10425ac2d602Sdownsj if (vp->v_type != VDIR) { 10435ac2d602Sdownsj error = ENOTDIR; 10445ac2d602Sdownsj break; 10455ac2d602Sdownsj } 10465ac2d602Sdownsj error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf, 10477f1901c5Sart sizeof (struct ext2fs_dirtemplate), (off_t)0, 1048717ea6e5Soga UIO_SYSSPACE, IO_NODELOCKED, cred, NULL, 1049717ea6e5Soga curproc); 10505ac2d602Sdownsj if (error != 0) 10515ac2d602Sdownsj break; 10525ac2d602Sdownsj namlen = dirbuf.dotdot_namlen; 10535ac2d602Sdownsj if (namlen != 2 || 10545ac2d602Sdownsj dirbuf.dotdot_name[0] != '.' || 10555ac2d602Sdownsj dirbuf.dotdot_name[1] != '.') { 10565ac2d602Sdownsj error = ENOTDIR; 10575ac2d602Sdownsj break; 10585ac2d602Sdownsj } 1059f7dbefaaSpelikan ino = letoh32(dirbuf.dotdot_ino); 10607f1901c5Sart if (ino == source->i_number) { 10615ac2d602Sdownsj error = EINVAL; 10625ac2d602Sdownsj break; 10635ac2d602Sdownsj } 10647f1901c5Sart if (ino == rootino) 10655ac2d602Sdownsj break; 10665ac2d602Sdownsj vput(vp); 10677f1901c5Sart error = VFS_VGET(vp->v_mount, ino, &vp); 10685ac2d602Sdownsj if (error != 0) { 10695ac2d602Sdownsj vp = NULL; 10705ac2d602Sdownsj break; 10715ac2d602Sdownsj } 10725ac2d602Sdownsj } 10735ac2d602Sdownsj 10745ac2d602Sdownsj out: 10755ac2d602Sdownsj if (error == ENOTDIR) { 10765ac2d602Sdownsj printf("checkpath: .. not a directory\n"); 10775ac2d602Sdownsj panic("checkpath"); 10785ac2d602Sdownsj } 10795ac2d602Sdownsj if (vp != NULL) 10805ac2d602Sdownsj vput(vp); 10815ac2d602Sdownsj return (error); 10825ac2d602Sdownsj } 1083