ext2_lookup.c revision 247055
1139778Simp/*- 212115Sdyson * modified for Lites 1.1 312115Sdyson * 412115Sdyson * Aug 1995, Godmar Back (gback@cs.utah.edu) 512115Sdyson * University of Utah, Department of Computer Science 612115Sdyson */ 7139778Simp/*- 812115Sdyson * Copyright (c) 1989, 1993 912115Sdyson * The Regents of the University of California. All rights reserved. 1012115Sdyson * (c) UNIX System Laboratories, Inc. 1112115Sdyson * All or some portions of this file are derived from material licensed 1212115Sdyson * to the University of California by American Telephone and Telegraph 1312115Sdyson * Co. or Unix System Laboratories, Inc. and are reproduced herein with 1412115Sdyson * the permission of UNIX System Laboratories, Inc. 1512115Sdyson * 1612115Sdyson * Redistribution and use in source and binary forms, with or without 1712115Sdyson * modification, are permitted provided that the following conditions 1812115Sdyson * are met: 1912115Sdyson * 1. Redistributions of source code must retain the above copyright 2012115Sdyson * notice, this list of conditions and the following disclaimer. 2112115Sdyson * 2. Redistributions in binary form must reproduce the above copyright 2212115Sdyson * notice, this list of conditions and the following disclaimer in the 2312115Sdyson * documentation and/or other materials provided with the distribution. 2412115Sdyson * 4. Neither the name of the University nor the names of its contributors 2512115Sdyson * may be used to endorse or promote products derived from this software 2612115Sdyson * without specific prior written permission. 2712115Sdyson * 2812115Sdyson * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2912115Sdyson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 3012115Sdyson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 3112115Sdyson * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 3212115Sdyson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3312115Sdyson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3412115Sdyson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3512115Sdyson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3612115Sdyson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3712115Sdyson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3812115Sdyson * SUCH DAMAGE. 3912115Sdyson * 4012115Sdyson * @(#)ufs_lookup.c 8.6 (Berkeley) 4/1/94 4193016Sbde * $FreeBSD: stable/9/sys/fs/ext2fs/ext2_lookup.c 247055 2013-02-20 20:56:07Z pfg $ 4212115Sdyson */ 4312115Sdyson 4412115Sdyson#include <sys/param.h> 4512159Sbde#include <sys/systm.h> 4612115Sdyson#include <sys/namei.h> 4760041Sphk#include <sys/bio.h> 4812115Sdyson#include <sys/buf.h> 49193377Sstas#include <sys/endian.h> 5012115Sdyson#include <sys/mount.h> 5112115Sdyson#include <sys/vnode.h> 5212115Sdyson#include <sys/malloc.h> 5312115Sdyson#include <sys/dirent.h> 5496753Siedowse#include <sys/sysctl.h> 5512115Sdyson 5612115Sdyson#include <ufs/ufs/dir.h> 5712115Sdyson 58202283Slulf#include <fs/ext2fs/inode.h> 59202283Slulf#include <fs/ext2fs/ext2_mount.h> 60202283Slulf#include <fs/ext2fs/ext2fs.h> 61221128Sjhb#include <fs/ext2fs/ext2_dinode.h> 62202283Slulf#include <fs/ext2fs/ext2_dir.h> 63221128Sjhb#include <fs/ext2fs/ext2_extern.h> 6412115Sdyson 6596753Siedowse#ifdef DIAGNOSTIC 6696753Siedowsestatic int dirchk = 1; 6796753Siedowse#else 6896753Siedowsestatic int dirchk = 0; 6996753Siedowse#endif 7096753Siedowse 71141633Sphkstatic SYSCTL_NODE(_vfs, OID_AUTO, e2fs, CTLFLAG_RD, 0, "EXT2FS filesystem"); 7296753SiedowseSYSCTL_INT(_vfs_e2fs, OID_AUTO, dircheck, CTLFLAG_RW, &dirchk, 0, ""); 7396753Siedowse 74111742Sdes/* 7512115Sdyson DIRBLKSIZE in ffs is DEV_BSIZE (in most cases 512) 7612115Sdyson while it is the native blocksize in ext2fs - thus, a #define 7712115Sdyson is no longer appropriate 7812115Sdyson*/ 7912115Sdyson#undef DIRBLKSIZ 8012115Sdyson 8155477Sbdestatic u_char ext2_ft_to_dt[] = { 8255477Sbde DT_UNKNOWN, /* EXT2_FT_UNKNOWN */ 8355477Sbde DT_REG, /* EXT2_FT_REG_FILE */ 8455477Sbde DT_DIR, /* EXT2_FT_DIR */ 8555477Sbde DT_CHR, /* EXT2_FT_CHRDEV */ 8655477Sbde DT_BLK, /* EXT2_FT_BLKDEV */ 8755477Sbde DT_FIFO, /* EXT2_FT_FIFO */ 8855477Sbde DT_SOCK, /* EXT2_FT_SOCK */ 8955477Sbde DT_LNK, /* EXT2_FT_SYMLINK */ 9055477Sbde}; 91247055Spfg#define FTTODT(ft) \ 92247055Spfg ((ft) < nitems(ext2_ft_to_dt) ? ext2_ft_to_dt[(ft)] : DT_UNKNOWN) 9355477Sbde 9455477Sbdestatic u_char dt_to_ext2_ft[] = { 9555477Sbde EXT2_FT_UNKNOWN, /* DT_UNKNOWN */ 9655477Sbde EXT2_FT_FIFO, /* DT_FIFO */ 9755477Sbde EXT2_FT_CHRDEV, /* DT_CHR */ 9855477Sbde EXT2_FT_UNKNOWN, /* unused */ 9955477Sbde EXT2_FT_DIR, /* DT_DIR */ 10055477Sbde EXT2_FT_UNKNOWN, /* unused */ 10155477Sbde EXT2_FT_BLKDEV, /* DT_BLK */ 10255477Sbde EXT2_FT_UNKNOWN, /* unused */ 10355477Sbde EXT2_FT_REG_FILE, /* DT_REG */ 10455477Sbde EXT2_FT_UNKNOWN, /* unused */ 10555477Sbde EXT2_FT_SYMLINK, /* DT_LNK */ 10655477Sbde EXT2_FT_UNKNOWN, /* unused */ 10755477Sbde EXT2_FT_SOCK, /* DT_SOCK */ 10855477Sbde EXT2_FT_UNKNOWN, /* unused */ 10955477Sbde EXT2_FT_UNKNOWN, /* DT_WHT */ 11055477Sbde}; 111247055Spfg#define DTTOFT(dt) \ 112247055Spfg ((dt) < nitems(dt_to_ext2_ft) ? dt_to_ext2_ft[(dt)] : EXT2_FT_UNKNOWN) 11355477Sbde 114202283Slulfstatic int ext2_dirbadentry(struct vnode *dp, struct ext2fs_direct_2 *de, 11593014Sbde int entryoffsetinblock); 116235820Spfgstatic int ext2_lookup_ino(struct vnode *vdp, struct vnode **vpp, 117235820Spfg struct componentname *cnp, ino_t *dd_ino); 11812159Sbde 11912115Sdyson/* 12012115Sdyson * Vnode op for reading directories. 12112115Sdyson * 122221867Sjhb * This function has to convert directory entries from the on-disk 123221867Sjhb * format to the format defined by <sys/dirent.h>. Unfortunately, the 124221867Sjhb * conversion will blow up some entries by four bytes, so it can't be 125221867Sjhb * done in place. Instead, the conversion is done entry by entry and 126221867Sjhb * the converted entry is sent via uiomove. 12712115Sdyson * 12812115Sdyson * XXX allocate a buffer, convert as many entries as possible, then send 12912115Sdyson * the whole buffer to uiomove 13012115Sdyson */ 13112115Sdysonint 13212115Sdysonext2_readdir(ap) 133111742Sdes struct vop_readdir_args /* { 134111742Sdes struct vnode *a_vp; 135111742Sdes struct uio *a_uio; 136111742Sdes struct ucred *a_cred; 137111742Sdes } */ *ap; 13812115Sdyson{ 139111742Sdes struct uio *uio = ap->a_uio; 140111742Sdes int count, error; 14112115Sdyson 142202283Slulf struct ext2fs_direct_2 *edp, *dp; 14324649Sdfr int ncookies; 14412115Sdyson struct dirent dstdp; 14512115Sdyson struct uio auio; 14612115Sdyson struct iovec aiov; 14712115Sdyson caddr_t dirbuf; 148202283Slulf int DIRBLKSIZ = VTOI(ap->a_vp)->i_e2fs->e2fs_bsize; 14912115Sdyson int readcnt; 15065780Sbde off_t startoffset = uio->uio_offset; 15112115Sdyson 15265780Sbde count = uio->uio_resid; 15365780Sbde /* 15465780Sbde * Avoid complications for partial directory entries by adjusting 15565780Sbde * the i/o to end at a block boundary. Don't give up (like ufs 15665780Sbde * does) if the initial adjustment gives a negative count, since 15765780Sbde * many callers don't supply a large enough buffer. The correct 15865780Sbde * size is a little larger than DIRBLKSIZ to allow for expansion 15965780Sbde * of directory entries, but some callers just use 512. 16065780Sbde */ 16165780Sbde count -= (uio->uio_offset + count) & (DIRBLKSIZ -1); 16265780Sbde if (count <= 0) 16365780Sbde count += DIRBLKSIZ; 16412115Sdyson auio = *uio; 16512115Sdyson auio.uio_iov = &aiov; 16612115Sdyson auio.uio_iovcnt = 1; 16765780Sbde auio.uio_resid = count; 16812115Sdyson auio.uio_segflg = UIO_SYSSPACE; 16912115Sdyson aiov.iov_len = count; 170184205Sdes dirbuf = malloc(count, M_TEMP, M_WAITOK); 17112115Sdyson aiov.iov_base = dirbuf; 17212115Sdyson error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred); 17312115Sdyson if (error == 0) { 17412115Sdyson readcnt = count - auio.uio_resid; 175202283Slulf edp = (struct ext2fs_direct_2 *)&dirbuf[readcnt]; 17624649Sdfr ncookies = 0; 17755477Sbde bzero(&dstdp, offsetof(struct dirent, d_name)); 178202283Slulf for (dp = (struct ext2fs_direct_2 *)dirbuf; 17955477Sbde !error && uio->uio_resid > 0 && dp < edp; ) { 18055477Sbde /*- 18155477Sbde * "New" ext2fs directory entries differ in 3 ways 18255477Sbde * from ufs on-disk ones: 18355477Sbde * - the name is not necessarily NUL-terminated. 18455477Sbde * - the file type field always exists and always 185125843Sbde * follows the name length field. 18655477Sbde * - the file type is encoded in a different way. 18755477Sbde * 18855477Sbde * "Old" ext2fs directory entries need no special 189125843Sbde * conversions, since they are binary compatible 190125843Sbde * with "new" entries having a file type of 0 (i.e., 19155477Sbde * EXT2_FT_UNKNOWN). Splitting the old name length 19255477Sbde * field didn't make a mess like it did in ufs, 193125843Sbde * because ext2fs uses a machine-independent disk 19455477Sbde * layout. 19555477Sbde */ 196202283Slulf dstdp.d_fileno = dp->e2d_ino; 197202283Slulf dstdp.d_type = FTTODT(dp->e2d_type); 198202283Slulf dstdp.d_namlen = dp->e2d_namlen; 19955477Sbde dstdp.d_reclen = GENERIC_DIRSIZ(&dstdp); 200202283Slulf bcopy(dp->e2d_name, dstdp.d_name, dstdp.d_namlen); 20155477Sbde bzero(dstdp.d_name + dstdp.d_namlen, 20255477Sbde dstdp.d_reclen - offsetof(struct dirent, d_name) - 20355477Sbde dstdp.d_namlen); 20455477Sbde 205202283Slulf if (dp->e2d_reclen > 0) { 20612115Sdyson if(dstdp.d_reclen <= uio->uio_resid) { 20712115Sdyson /* advance dp */ 208202283Slulf dp = (struct ext2fs_direct_2 *) 209202283Slulf ((char *)dp + dp->e2d_reclen); 210111742Sdes error = 211111741Sdes uiomove(&dstdp, dstdp.d_reclen, uio); 21224649Sdfr if (!error) 21324649Sdfr ncookies++; 21412115Sdyson } else 21512115Sdyson break; 21612115Sdyson } else { 21712115Sdyson error = EIO; 21812115Sdyson break; 21912115Sdyson } 22012115Sdyson } 22112115Sdyson /* we need to correct uio_offset */ 22212115Sdyson uio->uio_offset = startoffset + (caddr_t)dp - dirbuf; 22324649Sdfr 22424649Sdfr if (!error && ap->a_ncookies != NULL) { 22565780Sbde u_long *cookiep, *cookies, *ecookies; 22624649Sdfr off_t off; 22724649Sdfr 22824649Sdfr if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1) 229125843Sbde panic("ext2_readdir: unexpected uio from NFS server"); 230184205Sdes cookies = malloc(ncookies * sizeof(u_long), M_TEMP, 231111119Simp M_WAITOK); 23224649Sdfr off = startoffset; 233202283Slulf for (dp = (struct ext2fs_direct_2 *)dirbuf, 23465780Sbde cookiep = cookies, ecookies = cookies + ncookies; 23565780Sbde cookiep < ecookies; 236202283Slulf dp = (struct ext2fs_direct_2 *)((caddr_t) dp + dp->e2d_reclen)) { 237202283Slulf off += dp->e2d_reclen; 23824649Sdfr *cookiep++ = (u_long) off; 23924649Sdfr } 24024649Sdfr *ap->a_ncookies = ncookies; 24124649Sdfr *ap->a_cookies = cookies; 24224649Sdfr } 24312115Sdyson } 244184205Sdes free(dirbuf, M_TEMP); 24524649Sdfr if (ap->a_eofflag) 24624649Sdfr *ap->a_eofflag = VTOI(ap->a_vp)->i_size <= uio->uio_offset; 247111742Sdes return (error); 24812115Sdyson} 24912115Sdyson 25012115Sdyson/* 25112115Sdyson * Convert a component of a pathname into a pointer to a locked inode. 25212115Sdyson * This is a very central and rather complicated routine. 25312115Sdyson * If the file system is not maintained in a strict tree hierarchy, 25412115Sdyson * this can result in a deadlock situation (see comments in code below). 25512115Sdyson * 25612115Sdyson * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending 25712115Sdyson * on whether the name is to be looked up, created, renamed, or deleted. 25812115Sdyson * When CREATE, RENAME, or DELETE is specified, information usable in 25912115Sdyson * creating, renaming, or deleting a directory entry may be calculated. 26012115Sdyson * If flag has LOCKPARENT or'ed into it and the target of the pathname 26112115Sdyson * exists, lookup returns both the target and its parent directory locked. 26212115Sdyson * When creating or renaming and LOCKPARENT is specified, the target may 26312115Sdyson * not be ".". When deleting and LOCKPARENT is specified, the target may 26412115Sdyson * be "."., but the caller must check to ensure it does an vrele and vput 26512115Sdyson * instead of two vputs. 26612115Sdyson * 267125843Sbde * Overall outline of ext2_lookup: 26812115Sdyson * 26912115Sdyson * search for name in directory, to found or notfound 27012115Sdyson * notfound: 27112115Sdyson * if creating, return locked directory, leaving info on available slots 27212115Sdyson * else return error 27312115Sdyson * found: 27412115Sdyson * if at end of path and deleting, return information to allow delete 27512115Sdyson * if at end of path and rewriting (RENAME and LOCKPARENT), lock target 27612115Sdyson * inode and return info to allow rewrite 27712115Sdyson * if not at end, add name to cache; if at end and neither creating 27812115Sdyson * nor deleting, add name to cache 27912115Sdyson */ 28012115Sdysonint 28112115Sdysonext2_lookup(ap) 28228787Sphk struct vop_cachedlookup_args /* { 28312115Sdyson struct vnode *a_dvp; 28412115Sdyson struct vnode **a_vpp; 28512115Sdyson struct componentname *a_cnp; 28612115Sdyson } */ *ap; 28712115Sdyson{ 288235820Spfg 289235820Spfg return (ext2_lookup_ino(ap->a_dvp, ap->a_vpp, ap->a_cnp, NULL)); 290235820Spfg} 291235820Spfg 292235820Spfgstatic int 293235820Spfgext2_lookup_ino(struct vnode *vdp, struct vnode **vpp, struct componentname *cnp, 294235820Spfg ino_t *dd_ino) 295235820Spfg{ 29696752Siedowse struct inode *dp; /* inode for directory being searched */ 29712115Sdyson struct buf *bp; /* a buffer of directory entries */ 298202283Slulf struct ext2fs_direct_2 *ep; /* the current directory entry */ 29912115Sdyson int entryoffsetinblock; /* offset of ep in bp's buffer */ 30012115Sdyson enum {NONE, COMPACT, FOUND} slotstatus; 30112115Sdyson doff_t slotoffset; /* offset of area with free space */ 30212115Sdyson int slotsize; /* size of area at slotoffset */ 303202283Slulf doff_t i_diroff; /* cached i_diroff value */ 304202283Slulf doff_t i_offset; /* cached i_offset value */ 30512115Sdyson int slotfreespace; /* amount of space free in slot */ 30612115Sdyson int slotneeded; /* size of the entry we're seeking */ 30712115Sdyson int numdirpasses; /* strategy for directory search */ 30812115Sdyson doff_t endsearch; /* offset to end directory search */ 30912115Sdyson doff_t prevoff; /* prev entry dp->i_offset */ 31012115Sdyson struct vnode *pdp; /* saved dp during symlink work */ 31112115Sdyson struct vnode *tdp; /* returned by VFS_VGET */ 31212115Sdyson doff_t enduseful; /* pointer past last used dir slot */ 31312115Sdyson u_long bmask; /* block offset mask */ 31412115Sdyson int namlen, error; 31512115Sdyson struct ucred *cred = cnp->cn_cred; 31612115Sdyson int flags = cnp->cn_flags; 31712115Sdyson int nameiop = cnp->cn_nameiop; 318235820Spfg ino_t ino, ino1; 319202283Slulf int ltype; 32012115Sdyson 321235820Spfg int DIRBLKSIZ = VTOI(vdp)->i_e2fs->e2fs_bsize; 32212115Sdyson 323235820Spfg if (vpp != NULL) 324235820Spfg *vpp = NULL; 325235820Spfg 326235820Spfg dp = VTOI(vdp); 327235820Spfg bmask = VFSTOEXT2(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1; 328235820Spfgrestart: 32912115Sdyson bp = NULL; 33012115Sdyson slotoffset = -1; 331217584Sjhb 33212115Sdyson /* 33312115Sdyson * We now have a segment name to search for, and a directory to search. 33412115Sdyson */ 33512115Sdyson 33612115Sdyson /* 33712115Sdyson * Suppress search for slots unless creating 33812115Sdyson * file and at end of pathname, in which case 33912115Sdyson * we watch for a place to put the new file in 34012115Sdyson * case it doesn't already exist. 34112115Sdyson */ 342202283Slulf ino = 0; 343202283Slulf i_diroff = dp->i_diroff; 34412115Sdyson slotstatus = FOUND; 34512115Sdyson slotfreespace = slotsize = slotneeded = 0; 34612115Sdyson if ((nameiop == CREATE || nameiop == RENAME) && 34712115Sdyson (flags & ISLASTCN)) { 34812115Sdyson slotstatus = NONE; 349111742Sdes slotneeded = EXT2_DIR_REC_LEN(cnp->cn_namelen); 35012115Sdyson /* was 35112115Sdyson slotneeded = (sizeof(struct direct) - MAXNAMLEN + 35212115Sdyson cnp->cn_namelen + 3) &~ 3; */ 35312115Sdyson } 35412115Sdyson 35512115Sdyson /* 35612115Sdyson * If there is cached information on a previous search of 35712115Sdyson * this directory, pick up where we last left off. 35812115Sdyson * We cache only lookups as these are the most common 35912115Sdyson * and have the greatest payoff. Caching CREATE has little 36012115Sdyson * benefit as it usually must search the entire directory 36112115Sdyson * to determine that the entry does not exist. Caching the 36212115Sdyson * location of the last DELETE or RENAME has not reduced 36312115Sdyson * profiling time and hence has been removed in the interest 36412115Sdyson * of simplicity. 36512115Sdyson */ 366202283Slulf if (nameiop != LOOKUP || i_diroff == 0 || 367202283Slulf i_diroff > dp->i_size) { 36812115Sdyson entryoffsetinblock = 0; 369202283Slulf i_offset = 0; 37012115Sdyson numdirpasses = 1; 37112115Sdyson } else { 372202283Slulf i_offset = i_diroff; 373202283Slulf if ((entryoffsetinblock = i_offset & bmask) && 374202283Slulf (error = ext2_blkatoff(vdp, (off_t)i_offset, NULL, 37596749Siedowse &bp))) 37612115Sdyson return (error); 37712115Sdyson numdirpasses = 2; 37812115Sdyson nchstats.ncs_2passes++; 37912115Sdyson } 380202283Slulf prevoff = i_offset; 381202283Slulf endsearch = roundup2(dp->i_size, DIRBLKSIZ); 38212115Sdyson enduseful = 0; 38312115Sdyson 38412115Sdysonsearchloop: 385202283Slulf while (i_offset < endsearch) { 38612115Sdyson /* 38712115Sdyson * If necessary, get the next directory block. 38812115Sdyson */ 389202283Slulf if ((i_offset & bmask) == 0) { 39012115Sdyson if (bp != NULL) 39112115Sdyson brelse(bp); 39243301Sdillon if ((error = 393202283Slulf ext2_blkatoff(vdp, (off_t)i_offset, NULL, 39496749Siedowse &bp)) != 0) 39512115Sdyson return (error); 39612115Sdyson entryoffsetinblock = 0; 39712115Sdyson } 39812115Sdyson /* 39912115Sdyson * If still looking for a slot, and at a DIRBLKSIZE 40012115Sdyson * boundary, have to start looking for free space again. 40112115Sdyson */ 40212115Sdyson if (slotstatus == NONE && 40312115Sdyson (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) { 40412115Sdyson slotoffset = -1; 40512115Sdyson slotfreespace = 0; 40612115Sdyson } 40712115Sdyson /* 40812115Sdyson * Get pointer to next entry. 40912115Sdyson * Full validation checks are slow, so we only check 41012115Sdyson * enough to insure forward progress through the 41196753Siedowse * directory. Complete checks can be run by setting 41296753Siedowse * "vfs.e2fs.dirchk" to be true. 41312115Sdyson */ 414202283Slulf ep = (struct ext2fs_direct_2 *) 41512115Sdyson ((char *)bp->b_data + entryoffsetinblock); 416202283Slulf if (ep->e2d_reclen == 0 || 41712147Sdyson (dirchk && ext2_dirbadentry(vdp, ep, entryoffsetinblock))) { 41812115Sdyson int i; 419202283Slulf ext2_dirbad(dp, i_offset, "mangled entry"); 42012115Sdyson i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)); 421202283Slulf i_offset += i; 42212115Sdyson entryoffsetinblock += i; 42312115Sdyson continue; 42412115Sdyson } 42512115Sdyson 42612115Sdyson /* 42712115Sdyson * If an appropriate sized slot has not yet been found, 42812115Sdyson * check to see if one is available. Also accumulate space 42912115Sdyson * in the current block so that we can determine if 43012115Sdyson * compaction is viable. 43112115Sdyson */ 43212115Sdyson if (slotstatus != FOUND) { 433202283Slulf int size = ep->e2d_reclen; 43412115Sdyson 435202283Slulf if (ep->e2d_ino != 0) 436202283Slulf size -= EXT2_DIR_REC_LEN(ep->e2d_namlen); 43712115Sdyson if (size > 0) { 43812115Sdyson if (size >= slotneeded) { 43912115Sdyson slotstatus = FOUND; 440202283Slulf slotoffset = i_offset; 441202283Slulf slotsize = ep->e2d_reclen; 44212115Sdyson } else if (slotstatus == NONE) { 44312115Sdyson slotfreespace += size; 44412115Sdyson if (slotoffset == -1) 445202283Slulf slotoffset = i_offset; 44612115Sdyson if (slotfreespace >= slotneeded) { 44712115Sdyson slotstatus = COMPACT; 448202283Slulf slotsize = i_offset + 449202283Slulf ep->e2d_reclen - slotoffset; 45012115Sdyson } 45112115Sdyson } 45212115Sdyson } 45312115Sdyson } 45412115Sdyson 45512115Sdyson /* 45612115Sdyson * Check for a name match. 45712115Sdyson */ 458202283Slulf if (ep->e2d_ino) { 459202283Slulf namlen = ep->e2d_namlen; 46012115Sdyson if (namlen == cnp->cn_namelen && 461202283Slulf !bcmp(cnp->cn_nameptr, ep->e2d_name, 46212115Sdyson (unsigned)namlen)) { 46312115Sdyson /* 46412115Sdyson * Save directory entry's inode number and 46512115Sdyson * reclen in ndp->ni_ufs area, and release 46612115Sdyson * directory buffer. 46712115Sdyson */ 468202283Slulf ino = ep->e2d_ino; 46912115Sdyson goto found; 47012115Sdyson } 47112115Sdyson } 472202283Slulf prevoff = i_offset; 473202283Slulf i_offset += ep->e2d_reclen; 474202283Slulf entryoffsetinblock += ep->e2d_reclen; 475202283Slulf if (ep->e2d_ino) 476202283Slulf enduseful = i_offset; 47712115Sdyson } 47812115Sdyson/* notfound: */ 47912115Sdyson /* 48012115Sdyson * If we started in the middle of the directory and failed 48112115Sdyson * to find our target, we must check the beginning as well. 48212115Sdyson */ 48312115Sdyson if (numdirpasses == 2) { 48412115Sdyson numdirpasses--; 485202283Slulf i_offset = 0; 486202283Slulf endsearch = i_diroff; 48712115Sdyson goto searchloop; 48812115Sdyson } 489202283Slulf dp->i_offset = i_offset; 49012115Sdyson if (bp != NULL) 49112115Sdyson brelse(bp); 49212115Sdyson /* 49312115Sdyson * If creating, and at end of pathname and current 49412115Sdyson * directory has not been removed, then can consider 49512115Sdyson * allowing file to be created. 49612115Sdyson */ 49712115Sdyson if ((nameiop == CREATE || nameiop == RENAME) && 49812115Sdyson (flags & ISLASTCN) && dp->i_nlink != 0) { 49912115Sdyson /* 50012115Sdyson * Access for write is interpreted as allowing 50112115Sdyson * creation of files in the directory. 50212115Sdyson */ 50383366Sjulian if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0) 50412115Sdyson return (error); 50512115Sdyson /* 50612115Sdyson * Return an indication of where the new directory 50712115Sdyson * entry should be put. If we didn't find a slot, 50812115Sdyson * then set dp->i_count to 0 indicating 50912115Sdyson * that the new slot belongs at the end of the 51012115Sdyson * directory. If we found a slot, then the new entry 51112115Sdyson * can be put in the range from dp->i_offset to 51212115Sdyson * dp->i_offset + dp->i_count. 51312115Sdyson */ 51412115Sdyson if (slotstatus == NONE) { 515202283Slulf dp->i_offset = roundup2(dp->i_size, DIRBLKSIZ); 51612115Sdyson dp->i_count = 0; 51712115Sdyson enduseful = dp->i_offset; 51812115Sdyson } else { 51912115Sdyson dp->i_offset = slotoffset; 52012115Sdyson dp->i_count = slotsize; 52112115Sdyson if (enduseful < slotoffset + slotsize) 52212115Sdyson enduseful = slotoffset + slotsize; 52312115Sdyson } 524202283Slulf dp->i_endoff = roundup2(enduseful, DIRBLKSIZ); 52512115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 52612115Sdyson /* 52712115Sdyson * We return with the directory locked, so that 52812115Sdyson * the parameters we set up above will still be 52912115Sdyson * valid if we actually decide to do a direnter(). 53012115Sdyson * We return ni_vp == NULL to indicate that the entry 53112115Sdyson * does not currently exist; we leave a pointer to 53212115Sdyson * the (locked) directory inode in ndp->ni_dvp. 53312115Sdyson * The pathname buffer is saved so that the name 53412115Sdyson * can be obtained later. 53512115Sdyson * 53612115Sdyson * NB - if the directory is unlocked, then this 53712115Sdyson * information cannot be used. 53812115Sdyson */ 53912115Sdyson cnp->cn_flags |= SAVENAME; 54012115Sdyson return (EJUSTRETURN); 54112115Sdyson } 54212115Sdyson /* 54312115Sdyson * Insert name into cache (as non-existent) if appropriate. 54412115Sdyson */ 54512115Sdyson if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) 546235820Spfg cache_enter(vdp, NULL, cnp); 54712115Sdyson return (ENOENT); 54812115Sdyson 54912115Sdysonfound: 550235820Spfg if (dd_ino != NULL) 551235820Spfg *dd_ino = ino; 55212115Sdyson if (numdirpasses == 2) 55312115Sdyson nchstats.ncs_pass2++; 55412115Sdyson /* 55512115Sdyson * Check that directory length properly reflects presence 55612115Sdyson * of this entry. 55712115Sdyson */ 558202283Slulf if (entryoffsetinblock + EXT2_DIR_REC_LEN(ep->e2d_namlen) 55912115Sdyson > dp->i_size) { 560202283Slulf ext2_dirbad(dp, i_offset, "i_size too small"); 561202283Slulf dp->i_size = entryoffsetinblock+EXT2_DIR_REC_LEN(ep->e2d_namlen); 56212115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 56312115Sdyson } 564105420Sbde brelse(bp); 56512115Sdyson 56612115Sdyson /* 56712115Sdyson * Found component in pathname. 56812115Sdyson * If the final component of path name, save information 56912115Sdyson * in the cache as to where the entry was found. 57012115Sdyson */ 57112115Sdyson if ((flags & ISLASTCN) && nameiop == LOOKUP) 572202283Slulf dp->i_diroff = i_offset &~ (DIRBLKSIZ - 1); 573202283Slulf dp->i_offset = i_offset; 57412115Sdyson /* 57512115Sdyson * If deleting, and at end of pathname, return 57612115Sdyson * parameters which can be used to remove file. 57712115Sdyson */ 57812115Sdyson if (nameiop == DELETE && (flags & ISLASTCN)) { 57912115Sdyson /* 58012115Sdyson * Write access to directory required to delete files. 58112115Sdyson */ 58283366Sjulian if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0) 58312115Sdyson return (error); 58412115Sdyson /* 58512115Sdyson * Return pointer to current entry in dp->i_offset, 58612115Sdyson * and distance past previous entry (if there 58712115Sdyson * is a previous entry in this block) in dp->i_count. 58812115Sdyson * Save directory inode pointer in ndp->ni_dvp for dirremove(). 58912115Sdyson */ 59012115Sdyson if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0) 59112115Sdyson dp->i_count = 0; 59212115Sdyson else 59312115Sdyson dp->i_count = dp->i_offset - prevoff; 594235820Spfg if (dd_ino != NULL) 595235820Spfg return (0); 596202283Slulf if (dp->i_number == ino) { 59712115Sdyson VREF(vdp); 59812115Sdyson *vpp = vdp; 59912115Sdyson return (0); 60012115Sdyson } 601202283Slulf if ((error = VFS_VGET(vdp->v_mount, ino, LK_EXCLUSIVE, 60292462Smckusick &tdp)) != 0) 60312115Sdyson return (error); 60412115Sdyson /* 60512115Sdyson * If directory is "sticky", then user must own 60612115Sdyson * the directory, or the file in it, else she 60712115Sdyson * may not delete it (unless she's root). This 60812115Sdyson * implements append-only directories. 60912115Sdyson */ 61012115Sdyson if ((dp->i_mode & ISVTX) && 61112115Sdyson cred->cr_uid != 0 && 61212115Sdyson cred->cr_uid != dp->i_uid && 61312115Sdyson VTOI(tdp)->i_uid != cred->cr_uid) { 61412115Sdyson vput(tdp); 61512115Sdyson return (EPERM); 61612115Sdyson } 61712115Sdyson *vpp = tdp; 61812115Sdyson return (0); 61912115Sdyson } 62012115Sdyson 62112115Sdyson /* 62212115Sdyson * If rewriting (RENAME), return the inode and the 62312115Sdyson * information required to rewrite the present directory 62412115Sdyson * Must get inode of directory entry to verify it's a 62512115Sdyson * regular file, or empty directory. 62612115Sdyson */ 627144299Sjeff if (nameiop == RENAME && (flags & ISLASTCN)) { 62883366Sjulian if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0) 62912115Sdyson return (error); 63012115Sdyson /* 63112115Sdyson * Careful about locking second inode. 63212115Sdyson * This can only occur if the target is ".". 63312115Sdyson */ 634202283Slulf if (dp->i_number == ino) 63512115Sdyson return (EISDIR); 636235820Spfg if (dd_ino != NULL) 637235820Spfg return (0); 638202283Slulf if ((error = VFS_VGET(vdp->v_mount, ino, LK_EXCLUSIVE, 63992462Smckusick &tdp)) != 0) 64012115Sdyson return (error); 64112115Sdyson *vpp = tdp; 64212115Sdyson cnp->cn_flags |= SAVENAME; 64312115Sdyson return (0); 64412115Sdyson } 645235820Spfg if (dd_ino != NULL) 646235820Spfg return (0); 64712115Sdyson 64812115Sdyson /* 64912115Sdyson * Step through the translation in the name. We do not `vput' the 65012115Sdyson * directory because we may need it again if a symbolic link 65112115Sdyson * is relative to the current directory. Instead we save it 65212115Sdyson * unlocked as "pdp". We must get the target inode before unlocking 65312115Sdyson * the directory to insure that the inode will not be removed 65412115Sdyson * before we get it. We prevent deadlock by always fetching 65512115Sdyson * inodes from the root, moving down the directory tree. Thus 65612115Sdyson * when following backward pointers ".." we must unlock the 65712115Sdyson * parent directory before getting the requested directory. 65812115Sdyson * There is a potential race condition here if both the current 65912115Sdyson * and parent directories are removed before the VFS_VGET for the 66012115Sdyson * inode associated with ".." returns. We hope that this occurs 66112115Sdyson * infrequently since we cannot avoid this race condition without 66212115Sdyson * implementing a sophisticated deadlock detection algorithm. 66312115Sdyson * Note also that this simple deadlock detection scheme will not 66412115Sdyson * work if the file system has any hard links other than ".." 66512115Sdyson * that point backwards in the directory structure. 66612115Sdyson */ 66712115Sdyson pdp = vdp; 66812115Sdyson if (flags & ISDOTDOT) { 669202283Slulf ltype = VOP_ISLOCKED(pdp); 670175294Sattilio VOP_UNLOCK(pdp, 0); /* race to get the inode */ 671202283Slulf error = VFS_VGET(vdp->v_mount, ino, cnp->cn_lkflags, &tdp); 672202283Slulf vn_lock(pdp, ltype | LK_RETRY); 673235820Spfg if (pdp->v_iflag & VI_DOOMED) { 674235820Spfg if (error == 0) 675235820Spfg vput(tdp); 676235820Spfg error = ENOENT; 677235820Spfg } 678235820Spfg if (error) 67912115Sdyson return (error); 680235820Spfg /* 681235820Spfg * Recheck that ".." entry in the vdp directory points 682235820Spfg * to the inode we looked up before vdp lock was 683235820Spfg * dropped. 684235820Spfg */ 685235820Spfg error = ext2_lookup_ino(pdp, NULL, cnp, &ino1); 686235820Spfg if (error) { 687235820Spfg vput(tdp); 688235820Spfg return (error); 689235820Spfg } 690235820Spfg if (ino1 != ino) { 691235820Spfg vput(tdp); 692235820Spfg goto restart; 693235820Spfg } 69412115Sdyson *vpp = tdp; 695202283Slulf } else if (dp->i_number == ino) { 69612115Sdyson VREF(vdp); /* we want ourself, ie "." */ 697202283Slulf /* 698202283Slulf * When we lookup "." we still can be asked to lock it 699202283Slulf * differently. 700202283Slulf */ 701202283Slulf ltype = cnp->cn_lkflags & LK_TYPE_MASK; 702202283Slulf if (ltype != VOP_ISLOCKED(vdp)) { 703202283Slulf if (ltype == LK_EXCLUSIVE) 704202283Slulf vn_lock(vdp, LK_UPGRADE | LK_RETRY); 705202283Slulf else /* if (ltype == LK_SHARED) */ 706202283Slulf vn_lock(vdp, LK_DOWNGRADE | LK_RETRY); 707202283Slulf } 70812115Sdyson *vpp = vdp; 70912115Sdyson } else { 710202283Slulf if ((error = VFS_VGET(vdp->v_mount, ino, cnp->cn_lkflags, 71192462Smckusick &tdp)) != 0) 71212115Sdyson return (error); 71312115Sdyson *vpp = tdp; 71412115Sdyson } 71512115Sdyson 71612115Sdyson /* 71712115Sdyson * Insert name into cache if appropriate. 71812115Sdyson */ 71912115Sdyson if (cnp->cn_flags & MAKEENTRY) 72012115Sdyson cache_enter(vdp, *vpp, cnp); 72112115Sdyson return (0); 72212115Sdyson} 72312115Sdyson 72496749Siedowsevoid 72596749Siedowseext2_dirbad(ip, offset, how) 72696749Siedowse struct inode *ip; 72796749Siedowse doff_t offset; 72896749Siedowse char *how; 72996749Siedowse{ 73096749Siedowse struct mount *mp; 73196749Siedowse 73296749Siedowse mp = ITOV(ip)->v_mount; 733202283Slulf if ((mp->mnt_flag & MNT_RDONLY) == 0) 734202283Slulf panic("ext2_dirbad: %s: bad dir ino %lu at offset %ld: %s\n", 735202283Slulf mp->mnt_stat.f_mntonname, (u_long)ip->i_number,(long)offset, how); 736202283Slulf else 73796749Siedowse (void)printf("%s: bad dir ino %lu at offset %ld: %s\n", 738202283Slulf mp->mnt_stat.f_mntonname, (u_long)ip->i_number, (long)offset, how); 739202283Slulf 74096749Siedowse} 74196749Siedowse 74212115Sdyson/* 74312115Sdyson * Do consistency checking on a directory entry: 74412115Sdyson * record length must be multiple of 4 74512115Sdyson * entry must fit in rest of its DIRBLKSIZ block 74612115Sdyson * record must be large enough to contain entry 74712115Sdyson * name is not longer than MAXNAMLEN 74812115Sdyson * name must be as long as advertised, and null terminated 74912115Sdyson */ 75012115Sdyson/* 75112115Sdyson * changed so that it confirms to ext2_check_dir_entry 75212115Sdyson */ 75312159Sbdestatic int 75412115Sdysonext2_dirbadentry(dp, de, entryoffsetinblock) 75512115Sdyson struct vnode *dp; 756202283Slulf struct ext2fs_direct_2 *de; 75712115Sdyson int entryoffsetinblock; 75812115Sdyson{ 759202283Slulf int DIRBLKSIZ = VTOI(dp)->i_e2fs->e2fs_bsize; 76012115Sdyson 761111742Sdes char * error_msg = NULL; 76212115Sdyson 763202283Slulf if (de->e2d_reclen < EXT2_DIR_REC_LEN(1)) 764111742Sdes error_msg = "rec_len is smaller than minimal"; 765202283Slulf else if (de->e2d_reclen % 4 != 0) 766111742Sdes error_msg = "rec_len % 4 != 0"; 767202283Slulf else if (de->e2d_reclen < EXT2_DIR_REC_LEN(de->e2d_namlen)) 768111742Sdes error_msg = "reclen is too small for name_len"; 769202283Slulf else if (entryoffsetinblock + de->e2d_reclen > DIRBLKSIZ) 770111742Sdes error_msg = "directory entry across blocks"; 771111742Sdes /* else LATER 77212115Sdyson if (de->inode > dir->i_sb->u.ext2_sb.s_es->s_inodes_count) 773111742Sdes error_msg = "inode out of bounds"; 77412115Sdyson */ 77512115Sdyson 776111742Sdes if (error_msg != NULL) { 777111742Sdes printf("bad directory entry: %s\n", error_msg); 778111742Sdes printf("offset=%d, inode=%lu, rec_len=%u, name_len=%u\n", 779202283Slulf entryoffsetinblock, (unsigned long)de->e2d_ino, 780202283Slulf de->e2d_reclen, de->e2d_namlen); 781111742Sdes } 782111742Sdes return error_msg == NULL ? 0 : 1; 78312115Sdyson} 78412115Sdyson 78512115Sdyson/* 78612115Sdyson * Write a directory entry after a call to namei, using the parameters 78712115Sdyson * that it left in nameidata. The argument ip is the inode which the new 78812115Sdyson * directory entry will refer to. Dvp is a pointer to the directory to 78912115Sdyson * be written, which was left locked by namei. Remaining parameters 79012115Sdyson * (dp->i_offset, dp->i_count) indicate how the space for the new 79112115Sdyson * entry is to be obtained. 79212115Sdyson */ 79312115Sdysonint 79412115Sdysonext2_direnter(ip, dvp, cnp) 79512115Sdyson struct inode *ip; 79612115Sdyson struct vnode *dvp; 79796752Siedowse struct componentname *cnp; 79812115Sdyson{ 799202283Slulf struct ext2fs_direct_2 *ep, *nep; 80096752Siedowse struct inode *dp; 80112115Sdyson struct buf *bp; 802202283Slulf struct ext2fs_direct_2 newdir; 80312115Sdyson struct iovec aiov; 80412115Sdyson struct uio auio; 80512115Sdyson u_int dsize; 80612115Sdyson int error, loc, newentrysize, spacefree; 80712115Sdyson char *dirbuf; 808202283Slulf int DIRBLKSIZ = ip->i_e2fs->e2fs_bsize; 80912115Sdyson 81012115Sdyson 811153110Sru#ifdef DIAGNOSTIC 81212115Sdyson if ((cnp->cn_flags & SAVENAME) == 0) 813246892Spfg panic("ext2_direnter: missing name"); 81412115Sdyson#endif 81512115Sdyson dp = VTOI(dvp); 816202283Slulf newdir.e2d_ino = ip->i_number; 817202283Slulf newdir.e2d_namlen = cnp->cn_namelen; 818193377Sstas if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs, 819202283Slulf EXT2F_INCOMPAT_FTYPE)) 820202283Slulf newdir.e2d_type = DTTOFT(IFTODT(ip->i_mode)); 82155477Sbde else 822202283Slulf newdir.e2d_type = EXT2_FT_UNKNOWN; 823202283Slulf bcopy(cnp->cn_nameptr, newdir.e2d_name, (unsigned)cnp->cn_namelen + 1); 824202283Slulf newentrysize = EXT2_DIR_REC_LEN(newdir.e2d_namlen); 82512115Sdyson if (dp->i_count == 0) { 82612115Sdyson /* 82712115Sdyson * If dp->i_count is 0, then namei could find no 82812115Sdyson * space in the directory. Here, dp->i_offset will 82912115Sdyson * be on a directory block boundary and we will write the 83012115Sdyson * new entry into a fresh block. 83112115Sdyson */ 83212115Sdyson if (dp->i_offset & (DIRBLKSIZ - 1)) 83312115Sdyson panic("ext2_direnter: newblk"); 83412115Sdyson auio.uio_offset = dp->i_offset; 835202283Slulf newdir.e2d_reclen = DIRBLKSIZ; 83612115Sdyson auio.uio_resid = newentrysize; 83712115Sdyson aiov.iov_len = newentrysize; 83812115Sdyson aiov.iov_base = (caddr_t)&newdir; 83912115Sdyson auio.uio_iov = &aiov; 84012115Sdyson auio.uio_iovcnt = 1; 84112115Sdyson auio.uio_rw = UIO_WRITE; 84212115Sdyson auio.uio_segflg = UIO_SYSSPACE; 84383366Sjulian auio.uio_td = (struct thread *)0; 84412115Sdyson error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred); 84512115Sdyson if (DIRBLKSIZ > 84696749Siedowse VFSTOEXT2(dvp->v_mount)->um_mountp->mnt_stat.f_bsize) 84712115Sdyson /* XXX should grow with balloc() */ 84812115Sdyson panic("ext2_direnter: frag size"); 84912115Sdyson else if (!error) { 850202283Slulf dp->i_size = roundup2(dp->i_size, DIRBLKSIZ); 85112115Sdyson dp->i_flag |= IN_CHANGE; 85212115Sdyson } 85312115Sdyson return (error); 85412115Sdyson } 85512115Sdyson 85612115Sdyson /* 85712115Sdyson * If dp->i_count is non-zero, then namei found space 85812115Sdyson * for the new entry in the range dp->i_offset to 85912115Sdyson * dp->i_offset + dp->i_count in the directory. 86012115Sdyson * To use this space, we may have to compact the entries located 86112115Sdyson * there, by copying them together towards the beginning of the 86212115Sdyson * block, leaving the free space in one usable chunk at the end. 86312115Sdyson */ 86412115Sdyson 86512115Sdyson /* 86612115Sdyson * Increase size of directory if entry eats into new space. 86712115Sdyson * This should never push the size past a new multiple of 86812115Sdyson * DIRBLKSIZE. 86912115Sdyson * 87012115Sdyson * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN. 87112115Sdyson */ 87212115Sdyson if (dp->i_offset + dp->i_count > dp->i_size) 87312115Sdyson dp->i_size = dp->i_offset + dp->i_count; 87412115Sdyson /* 87512115Sdyson * Get the block containing the space for the new directory entry. 87612115Sdyson */ 87796749Siedowse if ((error = ext2_blkatoff(dvp, (off_t)dp->i_offset, &dirbuf, 87896749Siedowse &bp)) != 0) 87912115Sdyson return (error); 88012115Sdyson /* 88112115Sdyson * Find space for the new entry. In the simple case, the entry at 88212115Sdyson * offset base will have the space. If it does not, then namei 88312115Sdyson * arranged that compacting the region dp->i_offset to 88412115Sdyson * dp->i_offset + dp->i_count would yield the 88512115Sdyson * space. 88612115Sdyson */ 887202283Slulf ep = (struct ext2fs_direct_2 *)dirbuf; 888202283Slulf dsize = EXT2_DIR_REC_LEN(ep->e2d_namlen); 889202283Slulf spacefree = ep->e2d_reclen - dsize; 890202283Slulf for (loc = ep->e2d_reclen; loc < dp->i_count; ) { 891202283Slulf nep = (struct ext2fs_direct_2 *)(dirbuf + loc); 892202283Slulf if (ep->e2d_ino) { 89312115Sdyson /* trim the existing slot */ 894202283Slulf ep->e2d_reclen = dsize; 895202283Slulf ep = (struct ext2fs_direct_2 *)((char *)ep + dsize); 89612115Sdyson } else { 89712115Sdyson /* overwrite; nothing there; header is ours */ 89812115Sdyson spacefree += dsize; 89912115Sdyson } 900202283Slulf dsize = EXT2_DIR_REC_LEN(nep->e2d_namlen); 901202283Slulf spacefree += nep->e2d_reclen - dsize; 902202283Slulf loc += nep->e2d_reclen; 90312115Sdyson bcopy((caddr_t)nep, (caddr_t)ep, dsize); 90412115Sdyson } 90512115Sdyson /* 90612115Sdyson * Update the pointer fields in the previous entry (if any), 90712115Sdyson * copy in the new entry, and write out the block. 90812115Sdyson */ 909202283Slulf if (ep->e2d_ino == 0) { 91012115Sdyson if (spacefree + dsize < newentrysize) 91112115Sdyson panic("ext2_direnter: compact1"); 912202283Slulf newdir.e2d_reclen = spacefree + dsize; 91312115Sdyson } else { 91412115Sdyson if (spacefree < newentrysize) 91512115Sdyson panic("ext2_direnter: compact2"); 916202283Slulf newdir.e2d_reclen = spacefree; 917202283Slulf ep->e2d_reclen = dsize; 918202283Slulf ep = (struct ext2fs_direct_2 *)((char *)ep + dsize); 91912115Sdyson } 92012115Sdyson bcopy((caddr_t)&newdir, (caddr_t)ep, (u_int)newentrysize); 921221166Sjhb if (DOINGASYNC(dvp)) { 922221166Sjhb bdwrite(bp); 923221166Sjhb error = 0; 924221166Sjhb } else { 925221166Sjhb error = bwrite(bp); 926221166Sjhb } 92712115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 92812115Sdyson if (!error && dp->i_endoff && dp->i_endoff < dp->i_size) 92996749Siedowse error = ext2_truncate(dvp, (off_t)dp->i_endoff, IO_SYNC, 93083366Sjulian cnp->cn_cred, cnp->cn_thread); 93112115Sdyson return (error); 93212115Sdyson} 93312115Sdyson 93412115Sdyson/* 93512115Sdyson * Remove a directory entry after a call to namei, using 93612115Sdyson * the parameters which it left in nameidata. The entry 93712115Sdyson * dp->i_offset contains the offset into the directory of the 93812115Sdyson * entry to be eliminated. The dp->i_count field contains the 93912115Sdyson * size of the previous record in the directory. If this 94012115Sdyson * is 0, the first entry is being deleted, so we need only 94112115Sdyson * zero the inode number to mark the entry as free. If the 94212115Sdyson * entry is not the first in the directory, we must reclaim 94312115Sdyson * the space of the now empty record by adding the record size 94412115Sdyson * to the size of the previous entry. 94512115Sdyson */ 94612115Sdysonint 94712115Sdysonext2_dirremove(dvp, cnp) 94812115Sdyson struct vnode *dvp; 94912115Sdyson struct componentname *cnp; 95012115Sdyson{ 95196752Siedowse struct inode *dp; 952202283Slulf struct ext2fs_direct_2 *ep, *rep; 95312115Sdyson struct buf *bp; 95412115Sdyson int error; 955111742Sdes 95612115Sdyson dp = VTOI(dvp); 95712115Sdyson if (dp->i_count == 0) { 95812115Sdyson /* 95912115Sdyson * First entry in block: set d_ino to zero. 96012115Sdyson */ 96143301Sdillon if ((error = 96296749Siedowse ext2_blkatoff(dvp, (off_t)dp->i_offset, (char **)&ep, 96396749Siedowse &bp)) != 0) 96412115Sdyson return (error); 965202283Slulf ep->e2d_ino = 0; 966126853Sphk error = bwrite(bp); 96712115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 96812115Sdyson return (error); 96912115Sdyson } 97012115Sdyson /* 97112115Sdyson * Collapse new free space into previous entry. 97212115Sdyson */ 97396749Siedowse if ((error = ext2_blkatoff(dvp, (off_t)(dp->i_offset - dp->i_count), 97443301Sdillon (char **)&ep, &bp)) != 0) 97512115Sdyson return (error); 976202283Slulf 977202283Slulf /* Set 'rep' to the entry being removed. */ 978202283Slulf if (dp->i_count == 0) 979202283Slulf rep = ep; 980202283Slulf else 981202283Slulf rep = (struct ext2fs_direct_2 *)((char *)ep + ep->e2d_reclen); 982202283Slulf ep->e2d_reclen += rep->e2d_reclen; 983221166Sjhb if (DOINGASYNC(dvp) && dp->i_count != 0) 984221166Sjhb bdwrite(bp); 985221166Sjhb else 986221166Sjhb error = bwrite(bp); 98712115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 98812115Sdyson return (error); 98912115Sdyson} 99012115Sdyson 99112115Sdyson/* 99212115Sdyson * Rewrite an existing directory entry to point at the inode 99312115Sdyson * supplied. The parameters describing the directory entry are 99412115Sdyson * set up by a call to namei. 99512115Sdyson */ 99612115Sdysonint 99712115Sdysonext2_dirrewrite(dp, ip, cnp) 99812115Sdyson struct inode *dp, *ip; 99912115Sdyson struct componentname *cnp; 100012115Sdyson{ 100112115Sdyson struct buf *bp; 1002202283Slulf struct ext2fs_direct_2 *ep; 100312115Sdyson struct vnode *vdp = ITOV(dp); 100412115Sdyson int error; 100512115Sdyson 100696749Siedowse if ((error = ext2_blkatoff(vdp, (off_t)dp->i_offset, (char **)&ep, 100796749Siedowse &bp)) != 0) 100812115Sdyson return (error); 1009202283Slulf ep->e2d_ino = ip->i_number; 1010193377Sstas if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs, 1011202283Slulf EXT2F_INCOMPAT_FTYPE)) 1012202283Slulf ep->e2d_type = DTTOFT(IFTODT(ip->i_mode)); 101355477Sbde else 1014202283Slulf ep->e2d_type = EXT2_FT_UNKNOWN; 1015126853Sphk error = bwrite(bp); 101612115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 101712115Sdyson return (error); 101812115Sdyson} 101912115Sdyson 102012115Sdyson/* 102112115Sdyson * Check if a directory is empty or not. 102212115Sdyson * Inode supplied must be locked. 102312115Sdyson * 102412115Sdyson * Using a struct dirtemplate here is not precisely 102512115Sdyson * what we want, but better than using a struct direct. 102612115Sdyson * 102712115Sdyson * NB: does not handle corrupted directories. 102812115Sdyson */ 102912115Sdysonint 103012115Sdysonext2_dirempty(ip, parentino, cred) 103196752Siedowse struct inode *ip; 103212115Sdyson ino_t parentino; 103312115Sdyson struct ucred *cred; 103412115Sdyson{ 103596752Siedowse off_t off; 103612115Sdyson struct dirtemplate dbuf; 1037202283Slulf struct ext2fs_direct_2 *dp = (struct ext2fs_direct_2 *)&dbuf; 1038233353Skib int error, namlen; 1039233353Skib ssize_t count; 1040229549Spfg#define MINDIRSIZ (sizeof(struct dirtemplate) / 2) 104112115Sdyson 1042202283Slulf for (off = 0; off < ip->i_size; off += dp->e2d_reclen) { 1043101744Srwatson error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, 1044101744Srwatson off, UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK, cred, 1045101941Srwatson NOCRED, &count, (struct thread *)0); 104612115Sdyson /* 104712115Sdyson * Since we read MINDIRSIZ, residual must 104812115Sdyson * be 0 unless we're at end of file. 104912115Sdyson */ 105012115Sdyson if (error || count != 0) 105112115Sdyson return (0); 105212115Sdyson /* avoid infinite loops */ 1053202283Slulf if (dp->e2d_reclen == 0) 105412115Sdyson return (0); 105512115Sdyson /* skip empty entries */ 1056202283Slulf if (dp->e2d_ino == 0) 105712115Sdyson continue; 105812115Sdyson /* accept only "." and ".." */ 1059202283Slulf namlen = dp->e2d_namlen; 106012115Sdyson if (namlen > 2) 106112115Sdyson return (0); 1062202283Slulf if (dp->e2d_name[0] != '.') 106312115Sdyson return (0); 106412115Sdyson /* 106512115Sdyson * At this point namlen must be 1 or 2. 106612115Sdyson * 1 implies ".", 2 implies ".." if second 106712115Sdyson * char is also "." 106812115Sdyson */ 106912115Sdyson if (namlen == 1) 107012115Sdyson continue; 1071202283Slulf if (dp->e2d_name[1] == '.' && dp->e2d_ino == parentino) 107212115Sdyson continue; 107312115Sdyson return (0); 107412115Sdyson } 107512115Sdyson return (1); 107612115Sdyson} 107712115Sdyson 107812115Sdyson/* 107912115Sdyson * Check if source directory is in the path of the target directory. 108012115Sdyson * Target is supplied locked, source is unlocked. 108112115Sdyson * The target is always vput before returning. 108212115Sdyson */ 108312115Sdysonint 108412115Sdysonext2_checkpath(source, target, cred) 108512115Sdyson struct inode *source, *target; 108612115Sdyson struct ucred *cred; 108712115Sdyson{ 108812115Sdyson struct vnode *vp; 1089247055Spfg int error, namlen; 109012115Sdyson struct dirtemplate dirbuf; 109112115Sdyson 109212115Sdyson vp = ITOV(target); 109312115Sdyson if (target->i_number == source->i_number) { 109412115Sdyson error = EEXIST; 109512115Sdyson goto out; 109612115Sdyson } 1097247055Spfg if (target->i_number == EXT2_ROOTINO) { 1098247055Spfg error = 0; 109912115Sdyson goto out; 1100247055Spfg } 110112115Sdyson 110212115Sdyson for (;;) { 110312115Sdyson if (vp->v_type != VDIR) { 110412115Sdyson error = ENOTDIR; 110512115Sdyson break; 110612115Sdyson } 110712115Sdyson error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf, 1108229549Spfg sizeof(struct dirtemplate), (off_t)0, UIO_SYSSPACE, 1109194296Skib IO_NODELOCKED | IO_NOMACCHECK, cred, NOCRED, NULL, 1110194296Skib NULL); 111112115Sdyson if (error != 0) 111212115Sdyson break; 111357710Sbde namlen = dirbuf.dotdot_type; /* like ufs little-endian */ 111412115Sdyson if (namlen != 2 || 111512115Sdyson dirbuf.dotdot_name[0] != '.' || 111612115Sdyson dirbuf.dotdot_name[1] != '.') { 111712115Sdyson error = ENOTDIR; 111812115Sdyson break; 111912115Sdyson } 112012115Sdyson if (dirbuf.dotdot_ino == source->i_number) { 112112115Sdyson error = EINVAL; 112212115Sdyson break; 112312115Sdyson } 1124247055Spfg if (dirbuf.dotdot_ino == EXT2_ROOTINO) 112512115Sdyson break; 112612115Sdyson vput(vp); 112792462Smckusick if ((error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino, 112892462Smckusick LK_EXCLUSIVE, &vp)) != 0) { 112912115Sdyson vp = NULL; 113012115Sdyson break; 113112115Sdyson } 113212115Sdyson } 113312115Sdyson 113412115Sdysonout: 113512115Sdyson if (error == ENOTDIR) 113612115Sdyson printf("checkpath: .. not a directory\n"); 113712115Sdyson if (vp != NULL) 113812115Sdyson vput(vp); 113912115Sdyson return (error); 114012115Sdyson} 1141