ext2_lookup.c revision 57710
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 655477Sbde * 755477Sbde * $FreeBSD: head/sys/gnu/fs/ext2fs/ext2_lookup.c 57710 2000-03-03 08:00:27Z bde $ 812115Sdyson */ 912115Sdyson/* 1012115Sdyson * Copyright (c) 1989, 1993 1112115Sdyson * The Regents of the University of California. All rights reserved. 1212115Sdyson * (c) UNIX System Laboratories, Inc. 1312115Sdyson * All or some portions of this file are derived from material licensed 1412115Sdyson * to the University of California by American Telephone and Telegraph 1512115Sdyson * Co. or Unix System Laboratories, Inc. and are reproduced herein with 1612115Sdyson * the permission of UNIX System Laboratories, Inc. 1712115Sdyson * 1812115Sdyson * Redistribution and use in source and binary forms, with or without 1912115Sdyson * modification, are permitted provided that the following conditions 2012115Sdyson * are met: 2112115Sdyson * 1. Redistributions of source code must retain the above copyright 2212115Sdyson * notice, this list of conditions and the following disclaimer. 2312115Sdyson * 2. Redistributions in binary form must reproduce the above copyright 2412115Sdyson * notice, this list of conditions and the following disclaimer in the 2512115Sdyson * documentation and/or other materials provided with the distribution. 2612115Sdyson * 3. All advertising materials mentioning features or use of this software 2712115Sdyson * must display the following acknowledgement: 2812115Sdyson * This product includes software developed by the University of 2912115Sdyson * California, Berkeley and its contributors. 3012115Sdyson * 4. Neither the name of the University nor the names of its contributors 3112115Sdyson * may be used to endorse or promote products derived from this software 3212115Sdyson * without specific prior written permission. 3312115Sdyson * 3412115Sdyson * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 3512115Sdyson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 3612115Sdyson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 3712115Sdyson * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 3812115Sdyson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3912115Sdyson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 4012115Sdyson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 4112115Sdyson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 4212115Sdyson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 4312115Sdyson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 4412115Sdyson * SUCH DAMAGE. 4512115Sdyson * 4612115Sdyson * @(#)ufs_lookup.c 8.6 (Berkeley) 4/1/94 4712115Sdyson */ 4812115Sdyson 4955477Sbde#include <stddef.h> 5055477Sbde 5112115Sdyson#include <sys/param.h> 5212159Sbde#include <sys/systm.h> 5312115Sdyson#include <sys/namei.h> 5412115Sdyson#include <sys/buf.h> 5512115Sdyson#include <sys/mount.h> 5612115Sdyson#include <sys/vnode.h> 5712115Sdyson#include <sys/malloc.h> 5812115Sdyson#include <sys/dirent.h> 5912115Sdyson 6012115Sdyson#include <ufs/ufs/quota.h> 6112115Sdyson#include <ufs/ufs/inode.h> 6212115Sdyson#include <ufs/ufs/dir.h> 6312115Sdyson#include <ufs/ufs/ufsmount.h> 6412147Sdyson#include <ufs/ufs/ufs_extern.h> 6512115Sdyson 6612115Sdyson#include <gnu/ext2fs/ext2_extern.h> 6712115Sdyson#include <gnu/ext2fs/ext2_fs.h> 6812115Sdyson#include <gnu/ext2fs/ext2_fs_sb.h> 6912115Sdyson 7012115Sdyson/* 7112115Sdyson DIRBLKSIZE in ffs is DEV_BSIZE (in most cases 512) 7212115Sdyson while it is the native blocksize in ext2fs - thus, a #define 7312115Sdyson is no longer appropriate 7412115Sdyson*/ 7512115Sdyson#undef DIRBLKSIZ 7612115Sdyson 7712147Sdysonextern int dirchk; 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 11412159Sbdestatic int ext2_dirbadentry __P((struct vnode *dp, 11555477Sbde struct ext2_dir_entry_2 *de, 11612159Sbde int entryoffsetinblock)); 11712159Sbde 11812115Sdyson/* 11912115Sdyson * Vnode op for reading directories. 12012115Sdyson * 12112115Sdyson * The routine below assumes that the on-disk format of a directory 12212115Sdyson * is the same as that defined by <sys/dirent.h>. If the on-disk 12312115Sdyson * format changes, then it will be necessary to do a conversion 12412115Sdyson * from the on-disk format that read returns to the format defined 12512115Sdyson * by <sys/dirent.h>. 12612115Sdyson */ 12712115Sdyson/* 12812115Sdyson * this is exactly what we do here - the problem is that the conversion 12912115Sdyson * will blow up some entries by four bytes, so it can't be done in place. 13012115Sdyson * This is too bad. Right now the conversion is done entry by entry, the 13112115Sdyson * converted entry is sent via uiomove. 13212115Sdyson * 13312115Sdyson * XXX allocate a buffer, convert as many entries as possible, then send 13412115Sdyson * the whole buffer to uiomove 13512115Sdyson */ 13612115Sdysonint 13712115Sdysonext2_readdir(ap) 13812115Sdyson struct vop_readdir_args /* { 13912115Sdyson struct vnode *a_vp; 14012115Sdyson struct uio *a_uio; 14112115Sdyson struct ucred *a_cred; 14212115Sdyson } */ *ap; 14312115Sdyson{ 14412115Sdyson register struct uio *uio = ap->a_uio; 14512147Sdyson int count, error; 14612115Sdyson 14755477Sbde struct ext2_dir_entry_2 *edp, *dp; 14824649Sdfr int ncookies; 14912115Sdyson struct dirent dstdp; 15012115Sdyson struct uio auio; 15112115Sdyson struct iovec aiov; 15212115Sdyson caddr_t dirbuf; 15312115Sdyson int readcnt; 15412115Sdyson u_quad_t startoffset = uio->uio_offset; 15512115Sdyson 15612115Sdyson count = uio->uio_resid; /* legyenek boldogok akik akarnak ... */ 15712115Sdyson uio->uio_resid = count; 15812115Sdyson uio->uio_iov->iov_len = count; 15912115Sdyson 16012115Sdyson#if 0 16112115Sdysonprintf("ext2_readdir called uio->uio_offset %d uio->uio_resid %d count %d \n", 16212115Sdyson (int)uio->uio_offset, (int)uio->uio_resid, (int)count); 16312115Sdyson#endif 16412115Sdyson 16512115Sdyson auio = *uio; 16612115Sdyson auio.uio_iov = &aiov; 16712115Sdyson auio.uio_iovcnt = 1; 16812115Sdyson auio.uio_segflg = UIO_SYSSPACE; 16912115Sdyson aiov.iov_len = count; 17012115Sdyson MALLOC(dirbuf, caddr_t, count, M_TEMP, M_WAITOK); 17112115Sdyson aiov.iov_base = dirbuf; 17212115Sdyson error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred); 17312115Sdyson if (error == 0) { 17412115Sdyson readcnt = count - auio.uio_resid; 17555477Sbde edp = (struct ext2_dir_entry_2 *)&dirbuf[readcnt]; 17624649Sdfr ncookies = 0; 17755477Sbde bzero(&dstdp, offsetof(struct dirent, d_name)); 17855477Sbde for (dp = (struct ext2_dir_entry_2 *)dirbuf; 17955477Sbde !error && uio->uio_resid > 0 && dp < edp; ) { 18055477Sbde /*- 18155477Sbde * "New" ext2fs directory entries differ in 3 ways 18255477Sbde * from ufs on-disk ones: 18355477Sbde * - the name is not necessarily NUL-terminated. 18455477Sbde * - the file type field always exists and always 18555477Sbde * follows the name length field. 18655477Sbde * - the file type is encoded in a different way. 18755477Sbde * 18855477Sbde * "Old" ext2fs directory entries need no special 18955477Sbde * conversions, since they binary compatible with 19055477Sbde * "new" entries having a file type of 0 (i.e., 19155477Sbde * EXT2_FT_UNKNOWN). Splitting the old name length 19255477Sbde * field didn't make a mess like it did in ufs, 19355477Sbde * because ext2fs uses a machine-dependent disk 19455477Sbde * layout. 19555477Sbde */ 19655477Sbde dstdp.d_fileno = dp->inode; 19755477Sbde dstdp.d_type = FTTODT(dp->file_type); 19855477Sbde dstdp.d_namlen = dp->name_len; 19955477Sbde dstdp.d_reclen = GENERIC_DIRSIZ(&dstdp); 20055477Sbde bcopy(dp->name, dstdp.d_name, dstdp.d_namlen); 20155477Sbde bzero(dstdp.d_name + dstdp.d_namlen, 20255477Sbde dstdp.d_reclen - offsetof(struct dirent, d_name) - 20355477Sbde dstdp.d_namlen); 20455477Sbde 20512115Sdyson if (dp->rec_len > 0) { 20612115Sdyson if(dstdp.d_reclen <= uio->uio_resid) { 20712115Sdyson /* advance dp */ 20855477Sbde dp = (struct ext2_dir_entry_2 *) 20912115Sdyson ((char *)dp + dp->rec_len); 21012115Sdyson error = 21112159Sbde uiomove((caddr_t)&dstdp, 21212159Sbde dstdp.d_reclen, uio); 21324649Sdfr if (!error) 21424649Sdfr ncookies++; 21512115Sdyson } else 21612115Sdyson break; 21712115Sdyson } else { 21812115Sdyson error = EIO; 21912115Sdyson break; 22012115Sdyson } 22112115Sdyson } 22212115Sdyson /* we need to correct uio_offset */ 22312115Sdyson uio->uio_offset = startoffset + (caddr_t)dp - dirbuf; 22424649Sdfr 22524649Sdfr if (!error && ap->a_ncookies != NULL) { 22624649Sdfr u_long *cookies; 22724649Sdfr u_long *cookiep; 22824649Sdfr off_t off; 22924649Sdfr 23024649Sdfr if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1) 23124649Sdfr panic("ext2fs_readdir: unexpected uio from NFS server"); 23224649Sdfr MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP, 23324649Sdfr M_WAITOK); 23424649Sdfr off = startoffset; 23555477Sbde for (dp = (struct ext2_dir_entry_2 *)dirbuf, cookiep = cookies; 23624649Sdfr dp < edp; 23755477Sbde dp = (struct ext2_dir_entry_2 *)((caddr_t) dp + dp->rec_len)) { 23824649Sdfr off += dp->rec_len; 23924649Sdfr *cookiep++ = (u_long) off; 24024649Sdfr } 24124649Sdfr *ap->a_ncookies = ncookies; 24224649Sdfr *ap->a_cookies = cookies; 24324649Sdfr } 24412115Sdyson } 24512115Sdyson FREE(dirbuf, M_TEMP); 24624649Sdfr if (ap->a_eofflag) 24724649Sdfr *ap->a_eofflag = VTOI(ap->a_vp)->i_size <= uio->uio_offset; 24812115Sdyson return (error); 24912115Sdyson} 25012115Sdyson 25112115Sdyson/* 25212115Sdyson * Convert a component of a pathname into a pointer to a locked inode. 25312115Sdyson * This is a very central and rather complicated routine. 25412115Sdyson * If the file system is not maintained in a strict tree hierarchy, 25512115Sdyson * this can result in a deadlock situation (see comments in code below). 25612115Sdyson * 25712115Sdyson * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending 25812115Sdyson * on whether the name is to be looked up, created, renamed, or deleted. 25912115Sdyson * When CREATE, RENAME, or DELETE is specified, information usable in 26012115Sdyson * creating, renaming, or deleting a directory entry may be calculated. 26112115Sdyson * If flag has LOCKPARENT or'ed into it and the target of the pathname 26212115Sdyson * exists, lookup returns both the target and its parent directory locked. 26312115Sdyson * When creating or renaming and LOCKPARENT is specified, the target may 26412115Sdyson * not be ".". When deleting and LOCKPARENT is specified, the target may 26512115Sdyson * be "."., but the caller must check to ensure it does an vrele and vput 26612115Sdyson * instead of two vputs. 26712115Sdyson * 26812115Sdyson * Overall outline of ufs_lookup: 26912115Sdyson * 27012115Sdyson * search for name in directory, to found or notfound 27112115Sdyson * notfound: 27212115Sdyson * if creating, return locked directory, leaving info on available slots 27312115Sdyson * else return error 27412115Sdyson * found: 27512115Sdyson * if at end of path and deleting, return information to allow delete 27612115Sdyson * if at end of path and rewriting (RENAME and LOCKPARENT), lock target 27712115Sdyson * inode and return info to allow rewrite 27812115Sdyson * if not at end, add name to cache; if at end and neither creating 27912115Sdyson * nor deleting, add name to cache 28012115Sdyson */ 28112115Sdysonint 28212115Sdysonext2_lookup(ap) 28328787Sphk struct vop_cachedlookup_args /* { 28412115Sdyson struct vnode *a_dvp; 28512115Sdyson struct vnode **a_vpp; 28612115Sdyson struct componentname *a_cnp; 28712115Sdyson } */ *ap; 28812115Sdyson{ 28912115Sdyson register struct vnode *vdp; /* vnode for directory being searched */ 29012115Sdyson register struct inode *dp; /* inode for directory being searched */ 29112115Sdyson struct buf *bp; /* a buffer of directory entries */ 29255477Sbde register struct ext2_dir_entry_2 *ep; /* the current directory entry */ 29312115Sdyson int entryoffsetinblock; /* offset of ep in bp's buffer */ 29412115Sdyson enum {NONE, COMPACT, FOUND} slotstatus; 29512115Sdyson doff_t slotoffset; /* offset of area with free space */ 29612115Sdyson int slotsize; /* size of area at slotoffset */ 29712115Sdyson int slotfreespace; /* amount of space free in slot */ 29812115Sdyson int slotneeded; /* size of the entry we're seeking */ 29912115Sdyson int numdirpasses; /* strategy for directory search */ 30012115Sdyson doff_t endsearch; /* offset to end directory search */ 30112115Sdyson doff_t prevoff; /* prev entry dp->i_offset */ 30212115Sdyson struct vnode *pdp; /* saved dp during symlink work */ 30312115Sdyson struct vnode *tdp; /* returned by VFS_VGET */ 30412115Sdyson doff_t enduseful; /* pointer past last used dir slot */ 30512115Sdyson u_long bmask; /* block offset mask */ 30612115Sdyson int lockparent; /* 1 => lockparent flag is set */ 30712115Sdyson int wantparent; /* 1 => wantparent or lockparent flag */ 30812115Sdyson int namlen, error; 30912115Sdyson struct vnode **vpp = ap->a_vpp; 31012115Sdyson struct componentname *cnp = ap->a_cnp; 31112115Sdyson struct ucred *cred = cnp->cn_cred; 31212115Sdyson int flags = cnp->cn_flags; 31312115Sdyson int nameiop = cnp->cn_nameiop; 31422521Sdyson struct proc *p = cnp->cn_proc; 31512115Sdyson 31612115Sdyson int DIRBLKSIZ = VTOI(ap->a_dvp)->i_e2fs->s_blocksize; 31712115Sdyson 31812115Sdyson bp = NULL; 31912115Sdyson slotoffset = -1; 32012115Sdyson *vpp = NULL; 32112115Sdyson vdp = ap->a_dvp; 32212115Sdyson dp = VTOI(vdp); 32312115Sdyson lockparent = flags & LOCKPARENT; 32412115Sdyson wantparent = flags & (LOCKPARENT|WANTPARENT); 32512115Sdyson 32612115Sdyson /* 32712115Sdyson * We now have a segment name to search for, and a directory to search. 32812115Sdyson */ 32912115Sdyson 33012115Sdyson /* 33112115Sdyson * Suppress search for slots unless creating 33212115Sdyson * file and at end of pathname, in which case 33312115Sdyson * we watch for a place to put the new file in 33412115Sdyson * case it doesn't already exist. 33512115Sdyson */ 33612115Sdyson slotstatus = FOUND; 33712115Sdyson slotfreespace = slotsize = slotneeded = 0; 33812115Sdyson if ((nameiop == CREATE || nameiop == RENAME) && 33912115Sdyson (flags & ISLASTCN)) { 34012115Sdyson slotstatus = NONE; 34112115Sdyson slotneeded = EXT2_DIR_REC_LEN(cnp->cn_namelen); 34212115Sdyson /* was 34312115Sdyson slotneeded = (sizeof(struct direct) - MAXNAMLEN + 34412115Sdyson cnp->cn_namelen + 3) &~ 3; */ 34512115Sdyson } 34612115Sdyson 34712115Sdyson /* 34812115Sdyson * If there is cached information on a previous search of 34912115Sdyson * this directory, pick up where we last left off. 35012115Sdyson * We cache only lookups as these are the most common 35112115Sdyson * and have the greatest payoff. Caching CREATE has little 35212115Sdyson * benefit as it usually must search the entire directory 35312115Sdyson * to determine that the entry does not exist. Caching the 35412115Sdyson * location of the last DELETE or RENAME has not reduced 35512115Sdyson * profiling time and hence has been removed in the interest 35612115Sdyson * of simplicity. 35712115Sdyson */ 35812115Sdyson bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1; 35912115Sdyson if (nameiop != LOOKUP || dp->i_diroff == 0 || 36012115Sdyson dp->i_diroff > dp->i_size) { 36112115Sdyson entryoffsetinblock = 0; 36212115Sdyson dp->i_offset = 0; 36312115Sdyson numdirpasses = 1; 36412115Sdyson } else { 36512115Sdyson dp->i_offset = dp->i_diroff; 36612115Sdyson if ((entryoffsetinblock = dp->i_offset & bmask) && 36730474Sphk (error = UFS_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp))) 36812115Sdyson return (error); 36912115Sdyson numdirpasses = 2; 37012115Sdyson nchstats.ncs_2passes++; 37112115Sdyson } 37212115Sdyson prevoff = dp->i_offset; 37312115Sdyson endsearch = roundup(dp->i_size, DIRBLKSIZ); 37412115Sdyson enduseful = 0; 37512115Sdyson 37612115Sdysonsearchloop: 37712115Sdyson while (dp->i_offset < endsearch) { 37812115Sdyson /* 37912115Sdyson * If necessary, get the next directory block. 38012115Sdyson */ 38112115Sdyson if ((dp->i_offset & bmask) == 0) { 38212115Sdyson if (bp != NULL) 38312115Sdyson brelse(bp); 38443301Sdillon if ((error = 38543301Sdillon UFS_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp)) != 0) 38612115Sdyson return (error); 38712115Sdyson entryoffsetinblock = 0; 38812115Sdyson } 38912115Sdyson /* 39012115Sdyson * If still looking for a slot, and at a DIRBLKSIZE 39112115Sdyson * boundary, have to start looking for free space again. 39212115Sdyson */ 39312115Sdyson if (slotstatus == NONE && 39412115Sdyson (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) { 39512115Sdyson slotoffset = -1; 39612115Sdyson slotfreespace = 0; 39712115Sdyson } 39812115Sdyson /* 39912115Sdyson * Get pointer to next entry. 40012115Sdyson * Full validation checks are slow, so we only check 40112115Sdyson * enough to insure forward progress through the 40212115Sdyson * directory. Complete checks can be run by patching 40312115Sdyson * "dirchk" to be true. 40412115Sdyson */ 40555477Sbde ep = (struct ext2_dir_entry_2 *) 40612115Sdyson ((char *)bp->b_data + entryoffsetinblock); 40712115Sdyson if (ep->rec_len == 0 || 40812147Sdyson (dirchk && ext2_dirbadentry(vdp, ep, entryoffsetinblock))) { 40912115Sdyson int i; 41012115Sdyson ufs_dirbad(dp, dp->i_offset, "mangled entry"); 41112115Sdyson i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)); 41212115Sdyson dp->i_offset += i; 41312115Sdyson entryoffsetinblock += i; 41412115Sdyson continue; 41512115Sdyson } 41612115Sdyson 41712115Sdyson /* 41812115Sdyson * If an appropriate sized slot has not yet been found, 41912115Sdyson * check to see if one is available. Also accumulate space 42012115Sdyson * in the current block so that we can determine if 42112115Sdyson * compaction is viable. 42212115Sdyson */ 42312115Sdyson if (slotstatus != FOUND) { 42412115Sdyson int size = ep->rec_len; 42512115Sdyson 42612115Sdyson if (ep->inode != 0) 42712115Sdyson size -= EXT2_DIR_REC_LEN(ep->name_len); 42812115Sdyson if (size > 0) { 42912115Sdyson if (size >= slotneeded) { 43012115Sdyson slotstatus = FOUND; 43112115Sdyson slotoffset = dp->i_offset; 43212115Sdyson slotsize = ep->rec_len; 43312115Sdyson } else if (slotstatus == NONE) { 43412115Sdyson slotfreespace += size; 43512115Sdyson if (slotoffset == -1) 43612115Sdyson slotoffset = dp->i_offset; 43712115Sdyson if (slotfreespace >= slotneeded) { 43812115Sdyson slotstatus = COMPACT; 43912115Sdyson slotsize = dp->i_offset + 44012115Sdyson ep->rec_len - slotoffset; 44112115Sdyson } 44212115Sdyson } 44312115Sdyson } 44412115Sdyson } 44512115Sdyson 44612115Sdyson /* 44712115Sdyson * Check for a name match. 44812115Sdyson */ 44912115Sdyson if (ep->inode) { 45012115Sdyson namlen = ep->name_len; 45112115Sdyson if (namlen == cnp->cn_namelen && 45212115Sdyson !bcmp(cnp->cn_nameptr, ep->name, 45312115Sdyson (unsigned)namlen)) { 45412115Sdyson /* 45512115Sdyson * Save directory entry's inode number and 45612115Sdyson * reclen in ndp->ni_ufs area, and release 45712115Sdyson * directory buffer. 45812115Sdyson */ 45912115Sdyson dp->i_ino = ep->inode; 46012115Sdyson dp->i_reclen = ep->rec_len; 46112115Sdyson brelse(bp); 46212115Sdyson goto found; 46312115Sdyson } 46412115Sdyson } 46512115Sdyson prevoff = dp->i_offset; 46612115Sdyson dp->i_offset += ep->rec_len; 46712115Sdyson entryoffsetinblock += ep->rec_len; 46812115Sdyson if (ep->inode) 46912115Sdyson enduseful = dp->i_offset; 47012115Sdyson } 47112115Sdyson/* notfound: */ 47212115Sdyson /* 47312115Sdyson * If we started in the middle of the directory and failed 47412115Sdyson * to find our target, we must check the beginning as well. 47512115Sdyson */ 47612115Sdyson if (numdirpasses == 2) { 47712115Sdyson numdirpasses--; 47812115Sdyson dp->i_offset = 0; 47912115Sdyson endsearch = dp->i_diroff; 48012115Sdyson goto searchloop; 48112115Sdyson } 48212115Sdyson if (bp != NULL) 48312115Sdyson brelse(bp); 48412115Sdyson /* 48512115Sdyson * If creating, and at end of pathname and current 48612115Sdyson * directory has not been removed, then can consider 48712115Sdyson * allowing file to be created. 48812115Sdyson */ 48912115Sdyson if ((nameiop == CREATE || nameiop == RENAME) && 49012115Sdyson (flags & ISLASTCN) && dp->i_nlink != 0) { 49112115Sdyson /* 49212115Sdyson * Access for write is interpreted as allowing 49312115Sdyson * creation of files in the directory. 49412115Sdyson */ 49543301Sdillon if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0) 49612115Sdyson return (error); 49712115Sdyson /* 49812115Sdyson * Return an indication of where the new directory 49912115Sdyson * entry should be put. If we didn't find a slot, 50012115Sdyson * then set dp->i_count to 0 indicating 50112115Sdyson * that the new slot belongs at the end of the 50212115Sdyson * directory. If we found a slot, then the new entry 50312115Sdyson * can be put in the range from dp->i_offset to 50412115Sdyson * dp->i_offset + dp->i_count. 50512115Sdyson */ 50612115Sdyson if (slotstatus == NONE) { 50712115Sdyson dp->i_offset = roundup(dp->i_size, DIRBLKSIZ); 50812115Sdyson dp->i_count = 0; 50912115Sdyson enduseful = dp->i_offset; 51012115Sdyson } else { 51112115Sdyson dp->i_offset = slotoffset; 51212115Sdyson dp->i_count = slotsize; 51312115Sdyson if (enduseful < slotoffset + slotsize) 51412115Sdyson enduseful = slotoffset + slotsize; 51512115Sdyson } 51612115Sdyson dp->i_endoff = roundup(enduseful, DIRBLKSIZ); 51712115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 51812115Sdyson /* 51912115Sdyson * We return with the directory locked, so that 52012115Sdyson * the parameters we set up above will still be 52112115Sdyson * valid if we actually decide to do a direnter(). 52212115Sdyson * We return ni_vp == NULL to indicate that the entry 52312115Sdyson * does not currently exist; we leave a pointer to 52412115Sdyson * the (locked) directory inode in ndp->ni_dvp. 52512115Sdyson * The pathname buffer is saved so that the name 52612115Sdyson * can be obtained later. 52712115Sdyson * 52812115Sdyson * NB - if the directory is unlocked, then this 52912115Sdyson * information cannot be used. 53012115Sdyson */ 53112115Sdyson cnp->cn_flags |= SAVENAME; 53212115Sdyson if (!lockparent) 53322521Sdyson VOP_UNLOCK(vdp, 0, p); 53412115Sdyson return (EJUSTRETURN); 53512115Sdyson } 53612115Sdyson /* 53712115Sdyson * Insert name into cache (as non-existent) if appropriate. 53812115Sdyson */ 53912115Sdyson if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) 54012115Sdyson cache_enter(vdp, *vpp, cnp); 54112115Sdyson return (ENOENT); 54212115Sdyson 54312115Sdysonfound: 54412115Sdyson if (numdirpasses == 2) 54512115Sdyson nchstats.ncs_pass2++; 54612115Sdyson /* 54712115Sdyson * Check that directory length properly reflects presence 54812115Sdyson * of this entry. 54912115Sdyson */ 55012115Sdyson if (entryoffsetinblock + EXT2_DIR_REC_LEN(ep->name_len) 55112115Sdyson > dp->i_size) { 55212115Sdyson ufs_dirbad(dp, dp->i_offset, "i_size too small"); 55312115Sdyson dp->i_size = entryoffsetinblock+EXT2_DIR_REC_LEN(ep->name_len); 55412115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 55512115Sdyson } 55612115Sdyson 55712115Sdyson /* 55812115Sdyson * Found component in pathname. 55912115Sdyson * If the final component of path name, save information 56012115Sdyson * in the cache as to where the entry was found. 56112115Sdyson */ 56212115Sdyson if ((flags & ISLASTCN) && nameiop == LOOKUP) 56312115Sdyson dp->i_diroff = dp->i_offset &~ (DIRBLKSIZ - 1); 56412115Sdyson 56512115Sdyson /* 56612115Sdyson * If deleting, and at end of pathname, return 56712115Sdyson * parameters which can be used to remove file. 56812115Sdyson * If the wantparent flag isn't set, we return only 56912115Sdyson * the directory (in ndp->ni_dvp), otherwise we go 57012115Sdyson * on and lock the inode, being careful with ".". 57112115Sdyson */ 57212115Sdyson if (nameiop == DELETE && (flags & ISLASTCN)) { 57312115Sdyson /* 57412115Sdyson * Write access to directory required to delete files. 57512115Sdyson */ 57643301Sdillon if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0) 57712115Sdyson return (error); 57812115Sdyson /* 57912115Sdyson * Return pointer to current entry in dp->i_offset, 58012115Sdyson * and distance past previous entry (if there 58112115Sdyson * is a previous entry in this block) in dp->i_count. 58212115Sdyson * Save directory inode pointer in ndp->ni_dvp for dirremove(). 58312115Sdyson */ 58412115Sdyson if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0) 58512115Sdyson dp->i_count = 0; 58612115Sdyson else 58712115Sdyson dp->i_count = dp->i_offset - prevoff; 58812115Sdyson if (dp->i_number == dp->i_ino) { 58912115Sdyson VREF(vdp); 59012115Sdyson *vpp = vdp; 59112115Sdyson return (0); 59212115Sdyson } 59343301Sdillon if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) 59412115Sdyson return (error); 59512115Sdyson /* 59612115Sdyson * If directory is "sticky", then user must own 59712115Sdyson * the directory, or the file in it, else she 59812115Sdyson * may not delete it (unless she's root). This 59912115Sdyson * implements append-only directories. 60012115Sdyson */ 60112115Sdyson if ((dp->i_mode & ISVTX) && 60212115Sdyson cred->cr_uid != 0 && 60312115Sdyson cred->cr_uid != dp->i_uid && 60412115Sdyson VTOI(tdp)->i_uid != cred->cr_uid) { 60512115Sdyson vput(tdp); 60612115Sdyson return (EPERM); 60712115Sdyson } 60812115Sdyson *vpp = tdp; 60912115Sdyson if (!lockparent) 61022521Sdyson VOP_UNLOCK(vdp, 0, p); 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 */ 62012115Sdyson if (nameiop == RENAME && wantparent && 62112115Sdyson (flags & ISLASTCN)) { 62243301Sdillon if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0) 62312115Sdyson return (error); 62412115Sdyson /* 62512115Sdyson * Careful about locking second inode. 62612115Sdyson * This can only occur if the target is ".". 62712115Sdyson */ 62812115Sdyson if (dp->i_number == dp->i_ino) 62912115Sdyson return (EISDIR); 63043301Sdillon if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) 63112115Sdyson return (error); 63212115Sdyson *vpp = tdp; 63312115Sdyson cnp->cn_flags |= SAVENAME; 63412115Sdyson if (!lockparent) 63522521Sdyson VOP_UNLOCK(vdp, 0, p); 63612115Sdyson return (0); 63712115Sdyson } 63812115Sdyson 63912115Sdyson /* 64012115Sdyson * Step through the translation in the name. We do not `vput' the 64112115Sdyson * directory because we may need it again if a symbolic link 64212115Sdyson * is relative to the current directory. Instead we save it 64312115Sdyson * unlocked as "pdp". We must get the target inode before unlocking 64412115Sdyson * the directory to insure that the inode will not be removed 64512115Sdyson * before we get it. We prevent deadlock by always fetching 64612115Sdyson * inodes from the root, moving down the directory tree. Thus 64712115Sdyson * when following backward pointers ".." we must unlock the 64812115Sdyson * parent directory before getting the requested directory. 64912115Sdyson * There is a potential race condition here if both the current 65012115Sdyson * and parent directories are removed before the VFS_VGET for the 65112115Sdyson * inode associated with ".." returns. We hope that this occurs 65212115Sdyson * infrequently since we cannot avoid this race condition without 65312115Sdyson * implementing a sophisticated deadlock detection algorithm. 65412115Sdyson * Note also that this simple deadlock detection scheme will not 65512115Sdyson * work if the file system has any hard links other than ".." 65612115Sdyson * that point backwards in the directory structure. 65712115Sdyson */ 65812115Sdyson pdp = vdp; 65912115Sdyson if (flags & ISDOTDOT) { 66022521Sdyson VOP_UNLOCK(pdp, 0, p); /* race to get the inode */ 66143301Sdillon if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) { 66222521Sdyson vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p); 66312115Sdyson return (error); 66412115Sdyson } 66512115Sdyson if (lockparent && (flags & ISLASTCN) && 66622521Sdyson (error = vn_lock(pdp, LK_EXCLUSIVE, p))) { 66712115Sdyson vput(tdp); 66812115Sdyson return (error); 66912115Sdyson } 67012115Sdyson *vpp = tdp; 67112115Sdyson } else if (dp->i_number == dp->i_ino) { 67212115Sdyson VREF(vdp); /* we want ourself, ie "." */ 67312115Sdyson *vpp = vdp; 67412115Sdyson } else { 67543301Sdillon if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) 67612115Sdyson return (error); 67712115Sdyson if (!lockparent || !(flags & ISLASTCN)) 67822521Sdyson VOP_UNLOCK(pdp, 0, p); 67912115Sdyson *vpp = tdp; 68012115Sdyson } 68112115Sdyson 68212115Sdyson /* 68312115Sdyson * Insert name into cache if appropriate. 68412115Sdyson */ 68512115Sdyson if (cnp->cn_flags & MAKEENTRY) 68612115Sdyson cache_enter(vdp, *vpp, cnp); 68712115Sdyson return (0); 68812115Sdyson} 68912115Sdyson 69012115Sdyson/* 69112115Sdyson * Do consistency checking on a directory entry: 69212115Sdyson * record length must be multiple of 4 69312115Sdyson * entry must fit in rest of its DIRBLKSIZ block 69412115Sdyson * record must be large enough to contain entry 69512115Sdyson * name is not longer than MAXNAMLEN 69612115Sdyson * name must be as long as advertised, and null terminated 69712115Sdyson */ 69812115Sdyson/* 69912115Sdyson * changed so that it confirms to ext2_check_dir_entry 70012115Sdyson */ 70112159Sbdestatic int 70212115Sdysonext2_dirbadentry(dp, de, entryoffsetinblock) 70312115Sdyson struct vnode *dp; 70455477Sbde register struct ext2_dir_entry_2 *de; 70512115Sdyson int entryoffsetinblock; 70612115Sdyson{ 70712115Sdyson int DIRBLKSIZ = VTOI(dp)->i_e2fs->s_blocksize; 70812115Sdyson 70912115Sdyson char * error_msg = NULL; 71012115Sdyson 71112115Sdyson if (de->rec_len < EXT2_DIR_REC_LEN(1)) 71212115Sdyson error_msg = "rec_len is smaller than minimal"; 71312115Sdyson else if (de->rec_len % 4 != 0) 71412115Sdyson error_msg = "rec_len % 4 != 0"; 71512115Sdyson else if (de->rec_len < EXT2_DIR_REC_LEN(de->name_len)) 71612115Sdyson error_msg = "reclen is too small for name_len"; 71712115Sdyson else if (entryoffsetinblock + de->rec_len > DIRBLKSIZ) 71812115Sdyson error_msg = "directory entry across blocks"; 71912115Sdyson /* else LATER 72012115Sdyson if (de->inode > dir->i_sb->u.ext2_sb.s_es->s_inodes_count) 72112115Sdyson error_msg = "inode out of bounds"; 72212115Sdyson */ 72312115Sdyson 72424492Sbde if (error_msg != NULL) { 72524492Sbde printf("bad directory entry: %s\n", error_msg); 72637555Sbde printf("offset=%d, inode=%lu, rec_len=%u, name_len=%u\n", 72724492Sbde entryoffsetinblock, (unsigned long)de->inode, 72824492Sbde de->rec_len, de->name_len); 72924492Sbde } 73012115Sdyson return error_msg == NULL ? 0 : 1; 73112115Sdyson} 73212115Sdyson 73312115Sdyson/* 73412115Sdyson * Write a directory entry after a call to namei, using the parameters 73512115Sdyson * that it left in nameidata. The argument ip is the inode which the new 73612115Sdyson * directory entry will refer to. Dvp is a pointer to the directory to 73712115Sdyson * be written, which was left locked by namei. Remaining parameters 73812115Sdyson * (dp->i_offset, dp->i_count) indicate how the space for the new 73912115Sdyson * entry is to be obtained. 74012115Sdyson */ 74112115Sdysonint 74212115Sdysonext2_direnter(ip, dvp, cnp) 74312115Sdyson struct inode *ip; 74412115Sdyson struct vnode *dvp; 74512115Sdyson register struct componentname *cnp; 74612115Sdyson{ 74755477Sbde register struct ext2_dir_entry_2 *ep, *nep; 74812115Sdyson register struct inode *dp; 74912115Sdyson struct buf *bp; 75055477Sbde struct ext2_dir_entry_2 newdir; 75112115Sdyson struct iovec aiov; 75212115Sdyson struct uio auio; 75312115Sdyson u_int dsize; 75412115Sdyson int error, loc, newentrysize, spacefree; 75512115Sdyson char *dirbuf; 75612115Sdyson int DIRBLKSIZ = ip->i_e2fs->s_blocksize; 75712115Sdyson 75812115Sdyson 75912115Sdyson#if DIAGNOSTIC 76012115Sdyson if ((cnp->cn_flags & SAVENAME) == 0) 76112115Sdyson panic("direnter: missing name"); 76212115Sdyson#endif 76312115Sdyson dp = VTOI(dvp); 76412115Sdyson newdir.inode = ip->i_number; 76512115Sdyson newdir.name_len = cnp->cn_namelen; 76655477Sbde if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs->s_es, 76755477Sbde EXT2_FEATURE_INCOMPAT_FILETYPE)) 76855477Sbde newdir.file_type = DTTOFT(IFTODT(ip->i_mode)); 76955477Sbde else 77055477Sbde newdir.file_type = EXT2_FT_UNKNOWN; 77112115Sdyson bcopy(cnp->cn_nameptr, newdir.name, (unsigned)cnp->cn_namelen + 1); 77212115Sdyson newentrysize = EXT2_DIR_REC_LEN(newdir.name_len); 77312115Sdyson if (dp->i_count == 0) { 77412115Sdyson /* 77512115Sdyson * If dp->i_count is 0, then namei could find no 77612115Sdyson * space in the directory. Here, dp->i_offset will 77712115Sdyson * be on a directory block boundary and we will write the 77812115Sdyson * new entry into a fresh block. 77912115Sdyson */ 78012115Sdyson if (dp->i_offset & (DIRBLKSIZ - 1)) 78112115Sdyson panic("ext2_direnter: newblk"); 78212115Sdyson auio.uio_offset = dp->i_offset; 78312115Sdyson newdir.rec_len = DIRBLKSIZ; 78412115Sdyson auio.uio_resid = newentrysize; 78512115Sdyson aiov.iov_len = newentrysize; 78612115Sdyson aiov.iov_base = (caddr_t)&newdir; 78712115Sdyson auio.uio_iov = &aiov; 78812115Sdyson auio.uio_iovcnt = 1; 78912115Sdyson auio.uio_rw = UIO_WRITE; 79012115Sdyson auio.uio_segflg = UIO_SYSSPACE; 79112115Sdyson auio.uio_procp = (struct proc *)0; 79212115Sdyson error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred); 79312115Sdyson if (DIRBLKSIZ > 79412115Sdyson VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize) 79512115Sdyson /* XXX should grow with balloc() */ 79612115Sdyson panic("ext2_direnter: frag size"); 79712115Sdyson else if (!error) { 79812115Sdyson dp->i_size = roundup(dp->i_size, DIRBLKSIZ); 79912115Sdyson dp->i_flag |= IN_CHANGE; 80012115Sdyson } 80112115Sdyson return (error); 80212115Sdyson } 80312115Sdyson 80412115Sdyson /* 80512115Sdyson * If dp->i_count is non-zero, then namei found space 80612115Sdyson * for the new entry in the range dp->i_offset to 80712115Sdyson * dp->i_offset + dp->i_count in the directory. 80812115Sdyson * To use this space, we may have to compact the entries located 80912115Sdyson * there, by copying them together towards the beginning of the 81012115Sdyson * block, leaving the free space in one usable chunk at the end. 81112115Sdyson */ 81212115Sdyson 81312115Sdyson /* 81412115Sdyson * Increase size of directory if entry eats into new space. 81512115Sdyson * This should never push the size past a new multiple of 81612115Sdyson * DIRBLKSIZE. 81712115Sdyson * 81812115Sdyson * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN. 81912115Sdyson */ 82012115Sdyson if (dp->i_offset + dp->i_count > dp->i_size) 82112115Sdyson dp->i_size = dp->i_offset + dp->i_count; 82212115Sdyson /* 82312115Sdyson * Get the block containing the space for the new directory entry. 82412115Sdyson */ 82543301Sdillon if ((error = UFS_BLKATOFF(dvp, (off_t)dp->i_offset, &dirbuf, &bp)) != 0) 82612115Sdyson return (error); 82712115Sdyson /* 82812115Sdyson * Find space for the new entry. In the simple case, the entry at 82912115Sdyson * offset base will have the space. If it does not, then namei 83012115Sdyson * arranged that compacting the region dp->i_offset to 83112115Sdyson * dp->i_offset + dp->i_count would yield the 83212115Sdyson * space. 83312115Sdyson */ 83455477Sbde ep = (struct ext2_dir_entry_2 *)dirbuf; 83512115Sdyson dsize = EXT2_DIR_REC_LEN(ep->name_len); 83612115Sdyson spacefree = ep->rec_len - dsize; 83712115Sdyson for (loc = ep->rec_len; loc < dp->i_count; ) { 83855477Sbde nep = (struct ext2_dir_entry_2 *)(dirbuf + loc); 83912115Sdyson if (ep->inode) { 84012115Sdyson /* trim the existing slot */ 84112115Sdyson ep->rec_len = dsize; 84255477Sbde ep = (struct ext2_dir_entry_2 *)((char *)ep + dsize); 84312115Sdyson } else { 84412115Sdyson /* overwrite; nothing there; header is ours */ 84512115Sdyson spacefree += dsize; 84612115Sdyson } 84719541Sbde dsize = EXT2_DIR_REC_LEN(nep->name_len); 84812115Sdyson spacefree += nep->rec_len - dsize; 84912115Sdyson loc += nep->rec_len; 85012115Sdyson bcopy((caddr_t)nep, (caddr_t)ep, dsize); 85112115Sdyson } 85212115Sdyson /* 85312115Sdyson * Update the pointer fields in the previous entry (if any), 85412115Sdyson * copy in the new entry, and write out the block. 85512115Sdyson */ 85612115Sdyson if (ep->inode == 0) { 85712115Sdyson if (spacefree + dsize < newentrysize) 85812115Sdyson panic("ext2_direnter: compact1"); 85912115Sdyson newdir.rec_len = spacefree + dsize; 86012115Sdyson } else { 86112115Sdyson if (spacefree < newentrysize) 86212115Sdyson panic("ext2_direnter: compact2"); 86312115Sdyson newdir.rec_len = spacefree; 86412115Sdyson ep->rec_len = dsize; 86555477Sbde ep = (struct ext2_dir_entry_2 *)((char *)ep + dsize); 86612115Sdyson } 86712115Sdyson bcopy((caddr_t)&newdir, (caddr_t)ep, (u_int)newentrysize); 86847964Smckusick error = VOP_BWRITE(bp->b_vp, bp); 86912115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 87012115Sdyson if (!error && dp->i_endoff && dp->i_endoff < dp->i_size) 87130474Sphk error = UFS_TRUNCATE(dvp, (off_t)dp->i_endoff, IO_SYNC, 87212115Sdyson cnp->cn_cred, cnp->cn_proc); 87312115Sdyson return (error); 87412115Sdyson} 87512115Sdyson 87612115Sdyson/* 87712115Sdyson * Remove a directory entry after a call to namei, using 87812115Sdyson * the parameters which it left in nameidata. The entry 87912115Sdyson * dp->i_offset contains the offset into the directory of the 88012115Sdyson * entry to be eliminated. The dp->i_count field contains the 88112115Sdyson * size of the previous record in the directory. If this 88212115Sdyson * is 0, the first entry is being deleted, so we need only 88312115Sdyson * zero the inode number to mark the entry as free. If the 88412115Sdyson * entry is not the first in the directory, we must reclaim 88512115Sdyson * the space of the now empty record by adding the record size 88612115Sdyson * to the size of the previous entry. 88712115Sdyson */ 88812115Sdysonint 88912115Sdysonext2_dirremove(dvp, cnp) 89012115Sdyson struct vnode *dvp; 89112115Sdyson struct componentname *cnp; 89212115Sdyson{ 89312115Sdyson register struct inode *dp; 89455477Sbde struct ext2_dir_entry_2 *ep; 89512115Sdyson struct buf *bp; 89612115Sdyson int error; 89712115Sdyson 89812115Sdyson dp = VTOI(dvp); 89912115Sdyson if (dp->i_count == 0) { 90012115Sdyson /* 90112115Sdyson * First entry in block: set d_ino to zero. 90212115Sdyson */ 90343301Sdillon if ((error = 90443301Sdillon UFS_BLKATOFF(dvp, (off_t)dp->i_offset, (char **)&ep, &bp)) != 0) 90512115Sdyson return (error); 90612115Sdyson ep->inode = 0; 90747964Smckusick error = VOP_BWRITE(bp->b_vp, bp); 90812115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 90912115Sdyson return (error); 91012115Sdyson } 91112115Sdyson /* 91212115Sdyson * Collapse new free space into previous entry. 91312115Sdyson */ 91443301Sdillon if ((error = UFS_BLKATOFF(dvp, (off_t)(dp->i_offset - dp->i_count), 91543301Sdillon (char **)&ep, &bp)) != 0) 91612115Sdyson return (error); 91712115Sdyson ep->rec_len += dp->i_reclen; 91847964Smckusick error = VOP_BWRITE(bp->b_vp, bp); 91912115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 92012115Sdyson return (error); 92112115Sdyson} 92212115Sdyson 92312115Sdyson/* 92412115Sdyson * Rewrite an existing directory entry to point at the inode 92512115Sdyson * supplied. The parameters describing the directory entry are 92612115Sdyson * set up by a call to namei. 92712115Sdyson */ 92812115Sdysonint 92912115Sdysonext2_dirrewrite(dp, ip, cnp) 93012115Sdyson struct inode *dp, *ip; 93112115Sdyson struct componentname *cnp; 93212115Sdyson{ 93312115Sdyson struct buf *bp; 93455477Sbde struct ext2_dir_entry_2 *ep; 93512115Sdyson struct vnode *vdp = ITOV(dp); 93612115Sdyson int error; 93712115Sdyson 93843301Sdillon if ((error = UFS_BLKATOFF(vdp, (off_t)dp->i_offset, (char **)&ep, &bp)) != 0) 93912115Sdyson return (error); 94012115Sdyson ep->inode = ip->i_number; 94155477Sbde if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs->s_es, 94255477Sbde EXT2_FEATURE_INCOMPAT_FILETYPE)) 94355477Sbde ep->file_type = DTTOFT(IFTODT(ip->i_mode)); 94455477Sbde else 94555477Sbde ep->file_type = EXT2_FT_UNKNOWN; 94647964Smckusick error = VOP_BWRITE(bp->b_vp, bp); 94712115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 94812115Sdyson return (error); 94912115Sdyson} 95012115Sdyson 95112115Sdyson/* 95212115Sdyson * Check if a directory is empty or not. 95312115Sdyson * Inode supplied must be locked. 95412115Sdyson * 95512115Sdyson * Using a struct dirtemplate here is not precisely 95612115Sdyson * what we want, but better than using a struct direct. 95712115Sdyson * 95812115Sdyson * NB: does not handle corrupted directories. 95912115Sdyson */ 96012115Sdysonint 96112115Sdysonext2_dirempty(ip, parentino, cred) 96212115Sdyson register struct inode *ip; 96312115Sdyson ino_t parentino; 96412115Sdyson struct ucred *cred; 96512115Sdyson{ 96612115Sdyson register off_t off; 96712115Sdyson struct dirtemplate dbuf; 96855477Sbde register struct ext2_dir_entry_2 *dp = (struct ext2_dir_entry_2 *)&dbuf; 96912115Sdyson int error, count, namlen; 97012115Sdyson 97112115Sdyson#define MINDIRSIZ (sizeof (struct dirtemplate) / 2) 97212115Sdyson 97312115Sdyson for (off = 0; off < ip->i_size; off += dp->rec_len) { 97412115Sdyson error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off, 97512115Sdyson UIO_SYSSPACE, IO_NODELOCKED, cred, &count, (struct proc *)0); 97612115Sdyson /* 97712115Sdyson * Since we read MINDIRSIZ, residual must 97812115Sdyson * be 0 unless we're at end of file. 97912115Sdyson */ 98012115Sdyson if (error || count != 0) 98112115Sdyson return (0); 98212115Sdyson /* avoid infinite loops */ 98312115Sdyson if (dp->rec_len == 0) 98412115Sdyson return (0); 98512115Sdyson /* skip empty entries */ 98612115Sdyson if (dp->inode == 0) 98712115Sdyson continue; 98812115Sdyson /* accept only "." and ".." */ 98912115Sdyson namlen = dp->name_len; 99012115Sdyson if (namlen > 2) 99112115Sdyson return (0); 99212115Sdyson if (dp->name[0] != '.') 99312115Sdyson return (0); 99412115Sdyson /* 99512115Sdyson * At this point namlen must be 1 or 2. 99612115Sdyson * 1 implies ".", 2 implies ".." if second 99712115Sdyson * char is also "." 99812115Sdyson */ 99912115Sdyson if (namlen == 1) 100012115Sdyson continue; 100112115Sdyson if (dp->name[1] == '.' && dp->inode == parentino) 100212115Sdyson continue; 100312115Sdyson return (0); 100412115Sdyson } 100512115Sdyson return (1); 100612115Sdyson} 100712115Sdyson 100812115Sdyson/* 100912115Sdyson * Check if source directory is in the path of the target directory. 101012115Sdyson * Target is supplied locked, source is unlocked. 101112115Sdyson * The target is always vput before returning. 101212115Sdyson */ 101312115Sdysonint 101412115Sdysonext2_checkpath(source, target, cred) 101512115Sdyson struct inode *source, *target; 101612115Sdyson struct ucred *cred; 101712115Sdyson{ 101812115Sdyson struct vnode *vp; 101912115Sdyson int error, rootino, namlen; 102012115Sdyson struct dirtemplate dirbuf; 102112115Sdyson 102212115Sdyson vp = ITOV(target); 102312115Sdyson if (target->i_number == source->i_number) { 102412115Sdyson error = EEXIST; 102512115Sdyson goto out; 102612115Sdyson } 102712115Sdyson rootino = ROOTINO; 102812115Sdyson error = 0; 102912115Sdyson if (target->i_number == rootino) 103012115Sdyson goto out; 103112115Sdyson 103212115Sdyson for (;;) { 103312115Sdyson if (vp->v_type != VDIR) { 103412115Sdyson error = ENOTDIR; 103512115Sdyson break; 103612115Sdyson } 103712115Sdyson error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf, 103812115Sdyson sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE, 103912115Sdyson IO_NODELOCKED, cred, (int *)0, (struct proc *)0); 104012115Sdyson if (error != 0) 104112115Sdyson break; 104257710Sbde namlen = dirbuf.dotdot_type; /* like ufs little-endian */ 104312115Sdyson if (namlen != 2 || 104412115Sdyson dirbuf.dotdot_name[0] != '.' || 104512115Sdyson dirbuf.dotdot_name[1] != '.') { 104612115Sdyson error = ENOTDIR; 104712115Sdyson break; 104812115Sdyson } 104912115Sdyson if (dirbuf.dotdot_ino == source->i_number) { 105012115Sdyson error = EINVAL; 105112115Sdyson break; 105212115Sdyson } 105312115Sdyson if (dirbuf.dotdot_ino == rootino) 105412115Sdyson break; 105512115Sdyson vput(vp); 105643301Sdillon if ((error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino, &vp)) != 0) { 105712115Sdyson vp = NULL; 105812115Sdyson break; 105912115Sdyson } 106012115Sdyson } 106112115Sdyson 106212115Sdysonout: 106312115Sdyson if (error == ENOTDIR) 106412115Sdyson printf("checkpath: .. not a directory\n"); 106512115Sdyson if (vp != NULL) 106612115Sdyson vput(vp); 106712115Sdyson return (error); 106812115Sdyson} 106912115Sdyson 1070