ext2_lookup.c revision 145006
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: head/sys/gnu/fs/ext2fs/ext2_lookup.c 145006 2005-04-13 10:59:09Z jeff $ 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> 4912115Sdyson#include <sys/mount.h> 5012115Sdyson#include <sys/vnode.h> 5112115Sdyson#include <sys/malloc.h> 5212115Sdyson#include <sys/dirent.h> 5396753Siedowse#include <sys/sysctl.h> 5412115Sdyson 5512115Sdyson#include <ufs/ufs/dir.h> 5612115Sdyson 5796749Siedowse#include <gnu/ext2fs/inode.h> 5896749Siedowse#include <gnu/ext2fs/ext2_mount.h> 5912115Sdyson#include <gnu/ext2fs/ext2_extern.h> 6012115Sdyson#include <gnu/ext2fs/ext2_fs.h> 6112115Sdyson#include <gnu/ext2fs/ext2_fs_sb.h> 6212115Sdyson 6396753Siedowse#ifdef DIAGNOSTIC 6496753Siedowsestatic int dirchk = 1; 6596753Siedowse#else 6696753Siedowsestatic int dirchk = 0; 6796753Siedowse#endif 6896753Siedowse 69141633Sphkstatic SYSCTL_NODE(_vfs, OID_AUTO, e2fs, CTLFLAG_RD, 0, "EXT2FS filesystem"); 7096753SiedowseSYSCTL_INT(_vfs_e2fs, OID_AUTO, dircheck, CTLFLAG_RW, &dirchk, 0, ""); 7196753Siedowse 72111742Sdes/* 7312115Sdyson DIRBLKSIZE in ffs is DEV_BSIZE (in most cases 512) 7412115Sdyson while it is the native blocksize in ext2fs - thus, a #define 7512115Sdyson is no longer appropriate 7612115Sdyson*/ 7712115Sdyson#undef DIRBLKSIZ 7812115Sdyson 7955477Sbdestatic u_char ext2_ft_to_dt[] = { 8055477Sbde DT_UNKNOWN, /* EXT2_FT_UNKNOWN */ 8155477Sbde DT_REG, /* EXT2_FT_REG_FILE */ 8255477Sbde DT_DIR, /* EXT2_FT_DIR */ 8355477Sbde DT_CHR, /* EXT2_FT_CHRDEV */ 8455477Sbde DT_BLK, /* EXT2_FT_BLKDEV */ 8555477Sbde DT_FIFO, /* EXT2_FT_FIFO */ 8655477Sbde DT_SOCK, /* EXT2_FT_SOCK */ 8755477Sbde DT_LNK, /* EXT2_FT_SYMLINK */ 8855477Sbde}; 8955477Sbde#define FTTODT(ft) \ 9055477Sbde ((ft) > sizeof(ext2_ft_to_dt) / sizeof(ext2_ft_to_dt[0]) ? \ 9155477Sbde DT_UNKNOWN : ext2_ft_to_dt[(ft)]) 9255477Sbde 9355477Sbdestatic u_char dt_to_ext2_ft[] = { 9455477Sbde EXT2_FT_UNKNOWN, /* DT_UNKNOWN */ 9555477Sbde EXT2_FT_FIFO, /* DT_FIFO */ 9655477Sbde EXT2_FT_CHRDEV, /* DT_CHR */ 9755477Sbde EXT2_FT_UNKNOWN, /* unused */ 9855477Sbde EXT2_FT_DIR, /* DT_DIR */ 9955477Sbde EXT2_FT_UNKNOWN, /* unused */ 10055477Sbde EXT2_FT_BLKDEV, /* DT_BLK */ 10155477Sbde EXT2_FT_UNKNOWN, /* unused */ 10255477Sbde EXT2_FT_REG_FILE, /* DT_REG */ 10355477Sbde EXT2_FT_UNKNOWN, /* unused */ 10455477Sbde EXT2_FT_SYMLINK, /* DT_LNK */ 10555477Sbde EXT2_FT_UNKNOWN, /* unused */ 10655477Sbde EXT2_FT_SOCK, /* DT_SOCK */ 10755477Sbde EXT2_FT_UNKNOWN, /* unused */ 10855477Sbde EXT2_FT_UNKNOWN, /* DT_WHT */ 10955477Sbde}; 11055477Sbde#define DTTOFT(dt) \ 11155477Sbde ((dt) > sizeof(dt_to_ext2_ft) / sizeof(dt_to_ext2_ft[0]) ? \ 11255477Sbde EXT2_FT_UNKNOWN : dt_to_ext2_ft[(dt)]) 11355477Sbde 11493014Sbdestatic int ext2_dirbadentry(struct vnode *dp, struct ext2_dir_entry_2 *de, 11593014Sbde int entryoffsetinblock); 11612159Sbde 11712115Sdyson/* 11812115Sdyson * Vnode op for reading directories. 11912115Sdyson * 12012115Sdyson * The routine below assumes that the on-disk format of a directory 12112115Sdyson * is the same as that defined by <sys/dirent.h>. If the on-disk 12212115Sdyson * format changes, then it will be necessary to do a conversion 12312115Sdyson * from the on-disk format that read returns to the format defined 12412115Sdyson * by <sys/dirent.h>. 12512115Sdyson */ 12612115Sdyson/* 12712115Sdyson * this is exactly what we do here - the problem is that the conversion 12812115Sdyson * will blow up some entries by four bytes, so it can't be done in place. 12912115Sdyson * This is too bad. Right now the conversion is done entry by entry, the 130111742Sdes * converted entry is sent via uiomove. 13112115Sdyson * 13212115Sdyson * XXX allocate a buffer, convert as many entries as possible, then send 13312115Sdyson * the whole buffer to uiomove 13412115Sdyson */ 13512115Sdysonint 13612115Sdysonext2_readdir(ap) 137111742Sdes struct vop_readdir_args /* { 138111742Sdes struct vnode *a_vp; 139111742Sdes struct uio *a_uio; 140111742Sdes struct ucred *a_cred; 141111742Sdes } */ *ap; 14212115Sdyson{ 143111742Sdes struct uio *uio = ap->a_uio; 144111742Sdes int count, error; 14512115Sdyson 14655477Sbde struct ext2_dir_entry_2 *edp, *dp; 14724649Sdfr int ncookies; 14812115Sdyson struct dirent dstdp; 14912115Sdyson struct uio auio; 15012115Sdyson struct iovec aiov; 15112115Sdyson caddr_t dirbuf; 15265780Sbde int DIRBLKSIZ = VTOI(ap->a_vp)->i_e2fs->s_blocksize; 15312115Sdyson int readcnt; 15465780Sbde off_t startoffset = uio->uio_offset; 15512115Sdyson 15665780Sbde count = uio->uio_resid; 15765780Sbde /* 15865780Sbde * Avoid complications for partial directory entries by adjusting 15965780Sbde * the i/o to end at a block boundary. Don't give up (like ufs 16065780Sbde * does) if the initial adjustment gives a negative count, since 16165780Sbde * many callers don't supply a large enough buffer. The correct 16265780Sbde * size is a little larger than DIRBLKSIZ to allow for expansion 16365780Sbde * of directory entries, but some callers just use 512. 16465780Sbde */ 16565780Sbde count -= (uio->uio_offset + count) & (DIRBLKSIZ -1); 16665780Sbde if (count <= 0) 16765780Sbde count += DIRBLKSIZ; 16812115Sdyson 16965780Sbde#ifdef EXT2FS_DEBUG 170111742Sdes printf("ext2_readdir: uio_offset = %lld, uio_resid = %d, count = %d\n", 17165780Sbde uio->uio_offset, uio->uio_resid, count); 17212115Sdyson#endif 17312115Sdyson 17412115Sdyson auio = *uio; 17512115Sdyson auio.uio_iov = &aiov; 17612115Sdyson auio.uio_iovcnt = 1; 17765780Sbde auio.uio_resid = count; 17812115Sdyson auio.uio_segflg = UIO_SYSSPACE; 17912115Sdyson aiov.iov_len = count; 180111119Simp MALLOC(dirbuf, caddr_t, count, M_TEMP, M_WAITOK); 18112115Sdyson aiov.iov_base = dirbuf; 18212115Sdyson error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred); 18312115Sdyson if (error == 0) { 18412115Sdyson readcnt = count - auio.uio_resid; 18555477Sbde edp = (struct ext2_dir_entry_2 *)&dirbuf[readcnt]; 18624649Sdfr ncookies = 0; 18755477Sbde bzero(&dstdp, offsetof(struct dirent, d_name)); 188111742Sdes for (dp = (struct ext2_dir_entry_2 *)dirbuf; 18955477Sbde !error && uio->uio_resid > 0 && dp < edp; ) { 19055477Sbde /*- 19155477Sbde * "New" ext2fs directory entries differ in 3 ways 19255477Sbde * from ufs on-disk ones: 19355477Sbde * - the name is not necessarily NUL-terminated. 19455477Sbde * - the file type field always exists and always 195125843Sbde * follows the name length field. 19655477Sbde * - the file type is encoded in a different way. 19755477Sbde * 19855477Sbde * "Old" ext2fs directory entries need no special 199125843Sbde * conversions, since they are binary compatible 200125843Sbde * with "new" entries having a file type of 0 (i.e., 20155477Sbde * EXT2_FT_UNKNOWN). Splitting the old name length 20255477Sbde * field didn't make a mess like it did in ufs, 203125843Sbde * because ext2fs uses a machine-independent disk 20455477Sbde * layout. 20555477Sbde */ 20655477Sbde dstdp.d_fileno = dp->inode; 20755477Sbde dstdp.d_type = FTTODT(dp->file_type); 20855477Sbde dstdp.d_namlen = dp->name_len; 20955477Sbde dstdp.d_reclen = GENERIC_DIRSIZ(&dstdp); 21055477Sbde bcopy(dp->name, dstdp.d_name, dstdp.d_namlen); 21155477Sbde bzero(dstdp.d_name + dstdp.d_namlen, 21255477Sbde dstdp.d_reclen - offsetof(struct dirent, d_name) - 21355477Sbde dstdp.d_namlen); 21455477Sbde 21512115Sdyson if (dp->rec_len > 0) { 21612115Sdyson if(dstdp.d_reclen <= uio->uio_resid) { 21712115Sdyson /* advance dp */ 21855477Sbde dp = (struct ext2_dir_entry_2 *) 219111742Sdes ((char *)dp + dp->rec_len); 220111742Sdes error = 221111741Sdes uiomove(&dstdp, dstdp.d_reclen, uio); 22224649Sdfr if (!error) 22324649Sdfr ncookies++; 22412115Sdyson } else 22512115Sdyson break; 22612115Sdyson } else { 22712115Sdyson error = EIO; 22812115Sdyson break; 22912115Sdyson } 23012115Sdyson } 23112115Sdyson /* we need to correct uio_offset */ 23212115Sdyson uio->uio_offset = startoffset + (caddr_t)dp - dirbuf; 23324649Sdfr 23424649Sdfr if (!error && ap->a_ncookies != NULL) { 23565780Sbde u_long *cookiep, *cookies, *ecookies; 23624649Sdfr off_t off; 23724649Sdfr 23824649Sdfr if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1) 239125843Sbde panic("ext2_readdir: unexpected uio from NFS server"); 24024649Sdfr MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP, 241111119Simp M_WAITOK); 24224649Sdfr off = startoffset; 24365780Sbde for (dp = (struct ext2_dir_entry_2 *)dirbuf, 24465780Sbde cookiep = cookies, ecookies = cookies + ncookies; 24565780Sbde cookiep < ecookies; 24655477Sbde dp = (struct ext2_dir_entry_2 *)((caddr_t) dp + dp->rec_len)) { 24724649Sdfr off += dp->rec_len; 24824649Sdfr *cookiep++ = (u_long) off; 24924649Sdfr } 25024649Sdfr *ap->a_ncookies = ncookies; 25124649Sdfr *ap->a_cookies = cookies; 25224649Sdfr } 25312115Sdyson } 25412115Sdyson FREE(dirbuf, M_TEMP); 25524649Sdfr if (ap->a_eofflag) 25624649Sdfr *ap->a_eofflag = VTOI(ap->a_vp)->i_size <= uio->uio_offset; 257111742Sdes return (error); 25812115Sdyson} 25912115Sdyson 26012115Sdyson/* 26112115Sdyson * Convert a component of a pathname into a pointer to a locked inode. 26212115Sdyson * This is a very central and rather complicated routine. 26312115Sdyson * If the file system is not maintained in a strict tree hierarchy, 26412115Sdyson * this can result in a deadlock situation (see comments in code below). 26512115Sdyson * 26612115Sdyson * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending 26712115Sdyson * on whether the name is to be looked up, created, renamed, or deleted. 26812115Sdyson * When CREATE, RENAME, or DELETE is specified, information usable in 26912115Sdyson * creating, renaming, or deleting a directory entry may be calculated. 27012115Sdyson * If flag has LOCKPARENT or'ed into it and the target of the pathname 27112115Sdyson * exists, lookup returns both the target and its parent directory locked. 27212115Sdyson * When creating or renaming and LOCKPARENT is specified, the target may 27312115Sdyson * not be ".". When deleting and LOCKPARENT is specified, the target may 27412115Sdyson * be "."., but the caller must check to ensure it does an vrele and vput 27512115Sdyson * instead of two vputs. 27612115Sdyson * 277125843Sbde * Overall outline of ext2_lookup: 27812115Sdyson * 27912115Sdyson * search for name in directory, to found or notfound 28012115Sdyson * notfound: 28112115Sdyson * if creating, return locked directory, leaving info on available slots 28212115Sdyson * else return error 28312115Sdyson * found: 28412115Sdyson * if at end of path and deleting, return information to allow delete 28512115Sdyson * if at end of path and rewriting (RENAME and LOCKPARENT), lock target 28612115Sdyson * inode and return info to allow rewrite 28712115Sdyson * if not at end, add name to cache; if at end and neither creating 28812115Sdyson * nor deleting, add name to cache 28912115Sdyson */ 29012115Sdysonint 29112115Sdysonext2_lookup(ap) 29228787Sphk struct vop_cachedlookup_args /* { 29312115Sdyson struct vnode *a_dvp; 29412115Sdyson struct vnode **a_vpp; 29512115Sdyson struct componentname *a_cnp; 29612115Sdyson } */ *ap; 29712115Sdyson{ 29896752Siedowse struct vnode *vdp; /* vnode for directory being searched */ 29996752Siedowse struct inode *dp; /* inode for directory being searched */ 30012115Sdyson struct buf *bp; /* a buffer of directory entries */ 30196752Siedowse struct ext2_dir_entry_2 *ep; /* the current directory entry */ 30212115Sdyson int entryoffsetinblock; /* offset of ep in bp's buffer */ 30312115Sdyson enum {NONE, COMPACT, FOUND} slotstatus; 30412115Sdyson doff_t slotoffset; /* offset of area with free space */ 30512115Sdyson int slotsize; /* size of area at slotoffset */ 30612115Sdyson int slotfreespace; /* amount of space free in slot */ 30712115Sdyson int slotneeded; /* size of the entry we're seeking */ 30812115Sdyson int numdirpasses; /* strategy for directory search */ 30912115Sdyson doff_t endsearch; /* offset to end directory search */ 31012115Sdyson doff_t prevoff; /* prev entry dp->i_offset */ 31112115Sdyson struct vnode *pdp; /* saved dp during symlink work */ 31212115Sdyson struct vnode *tdp; /* returned by VFS_VGET */ 31312115Sdyson doff_t enduseful; /* pointer past last used dir slot */ 31412115Sdyson u_long bmask; /* block offset mask */ 31512115Sdyson int namlen, error; 31612115Sdyson struct vnode **vpp = ap->a_vpp; 31712115Sdyson struct componentname *cnp = ap->a_cnp; 31812115Sdyson struct ucred *cred = cnp->cn_cred; 31912115Sdyson int flags = cnp->cn_flags; 32012115Sdyson int nameiop = cnp->cn_nameiop; 32183366Sjulian struct thread *td = cnp->cn_thread; 32212115Sdyson 32312115Sdyson int DIRBLKSIZ = VTOI(ap->a_dvp)->i_e2fs->s_blocksize; 32412115Sdyson 32512115Sdyson bp = NULL; 32612115Sdyson slotoffset = -1; 32712115Sdyson *vpp = NULL; 32812115Sdyson vdp = ap->a_dvp; 32912115Sdyson dp = VTOI(vdp); 33012115Sdyson /* 33112115Sdyson * We now have a segment name to search for, and a directory to search. 33212115Sdyson */ 33312115Sdyson 33412115Sdyson /* 33512115Sdyson * Suppress search for slots unless creating 33612115Sdyson * file and at end of pathname, in which case 33712115Sdyson * we watch for a place to put the new file in 33812115Sdyson * case it doesn't already exist. 33912115Sdyson */ 34012115Sdyson slotstatus = FOUND; 34112115Sdyson slotfreespace = slotsize = slotneeded = 0; 34212115Sdyson if ((nameiop == CREATE || nameiop == RENAME) && 34312115Sdyson (flags & ISLASTCN)) { 34412115Sdyson slotstatus = NONE; 345111742Sdes slotneeded = EXT2_DIR_REC_LEN(cnp->cn_namelen); 34612115Sdyson /* was 34712115Sdyson slotneeded = (sizeof(struct direct) - MAXNAMLEN + 34812115Sdyson cnp->cn_namelen + 3) &~ 3; */ 34912115Sdyson } 35012115Sdyson 35112115Sdyson /* 35212115Sdyson * If there is cached information on a previous search of 35312115Sdyson * this directory, pick up where we last left off. 35412115Sdyson * We cache only lookups as these are the most common 35512115Sdyson * and have the greatest payoff. Caching CREATE has little 35612115Sdyson * benefit as it usually must search the entire directory 35712115Sdyson * to determine that the entry does not exist. Caching the 35812115Sdyson * location of the last DELETE or RENAME has not reduced 35912115Sdyson * profiling time and hence has been removed in the interest 36012115Sdyson * of simplicity. 36112115Sdyson */ 36296749Siedowse bmask = VFSTOEXT2(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1; 36312115Sdyson if (nameiop != LOOKUP || dp->i_diroff == 0 || 36412115Sdyson dp->i_diroff > dp->i_size) { 36512115Sdyson entryoffsetinblock = 0; 36612115Sdyson dp->i_offset = 0; 36712115Sdyson numdirpasses = 1; 36812115Sdyson } else { 36912115Sdyson dp->i_offset = dp->i_diroff; 37012115Sdyson if ((entryoffsetinblock = dp->i_offset & bmask) && 37196749Siedowse (error = ext2_blkatoff(vdp, (off_t)dp->i_offset, NULL, 37296749Siedowse &bp))) 37312115Sdyson return (error); 37412115Sdyson numdirpasses = 2; 37512115Sdyson nchstats.ncs_2passes++; 37612115Sdyson } 37712115Sdyson prevoff = dp->i_offset; 37812115Sdyson endsearch = roundup(dp->i_size, DIRBLKSIZ); 37912115Sdyson enduseful = 0; 38012115Sdyson 38112115Sdysonsearchloop: 38212115Sdyson while (dp->i_offset < endsearch) { 38312115Sdyson /* 38412115Sdyson * If necessary, get the next directory block. 38512115Sdyson */ 38612115Sdyson if ((dp->i_offset & bmask) == 0) { 38712115Sdyson if (bp != NULL) 38812115Sdyson brelse(bp); 38943301Sdillon if ((error = 39096749Siedowse ext2_blkatoff(vdp, (off_t)dp->i_offset, NULL, 39196749Siedowse &bp)) != 0) 39212115Sdyson return (error); 39312115Sdyson entryoffsetinblock = 0; 39412115Sdyson } 39512115Sdyson /* 39612115Sdyson * If still looking for a slot, and at a DIRBLKSIZE 39712115Sdyson * boundary, have to start looking for free space again. 39812115Sdyson */ 39912115Sdyson if (slotstatus == NONE && 40012115Sdyson (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) { 40112115Sdyson slotoffset = -1; 40212115Sdyson slotfreespace = 0; 40312115Sdyson } 40412115Sdyson /* 40512115Sdyson * Get pointer to next entry. 40612115Sdyson * Full validation checks are slow, so we only check 40712115Sdyson * enough to insure forward progress through the 40896753Siedowse * directory. Complete checks can be run by setting 40996753Siedowse * "vfs.e2fs.dirchk" to be true. 41012115Sdyson */ 41155477Sbde ep = (struct ext2_dir_entry_2 *) 41212115Sdyson ((char *)bp->b_data + entryoffsetinblock); 41312115Sdyson if (ep->rec_len == 0 || 41412147Sdyson (dirchk && ext2_dirbadentry(vdp, ep, entryoffsetinblock))) { 41512115Sdyson int i; 41696749Siedowse ext2_dirbad(dp, dp->i_offset, "mangled entry"); 41712115Sdyson i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)); 41812115Sdyson dp->i_offset += i; 41912115Sdyson entryoffsetinblock += i; 42012115Sdyson continue; 42112115Sdyson } 42212115Sdyson 42312115Sdyson /* 42412115Sdyson * If an appropriate sized slot has not yet been found, 42512115Sdyson * check to see if one is available. Also accumulate space 42612115Sdyson * in the current block so that we can determine if 42712115Sdyson * compaction is viable. 42812115Sdyson */ 42912115Sdyson if (slotstatus != FOUND) { 43012115Sdyson int size = ep->rec_len; 43112115Sdyson 43212115Sdyson if (ep->inode != 0) 43312115Sdyson size -= EXT2_DIR_REC_LEN(ep->name_len); 43412115Sdyson if (size > 0) { 43512115Sdyson if (size >= slotneeded) { 43612115Sdyson slotstatus = FOUND; 43712115Sdyson slotoffset = dp->i_offset; 43812115Sdyson slotsize = ep->rec_len; 43912115Sdyson } else if (slotstatus == NONE) { 44012115Sdyson slotfreespace += size; 44112115Sdyson if (slotoffset == -1) 44212115Sdyson slotoffset = dp->i_offset; 44312115Sdyson if (slotfreespace >= slotneeded) { 44412115Sdyson slotstatus = COMPACT; 44512115Sdyson slotsize = dp->i_offset + 44612115Sdyson ep->rec_len - slotoffset; 44712115Sdyson } 44812115Sdyson } 44912115Sdyson } 45012115Sdyson } 45112115Sdyson 45212115Sdyson /* 45312115Sdyson * Check for a name match. 45412115Sdyson */ 45512115Sdyson if (ep->inode) { 45612115Sdyson namlen = ep->name_len; 45712115Sdyson if (namlen == cnp->cn_namelen && 45812115Sdyson !bcmp(cnp->cn_nameptr, ep->name, 45912115Sdyson (unsigned)namlen)) { 46012115Sdyson /* 46112115Sdyson * Save directory entry's inode number and 46212115Sdyson * reclen in ndp->ni_ufs area, and release 46312115Sdyson * directory buffer. 46412115Sdyson */ 46512115Sdyson dp->i_ino = ep->inode; 46612115Sdyson dp->i_reclen = ep->rec_len; 46712115Sdyson goto found; 46812115Sdyson } 46912115Sdyson } 47012115Sdyson prevoff = dp->i_offset; 47112115Sdyson dp->i_offset += ep->rec_len; 47212115Sdyson entryoffsetinblock += ep->rec_len; 47312115Sdyson if (ep->inode) 47412115Sdyson enduseful = dp->i_offset; 47512115Sdyson } 47612115Sdyson/* notfound: */ 47712115Sdyson /* 47812115Sdyson * If we started in the middle of the directory and failed 47912115Sdyson * to find our target, we must check the beginning as well. 48012115Sdyson */ 48112115Sdyson if (numdirpasses == 2) { 48212115Sdyson numdirpasses--; 48312115Sdyson dp->i_offset = 0; 48412115Sdyson endsearch = dp->i_diroff; 48512115Sdyson goto searchloop; 48612115Sdyson } 48712115Sdyson if (bp != NULL) 48812115Sdyson brelse(bp); 48912115Sdyson /* 49012115Sdyson * If creating, and at end of pathname and current 49112115Sdyson * directory has not been removed, then can consider 49212115Sdyson * allowing file to be created. 49312115Sdyson */ 49412115Sdyson if ((nameiop == CREATE || nameiop == RENAME) && 49512115Sdyson (flags & ISLASTCN) && dp->i_nlink != 0) { 49612115Sdyson /* 49712115Sdyson * Access for write is interpreted as allowing 49812115Sdyson * creation of files in the directory. 49912115Sdyson */ 50083366Sjulian if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0) 50112115Sdyson return (error); 50212115Sdyson /* 50312115Sdyson * Return an indication of where the new directory 50412115Sdyson * entry should be put. If we didn't find a slot, 50512115Sdyson * then set dp->i_count to 0 indicating 50612115Sdyson * that the new slot belongs at the end of the 50712115Sdyson * directory. If we found a slot, then the new entry 50812115Sdyson * can be put in the range from dp->i_offset to 50912115Sdyson * dp->i_offset + dp->i_count. 51012115Sdyson */ 51112115Sdyson if (slotstatus == NONE) { 51212115Sdyson dp->i_offset = roundup(dp->i_size, DIRBLKSIZ); 51312115Sdyson dp->i_count = 0; 51412115Sdyson enduseful = dp->i_offset; 51512115Sdyson } else { 51612115Sdyson dp->i_offset = slotoffset; 51712115Sdyson dp->i_count = slotsize; 51812115Sdyson if (enduseful < slotoffset + slotsize) 51912115Sdyson enduseful = slotoffset + slotsize; 52012115Sdyson } 52112115Sdyson dp->i_endoff = roundup(enduseful, DIRBLKSIZ); 52212115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 52312115Sdyson /* 52412115Sdyson * We return with the directory locked, so that 52512115Sdyson * the parameters we set up above will still be 52612115Sdyson * valid if we actually decide to do a direnter(). 52712115Sdyson * We return ni_vp == NULL to indicate that the entry 52812115Sdyson * does not currently exist; we leave a pointer to 52912115Sdyson * the (locked) directory inode in ndp->ni_dvp. 53012115Sdyson * The pathname buffer is saved so that the name 53112115Sdyson * can be obtained later. 53212115Sdyson * 53312115Sdyson * NB - if the directory is unlocked, then this 53412115Sdyson * information cannot be used. 53512115Sdyson */ 53612115Sdyson cnp->cn_flags |= SAVENAME; 53712115Sdyson return (EJUSTRETURN); 53812115Sdyson } 53912115Sdyson /* 54012115Sdyson * Insert name into cache (as non-existent) if appropriate. 54112115Sdyson */ 54212115Sdyson if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) 54312115Sdyson cache_enter(vdp, *vpp, cnp); 54412115Sdyson return (ENOENT); 54512115Sdyson 54612115Sdysonfound: 54712115Sdyson if (numdirpasses == 2) 54812115Sdyson nchstats.ncs_pass2++; 54912115Sdyson /* 55012115Sdyson * Check that directory length properly reflects presence 55112115Sdyson * of this entry. 55212115Sdyson */ 55312115Sdyson if (entryoffsetinblock + EXT2_DIR_REC_LEN(ep->name_len) 55412115Sdyson > dp->i_size) { 55596749Siedowse ext2_dirbad(dp, dp->i_offset, "i_size too small"); 55612115Sdyson dp->i_size = entryoffsetinblock+EXT2_DIR_REC_LEN(ep->name_len); 55712115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 55812115Sdyson } 559105420Sbde brelse(bp); 56012115Sdyson 56112115Sdyson /* 56212115Sdyson * Found component in pathname. 56312115Sdyson * If the final component of path name, save information 56412115Sdyson * in the cache as to where the entry was found. 56512115Sdyson */ 56612115Sdyson if ((flags & ISLASTCN) && nameiop == LOOKUP) 56712115Sdyson dp->i_diroff = dp->i_offset &~ (DIRBLKSIZ - 1); 56812115Sdyson 56912115Sdyson /* 57012115Sdyson * If deleting, and at end of pathname, return 57112115Sdyson * parameters which can be used to remove file. 57212115Sdyson */ 57312115Sdyson if (nameiop == DELETE && (flags & ISLASTCN)) { 57412115Sdyson /* 57512115Sdyson * Write access to directory required to delete files. 57612115Sdyson */ 57783366Sjulian if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0) 57812115Sdyson return (error); 57912115Sdyson /* 58012115Sdyson * Return pointer to current entry in dp->i_offset, 58112115Sdyson * and distance past previous entry (if there 58212115Sdyson * is a previous entry in this block) in dp->i_count. 58312115Sdyson * Save directory inode pointer in ndp->ni_dvp for dirremove(). 58412115Sdyson */ 58512115Sdyson if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0) 58612115Sdyson dp->i_count = 0; 58712115Sdyson else 58812115Sdyson dp->i_count = dp->i_offset - prevoff; 58912115Sdyson if (dp->i_number == dp->i_ino) { 59012115Sdyson VREF(vdp); 59112115Sdyson *vpp = vdp; 59212115Sdyson return (0); 59312115Sdyson } 59492462Smckusick if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, LK_EXCLUSIVE, 59592462Smckusick &tdp)) != 0) 59612115Sdyson return (error); 59712115Sdyson /* 59812115Sdyson * If directory is "sticky", then user must own 59912115Sdyson * the directory, or the file in it, else she 60012115Sdyson * may not delete it (unless she's root). This 60112115Sdyson * implements append-only directories. 60212115Sdyson */ 60312115Sdyson if ((dp->i_mode & ISVTX) && 60412115Sdyson cred->cr_uid != 0 && 60512115Sdyson cred->cr_uid != dp->i_uid && 60612115Sdyson VTOI(tdp)->i_uid != cred->cr_uid) { 60712115Sdyson vput(tdp); 60812115Sdyson return (EPERM); 60912115Sdyson } 61012115Sdyson *vpp = tdp; 61112115Sdyson return (0); 61212115Sdyson } 61312115Sdyson 61412115Sdyson /* 61512115Sdyson * If rewriting (RENAME), return the inode and the 61612115Sdyson * information required to rewrite the present directory 61712115Sdyson * Must get inode of directory entry to verify it's a 61812115Sdyson * regular file, or empty directory. 61912115Sdyson */ 620144299Sjeff if (nameiop == RENAME && (flags & ISLASTCN)) { 62183366Sjulian if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0) 62212115Sdyson return (error); 62312115Sdyson /* 62412115Sdyson * Careful about locking second inode. 62512115Sdyson * This can only occur if the target is ".". 62612115Sdyson */ 62712115Sdyson if (dp->i_number == dp->i_ino) 62812115Sdyson return (EISDIR); 62992462Smckusick if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, LK_EXCLUSIVE, 63092462Smckusick &tdp)) != 0) 63112115Sdyson return (error); 63212115Sdyson *vpp = tdp; 63312115Sdyson cnp->cn_flags |= SAVENAME; 63412115Sdyson return (0); 63512115Sdyson } 63612115Sdyson 63712115Sdyson /* 63812115Sdyson * Step through the translation in the name. We do not `vput' the 63912115Sdyson * directory because we may need it again if a symbolic link 64012115Sdyson * is relative to the current directory. Instead we save it 64112115Sdyson * unlocked as "pdp". We must get the target inode before unlocking 64212115Sdyson * the directory to insure that the inode will not be removed 64312115Sdyson * before we get it. We prevent deadlock by always fetching 64412115Sdyson * inodes from the root, moving down the directory tree. Thus 64512115Sdyson * when following backward pointers ".." we must unlock the 64612115Sdyson * parent directory before getting the requested directory. 64712115Sdyson * There is a potential race condition here if both the current 64812115Sdyson * and parent directories are removed before the VFS_VGET for the 64912115Sdyson * inode associated with ".." returns. We hope that this occurs 65012115Sdyson * infrequently since we cannot avoid this race condition without 65112115Sdyson * implementing a sophisticated deadlock detection algorithm. 65212115Sdyson * Note also that this simple deadlock detection scheme will not 65312115Sdyson * work if the file system has any hard links other than ".." 65412115Sdyson * that point backwards in the directory structure. 65512115Sdyson */ 65612115Sdyson pdp = vdp; 65712115Sdyson if (flags & ISDOTDOT) { 65883366Sjulian VOP_UNLOCK(pdp, 0, td); /* race to get the inode */ 659145006Sjeff error = VFS_VGET(vdp->v_mount, dp->i_ino, LK_EXCLUSIVE, &tdp); 660145006Sjeff vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, td); 661145006Sjeff if (error != 0) 66212115Sdyson return (error); 66312115Sdyson *vpp = tdp; 66412115Sdyson } else if (dp->i_number == dp->i_ino) { 66512115Sdyson VREF(vdp); /* we want ourself, ie "." */ 66612115Sdyson *vpp = vdp; 66712115Sdyson } else { 66892462Smckusick if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, LK_EXCLUSIVE, 66992462Smckusick &tdp)) != 0) 67012115Sdyson return (error); 67112115Sdyson *vpp = tdp; 67212115Sdyson } 67312115Sdyson 67412115Sdyson /* 67512115Sdyson * Insert name into cache if appropriate. 67612115Sdyson */ 67712115Sdyson if (cnp->cn_flags & MAKEENTRY) 67812115Sdyson cache_enter(vdp, *vpp, cnp); 67912115Sdyson return (0); 68012115Sdyson} 68112115Sdyson 68296749Siedowsevoid 68396749Siedowseext2_dirbad(ip, offset, how) 68496749Siedowse struct inode *ip; 68596749Siedowse doff_t offset; 68696749Siedowse char *how; 68796749Siedowse{ 68896749Siedowse struct mount *mp; 68996749Siedowse 69096749Siedowse mp = ITOV(ip)->v_mount; 69196749Siedowse (void)printf("%s: bad dir ino %lu at offset %ld: %s\n", 69296749Siedowse mp->mnt_stat.f_mntonname, (u_long)ip->i_number, (long)offset, how); 69396749Siedowse if ((mp->mnt_flag & MNT_RDONLY) == 0) 69496749Siedowse panic("ext2_dirbad: bad dir"); 69596749Siedowse} 69696749Siedowse 69712115Sdyson/* 69812115Sdyson * Do consistency checking on a directory entry: 69912115Sdyson * record length must be multiple of 4 70012115Sdyson * entry must fit in rest of its DIRBLKSIZ block 70112115Sdyson * record must be large enough to contain entry 70212115Sdyson * name is not longer than MAXNAMLEN 70312115Sdyson * name must be as long as advertised, and null terminated 70412115Sdyson */ 70512115Sdyson/* 70612115Sdyson * changed so that it confirms to ext2_check_dir_entry 70712115Sdyson */ 70812159Sbdestatic int 70912115Sdysonext2_dirbadentry(dp, de, entryoffsetinblock) 71012115Sdyson struct vnode *dp; 71196752Siedowse struct ext2_dir_entry_2 *de; 71212115Sdyson int entryoffsetinblock; 71312115Sdyson{ 71412115Sdyson int DIRBLKSIZ = VTOI(dp)->i_e2fs->s_blocksize; 71512115Sdyson 716111742Sdes char * error_msg = NULL; 71712115Sdyson 718111742Sdes if (de->rec_len < EXT2_DIR_REC_LEN(1)) 719111742Sdes error_msg = "rec_len is smaller than minimal"; 720111742Sdes else if (de->rec_len % 4 != 0) 721111742Sdes error_msg = "rec_len % 4 != 0"; 722111742Sdes else if (de->rec_len < EXT2_DIR_REC_LEN(de->name_len)) 723111742Sdes error_msg = "reclen is too small for name_len"; 724111742Sdes else if (entryoffsetinblock + de->rec_len > DIRBLKSIZ) 725111742Sdes error_msg = "directory entry across blocks"; 726111742Sdes /* else LATER 72712115Sdyson if (de->inode > dir->i_sb->u.ext2_sb.s_es->s_inodes_count) 728111742Sdes error_msg = "inode out of bounds"; 72912115Sdyson */ 73012115Sdyson 731111742Sdes if (error_msg != NULL) { 732111742Sdes printf("bad directory entry: %s\n", error_msg); 733111742Sdes printf("offset=%d, inode=%lu, rec_len=%u, name_len=%u\n", 73424492Sbde entryoffsetinblock, (unsigned long)de->inode, 73524492Sbde de->rec_len, de->name_len); 736111742Sdes } 737111742Sdes return error_msg == NULL ? 0 : 1; 73812115Sdyson} 73912115Sdyson 74012115Sdyson/* 74112115Sdyson * Write a directory entry after a call to namei, using the parameters 74212115Sdyson * that it left in nameidata. The argument ip is the inode which the new 74312115Sdyson * directory entry will refer to. Dvp is a pointer to the directory to 74412115Sdyson * be written, which was left locked by namei. Remaining parameters 74512115Sdyson * (dp->i_offset, dp->i_count) indicate how the space for the new 74612115Sdyson * entry is to be obtained. 74712115Sdyson */ 74812115Sdysonint 74912115Sdysonext2_direnter(ip, dvp, cnp) 75012115Sdyson struct inode *ip; 75112115Sdyson struct vnode *dvp; 75296752Siedowse struct componentname *cnp; 75312115Sdyson{ 75496752Siedowse struct ext2_dir_entry_2 *ep, *nep; 75596752Siedowse struct inode *dp; 75612115Sdyson struct buf *bp; 75755477Sbde struct ext2_dir_entry_2 newdir; 75812115Sdyson struct iovec aiov; 75912115Sdyson struct uio auio; 76012115Sdyson u_int dsize; 76112115Sdyson int error, loc, newentrysize, spacefree; 76212115Sdyson char *dirbuf; 76312115Sdyson int DIRBLKSIZ = ip->i_e2fs->s_blocksize; 76412115Sdyson 76512115Sdyson 76612115Sdyson#if DIAGNOSTIC 76712115Sdyson if ((cnp->cn_flags & SAVENAME) == 0) 76812115Sdyson panic("direnter: missing name"); 76912115Sdyson#endif 77012115Sdyson dp = VTOI(dvp); 77112115Sdyson newdir.inode = ip->i_number; 77212115Sdyson newdir.name_len = cnp->cn_namelen; 77355477Sbde if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs->s_es, 77455477Sbde EXT2_FEATURE_INCOMPAT_FILETYPE)) 77555477Sbde newdir.file_type = DTTOFT(IFTODT(ip->i_mode)); 77655477Sbde else 77755477Sbde newdir.file_type = EXT2_FT_UNKNOWN; 77812115Sdyson bcopy(cnp->cn_nameptr, newdir.name, (unsigned)cnp->cn_namelen + 1); 77912115Sdyson newentrysize = EXT2_DIR_REC_LEN(newdir.name_len); 78012115Sdyson if (dp->i_count == 0) { 78112115Sdyson /* 78212115Sdyson * If dp->i_count is 0, then namei could find no 78312115Sdyson * space in the directory. Here, dp->i_offset will 78412115Sdyson * be on a directory block boundary and we will write the 78512115Sdyson * new entry into a fresh block. 78612115Sdyson */ 78712115Sdyson if (dp->i_offset & (DIRBLKSIZ - 1)) 78812115Sdyson panic("ext2_direnter: newblk"); 78912115Sdyson auio.uio_offset = dp->i_offset; 79012115Sdyson newdir.rec_len = DIRBLKSIZ; 79112115Sdyson auio.uio_resid = newentrysize; 79212115Sdyson aiov.iov_len = newentrysize; 79312115Sdyson aiov.iov_base = (caddr_t)&newdir; 79412115Sdyson auio.uio_iov = &aiov; 79512115Sdyson auio.uio_iovcnt = 1; 79612115Sdyson auio.uio_rw = UIO_WRITE; 79712115Sdyson auio.uio_segflg = UIO_SYSSPACE; 79883366Sjulian auio.uio_td = (struct thread *)0; 79912115Sdyson error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred); 80012115Sdyson if (DIRBLKSIZ > 80196749Siedowse VFSTOEXT2(dvp->v_mount)->um_mountp->mnt_stat.f_bsize) 80212115Sdyson /* XXX should grow with balloc() */ 80312115Sdyson panic("ext2_direnter: frag size"); 80412115Sdyson else if (!error) { 80512115Sdyson dp->i_size = roundup(dp->i_size, DIRBLKSIZ); 80612115Sdyson dp->i_flag |= IN_CHANGE; 80712115Sdyson } 80812115Sdyson return (error); 80912115Sdyson } 81012115Sdyson 81112115Sdyson /* 81212115Sdyson * If dp->i_count is non-zero, then namei found space 81312115Sdyson * for the new entry in the range dp->i_offset to 81412115Sdyson * dp->i_offset + dp->i_count in the directory. 81512115Sdyson * To use this space, we may have to compact the entries located 81612115Sdyson * there, by copying them together towards the beginning of the 81712115Sdyson * block, leaving the free space in one usable chunk at the end. 81812115Sdyson */ 81912115Sdyson 82012115Sdyson /* 82112115Sdyson * Increase size of directory if entry eats into new space. 82212115Sdyson * This should never push the size past a new multiple of 82312115Sdyson * DIRBLKSIZE. 82412115Sdyson * 82512115Sdyson * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN. 82612115Sdyson */ 82712115Sdyson if (dp->i_offset + dp->i_count > dp->i_size) 82812115Sdyson dp->i_size = dp->i_offset + dp->i_count; 82912115Sdyson /* 83012115Sdyson * Get the block containing the space for the new directory entry. 83112115Sdyson */ 83296749Siedowse if ((error = ext2_blkatoff(dvp, (off_t)dp->i_offset, &dirbuf, 83396749Siedowse &bp)) != 0) 83412115Sdyson return (error); 83512115Sdyson /* 83612115Sdyson * Find space for the new entry. In the simple case, the entry at 83712115Sdyson * offset base will have the space. If it does not, then namei 83812115Sdyson * arranged that compacting the region dp->i_offset to 83912115Sdyson * dp->i_offset + dp->i_count would yield the 84012115Sdyson * space. 84112115Sdyson */ 84255477Sbde ep = (struct ext2_dir_entry_2 *)dirbuf; 84312115Sdyson dsize = EXT2_DIR_REC_LEN(ep->name_len); 84412115Sdyson spacefree = ep->rec_len - dsize; 84512115Sdyson for (loc = ep->rec_len; loc < dp->i_count; ) { 84655477Sbde nep = (struct ext2_dir_entry_2 *)(dirbuf + loc); 84712115Sdyson if (ep->inode) { 84812115Sdyson /* trim the existing slot */ 84912115Sdyson ep->rec_len = dsize; 85055477Sbde ep = (struct ext2_dir_entry_2 *)((char *)ep + dsize); 85112115Sdyson } else { 85212115Sdyson /* overwrite; nothing there; header is ours */ 85312115Sdyson spacefree += dsize; 85412115Sdyson } 85519541Sbde dsize = EXT2_DIR_REC_LEN(nep->name_len); 85612115Sdyson spacefree += nep->rec_len - dsize; 85712115Sdyson loc += nep->rec_len; 85812115Sdyson bcopy((caddr_t)nep, (caddr_t)ep, dsize); 85912115Sdyson } 86012115Sdyson /* 86112115Sdyson * Update the pointer fields in the previous entry (if any), 86212115Sdyson * copy in the new entry, and write out the block. 86312115Sdyson */ 86412115Sdyson if (ep->inode == 0) { 86512115Sdyson if (spacefree + dsize < newentrysize) 86612115Sdyson panic("ext2_direnter: compact1"); 86712115Sdyson newdir.rec_len = spacefree + dsize; 86812115Sdyson } else { 86912115Sdyson if (spacefree < newentrysize) 87012115Sdyson panic("ext2_direnter: compact2"); 87112115Sdyson newdir.rec_len = spacefree; 87212115Sdyson ep->rec_len = dsize; 87355477Sbde ep = (struct ext2_dir_entry_2 *)((char *)ep + dsize); 87412115Sdyson } 87512115Sdyson bcopy((caddr_t)&newdir, (caddr_t)ep, (u_int)newentrysize); 876126853Sphk error = bwrite(bp); 87712115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 87812115Sdyson if (!error && dp->i_endoff && dp->i_endoff < dp->i_size) 87996749Siedowse error = ext2_truncate(dvp, (off_t)dp->i_endoff, IO_SYNC, 88083366Sjulian cnp->cn_cred, cnp->cn_thread); 88112115Sdyson return (error); 88212115Sdyson} 88312115Sdyson 88412115Sdyson/* 88512115Sdyson * Remove a directory entry after a call to namei, using 88612115Sdyson * the parameters which it left in nameidata. The entry 88712115Sdyson * dp->i_offset contains the offset into the directory of the 88812115Sdyson * entry to be eliminated. The dp->i_count field contains the 88912115Sdyson * size of the previous record in the directory. If this 89012115Sdyson * is 0, the first entry is being deleted, so we need only 89112115Sdyson * zero the inode number to mark the entry as free. If the 89212115Sdyson * entry is not the first in the directory, we must reclaim 89312115Sdyson * the space of the now empty record by adding the record size 89412115Sdyson * to the size of the previous entry. 89512115Sdyson */ 89612115Sdysonint 89712115Sdysonext2_dirremove(dvp, cnp) 89812115Sdyson struct vnode *dvp; 89912115Sdyson struct componentname *cnp; 90012115Sdyson{ 90196752Siedowse struct inode *dp; 90255477Sbde struct ext2_dir_entry_2 *ep; 90312115Sdyson struct buf *bp; 90412115Sdyson int error; 905111742Sdes 90612115Sdyson dp = VTOI(dvp); 90712115Sdyson if (dp->i_count == 0) { 90812115Sdyson /* 90912115Sdyson * First entry in block: set d_ino to zero. 91012115Sdyson */ 91143301Sdillon if ((error = 91296749Siedowse ext2_blkatoff(dvp, (off_t)dp->i_offset, (char **)&ep, 91396749Siedowse &bp)) != 0) 91412115Sdyson return (error); 91512115Sdyson ep->inode = 0; 916126853Sphk error = bwrite(bp); 91712115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 91812115Sdyson return (error); 91912115Sdyson } 92012115Sdyson /* 92112115Sdyson * Collapse new free space into previous entry. 92212115Sdyson */ 92396749Siedowse if ((error = ext2_blkatoff(dvp, (off_t)(dp->i_offset - dp->i_count), 92443301Sdillon (char **)&ep, &bp)) != 0) 92512115Sdyson return (error); 92612115Sdyson ep->rec_len += dp->i_reclen; 927126853Sphk error = bwrite(bp); 92812115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 92912115Sdyson return (error); 93012115Sdyson} 93112115Sdyson 93212115Sdyson/* 93312115Sdyson * Rewrite an existing directory entry to point at the inode 93412115Sdyson * supplied. The parameters describing the directory entry are 93512115Sdyson * set up by a call to namei. 93612115Sdyson */ 93712115Sdysonint 93812115Sdysonext2_dirrewrite(dp, ip, cnp) 93912115Sdyson struct inode *dp, *ip; 94012115Sdyson struct componentname *cnp; 94112115Sdyson{ 94212115Sdyson struct buf *bp; 94355477Sbde struct ext2_dir_entry_2 *ep; 94412115Sdyson struct vnode *vdp = ITOV(dp); 94512115Sdyson int error; 94612115Sdyson 94796749Siedowse if ((error = ext2_blkatoff(vdp, (off_t)dp->i_offset, (char **)&ep, 94896749Siedowse &bp)) != 0) 94912115Sdyson return (error); 95012115Sdyson ep->inode = ip->i_number; 95155477Sbde if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs->s_es, 95255477Sbde EXT2_FEATURE_INCOMPAT_FILETYPE)) 95355477Sbde ep->file_type = DTTOFT(IFTODT(ip->i_mode)); 95455477Sbde else 95555477Sbde ep->file_type = EXT2_FT_UNKNOWN; 956126853Sphk error = bwrite(bp); 95712115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 95812115Sdyson return (error); 95912115Sdyson} 96012115Sdyson 96112115Sdyson/* 96212115Sdyson * Check if a directory is empty or not. 96312115Sdyson * Inode supplied must be locked. 96412115Sdyson * 96512115Sdyson * Using a struct dirtemplate here is not precisely 96612115Sdyson * what we want, but better than using a struct direct. 96712115Sdyson * 96812115Sdyson * NB: does not handle corrupted directories. 96912115Sdyson */ 97012115Sdysonint 97112115Sdysonext2_dirempty(ip, parentino, cred) 97296752Siedowse struct inode *ip; 97312115Sdyson ino_t parentino; 97412115Sdyson struct ucred *cred; 97512115Sdyson{ 97696752Siedowse off_t off; 97712115Sdyson struct dirtemplate dbuf; 97896752Siedowse struct ext2_dir_entry_2 *dp = (struct ext2_dir_entry_2 *)&dbuf; 97912115Sdyson int error, count, namlen; 98012115Sdyson#define MINDIRSIZ (sizeof (struct dirtemplate) / 2) 98112115Sdyson 98212115Sdyson for (off = 0; off < ip->i_size; off += dp->rec_len) { 983101744Srwatson error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, 984101744Srwatson off, UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK, cred, 985101941Srwatson NOCRED, &count, (struct thread *)0); 98612115Sdyson /* 98712115Sdyson * Since we read MINDIRSIZ, residual must 98812115Sdyson * be 0 unless we're at end of file. 98912115Sdyson */ 99012115Sdyson if (error || count != 0) 99112115Sdyson return (0); 99212115Sdyson /* avoid infinite loops */ 99312115Sdyson if (dp->rec_len == 0) 99412115Sdyson return (0); 99512115Sdyson /* skip empty entries */ 99612115Sdyson if (dp->inode == 0) 99712115Sdyson continue; 99812115Sdyson /* accept only "." and ".." */ 99912115Sdyson namlen = dp->name_len; 100012115Sdyson if (namlen > 2) 100112115Sdyson return (0); 100212115Sdyson if (dp->name[0] != '.') 100312115Sdyson return (0); 100412115Sdyson /* 100512115Sdyson * At this point namlen must be 1 or 2. 100612115Sdyson * 1 implies ".", 2 implies ".." if second 100712115Sdyson * char is also "." 100812115Sdyson */ 100912115Sdyson if (namlen == 1) 101012115Sdyson continue; 101112115Sdyson if (dp->name[1] == '.' && dp->inode == parentino) 101212115Sdyson continue; 101312115Sdyson return (0); 101412115Sdyson } 101512115Sdyson return (1); 101612115Sdyson} 101712115Sdyson 101812115Sdyson/* 101912115Sdyson * Check if source directory is in the path of the target directory. 102012115Sdyson * Target is supplied locked, source is unlocked. 102112115Sdyson * The target is always vput before returning. 102212115Sdyson */ 102312115Sdysonint 102412115Sdysonext2_checkpath(source, target, cred) 102512115Sdyson struct inode *source, *target; 102612115Sdyson struct ucred *cred; 102712115Sdyson{ 102812115Sdyson struct vnode *vp; 102912115Sdyson int error, rootino, namlen; 103012115Sdyson struct dirtemplate dirbuf; 103112115Sdyson 103212115Sdyson vp = ITOV(target); 103312115Sdyson if (target->i_number == source->i_number) { 103412115Sdyson error = EEXIST; 103512115Sdyson goto out; 103612115Sdyson } 103712115Sdyson rootino = ROOTINO; 103812115Sdyson error = 0; 103912115Sdyson if (target->i_number == rootino) 104012115Sdyson goto out; 104112115Sdyson 104212115Sdyson for (;;) { 104312115Sdyson if (vp->v_type != VDIR) { 104412115Sdyson error = ENOTDIR; 104512115Sdyson break; 104612115Sdyson } 104712115Sdyson error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf, 104812115Sdyson sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE, 1049101941Srwatson IO_NODELOCKED | IO_NOMACCHECK, cred, NOCRED, (int *)0, 1050101744Srwatson (struct thread *)0); 105112115Sdyson if (error != 0) 105212115Sdyson break; 105357710Sbde namlen = dirbuf.dotdot_type; /* like ufs little-endian */ 105412115Sdyson if (namlen != 2 || 105512115Sdyson dirbuf.dotdot_name[0] != '.' || 105612115Sdyson dirbuf.dotdot_name[1] != '.') { 105712115Sdyson error = ENOTDIR; 105812115Sdyson break; 105912115Sdyson } 106012115Sdyson if (dirbuf.dotdot_ino == source->i_number) { 106112115Sdyson error = EINVAL; 106212115Sdyson break; 106312115Sdyson } 106412115Sdyson if (dirbuf.dotdot_ino == rootino) 106512115Sdyson break; 106612115Sdyson vput(vp); 106792462Smckusick if ((error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino, 106892462Smckusick LK_EXCLUSIVE, &vp)) != 0) { 106912115Sdyson vp = NULL; 107012115Sdyson break; 107112115Sdyson } 107212115Sdyson } 107312115Sdyson 107412115Sdysonout: 107512115Sdyson if (error == ENOTDIR) 107612115Sdyson printf("checkpath: .. not a directory\n"); 107712115Sdyson if (vp != NULL) 107812115Sdyson vput(vp); 107912115Sdyson return (error); 108012115Sdyson} 1081