ext2_lookup.c revision 105420
112115Sdyson/* 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 */ 712115Sdyson/* 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 * 3. All advertising materials mentioning features or use of this software 2512115Sdyson * must display the following acknowledgement: 2612115Sdyson * This product includes software developed by the University of 2712115Sdyson * California, Berkeley and its contributors. 2812115Sdyson * 4. Neither the name of the University nor the names of its contributors 2912115Sdyson * may be used to endorse or promote products derived from this software 3012115Sdyson * without specific prior written permission. 3112115Sdyson * 3212115Sdyson * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 3312115Sdyson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 3412115Sdyson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 3512115Sdyson * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 3612115Sdyson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3712115Sdyson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3812115Sdyson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3912115Sdyson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 4012115Sdyson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 4112115Sdyson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 4212115Sdyson * SUCH DAMAGE. 4312115Sdyson * 4412115Sdyson * @(#)ufs_lookup.c 8.6 (Berkeley) 4/1/94 4593016Sbde * $FreeBSD: head/sys/gnu/fs/ext2fs/ext2_lookup.c 105420 2002-10-18 21:41:41Z bde $ 4612115Sdyson */ 4712115Sdyson 4812115Sdyson#include <sys/param.h> 4912159Sbde#include <sys/systm.h> 5012115Sdyson#include <sys/namei.h> 5160041Sphk#include <sys/bio.h> 5212115Sdyson#include <sys/buf.h> 5312115Sdyson#include <sys/mount.h> 5412115Sdyson#include <sys/vnode.h> 5512115Sdyson#include <sys/malloc.h> 5612115Sdyson#include <sys/dirent.h> 5796753Siedowse#include <sys/sysctl.h> 5812115Sdyson 5912115Sdyson#include <ufs/ufs/dir.h> 6012115Sdyson 6196749Siedowse#include <gnu/ext2fs/inode.h> 6296749Siedowse#include <gnu/ext2fs/ext2_mount.h> 6312115Sdyson#include <gnu/ext2fs/ext2_extern.h> 6412115Sdyson#include <gnu/ext2fs/ext2_fs.h> 6512115Sdyson#include <gnu/ext2fs/ext2_fs_sb.h> 6612115Sdyson 6796753Siedowse#ifdef DIAGNOSTIC 6896753Siedowsestatic int dirchk = 1; 6996753Siedowse#else 7096753Siedowsestatic int dirchk = 0; 7196753Siedowse#endif 7296753Siedowse 7396753SiedowseSYSCTL_NODE(_vfs, OID_AUTO, e2fs, CTLFLAG_RD, 0, "EXT2FS filesystem"); 7496753SiedowseSYSCTL_INT(_vfs_e2fs, OID_AUTO, dircheck, CTLFLAG_RW, &dirchk, 0, ""); 7596753Siedowse 7612115Sdyson/* 7712115Sdyson DIRBLKSIZE in ffs is DEV_BSIZE (in most cases 512) 7812115Sdyson while it is the native blocksize in ext2fs - thus, a #define 7912115Sdyson is no longer appropriate 8012115Sdyson*/ 8112115Sdyson#undef DIRBLKSIZ 8212115Sdyson 8355477Sbdestatic u_char ext2_ft_to_dt[] = { 8455477Sbde DT_UNKNOWN, /* EXT2_FT_UNKNOWN */ 8555477Sbde DT_REG, /* EXT2_FT_REG_FILE */ 8655477Sbde DT_DIR, /* EXT2_FT_DIR */ 8755477Sbde DT_CHR, /* EXT2_FT_CHRDEV */ 8855477Sbde DT_BLK, /* EXT2_FT_BLKDEV */ 8955477Sbde DT_FIFO, /* EXT2_FT_FIFO */ 9055477Sbde DT_SOCK, /* EXT2_FT_SOCK */ 9155477Sbde DT_LNK, /* EXT2_FT_SYMLINK */ 9255477Sbde}; 9355477Sbde#define FTTODT(ft) \ 9455477Sbde ((ft) > sizeof(ext2_ft_to_dt) / sizeof(ext2_ft_to_dt[0]) ? \ 9555477Sbde DT_UNKNOWN : ext2_ft_to_dt[(ft)]) 9655477Sbde 9755477Sbdestatic u_char dt_to_ext2_ft[] = { 9855477Sbde EXT2_FT_UNKNOWN, /* DT_UNKNOWN */ 9955477Sbde EXT2_FT_FIFO, /* DT_FIFO */ 10055477Sbde EXT2_FT_CHRDEV, /* DT_CHR */ 10155477Sbde EXT2_FT_UNKNOWN, /* unused */ 10255477Sbde EXT2_FT_DIR, /* DT_DIR */ 10355477Sbde EXT2_FT_UNKNOWN, /* unused */ 10455477Sbde EXT2_FT_BLKDEV, /* DT_BLK */ 10555477Sbde EXT2_FT_UNKNOWN, /* unused */ 10655477Sbde EXT2_FT_REG_FILE, /* DT_REG */ 10755477Sbde EXT2_FT_UNKNOWN, /* unused */ 10855477Sbde EXT2_FT_SYMLINK, /* DT_LNK */ 10955477Sbde EXT2_FT_UNKNOWN, /* unused */ 11055477Sbde EXT2_FT_SOCK, /* DT_SOCK */ 11155477Sbde EXT2_FT_UNKNOWN, /* unused */ 11255477Sbde EXT2_FT_UNKNOWN, /* DT_WHT */ 11355477Sbde}; 11455477Sbde#define DTTOFT(dt) \ 11555477Sbde ((dt) > sizeof(dt_to_ext2_ft) / sizeof(dt_to_ext2_ft[0]) ? \ 11655477Sbde EXT2_FT_UNKNOWN : dt_to_ext2_ft[(dt)]) 11755477Sbde 11893014Sbdestatic int ext2_dirbadentry(struct vnode *dp, struct ext2_dir_entry_2 *de, 11993014Sbde int entryoffsetinblock); 12012159Sbde 12112115Sdyson/* 12212115Sdyson * Vnode op for reading directories. 12312115Sdyson * 12412115Sdyson * The routine below assumes that the on-disk format of a directory 12512115Sdyson * is the same as that defined by <sys/dirent.h>. If the on-disk 12612115Sdyson * format changes, then it will be necessary to do a conversion 12712115Sdyson * from the on-disk format that read returns to the format defined 12812115Sdyson * by <sys/dirent.h>. 12912115Sdyson */ 13012115Sdyson/* 13112115Sdyson * this is exactly what we do here - the problem is that the conversion 13212115Sdyson * will blow up some entries by four bytes, so it can't be done in place. 13312115Sdyson * This is too bad. Right now the conversion is done entry by entry, the 13412115Sdyson * converted entry is sent via uiomove. 13512115Sdyson * 13612115Sdyson * XXX allocate a buffer, convert as many entries as possible, then send 13712115Sdyson * the whole buffer to uiomove 13812115Sdyson */ 13912115Sdysonint 14012115Sdysonext2_readdir(ap) 14112115Sdyson struct vop_readdir_args /* { 14212115Sdyson struct vnode *a_vp; 14312115Sdyson struct uio *a_uio; 14412115Sdyson struct ucred *a_cred; 14512115Sdyson } */ *ap; 14612115Sdyson{ 14796752Siedowse struct uio *uio = ap->a_uio; 14812147Sdyson int count, error; 14912115Sdyson 15055477Sbde struct ext2_dir_entry_2 *edp, *dp; 15124649Sdfr int ncookies; 15212115Sdyson struct dirent dstdp; 15312115Sdyson struct uio auio; 15412115Sdyson struct iovec aiov; 15512115Sdyson caddr_t dirbuf; 15665780Sbde int DIRBLKSIZ = VTOI(ap->a_vp)->i_e2fs->s_blocksize; 15712115Sdyson int readcnt; 15865780Sbde off_t startoffset = uio->uio_offset; 15912115Sdyson 16065780Sbde count = uio->uio_resid; 16165780Sbde /* 16265780Sbde * Avoid complications for partial directory entries by adjusting 16365780Sbde * the i/o to end at a block boundary. Don't give up (like ufs 16465780Sbde * does) if the initial adjustment gives a negative count, since 16565780Sbde * many callers don't supply a large enough buffer. The correct 16665780Sbde * size is a little larger than DIRBLKSIZ to allow for expansion 16765780Sbde * of directory entries, but some callers just use 512. 16865780Sbde */ 16965780Sbde count -= (uio->uio_offset + count) & (DIRBLKSIZ -1); 17065780Sbde if (count <= 0) 17165780Sbde count += DIRBLKSIZ; 17212115Sdyson 17365780Sbde#ifdef EXT2FS_DEBUG 17465780Sbde printf("ext2_readdir: uio_offset = %lld, uio_resid = %d, count = %d\n", 17565780Sbde uio->uio_offset, uio->uio_resid, count); 17612115Sdyson#endif 17712115Sdyson 17812115Sdyson auio = *uio; 17912115Sdyson auio.uio_iov = &aiov; 18012115Sdyson auio.uio_iovcnt = 1; 18165780Sbde auio.uio_resid = count; 18212115Sdyson auio.uio_segflg = UIO_SYSSPACE; 18312115Sdyson aiov.iov_len = count; 18412115Sdyson MALLOC(dirbuf, caddr_t, count, M_TEMP, M_WAITOK); 18512115Sdyson aiov.iov_base = dirbuf; 18612115Sdyson error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred); 18712115Sdyson if (error == 0) { 18812115Sdyson readcnt = count - auio.uio_resid; 18955477Sbde edp = (struct ext2_dir_entry_2 *)&dirbuf[readcnt]; 19024649Sdfr ncookies = 0; 19155477Sbde bzero(&dstdp, offsetof(struct dirent, d_name)); 19255477Sbde for (dp = (struct ext2_dir_entry_2 *)dirbuf; 19355477Sbde !error && uio->uio_resid > 0 && dp < edp; ) { 19455477Sbde /*- 19555477Sbde * "New" ext2fs directory entries differ in 3 ways 19655477Sbde * from ufs on-disk ones: 19755477Sbde * - the name is not necessarily NUL-terminated. 19855477Sbde * - the file type field always exists and always 19955477Sbde * follows the name length field. 20055477Sbde * - the file type is encoded in a different way. 20155477Sbde * 20255477Sbde * "Old" ext2fs directory entries need no special 20355477Sbde * conversions, since they binary compatible with 20455477Sbde * "new" entries having a file type of 0 (i.e., 20555477Sbde * EXT2_FT_UNKNOWN). Splitting the old name length 20655477Sbde * field didn't make a mess like it did in ufs, 20755477Sbde * because ext2fs uses a machine-dependent disk 20855477Sbde * layout. 20955477Sbde */ 21055477Sbde dstdp.d_fileno = dp->inode; 21155477Sbde dstdp.d_type = FTTODT(dp->file_type); 21255477Sbde dstdp.d_namlen = dp->name_len; 21355477Sbde dstdp.d_reclen = GENERIC_DIRSIZ(&dstdp); 21455477Sbde bcopy(dp->name, dstdp.d_name, dstdp.d_namlen); 21555477Sbde bzero(dstdp.d_name + dstdp.d_namlen, 21655477Sbde dstdp.d_reclen - offsetof(struct dirent, d_name) - 21755477Sbde dstdp.d_namlen); 21855477Sbde 21912115Sdyson if (dp->rec_len > 0) { 22012115Sdyson if(dstdp.d_reclen <= uio->uio_resid) { 22112115Sdyson /* advance dp */ 22255477Sbde dp = (struct ext2_dir_entry_2 *) 22312115Sdyson ((char *)dp + dp->rec_len); 22412115Sdyson error = 22512159Sbde uiomove((caddr_t)&dstdp, 22612159Sbde dstdp.d_reclen, uio); 22724649Sdfr if (!error) 22824649Sdfr ncookies++; 22912115Sdyson } else 23012115Sdyson break; 23112115Sdyson } else { 23212115Sdyson error = EIO; 23312115Sdyson break; 23412115Sdyson } 23512115Sdyson } 23612115Sdyson /* we need to correct uio_offset */ 23712115Sdyson uio->uio_offset = startoffset + (caddr_t)dp - dirbuf; 23824649Sdfr 23924649Sdfr if (!error && ap->a_ncookies != NULL) { 24065780Sbde u_long *cookiep, *cookies, *ecookies; 24124649Sdfr off_t off; 24224649Sdfr 24324649Sdfr if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1) 24424649Sdfr panic("ext2fs_readdir: unexpected uio from NFS server"); 24524649Sdfr MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP, 24624649Sdfr M_WAITOK); 24724649Sdfr off = startoffset; 24865780Sbde for (dp = (struct ext2_dir_entry_2 *)dirbuf, 24965780Sbde cookiep = cookies, ecookies = cookies + ncookies; 25065780Sbde cookiep < ecookies; 25155477Sbde dp = (struct ext2_dir_entry_2 *)((caddr_t) dp + dp->rec_len)) { 25224649Sdfr off += dp->rec_len; 25324649Sdfr *cookiep++ = (u_long) off; 25424649Sdfr } 25524649Sdfr *ap->a_ncookies = ncookies; 25624649Sdfr *ap->a_cookies = cookies; 25724649Sdfr } 25812115Sdyson } 25912115Sdyson FREE(dirbuf, M_TEMP); 26024649Sdfr if (ap->a_eofflag) 26124649Sdfr *ap->a_eofflag = VTOI(ap->a_vp)->i_size <= uio->uio_offset; 26212115Sdyson return (error); 26312115Sdyson} 26412115Sdyson 26512115Sdyson/* 26612115Sdyson * Convert a component of a pathname into a pointer to a locked inode. 26712115Sdyson * This is a very central and rather complicated routine. 26812115Sdyson * If the file system is not maintained in a strict tree hierarchy, 26912115Sdyson * this can result in a deadlock situation (see comments in code below). 27012115Sdyson * 27112115Sdyson * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending 27212115Sdyson * on whether the name is to be looked up, created, renamed, or deleted. 27312115Sdyson * When CREATE, RENAME, or DELETE is specified, information usable in 27412115Sdyson * creating, renaming, or deleting a directory entry may be calculated. 27512115Sdyson * If flag has LOCKPARENT or'ed into it and the target of the pathname 27612115Sdyson * exists, lookup returns both the target and its parent directory locked. 27712115Sdyson * When creating or renaming and LOCKPARENT is specified, the target may 27812115Sdyson * not be ".". When deleting and LOCKPARENT is specified, the target may 27912115Sdyson * be "."., but the caller must check to ensure it does an vrele and vput 28012115Sdyson * instead of two vputs. 28112115Sdyson * 28212115Sdyson * Overall outline of ufs_lookup: 28312115Sdyson * 28412115Sdyson * search for name in directory, to found or notfound 28512115Sdyson * notfound: 28612115Sdyson * if creating, return locked directory, leaving info on available slots 28712115Sdyson * else return error 28812115Sdyson * found: 28912115Sdyson * if at end of path and deleting, return information to allow delete 29012115Sdyson * if at end of path and rewriting (RENAME and LOCKPARENT), lock target 29112115Sdyson * inode and return info to allow rewrite 29212115Sdyson * if not at end, add name to cache; if at end and neither creating 29312115Sdyson * nor deleting, add name to cache 29412115Sdyson */ 29512115Sdysonint 29612115Sdysonext2_lookup(ap) 29728787Sphk struct vop_cachedlookup_args /* { 29812115Sdyson struct vnode *a_dvp; 29912115Sdyson struct vnode **a_vpp; 30012115Sdyson struct componentname *a_cnp; 30112115Sdyson } */ *ap; 30212115Sdyson{ 30396752Siedowse struct vnode *vdp; /* vnode for directory being searched */ 30496752Siedowse struct inode *dp; /* inode for directory being searched */ 30512115Sdyson struct buf *bp; /* a buffer of directory entries */ 30696752Siedowse struct ext2_dir_entry_2 *ep; /* the current directory entry */ 30712115Sdyson int entryoffsetinblock; /* offset of ep in bp's buffer */ 30812115Sdyson enum {NONE, COMPACT, FOUND} slotstatus; 30912115Sdyson doff_t slotoffset; /* offset of area with free space */ 31012115Sdyson int slotsize; /* size of area at slotoffset */ 31112115Sdyson int slotfreespace; /* amount of space free in slot */ 31212115Sdyson int slotneeded; /* size of the entry we're seeking */ 31312115Sdyson int numdirpasses; /* strategy for directory search */ 31412115Sdyson doff_t endsearch; /* offset to end directory search */ 31512115Sdyson doff_t prevoff; /* prev entry dp->i_offset */ 31612115Sdyson struct vnode *pdp; /* saved dp during symlink work */ 31712115Sdyson struct vnode *tdp; /* returned by VFS_VGET */ 31812115Sdyson doff_t enduseful; /* pointer past last used dir slot */ 31912115Sdyson u_long bmask; /* block offset mask */ 32012115Sdyson int lockparent; /* 1 => lockparent flag is set */ 32112115Sdyson int wantparent; /* 1 => wantparent or lockparent flag */ 32212115Sdyson int namlen, error; 32312115Sdyson struct vnode **vpp = ap->a_vpp; 32412115Sdyson struct componentname *cnp = ap->a_cnp; 32512115Sdyson struct ucred *cred = cnp->cn_cred; 32612115Sdyson int flags = cnp->cn_flags; 32712115Sdyson int nameiop = cnp->cn_nameiop; 32883366Sjulian struct thread *td = cnp->cn_thread; 32912115Sdyson 33012115Sdyson int DIRBLKSIZ = VTOI(ap->a_dvp)->i_e2fs->s_blocksize; 33112115Sdyson 33212115Sdyson bp = NULL; 33312115Sdyson slotoffset = -1; 33412115Sdyson *vpp = NULL; 33512115Sdyson vdp = ap->a_dvp; 33612115Sdyson dp = VTOI(vdp); 33712115Sdyson lockparent = flags & LOCKPARENT; 33812115Sdyson wantparent = flags & (LOCKPARENT|WANTPARENT); 33912115Sdyson 34012115Sdyson /* 34112115Sdyson * We now have a segment name to search for, and a directory to search. 34212115Sdyson */ 34312115Sdyson 34412115Sdyson /* 34512115Sdyson * Suppress search for slots unless creating 34612115Sdyson * file and at end of pathname, in which case 34712115Sdyson * we watch for a place to put the new file in 34812115Sdyson * case it doesn't already exist. 34912115Sdyson */ 35012115Sdyson slotstatus = FOUND; 35112115Sdyson slotfreespace = slotsize = slotneeded = 0; 35212115Sdyson if ((nameiop == CREATE || nameiop == RENAME) && 35312115Sdyson (flags & ISLASTCN)) { 35412115Sdyson slotstatus = NONE; 35512115Sdyson slotneeded = EXT2_DIR_REC_LEN(cnp->cn_namelen); 35612115Sdyson /* was 35712115Sdyson slotneeded = (sizeof(struct direct) - MAXNAMLEN + 35812115Sdyson cnp->cn_namelen + 3) &~ 3; */ 35912115Sdyson } 36012115Sdyson 36112115Sdyson /* 36212115Sdyson * If there is cached information on a previous search of 36312115Sdyson * this directory, pick up where we last left off. 36412115Sdyson * We cache only lookups as these are the most common 36512115Sdyson * and have the greatest payoff. Caching CREATE has little 36612115Sdyson * benefit as it usually must search the entire directory 36712115Sdyson * to determine that the entry does not exist. Caching the 36812115Sdyson * location of the last DELETE or RENAME has not reduced 36912115Sdyson * profiling time and hence has been removed in the interest 37012115Sdyson * of simplicity. 37112115Sdyson */ 37296749Siedowse bmask = VFSTOEXT2(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1; 37312115Sdyson if (nameiop != LOOKUP || dp->i_diroff == 0 || 37412115Sdyson dp->i_diroff > dp->i_size) { 37512115Sdyson entryoffsetinblock = 0; 37612115Sdyson dp->i_offset = 0; 37712115Sdyson numdirpasses = 1; 37812115Sdyson } else { 37912115Sdyson dp->i_offset = dp->i_diroff; 38012115Sdyson if ((entryoffsetinblock = dp->i_offset & bmask) && 38196749Siedowse (error = ext2_blkatoff(vdp, (off_t)dp->i_offset, NULL, 38296749Siedowse &bp))) 38312115Sdyson return (error); 38412115Sdyson numdirpasses = 2; 38512115Sdyson nchstats.ncs_2passes++; 38612115Sdyson } 38712115Sdyson prevoff = dp->i_offset; 38812115Sdyson endsearch = roundup(dp->i_size, DIRBLKSIZ); 38912115Sdyson enduseful = 0; 39012115Sdyson 39112115Sdysonsearchloop: 39212115Sdyson while (dp->i_offset < endsearch) { 39312115Sdyson /* 39412115Sdyson * If necessary, get the next directory block. 39512115Sdyson */ 39612115Sdyson if ((dp->i_offset & bmask) == 0) { 39712115Sdyson if (bp != NULL) 39812115Sdyson brelse(bp); 39943301Sdillon if ((error = 40096749Siedowse ext2_blkatoff(vdp, (off_t)dp->i_offset, NULL, 40196749Siedowse &bp)) != 0) 40212115Sdyson return (error); 40312115Sdyson entryoffsetinblock = 0; 40412115Sdyson } 40512115Sdyson /* 40612115Sdyson * If still looking for a slot, and at a DIRBLKSIZE 40712115Sdyson * boundary, have to start looking for free space again. 40812115Sdyson */ 40912115Sdyson if (slotstatus == NONE && 41012115Sdyson (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) { 41112115Sdyson slotoffset = -1; 41212115Sdyson slotfreespace = 0; 41312115Sdyson } 41412115Sdyson /* 41512115Sdyson * Get pointer to next entry. 41612115Sdyson * Full validation checks are slow, so we only check 41712115Sdyson * enough to insure forward progress through the 41896753Siedowse * directory. Complete checks can be run by setting 41996753Siedowse * "vfs.e2fs.dirchk" to be true. 42012115Sdyson */ 42155477Sbde ep = (struct ext2_dir_entry_2 *) 42212115Sdyson ((char *)bp->b_data + entryoffsetinblock); 42312115Sdyson if (ep->rec_len == 0 || 42412147Sdyson (dirchk && ext2_dirbadentry(vdp, ep, entryoffsetinblock))) { 42512115Sdyson int i; 42696749Siedowse ext2_dirbad(dp, dp->i_offset, "mangled entry"); 42712115Sdyson i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)); 42812115Sdyson dp->i_offset += i; 42912115Sdyson entryoffsetinblock += i; 43012115Sdyson continue; 43112115Sdyson } 43212115Sdyson 43312115Sdyson /* 43412115Sdyson * If an appropriate sized slot has not yet been found, 43512115Sdyson * check to see if one is available. Also accumulate space 43612115Sdyson * in the current block so that we can determine if 43712115Sdyson * compaction is viable. 43812115Sdyson */ 43912115Sdyson if (slotstatus != FOUND) { 44012115Sdyson int size = ep->rec_len; 44112115Sdyson 44212115Sdyson if (ep->inode != 0) 44312115Sdyson size -= EXT2_DIR_REC_LEN(ep->name_len); 44412115Sdyson if (size > 0) { 44512115Sdyson if (size >= slotneeded) { 44612115Sdyson slotstatus = FOUND; 44712115Sdyson slotoffset = dp->i_offset; 44812115Sdyson slotsize = ep->rec_len; 44912115Sdyson } else if (slotstatus == NONE) { 45012115Sdyson slotfreespace += size; 45112115Sdyson if (slotoffset == -1) 45212115Sdyson slotoffset = dp->i_offset; 45312115Sdyson if (slotfreespace >= slotneeded) { 45412115Sdyson slotstatus = COMPACT; 45512115Sdyson slotsize = dp->i_offset + 45612115Sdyson ep->rec_len - slotoffset; 45712115Sdyson } 45812115Sdyson } 45912115Sdyson } 46012115Sdyson } 46112115Sdyson 46212115Sdyson /* 46312115Sdyson * Check for a name match. 46412115Sdyson */ 46512115Sdyson if (ep->inode) { 46612115Sdyson namlen = ep->name_len; 46712115Sdyson if (namlen == cnp->cn_namelen && 46812115Sdyson !bcmp(cnp->cn_nameptr, ep->name, 46912115Sdyson (unsigned)namlen)) { 47012115Sdyson /* 47112115Sdyson * Save directory entry's inode number and 47212115Sdyson * reclen in ndp->ni_ufs area, and release 47312115Sdyson * directory buffer. 47412115Sdyson */ 47512115Sdyson dp->i_ino = ep->inode; 47612115Sdyson dp->i_reclen = ep->rec_len; 47712115Sdyson goto found; 47812115Sdyson } 47912115Sdyson } 48012115Sdyson prevoff = dp->i_offset; 48112115Sdyson dp->i_offset += ep->rec_len; 48212115Sdyson entryoffsetinblock += ep->rec_len; 48312115Sdyson if (ep->inode) 48412115Sdyson enduseful = dp->i_offset; 48512115Sdyson } 48612115Sdyson/* notfound: */ 48712115Sdyson /* 48812115Sdyson * If we started in the middle of the directory and failed 48912115Sdyson * to find our target, we must check the beginning as well. 49012115Sdyson */ 49112115Sdyson if (numdirpasses == 2) { 49212115Sdyson numdirpasses--; 49312115Sdyson dp->i_offset = 0; 49412115Sdyson endsearch = dp->i_diroff; 49512115Sdyson goto searchloop; 49612115Sdyson } 49712115Sdyson if (bp != NULL) 49812115Sdyson brelse(bp); 49912115Sdyson /* 50012115Sdyson * If creating, and at end of pathname and current 50112115Sdyson * directory has not been removed, then can consider 50212115Sdyson * allowing file to be created. 50312115Sdyson */ 50412115Sdyson if ((nameiop == CREATE || nameiop == RENAME) && 50512115Sdyson (flags & ISLASTCN) && dp->i_nlink != 0) { 50612115Sdyson /* 50712115Sdyson * Access for write is interpreted as allowing 50812115Sdyson * creation of files in the directory. 50912115Sdyson */ 51083366Sjulian if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0) 51112115Sdyson return (error); 51212115Sdyson /* 51312115Sdyson * Return an indication of where the new directory 51412115Sdyson * entry should be put. If we didn't find a slot, 51512115Sdyson * then set dp->i_count to 0 indicating 51612115Sdyson * that the new slot belongs at the end of the 51712115Sdyson * directory. If we found a slot, then the new entry 51812115Sdyson * can be put in the range from dp->i_offset to 51912115Sdyson * dp->i_offset + dp->i_count. 52012115Sdyson */ 52112115Sdyson if (slotstatus == NONE) { 52212115Sdyson dp->i_offset = roundup(dp->i_size, DIRBLKSIZ); 52312115Sdyson dp->i_count = 0; 52412115Sdyson enduseful = dp->i_offset; 52512115Sdyson } else { 52612115Sdyson dp->i_offset = slotoffset; 52712115Sdyson dp->i_count = slotsize; 52812115Sdyson if (enduseful < slotoffset + slotsize) 52912115Sdyson enduseful = slotoffset + slotsize; 53012115Sdyson } 53112115Sdyson dp->i_endoff = roundup(enduseful, DIRBLKSIZ); 53212115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 53312115Sdyson /* 53412115Sdyson * We return with the directory locked, so that 53512115Sdyson * the parameters we set up above will still be 53612115Sdyson * valid if we actually decide to do a direnter(). 53712115Sdyson * We return ni_vp == NULL to indicate that the entry 53812115Sdyson * does not currently exist; we leave a pointer to 53912115Sdyson * the (locked) directory inode in ndp->ni_dvp. 54012115Sdyson * The pathname buffer is saved so that the name 54112115Sdyson * can be obtained later. 54212115Sdyson * 54312115Sdyson * NB - if the directory is unlocked, then this 54412115Sdyson * information cannot be used. 54512115Sdyson */ 54612115Sdyson cnp->cn_flags |= SAVENAME; 54712115Sdyson if (!lockparent) 54883366Sjulian VOP_UNLOCK(vdp, 0, td); 54912115Sdyson return (EJUSTRETURN); 55012115Sdyson } 55112115Sdyson /* 55212115Sdyson * Insert name into cache (as non-existent) if appropriate. 55312115Sdyson */ 55412115Sdyson if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) 55512115Sdyson cache_enter(vdp, *vpp, cnp); 55612115Sdyson return (ENOENT); 55712115Sdyson 55812115Sdysonfound: 55912115Sdyson if (numdirpasses == 2) 56012115Sdyson nchstats.ncs_pass2++; 56112115Sdyson /* 56212115Sdyson * Check that directory length properly reflects presence 56312115Sdyson * of this entry. 56412115Sdyson */ 56512115Sdyson if (entryoffsetinblock + EXT2_DIR_REC_LEN(ep->name_len) 56612115Sdyson > dp->i_size) { 56796749Siedowse ext2_dirbad(dp, dp->i_offset, "i_size too small"); 56812115Sdyson dp->i_size = entryoffsetinblock+EXT2_DIR_REC_LEN(ep->name_len); 56912115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 57012115Sdyson } 571105420Sbde brelse(bp); 57212115Sdyson 57312115Sdyson /* 57412115Sdyson * Found component in pathname. 57512115Sdyson * If the final component of path name, save information 57612115Sdyson * in the cache as to where the entry was found. 57712115Sdyson */ 57812115Sdyson if ((flags & ISLASTCN) && nameiop == LOOKUP) 57912115Sdyson dp->i_diroff = dp->i_offset &~ (DIRBLKSIZ - 1); 58012115Sdyson 58112115Sdyson /* 58212115Sdyson * If deleting, and at end of pathname, return 58312115Sdyson * parameters which can be used to remove file. 58412115Sdyson * If the wantparent flag isn't set, we return only 58512115Sdyson * the directory (in ndp->ni_dvp), otherwise we go 58612115Sdyson * on and lock the inode, being careful with ".". 58712115Sdyson */ 58812115Sdyson if (nameiop == DELETE && (flags & ISLASTCN)) { 58912115Sdyson /* 59012115Sdyson * Write access to directory required to delete files. 59112115Sdyson */ 59283366Sjulian if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0) 59312115Sdyson return (error); 59412115Sdyson /* 59512115Sdyson * Return pointer to current entry in dp->i_offset, 59612115Sdyson * and distance past previous entry (if there 59712115Sdyson * is a previous entry in this block) in dp->i_count. 59812115Sdyson * Save directory inode pointer in ndp->ni_dvp for dirremove(). 59912115Sdyson */ 60012115Sdyson if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0) 60112115Sdyson dp->i_count = 0; 60212115Sdyson else 60312115Sdyson dp->i_count = dp->i_offset - prevoff; 60412115Sdyson if (dp->i_number == dp->i_ino) { 60512115Sdyson VREF(vdp); 60612115Sdyson *vpp = vdp; 60712115Sdyson return (0); 60812115Sdyson } 60992462Smckusick if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, LK_EXCLUSIVE, 61092462Smckusick &tdp)) != 0) 61112115Sdyson return (error); 61212115Sdyson /* 61312115Sdyson * If directory is "sticky", then user must own 61412115Sdyson * the directory, or the file in it, else she 61512115Sdyson * may not delete it (unless she's root). This 61612115Sdyson * implements append-only directories. 61712115Sdyson */ 61812115Sdyson if ((dp->i_mode & ISVTX) && 61912115Sdyson cred->cr_uid != 0 && 62012115Sdyson cred->cr_uid != dp->i_uid && 62112115Sdyson VTOI(tdp)->i_uid != cred->cr_uid) { 62212115Sdyson vput(tdp); 62312115Sdyson return (EPERM); 62412115Sdyson } 62512115Sdyson *vpp = tdp; 62612115Sdyson if (!lockparent) 62783366Sjulian VOP_UNLOCK(vdp, 0, td); 62812115Sdyson return (0); 62912115Sdyson } 63012115Sdyson 63112115Sdyson /* 63212115Sdyson * If rewriting (RENAME), return the inode and the 63312115Sdyson * information required to rewrite the present directory 63412115Sdyson * Must get inode of directory entry to verify it's a 63512115Sdyson * regular file, or empty directory. 63612115Sdyson */ 63712115Sdyson if (nameiop == RENAME && wantparent && 63812115Sdyson (flags & ISLASTCN)) { 63983366Sjulian if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0) 64012115Sdyson return (error); 64112115Sdyson /* 64212115Sdyson * Careful about locking second inode. 64312115Sdyson * This can only occur if the target is ".". 64412115Sdyson */ 64512115Sdyson if (dp->i_number == dp->i_ino) 64612115Sdyson return (EISDIR); 64792462Smckusick if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, LK_EXCLUSIVE, 64892462Smckusick &tdp)) != 0) 64912115Sdyson return (error); 65012115Sdyson *vpp = tdp; 65112115Sdyson cnp->cn_flags |= SAVENAME; 65212115Sdyson if (!lockparent) 65383366Sjulian VOP_UNLOCK(vdp, 0, td); 65412115Sdyson return (0); 65512115Sdyson } 65612115Sdyson 65712115Sdyson /* 65812115Sdyson * Step through the translation in the name. We do not `vput' the 65912115Sdyson * directory because we may need it again if a symbolic link 66012115Sdyson * is relative to the current directory. Instead we save it 66112115Sdyson * unlocked as "pdp". We must get the target inode before unlocking 66212115Sdyson * the directory to insure that the inode will not be removed 66312115Sdyson * before we get it. We prevent deadlock by always fetching 66412115Sdyson * inodes from the root, moving down the directory tree. Thus 66512115Sdyson * when following backward pointers ".." we must unlock the 66612115Sdyson * parent directory before getting the requested directory. 66712115Sdyson * There is a potential race condition here if both the current 66812115Sdyson * and parent directories are removed before the VFS_VGET for the 66912115Sdyson * inode associated with ".." returns. We hope that this occurs 67012115Sdyson * infrequently since we cannot avoid this race condition without 67112115Sdyson * implementing a sophisticated deadlock detection algorithm. 67212115Sdyson * Note also that this simple deadlock detection scheme will not 67312115Sdyson * work if the file system has any hard links other than ".." 67412115Sdyson * that point backwards in the directory structure. 67512115Sdyson */ 67612115Sdyson pdp = vdp; 67712115Sdyson if (flags & ISDOTDOT) { 67883366Sjulian VOP_UNLOCK(pdp, 0, td); /* race to get the inode */ 67992462Smckusick if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, LK_EXCLUSIVE, 68092462Smckusick &tdp)) != 0) { 68183366Sjulian vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, td); 68212115Sdyson return (error); 68312115Sdyson } 68412115Sdyson if (lockparent && (flags & ISLASTCN) && 68583366Sjulian (error = vn_lock(pdp, LK_EXCLUSIVE, td))) { 68612115Sdyson vput(tdp); 68712115Sdyson return (error); 68812115Sdyson } 68912115Sdyson *vpp = tdp; 69012115Sdyson } else if (dp->i_number == dp->i_ino) { 69112115Sdyson VREF(vdp); /* we want ourself, ie "." */ 69212115Sdyson *vpp = vdp; 69312115Sdyson } else { 69492462Smckusick if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, LK_EXCLUSIVE, 69592462Smckusick &tdp)) != 0) 69612115Sdyson return (error); 69712115Sdyson if (!lockparent || !(flags & ISLASTCN)) 69883366Sjulian VOP_UNLOCK(pdp, 0, td); 69912115Sdyson *vpp = tdp; 70012115Sdyson } 70112115Sdyson 70212115Sdyson /* 70312115Sdyson * Insert name into cache if appropriate. 70412115Sdyson */ 70512115Sdyson if (cnp->cn_flags & MAKEENTRY) 70612115Sdyson cache_enter(vdp, *vpp, cnp); 70712115Sdyson return (0); 70812115Sdyson} 70912115Sdyson 71096749Siedowsevoid 71196749Siedowseext2_dirbad(ip, offset, how) 71296749Siedowse struct inode *ip; 71396749Siedowse doff_t offset; 71496749Siedowse char *how; 71596749Siedowse{ 71696749Siedowse struct mount *mp; 71796749Siedowse 71896749Siedowse mp = ITOV(ip)->v_mount; 71996749Siedowse (void)printf("%s: bad dir ino %lu at offset %ld: %s\n", 72096749Siedowse mp->mnt_stat.f_mntonname, (u_long)ip->i_number, (long)offset, how); 72196749Siedowse if ((mp->mnt_flag & MNT_RDONLY) == 0) 72296749Siedowse panic("ext2_dirbad: bad dir"); 72396749Siedowse} 72496749Siedowse 72512115Sdyson/* 72612115Sdyson * Do consistency checking on a directory entry: 72712115Sdyson * record length must be multiple of 4 72812115Sdyson * entry must fit in rest of its DIRBLKSIZ block 72912115Sdyson * record must be large enough to contain entry 73012115Sdyson * name is not longer than MAXNAMLEN 73112115Sdyson * name must be as long as advertised, and null terminated 73212115Sdyson */ 73312115Sdyson/* 73412115Sdyson * changed so that it confirms to ext2_check_dir_entry 73512115Sdyson */ 73612159Sbdestatic int 73712115Sdysonext2_dirbadentry(dp, de, entryoffsetinblock) 73812115Sdyson struct vnode *dp; 73996752Siedowse struct ext2_dir_entry_2 *de; 74012115Sdyson int entryoffsetinblock; 74112115Sdyson{ 74212115Sdyson int DIRBLKSIZ = VTOI(dp)->i_e2fs->s_blocksize; 74312115Sdyson 74412115Sdyson char * error_msg = NULL; 74512115Sdyson 74612115Sdyson if (de->rec_len < EXT2_DIR_REC_LEN(1)) 74712115Sdyson error_msg = "rec_len is smaller than minimal"; 74812115Sdyson else if (de->rec_len % 4 != 0) 74912115Sdyson error_msg = "rec_len % 4 != 0"; 75012115Sdyson else if (de->rec_len < EXT2_DIR_REC_LEN(de->name_len)) 75112115Sdyson error_msg = "reclen is too small for name_len"; 75212115Sdyson else if (entryoffsetinblock + de->rec_len > DIRBLKSIZ) 75312115Sdyson error_msg = "directory entry across blocks"; 75412115Sdyson /* else LATER 75512115Sdyson if (de->inode > dir->i_sb->u.ext2_sb.s_es->s_inodes_count) 75612115Sdyson error_msg = "inode out of bounds"; 75712115Sdyson */ 75812115Sdyson 75924492Sbde if (error_msg != NULL) { 76024492Sbde printf("bad directory entry: %s\n", error_msg); 76137555Sbde printf("offset=%d, inode=%lu, rec_len=%u, name_len=%u\n", 76224492Sbde entryoffsetinblock, (unsigned long)de->inode, 76324492Sbde de->rec_len, de->name_len); 76424492Sbde } 76512115Sdyson return error_msg == NULL ? 0 : 1; 76612115Sdyson} 76712115Sdyson 76812115Sdyson/* 76912115Sdyson * Write a directory entry after a call to namei, using the parameters 77012115Sdyson * that it left in nameidata. The argument ip is the inode which the new 77112115Sdyson * directory entry will refer to. Dvp is a pointer to the directory to 77212115Sdyson * be written, which was left locked by namei. Remaining parameters 77312115Sdyson * (dp->i_offset, dp->i_count) indicate how the space for the new 77412115Sdyson * entry is to be obtained. 77512115Sdyson */ 77612115Sdysonint 77712115Sdysonext2_direnter(ip, dvp, cnp) 77812115Sdyson struct inode *ip; 77912115Sdyson struct vnode *dvp; 78096752Siedowse struct componentname *cnp; 78112115Sdyson{ 78296752Siedowse struct ext2_dir_entry_2 *ep, *nep; 78396752Siedowse struct inode *dp; 78412115Sdyson struct buf *bp; 78555477Sbde struct ext2_dir_entry_2 newdir; 78612115Sdyson struct iovec aiov; 78712115Sdyson struct uio auio; 78812115Sdyson u_int dsize; 78912115Sdyson int error, loc, newentrysize, spacefree; 79012115Sdyson char *dirbuf; 79112115Sdyson int DIRBLKSIZ = ip->i_e2fs->s_blocksize; 79212115Sdyson 79312115Sdyson 79412115Sdyson#if DIAGNOSTIC 79512115Sdyson if ((cnp->cn_flags & SAVENAME) == 0) 79612115Sdyson panic("direnter: missing name"); 79712115Sdyson#endif 79812115Sdyson dp = VTOI(dvp); 79912115Sdyson newdir.inode = ip->i_number; 80012115Sdyson newdir.name_len = cnp->cn_namelen; 80155477Sbde if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs->s_es, 80255477Sbde EXT2_FEATURE_INCOMPAT_FILETYPE)) 80355477Sbde newdir.file_type = DTTOFT(IFTODT(ip->i_mode)); 80455477Sbde else 80555477Sbde newdir.file_type = EXT2_FT_UNKNOWN; 80612115Sdyson bcopy(cnp->cn_nameptr, newdir.name, (unsigned)cnp->cn_namelen + 1); 80712115Sdyson newentrysize = EXT2_DIR_REC_LEN(newdir.name_len); 80812115Sdyson if (dp->i_count == 0) { 80912115Sdyson /* 81012115Sdyson * If dp->i_count is 0, then namei could find no 81112115Sdyson * space in the directory. Here, dp->i_offset will 81212115Sdyson * be on a directory block boundary and we will write the 81312115Sdyson * new entry into a fresh block. 81412115Sdyson */ 81512115Sdyson if (dp->i_offset & (DIRBLKSIZ - 1)) 81612115Sdyson panic("ext2_direnter: newblk"); 81712115Sdyson auio.uio_offset = dp->i_offset; 81812115Sdyson newdir.rec_len = DIRBLKSIZ; 81912115Sdyson auio.uio_resid = newentrysize; 82012115Sdyson aiov.iov_len = newentrysize; 82112115Sdyson aiov.iov_base = (caddr_t)&newdir; 82212115Sdyson auio.uio_iov = &aiov; 82312115Sdyson auio.uio_iovcnt = 1; 82412115Sdyson auio.uio_rw = UIO_WRITE; 82512115Sdyson auio.uio_segflg = UIO_SYSSPACE; 82683366Sjulian auio.uio_td = (struct thread *)0; 82712115Sdyson error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred); 82812115Sdyson if (DIRBLKSIZ > 82996749Siedowse VFSTOEXT2(dvp->v_mount)->um_mountp->mnt_stat.f_bsize) 83012115Sdyson /* XXX should grow with balloc() */ 83112115Sdyson panic("ext2_direnter: frag size"); 83212115Sdyson else if (!error) { 83312115Sdyson dp->i_size = roundup(dp->i_size, DIRBLKSIZ); 83412115Sdyson dp->i_flag |= IN_CHANGE; 83512115Sdyson } 83612115Sdyson return (error); 83712115Sdyson } 83812115Sdyson 83912115Sdyson /* 84012115Sdyson * If dp->i_count is non-zero, then namei found space 84112115Sdyson * for the new entry in the range dp->i_offset to 84212115Sdyson * dp->i_offset + dp->i_count in the directory. 84312115Sdyson * To use this space, we may have to compact the entries located 84412115Sdyson * there, by copying them together towards the beginning of the 84512115Sdyson * block, leaving the free space in one usable chunk at the end. 84612115Sdyson */ 84712115Sdyson 84812115Sdyson /* 84912115Sdyson * Increase size of directory if entry eats into new space. 85012115Sdyson * This should never push the size past a new multiple of 85112115Sdyson * DIRBLKSIZE. 85212115Sdyson * 85312115Sdyson * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN. 85412115Sdyson */ 85512115Sdyson if (dp->i_offset + dp->i_count > dp->i_size) 85612115Sdyson dp->i_size = dp->i_offset + dp->i_count; 85712115Sdyson /* 85812115Sdyson * Get the block containing the space for the new directory entry. 85912115Sdyson */ 86096749Siedowse if ((error = ext2_blkatoff(dvp, (off_t)dp->i_offset, &dirbuf, 86196749Siedowse &bp)) != 0) 86212115Sdyson return (error); 86312115Sdyson /* 86412115Sdyson * Find space for the new entry. In the simple case, the entry at 86512115Sdyson * offset base will have the space. If it does not, then namei 86612115Sdyson * arranged that compacting the region dp->i_offset to 86712115Sdyson * dp->i_offset + dp->i_count would yield the 86812115Sdyson * space. 86912115Sdyson */ 87055477Sbde ep = (struct ext2_dir_entry_2 *)dirbuf; 87112115Sdyson dsize = EXT2_DIR_REC_LEN(ep->name_len); 87212115Sdyson spacefree = ep->rec_len - dsize; 87312115Sdyson for (loc = ep->rec_len; loc < dp->i_count; ) { 87455477Sbde nep = (struct ext2_dir_entry_2 *)(dirbuf + loc); 87512115Sdyson if (ep->inode) { 87612115Sdyson /* trim the existing slot */ 87712115Sdyson ep->rec_len = dsize; 87855477Sbde ep = (struct ext2_dir_entry_2 *)((char *)ep + dsize); 87912115Sdyson } else { 88012115Sdyson /* overwrite; nothing there; header is ours */ 88112115Sdyson spacefree += dsize; 88212115Sdyson } 88319541Sbde dsize = EXT2_DIR_REC_LEN(nep->name_len); 88412115Sdyson spacefree += nep->rec_len - dsize; 88512115Sdyson loc += nep->rec_len; 88612115Sdyson bcopy((caddr_t)nep, (caddr_t)ep, dsize); 88712115Sdyson } 88812115Sdyson /* 88912115Sdyson * Update the pointer fields in the previous entry (if any), 89012115Sdyson * copy in the new entry, and write out the block. 89112115Sdyson */ 89212115Sdyson if (ep->inode == 0) { 89312115Sdyson if (spacefree + dsize < newentrysize) 89412115Sdyson panic("ext2_direnter: compact1"); 89512115Sdyson newdir.rec_len = spacefree + dsize; 89612115Sdyson } else { 89712115Sdyson if (spacefree < newentrysize) 89812115Sdyson panic("ext2_direnter: compact2"); 89912115Sdyson newdir.rec_len = spacefree; 90012115Sdyson ep->rec_len = dsize; 90155477Sbde ep = (struct ext2_dir_entry_2 *)((char *)ep + dsize); 90212115Sdyson } 90312115Sdyson bcopy((caddr_t)&newdir, (caddr_t)ep, (u_int)newentrysize); 90458349Sphk error = BUF_WRITE(bp); 90512115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 90612115Sdyson if (!error && dp->i_endoff && dp->i_endoff < dp->i_size) 90796749Siedowse error = ext2_truncate(dvp, (off_t)dp->i_endoff, IO_SYNC, 90883366Sjulian cnp->cn_cred, cnp->cn_thread); 90912115Sdyson return (error); 91012115Sdyson} 91112115Sdyson 91212115Sdyson/* 91312115Sdyson * Remove a directory entry after a call to namei, using 91412115Sdyson * the parameters which it left in nameidata. The entry 91512115Sdyson * dp->i_offset contains the offset into the directory of the 91612115Sdyson * entry to be eliminated. The dp->i_count field contains the 91712115Sdyson * size of the previous record in the directory. If this 91812115Sdyson * is 0, the first entry is being deleted, so we need only 91912115Sdyson * zero the inode number to mark the entry as free. If the 92012115Sdyson * entry is not the first in the directory, we must reclaim 92112115Sdyson * the space of the now empty record by adding the record size 92212115Sdyson * to the size of the previous entry. 92312115Sdyson */ 92412115Sdysonint 92512115Sdysonext2_dirremove(dvp, cnp) 92612115Sdyson struct vnode *dvp; 92712115Sdyson struct componentname *cnp; 92812115Sdyson{ 92996752Siedowse struct inode *dp; 93055477Sbde struct ext2_dir_entry_2 *ep; 93112115Sdyson struct buf *bp; 93212115Sdyson int error; 93312115Sdyson 93412115Sdyson dp = VTOI(dvp); 93512115Sdyson if (dp->i_count == 0) { 93612115Sdyson /* 93712115Sdyson * First entry in block: set d_ino to zero. 93812115Sdyson */ 93943301Sdillon if ((error = 94096749Siedowse ext2_blkatoff(dvp, (off_t)dp->i_offset, (char **)&ep, 94196749Siedowse &bp)) != 0) 94212115Sdyson return (error); 94312115Sdyson ep->inode = 0; 94458349Sphk error = BUF_WRITE(bp); 94512115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 94612115Sdyson return (error); 94712115Sdyson } 94812115Sdyson /* 94912115Sdyson * Collapse new free space into previous entry. 95012115Sdyson */ 95196749Siedowse if ((error = ext2_blkatoff(dvp, (off_t)(dp->i_offset - dp->i_count), 95243301Sdillon (char **)&ep, &bp)) != 0) 95312115Sdyson return (error); 95412115Sdyson ep->rec_len += dp->i_reclen; 95558349Sphk error = BUF_WRITE(bp); 95612115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 95712115Sdyson return (error); 95812115Sdyson} 95912115Sdyson 96012115Sdyson/* 96112115Sdyson * Rewrite an existing directory entry to point at the inode 96212115Sdyson * supplied. The parameters describing the directory entry are 96312115Sdyson * set up by a call to namei. 96412115Sdyson */ 96512115Sdysonint 96612115Sdysonext2_dirrewrite(dp, ip, cnp) 96712115Sdyson struct inode *dp, *ip; 96812115Sdyson struct componentname *cnp; 96912115Sdyson{ 97012115Sdyson struct buf *bp; 97155477Sbde struct ext2_dir_entry_2 *ep; 97212115Sdyson struct vnode *vdp = ITOV(dp); 97312115Sdyson int error; 97412115Sdyson 97596749Siedowse if ((error = ext2_blkatoff(vdp, (off_t)dp->i_offset, (char **)&ep, 97696749Siedowse &bp)) != 0) 97712115Sdyson return (error); 97812115Sdyson ep->inode = ip->i_number; 97955477Sbde if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs->s_es, 98055477Sbde EXT2_FEATURE_INCOMPAT_FILETYPE)) 98155477Sbde ep->file_type = DTTOFT(IFTODT(ip->i_mode)); 98255477Sbde else 98355477Sbde ep->file_type = EXT2_FT_UNKNOWN; 98458349Sphk error = BUF_WRITE(bp); 98512115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 98612115Sdyson return (error); 98712115Sdyson} 98812115Sdyson 98912115Sdyson/* 99012115Sdyson * Check if a directory is empty or not. 99112115Sdyson * Inode supplied must be locked. 99212115Sdyson * 99312115Sdyson * Using a struct dirtemplate here is not precisely 99412115Sdyson * what we want, but better than using a struct direct. 99512115Sdyson * 99612115Sdyson * NB: does not handle corrupted directories. 99712115Sdyson */ 99812115Sdysonint 99912115Sdysonext2_dirempty(ip, parentino, cred) 100096752Siedowse struct inode *ip; 100112115Sdyson ino_t parentino; 100212115Sdyson struct ucred *cred; 100312115Sdyson{ 100496752Siedowse off_t off; 100512115Sdyson struct dirtemplate dbuf; 100696752Siedowse struct ext2_dir_entry_2 *dp = (struct ext2_dir_entry_2 *)&dbuf; 100712115Sdyson int error, count, namlen; 100812115Sdyson 100912115Sdyson#define MINDIRSIZ (sizeof (struct dirtemplate) / 2) 101012115Sdyson 101112115Sdyson for (off = 0; off < ip->i_size; off += dp->rec_len) { 1012101744Srwatson error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, 1013101744Srwatson off, UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK, cred, 1014101941Srwatson NOCRED, &count, (struct thread *)0); 101512115Sdyson /* 101612115Sdyson * Since we read MINDIRSIZ, residual must 101712115Sdyson * be 0 unless we're at end of file. 101812115Sdyson */ 101912115Sdyson if (error || count != 0) 102012115Sdyson return (0); 102112115Sdyson /* avoid infinite loops */ 102212115Sdyson if (dp->rec_len == 0) 102312115Sdyson return (0); 102412115Sdyson /* skip empty entries */ 102512115Sdyson if (dp->inode == 0) 102612115Sdyson continue; 102712115Sdyson /* accept only "." and ".." */ 102812115Sdyson namlen = dp->name_len; 102912115Sdyson if (namlen > 2) 103012115Sdyson return (0); 103112115Sdyson if (dp->name[0] != '.') 103212115Sdyson return (0); 103312115Sdyson /* 103412115Sdyson * At this point namlen must be 1 or 2. 103512115Sdyson * 1 implies ".", 2 implies ".." if second 103612115Sdyson * char is also "." 103712115Sdyson */ 103812115Sdyson if (namlen == 1) 103912115Sdyson continue; 104012115Sdyson if (dp->name[1] == '.' && dp->inode == parentino) 104112115Sdyson continue; 104212115Sdyson return (0); 104312115Sdyson } 104412115Sdyson return (1); 104512115Sdyson} 104612115Sdyson 104712115Sdyson/* 104812115Sdyson * Check if source directory is in the path of the target directory. 104912115Sdyson * Target is supplied locked, source is unlocked. 105012115Sdyson * The target is always vput before returning. 105112115Sdyson */ 105212115Sdysonint 105312115Sdysonext2_checkpath(source, target, cred) 105412115Sdyson struct inode *source, *target; 105512115Sdyson struct ucred *cred; 105612115Sdyson{ 105712115Sdyson struct vnode *vp; 105812115Sdyson int error, rootino, namlen; 105912115Sdyson struct dirtemplate dirbuf; 106012115Sdyson 106112115Sdyson vp = ITOV(target); 106212115Sdyson if (target->i_number == source->i_number) { 106312115Sdyson error = EEXIST; 106412115Sdyson goto out; 106512115Sdyson } 106612115Sdyson rootino = ROOTINO; 106712115Sdyson error = 0; 106812115Sdyson if (target->i_number == rootino) 106912115Sdyson goto out; 107012115Sdyson 107112115Sdyson for (;;) { 107212115Sdyson if (vp->v_type != VDIR) { 107312115Sdyson error = ENOTDIR; 107412115Sdyson break; 107512115Sdyson } 107612115Sdyson error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf, 107712115Sdyson sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE, 1078101941Srwatson IO_NODELOCKED | IO_NOMACCHECK, cred, NOCRED, (int *)0, 1079101744Srwatson (struct thread *)0); 108012115Sdyson if (error != 0) 108112115Sdyson break; 108257710Sbde namlen = dirbuf.dotdot_type; /* like ufs little-endian */ 108312115Sdyson if (namlen != 2 || 108412115Sdyson dirbuf.dotdot_name[0] != '.' || 108512115Sdyson dirbuf.dotdot_name[1] != '.') { 108612115Sdyson error = ENOTDIR; 108712115Sdyson break; 108812115Sdyson } 108912115Sdyson if (dirbuf.dotdot_ino == source->i_number) { 109012115Sdyson error = EINVAL; 109112115Sdyson break; 109212115Sdyson } 109312115Sdyson if (dirbuf.dotdot_ino == rootino) 109412115Sdyson break; 109512115Sdyson vput(vp); 109692462Smckusick if ((error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino, 109792462Smckusick LK_EXCLUSIVE, &vp)) != 0) { 109812115Sdyson vp = NULL; 109912115Sdyson break; 110012115Sdyson } 110112115Sdyson } 110212115Sdyson 110312115Sdysonout: 110412115Sdyson if (error == ENOTDIR) 110512115Sdyson printf("checkpath: .. not a directory\n"); 110612115Sdyson if (vp != NULL) 110712115Sdyson vput(vp); 110812115Sdyson return (error); 110912115Sdyson} 111012115Sdyson 1111