ext2_lookup.c revision 202283
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/fs/ext2fs/ext2_lookup.c 202283 2010-01-14 14:30:54Z lulf $ 4212115Sdyson */ 4312115Sdyson 4412115Sdyson#include <sys/param.h> 4512159Sbde#include <sys/systm.h> 4612115Sdyson#include <sys/namei.h> 4760041Sphk#include <sys/bio.h> 4812115Sdyson#include <sys/buf.h> 49193377Sstas#include <sys/endian.h> 5012115Sdyson#include <sys/mount.h> 5112115Sdyson#include <sys/vnode.h> 5212115Sdyson#include <sys/malloc.h> 5312115Sdyson#include <sys/dirent.h> 5496753Siedowse#include <sys/sysctl.h> 5512115Sdyson 5612115Sdyson#include <ufs/ufs/dir.h> 5712115Sdyson 58202283Slulf#include <fs/ext2fs/inode.h> 59202283Slulf#include <fs/ext2fs/ext2_mount.h> 60202283Slulf#include <fs/ext2fs/ext2_extern.h> 61202283Slulf#include <fs/ext2fs/ext2fs.h> 62202283Slulf#include <fs/ext2fs/ext2_dir.h> 6312115Sdyson 6496753Siedowse#ifdef DIAGNOSTIC 6596753Siedowsestatic int dirchk = 1; 6696753Siedowse#else 6796753Siedowsestatic int dirchk = 0; 6896753Siedowse#endif 6996753Siedowse 70141633Sphkstatic SYSCTL_NODE(_vfs, OID_AUTO, e2fs, CTLFLAG_RD, 0, "EXT2FS filesystem"); 7196753SiedowseSYSCTL_INT(_vfs_e2fs, OID_AUTO, dircheck, CTLFLAG_RW, &dirchk, 0, ""); 7296753Siedowse 73111742Sdes/* 7412115Sdyson DIRBLKSIZE in ffs is DEV_BSIZE (in most cases 512) 7512115Sdyson while it is the native blocksize in ext2fs - thus, a #define 7612115Sdyson is no longer appropriate 7712115Sdyson*/ 7812115Sdyson#undef DIRBLKSIZ 7912115Sdyson 8055477Sbdestatic u_char ext2_ft_to_dt[] = { 8155477Sbde DT_UNKNOWN, /* EXT2_FT_UNKNOWN */ 8255477Sbde DT_REG, /* EXT2_FT_REG_FILE */ 8355477Sbde DT_DIR, /* EXT2_FT_DIR */ 8455477Sbde DT_CHR, /* EXT2_FT_CHRDEV */ 8555477Sbde DT_BLK, /* EXT2_FT_BLKDEV */ 8655477Sbde DT_FIFO, /* EXT2_FT_FIFO */ 8755477Sbde DT_SOCK, /* EXT2_FT_SOCK */ 8855477Sbde DT_LNK, /* EXT2_FT_SYMLINK */ 8955477Sbde}; 9055477Sbde#define FTTODT(ft) \ 9155477Sbde ((ft) > sizeof(ext2_ft_to_dt) / sizeof(ext2_ft_to_dt[0]) ? \ 9255477Sbde DT_UNKNOWN : ext2_ft_to_dt[(ft)]) 9355477Sbde 9455477Sbdestatic u_char dt_to_ext2_ft[] = { 9555477Sbde EXT2_FT_UNKNOWN, /* DT_UNKNOWN */ 9655477Sbde EXT2_FT_FIFO, /* DT_FIFO */ 9755477Sbde EXT2_FT_CHRDEV, /* DT_CHR */ 9855477Sbde EXT2_FT_UNKNOWN, /* unused */ 9955477Sbde EXT2_FT_DIR, /* DT_DIR */ 10055477Sbde EXT2_FT_UNKNOWN, /* unused */ 10155477Sbde EXT2_FT_BLKDEV, /* DT_BLK */ 10255477Sbde EXT2_FT_UNKNOWN, /* unused */ 10355477Sbde EXT2_FT_REG_FILE, /* DT_REG */ 10455477Sbde EXT2_FT_UNKNOWN, /* unused */ 10555477Sbde EXT2_FT_SYMLINK, /* DT_LNK */ 10655477Sbde EXT2_FT_UNKNOWN, /* unused */ 10755477Sbde EXT2_FT_SOCK, /* DT_SOCK */ 10855477Sbde EXT2_FT_UNKNOWN, /* unused */ 10955477Sbde EXT2_FT_UNKNOWN, /* DT_WHT */ 11055477Sbde}; 11155477Sbde#define DTTOFT(dt) \ 11255477Sbde ((dt) > sizeof(dt_to_ext2_ft) / sizeof(dt_to_ext2_ft[0]) ? \ 11355477Sbde EXT2_FT_UNKNOWN : dt_to_ext2_ft[(dt)]) 11455477Sbde 115202283Slulfstatic int ext2_dirbadentry(struct vnode *dp, struct ext2fs_direct_2 *de, 11693014Sbde 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 131111742Sdes * 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) 138111742Sdes struct vop_readdir_args /* { 139111742Sdes struct vnode *a_vp; 140111742Sdes struct uio *a_uio; 141111742Sdes struct ucred *a_cred; 142111742Sdes } */ *ap; 14312115Sdyson{ 144111742Sdes struct uio *uio = ap->a_uio; 145111742Sdes int count, error; 14612115Sdyson 147202283Slulf struct ext2fs_direct_2 *edp, *dp; 14824649Sdfr int ncookies; 14912115Sdyson struct dirent dstdp; 15012115Sdyson struct uio auio; 15112115Sdyson struct iovec aiov; 15212115Sdyson caddr_t dirbuf; 153202283Slulf int DIRBLKSIZ = VTOI(ap->a_vp)->i_e2fs->e2fs_bsize; 15412115Sdyson int readcnt; 15565780Sbde off_t startoffset = uio->uio_offset; 15612115Sdyson 15765780Sbde count = uio->uio_resid; 15865780Sbde /* 15965780Sbde * Avoid complications for partial directory entries by adjusting 16065780Sbde * the i/o to end at a block boundary. Don't give up (like ufs 16165780Sbde * does) if the initial adjustment gives a negative count, since 16265780Sbde * many callers don't supply a large enough buffer. The correct 16365780Sbde * size is a little larger than DIRBLKSIZ to allow for expansion 16465780Sbde * of directory entries, but some callers just use 512. 16565780Sbde */ 16665780Sbde count -= (uio->uio_offset + count) & (DIRBLKSIZ -1); 16765780Sbde if (count <= 0) 16865780Sbde count += DIRBLKSIZ; 16912115Sdyson auio = *uio; 17012115Sdyson auio.uio_iov = &aiov; 17112115Sdyson auio.uio_iovcnt = 1; 17265780Sbde auio.uio_resid = count; 17312115Sdyson auio.uio_segflg = UIO_SYSSPACE; 17412115Sdyson aiov.iov_len = count; 175184205Sdes dirbuf = malloc(count, M_TEMP, M_WAITOK); 17612115Sdyson aiov.iov_base = dirbuf; 17712115Sdyson error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred); 17812115Sdyson if (error == 0) { 17912115Sdyson readcnt = count - auio.uio_resid; 180202283Slulf edp = (struct ext2fs_direct_2 *)&dirbuf[readcnt]; 18124649Sdfr ncookies = 0; 18255477Sbde bzero(&dstdp, offsetof(struct dirent, d_name)); 183202283Slulf for (dp = (struct ext2fs_direct_2 *)dirbuf; 18455477Sbde !error && uio->uio_resid > 0 && dp < edp; ) { 18555477Sbde /*- 18655477Sbde * "New" ext2fs directory entries differ in 3 ways 18755477Sbde * from ufs on-disk ones: 18855477Sbde * - the name is not necessarily NUL-terminated. 18955477Sbde * - the file type field always exists and always 190125843Sbde * follows the name length field. 19155477Sbde * - the file type is encoded in a different way. 19255477Sbde * 19355477Sbde * "Old" ext2fs directory entries need no special 194125843Sbde * conversions, since they are binary compatible 195125843Sbde * with "new" entries having a file type of 0 (i.e., 19655477Sbde * EXT2_FT_UNKNOWN). Splitting the old name length 19755477Sbde * field didn't make a mess like it did in ufs, 198125843Sbde * because ext2fs uses a machine-independent disk 19955477Sbde * layout. 20055477Sbde */ 201202283Slulf dstdp.d_fileno = dp->e2d_ino; 202202283Slulf dstdp.d_type = FTTODT(dp->e2d_type); 203202283Slulf dstdp.d_namlen = dp->e2d_namlen; 20455477Sbde dstdp.d_reclen = GENERIC_DIRSIZ(&dstdp); 205202283Slulf bcopy(dp->e2d_name, dstdp.d_name, dstdp.d_namlen); 20655477Sbde bzero(dstdp.d_name + dstdp.d_namlen, 20755477Sbde dstdp.d_reclen - offsetof(struct dirent, d_name) - 20855477Sbde dstdp.d_namlen); 20955477Sbde 210202283Slulf if (dp->e2d_reclen > 0) { 21112115Sdyson if(dstdp.d_reclen <= uio->uio_resid) { 21212115Sdyson /* advance dp */ 213202283Slulf dp = (struct ext2fs_direct_2 *) 214202283Slulf ((char *)dp + dp->e2d_reclen); 215111742Sdes error = 216111741Sdes uiomove(&dstdp, dstdp.d_reclen, uio); 21724649Sdfr if (!error) 21824649Sdfr ncookies++; 21912115Sdyson } else 22012115Sdyson break; 22112115Sdyson } else { 22212115Sdyson error = EIO; 22312115Sdyson break; 22412115Sdyson } 22512115Sdyson } 22612115Sdyson /* we need to correct uio_offset */ 22712115Sdyson uio->uio_offset = startoffset + (caddr_t)dp - dirbuf; 22824649Sdfr 22924649Sdfr if (!error && ap->a_ncookies != NULL) { 23065780Sbde u_long *cookiep, *cookies, *ecookies; 23124649Sdfr off_t off; 23224649Sdfr 23324649Sdfr if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1) 234125843Sbde panic("ext2_readdir: unexpected uio from NFS server"); 235184205Sdes cookies = malloc(ncookies * sizeof(u_long), M_TEMP, 236111119Simp M_WAITOK); 23724649Sdfr off = startoffset; 238202283Slulf for (dp = (struct ext2fs_direct_2 *)dirbuf, 23965780Sbde cookiep = cookies, ecookies = cookies + ncookies; 24065780Sbde cookiep < ecookies; 241202283Slulf dp = (struct ext2fs_direct_2 *)((caddr_t) dp + dp->e2d_reclen)) { 242202283Slulf off += dp->e2d_reclen; 24324649Sdfr *cookiep++ = (u_long) off; 24424649Sdfr } 24524649Sdfr *ap->a_ncookies = ncookies; 24624649Sdfr *ap->a_cookies = cookies; 24724649Sdfr } 24812115Sdyson } 249184205Sdes free(dirbuf, M_TEMP); 25024649Sdfr if (ap->a_eofflag) 25124649Sdfr *ap->a_eofflag = VTOI(ap->a_vp)->i_size <= uio->uio_offset; 252111742Sdes return (error); 25312115Sdyson} 25412115Sdyson 25512115Sdyson/* 25612115Sdyson * Convert a component of a pathname into a pointer to a locked inode. 25712115Sdyson * This is a very central and rather complicated routine. 25812115Sdyson * If the file system is not maintained in a strict tree hierarchy, 25912115Sdyson * this can result in a deadlock situation (see comments in code below). 26012115Sdyson * 26112115Sdyson * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending 26212115Sdyson * on whether the name is to be looked up, created, renamed, or deleted. 26312115Sdyson * When CREATE, RENAME, or DELETE is specified, information usable in 26412115Sdyson * creating, renaming, or deleting a directory entry may be calculated. 26512115Sdyson * If flag has LOCKPARENT or'ed into it and the target of the pathname 26612115Sdyson * exists, lookup returns both the target and its parent directory locked. 26712115Sdyson * When creating or renaming and LOCKPARENT is specified, the target may 26812115Sdyson * not be ".". When deleting and LOCKPARENT is specified, the target may 26912115Sdyson * be "."., but the caller must check to ensure it does an vrele and vput 27012115Sdyson * instead of two vputs. 27112115Sdyson * 272125843Sbde * Overall outline of ext2_lookup: 27312115Sdyson * 27412115Sdyson * search for name in directory, to found or notfound 27512115Sdyson * notfound: 27612115Sdyson * if creating, return locked directory, leaving info on available slots 27712115Sdyson * else return error 27812115Sdyson * found: 27912115Sdyson * if at end of path and deleting, return information to allow delete 28012115Sdyson * if at end of path and rewriting (RENAME and LOCKPARENT), lock target 28112115Sdyson * inode and return info to allow rewrite 28212115Sdyson * if not at end, add name to cache; if at end and neither creating 28312115Sdyson * nor deleting, add name to cache 28412115Sdyson */ 28512115Sdysonint 28612115Sdysonext2_lookup(ap) 28728787Sphk struct vop_cachedlookup_args /* { 28812115Sdyson struct vnode *a_dvp; 28912115Sdyson struct vnode **a_vpp; 29012115Sdyson struct componentname *a_cnp; 29112115Sdyson } */ *ap; 29212115Sdyson{ 29396752Siedowse struct vnode *vdp; /* vnode for directory being searched */ 29496752Siedowse struct inode *dp; /* inode for directory being searched */ 29512115Sdyson struct buf *bp; /* a buffer of directory entries */ 296202283Slulf struct ext2fs_direct_2 *ep; /* the current directory entry */ 29712115Sdyson int entryoffsetinblock; /* offset of ep in bp's buffer */ 29812115Sdyson enum {NONE, COMPACT, FOUND} slotstatus; 29912115Sdyson doff_t slotoffset; /* offset of area with free space */ 30012115Sdyson int slotsize; /* size of area at slotoffset */ 301202283Slulf doff_t i_diroff; /* cached i_diroff value */ 302202283Slulf doff_t i_offset; /* cached i_offset value */ 30312115Sdyson int slotfreespace; /* amount of space free in slot */ 30412115Sdyson int slotneeded; /* size of the entry we're seeking */ 30512115Sdyson int numdirpasses; /* strategy for directory search */ 30612115Sdyson doff_t endsearch; /* offset to end directory search */ 30712115Sdyson doff_t prevoff; /* prev entry dp->i_offset */ 30812115Sdyson struct vnode *pdp; /* saved dp during symlink work */ 30912115Sdyson struct vnode *tdp; /* returned by VFS_VGET */ 31012115Sdyson doff_t enduseful; /* pointer past last used dir slot */ 31112115Sdyson u_long bmask; /* block offset mask */ 31212115Sdyson int namlen, error; 31312115Sdyson struct vnode **vpp = ap->a_vpp; 31412115Sdyson struct componentname *cnp = ap->a_cnp; 31512115Sdyson struct ucred *cred = cnp->cn_cred; 31612115Sdyson int flags = cnp->cn_flags; 31712115Sdyson int nameiop = cnp->cn_nameiop; 318202283Slulf ino_t ino; 319202283Slulf int ltype; 32012115Sdyson 321202283Slulf int DIRBLKSIZ = VTOI(ap->a_dvp)->i_e2fs->e2fs_bsize; 32212115Sdyson 32312115Sdyson bp = NULL; 32412115Sdyson slotoffset = -1; 32512115Sdyson *vpp = NULL; 32612115Sdyson vdp = ap->a_dvp; 32712115Sdyson dp = VTOI(vdp); 32812115Sdyson /* 32912115Sdyson * We now have a segment name to search for, and a directory to search. 33012115Sdyson */ 33112115Sdyson 33212115Sdyson /* 33312115Sdyson * Suppress search for slots unless creating 33412115Sdyson * file and at end of pathname, in which case 33512115Sdyson * we watch for a place to put the new file in 33612115Sdyson * case it doesn't already exist. 33712115Sdyson */ 338202283Slulf ino = 0; 339202283Slulf i_diroff = dp->i_diroff; 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; 363202283Slulf if (nameiop != LOOKUP || i_diroff == 0 || 364202283Slulf i_diroff > dp->i_size) { 36512115Sdyson entryoffsetinblock = 0; 366202283Slulf i_offset = 0; 36712115Sdyson numdirpasses = 1; 36812115Sdyson } else { 369202283Slulf i_offset = i_diroff; 370202283Slulf if ((entryoffsetinblock = i_offset & bmask) && 371202283Slulf (error = ext2_blkatoff(vdp, (off_t)i_offset, NULL, 37296749Siedowse &bp))) 37312115Sdyson return (error); 37412115Sdyson numdirpasses = 2; 37512115Sdyson nchstats.ncs_2passes++; 37612115Sdyson } 377202283Slulf prevoff = i_offset; 378202283Slulf endsearch = roundup2(dp->i_size, DIRBLKSIZ); 37912115Sdyson enduseful = 0; 38012115Sdyson 38112115Sdysonsearchloop: 382202283Slulf while (i_offset < endsearch) { 38312115Sdyson /* 38412115Sdyson * If necessary, get the next directory block. 38512115Sdyson */ 386202283Slulf if ((i_offset & bmask) == 0) { 38712115Sdyson if (bp != NULL) 38812115Sdyson brelse(bp); 38943301Sdillon if ((error = 390202283Slulf ext2_blkatoff(vdp, (off_t)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 */ 411202283Slulf ep = (struct ext2fs_direct_2 *) 41212115Sdyson ((char *)bp->b_data + entryoffsetinblock); 413202283Slulf if (ep->e2d_reclen == 0 || 41412147Sdyson (dirchk && ext2_dirbadentry(vdp, ep, entryoffsetinblock))) { 41512115Sdyson int i; 416202283Slulf ext2_dirbad(dp, i_offset, "mangled entry"); 41712115Sdyson i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)); 418202283Slulf 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) { 430202283Slulf int size = ep->e2d_reclen; 43112115Sdyson 432202283Slulf if (ep->e2d_ino != 0) 433202283Slulf size -= EXT2_DIR_REC_LEN(ep->e2d_namlen); 43412115Sdyson if (size > 0) { 43512115Sdyson if (size >= slotneeded) { 43612115Sdyson slotstatus = FOUND; 437202283Slulf slotoffset = i_offset; 438202283Slulf slotsize = ep->e2d_reclen; 43912115Sdyson } else if (slotstatus == NONE) { 44012115Sdyson slotfreespace += size; 44112115Sdyson if (slotoffset == -1) 442202283Slulf slotoffset = i_offset; 44312115Sdyson if (slotfreespace >= slotneeded) { 44412115Sdyson slotstatus = COMPACT; 445202283Slulf slotsize = i_offset + 446202283Slulf ep->e2d_reclen - slotoffset; 44712115Sdyson } 44812115Sdyson } 44912115Sdyson } 45012115Sdyson } 45112115Sdyson 45212115Sdyson /* 45312115Sdyson * Check for a name match. 45412115Sdyson */ 455202283Slulf if (ep->e2d_ino) { 456202283Slulf namlen = ep->e2d_namlen; 45712115Sdyson if (namlen == cnp->cn_namelen && 458202283Slulf !bcmp(cnp->cn_nameptr, ep->e2d_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 */ 465202283Slulf ino = ep->e2d_ino; 46612115Sdyson goto found; 46712115Sdyson } 46812115Sdyson } 469202283Slulf prevoff = i_offset; 470202283Slulf i_offset += ep->e2d_reclen; 471202283Slulf entryoffsetinblock += ep->e2d_reclen; 472202283Slulf if (ep->e2d_ino) 473202283Slulf enduseful = i_offset; 47412115Sdyson } 47512115Sdyson/* notfound: */ 47612115Sdyson /* 47712115Sdyson * If we started in the middle of the directory and failed 47812115Sdyson * to find our target, we must check the beginning as well. 47912115Sdyson */ 48012115Sdyson if (numdirpasses == 2) { 48112115Sdyson numdirpasses--; 482202283Slulf i_offset = 0; 483202283Slulf endsearch = i_diroff; 48412115Sdyson goto searchloop; 48512115Sdyson } 486202283Slulf dp->i_offset = i_offset; 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) { 512202283Slulf dp->i_offset = roundup2(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 } 521202283Slulf dp->i_endoff = roundup2(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 */ 553202283Slulf if (entryoffsetinblock + EXT2_DIR_REC_LEN(ep->e2d_namlen) 55412115Sdyson > dp->i_size) { 555202283Slulf ext2_dirbad(dp, i_offset, "i_size too small"); 556202283Slulf dp->i_size = entryoffsetinblock+EXT2_DIR_REC_LEN(ep->e2d_namlen); 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) 567202283Slulf dp->i_diroff = i_offset &~ (DIRBLKSIZ - 1); 568202283Slulf dp->i_offset = i_offset; 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; 589202283Slulf if (dp->i_number == ino) { 59012115Sdyson VREF(vdp); 59112115Sdyson *vpp = vdp; 59212115Sdyson return (0); 59312115Sdyson } 594202283Slulf if ((error = VFS_VGET(vdp->v_mount, 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 */ 627202283Slulf if (dp->i_number == ino) 62812115Sdyson return (EISDIR); 629202283Slulf if ((error = VFS_VGET(vdp->v_mount, 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) { 658202283Slulf ltype = VOP_ISLOCKED(pdp); 659175294Sattilio VOP_UNLOCK(pdp, 0); /* race to get the inode */ 660202283Slulf error = VFS_VGET(vdp->v_mount, ino, cnp->cn_lkflags, &tdp); 661202283Slulf vn_lock(pdp, ltype | LK_RETRY); 662145006Sjeff if (error != 0) 66312115Sdyson return (error); 66412115Sdyson *vpp = tdp; 665202283Slulf } else if (dp->i_number == ino) { 66612115Sdyson VREF(vdp); /* we want ourself, ie "." */ 667202283Slulf /* 668202283Slulf * When we lookup "." we still can be asked to lock it 669202283Slulf * differently. 670202283Slulf */ 671202283Slulf ltype = cnp->cn_lkflags & LK_TYPE_MASK; 672202283Slulf if (ltype != VOP_ISLOCKED(vdp)) { 673202283Slulf if (ltype == LK_EXCLUSIVE) 674202283Slulf vn_lock(vdp, LK_UPGRADE | LK_RETRY); 675202283Slulf else /* if (ltype == LK_SHARED) */ 676202283Slulf vn_lock(vdp, LK_DOWNGRADE | LK_RETRY); 677202283Slulf } 67812115Sdyson *vpp = vdp; 67912115Sdyson } else { 680202283Slulf if ((error = VFS_VGET(vdp->v_mount, ino, cnp->cn_lkflags, 68192462Smckusick &tdp)) != 0) 68212115Sdyson return (error); 68312115Sdyson *vpp = tdp; 68412115Sdyson } 68512115Sdyson 68612115Sdyson /* 68712115Sdyson * Insert name into cache if appropriate. 68812115Sdyson */ 68912115Sdyson if (cnp->cn_flags & MAKEENTRY) 69012115Sdyson cache_enter(vdp, *vpp, cnp); 69112115Sdyson return (0); 69212115Sdyson} 69312115Sdyson 69496749Siedowsevoid 69596749Siedowseext2_dirbad(ip, offset, how) 69696749Siedowse struct inode *ip; 69796749Siedowse doff_t offset; 69896749Siedowse char *how; 69996749Siedowse{ 70096749Siedowse struct mount *mp; 70196749Siedowse 70296749Siedowse mp = ITOV(ip)->v_mount; 703202283Slulf if ((mp->mnt_flag & MNT_RDONLY) == 0) 704202283Slulf panic("ext2_dirbad: %s: bad dir ino %lu at offset %ld: %s\n", 705202283Slulf mp->mnt_stat.f_mntonname, (u_long)ip->i_number,(long)offset, how); 706202283Slulf else 70796749Siedowse (void)printf("%s: bad dir ino %lu at offset %ld: %s\n", 708202283Slulf mp->mnt_stat.f_mntonname, (u_long)ip->i_number, (long)offset, how); 709202283Slulf 71096749Siedowse} 71196749Siedowse 71212115Sdyson/* 71312115Sdyson * Do consistency checking on a directory entry: 71412115Sdyson * record length must be multiple of 4 71512115Sdyson * entry must fit in rest of its DIRBLKSIZ block 71612115Sdyson * record must be large enough to contain entry 71712115Sdyson * name is not longer than MAXNAMLEN 71812115Sdyson * name must be as long as advertised, and null terminated 71912115Sdyson */ 72012115Sdyson/* 72112115Sdyson * changed so that it confirms to ext2_check_dir_entry 72212115Sdyson */ 72312159Sbdestatic int 72412115Sdysonext2_dirbadentry(dp, de, entryoffsetinblock) 72512115Sdyson struct vnode *dp; 726202283Slulf struct ext2fs_direct_2 *de; 72712115Sdyson int entryoffsetinblock; 72812115Sdyson{ 729202283Slulf int DIRBLKSIZ = VTOI(dp)->i_e2fs->e2fs_bsize; 73012115Sdyson 731111742Sdes char * error_msg = NULL; 73212115Sdyson 733202283Slulf if (de->e2d_reclen < EXT2_DIR_REC_LEN(1)) 734111742Sdes error_msg = "rec_len is smaller than minimal"; 735202283Slulf else if (de->e2d_reclen % 4 != 0) 736111742Sdes error_msg = "rec_len % 4 != 0"; 737202283Slulf else if (de->e2d_reclen < EXT2_DIR_REC_LEN(de->e2d_namlen)) 738111742Sdes error_msg = "reclen is too small for name_len"; 739202283Slulf else if (entryoffsetinblock + de->e2d_reclen > DIRBLKSIZ) 740111742Sdes error_msg = "directory entry across blocks"; 741111742Sdes /* else LATER 74212115Sdyson if (de->inode > dir->i_sb->u.ext2_sb.s_es->s_inodes_count) 743111742Sdes error_msg = "inode out of bounds"; 74412115Sdyson */ 74512115Sdyson 746111742Sdes if (error_msg != NULL) { 747111742Sdes printf("bad directory entry: %s\n", error_msg); 748111742Sdes printf("offset=%d, inode=%lu, rec_len=%u, name_len=%u\n", 749202283Slulf entryoffsetinblock, (unsigned long)de->e2d_ino, 750202283Slulf de->e2d_reclen, de->e2d_namlen); 751111742Sdes } 752111742Sdes return error_msg == NULL ? 0 : 1; 75312115Sdyson} 75412115Sdyson 75512115Sdyson/* 75612115Sdyson * Write a directory entry after a call to namei, using the parameters 75712115Sdyson * that it left in nameidata. The argument ip is the inode which the new 75812115Sdyson * directory entry will refer to. Dvp is a pointer to the directory to 75912115Sdyson * be written, which was left locked by namei. Remaining parameters 76012115Sdyson * (dp->i_offset, dp->i_count) indicate how the space for the new 76112115Sdyson * entry is to be obtained. 76212115Sdyson */ 76312115Sdysonint 76412115Sdysonext2_direnter(ip, dvp, cnp) 76512115Sdyson struct inode *ip; 76612115Sdyson struct vnode *dvp; 76796752Siedowse struct componentname *cnp; 76812115Sdyson{ 769202283Slulf struct ext2fs_direct_2 *ep, *nep; 77096752Siedowse struct inode *dp; 77112115Sdyson struct buf *bp; 772202283Slulf struct ext2fs_direct_2 newdir; 77312115Sdyson struct iovec aiov; 77412115Sdyson struct uio auio; 77512115Sdyson u_int dsize; 77612115Sdyson int error, loc, newentrysize, spacefree; 77712115Sdyson char *dirbuf; 778202283Slulf int DIRBLKSIZ = ip->i_e2fs->e2fs_bsize; 77912115Sdyson 78012115Sdyson 781153110Sru#ifdef DIAGNOSTIC 78212115Sdyson if ((cnp->cn_flags & SAVENAME) == 0) 78312115Sdyson panic("direnter: missing name"); 78412115Sdyson#endif 78512115Sdyson dp = VTOI(dvp); 786202283Slulf newdir.e2d_ino = ip->i_number; 787202283Slulf newdir.e2d_namlen = cnp->cn_namelen; 788193377Sstas if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs, 789202283Slulf EXT2F_INCOMPAT_FTYPE)) 790202283Slulf newdir.e2d_type = DTTOFT(IFTODT(ip->i_mode)); 79155477Sbde else 792202283Slulf newdir.e2d_type = EXT2_FT_UNKNOWN; 793202283Slulf bcopy(cnp->cn_nameptr, newdir.e2d_name, (unsigned)cnp->cn_namelen + 1); 794202283Slulf newentrysize = EXT2_DIR_REC_LEN(newdir.e2d_namlen); 79512115Sdyson if (dp->i_count == 0) { 79612115Sdyson /* 79712115Sdyson * If dp->i_count is 0, then namei could find no 79812115Sdyson * space in the directory. Here, dp->i_offset will 79912115Sdyson * be on a directory block boundary and we will write the 80012115Sdyson * new entry into a fresh block. 80112115Sdyson */ 80212115Sdyson if (dp->i_offset & (DIRBLKSIZ - 1)) 80312115Sdyson panic("ext2_direnter: newblk"); 80412115Sdyson auio.uio_offset = dp->i_offset; 805202283Slulf newdir.e2d_reclen = DIRBLKSIZ; 80612115Sdyson auio.uio_resid = newentrysize; 80712115Sdyson aiov.iov_len = newentrysize; 80812115Sdyson aiov.iov_base = (caddr_t)&newdir; 80912115Sdyson auio.uio_iov = &aiov; 81012115Sdyson auio.uio_iovcnt = 1; 81112115Sdyson auio.uio_rw = UIO_WRITE; 81212115Sdyson auio.uio_segflg = UIO_SYSSPACE; 81383366Sjulian auio.uio_td = (struct thread *)0; 81412115Sdyson error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred); 81512115Sdyson if (DIRBLKSIZ > 81696749Siedowse VFSTOEXT2(dvp->v_mount)->um_mountp->mnt_stat.f_bsize) 81712115Sdyson /* XXX should grow with balloc() */ 81812115Sdyson panic("ext2_direnter: frag size"); 81912115Sdyson else if (!error) { 820202283Slulf dp->i_size = roundup2(dp->i_size, DIRBLKSIZ); 82112115Sdyson dp->i_flag |= IN_CHANGE; 82212115Sdyson } 82312115Sdyson return (error); 82412115Sdyson } 82512115Sdyson 82612115Sdyson /* 82712115Sdyson * If dp->i_count is non-zero, then namei found space 82812115Sdyson * for the new entry in the range dp->i_offset to 82912115Sdyson * dp->i_offset + dp->i_count in the directory. 83012115Sdyson * To use this space, we may have to compact the entries located 83112115Sdyson * there, by copying them together towards the beginning of the 83212115Sdyson * block, leaving the free space in one usable chunk at the end. 83312115Sdyson */ 83412115Sdyson 83512115Sdyson /* 83612115Sdyson * Increase size of directory if entry eats into new space. 83712115Sdyson * This should never push the size past a new multiple of 83812115Sdyson * DIRBLKSIZE. 83912115Sdyson * 84012115Sdyson * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN. 84112115Sdyson */ 84212115Sdyson if (dp->i_offset + dp->i_count > dp->i_size) 84312115Sdyson dp->i_size = dp->i_offset + dp->i_count; 84412115Sdyson /* 84512115Sdyson * Get the block containing the space for the new directory entry. 84612115Sdyson */ 84796749Siedowse if ((error = ext2_blkatoff(dvp, (off_t)dp->i_offset, &dirbuf, 84896749Siedowse &bp)) != 0) 84912115Sdyson return (error); 85012115Sdyson /* 85112115Sdyson * Find space for the new entry. In the simple case, the entry at 85212115Sdyson * offset base will have the space. If it does not, then namei 85312115Sdyson * arranged that compacting the region dp->i_offset to 85412115Sdyson * dp->i_offset + dp->i_count would yield the 85512115Sdyson * space. 85612115Sdyson */ 857202283Slulf ep = (struct ext2fs_direct_2 *)dirbuf; 858202283Slulf dsize = EXT2_DIR_REC_LEN(ep->e2d_namlen); 859202283Slulf spacefree = ep->e2d_reclen - dsize; 860202283Slulf for (loc = ep->e2d_reclen; loc < dp->i_count; ) { 861202283Slulf nep = (struct ext2fs_direct_2 *)(dirbuf + loc); 862202283Slulf if (ep->e2d_ino) { 86312115Sdyson /* trim the existing slot */ 864202283Slulf ep->e2d_reclen = dsize; 865202283Slulf ep = (struct ext2fs_direct_2 *)((char *)ep + dsize); 86612115Sdyson } else { 86712115Sdyson /* overwrite; nothing there; header is ours */ 86812115Sdyson spacefree += dsize; 86912115Sdyson } 870202283Slulf dsize = EXT2_DIR_REC_LEN(nep->e2d_namlen); 871202283Slulf spacefree += nep->e2d_reclen - dsize; 872202283Slulf loc += nep->e2d_reclen; 87312115Sdyson bcopy((caddr_t)nep, (caddr_t)ep, dsize); 87412115Sdyson } 87512115Sdyson /* 87612115Sdyson * Update the pointer fields in the previous entry (if any), 87712115Sdyson * copy in the new entry, and write out the block. 87812115Sdyson */ 879202283Slulf if (ep->e2d_ino == 0) { 88012115Sdyson if (spacefree + dsize < newentrysize) 88112115Sdyson panic("ext2_direnter: compact1"); 882202283Slulf newdir.e2d_reclen = spacefree + dsize; 88312115Sdyson } else { 88412115Sdyson if (spacefree < newentrysize) 88512115Sdyson panic("ext2_direnter: compact2"); 886202283Slulf newdir.e2d_reclen = spacefree; 887202283Slulf ep->e2d_reclen = dsize; 888202283Slulf ep = (struct ext2fs_direct_2 *)((char *)ep + dsize); 88912115Sdyson } 89012115Sdyson bcopy((caddr_t)&newdir, (caddr_t)ep, (u_int)newentrysize); 891126853Sphk error = bwrite(bp); 89212115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 89312115Sdyson if (!error && dp->i_endoff && dp->i_endoff < dp->i_size) 89496749Siedowse error = ext2_truncate(dvp, (off_t)dp->i_endoff, IO_SYNC, 89583366Sjulian cnp->cn_cred, cnp->cn_thread); 89612115Sdyson return (error); 89712115Sdyson} 89812115Sdyson 89912115Sdyson/* 90012115Sdyson * Remove a directory entry after a call to namei, using 90112115Sdyson * the parameters which it left in nameidata. The entry 90212115Sdyson * dp->i_offset contains the offset into the directory of the 90312115Sdyson * entry to be eliminated. The dp->i_count field contains the 90412115Sdyson * size of the previous record in the directory. If this 90512115Sdyson * is 0, the first entry is being deleted, so we need only 90612115Sdyson * zero the inode number to mark the entry as free. If the 90712115Sdyson * entry is not the first in the directory, we must reclaim 90812115Sdyson * the space of the now empty record by adding the record size 90912115Sdyson * to the size of the previous entry. 91012115Sdyson */ 91112115Sdysonint 91212115Sdysonext2_dirremove(dvp, cnp) 91312115Sdyson struct vnode *dvp; 91412115Sdyson struct componentname *cnp; 91512115Sdyson{ 91696752Siedowse struct inode *dp; 917202283Slulf struct ext2fs_direct_2 *ep, *rep; 91812115Sdyson struct buf *bp; 91912115Sdyson int error; 920111742Sdes 92112115Sdyson dp = VTOI(dvp); 92212115Sdyson if (dp->i_count == 0) { 92312115Sdyson /* 92412115Sdyson * First entry in block: set d_ino to zero. 92512115Sdyson */ 92643301Sdillon if ((error = 92796749Siedowse ext2_blkatoff(dvp, (off_t)dp->i_offset, (char **)&ep, 92896749Siedowse &bp)) != 0) 92912115Sdyson return (error); 930202283Slulf ep->e2d_ino = 0; 931126853Sphk error = bwrite(bp); 93212115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 93312115Sdyson return (error); 93412115Sdyson } 93512115Sdyson /* 93612115Sdyson * Collapse new free space into previous entry. 93712115Sdyson */ 93896749Siedowse if ((error = ext2_blkatoff(dvp, (off_t)(dp->i_offset - dp->i_count), 93943301Sdillon (char **)&ep, &bp)) != 0) 94012115Sdyson return (error); 941202283Slulf 942202283Slulf /* Set 'rep' to the entry being removed. */ 943202283Slulf if (dp->i_count == 0) 944202283Slulf rep = ep; 945202283Slulf else 946202283Slulf rep = (struct ext2fs_direct_2 *)((char *)ep + ep->e2d_reclen); 947202283Slulf ep->e2d_reclen += rep->e2d_reclen; 948126853Sphk error = bwrite(bp); 94912115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 95012115Sdyson return (error); 95112115Sdyson} 95212115Sdyson 95312115Sdyson/* 95412115Sdyson * Rewrite an existing directory entry to point at the inode 95512115Sdyson * supplied. The parameters describing the directory entry are 95612115Sdyson * set up by a call to namei. 95712115Sdyson */ 95812115Sdysonint 95912115Sdysonext2_dirrewrite(dp, ip, cnp) 96012115Sdyson struct inode *dp, *ip; 96112115Sdyson struct componentname *cnp; 96212115Sdyson{ 96312115Sdyson struct buf *bp; 964202283Slulf struct ext2fs_direct_2 *ep; 96512115Sdyson struct vnode *vdp = ITOV(dp); 96612115Sdyson int error; 96712115Sdyson 96896749Siedowse if ((error = ext2_blkatoff(vdp, (off_t)dp->i_offset, (char **)&ep, 96996749Siedowse &bp)) != 0) 97012115Sdyson return (error); 971202283Slulf ep->e2d_ino = ip->i_number; 972193377Sstas if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs, 973202283Slulf EXT2F_INCOMPAT_FTYPE)) 974202283Slulf ep->e2d_type = DTTOFT(IFTODT(ip->i_mode)); 97555477Sbde else 976202283Slulf ep->e2d_type = EXT2_FT_UNKNOWN; 977126853Sphk error = bwrite(bp); 97812115Sdyson dp->i_flag |= IN_CHANGE | IN_UPDATE; 97912115Sdyson return (error); 98012115Sdyson} 98112115Sdyson 98212115Sdyson/* 98312115Sdyson * Check if a directory is empty or not. 98412115Sdyson * Inode supplied must be locked. 98512115Sdyson * 98612115Sdyson * Using a struct dirtemplate here is not precisely 98712115Sdyson * what we want, but better than using a struct direct. 98812115Sdyson * 98912115Sdyson * NB: does not handle corrupted directories. 99012115Sdyson */ 99112115Sdysonint 99212115Sdysonext2_dirempty(ip, parentino, cred) 99396752Siedowse struct inode *ip; 99412115Sdyson ino_t parentino; 99512115Sdyson struct ucred *cred; 99612115Sdyson{ 99796752Siedowse off_t off; 99812115Sdyson struct dirtemplate dbuf; 999202283Slulf struct ext2fs_direct_2 *dp = (struct ext2fs_direct_2 *)&dbuf; 100012115Sdyson int error, count, namlen; 100112115Sdyson#define MINDIRSIZ (sizeof (struct dirtemplate) / 2) 100212115Sdyson 1003202283Slulf for (off = 0; off < ip->i_size; off += dp->e2d_reclen) { 1004101744Srwatson error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, 1005101744Srwatson off, UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK, cred, 1006101941Srwatson NOCRED, &count, (struct thread *)0); 100712115Sdyson /* 100812115Sdyson * Since we read MINDIRSIZ, residual must 100912115Sdyson * be 0 unless we're at end of file. 101012115Sdyson */ 101112115Sdyson if (error || count != 0) 101212115Sdyson return (0); 101312115Sdyson /* avoid infinite loops */ 1014202283Slulf if (dp->e2d_reclen == 0) 101512115Sdyson return (0); 101612115Sdyson /* skip empty entries */ 1017202283Slulf if (dp->e2d_ino == 0) 101812115Sdyson continue; 101912115Sdyson /* accept only "." and ".." */ 1020202283Slulf namlen = dp->e2d_namlen; 102112115Sdyson if (namlen > 2) 102212115Sdyson return (0); 1023202283Slulf if (dp->e2d_name[0] != '.') 102412115Sdyson return (0); 102512115Sdyson /* 102612115Sdyson * At this point namlen must be 1 or 2. 102712115Sdyson * 1 implies ".", 2 implies ".." if second 102812115Sdyson * char is also "." 102912115Sdyson */ 103012115Sdyson if (namlen == 1) 103112115Sdyson continue; 1032202283Slulf if (dp->e2d_name[1] == '.' && dp->e2d_ino == parentino) 103312115Sdyson continue; 103412115Sdyson return (0); 103512115Sdyson } 103612115Sdyson return (1); 103712115Sdyson} 103812115Sdyson 103912115Sdyson/* 104012115Sdyson * Check if source directory is in the path of the target directory. 104112115Sdyson * Target is supplied locked, source is unlocked. 104212115Sdyson * The target is always vput before returning. 104312115Sdyson */ 104412115Sdysonint 104512115Sdysonext2_checkpath(source, target, cred) 104612115Sdyson struct inode *source, *target; 104712115Sdyson struct ucred *cred; 104812115Sdyson{ 104912115Sdyson struct vnode *vp; 105012115Sdyson int error, rootino, namlen; 105112115Sdyson struct dirtemplate dirbuf; 105212115Sdyson 105312115Sdyson vp = ITOV(target); 105412115Sdyson if (target->i_number == source->i_number) { 105512115Sdyson error = EEXIST; 105612115Sdyson goto out; 105712115Sdyson } 105812115Sdyson rootino = ROOTINO; 105912115Sdyson error = 0; 106012115Sdyson if (target->i_number == rootino) 106112115Sdyson goto out; 106212115Sdyson 106312115Sdyson for (;;) { 106412115Sdyson if (vp->v_type != VDIR) { 106512115Sdyson error = ENOTDIR; 106612115Sdyson break; 106712115Sdyson } 106812115Sdyson error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf, 106912115Sdyson sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE, 1070194296Skib IO_NODELOCKED | IO_NOMACCHECK, cred, NOCRED, NULL, 1071194296Skib NULL); 107212115Sdyson if (error != 0) 107312115Sdyson break; 107457710Sbde namlen = dirbuf.dotdot_type; /* like ufs little-endian */ 107512115Sdyson if (namlen != 2 || 107612115Sdyson dirbuf.dotdot_name[0] != '.' || 107712115Sdyson dirbuf.dotdot_name[1] != '.') { 107812115Sdyson error = ENOTDIR; 107912115Sdyson break; 108012115Sdyson } 108112115Sdyson if (dirbuf.dotdot_ino == source->i_number) { 108212115Sdyson error = EINVAL; 108312115Sdyson break; 108412115Sdyson } 108512115Sdyson if (dirbuf.dotdot_ino == rootino) 108612115Sdyson break; 108712115Sdyson vput(vp); 108892462Smckusick if ((error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino, 108992462Smckusick LK_EXCLUSIVE, &vp)) != 0) { 109012115Sdyson vp = NULL; 109112115Sdyson break; 109212115Sdyson } 109312115Sdyson } 109412115Sdyson 109512115Sdysonout: 109612115Sdyson if (error == ENOTDIR) 109712115Sdyson printf("checkpath: .. not a directory\n"); 109812115Sdyson if (vp != NULL) 109912115Sdyson vput(vp); 110012115Sdyson return (error); 110112115Sdyson} 1102