diff options
author | Jason Downs <downsj@cvs.openbsd.org> | 1997-05-30 05:18:52 +0000 |
---|---|---|
committer | Jason Downs <downsj@cvs.openbsd.org> | 1997-05-30 05:18:52 +0000 |
commit | d908a83a81adfc346b70e30ce4fc7853f62e0df6 (patch) | |
tree | db8ae64ff36c9b327a35d028e87f85c91ea87565 | |
parent | 033264ad4b94e60bb5c7599637dfd6ef0cfe6bd5 (diff) |
Non-GPLd ext2fs code from Manuel, unmodified.
-rw-r--r-- | sys/ufs/ext2fs/ext2fs.h | 236 | ||||
-rw-r--r-- | sys/ufs/ext2fs/ext2fs_alloc.c | 650 | ||||
-rw-r--r-- | sys/ufs/ext2fs/ext2fs_balloc.c | 247 | ||||
-rw-r--r-- | sys/ufs/ext2fs/ext2fs_bmap.c | 222 | ||||
-rw-r--r-- | sys/ufs/ext2fs/ext2fs_dinode.h | 132 | ||||
-rw-r--r-- | sys/ufs/ext2fs/ext2fs_dir.h | 114 | ||||
-rw-r--r-- | sys/ufs/ext2fs/ext2fs_extern.h | 154 | ||||
-rw-r--r-- | sys/ufs/ext2fs/ext2fs_inode.c | 528 | ||||
-rw-r--r-- | sys/ufs/ext2fs/ext2fs_lookup.c | 1066 | ||||
-rw-r--r-- | sys/ufs/ext2fs/ext2fs_readwrite.c | 298 | ||||
-rw-r--r-- | sys/ufs/ext2fs/ext2fs_subr.c | 120 | ||||
-rw-r--r-- | sys/ufs/ext2fs/ext2fs_vfsops.c | 996 | ||||
-rw-r--r-- | sys/ufs/ext2fs/ext2fs_vnops.c | 1541 |
13 files changed, 6304 insertions, 0 deletions
diff --git a/sys/ufs/ext2fs/ext2fs.h b/sys/ufs/ext2fs/ext2fs.h new file mode 100644 index 00000000000..136267f4df3 --- /dev/null +++ b/sys/ufs/ext2fs/ext2fs.h @@ -0,0 +1,236 @@ +/* $NetBSD: fs.h,v 1.6 1995/04/12 21:21:02 mycroft Exp $ */ + +/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */ + +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)fs.h 8.10 (Berkeley) 10/27/94 + */ + +/* + * Each disk drive contains some number of file systems. + * A file system consists of a number of cylinder groups. + * Each cylinder group has inodes and data. + * + * A file system is described by its super-block, which in turn + * describes the cylinder groups. The super-block is critical + * data and is replicated in each cylinder group to protect against + * catastrophic loss. This is done at `newfs' time and the critical + * super-block data does not change, so the copies need not be + * referenced further unless disaster strikes. + * + * The first boot and super blocks are given in absolute disk addresses. + * The byte-offset forms are preferred, as they don't imply a sector size. + */ +#define BBSIZE 1024 +#define SBSIZE 1024 +#define BBOFF ((off_t)(0)) +#define SBOFF ((off_t)(BBOFF + BBSIZE)) +#define BBLOCK ((daddr_t)(0)) +#define SBLOCK ((daddr_t)(BBLOCK + BBSIZE / DEV_BSIZE)) + +/* + * Addresses stored in inodes are capable of addressing blocks + * XXX + */ + +/* + * MINBSIZE is the smallest allowable block size. + * MINBSIZE must be big enough to hold a cylinder group block, + * thus changes to (struct cg) must keep its size within MINBSIZE. + * Note that super blocks are always of size SBSIZE, + * and that both SBSIZE and MAXBSIZE must be >= MINBSIZE. + */ +#define LOG_MINBSIZE 10 +#define MINBSIZE (1 << LOG_MINBSIZE) + +/* + * The path name on which the file system is mounted is maintained + * in fs_fsmnt. MAXMNTLEN defines the amount of space allocated in + * the super block for this name. + */ +#define MAXMNTLEN 512 + +/* + * MINFREE gives the minimum acceptable percentage of file system + * blocks which may be free. If the freelist drops below this level + * only the superuser may continue to allocate blocks. This may + * be set to 0 if no reserve of free blocks is deemed necessary, + * however throughput drops by fifty percent if the file system + * is run at between 95% and 100% full; thus the minimum default + * value of fs_minfree is 5%. However, to get good clustering + * performance, 10% is a better choice. hence we use 10% as our + * default value. With 10% free space, fragmentation is not a + * problem, so we choose to optimize for time. + */ +#define MINFREE 5 + +/* + * Super block for an ext2fs file system. + */ +struct ext2fs { + u_int32_t e2fs_icount; /* Inode count */ + u_int32_t e2fs_bcount; /* blocks count */ + u_int32_t e2fs_rbcount; /* reserved blocks count */ + u_int32_t e2fs_fbcount; /* free blocks count */ + u_int32_t e2fs_ficount; /* free inodes count */ + u_int32_t e2fs_first_dblock; /* first data block */ + u_int32_t e2fs_log_bsize; /* block size = 1024*(2^e2fs_log_bsize) */ + u_int32_t e2fs_fsize; /* fragment size */ + u_int32_t e2fs_bpg; /* blocks per group */ + u_int32_t e2fs_fpg; /* frags per group */ + u_int32_t e2fs_ipg; /* inodes per group */ + u_int32_t e2fs_mtime; /* mount time */ + u_int32_t e2fs_wtime; /* write time */ + u_int16_t e2fs_mnt_count; /* mount count */ + u_int16_t e2fs_max_mnt_count; /* max mount count */ + u_int16_t e2fs_magic; /* magic number */ + u_int16_t e2fs_state; /* file system state */ + u_int16_t e2fs_beh; /* behavior on errors */ + u_int16_t reserved; + u_int32_t e2fs_lastfsck; /* time of last fsck */ + u_int32_t e2fs_fsckintv; /* max time between fscks */ + u_int32_t e2fs_creator; /* creator OS */ + u_int32_t e2fs_rev; /* revision level */ + u_int16_t e2fs_ruid; /* default uid for reserved blocks */ + u_int16_t e2fs_rgid; /* default gid for reserved blocks */ + u_int32_t reserved2[235]; +}; + + +/* in-memory data for ext2fs */ +struct m_ext2fs { + struct ext2fs e2fs; + u_char e2fs_fsmnt[MAXMNTLEN]; /* name mounted on */ + int8_t e2fs_ronly; /* mounted read-only flag */ + int8_t e2fs_fmod; /* super block modified flag */ + int32_t e2fs_bsize; /* block size */ + int32_t e2fs_bshift; /* ``lblkno'' calc of logical blkno */ + int32_t e2fs_bmask; /* ``blkoff'' calc of blk offsets */ + int64_t e2fs_qbmask; /* ~fs_bmask - for use with quad size */ + int32_t e2fs_fsbtodb; /* fsbtodb and dbtofsb shift constant */ + int32_t e2fs_ncg; /* number of cylinder groups */ + int32_t e2fs_ngdb; /* number of group descriptor block */ + int32_t e2fs_ipb; /* number of inodes per block */ + int32_t e2fs_itpg; /* number of inode table per group */ + struct ext2_gd *e2fs_gd; /* group descripors */ +}; + + + +/* + * Filesystem identification + */ +#define E2FS_MAGIC 0xef53 /* the ext2fs magic number */ +#define E2FS_REV 0 /* revision level */ + +/* + * OS identification + */ +#define E2FS_OS_LINUX 0 +#define E2FS_OS_HURD 1 +#define E2FS_OS_MASIX 2 + +/* + * Filesystem clean flags + */ +#define E2FS_ISCLEAN 0x01 +#define E2FS_ERRORS 0x02 + +/* ext2 file system block group descriptor */ + +struct ext2_gd { + u_int32_t ext2bgd_b_bitmap; /* blocks bitmap block */ + u_int32_t ext2bgd_i_bitmap; /* inodes bitmap block */ + u_int32_t ext2bgd_i_tables; /* inodes table block */ + u_int16_t ext2bgd_nbfree; /* number of free blocks */ + u_int16_t ext2bgd_nifree; /* number of free inodes */ + u_int16_t ext2bgd_ndirs; /* number of directories */ + u_int16_t reserved; + u_int32_t reserved2[3]; + +}; + +/* + * Turn file system block numbers into disk block addresses. + * This maps file system blocks to device size blocks. + */ +#define fsbtodb(fs, b) ((b) << (fs)->e2fs_fsbtodb) +#define dbtofsb(fs, b) ((b) >> (fs)->e2fs_fsbtodb) + +/* + * Macros for handling inode numbers: + * inode number to file system block offset. + * inode number to cylinder group number. + * inode number to file system block address. + */ +#define ino_to_cg(fs, x) (((x) - 1) / (fs)->e2fs.e2fs_ipg) +#define ino_to_fsba(fs, x) \ + ((fs)->e2fs_gd[ino_to_cg(fs, x)].ext2bgd_i_tables + \ + (((x)-1) % (fs)->e2fs.e2fs_ipg)/(fs)->e2fs_ipb) +#define ino_to_fsbo(fs, x) (((x)-1) % (fs)->e2fs_ipb) + +/* + * Give cylinder group number for a file system block. + * Give cylinder group block number for a file system block. + */ +#define dtog(fs, d) (((d) - (fs)->e2fs.e2fs_first_dblock) / (fs)->e2fs.e2fs_fpg) +#define dtogd(fs, d) \ + (((d) - (fs)->e2fs.e2fs_first_dblock) % (fs)->e2fs.e2fs_fpg) + +/* + * The following macros optimize certain frequently calculated + * quantities by using shifts and masks in place of divisions + * modulos and multiplications. + */ +#define blkoff(fs, loc) /* calculates (loc % fs->e2fs_bsize) */ \ + ((loc) & (fs)->e2fs_qbmask) +#define lblktosize(fs, blk) /* calculates (blk * fs->e2fs_bsize) */ \ + ((blk) << (fs)->e2fs_bshift) +#define lblkno(fs, loc) /* calculates (loc / fs->e2fs_bsize) */ \ + ((loc) >> (fs)->e2fs_bshift) +#define blkroundup(fs, size) /* calculates roundup(size, fs->e2fs_bsize) */ \ + (((size) + (fs)->e2fs_qbmask) & (fs)->e2fs_bmask) +#define fragroundup(fs, size) /* calculates roundup(size, fs->e2fs_bsize) */ \ + (((size) + (fs)->e2fs_qbmask) & (fs)->e2fs_bmask) +/* + * Determine the number of available frags given a + * percentage to hold in reserve. + */ +#define freespace(fs) \ + ((fs)->e2fs.e2fs_fbcount - (fs)->e2fs.e2fs_rbcount) + +/* + * Number of indirects in a file system block. + */ +#define NINDIR(fs) ((fs)->e2fs_bsize / sizeof(u_int32_t)) diff --git a/sys/ufs/ext2fs/ext2fs_alloc.c b/sys/ufs/ext2fs/ext2fs_alloc.c new file mode 100644 index 00000000000..14e93c7bf79 --- /dev/null +++ b/sys/ufs/ext2fs/ext2fs_alloc.c @@ -0,0 +1,650 @@ +/* $NetBSD: ffs_alloc.c,v 1.13 1996/10/12 21:58:44 christos Exp $ */ + +/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */ + +/* + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ffs_alloc.c 8.11 (Berkeley) 10/27/94 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/vnode.h> +#include <sys/mount.h> +#include <sys/kernel.h> +#include <sys/syslog.h> + +#include <vm/vm.h> + +#include <ufs/ufs/quota.h> +#include <ufs/ufs/inode.h> +#include <ufs/ufs/ufs_extern.h> + +#include <ufs/ext2fs/ext2fs.h> +#include <ufs/ext2fs/ext2fs_extern.h> + +u_long ext2gennumber; + +static daddr_t ext2fs_alloccg __P((struct inode *, int, daddr_t, int)); +static u_long ext2fs_dirpref __P((struct m_ext2fs *)); +static void ext2fs_fserr __P((struct m_ext2fs *, u_int, char *)); +static u_long ext2fs_hashalloc __P((struct inode *, int, long, int, + daddr_t (*)(struct inode *, int, daddr_t, + int))); +static daddr_t ext2fs_nodealloccg __P((struct inode *, int, daddr_t, int)); +static daddr_t ext2fs_mapsearch __P((struct m_ext2fs *, char *, daddr_t)); + +/* + * Allocate a block in the file system. + * + * A preference may be optionally specified. If a preference is given + * the following hierarchy is used to allocate a block: + * 1) allocate the requested block. + * 2) allocate a rotationally optimal block in the same cylinder. + * 3) allocate a block in the same cylinder group. + * 4) quadradically rehash into other cylinder groups, until an + * available block is located. + * If no block preference is given the following heirarchy is used + * to allocate a block: + * 1) allocate a block in the cylinder group that contains the + * inode for the file. + * 2) quadradically rehash into other cylinder groups, until an + * available block is located. + */ +int +ext2fs_alloc(ip, lbn, bpref, cred, bnp) + register struct inode *ip; + daddr_t lbn, bpref; + struct ucred *cred; + daddr_t *bnp; +{ + register struct m_ext2fs *fs; + daddr_t bno; + int cg; + + *bnp = 0; + fs = ip->i_e2fs; +#ifdef DIAGNOSTIC + if (cred == NOCRED) + panic("ext2fs_alloc: missing credential\n"); +#endif /* DIAGNOSTIC */ + if (fs->e2fs.e2fs_fbcount == 0) + goto nospace; + if (cred->cr_uid != 0 && freespace(fs) <= 0) + goto nospace; + if (bpref >= fs->e2fs.e2fs_bcount) + bpref = 0; + if (bpref == 0) + cg = ino_to_cg(fs, ip->i_number); + else + cg = dtog(fs, bpref); + bno = (daddr_t)ext2fs_hashalloc(ip, cg, (long)bpref, fs->e2fs_bsize, + ext2fs_alloccg); + if (bno > 0) { + ip->i_e2fs_nblock += btodb(fs->e2fs_bsize); + ip->i_flag |= IN_CHANGE | IN_UPDATE; + *bnp = bno; + return (0); + } +nospace: + ext2fs_fserr(fs, cred->cr_uid, "file system full"); + uprintf("\n%s: write failed, file system is full\n", fs->e2fs_fsmnt); + return (ENOSPC); +} + +/* + * Allocate an inode in the file system. + * + * If allocating a directory, use ext2fs_dirpref to select the inode. + * If allocating in a directory, the following hierarchy is followed: + * 1) allocate the preferred inode. + * 2) allocate an inode in the same cylinder group. + * 3) quadradically rehash into other cylinder groups, until an + * available inode is located. + * If no inode preference is given the following heirarchy is used + * to allocate an inode: + * 1) allocate an inode in cylinder group 0. + * 2) quadradically rehash into other cylinder groups, until an + * available inode is located. + */ +int +ext2fs_valloc(v) + void *v; +{ + struct vop_valloc_args /* { + struct vnode *a_pvp; + int a_mode; + struct ucred *a_cred; + struct vnode **a_vpp; + } */ *ap = v; + register struct vnode *pvp = ap->a_pvp; + register struct inode *pip; + register struct m_ext2fs *fs; + register struct inode *ip; + mode_t mode = ap->a_mode; + ino_t ino, ipref; + int cg, error; + + *ap->a_vpp = NULL; + pip = VTOI(pvp); + fs = pip->i_e2fs; + if (fs->e2fs.e2fs_ficount == 0) + goto noinodes; + + if ((mode & IFMT) == IFDIR) + cg = ext2fs_dirpref(fs); + else + cg = ino_to_cg(fs, pip->i_number); + ipref = cg * fs->e2fs.e2fs_ipg; + ino = (ino_t)ext2fs_hashalloc(pip, cg, (long)ipref, mode, ext2fs_nodealloccg); + if (ino == 0) + goto noinodes; + error = VFS_VGET(pvp->v_mount, ino, ap->a_vpp); + if (error) { + VOP_VFREE(pvp, ino, mode); + return (error); + } + ip = VTOI(*ap->a_vpp); + if (ip->i_e2fs_mode && ip->i_e2fs_nlink != 0) { + printf("mode = 0%o, nlinks %d, inum = %d, fs = %s\n", + ip->i_e2fs_mode, ip->i_e2fs_nlink, ip->i_number, fs->e2fs_fsmnt); + panic("ext2fs_valloc: dup alloc"); + } + + bzero(&(ip->i_din.e2fs_din), sizeof(struct ext2fs_dinode)); + + /* + * Set up a new generation number for this inode. + */ + if (++ext2gennumber < (u_long)time.tv_sec) + ext2gennumber = time.tv_sec; + ip->i_e2fs_gen = ext2gennumber; + return (0); +noinodes: + ext2fs_fserr(fs, ap->a_cred->cr_uid, "out of inodes"); + uprintf("\n%s: create/symlink failed, no inodes free\n", fs->e2fs_fsmnt); + return (ENOSPC); +} + +/* + * Find a cylinder to place a directory. + * + * The policy implemented by this algorithm is to select from + * among those cylinder groups with above the average number of + * free inodes, the one with the smallest number of directories. + */ +static u_long +ext2fs_dirpref(fs) + register struct m_ext2fs *fs; +{ + int cg, maxspace, mincg, avgifree; + + avgifree = fs->e2fs.e2fs_ficount / fs->e2fs_ncg; + maxspace = 0; + mincg = -1; + for (cg = 0; cg < fs->e2fs_ncg; cg++) + if ( fs->e2fs_gd[cg].ext2bgd_nifree >= avgifree) { + if (mincg == -1 || fs->e2fs_gd[cg].ext2bgd_nbfree > maxspace) { + mincg = cg; + maxspace = fs->e2fs_gd[cg].ext2bgd_nbfree; + } + } + return mincg; +} + +/* + * Select the desired position for the next block in a file. The file is + * logically divided into sections. The first section is composed of the + * direct blocks. Each additional section contains fs_maxbpg blocks. + * + * If no blocks have been allocated in the first section, the policy is to + * request a block in the same cylinder group as the inode that describes + * the file. Otherwise, the policy is to try to allocate the blocks + * contigously. The two fields of the ext2 inode extention (see + * ufs/ufs/inode.h) help this. + */ +daddr_t +ext2fs_blkpref(ip, lbn, indx, bap) + struct inode *ip; + daddr_t lbn; + int indx; + daddr_t *bap; +{ + register struct m_ext2fs *fs; + register int cg, i; + + fs = ip->i_e2fs; + /* + * if we are doing contigous lbn allocation, try to alloc blocks + * contigously on disk + */ + + if ( ip->i_e2fs_last_blk && lbn == ip->i_e2fs_last_lblk + 1) { + return ip->i_e2fs_last_blk + 1; + } + + /* + * bap, if provided, gives us a list of blocks to which we want to + * stay close + */ + + if (bap) { + for (i = indx; i >= 0 ; i--) { + if (bap[i]) { + return bap[i] + 1; + } + } + } + + /* fall back to the first block of the cylinder containing the inode */ + + cg = ino_to_cg(fs, ip->i_number); + return fs->e2fs.e2fs_bpg * cg + fs->e2fs.e2fs_first_dblock + 1; +} + +/* + * Implement the cylinder overflow algorithm. + * + * The policy implemented by this algorithm is: + * 1) allocate the block in its requested cylinder group. + * 2) quadradically rehash on the cylinder group number. + * 3) brute force search for a free block. + */ +static u_long +ext2fs_hashalloc(ip, cg, pref, size, allocator) + struct inode *ip; + int cg; + long pref; + int size; /* size for data blocks, mode for inodes */ + daddr_t (*allocator) __P((struct inode *, int, daddr_t, int)); +{ + register struct m_ext2fs *fs; + long result; + int i, icg = cg; + + fs = ip->i_e2fs; + /* + * 1: preferred cylinder group + */ + result = (*allocator)(ip, cg, pref, size); + if (result) + return (result); + /* + * 2: quadratic rehash + */ + for (i = 1; i < fs->e2fs_ncg; i *= 2) { + cg += i; + if (cg >= fs->e2fs_ncg) + cg -= fs->e2fs_ncg; + result = (*allocator)(ip, cg, 0, size); + if (result) + return (result); + } + /* + * 3: brute force search + * Note that we start at i == 2, since 0 was checked initially, + * and 1 is always checked in the quadratic rehash. + */ + cg = (icg + 2) % fs->e2fs_ncg; + for (i = 2; i < fs->e2fs_ncg; i++) { + result = (*allocator)(ip, cg, 0, size); + if (result) + return (result); + cg++; + if (cg == fs->e2fs_ncg) + cg = 0; + } + return (NULL); +} + +/* + * Determine whether a block can be allocated. + * + * Check to see if a block of the appropriate size is available, + * and if it is, allocate it. + */ + +static daddr_t +ext2fs_alloccg(ip, cg, bpref, size) + struct inode *ip; + int cg; + daddr_t bpref; + int size; +{ + register struct m_ext2fs *fs; + register char *bbp; + struct buf *bp; + int error, bno, start, end, loc; + + fs = ip->i_e2fs; + if (fs->e2fs_gd[cg].ext2bgd_nbfree == 0) + return (NULL); + error = bread(ip->i_devvp, fsbtodb(fs, fs->e2fs_gd[cg].ext2bgd_b_bitmap), + (int)fs->e2fs_bsize, NOCRED, &bp); + if (error) { + brelse(bp); + return (NULL); + } + bbp = (char *)bp->b_data; + + if (dtog(fs, bpref) != cg) + bpref = 0; + if (bpref != 0) { + bpref = dtogd(fs, bpref); + /* + * if the requested block is available, use it + */ + if (isclr(bbp, bpref)) { + bno = bpref; + goto gotit; + } + } + /* + * no blocks in the requested cylinder, so take next + * available one in this cylinder group. + * first try to get 8 contigous blocks, then fall back to a single + * block. + */ + if (bpref) + start = dtogd(fs, bpref) / NBBY; + else + start = 0; + end = howmany(fs->e2fs.e2fs_fpg, NBBY) - start; + for (loc = start; loc < end; loc++) { + if (bbp[loc] == 0) { + bno = loc * NBBY; + goto gotit; + } + } + for (loc = 0; loc < start; loc++) { + if (bbp[loc] == 0) { + bno = loc * NBBY; + goto gotit; + } + } + + bno = ext2fs_mapsearch(fs, bbp, bpref); + if (bno < 0) + return (NULL); +gotit: +#ifdef DIAGNOSTIC + if (isset(bbp, (long)bno)) { + printf("ext2fs_alloccgblk: cg=%d bno=%d fs=%s\n", + cg, bno, fs->e2fs_fsmnt); + panic("ext2fs_valloc: dup alloc"); + } +#endif + setbit(bbp, (long)bno); + fs->e2fs.e2fs_fbcount--; + fs->e2fs_gd[cg].ext2bgd_nbfree--; + fs->e2fs_fmod = 1; + bdwrite(bp); + return (cg * fs->e2fs.e2fs_fpg + fs->e2fs.e2fs_first_dblock + bno); +} + +/* + * Determine whether an inode can be allocated. + * + * Check to see if an inode is available, and if it is, + * allocate it using the following policy: + * 1) allocate the requested inode. + * 2) allocate the next available inode after the requested + * inode in the specified cylinder group. + */ +static daddr_t +ext2fs_nodealloccg(ip, cg, ipref, mode) + struct inode *ip; + int cg; + daddr_t ipref; + int mode; +{ + register struct m_ext2fs *fs; + register char *ibp; + struct buf *bp; + int error, start, len, loc, map, i; + + ipref--; /* to avoid a lot of (ipref -1) */ + fs = ip->i_e2fs; + if (fs->e2fs_gd[cg].ext2bgd_nifree == 0) + return (NULL); + error = bread(ip->i_devvp, fsbtodb(fs, fs->e2fs_gd[cg].ext2bgd_i_bitmap), + (int)fs->e2fs_bsize, NOCRED, &bp); + if (error) { + brelse(bp); + return (NULL); + } + ibp = (char *)bp->b_data; + if (ipref) { + ipref %= fs->e2fs.e2fs_ipg; + if (isclr(ibp, ipref)) + goto gotit; + } + start = ipref / NBBY; + len = howmany(fs->e2fs.e2fs_ipg - ipref, NBBY); + loc = skpc(0xff, len, &ibp[start]); + if (loc == 0) { + len = start + 1; + start = 0; + loc = skpc(0xff, len, &ibp[0]); + if (loc == 0) { + printf("cg = %d, ipref = %d, fs = %s\n", + cg, ipref, fs->e2fs_fsmnt); + panic("ext2fs_nodealloccg: map corrupted"); + /* NOTREACHED */ + } + } + i = start + len - loc; + map = ibp[i]; + ipref = i * NBBY; + for (i = 1; i < (1 << NBBY); i <<= 1, ipref++) { + if ((map & i) == 0) { + goto gotit; + } + } + printf("fs = %s\n", fs->e2fs_fsmnt); + panic("ext2fs_nodealloccg: block not in map"); + /* NOTREACHED */ +gotit: + setbit(ibp, ipref); + fs->e2fs.e2fs_ficount--; + fs->e2fs_gd[cg].ext2bgd_nifree--; + fs->e2fs_fmod = 1; + if ((mode & IFMT) == IFDIR) { + fs->e2fs_gd[cg].ext2bgd_ndirs++; + } + bdwrite(bp); + return (cg * fs->e2fs.e2fs_ipg + ipref +1); +} + +/* + * Free a block. + * + * The specified block is placed back in the + * free map. + */ +void +ext2fs_blkfree(ip, bno) + register struct inode *ip; + daddr_t bno; +{ + register struct m_ext2fs *fs; + register char *bbp; + struct buf *bp; + int error, cg; + + fs = ip->i_e2fs; + cg = dtog(fs, bno); + if ((u_int)bno >= fs->e2fs.e2fs_bcount) { + printf("bad block %d, ino %d\n", bno, ip->i_number); + ext2fs_fserr(fs, ip->i_e2fs_uid, "bad block"); + return; + } + error = bread(ip->i_devvp, fsbtodb(fs, fs->e2fs_gd[cg].ext2bgd_b_bitmap), + (int)fs->e2fs_bsize, NOCRED, &bp); + if (error) { + brelse(bp); + return; + } + bbp = (char *)bp->b_data; + bno = dtogd(fs, bno); + if (isclr(bbp, bno)) { + printf("dev = 0x%x, block = %d, fs = %s\n", + ip->i_dev, bno, fs->e2fs_fsmnt); + panic("blkfree: freeing free block"); + } + clrbit(bbp, bno); + fs->e2fs.e2fs_fbcount++; + fs->e2fs_gd[cg].ext2bgd_nbfree++; + + fs->e2fs_fmod = 1; + bdwrite(bp); +} + +/* + * Free an inode. + * + * The specified inode is placed back in the free map. + */ +int +ext2fs_vfree(v) + void *v; +{ + struct vop_vfree_args /* { + struct vnode *a_pvp; + ino_t a_ino; + int a_mode; + } */ *ap = v; + register struct m_ext2fs *fs; + register char *ibp; + register struct inode *pip; + ino_t ino = ap->a_ino; + struct buf *bp; + int error, cg; + + pip = VTOI(ap->a_pvp); + fs = pip->i_e2fs; + if ((u_int)ino >= fs->e2fs.e2fs_icount || (u_int)ino < EXT2_FIRSTINO) + panic("ifree: range: dev = 0x%x, ino = %d, fs = %s\n", + pip->i_dev, ino, fs->e2fs_fsmnt); + cg = ino_to_cg(fs, ino); + error = bread(pip->i_devvp, fsbtodb(fs, fs->e2fs_gd[cg].ext2bgd_i_bitmap), + (int)fs->e2fs_bsize, NOCRED, &bp); + if (error) { + brelse(bp); + return (0); + } + ibp = (char *)bp->b_data; + ino = (ino - 1) % fs->e2fs.e2fs_ipg; + if (isclr(ibp, ino)) { + printf("dev = 0x%x, ino = %d, fs = %s\n", + pip->i_dev, ino, fs->e2fs_fsmnt); + if (fs->e2fs_ronly == 0) + panic("ifree: freeing free inode"); + } + clrbit(ibp, ino); + fs->e2fs.e2fs_ficount++; + fs->e2fs_gd[cg].ext2bgd_nifree++; + if ((ap->a_mode & IFMT) == IFDIR) { + fs->e2fs_gd[cg].ext2bgd_ndirs--; + } + fs->e2fs_fmod = 1; + bdwrite(bp); + return (0); +} + +/* + * Find a block in the specified cylinder group. + * + * It is a panic if a request is made to find a block if none are + * available. + */ + +static daddr_t +ext2fs_mapsearch(fs, bbp, bpref) + register struct m_ext2fs *fs; + register char *bbp; + daddr_t bpref; +{ + daddr_t bno; + int start, len, loc, i, map; + + /* + * find the fragment by searching through the free block + * map for an appropriate bit pattern + */ + if (bpref) + start = dtogd(fs, bpref) / NBBY; + else + start = 0; + len = howmany(fs->e2fs.e2fs_fpg, NBBY) - start; + loc = skpc(0xff, len, &bbp[start]); + if (loc == 0) { + len = start + 1; + start = 0; + loc = skpc(0xff, len, &bbp[start]); + if (loc == 0) { + printf("start = %d, len = %d, fs = %s\n", + start, len, fs->e2fs_fsmnt); + panic("ext2fs_alloccg: map corrupted"); + /* NOTREACHED */ + } + } + i = start + len - loc; + map = bbp[i]; + bno = i * NBBY; + for (i = 1; i < (1 << NBBY); i <<= 1, bno++) { + if ((map & i) == 0) + return (bno); + } + printf("fs = %s\n", fs->e2fs_fsmnt); + panic("ext2fs_mapsearch: block not in map"); + /* NOTREACHED */ +} + +/* + * Fserr prints the name of a file system with an error diagnostic. + * + * The form of the error message is: + * fs: error message + */ +static void +ext2fs_fserr(fs, uid, cp) + struct m_ext2fs *fs; + u_int uid; + char *cp; +{ + + log(LOG_ERR, "uid %d on %s: %s\n", uid, fs->e2fs_fsmnt, cp); +} diff --git a/sys/ufs/ext2fs/ext2fs_balloc.c b/sys/ufs/ext2fs/ext2fs_balloc.c new file mode 100644 index 00000000000..af1fe8c6803 --- /dev/null +++ b/sys/ufs/ext2fs/ext2fs_balloc.c @@ -0,0 +1,247 @@ +/* $NetBSD: ffs_balloc.c,v 1.3 1996/02/09 22:22:21 christos Exp $ */ + +/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */ + +/* + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ffs_balloc.c 8.4 (Berkeley) 9/23/93 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/file.h> +#include <sys/vnode.h> + +#include <vm/vm.h> + +#include <ufs/ufs/quota.h> +#include <ufs/ufs/inode.h> +#include <ufs/ufs/ufs_extern.h> + +#include <ufs/ext2fs/ext2fs.h> +#include <ufs/ext2fs/ext2fs_extern.h> + +/* + * Balloc defines the structure of file system storage + * by allocating the physical blocks on a device given + * the inode and the logical block number in a file. + */ +int +ext2fs_balloc(ip, bn, size, cred, bpp, flags) + register struct inode *ip; + register daddr_t bn; + int size; + struct ucred *cred; + struct buf **bpp; + int flags; +{ + register struct m_ext2fs *fs; + register daddr_t nb; + struct buf *bp, *nbp; + struct vnode *vp = ITOV(ip); + struct indir indirs[NIADDR + 2]; + daddr_t newb, lbn, *bap, pref; + int num, i, error; + + *bpp = NULL; + if (bn < 0) + return (EFBIG); + fs = ip->i_e2fs; + lbn = bn; + + /* + * The first NDADDR blocks are direct blocks + */ + if (bn < NDADDR) { + nb = ip->i_e2fs_blocks[bn]; + if (nb != 0) { + error = bread(vp, bn, fs->e2fs_bsize, NOCRED, &bp); + if (error) { + brelse(bp); + return (error); + } + *bpp = bp; + return (0); + } else { + error = ext2fs_alloc(ip, bn, + ext2fs_blkpref(ip, bn, (int)bn, &ip->i_e2fs_blocks[0]), + cred, &newb); + if (error) + return (error); + ip->i_e2fs_last_lblk = lbn; + ip->i_e2fs_last_blk = newb; + bp = getblk(vp, bn, fs->e2fs_bsize, 0, 0); + bp->b_blkno = fsbtodb(fs, newb); + if (flags & B_CLRBUF) + clrbuf(bp); + } + ip->i_e2fs_blocks[bn] = dbtofsb(fs, bp->b_blkno); + ip->i_flag |= IN_CHANGE | IN_UPDATE; + *bpp = bp; + return (0); + } + /* + * Determine the number of levels of indirection. + */ + pref = 0; + if ((error = ufs_getlbns(vp, bn, indirs, &num)) != 0) + return(error); +#ifdef DIAGNOSTIC + if (num < 1) + panic ("ext2fs_balloc: ufs_getlbns returned indirect block\n"); +#endif + /* + * Fetch the first indirect block allocating if necessary. + */ + --num; + nb = ip->i_e2fs_blocks[NDADDR + indirs[0].in_off]; + if (nb == 0) { + pref = ext2fs_blkpref(ip, lbn, 0, (daddr_t *)0); + error = ext2fs_alloc(ip, lbn, pref, + cred, &newb); + if (error) + return (error); + nb = newb; + ip->i_e2fs_last_blk = newb; + bp = getblk(vp, indirs[1].in_lbn, fs->e2fs_bsize, 0, 0); + bp->b_blkno = fsbtodb(fs, newb); + clrbuf(bp); + /* + * Write synchronously so that indirect blocks + * never point at garbage. + */ + if ((error = bwrite(bp)) != 0) { + ext2fs_blkfree(ip, nb); + return (error); + } + ip->i_e2fs_blocks[NDADDR + indirs[0].in_off] = newb; + ip->i_flag |= IN_CHANGE | IN_UPDATE; + } + /* + * Fetch through the indirect blocks, allocating as necessary. + */ + for (i = 1;;) { + error = bread(vp, + indirs[i].in_lbn, (int)fs->e2fs_bsize, NOCRED, &bp); + if (error) { + brelse(bp); + return (error); + } + bap = (daddr_t *)bp->b_data; + nb = bap[indirs[i].in_off]; + if (i == num) + break; + i += 1; + if (nb != 0) { + brelse(bp); + continue; + } + pref = ext2fs_blkpref(ip, lbn, 0, (daddr_t *)0); + error = ext2fs_alloc(ip, lbn, pref, cred, + &newb); + if (error) { + brelse(bp); + return (error); + } + nb = newb; + ip->i_e2fs_last_blk = newb; + nbp = getblk(vp, indirs[i].in_lbn, fs->e2fs_bsize, 0, 0); + nbp->b_blkno = fsbtodb(fs, nb); + clrbuf(nbp); + /* + * Write synchronously so that indirect blocks + * never point at garbage. + */ + if ((error = bwrite(nbp)) != 0) { + ext2fs_blkfree(ip, nb); + brelse(bp); + return (error); + } + bap[indirs[i - 1].in_off] = nb; + /* + * If required, write synchronously, otherwise use + * delayed write. + */ + if (flags & B_SYNC) { + bwrite(bp); + } else { + bdwrite(bp); + } + } + /* + * Get the data block, allocating if necessary. + */ + if (nb == 0) { + pref = ext2fs_blkpref(ip, lbn, indirs[i].in_off, &bap[0]); + error = ext2fs_alloc(ip, lbn, pref, cred, + &newb); + if (error) { + brelse(bp); + return (error); + } + nb = newb; + ip->i_e2fs_last_lblk = lbn; + ip->i_e2fs_last_blk = newb; + nbp = getblk(vp, lbn, fs->e2fs_bsize, 0, 0); + nbp->b_blkno = fsbtodb(fs, nb); + if (flags & B_CLRBUF) + clrbuf(nbp); + bap[indirs[i].in_off] = nb; + /* + * If required, write synchronously, otherwise use + * delayed write. + */ + if (flags & B_SYNC) { + bwrite(bp); + } else { + bdwrite(bp); + } + *bpp = nbp; + return (0); + } + brelse(bp); + if (flags & B_CLRBUF) { + error = bread(vp, lbn, (int)fs->e2fs_bsize, NOCRED, &nbp); + if (error) { + brelse(nbp); + return (error); + } + } else { + nbp = getblk(vp, lbn, fs->e2fs_bsize, 0, 0); + nbp->b_blkno = fsbtodb(fs, nb); + } + *bpp = nbp; + return (0); +} diff --git a/sys/ufs/ext2fs/ext2fs_bmap.c b/sys/ufs/ext2fs/ext2fs_bmap.c new file mode 100644 index 00000000000..fb6bef9b8d2 --- /dev/null +++ b/sys/ufs/ext2fs/ext2fs_bmap.c @@ -0,0 +1,222 @@ +/* $NetBSD: ufs_bmap.c,v 1.3 1996/02/09 22:36:00 christos Exp $ */ + +/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */ + +/* + * Copyright (c) 1989, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ufs_bmap.c 8.6 (Berkeley) 1/21/94 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/vnode.h> +#include <sys/mount.h> +#include <sys/resourcevar.h> +#include <sys/trace.h> + +#include <miscfs/specfs/specdev.h> + +#include <ufs/ufs/quota.h> +#include <ufs/ufs/inode.h> +#include <ufs/ufs/ufsmount.h> +#include <ufs/ufs/ufs_extern.h> +#include <ufs/ext2fs/ext2fs_extern.h> + +static int ext2fs_bmaparray __P((struct vnode *, daddr_t, daddr_t *, + struct indir *, int *, int *)); + +/* + * Bmap converts a the logical block number of a file to its physical block + * number on the disk. The conversion is done by using the logical block + * number to index into the array of block pointers described by the dinode. + */ +int +ext2fs_bmap(v) + void *v; +{ + struct vop_bmap_args /* { + struct vnode *a_vp; + daddr_t a_bn; + struct vnode **a_vpp; + daddr_t *a_bnp; + int *a_runp; + } */ *ap = v; + /* + * Check for underlying vnode requests and ensure that logical + * to physical mapping is requested. + */ + if (ap->a_vpp != NULL) + *ap->a_vpp = VTOI(ap->a_vp)->i_devvp; + if (ap->a_bnp == NULL) + return (0); + + return (ext2fs_bmaparray(ap->a_vp, ap->a_bn, ap->a_bnp, NULL, NULL, + ap->a_runp)); +} + +/* + * Indirect blocks are now on the vnode for the file. They are given negative + * logical block numbers. Indirect blocks are addressed by the negative + * address of the first data block to which they point. Double indirect blocks + * are addressed by one less than the address of the first indirect block to + * which they point. Triple indirect blocks are addressed by one less than + * the address of the first double indirect block to which they point. + * + * ext2fs_bmaparray does the bmap conversion, and if requested returns the + * array of logical blocks which must be traversed to get to a block. + * Each entry contains the offset into that block that gets you to the + * next block and the disk address of the block (if it is assigned). + */ + +int +ext2fs_bmaparray(vp, bn, bnp, ap, nump, runp) + struct vnode *vp; + register daddr_t bn; + daddr_t *bnp; + struct indir *ap; + int *nump; + int *runp; +{ + register struct inode *ip; + struct buf *bp; + struct ufsmount *ump; + struct mount *mp; + struct vnode *devvp; + struct indir a[NIADDR], *xap; + daddr_t daddr; + long metalbn; + int error, maxrun = 0, num; + + ip = VTOI(vp); + mp = vp->v_mount; + ump = VFSTOUFS(mp); +#ifdef DIAGNOSTIC + if ((ap != NULL && nump == NULL) || (ap == NULL && nump != NULL)) + panic("ext2fs_bmaparray: invalid arguments"); +#endif + + if (runp) { + /* + * XXX + * If MAXBSIZE is the largest transfer the disks can handle, + * we probably want maxrun to be 1 block less so that we + * don't create a block larger than the device can handle. + */ + *runp = 0; + maxrun = MAXBSIZE / mp->mnt_stat.f_iosize - 1; + } + + xap = ap == NULL ? a : ap; + if (!nump) + nump = # + if ((error = ufs_getlbns(vp, bn, xap, nump)) != 0) + return (error); + + num = *nump; + if (num == 0) { + *bnp = blkptrtodb(ump, ip->i_e2fs_blocks[bn]); + if (*bnp == 0) + *bnp = -1; + else if (runp) + for (++bn; bn < NDADDR && *runp < maxrun && + is_sequential(ump, ip->i_e2fs_blocks[bn - 1], + ip->i_e2fs_blocks[bn]); + ++bn, ++*runp); + return (0); + } + + + /* Get disk address out of indirect block array */ + daddr = ip->i_e2fs_blocks[NDADDR + xap->in_off]; + + devvp = VFSTOUFS(vp->v_mount)->um_devvp; + for (bp = NULL, ++xap; --num; ++xap) { + /* + * Exit the loop if there is no disk address assigned yet and + * the indirect block isn't in the cache, or if we were + * looking for an indirect block and we've found it. + */ + + metalbn = xap->in_lbn; + if ((daddr == 0 && !incore(vp, metalbn)) || metalbn == bn) + break; + /* + * If we get here, we've either got the block in the cache + * or we have a disk address for it, go fetch it. + */ + if (bp) + brelse(bp); + + xap->in_exists = 1; + bp = getblk(vp, metalbn, mp->mnt_stat.f_iosize, 0, 0); + if (bp->b_flags & (B_DONE | B_DELWRI)) { + trace(TR_BREADHIT, pack(vp, size), metalbn); + } +#ifdef DIAGNOSTIC + else if (!daddr) + panic("ext2fs_bmaparry: indirect block not in cache"); +#endif + else { + trace(TR_BREADMISS, pack(vp, size), metalbn); + bp->b_blkno = blkptrtodb(ump, daddr); + bp->b_flags |= B_READ; + VOP_STRATEGY(bp); + curproc->p_stats->p_ru.ru_inblock++; /* XXX */ + if ((error = biowait(bp)) != 0) { + brelse(bp); + return (error); + } + } + + daddr = ((daddr_t *)bp->b_data)[xap->in_off]; + if (num == 1 && daddr && runp) + for (bn = xap->in_off + 1; + bn < MNINDIR(ump) && *runp < maxrun && + is_sequential(ump, ((daddr_t *)bp->b_data)[bn - 1], + ((daddr_t *)bp->b_data)[bn]); + ++bn, ++*runp); + } + if (bp) + brelse(bp); + + daddr = blkptrtodb(ump, daddr); + *bnp = daddr == 0 ? -1 : daddr; + return (0); +} diff --git a/sys/ufs/ext2fs/ext2fs_dinode.h b/sys/ufs/ext2fs/ext2fs_dinode.h new file mode 100644 index 00000000000..db0ce26ec92 --- /dev/null +++ b/sys/ufs/ext2fs/ext2fs_dinode.h @@ -0,0 +1,132 @@ +/* $NetBSD: dinode.h,v 1.7 1995/06/15 23:22:48 cgd Exp $ */ + +/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */ + +/* + * Copyright (c) 1982, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)dinode.h 8.6 (Berkeley) 9/13/94 + */ + +#include <sys/stat.h> + +/* + * The root inode is the root of the file system. Inode 0 can't be used for + * normal purposes and bad blocks are normally linked to inode 1, thus + * the root inode is 2. + * Inode 3 to 10 are reserved in ext2fs. + */ +#define EXT2_ROOTINO ((ino_t)2) +#define EXT2_FIRSTINO ((ino_t)11) + +/* + * A dinode contains all the meta-data associated with a UFS file. + * This structure defines the on-disk format of a dinode. Since + * this structure describes an on-disk structure, all its fields + * are defined by types with precise widths. + */ + +#define NDADDR 12 /* Direct addresses in inode. */ +#define NIADDR 3 /* Indirect addresses in inode. */ + +#define EXT2_MAXSYMLINKLEN ((NDADDR+NIADDR) * sizeof (u_int32_t)) + +struct ext2fs_dinode { + u_int16_t e2di_mode; /* 0: IFMT, permissions; see below. */ + u_int16_t e2di_uid; /* 2: Owner UID */ + u_int32_t e2di_size; /* 4: Size (in bytes) */ + u_int32_t e2di_atime; /* 8: Acces time */ + u_int32_t e2di_ctime; /* 12: Create time */ + u_int32_t e2di_mtime; /* 16: Modification time */ + u_int32_t e2di_dtime; /* 20: Deletion time */ + u_int16_t e2di_gid; /* 24: Owner GID */ + u_int16_t e2di_nlink; /* 26: File link count */ + u_int32_t e2di_nblock;/* 28: Blocks count */ + u_int32_t e2di_flags; /* 32: Status flags (chflags) */ + u_int32_t e2di_linux_reserved1; /* 36 */ + u_int32_t e2di_blocks[NDADDR+NIADDR]; /* 40: disk blocks */ + u_int32_t e2di_gen; /* 100: generation number (file version) */ + u_int32_t e2di_facl; /* 104: file ACL (not implemented) */ + u_int32_t e2di_dacl; /* 108: dir ACL (not implemented) */ + u_int32_t e2di_faddr; /* 112: fragment address */ + u_int8_t e2di_nfrag; /* 116: fragment number */ + u_int8_t e2di_fsize; /* 117: fragment size */ + u_int16_t e2di_linux_reserved2; /* 118 */ + u_int32_t e2di_linux_reserved3[2]; /* 120 */ +}; + + + +#define E2MAXSYMLINKLEN ((NDADDR + NIADDR) * sizeof(u_int32_t)) + +/* File permissions. */ +#define EXT2_IEXEC 0000100 /* Executable. */ +#define EXT2_IWRITE 0000200 /* Writeable. */ +#define EXT2_IREAD 0000400 /* Readable. */ +#define EXT2_ISVTX 0001000 /* Sticky bit. */ +#define EXT2_ISGID 0002000 /* Set-gid. */ +#define EXT2_ISUID 0004000 /* Set-uid. */ + +/* File types. */ +#define EXT2_IFMT 0170000 /* Mask of file type. */ +#define EXT2_IFIFO 0010000 /* Named pipe (fifo). */ +#define EXT2_IFCHR 0020000 /* Character device. */ +#define EXT2_IFDIR 0040000 /* Directory file. */ +#define EXT2_IFBLK 0060000 /* Block device. */ +#define EXT2_IFREG 0100000 /* Regular file. */ +#define EXT2_IFLNK 0120000 /* Symbolic link. */ +#define EXT2_IFSOCK 0140000 /* UNIX domain socket. */ + +/* file flags */ +#define EXT2_SECRM 0x00000001 /* Secure deletion */ +#define EXT2_UNRM 0x00000002 /* Undelete */ +#define EXT2_COMPR 0x00000004 /* Compress file */ +#define EXT2_SYNC 0x00000008 /* Synchronous updates */ +#define EXT2_IMMUTABLE 0x00000010 /* Immutable file */ +#define EXT2_APPEND 0x00000020 /* writes to file may only append */ +#define EXT2_NODUMP 0x00000040 /* do not dump file */ + +/* + * The e2di_blocks fields may be overlaid with other information for + * file types that do not have associated disk storage. Block + * and character devices overlay the first data block with their + * dev_t value. Short symbolic links place their path in the + * di_db area. + */ + +#define e2di_rdev e2di_blocks[0] +#define e2di_shortlink e2di_blocks diff --git a/sys/ufs/ext2fs/ext2fs_dir.h b/sys/ufs/ext2fs/ext2fs_dir.h new file mode 100644 index 00000000000..341dd5ca21d --- /dev/null +++ b/sys/ufs/ext2fs/ext2fs_dir.h @@ -0,0 +1,114 @@ +/* $NetBSD: dir.h,v 1.8 1996/03/09 19:42:41 scottr Exp $ */ + +/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */ + +/* + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)dir.h 8.4 (Berkeley) 8/10/94 + */ + +#ifndef _EXT2FS_DIR_H_ +#define _EXT2FS_DIR_H_ + +/* + * Theoretically, directories can be more than 2Gb in length, however, in + * practice this seems unlikely. So, we define the type doff_t as a 32-bit + * quantity to keep down the cost of doing lookup on a 32-bit machine. +*/ +#define doff_t int32_t +#define EXT2FS_MAXDIRSIZE (0x7fffffff) + +/* + * A directory consists of some number of blocks of e2fs_bsize bytes. + * + * Each block contains some number of directory entry + * structures, which are of variable length. Each directory entry has + * a struct direct at the front of it, containing its inode number, + * the length of the entry, and the length of the name contained in + * the entry. These are followed by the name padded to a 4 byte boundary + * with null bytes. All names are guaranteed null terminated. + * The maximum length of a name in a directory is EXT2FS_MAXNAMLEN. + * + * The macro EXT2FS_DIRSIZ(fmt, dp) gives the amount of space required to + * represent a directory entry. Free space in a directory is represented by + * entries which have dp->e2d_reclen > DIRSIZ(fmt, dp). All d2fs_bsize bytes + * in a directory block are claimed by the directory entries. This + * usually results in the last entry in a directory having a large + * dp->e2d_reclen. When entries are deleted from a directory, the + * space is returned to the previous entry in the same directory + * block by increasing its dp->e2d_reclen. If the first entry of + * a directory block is free, then its dp->e2d_ino is set to 0. + * Entries other than the first in a directory do not normally have + * dp->e2d_ino set to 0. + */ + +#define EXT2FS_MAXNAMLEN 255 + +struct ext2fs_direct { + u_int32_t e2d_ino; /* inode number of entry */ + u_int16_t e2d_reclen; /* length of this record */ + u_int16_t e2d_namlen; /* length of string in d_name */ + char e2d_name[EXT2FS_MAXNAMLEN];/* name with length <= EXT2FS_MAXNAMLEN */ +}; + +/* + * The EXT2FS_DIRSIZ macro gives the minimum record length which will hold + * the directory entryfor a name len "len" (without the terminating null byte). + * This requires the amount of space in struct direct + * without the d_name field, plus enough space for the name without a + * terminating null byte, rounded up to a 4 byte boundary. + */ +#define EXT2FS_DIRSIZ(len) \ + (( 8 + len + 3) &~ 3) + +/* + * Template for manipulating directories. Should use struct direct's, + * but the name field is EXT2FS_MAXNAMLEN - 1, and this just won't do. + */ +struct ext2fs_dirtemplate { + u_int32_t dot_ino; + int16_t dot_reclen; + u_int16_t dot_namlen; + char dot_name[4]; /* must be multiple of 4 */ + u_int32_t dotdot_ino; + int16_t dotdot_reclen; + u_int16_t dotdot_namlen; + char dotdot_name[4]; /* ditto */ +}; + +#endif /* !_EXT2FS_DIR_H_ */ diff --git a/sys/ufs/ext2fs/ext2fs_extern.h b/sys/ufs/ext2fs/ext2fs_extern.h new file mode 100644 index 00000000000..10681c180ce --- /dev/null +++ b/sys/ufs/ext2fs/ext2fs_extern.h @@ -0,0 +1,154 @@ +/* $NetBSD: ffs_extern.h,v 1.5 1996/09/01 23:49:18 mycroft Exp $ */ + +/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ffs_extern.h 8.3 (Berkeley) 4/16/94 + */ + +struct buf; +struct fid; +struct m_ext2fs; +struct inode; +struct mount; +struct nameidata; +struct proc; +struct statfs; +struct timeval; +struct ucred; +struct ufsmount; +struct uio; +struct vnode; +struct mbuf; +struct componentname; + +__BEGIN_DECLS + +/* ext2fs_alloc.c */ +int ext2fs_alloc __P((struct inode *, daddr_t, daddr_t , struct ucred *, + daddr_t *)); +int ext2fs_realloccg __P((struct inode *, daddr_t, daddr_t, int, int , + struct ucred *, struct buf **)); +int ext2fs_reallocblks __P((void *)); +int ext2fs_valloc __P((void *)); +daddr_t ext2fs_blkpref __P((struct inode *, daddr_t, int, daddr_t *)); +void ext2fs_blkfree __P((struct inode *, daddr_t)); +int ext2fs_vfree __P((void *)); + +/* ext2fs_balloc.c */ +int ext2fs_balloc __P((struct inode *, daddr_t, int, struct ucred *, + struct buf **, int)); + +/* ext2fs_bmap.c */ +int ext2fs_bmap __P((void *)); + +/* ext2fs_inode.c */ +void ext2fs_init __P((void)); +int ext2fs_update __P((void *)); +int ext2fs_truncate __P((void *)); +int ext2fs_inactive __P((void *)); + +/* ext2fs_lookup.c */ +int ext2fs_readdir __P((void *)); +int ext2fs_lookup __P((void *)); +int ext2fs_direnter __P((struct inode *, struct vnode *, + struct componentname *)); +int ext2fs_dirremove __P((struct vnode *, struct componentname *)); +int ext2fs_dirrewrite __P((struct inode *, struct inode *, + struct componentname *)); +int ext2fs_dirempty __P((struct inode *, ino_t, struct ucred *)); +int ext2fs_checkpath __P((struct inode *, struct inode *, struct ucred *)); + +/* ext2fs_subr.c */ +int ext2fs_blkatoff __P((void *)); +void ext2fs_fragacct __P((struct m_ext2fs *, int, int32_t[], int)); +#ifdef DIAGNOSTIC +void ext2fs_checkoverlap __P((struct buf *, struct inode *)); +#endif + +/* ext2fs_vfsops.c */ +int ext2fs_mountroot __P((void)); +int ext2fs_mount __P((struct mount *, const char *, void *, struct nameidata *, + struct proc *)); +int ext2fs_reload __P((struct mount *, struct ucred *, struct proc *)); +int ext2fs_mountfs __P((struct vnode *, struct mount *, struct proc *)); +int ext2fs_unmount __P((struct mount *, int, struct proc *)); +int ext2fs_flushfiles __P((struct mount *, int, struct proc *)); +int ext2fs_statfs __P((struct mount *, struct statfs *, struct proc *)); +int ext2fs_sync __P((struct mount *, int, struct ucred *, struct proc *)); +int ext2fs_vget __P((struct mount *, ino_t, struct vnode **)); +int ext2fs_fhtovp __P((struct mount *, struct fid *, struct mbuf *, + struct vnode **, int *, struct ucred **)); +int ext2fs_vptofh __P((struct vnode *, struct fid *)); +int ext2fs_sbupdate __P((struct ufsmount *, int)); +int ext2fs_cgupdate __P((struct ufsmount *, int)); + +/* ext2fs_readwrite.c */ +int ext2fs_read __P((void *)); +int ext2fs_write __P((void *)); + +/* ext2fs_vnops.c */ +int ext2fs_create __P((void *)); +int ext2fs_mknod __P((void *)); +int ext2fs_open __P((void *)); +int ext2fs_access __P((void *)); +int ext2fs_getattr __P((void *)); +int ext2fs_setattr __P((void *)); +int ext2fs_remove __P((void *)); +int ext2fs_link __P((void *)); +int ext2fs_rename __P((void *)); +int ext2fs_mkdir __P((void *)); +int ext2fs_rmdir __P((void *)); +int ext2fs_symlink __P((void *)); +int ext2fs_readlink __P((void *)); +int ext2fs_advlock __P((void *)); +int ext2fs_vinit __P(( struct mount *, int (**specops) __P((void *)), + int (**fifoops) __P((void *)), struct vnode **)); +int ext2fs_makeinode __P((int, struct vnode *, struct vnode **, + struct componentname *cnp)); +int ext2fs_reclaim __P((void *)); + +#define ext2fs_fsync genfs_fsync +__END_DECLS + +#define IS_EXT2_VNODE(vp) (vp->v_tag == VT_EXT2FS) + +extern int (**ext2fs_vnodeop_p) __P((void *)); +extern int (**ext2fs_specop_p) __P((void *)); +#ifdef FIFO +extern int (**ext2fs_fifoop_p) __P((void *)); +#define EXT2FS_FIFOOPS ext2fs_fifoop_p +#else +#define EXT2FS_FIFOOPS NULL +#endif diff --git a/sys/ufs/ext2fs/ext2fs_inode.c b/sys/ufs/ext2fs/ext2fs_inode.c new file mode 100644 index 00000000000..0609c3c13a6 --- /dev/null +++ b/sys/ufs/ext2fs/ext2fs_inode.c @@ -0,0 +1,528 @@ +/* $NetBSD: ffs_inode.c,v 1.12 1996/11/06 03:02:59 thorpej Exp $ */ + +/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */ + +/* + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ffs_inode.c 8.8 (Berkeley) 10/19/94 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mount.h> +#include <sys/proc.h> +#include <sys/file.h> +#include <sys/buf.h> +#include <sys/vnode.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/trace.h> +#include <sys/resourcevar.h> + +#include <vm/vm.h> + +#include <ufs/ufs/quota.h> +#include <ufs/ufs/inode.h> +#include <ufs/ufs/ufsmount.h> +#include <ufs/ufs/ufs_extern.h> + +#include <ufs/ext2fs/ext2fs.h> +#include <ufs/ext2fs/ext2fs_extern.h> + +static int ext2fs_indirtrunc __P((struct inode *, daddr_t, daddr_t, + daddr_t, int, long *)); + +void +ext2fs_init() +{ + static int done = 0; + + if (done) + return; + done = 1; + ufs_ihashinit(); + return; +} + +/* + * Last reference to an inode. If necessary, write or delete it. + */ +int +ext2fs_inactive(v) + void *v; +{ + struct vop_inactive_args /* { + struct vnode *a_vp; + } */ *ap = v; + register struct vnode *vp = ap->a_vp; + register struct inode *ip = VTOI(vp); + struct timespec ts; + int error; + extern int prtactive; + + if (prtactive && vp->v_usecount != 0) + vprint("ffs_inactive: pushing active", vp); + /* Get rid of inodes related to stale file handles. */ + if (ip->i_e2fs_mode == 0 || ip->i_e2fs_dtime != 0) { + if ((vp->v_flag & VXLOCK) == 0) + vgone(vp); + return (0); + } + + error = 0; +#ifdef DIAGNOSTIC + if (VOP_ISLOCKED(vp)) + panic("ffs_inactive: locked inode"); + if (curproc) + ip->i_lockholder = curproc->p_pid; + else + ip->i_lockholder = -1; +#endif + ip->i_flag |= IN_LOCKED; + if (ip->i_e2fs_nlink == 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { + error = VOP_TRUNCATE(vp, (off_t)0, 0, NOCRED, NULL); + TIMEVAL_TO_TIMESPEC(&time, &ts); + ip->i_e2fs_dtime = ts.tv_sec; + ip->i_flag |= IN_CHANGE | IN_UPDATE; + VOP_VFREE(vp, ip->i_number, ip->i_e2fs_mode); + } + if (ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) { + TIMEVAL_TO_TIMESPEC(&time, &ts); + VOP_UPDATE(vp, &ts, &ts, 0); + } + VOP_UNLOCK(vp); + /* + * If we are done with the inode, reclaim it + * so that it can be reused immediately. + */ + if (vp->v_usecount == 0 && ip->i_e2fs_dtime != 0) + vgone(vp); + return (error); +} + + +/* + * Update the access, modified, and inode change times as specified by the + * IACCESS, IUPDATE, and ICHANGE flags respectively. The IMODIFIED flag is + * used to specify that the inode needs to be updated but that the times have + * already been set. The access and modified times are taken from the second + * and third parameters; the inode change time is always taken from the current + * time. If waitfor is set, then wait for the disk write of the inode to + * complete. + */ +int +ext2fs_update(v) + void *v; +{ + struct vop_update_args /* { + struct vnode *a_vp; + struct timespec *a_access; + struct timespec *a_modify; + int a_waitfor; + } */ *ap = v; + register struct m_ext2fs *fs; + struct buf *bp; + struct inode *ip; + int error; + struct timespec ts; + + if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) + return (0); + ip = VTOI(ap->a_vp); + TIMEVAL_TO_TIMESPEC(&time, &ts); + EXT2FS_ITIMES(ip, ap->a_access, ap->a_modify, &ts); + if ((ip->i_flag & IN_MODIFIED) == 0) + return (0); + ip->i_flag &= ~IN_MODIFIED; + fs = ip->i_e2fs; + error = bread(ip->i_devvp, + fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), + (int)fs->e2fs_bsize, NOCRED, &bp); + if (error) { + brelse(bp); + return (error); + } + bcopy(&ip->i_din.e2fs_din, + ((struct ext2fs_dinode *)bp->b_data + ino_to_fsbo(fs, ip->i_number)), + sizeof(struct ext2fs_dinode)); + if (ap->a_waitfor) + return (bwrite(bp)); + else { + bdwrite(bp); + return (0); + } +} + +#define SINGLE 0 /* index of single indirect block */ +#define DOUBLE 1 /* index of double indirect block */ +#define TRIPLE 2 /* index of triple indirect block */ +/* + * Truncate the inode oip to at most length size, freeing the + * disk blocks. + */ +int +ext2fs_truncate(v) + void *v; +{ + struct vop_truncate_args /* { + struct vnode *a_vp; + off_t a_length; + int a_flags; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap = v; + register struct vnode *ovp = ap->a_vp; + register daddr_t lastblock; + register struct inode *oip; + daddr_t bn, lbn, lastiblock[NIADDR], indir_lbn[NIADDR]; + daddr_t oldblks[NDADDR + NIADDR], newblks[NDADDR + NIADDR]; + off_t length = ap->a_length; + register struct m_ext2fs *fs; + struct buf *bp; + int offset, size, level; + long count, nblocks, vflags, blocksreleased = 0; + struct timespec ts; + register int i; + int aflags, error, allerror; + off_t osize; + + if (length < 0) + return (EINVAL); + + oip = VTOI(ovp); + TIMEVAL_TO_TIMESPEC(&time, &ts); + if (ovp->v_type == VLNK && + (oip->i_e2fs_size < ovp->v_mount->mnt_maxsymlinklen || + (ovp->v_mount->mnt_maxsymlinklen == 0 && + oip->i_e2fs_nblock == 0))) { +#ifdef DIAGNOSTIC + if (length != 0) + panic("ext2fs_truncate: partial truncate of symlink"); +#endif + bzero((char *)&oip->i_din.e2fs_din.e2di_shortlink, + (u_int)oip->i_e2fs_size); + oip->i_e2fs_size = 0; + oip->i_flag |= IN_CHANGE | IN_UPDATE; + return (VOP_UPDATE(ovp, &ts, &ts, 1)); + } + if (oip->i_e2fs_size == length) { + oip->i_flag |= IN_CHANGE | IN_UPDATE; + return (VOP_UPDATE(ovp, &ts, &ts, 0)); + } + fs = oip->i_e2fs; + osize = oip->i_e2fs_size; + /* + * Lengthen the size of the file. We must ensure that the + * last byte of the file is allocated. Since the smallest + * value of osize is 0, length will be at least 1. + */ + if (osize < length) { +#if 0 /* XXX */ + if (length > fs->fs_maxfilesize) + return (EFBIG); +#endif + offset = blkoff(fs, length - 1); + lbn = lblkno(fs, length - 1); + aflags = B_CLRBUF; + if (ap->a_flags & IO_SYNC) + aflags |= B_SYNC; + error = ext2fs_balloc(oip, lbn, offset + 1, ap->a_cred, &bp, + aflags); + if (error) + return (error); + oip->i_e2fs_size = length; + vnode_pager_setsize(ovp, (u_long)length); + (void) vnode_pager_uncache(ovp); + if (aflags & B_SYNC) + bwrite(bp); + else + bawrite(bp); + oip->i_flag |= IN_CHANGE | IN_UPDATE; + return (VOP_UPDATE(ovp, &ts, &ts, 1)); + } + /* + * Shorten the size of the file. If the file is not being + * truncated to a block boundry, the contents of the + * partial block following the end of the file must be + * zero'ed in case it ever become accessable again because + * of subsequent file growth. + */ + offset = blkoff(fs, length); + if (offset == 0) { + oip->i_e2fs_size = length; + } else { + lbn = lblkno(fs, length); + aflags = B_CLRBUF; + if (ap->a_flags & IO_SYNC) + aflags |= B_SYNC; + error = ext2fs_balloc(oip, lbn, offset, ap->a_cred, &bp, aflags); + if (error) + return (error); + oip->i_e2fs_size = length; + size = fs->e2fs_bsize; + vnode_pager_setsize(ovp, (u_long)length); + (void) vnode_pager_uncache(ovp); + bzero((char *)bp->b_data + offset, (u_int)(size - offset)); + allocbuf(bp, size); + if (aflags & B_SYNC) + bwrite(bp); + else + bawrite(bp); + } + /* + * Calculate index into inode's block list of + * last direct and indirect blocks (if any) + * which we want to keep. Lastblock is -1 when + * the file is truncated to 0. + */ + lastblock = lblkno(fs, length + fs->e2fs_bsize - 1) - 1; + lastiblock[SINGLE] = lastblock - NDADDR; + lastiblock[DOUBLE] = lastiblock[SINGLE] - NINDIR(fs); + lastiblock[TRIPLE] = lastiblock[DOUBLE] - NINDIR(fs) * NINDIR(fs); + nblocks = btodb(fs->e2fs_bsize); + /* + * Update file and block pointers on disk before we start freeing + * blocks. If we crash before free'ing blocks below, the blocks + * will be returned to the free list. lastiblock values are also + * normalized to -1 for calls to ext2fs_indirtrunc below. + */ + bcopy((caddr_t)&oip->i_e2fs_blocks[0], (caddr_t)oldblks, sizeof oldblks); + for (level = TRIPLE; level >= SINGLE; level--) + if (lastiblock[level] < 0) { + oip->i_e2fs_blocks[NDADDR + level] = 0; + lastiblock[level] = -1; + } + for (i = NDADDR - 1; i > lastblock; i--) + oip->i_e2fs_blocks[i] = 0; + oip->i_flag |= IN_CHANGE | IN_UPDATE; + if ((error = VOP_UPDATE(ovp, &ts, &ts, 1)) != 0) + allerror = error; + /* + * Having written the new inode to disk, save its new configuration + * and put back the old block pointers long enough to process them. + * Note that we save the new block configuration so we can check it + * when we are done. + */ + bcopy((caddr_t)&oip->i_e2fs_blocks[0], (caddr_t)newblks, sizeof newblks); + bcopy((caddr_t)oldblks, (caddr_t)&oip->i_e2fs_blocks[0], sizeof oldblks); + oip->i_e2fs_size = osize; + vflags = ((length > 0) ? V_SAVE : 0) | V_SAVEMETA; + allerror = vinvalbuf(ovp, vflags, ap->a_cred, ap->a_p, 0, 0); + + /* + * Indirect blocks first. + */ + indir_lbn[SINGLE] = -NDADDR; + indir_lbn[DOUBLE] = indir_lbn[SINGLE] - NINDIR(fs) -1; + indir_lbn[TRIPLE] = indir_lbn[DOUBLE] - NINDIR(fs) * NINDIR(fs) - 1; + for (level = TRIPLE; level >= SINGLE; level--) { + bn = oip->i_e2fs_blocks[NDADDR + level]; + if (bn != 0) { + error = ext2fs_indirtrunc(oip, indir_lbn[level], + fsbtodb(fs, bn), lastiblock[level], level, &count); + if (error) + allerror = error; + blocksreleased += count; + if (lastiblock[level] < 0) { + oip->i_e2fs_blocks[NDADDR + level] = 0; + ext2fs_blkfree(oip, bn); + blocksreleased += nblocks; + } + } + if (lastiblock[level] >= 0) + goto done; + } + + /* + * All whole direct blocks or frags. + */ + for (i = NDADDR - 1; i > lastblock; i--) { + bn = oip->i_e2fs_blocks[i]; + if (bn == 0) + continue; + oip->i_e2fs_blocks[i] = 0; + ext2fs_blkfree(oip, bn); + blocksreleased += btodb(fs->e2fs_bsize); + } + if (lastblock < 0) + goto done; + +done: +#ifdef DIAGNOSTIC + for (level = SINGLE; level <= TRIPLE; level++) + if (newblks[NDADDR + level] != oip->i_e2fs_blocks[NDADDR + level]) + panic("itrunc1"); + for (i = 0; i < NDADDR; i++) + if (newblks[i] != oip->i_e2fs_blocks[i]) + panic("itrunc2"); + if (length == 0 && + (ovp->v_dirtyblkhd.lh_first || ovp->v_cleanblkhd.lh_first)) + panic("itrunc3"); +#endif /* DIAGNOSTIC */ + /* + * Put back the real size. + */ + oip->i_e2fs_size = length; + oip->i_e2fs_nblock -= blocksreleased; + if (oip->i_e2fs_nblock < 0) /* sanity */ + oip->i_e2fs_nblock = 0; + oip->i_flag |= IN_CHANGE; + return (allerror); +} + +/* + * Release blocks associated with the inode ip and stored in the indirect + * block bn. Blocks are free'd in LIFO order up to (but not including) + * lastbn. If level is greater than SINGLE, the block is an indirect block + * and recursive calls to indirtrunc must be used to cleanse other indirect + * blocks. + * + * NB: triple indirect blocks are untested. + */ +static int +ext2fs_indirtrunc(ip, lbn, dbn, lastbn, level, countp) + register struct inode *ip; + daddr_t lbn, lastbn; + daddr_t dbn; + int level; + long *countp; +{ + register int i; + struct buf *bp; + register struct m_ext2fs *fs = ip->i_e2fs; + register daddr_t *bap; + struct vnode *vp; + daddr_t *copy = NULL, nb, nlbn, last; + long blkcount, factor; + int nblocks, blocksreleased = 0; + int error = 0, allerror = 0; + + /* + * Calculate index in current block of last + * block to be kept. -1 indicates the entire + * block so we need not calculate the index. + */ + factor = 1; + for (i = SINGLE; i < level; i++) + factor *= NINDIR(fs); + last = lastbn; + if (lastbn > 0) + last /= factor; + nblocks = btodb(fs->e2fs_bsize); + /* + * Get buffer of block pointers, zero those entries corresponding + * to blocks to be free'd, and update on disk copy first. Since + * double(triple) indirect before single(double) indirect, calls + * to bmap on these blocks will fail. However, we already have + * the on disk address, so we have to set the b_blkno field + * explicitly instead of letting bread do everything for us. + */ + vp = ITOV(ip); + bp = getblk(vp, lbn, (int)fs->e2fs_bsize, 0, 0); + if (bp->b_flags & (B_DONE | B_DELWRI)) { + /* Braces must be here in case trace evaluates to nothing. */ + trace(TR_BREADHIT, pack(vp, fs->e2fs_bsize), lbn); + } else { + trace(TR_BREADMISS, pack(vp, fs->e2fs_bsize), lbn); + curproc->p_stats->p_ru.ru_inblock++; /* pay for read */ + bp->b_flags |= B_READ; + if (bp->b_bcount > bp->b_bufsize) + panic("ext2fs_indirtrunc: bad buffer size"); + bp->b_blkno = dbn; + VOP_STRATEGY(bp); + error = biowait(bp); + } + if (error) { + brelse(bp); + *countp = 0; + return (error); + } + + bap = (daddr_t *)bp->b_data; + if (lastbn != -1) { + MALLOC(copy, daddr_t *, fs->e2fs_bsize, M_TEMP, M_WAITOK); + bcopy((caddr_t)bap, (caddr_t)copy, (u_int)fs->e2fs_bsize); + bzero((caddr_t)&bap[last + 1], + (u_int)(NINDIR(fs) - (last + 1)) * sizeof (u_int32_t)); + error = bwrite(bp); + if (error) + allerror = error; + bap = copy; + } + + /* + * Recursively free totally unused blocks. + */ + for (i = NINDIR(fs) - 1, + nlbn = lbn + 1 - i * factor; i > last; + i--, nlbn += factor) { + nb = bap[i]; + if (nb == 0) + continue; + if (level > SINGLE) { + error = ext2fs_indirtrunc(ip, nlbn, fsbtodb(fs, nb), + (daddr_t)-1, level - 1, + &blkcount); + if (error) + allerror = error; + blocksreleased += blkcount; + } + ext2fs_blkfree(ip, nb); + blocksreleased += nblocks; + } + + /* + * Recursively free last partial block. + */ + if (level > SINGLE && lastbn >= 0) { + last = lastbn % factor; + nb = bap[i]; + if (nb != 0) { + error = ext2fs_indirtrunc(ip, nlbn, fsbtodb(fs, nb), + last, level - 1, &blkcount); + if (error) + allerror = error; + blocksreleased += blkcount; + } + } + + if (copy != NULL) { + FREE(copy, M_TEMP); + } else { + bp->b_flags |= B_INVAL; + brelse(bp); + } + + *countp = blocksreleased; + return (allerror); +} diff --git a/sys/ufs/ext2fs/ext2fs_lookup.c b/sys/ufs/ext2fs/ext2fs_lookup.c new file mode 100644 index 00000000000..1af31ab1869 --- /dev/null +++ b/sys/ufs/ext2fs/ext2fs_lookup.c @@ -0,0 +1,1066 @@ +/* $OpenBSD: ext2fs_lookup.c,v 1.1 1997/05/30 05:18:51 downsj Exp $ */ + +/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */ + +/* + * modified for Lites 1.1 + * + * Aug 1995, Godmar Back (gback@cs.utah.edu) + * University of Utah, Department of Computer Science + */ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ufs_lookup.c 8.6 (Berkeley) 4/1/94 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/namei.h> +#include <sys/buf.h> +#include <sys/file.h> +#include <sys/mount.h> +#include <sys/vnode.h> +#include <sys/malloc.h> +#include <sys/dirent.h> + +#include <ufs/ufs/quota.h> +#include <ufs/ufs/inode.h> +#include <ufs/ufs/ufsmount.h> +#include <ufs/ufs/ufs_extern.h> + +#include <ufs/ext2fs/ext2fs_extern.h> +#include <ufs/ext2fs/ext2fs_dir.h> +#include <ufs/ext2fs/ext2fs.h> + +extern int dirchk; + +static void ext2fs_dirconv2ffs __P((struct ext2fs_direct *e2dir, + struct dirent *ffsdir)); +static int ext2fs_dirbadentry __P((struct vnode *dp, + struct ext2fs_direct *de, + int entryoffsetinblock)); + +/* + * the problem that is tackled below is the fact that FFS + * includes the terminating zero on disk while EXT2FS doesn't + * this implies that we need to introduce some padding. + * For instance, a filename "sbin" has normally a reclen 12 + * in EXT2, but 16 in FFS. + * This reminds me of that Pepsi commercial: 'Kid saved a lousy nine cents...' + * If it wasn't for that, the complete ufs code for directories would + * have worked w/o changes (except for the difference in DIRBLKSIZ) + */ +static void +ext2fs_dirconv2ffs( e2dir, ffsdir) + struct ext2fs_direct *e2dir; + struct dirent *ffsdir; +{ + bzero(ffsdir, sizeof(struct dirent)); + ffsdir->d_fileno = e2dir->e2d_ino; + ffsdir->d_namlen = e2dir->e2d_namlen; + + ffsdir->d_type = DT_UNKNOWN; /* don't know more here */ +#ifdef DIAGNOSTIC + /* + * XXX Rigth now this can't happen, but if one day + * MAXNAMLEN != E2FS_MAXNAMLEN we should handle this more gracefully ! + */ + if (e2dir->e2d_namlen > MAXNAMLEN) panic("ext2fs: e2dir->e2d_namlen\n"); +#endif + strncpy(ffsdir->d_name, e2dir->e2d_name, ffsdir->d_namlen); + + /* Godmar thinks: since e2dir->e2d_reclen can be big and means + nothing anyway, we compute our own reclen according to what + we think is right + */ + ffsdir->d_reclen = DIRENT_SIZE(ffsdir); +} + +/* + * Vnode op for reading directories. + * + * Convert the on-disk entries to <sys/dirent.h> entries. + * the problem is that the conversion will blow up some entries by four bytes, + * so it can't be done in place. This is too bad. Right now the conversion is + * done entry by entry, the converted entry is sent via uiomove. + * + * XXX allocate a buffer, convert as many entries as possible, then send + * the whole buffer to uiomove + */ +int +ext2fs_readdir(v) + void *v; +{ + struct vop_readdir_args /* { + struct vnode *a_vp; + struct uio *a_uio; + struct ucred *a_cred; + int *a_eofflag; + u_long *a_cookies; + int ncookies; + } */ *ap = v; + register struct uio *uio = ap->a_uio; + int error; + size_t count, readcnt, lost; + struct m_ext2fs *fs = VTOI(ap->a_vp)->i_e2fs; + + struct ext2fs_direct *dp; + struct dirent dstd; + struct uio auio; + struct iovec aiov; + caddr_t dirbuf; + off_t off = uio->uio_offset; + u_long *cookies = ap->a_cookies; + int ncookies = ap->a_ncookies; + + count = uio->uio_resid; + /* Make sure we don't return partial entries. */ + count -= (uio->uio_offset + count) & (fs->e2fs_bsize -1); + if (count <= 0) + return (EINVAL); + lost = uio->uio_resid - count; + uio->uio_resid = count; + uio->uio_iov->iov_len = count; + + auio = *uio; + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_segflg = UIO_SYSSPACE; + aiov.iov_len = count; + MALLOC(dirbuf, caddr_t, count, M_TEMP, M_WAITOK); + bzero(dirbuf, count); + aiov.iov_base = dirbuf; + + error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred); + if (error == 0) { + readcnt = count - auio.uio_resid; + for (dp = (struct ext2fs_direct *)dirbuf; + (char *)dp < (char *)dirbuf + readcnt; ) { + if (dp->e2d_reclen <= 0) { + error = EIO; + break; + } + ext2fs_dirconv2ffs(dp, &dstd); + if(dstd.d_reclen > uio->uio_resid) { + break; + } + if ((error = uiomove((caddr_t)&dstd, dstd.d_reclen, uio)) != 0) { + break; + } + off = off + dp->e2d_reclen; + if (cookies != NULL) { + *cookies++ = off; + if (--ncookies <= 0){ + break; /* out of cookies */ + } + } + /* advance dp */ + dp = (struct ext2fs_direct *) ((char *)dp + dp->e2d_reclen); + } + /* we need to correct uio_offset */ + uio->uio_offset = off; + } + FREE(dirbuf, M_TEMP); + *ap->a_eofflag = VTOI(ap->a_vp)->i_e2fs_size <= uio->uio_offset; + uio->uio_resid += lost; + return (error); +} + +/* + * Convert a component of a pathname into a pointer to a locked inode. + * This is a very central and rather complicated routine. + * If the file system is not maintained in a strict tree hierarchy, + * this can result in a deadlock situation (see comments in code below). + * + * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending + * on whether the name is to be looked up, created, renamed, or deleted. + * When CREATE, RENAME, or DELETE is specified, information usable in + * creating, renaming, or deleting a directory entry may be calculated. + * If flag has LOCKPARENT or'ed into it and the target of the pathname + * exists, lookup returns both the target and its parent directory locked. + * When creating or renaming and LOCKPARENT is specified, the target may + * not be ".". When deleting and LOCKPARENT is specified, the target may + * be "."., but the caller must check to ensure it does an vrele and vput + * instead of two vputs. + * + * Overall outline of ext2fs_lookup: + * + * check accessibility of directory + * look for name in cache, if found, then if at end of path + * and deleting or creating, drop it, else return name + * search for name in directory, to found or notfound + * notfound: + * if creating, return locked directory, leaving info on available slots + * else return error + * found: + * if at end of path and deleting, return information to allow delete + * if at end of path and rewriting (RENAME and LOCKPARENT), lock target + * inode and return info to allow rewrite + * if not at end, add name to cache; if at end and neither creating + * nor deleting, add name to cache + */ +int +ext2fs_lookup(v) + void *v; +{ + struct vop_lookup_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + } */ *ap = v; + register struct vnode *vdp; /* vnode for directory being searched */ + register struct inode *dp; /* inode for directory being searched */ + struct buf *bp; /* a buffer of directory entries */ + register struct ext2fs_direct *ep; /* the current directory entry */ + int entryoffsetinblock; /* offset of ep in bp's buffer */ + enum {NONE, COMPACT, FOUND} slotstatus; + doff_t slotoffset; /* offset of area with free space */ + int slotsize; /* size of area at slotoffset */ + int slotfreespace; /* amount of space free in slot */ + int slotneeded; /* size of the entry we're seeking */ + int numdirpasses; /* strategy for directory search */ + doff_t endsearch; /* offset to end directory search */ + doff_t prevoff; /* prev entry dp->i_offset */ + struct vnode *pdp; /* saved dp during symlink work */ + struct vnode *tdp; /* returned by VFS_VGET */ + doff_t enduseful; /* pointer past last used dir slot */ + u_long bmask; /* block offset mask */ + int lockparent; /* 1 => lockparent flag is set */ + int wantparent; /* 1 => wantparent or lockparent flag */ + int namlen, error; + struct vnode **vpp = ap->a_vpp; + struct componentname *cnp = ap->a_cnp; + struct ucred *cred = cnp->cn_cred; + int flags = cnp->cn_flags; + int nameiop = cnp->cn_nameiop; + + int dirblksize = VTOI(ap->a_dvp)->i_e2fs->e2fs_bsize; + + bp = NULL; + slotoffset = -1; + *vpp = NULL; + vdp = ap->a_dvp; + dp = VTOI(vdp); + lockparent = flags & LOCKPARENT; + wantparent = flags & (LOCKPARENT|WANTPARENT); + /* + * Check accessiblity of directory. + */ + if ((dp->i_e2fs_mode & IFMT) != IFDIR) + return (ENOTDIR); + if ((error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc)) != 0) + return (error); + + /* + * We now have a segment name to search for, and a directory to search. + * + * Before tediously performing a linear scan of the directory, + * check the name cache to see if the directory/name pair + * we are looking for is known already. + */ + if ((error = cache_lookup(vdp, vpp, cnp)) != 0) { + int vpid; /* capability number of vnode */ + + if (error == ENOENT) + return (error); + /* + * Get the next vnode in the path. + * See comment below starting `Step through' for + * an explaination of the locking protocol. + */ + pdp = vdp; + dp = VTOI(*vpp); + vdp = *vpp; + vpid = vdp->v_id; + if (pdp == vdp) { /* lookup on "." */ + VREF(vdp); + error = 0; + } else if (flags & ISDOTDOT) { + VOP_UNLOCK(pdp); + error = vget(vdp, 1); + if (!error && lockparent && (flags & ISLASTCN)) + error = VOP_LOCK(pdp); + } else { + error = vget(vdp, 1); + if (!lockparent || error || !(flags & ISLASTCN)) + VOP_UNLOCK(pdp); + } + /* + * Check that the capability number did not change + * while we were waiting for the lock. + */ + if (!error) { + if (vpid == vdp->v_id) + return (0); + vput(vdp); + if (lockparent && pdp != vdp && (flags & ISLASTCN)) + VOP_UNLOCK(pdp); + } + if ((error = VOP_LOCK(pdp)) != 0) + return (error); + vdp = pdp; + dp = VTOI(pdp); + *vpp = NULL; + } + + /* + * Suppress search for slots unless creating + * file and at end of pathname, in which case + * we watch for a place to put the new file in + * case it doesn't already exist. + */ + slotstatus = FOUND; + slotfreespace = slotsize = slotneeded = 0; + if ((nameiop == CREATE || nameiop == RENAME) && + (flags & ISLASTCN)) { + slotstatus = NONE; + slotneeded = EXT2FS_DIRSIZ(cnp->cn_namelen); + } + + /* + * If there is cached information on a previous search of + * this directory, pick up where we last left off. + * We cache only lookups as these are the most common + * and have the greatest payoff. Caching CREATE has little + * benefit as it usually must search the entire directory + * to determine that the entry does not exist. Caching the + * location of the last DELETE or RENAME has not reduced + * profiling time and hence has been removed in the interest + * of simplicity. + */ + bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1; + if (nameiop != LOOKUP || dp->i_diroff == 0 || + dp->i_diroff > dp->i_e2fs_size) { + entryoffsetinblock = 0; + dp->i_offset = 0; + numdirpasses = 1; + } else { + dp->i_offset = dp->i_diroff; + if ((entryoffsetinblock = dp->i_offset & bmask) && + (error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp))) + return (error); + numdirpasses = 2; + } + prevoff = dp->i_offset; + endsearch = roundup(dp->i_e2fs_size, dirblksize); + enduseful = 0; + +searchloop: + while (dp->i_offset < endsearch) { + /* + * If necessary, get the next directory block. + */ + if ((dp->i_offset & bmask) == 0) { + if (bp != NULL) + brelse(bp); + error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp); + if (error != 0) + return (error); + entryoffsetinblock = 0; + } + /* + * If still looking for a slot, and at a dirblksize + * boundary, have to start looking for free space again. + */ + if (slotstatus == NONE && + (entryoffsetinblock & (dirblksize - 1)) == 0) { + slotoffset = -1; + slotfreespace = 0; + } + /* + * Get pointer to next entry. + * Full validation checks are slow, so we only check + * enough to insure forward progress through the + * directory. Complete checks can be run by patching + * "dirchk" to be true. + */ + ep = (struct ext2fs_direct *) + ((char *)bp->b_data + entryoffsetinblock); + if (ep->e2d_reclen == 0 || + (dirchk && ext2fs_dirbadentry(vdp, ep, entryoffsetinblock))) { + int i; + ufs_dirbad(dp, dp->i_offset, "mangled entry"); + i = dirblksize - (entryoffsetinblock & (dirblksize - 1)); + dp->i_offset += i; + entryoffsetinblock += i; + continue; + } + + /* + * If an appropriate sized slot has not yet been found, + * check to see if one is available. Also accumulate space + * in the current block so that we can determine if + * compaction is viable. + */ + if (slotstatus != FOUND) { + int size = ep->e2d_reclen; + + if (ep->e2d_ino != 0) + size -= EXT2FS_DIRSIZ(ep->e2d_namlen); + if (size > 0) { + if (size >= slotneeded) { + slotstatus = FOUND; + slotoffset = dp->i_offset; + slotsize = ep->e2d_reclen; + } else if (slotstatus == NONE) { + slotfreespace += size; + if (slotoffset == -1) + slotoffset = dp->i_offset; + if (slotfreespace >= slotneeded) { + slotstatus = COMPACT; + slotsize = dp->i_offset + + ep->e2d_reclen - slotoffset; + } + } + } + } + + /* + * Check for a name match. + */ + if (ep->e2d_ino) { + namlen = ep->e2d_namlen; + if (namlen == cnp->cn_namelen && + !bcmp(cnp->cn_nameptr, ep->e2d_name, + (unsigned)namlen)) { + /* + * Save directory entry's inode number and + * reclen in ndp->ni_ufs area, and release + * directory buffer. + */ + dp->i_ino = ep->e2d_ino; + dp->i_reclen = ep->e2d_reclen; + brelse(bp); + goto found; + } + } + prevoff = dp->i_offset; + dp->i_offset += ep->e2d_reclen; + entryoffsetinblock += ep->e2d_reclen; + if (ep->e2d_ino) + enduseful = dp->i_offset; + } +/* notfound: */ + /* + * If we started in the middle of the directory and failed + * to find our target, we must check the beginning as well. + */ + if (numdirpasses == 2) { + numdirpasses--; + dp->i_offset = 0; + endsearch = dp->i_diroff; + goto searchloop; + } + if (bp != NULL) + brelse(bp); + /* + * If creating, and at end of pathname and current + * directory has not been removed, then can consider + * allowing file to be created. + */ + if ((nameiop == CREATE || nameiop == RENAME) && + (flags & ISLASTCN) && dp->i_e2fs_nlink != 0) { + /* + * Access for write is interpreted as allowing + * creation of files in the directory. + */ + if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0) + return (error); + /* + * Return an indication of where the new directory + * entry should be put. If we didn't find a slot, + * then set dp->i_count to 0 indicating + * that the new slot belongs at the end of the + * directory. If we found a slot, then the new entry + * can be put in the range from dp->i_offset to + * dp->i_offset + dp->i_count. + */ + if (slotstatus == NONE) { + dp->i_offset = roundup(dp->i_e2fs_size, dirblksize); + dp->i_count = 0; + enduseful = dp->i_offset; + } else { + dp->i_offset = slotoffset; + dp->i_count = slotsize; + if (enduseful < slotoffset + slotsize) + enduseful = slotoffset + slotsize; + } + dp->i_endoff = roundup(enduseful, dirblksize); + dp->i_flag |= IN_CHANGE | IN_UPDATE; + /* + * We return with the directory locked, so that + * the parameters we set up above will still be + * valid if we actually decide to do a direnter(). + * We return ni_vp == NULL to indicate that the entry + * does not currently exist; we leave a pointer to + * the (locked) directory inode in ndp->ni_dvp. + * The pathname buffer is saved so that the name + * can be obtained later. + * + * NB - if the directory is unlocked, then this + * information cannot be used. + */ + cnp->cn_flags |= SAVENAME; + if (!lockparent) + VOP_UNLOCK(vdp); + return (EJUSTRETURN); + } + /* + * Insert name into cache (as non-existent) if appropriate. + */ + if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) + cache_enter(vdp, *vpp, cnp); + return (ENOENT); + +found: + /* + * Check that directory length properly reflects presence + * of this entry. + */ + if (entryoffsetinblock + EXT2FS_DIRSIZ(ep->e2d_namlen) + > dp->i_e2fs_size) { + ufs_dirbad(dp, dp->i_offset, "i_size too small"); + dp->i_e2fs_size = entryoffsetinblock+EXT2FS_DIRSIZ(ep->e2d_namlen); + dp->i_flag |= IN_CHANGE | IN_UPDATE; + } + + /* + * Found component in pathname. + * If the final component of path name, save information + * in the cache as to where the entry was found. + */ + if ((flags & ISLASTCN) && nameiop == LOOKUP) + dp->i_diroff = dp->i_offset &~ (dirblksize - 1); + + /* + * If deleting, and at end of pathname, return + * parameters which can be used to remove file. + * If the wantparent flag isn't set, we return only + * the directory (in ndp->ni_dvp), otherwise we go + * on and lock the inode, being careful with ".". + */ + if (nameiop == DELETE && (flags & ISLASTCN)) { + /* + * Write access to directory required to delete files. + */ + if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0) + return (error); + /* + * Return pointer to current entry in dp->i_offset, + * and distance past previous entry (if there + * is a previous entry in this block) in dp->i_count. + * Save directory inode pointer in ndp->ni_dvp for dirremove(). + */ + if ((dp->i_offset & (dirblksize - 1)) == 0) + dp->i_count = 0; + else + dp->i_count = dp->i_offset - prevoff; + if (dp->i_number == dp->i_ino) { + VREF(vdp); + *vpp = vdp; + return (0); + } + if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) + return (error); + /* + * If directory is "sticky", then user must own + * the directory, or the file in it, else she + * may not delete it (unless she's root). This + * implements append-only directories. + */ + if ((dp->i_e2fs_mode & ISVTX) && + cred->cr_uid != 0 && + cred->cr_uid != dp->i_e2fs_uid && + VTOI(tdp)->i_e2fs_uid != cred->cr_uid) { + vput(tdp); + return (EPERM); + } + *vpp = tdp; + if (!lockparent) + VOP_UNLOCK(vdp); + return (0); + } + + /* + * If rewriting (RENAME), return the inode and the + * information required to rewrite the present directory + * Must get inode of directory entry to verify it's a + * regular file, or empty directory. + */ + if (nameiop == RENAME && wantparent && + (flags & ISLASTCN)) { + if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0) + return (error); + /* + * Careful about locking second inode. + * This can only occur if the target is ".". + */ + if (dp->i_number == dp->i_ino) + return (EISDIR); + if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) + return (error); + *vpp = tdp; + cnp->cn_flags |= SAVENAME; + if (!lockparent) + VOP_UNLOCK(vdp); + return (0); + } + + /* + * Step through the translation in the name. We do not `vput' the + * directory because we may need it again if a symbolic link + * is relative to the current directory. Instead we save it + * unlocked as "pdp". We must get the target inode before unlocking + * the directory to insure that the inode will not be removed + * before we get it. We prevent deadlock by always fetching + * inodes from the root, moving down the directory tree. Thus + * when following backward pointers ".." we must unlock the + * parent directory before getting the requested directory. + * There is a potential race condition here if both the current + * and parent directories are removed before the VFS_VGET for the + * inode associated with ".." returns. We hope that this occurs + * infrequently since we cannot avoid this race condition without + * implementing a sophisticated deadlock detection algorithm. + * Note also that this simple deadlock detection scheme will not + * work if the file system has any hard links other than ".." + * that point backwards in the directory structure. + */ + pdp = vdp; + if (flags & ISDOTDOT) { + VOP_UNLOCK(pdp); /* race to get the inode */ + if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) { + VOP_LOCK(pdp); + return (error); + } + if (lockparent && (flags & ISLASTCN) && + (error = VOP_LOCK(pdp)) != 0) { + vput(tdp); + return (error); + } + *vpp = tdp; + } else if (dp->i_number == dp->i_ino) { + VREF(vdp); /* we want ourself, ie "." */ + *vpp = vdp; + } else { + if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) + return (error); + if (!lockparent || !(flags & ISLASTCN)) + VOP_UNLOCK(pdp); + *vpp = tdp; + } + + /* + * Insert name into cache if appropriate. + */ + if (cnp->cn_flags & MAKEENTRY) + cache_enter(vdp, *vpp, cnp); + return (0); +} + +/* + * Do consistency checking on a directory entry: + * record length must be multiple of 4 + * entry must fit in rest of its dirblksize block + * record must be large enough to contain entry + * name is not longer than MAXNAMLEN + * name must be as long as advertised, and null terminated + */ +/* + * changed so that it confirms to ext2fs_check_dir_entry + */ +static int +ext2fs_dirbadentry(dp, de, entryoffsetinblock) + struct vnode *dp; + register struct ext2fs_direct *de; + int entryoffsetinblock; +{ + int dirblksize = VTOI(dp)->i_e2fs->e2fs_bsize; + + char * error_msg = NULL; + + if (de->e2d_reclen < EXT2FS_DIRSIZ(1)) /* e2d_namlen = 1 */ + error_msg = "rec_len is smaller than minimal"; + else if (de->e2d_reclen % 4 != 0) + error_msg = "rec_len % 4 != 0"; + else if (de->e2d_reclen < EXT2FS_DIRSIZ(de->e2d_namlen)) + error_msg = "reclen is too small for name_len"; + else if (entryoffsetinblock + de->e2d_reclen > dirblksize) + error_msg = "directory entry across blocks"; + else if (de->e2d_ino > VTOI(dp)->i_e2fs->e2fs.e2fs_icount) + error_msg = "inode out of bounds"; + + if (error_msg != NULL) { + printf( "bad directory entry: %s\n" + "offset=%d, inode=%lu, rec_len=%d, name_len=%d \n", + error_msg, entryoffsetinblock, + (unsigned long) de->e2d_ino, de->e2d_reclen, de->e2d_namlen); + panic("ext2fs_dirbadentry"); + } + return error_msg == NULL ? 0 : 1; +} + +/* + * Write a directory entry after a call to namei, using the parameters + * that it left in nameidata. The argument ip is the inode which the new + * directory entry will refer to. Dvp is a pointer to the directory to + * be written, which was left locked by namei. Remaining parameters + * (dp->i_offset, dp->i_count) indicate how the space for the new + * entry is to be obtained. + */ +int +ext2fs_direnter(ip, dvp, cnp) + struct inode *ip; + struct vnode *dvp; + register struct componentname *cnp; +{ + register struct ext2fs_direct *ep, *nep; + register struct inode *dp; + struct buf *bp; + struct ext2fs_direct newdir; + struct iovec aiov; + struct uio auio; + u_int dsize; + int error, loc, newentrysize, spacefree; + char *dirbuf; + int dirblksize = ip->i_e2fs->e2fs_bsize; + + +#ifdef DIAGNOSTIC + if ((cnp->cn_flags & SAVENAME) == 0) + panic("direnter: missing name"); +#endif + dp = VTOI(dvp); + newdir.e2d_ino = ip->i_number; + newdir.e2d_namlen = cnp->cn_namelen; + bcopy(cnp->cn_nameptr, newdir.e2d_name, (unsigned)cnp->cn_namelen + 1); + newentrysize = EXT2FS_DIRSIZ(newdir.e2d_namlen); + if (dp->i_count == 0) { + /* + * If dp->i_count is 0, then namei could find no + * space in the directory. Here, dp->i_offset will + * be on a directory block boundary and we will write the + * new entry into a fresh block. + */ + if (dp->i_offset & (dirblksize - 1)) + panic("ext2fs_direnter: newblk"); + auio.uio_offset = dp->i_offset; + newdir.e2d_reclen = dirblksize; + auio.uio_resid = newentrysize; + aiov.iov_len = newentrysize; + aiov.iov_base = (caddr_t)&newdir; + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_rw = UIO_WRITE; + auio.uio_segflg = UIO_SYSSPACE; + auio.uio_procp = (struct proc *)0; + error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred); + if (dirblksize > + VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize) + /* XXX should grow with balloc() */ + panic("ext2fs_direnter: frag size"); + else if (!error) { + dp->i_e2fs_size = roundup(dp->i_e2fs_size, dirblksize); + dp->i_flag |= IN_CHANGE; + } + return (error); + } + + /* + * If dp->i_count is non-zero, then namei found space + * for the new entry in the range dp->i_offset to + * dp->i_offset + dp->i_count in the directory. + * To use this space, we may have to compact the entries located + * there, by copying them together towards the beginning of the + * block, leaving the free space in one usable chunk at the end. + */ + + /* + * Get the block containing the space for the new directory entry. + */ + if ((error = VOP_BLKATOFF(dvp, (off_t)dp->i_offset, &dirbuf, &bp)) != 0) + return (error); + /* + * Find space for the new entry. In the simple case, the entry at + * offset base will have the space. If it does not, then namei + * arranged that compacting the region dp->i_offset to + * dp->i_offset + dp->i_count would yield the + * space. + */ + ep = (struct ext2fs_direct *)dirbuf; + dsize = EXT2FS_DIRSIZ(ep->e2d_namlen); + spacefree = ep->e2d_reclen - dsize; + for (loc = ep->e2d_reclen; loc < dp->i_count; ) { + nep = (struct ext2fs_direct *)(dirbuf + loc); + if (ep->e2d_ino) { + /* trim the existing slot */ + ep->e2d_reclen = dsize; + ep = (struct ext2fs_direct *)((char *)ep + dsize); + } else { + /* overwrite; nothing there; header is ours */ + spacefree += dsize; + } + dsize = EXT2FS_DIRSIZ(nep->e2d_namlen); + spacefree += nep->e2d_reclen - dsize; + loc += nep->e2d_reclen; + bcopy((caddr_t)nep, (caddr_t)ep, dsize); + } + /* + * Update the pointer fields in the previous entry (if any), + * copy in the new entry, and write out the block. + */ + if (ep->e2d_ino == 0) { +#ifdef DIAGNOSTIC + if (spacefree + dsize < newentrysize) + panic("ext2fs_direnter: compact1"); +#endif + newdir.e2d_reclen = spacefree + dsize; + } else { +#ifdef DIAGNOSTIC + if (spacefree < newentrysize) { + printf("ext2fs_direnter: compact2 %u %u", + (u_int)spacefree, (u_int)newentrysize); + panic("ext2fs_direnter: compact2"); + } +#endif + newdir.e2d_reclen = spacefree; + ep->e2d_reclen = dsize; + ep = (struct ext2fs_direct *)((char *)ep + dsize); + } + bcopy((caddr_t)&newdir, (caddr_t)ep, (u_int)newentrysize); + error = VOP_BWRITE(bp); + dp->i_flag |= IN_CHANGE | IN_UPDATE; + if (!error && dp->i_endoff && dp->i_endoff < dp->i_e2fs_size) + error = VOP_TRUNCATE(dvp, (off_t)dp->i_endoff, IO_SYNC, + cnp->cn_cred, cnp->cn_proc); + return (error); +} + +/* + * Remove a directory entry after a call to namei, using + * the parameters which it left in nameidata. The entry + * dp->i_offset contains the offset into the directory of the + * entry to be eliminated. The dp->i_count field contains the + * size of the previous record in the directory. If this + * is 0, the first entry is being deleted, so we need only + * zero the inode number to mark the entry as free. If the + * entry is not the first in the directory, we must reclaim + * the space of the now empty record by adding the record size + * to the size of the previous entry. + */ +int +ext2fs_dirremove(dvp, cnp) + struct vnode *dvp; + struct componentname *cnp; +{ + register struct inode *dp; + struct ext2fs_direct *ep; + struct buf *bp; + int error; + + dp = VTOI(dvp); + if (dp->i_count == 0) { + /* + * First entry in block: set d_ino to zero. + */ + error = VOP_BLKATOFF(dvp, (off_t)dp->i_offset, (char **)&ep, &bp); + if (error != 0) + return (error); + ep->e2d_ino = 0; + error = VOP_BWRITE(bp); + dp->i_flag |= IN_CHANGE | IN_UPDATE; + return (error); + } + /* + * Collapse new free space into previous entry. + */ + error = VOP_BLKATOFF(dvp, (off_t)(dp->i_offset - dp->i_count), + (char **)&ep, &bp); + if (error != 0) + return (error); + ep->e2d_reclen += dp->i_reclen; + error = VOP_BWRITE(bp); + dp->i_flag |= IN_CHANGE | IN_UPDATE; + return (error); +} + +/* + * Rewrite an existing directory entry to point at the inode + * supplied. The parameters describing the directory entry are + * set up by a call to namei. + */ +int +ext2fs_dirrewrite(dp, ip, cnp) + struct inode *dp, *ip; + struct componentname *cnp; +{ + struct buf *bp; + struct ext2fs_direct *ep; + struct vnode *vdp = ITOV(dp); + int error; + + error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, (char **)&ep, &bp); + if (error != 0) + return (error); + ep->e2d_ino = ip->i_number; + error = VOP_BWRITE(bp); + dp->i_flag |= IN_CHANGE | IN_UPDATE; + return (error); +} + +/* + * Check if a directory is empty or not. + * Inode supplied must be locked. + * + * Using a struct dirtemplate here is not precisely + * what we want, but better than using a struct ext2fs_direct. + * + * NB: does not handle corrupted directories. + */ +int +ext2fs_dirempty(ip, parentino, cred) + register struct inode *ip; + ino_t parentino; + struct ucred *cred; +{ + register off_t off; + struct ext2fs_dirtemplate dbuf; + register struct ext2fs_direct *dp = (struct ext2fs_direct *)&dbuf; + int error, count, namlen; + +#define MINDIRSIZ (sizeof (struct ext2fs_dirtemplate) / 2) + + for (off = 0; off < ip->i_e2fs_size; off += dp->e2d_reclen) { + error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off, + UIO_SYSSPACE, IO_NODELOCKED, cred, &count, (struct proc *)0); + /* + * Since we read MINDIRSIZ, residual must + * be 0 unless we're at end of file. + */ + if (error || count != 0) + return (0); + /* avoid infinite loops */ + if (dp->e2d_reclen == 0) + return (0); + /* skip empty entries */ + if (dp->e2d_ino == 0) + continue; + /* accept only "." and ".." */ + namlen = dp->e2d_namlen; + if (namlen > 2) + return (0); + if (dp->e2d_name[0] != '.') + return (0); + /* + * At this point namlen must be 1 or 2. + * 1 implies ".", 2 implies ".." if second + * char is also "." + */ + if (namlen == 1) + continue; + if (dp->e2d_name[1] == '.' && dp->e2d_ino == parentino) + continue; + return (0); + } + return (1); +} + +/* + * Check if source directory is in the path of the target directory. + * Target is supplied locked, source is unlocked. + * The target is always vput before returning. + */ +int +ext2fs_checkpath(source, target, cred) + struct inode *source, *target; + struct ucred *cred; +{ + struct vnode *vp; + int error, rootino, namlen; + struct ext2fs_dirtemplate dirbuf; + + vp = ITOV(target); + if (target->i_number == source->i_number) { + error = EEXIST; + goto out; + } + rootino = ROOTINO; + error = 0; + if (target->i_number == rootino) + goto out; + + for (;;) { + if (vp->v_type != VDIR) { + error = ENOTDIR; + break; + } + error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf, + sizeof (struct ext2fs_dirtemplate), (off_t)0, UIO_SYSSPACE, + IO_NODELOCKED, cred, (int *)0, (struct proc *)0); + if (error != 0) + break; + namlen = dirbuf.dotdot_namlen; + if (namlen != 2 || + dirbuf.dotdot_name[0] != '.' || + dirbuf.dotdot_name[1] != '.') { + error = ENOTDIR; + break; + } + if (dirbuf.dotdot_ino == source->i_number) { + error = EINVAL; + break; + } + if (dirbuf.dotdot_ino == rootino) + break; + vput(vp); + error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino, &vp); + if (error != 0) { + vp = NULL; + break; + } + } + +out: + if (error == ENOTDIR) { + printf("checkpath: .. not a directory\n"); + panic("checkpath"); + } + if (vp != NULL) + vput(vp); + return (error); +} + diff --git a/sys/ufs/ext2fs/ext2fs_readwrite.c b/sys/ufs/ext2fs/ext2fs_readwrite.c new file mode 100644 index 00000000000..a94e18deb80 --- /dev/null +++ b/sys/ufs/ext2fs/ext2fs_readwrite.c @@ -0,0 +1,298 @@ +/* $NetBSD: ufs_readwrite.c,v 1.10 1997/01/30 09:52:26 tls Exp $ */ + +/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ufs_readwrite.c 8.8 (Berkeley) 8/4/94 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/resourcevar.h> +#include <sys/kernel.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/conf.h> +#include <sys/mount.h> +#include <sys/vnode.h> +#include <sys/malloc.h> +#include <sys/signalvar.h> + +#include <vm/vm.h> + +#include <ufs/ufs/quota.h> +#include <ufs/ufs/inode.h> +#include <ufs/ext2fs/ext2fs.h> +#include <ufs/ext2fs/ext2fs_extern.h> + + +#define doclusterread 0 /* XXX underway */ +#define doclusterwrite 0 + +/* + * Vnode op for reading. + */ +/* ARGSUSED */ +int +ext2fs_read(v) + void *v; +{ + struct vop_read_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap = v; + register struct vnode *vp; + register struct inode *ip; + register struct uio *uio; + register struct m_ext2fs *fs; + struct buf *bp; + daddr_t lbn, nextlbn; + off_t bytesinfile; + long size, xfersize, blkoffset; + int error; + u_short mode; + + vp = ap->a_vp; + ip = VTOI(vp); + mode = ip->i_e2fs_mode; + uio = ap->a_uio; + +#ifdef DIAGNOSTIC + if (uio->uio_rw != UIO_READ) + panic("%s: mode", "ext2fs_read"); + + if (vp->v_type == VLNK) { + if ((int)ip->i_e2fs_size < vp->v_mount->mnt_maxsymlinklen || + (vp->v_mount->mnt_maxsymlinklen == 0 && + ip->i_e2fs_nblock == 0)) + panic("%s: short symlink", "ext2fs_read"); + } else if (vp->v_type != VREG && vp->v_type != VDIR) + panic("%s: type %d", "ext2fs_read", vp->v_type); +#endif + fs = ip->i_e2fs; + if ((u_int64_t)uio->uio_offset > + ((u_int64_t)0x80000000 * fs->e2fs_bsize - 1)) + return (EFBIG); + + for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) { + if ((bytesinfile = ip->i_e2fs_size - uio->uio_offset) <= 0) + break; + lbn = lblkno(fs, uio->uio_offset); + nextlbn = lbn + 1; + size = fs->e2fs_bsize; + blkoffset = blkoff(fs, uio->uio_offset); + xfersize = fs->e2fs_bsize - blkoffset; + if (uio->uio_resid < xfersize) + xfersize = uio->uio_resid; + if (bytesinfile < xfersize) + xfersize = bytesinfile; + + if (lblktosize(fs, nextlbn) >= ip->i_e2fs_size) + error = bread(vp, lbn, size, NOCRED, &bp); + else if (doclusterread) + error = cluster_read(vp, + ip->i_e2fs_size, lbn, size, NOCRED, &bp); + else if (lbn - 1 == vp->v_lastr) { + int nextsize = fs->e2fs_bsize; + error = breadn(vp, lbn, + size, &nextlbn, &nextsize, 1, NOCRED, &bp); + } else + error = bread(vp, lbn, size, NOCRED, &bp); + if (error) + break; + vp->v_lastr = lbn; + + /* + * We should only get non-zero b_resid when an I/O error + * has occurred, which should cause us to break above. + * However, if the short read did not cause an error, + * then we want to ensure that we do not uiomove bad + * or uninitialized data. + */ + size -= bp->b_resid; + if (size < xfersize) { + if (size == 0) + break; + xfersize = size; + } + error = uiomove((char *)bp->b_data + blkoffset, (int)xfersize, + uio); + if (error) + break; + brelse(bp); + } + if (bp != NULL) + brelse(bp); + if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) + ip->i_flag |= IN_ACCESS; + return (error); +} + +/* + * Vnode op for writing. + */ +int +ext2fs_write(v) + void *v; +{ + struct vop_write_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap = v; + register struct vnode *vp; + register struct uio *uio; + register struct inode *ip; + register struct m_ext2fs *fs; + struct buf *bp; + struct proc *p; + daddr_t lbn; + off_t osize; + int blkoffset, error, flags, ioflag, resid, size, xfersize; + struct timespec ts; + + ioflag = ap->a_ioflag; + uio = ap->a_uio; + vp = ap->a_vp; + ip = VTOI(vp); + +#ifdef DIAGNOSTIC + if (uio->uio_rw != UIO_WRITE) + panic("%s: mode", "ext2fs_write"); +#endif + + switch (vp->v_type) { + case VREG: + if (ioflag & IO_APPEND) + uio->uio_offset = ip->i_e2fs_size; + if ((ip->i_e2fs_flags & EXT2_APPEND) && + uio->uio_offset != ip->i_e2fs_size) + return (EPERM); + /* FALLTHROUGH */ + case VLNK: + break; + case VDIR: + if ((ioflag & IO_SYNC) == 0) + panic("%s: nonsync dir write", "ext2fs_write"); + break; + default: + panic("%s: type", "ext2fs_write"); + } + + fs = ip->i_e2fs; + if (uio->uio_offset < 0 || + (u_int64_t)uio->uio_offset + uio->uio_resid > + ((u_int64_t)0x80000000 * fs->e2fs_bsize - 1)) + return (EFBIG); + /* + * Maybe this should be above the vnode op call, but so long as + * file servers have no limits, I don't think it matters. + */ + p = uio->uio_procp; + if (vp->v_type == VREG && p && + uio->uio_offset + uio->uio_resid > + p->p_rlimit[RLIMIT_FSIZE].rlim_cur) { + psignal(p, SIGXFSZ); + return (EFBIG); + } + + resid = uio->uio_resid; + osize = ip->i_e2fs_size; + flags = ioflag & IO_SYNC ? B_SYNC : 0; + + for (error = 0; uio->uio_resid > 0;) { + lbn = lblkno(fs, uio->uio_offset); + blkoffset = blkoff(fs, uio->uio_offset); + xfersize = fs->e2fs_bsize - blkoffset; + if (uio->uio_resid < xfersize) + xfersize = uio->uio_resid; + if (fs->e2fs_bsize > xfersize) + flags |= B_CLRBUF; + else + flags &= ~B_CLRBUF; + + error = ext2fs_balloc(ip, + lbn, blkoffset + xfersize, ap->a_cred, &bp, flags); + if (error) + break; + if (uio->uio_offset + xfersize > ip->i_e2fs_size) { + ip->i_e2fs_size = uio->uio_offset + xfersize; + vnode_pager_setsize(vp, (u_long)ip->i_e2fs_size); + } + (void)vnode_pager_uncache(vp); + + size = fs->e2fs_bsize - bp->b_resid; + if (size < xfersize) + xfersize = size; + + error = + uiomove((char *)bp->b_data + blkoffset, (int)xfersize, uio); + if (ioflag & IO_SYNC) + (void)bwrite(bp); + else if (xfersize + blkoffset == fs->e2fs_bsize) + if (doclusterwrite) + cluster_write(bp, ip->i_e2fs_size); + else + bawrite(bp); + else + bdwrite(bp); + if (error || xfersize == 0) + break; + ip->i_flag |= IN_CHANGE | IN_UPDATE; + } + /* + * If we successfully wrote any data, and we are not the superuser + * we clear the setuid and setgid bits as a precaution against + * tampering. + */ + if (resid > uio->uio_resid && ap->a_cred && ap->a_cred->cr_uid != 0) + ip->i_e2fs_mode &= ~(ISUID | ISGID); + if (error) { + if (ioflag & IO_UNIT) { + (void)VOP_TRUNCATE(vp, osize, + ioflag & IO_SYNC, ap->a_cred, uio->uio_procp); + uio->uio_offset -= resid - uio->uio_resid; + uio->uio_resid = resid; + } + } else if (resid > uio->uio_resid && (ioflag & IO_SYNC)) { + TIMEVAL_TO_TIMESPEC(&time, &ts); + error = VOP_UPDATE(vp, &ts, &ts, 1); + } + return (error); +} diff --git a/sys/ufs/ext2fs/ext2fs_subr.c b/sys/ufs/ext2fs/ext2fs_subr.c new file mode 100644 index 00000000000..0c6ce13c12f --- /dev/null +++ b/sys/ufs/ext2fs/ext2fs_subr.c @@ -0,0 +1,120 @@ +/* $NetBSD: ffs_subr.c,v 1.9 1996/10/12 21:58:45 christos Exp $ */ + +/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */ + +/* + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ffs_subr.c 8.2 (Berkeley) 9/21/93 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <ufs/ext2fs/ext2fs.h> +#include <ufs/ext2fs/ext2fs_extern.h> + +#ifdef _KERNEL +#include <sys/vnode.h> +#include <sys/buf.h> +#include <ufs/ufs/quota.h> +#include <ufs/ufs/inode.h> + +/* + * Return buffer with the contents of block "offset" from the beginning of + * directory "ip". If "res" is non-zero, fill it in with a pointer to the + * remaining space in the directory. + */ +int +ext2fs_blkatoff(v) + void *v; +{ + struct vop_blkatoff_args /* { + struct vnode *a_vp; + off_t a_offset; + char **a_res; + struct buf **a_bpp; + } */ *ap = v; + struct inode *ip; + register struct m_ext2fs *fs; + struct buf *bp; + daddr_t lbn; + int error; + + ip = VTOI(ap->a_vp); + fs = ip->i_e2fs; + lbn = lblkno(fs, ap->a_offset); + + *ap->a_bpp = NULL; + if ((error = bread(ap->a_vp, lbn, fs->e2fs_bsize, NOCRED, &bp)) != 0) { + brelse(bp); + return (error); + } + if (ap->a_res) + *ap->a_res = (char *)bp->b_data + blkoff(fs, ap->a_offset); + *ap->a_bpp = bp; + return (0); +} +#endif + +#if defined(_KERNEL) && defined(DIAGNOSTIC) +void +ext2fs_checkoverlap(bp, ip) + struct buf *bp; + struct inode *ip; +{ + register struct buf *ebp, *ep; + register daddr_t start, last; + struct vnode *vp; + + ebp = &buf[nbuf]; + start = bp->b_blkno; + last = start + btodb(bp->b_bcount) - 1; + for (ep = buf; ep < ebp; ep++) { + if (ep == bp || (ep->b_flags & B_INVAL) || + ep->b_vp == NULLVP) + continue; + if (VOP_BMAP(ep->b_vp, (daddr_t)0, &vp, (daddr_t)0, NULL)) + continue; + if (vp != ip->i_devvp) + continue; + /* look for overlap */ + if (ep->b_bcount == 0 || ep->b_blkno > last || + ep->b_blkno + btodb(ep->b_bcount) <= start) + continue; + vprint("Disk overlap", vp); + printf("\tstart %d, end %d overlap start %d, end %ld\n", + start, last, ep->b_blkno, + ep->b_blkno + btodb(ep->b_bcount) - 1); + panic("Disk buffer overlap"); + } +} +#endif /* DIAGNOSTIC */ diff --git a/sys/ufs/ext2fs/ext2fs_vfsops.c b/sys/ufs/ext2fs/ext2fs_vfsops.c new file mode 100644 index 00000000000..bbfa736b6bc --- /dev/null +++ b/sys/ufs/ext2fs/ext2fs_vfsops.c @@ -0,0 +1,996 @@ +/* $NetBSD: ffs_vfsops.c,v 1.21 1996/10/12 21:58:47 christos Exp $ */ + +/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */ + +/* + * Copyright (c) 1989, 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ffs_vfsops.c 8.14 (Berkeley) 11/28/94 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/namei.h> +#include <sys/proc.h> +#include <sys/kernel.h> +#include <sys/vnode.h> +#include <sys/socket.h> +#include <sys/mount.h> +#include <sys/buf.h> +#include <sys/device.h> +#include <sys/mbuf.h> +#include <sys/file.h> +#include <sys/disklabel.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/malloc.h> + +#include <miscfs/specfs/specdev.h> + +#include <ufs/ufs/quota.h> +#include <ufs/ufs/ufsmount.h> +#include <ufs/ufs/inode.h> +#include <ufs/ufs/dir.h> +#include <ufs/ufs/ufs_extern.h> + +#include <ufs/ext2fs/ext2fs.h> +#include <ufs/ext2fs/ext2fs_extern.h> + +int ext2fs_sbupdate __P((struct ufsmount *, int)); +int ext2fs_check_export __P((struct mount *, struct ufid *, struct mbuf *, + struct vnode **, int *, struct ucred **)); + +struct vfsops ext2fs_vfsops = { + MOUNT_EXT2FS, + ext2fs_mount, + ufs_start, + ext2fs_unmount, + ufs_root, + ufs_quotactl, + ext2fs_statfs, + ext2fs_sync, + ext2fs_vget, + ext2fs_fhtovp, + ext2fs_vptofh, + ext2fs_init, + ext2fs_mountroot, +}; + +extern u_long ext2gennumber; + +/* + * This is the generic part of fhtovp called after the underlying + * filesystem has validated the file handle. + * + * Verify that a host should have access to a filesystem, and if so + * return a vnode for the presented file handle. + */ +int +ext2fs_check_export(mp, ufhp, nam, vpp, exflagsp, credanonp) + register struct mount *mp; + struct ufid *ufhp; + struct mbuf *nam; + struct vnode **vpp; + int *exflagsp; + struct ucred **credanonp; +{ + register struct inode *ip; + register struct netcred *np; + register struct ufsmount *ump = VFSTOUFS(mp); + struct vnode *nvp; + int error; + + /* + * Get the export permission structure for this <mp, client> tuple. + */ + np = vfs_export_lookup(mp, &ump->um_export, nam); + if (np == NULL) + return (EACCES); + + if ((error = VFS_VGET(mp, ufhp->ufid_ino, &nvp)) != 0) { + *vpp = NULLVP; + return (error); + } + ip = VTOI(nvp); + if (ip->i_e2fs_mode == 0 || ip->i_e2fs_dtime != 0 || + ip->i_e2fs_gen != ufhp->ufid_gen) { + vput(nvp); + *vpp = NULLVP; + return (ESTALE); + } + *vpp = nvp; + *exflagsp = np->netc_exflags; + *credanonp = &np->netc_anon; + return (0); +} + + +/* + * Called by main() when ext2fs is going to be mounted as root. + * + * Name is updated by mount(8) after booting. + */ +#define ROOTNAME "root_device" + +int +ext2fs_mountroot() +{ + extern struct vnode *rootvp; + register struct m_ext2fs *fs; + register struct mount *mp; + struct proc *p = curproc; /* XXX */ + struct ufsmount *ump; + size_t size; + int error; + + if (root_device->dv_class != DV_DISK) + return (ENODEV); + + /* + * Get vnodes for swapdev and rootdev. + */ + if (bdevvp(swapdev, &swapdev_vp) || bdevvp(rootdev, &rootvp)) + panic("ext2fs_mountroot: can't setup bdevvp's"); + + mp = malloc(sizeof(struct mount), M_MOUNT, M_WAITOK); + bzero((char *)mp, sizeof(struct mount)); + mp->mnt_op = &ext2fs_vfsops; + mp->mnt_flag = MNT_RDONLY; + if ((error = ext2fs_mountfs(rootvp, mp, p)) != 0) { + free(mp, M_MOUNT); + return (error); + } + if ((error = vfs_lock(mp)) != 0) { + (void)ext2fs_unmount(mp, 0, p); + free(mp, M_MOUNT); + return (error); + } + CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list); + mp->mnt_vnodecovered = NULLVP; + ump = VFSTOUFS(mp); + fs = ump->um_e2fs; + bzero(fs->e2fs_fsmnt, sizeof(fs->e2fs_fsmnt)); + fs->e2fs_fsmnt[0] = '/'; + bcopy(fs->e2fs_fsmnt, mp->mnt_stat.f_mntonname, MNAMELEN); + (void) copystr(ROOTNAME, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, + &size); + bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); + (void)ext2fs_statfs(mp, &mp->mnt_stat, p); + vfs_unlock(mp); + inittodr(fs->e2fs.e2fs_wtime); + return (0); +} + +/* + * VFS Operations. + * + * mount system call + */ +int +ext2fs_mount(mp, path, data, ndp, p) + register struct mount *mp; + const char *path; + void * data; + struct nameidata *ndp; + struct proc *p; +{ + struct vnode *devvp; + struct ufs_args args; + struct ufsmount *ump = NULL; + register struct m_ext2fs *fs; + size_t size; + int error, flags; + mode_t accessmode; + + error = copyin(data, (caddr_t)&args, sizeof (struct ufs_args)); + if (error) + return (error); + /* + * If updating, check whether changing from read-only to + * read/write; if there is no device name, that's all we do. + */ + if (mp->mnt_flag & MNT_UPDATE) { + ump = VFSTOUFS(mp); + fs = ump->um_e2fs; + if (fs->e2fs_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) { + flags = WRITECLOSE; + if (mp->mnt_flag & MNT_FORCE) + flags |= FORCECLOSE; + if (vfs_busy(mp)) + return (EBUSY); + error = ext2fs_flushfiles(mp, flags, p); + if (error == 0 && + ext2fs_cgupdate(ump, MNT_WAIT) == 0 && + (fs->e2fs.e2fs_state & E2FS_ERRORS) == 0) { + fs->e2fs.e2fs_state = E2FS_ISCLEAN; + (void) ext2fs_sbupdate(ump, MNT_WAIT); + } + vfs_unbusy(mp); + if (error) + return (error); + fs->e2fs_ronly = 1; + } + if (mp->mnt_flag & MNT_RELOAD) { + error = ext2fs_reload(mp, ndp->ni_cnd.cn_cred, p); + if (error) + return (error); + } + if (fs->e2fs_ronly && (mp->mnt_flag & MNT_WANTRDWR)) { + /* + * If upgrade to read-write by non-root, then verify + * that user has necessary permissions on the device. + */ + if (p->p_ucred->cr_uid != 0) { + devvp = ump->um_devvp; + VOP_LOCK(devvp); + error = VOP_ACCESS(devvp, VREAD | VWRITE, + p->p_ucred, p); + if (error) { + VOP_UNLOCK(devvp); + return (error); + } + VOP_UNLOCK(devvp); + } + fs->e2fs_ronly = 0; + if (fs->e2fs.e2fs_state == E2FS_ISCLEAN) + fs->e2fs.e2fs_state = 0; + else + fs->e2fs.e2fs_state = E2FS_ERRORS; + fs->e2fs_fmod = 1; + } + if (args.fspec == 0) { + /* + * Process export requests. + */ + return (vfs_export(mp, &ump->um_export, &args.export)); + } + } + /* + * Not an update, or updating the name: look up the name + * and verify that it refers to a sensible block device. + */ + NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p); + if ((error = namei(ndp)) != 0) + return (error); + devvp = ndp->ni_vp; + + if (devvp->v_type != VBLK) { + vrele(devvp); + return (ENOTBLK); + } + if (major(devvp->v_rdev) >= nblkdev) { + vrele(devvp); + return (ENXIO); + } + /* + * If mount by non-root, then verify that user has necessary + * permissions on the device. + */ + if (p->p_ucred->cr_uid != 0) { + accessmode = VREAD; + if ((mp->mnt_flag & MNT_RDONLY) == 0) + accessmode |= VWRITE; + VOP_LOCK(devvp); + error = VOP_ACCESS(devvp, accessmode, p->p_ucred, p); + if (error) { + vput(devvp); + return (error); + } + VOP_UNLOCK(devvp); + } + if ((mp->mnt_flag & MNT_UPDATE) == 0) + error = ext2fs_mountfs(devvp, mp, p); + else { + if (devvp != ump->um_devvp) + error = EINVAL; /* needs translation */ + else + vrele(devvp); + } + if (error) { + vrele(devvp); + return (error); + } + ump = VFSTOUFS(mp); + fs = ump->um_e2fs; + (void) copyinstr(path, fs->e2fs_fsmnt, sizeof(fs->e2fs_fsmnt) - 1, &size); + bzero(fs->e2fs_fsmnt + size, sizeof(fs->e2fs_fsmnt) - size); + bcopy(fs->e2fs_fsmnt, mp->mnt_stat.f_mntonname, MNAMELEN); + (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, + &size); + bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); + if (fs->e2fs_fmod != 0) { /* XXX */ + fs->e2fs_fmod = 0; + if (fs->e2fs.e2fs_state == 0) + fs->e2fs.e2fs_wtime = time.tv_sec; + else + printf("%s: file system not clean; please fsck(8)\n", + mp->mnt_stat.f_mntfromname); + (void) ext2fs_cgupdate(ump, MNT_WAIT); + } + return (0); +} + +/* + * Reload all incore data for a filesystem (used after running fsck on + * the root filesystem and finding things to fix). The filesystem must + * be mounted read-only. + * + * Things to do to update the mount: + * 1) invalidate all cached meta-data. + * 2) re-read superblock from disk. + * 3) re-read summary information from disk. + * 4) invalidate all inactive vnodes. + * 5) invalidate all cached file data. + * 6) re-read inode data for all active vnodes. + */ +int +ext2fs_reload(mountp, cred, p) + register struct mount *mountp; + struct ucred *cred; + struct proc *p; +{ + register struct vnode *vp, *nvp, *devvp; + struct inode *ip; + struct buf *bp; + struct m_ext2fs *fs; + struct ext2fs *newfs; + struct partinfo dpart; + int i, size, error; + + if ((mountp->mnt_flag & MNT_RDONLY) == 0) + return (EINVAL); + /* + * Step 1: invalidate all cached meta-data. + */ + devvp = VFSTOUFS(mountp)->um_devvp; + if (vinvalbuf(devvp, 0, cred, p, 0, 0)) + panic("ext2fs_reload: dirty1"); + /* + * Step 2: re-read superblock from disk. + */ + if (VOP_IOCTL(devvp, DIOCGPART, (caddr_t)&dpart, FREAD, NOCRED, p) != 0) + size = DEV_BSIZE; + else + size = dpart.disklab->d_secsize; + error = bread(devvp, (daddr_t)(SBOFF / size), SBSIZE, NOCRED, &bp); + if (error) + return (error); + newfs = (struct ext2fs *)bp->b_data; + if (newfs->e2fs_magic != E2FS_MAGIC || newfs->e2fs_rev != E2FS_REV) { +#ifdef DIAGNOSTIC + printf("Wrong magic number: %x (expected %x for ext2 fs)", + newfs->e2fs_magic, E2FS_MAGIC); + printf("or wrong revision number: %x (expected %x for ext2 fs)", + newfs->e2fs_rev, E2FS_REV); +#endif + brelse(bp); + return (EIO); /* XXX needs translation */ + } + if (newfs->e2fs_log_bsize > 2) { /* block size = 1024|2048|4096 */ +#ifdef DIAGNOSTIC + printf("wrong block size: %d (expected <=2 for ext2 fs)\n", + newfs->e2fs_log_bsize); +#endif + brelse(bp); + return (EIO); /* XXX needs translation */ + } + + + fs = VFSTOUFS(mountp)->um_e2fs; + /* + * copy in new superblock, and compute in-memory values + */ + bcopy(newfs, &fs->e2fs, SBSIZE); + fs->e2fs_ncg = howmany(fs->e2fs.e2fs_bcount - fs->e2fs.e2fs_first_dblock, + fs->e2fs.e2fs_bpg); + /* XXX assume hw bsize = 512 */ + fs->e2fs_fsbtodb = fs->e2fs.e2fs_log_bsize + 1; + fs->e2fs_bsize = 1024 << fs->e2fs.e2fs_log_bsize; + fs->e2fs_bshift = LOG_MINBSIZE + fs->e2fs.e2fs_log_bsize; + fs->e2fs_qbmask = fs->e2fs_bsize - 1; + fs->e2fs_bmask = ~fs->e2fs_qbmask; + fs->e2fs_ngdb = howmany(fs->e2fs_ncg, + fs->e2fs_bsize / sizeof(struct ext2_gd)); + fs->e2fs_ipb = fs->e2fs_bsize / sizeof(struct ext2fs_dinode); + fs->e2fs_itpg = fs->e2fs.e2fs_ipg/fs->e2fs_ipb; + + /* + * Step 3: re-read summary information from disk. + */ + + for (i=0; i < fs->e2fs_ngdb; i++) { + error = bread(devvp , fsbtodb(fs, ((fs->e2fs_bsize>1024)?0:1)+i+1), + fs->e2fs_bsize, NOCRED, &bp); + if (error) + return (error); + bcopy(bp->b_data, + &fs->e2fs_gd[i* fs->e2fs_bsize / sizeof(struct ext2_gd)], + fs->e2fs_bsize); + brelse(bp); + } + +loop: + for (vp = mountp->mnt_vnodelist.lh_first; vp != NULL; vp = nvp) { + nvp = vp->v_mntvnodes.le_next; + /* + * Step 4: invalidate all inactive vnodes. + */ + if (vp->v_usecount == 0) { + vgone(vp); + continue; + } + /* + * Step 5: invalidate all cached file data. + */ + if (vget(vp, 1)) + goto loop; + if (vinvalbuf(vp, 0, cred, p, 0, 0)) + panic("ext2fs_reload: dirty2"); + /* + * Step 6: re-read inode data for all active vnodes. + */ + ip = VTOI(vp); + error = bread(devvp, fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), + (int)fs->e2fs_bsize, NOCRED, &bp); + if (error) { + vput(vp); + return (error); + } + bcopy((struct ext2fs_dinode *)bp->b_data + + ino_to_fsbo(fs, ip->i_number), + &ip->i_din.e2fs_din, sizeof(struct ext2fs_dinode)); + brelse(bp); + vput(vp); + if (vp->v_mount != mountp) + goto loop; + } + return (0); +} + +/* + * Common code for mount and mountroot + */ +int +ext2fs_mountfs(devvp, mp, p) + register struct vnode *devvp; + struct mount *mp; + struct proc *p; +{ + register struct ufsmount *ump; + struct buf *bp; + register struct ext2fs *fs; + register struct m_ext2fs *m_fs; + dev_t dev; + struct partinfo dpart; + int error, i, size, ronly; + struct ucred *cred; + extern struct vnode *rootvp; + + dev = devvp->v_rdev; + cred = p ? p->p_ucred : NOCRED; + /* + * Disallow multiple mounts of the same device. + * Disallow mounting of a device that is currently in use + * (except for root, which might share swap device for miniroot). + * Flush out any old buffers remaining from a previous use. + */ + if ((error = vfs_mountedon(devvp)) != 0) + return (error); + if (vcount(devvp) > 1 && devvp != rootvp) + return (EBUSY); + if ((error = vinvalbuf(devvp, V_SAVE, cred, p, 0, 0)) != 0) + return (error); + + ronly = (mp->mnt_flag & MNT_RDONLY) != 0; + error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p); + if (error) + return (error); + if (VOP_IOCTL(devvp, DIOCGPART, (caddr_t)&dpart, FREAD, cred, p) != 0) + size = DEV_BSIZE; + else + size = dpart.disklab->d_secsize; + + bp = NULL; + ump = NULL; + +#ifdef DEBUG_EXT2 + printf("sb size: %d ino size %d\n", sizeof(struct ext2fs), + sizeof(struct ext2fs_dinode)); +#endif + error = bread(devvp, (SBOFF / DEV_BSIZE), SBSIZE, cred, &bp); + if (error) + goto out; + fs = (struct ext2fs *)bp->b_data; + if (fs->e2fs_magic != E2FS_MAGIC || fs->e2fs_rev != E2FS_REV) { +#ifdef DIAGNOSTIC + printf("Wrong magic number: %x (expected %x for ext2 fs)", + fs->e2fs_magic, E2FS_MAGIC); + printf(" or wrong revision number: %x (expected %x for ext2 fs)\n", + fs->e2fs_rev, E2FS_REV); +#endif + error = EINVAL; /* XXX needs translation */ + goto out; + } + + if (fs->e2fs_log_bsize > 2) { /* block size = 1024|2048|4096 */ +#ifdef DIAGNOSTIC + printf("wrong block size: %d (expected <2 for ext2 fs)\n", + fs->e2fs_log_bsize); +#endif + error = EINVAL; /* XXX needs translation */ + goto out; + } + + ump = malloc(sizeof *ump, M_UFSMNT, M_WAITOK); + bzero((caddr_t)ump, sizeof *ump); + ump->um_e2fs = malloc(sizeof(struct m_ext2fs), M_UFSMNT, M_WAITOK); + bcopy(bp->b_data, ump->um_e2fs, SBSIZE); + brelse(bp); + bp = NULL; + m_fs = ump->um_e2fs; + m_fs->e2fs_ronly = ronly; + if (ronly == 0) { + if (m_fs->e2fs.e2fs_state == E2FS_ISCLEAN) + m_fs->e2fs.e2fs_state = 0; + else + m_fs->e2fs.e2fs_state = E2FS_ERRORS; + m_fs->e2fs_fmod = 1; + } + + /* compute dynamic sb infos */ + m_fs->e2fs_ncg = + howmany(m_fs->e2fs.e2fs_bcount - m_fs->e2fs.e2fs_first_dblock, + m_fs->e2fs.e2fs_bpg); + /* XXX assume hw bsize = 512 */ + m_fs->e2fs_fsbtodb = m_fs->e2fs.e2fs_log_bsize + 1; + m_fs->e2fs_bsize = 1024 << m_fs->e2fs.e2fs_log_bsize; + m_fs->e2fs_bshift = LOG_MINBSIZE + m_fs->e2fs.e2fs_log_bsize; + m_fs->e2fs_qbmask = m_fs->e2fs_bsize - 1; + m_fs->e2fs_bmask = ~m_fs->e2fs_qbmask; + m_fs->e2fs_ngdb = howmany(m_fs->e2fs_ncg, + m_fs->e2fs_bsize / sizeof(struct ext2_gd)); + m_fs->e2fs_ipb = m_fs->e2fs_bsize / sizeof(struct ext2fs_dinode); + m_fs->e2fs_itpg = m_fs->e2fs.e2fs_ipg/m_fs->e2fs_ipb; + + m_fs->e2fs_gd = malloc(m_fs->e2fs_ngdb * m_fs->e2fs_bsize, + M_UFSMNT, M_WAITOK); + for (i=0; i < m_fs->e2fs_ngdb; i++) { + error = bread(devvp , fsbtodb(m_fs, ((m_fs->e2fs_bsize>1024)?0:1)+i+1), + m_fs->e2fs_bsize, NOCRED, &bp); + if (error) { + free(m_fs->e2fs_gd, M_UFSMNT); + goto out; + } + bcopy(bp->b_data, + &m_fs->e2fs_gd[i* m_fs->e2fs_bsize / sizeof(struct ext2_gd)], + m_fs->e2fs_bsize); + brelse(bp); + bp = NULL; + } + + mp->mnt_data = (qaddr_t)ump; + mp->mnt_stat.f_fsid.val[0] = (long)dev; + mp->mnt_stat.f_fsid.val[1] = makefstype(MOUNT_EXT2FS); + mp->mnt_maxsymlinklen = EXT2_MAXSYMLINKLEN; + mp->mnt_flag |= MNT_LOCAL; + ump->um_mountp = mp; + ump->um_dev = dev; + ump->um_devvp = devvp; + ump->um_nindir = NINDIR(m_fs); + ump->um_bptrtodb = m_fs->e2fs_fsbtodb; + ump->um_seqinc = 1; /* no frags */ + devvp->v_specflags |= SI_MOUNTEDON; + return (0); +out: + if (bp) + brelse(bp); + (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, cred, p); + if (ump) { + free(ump->um_e2fs, M_UFSMNT); + free(ump, M_UFSMNT); + mp->mnt_data = (qaddr_t)0; + } + return (error); +} + +/* + * unmount system call + */ +int +ext2fs_unmount(mp, mntflags, p) + struct mount *mp; + int mntflags; + struct proc *p; +{ + register struct ufsmount *ump; + register struct m_ext2fs *fs; + int error, flags; + + flags = 0; + if (mntflags & MNT_FORCE) + flags |= FORCECLOSE; + if ((error = ext2fs_flushfiles(mp, flags, p)) != 0) + return (error); + ump = VFSTOUFS(mp); + fs = ump->um_e2fs; + if (fs->e2fs_ronly == 0 && + ext2fs_cgupdate(ump, MNT_WAIT) == 0 && + (fs->e2fs.e2fs_state & E2FS_ERRORS) == 0) { + fs->e2fs.e2fs_state = E2FS_ISCLEAN; + (void) ext2fs_sbupdate(ump, MNT_WAIT); + } + ump->um_devvp->v_specflags &= ~SI_MOUNTEDON; + error = VOP_CLOSE(ump->um_devvp, fs->e2fs_ronly ? FREAD : FREAD|FWRITE, + NOCRED, p); + vrele(ump->um_devvp); + free(fs->e2fs_gd, M_UFSMNT); + free(fs, M_UFSMNT); + free(ump, M_UFSMNT); + mp->mnt_data = (qaddr_t)0; + mp->mnt_flag &= ~MNT_LOCAL; + return (error); +} + +/* + * Flush out all the files in a filesystem. + */ +int +ext2fs_flushfiles(mp, flags, p) + register struct mount *mp; + int flags; + struct proc *p; +{ + extern int doforce; + register struct ufsmount *ump; + int error; + + if (!doforce) + flags &= ~FORCECLOSE; + ump = VFSTOUFS(mp); + error = vflush(mp, NULLVP, flags); + return (error); +} + +/* + * Get file system statistics. + */ +int +ext2fs_statfs(mp, sbp, p) + struct mount *mp; + register struct statfs *sbp; + struct proc *p; +{ + register struct ufsmount *ump; + register struct m_ext2fs *fs; + u_int32_t overhead, overhead_per_group; + + ump = VFSTOUFS(mp); + fs = ump->um_e2fs; + if (fs->e2fs.e2fs_magic != E2FS_MAGIC) + panic("ext2fs_statfs"); +#ifdef COMPAT_09 + sbp->f_type = 1; +#else + sbp->f_type = 0; +#endif + + /* + * Compute the overhead (FS structures) + */ + overhead_per_group = 1 /* super block */ + + fs->e2fs_ngdb + + 1 /* block bitmap */ + + 1 /* inode bitmap */ + + fs->e2fs_itpg; + overhead = fs->e2fs.e2fs_first_dblock + + fs->e2fs_ncg * overhead_per_group; + + + sbp->f_bsize = fs->e2fs_bsize; + sbp->f_iosize = fs->e2fs_bsize; + sbp->f_blocks = fs->e2fs.e2fs_bcount - overhead; + sbp->f_bfree = fs->e2fs.e2fs_fbcount; + sbp->f_bavail = sbp->f_bfree - fs->e2fs.e2fs_rbcount; + sbp->f_files = fs->e2fs.e2fs_icount; + sbp->f_ffree = fs->e2fs.e2fs_ficount; + if (sbp != &mp->mnt_stat) { + bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); + bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); + } + strncpy(sbp->f_fstypename, mp->mnt_op->vfs_name, MFSNAMELEN); + return (0); +} + +/* + * Go through the disk queues to initiate sandbagged IO; + * go through the inodes to write those that have been modified; + * initiate the writing of the super block if it has been modified. + * + * Note: we are always called with the filesystem marked `MPBUSY'. + */ +int +ext2fs_sync(mp, waitfor, cred, p) + struct mount *mp; + int waitfor; + struct ucred *cred; + struct proc *p; +{ + register struct vnode *vp; + register struct inode *ip; + register struct ufsmount *ump = VFSTOUFS(mp); + register struct m_ext2fs *fs; + int error, allerror = 0; + + fs = ump->um_e2fs; + /* + * Write back modified superblock. + * Consistency check that the superblock + * is still in the buffer cache. + */ + if (fs->e2fs_fmod != 0) { + if (fs->e2fs_ronly != 0) { /* XXX */ + printf("fs = %s\n", fs->e2fs_fsmnt); + panic("update: rofs mod"); + } + fs->e2fs_fmod = 0; + fs->e2fs.e2fs_wtime = time.tv_sec; + allerror = ext2fs_cgupdate(ump, waitfor); + } + /* + * Write back each (modified) inode. + */ +loop: + for (vp = mp->mnt_vnodelist.lh_first; + vp != NULL; + vp = vp->v_mntvnodes.le_next) { + /* + * If the vnode that we are about to sync is no longer + * associated with this mount point, start over. + */ + if (vp->v_mount != mp) + goto loop; + if (VOP_ISLOCKED(vp)) + continue; + ip = VTOI(vp); + if ((ip->i_flag & + (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0 && + vp->v_dirtyblkhd.lh_first == NULL) + continue; + if (vget(vp, 1)) + goto loop; + if ((error = VOP_FSYNC(vp, cred, waitfor, p)) != 0) + allerror = error; + vput(vp); + } + /* + * Force stale file system control information to be flushed. + */ + if ((error = VOP_FSYNC(ump->um_devvp, cred, waitfor, p)) != 0) + allerror = error; + return (allerror); +} + +/* + * Look up a EXT2FS dinode number to find its incore vnode, otherwise read it + * in from disk. If it is in core, wait for the lock bit to clear, then + * return the inode locked. Detection and handling of mount points must be + * done by the calling routine. + */ +int +ext2fs_vget(mp, ino, vpp) + struct mount *mp; + ino_t ino; + struct vnode **vpp; +{ + register struct m_ext2fs *fs; + register struct inode *ip; + struct ufsmount *ump; + struct buf *bp; + struct vnode *vp; + dev_t dev; + int error; + + ump = VFSTOUFS(mp); + dev = ump->um_dev; + if ((*vpp = ufs_ihashget(dev, ino)) != NULL) + return (0); + + /* Allocate a new vnode/inode. */ + if ((error = getnewvnode(VT_EXT2FS, mp, ext2fs_vnodeop_p, &vp)) != 0) { + *vpp = NULL; + return (error); + } + MALLOC(ip, struct inode *, sizeof(struct inode), M_EXT2FSNODE, M_WAITOK); + bzero((caddr_t)ip, sizeof(struct inode)); + vp->v_data = ip; + ip->i_vnode = vp; + ip->i_e2fs = fs = ump->um_e2fs; + ip->i_dev = dev; + ip->i_number = ino; + ip->i_e2fs_last_lblk = 0; + ip->i_e2fs_last_blk = 0; + + /* + * Put it onto its hash chain and lock it so that other requests for + * this inode will block if they arrive while we are sleeping waiting + * for old data structures to be purged or for the contents of the + * disk portion of this inode to be read. + */ + ufs_ihashins(ip); + + /* Read in the disk contents for the inode, copy into the inode. */ + error = bread(ump->um_devvp, fsbtodb(fs, ino_to_fsba(fs, ino)), + (int)fs->e2fs_bsize, NOCRED, &bp); + if (error) { + /* + * The inode does not contain anything useful, so it would + * be misleading to leave it on its hash chain. With mode + * still zero, it will be unlinked and returned to the free + * list by vput(). + */ + vput(vp); + brelse(bp); + *vpp = NULL; + return (error); + } + bcopy(((struct ext2fs_dinode*)bp->b_data + ino_to_fsbo(fs, ino)), + &ip->i_din, sizeof(struct ext2fs_dinode)); + + brelse(bp); + + /* + * Initialize the vnode from the inode, check for aliases. + * Note that the underlying vnode may have changed. + */ + error = ufs_vinit(mp, ext2fs_specop_p, EXT2FS_FIFOOPS, &vp); + if (error) { + vput(vp); + *vpp = NULL; + return (error); + } + /* + * Finish inode initialization now that aliasing has been resolved. + */ + ip->i_devvp = ump->um_devvp; + VREF(ip->i_devvp); + /* + * Set up a generation number for this inode if it does not + * already have one. This should only happen on old filesystems. + */ + if (ip->i_e2fs_gen == 0) { + if (++ext2gennumber < (u_long)time.tv_sec) + ext2gennumber = time.tv_sec; + ip->i_e2fs_gen = ext2gennumber; + if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) + ip->i_flag |= IN_MODIFIED; + } + + *vpp = vp; + return (0); +} + +/* + * File handle to vnode + * + * Have to be really careful about stale file handles: + * - check that the inode number is valid + * - call ext2fs_vget() to get the locked inode + * - check for an unallocated inode (i_mode == 0) + * - check that the given client host has export rights and return + * those rights via. exflagsp and credanonp + */ +int +ext2fs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp) + register struct mount *mp; + struct fid *fhp; + struct mbuf *nam; + struct vnode **vpp; + int *exflagsp; + struct ucred **credanonp; +{ + register struct ufid *ufhp; + struct m_ext2fs *fs; + + ufhp = (struct ufid *)fhp; + fs = VFSTOUFS(mp)->um_e2fs; + if ((ufhp->ufid_ino < EXT2_FIRSTINO && ufhp->ufid_ino != EXT2_ROOTINO) || + ufhp->ufid_ino >= fs->e2fs_ncg * fs->e2fs.e2fs_ipg) + return (ESTALE); + return (ext2fs_check_export(mp, ufhp, nam, vpp, exflagsp, credanonp)); +} + +/* + * Vnode pointer to File handle + */ +/* ARGSUSED */ +int +ext2fs_vptofh(vp, fhp) + struct vnode *vp; + struct fid *fhp; +{ + register struct inode *ip; + register struct ufid *ufhp; + + ip = VTOI(vp); + ufhp = (struct ufid *)fhp; + ufhp->ufid_len = sizeof(struct ufid); + ufhp->ufid_ino = ip->i_number; + ufhp->ufid_gen = ip->i_e2fs_gen; + return (0); +} + +/* + * Write a superblock and associated information back to disk. + */ +int +ext2fs_sbupdate(mp, waitfor) + struct ufsmount *mp; + int waitfor; +{ + register struct m_ext2fs *fs = mp->um_e2fs; + register struct buf *bp; + int error = 0; + + bp = getblk(mp->um_devvp, SBLOCK, SBSIZE, 0, 0); + bcopy((caddr_t)(&fs->e2fs), bp->b_data, SBSIZE); + if (waitfor == MNT_WAIT) + error = bwrite(bp); + else + bawrite(bp); + return (error); +} + +int +ext2fs_cgupdate(mp, waitfor) + struct ufsmount *mp; + int waitfor; +{ + register struct m_ext2fs *fs = mp->um_e2fs; + register struct buf *bp; + int i, error = 0, allerror = 0; + + allerror = ext2fs_sbupdate(mp, waitfor); + for (i = 0; i < fs->e2fs_ngdb; i++) { + bp = getblk(mp->um_devvp, fsbtodb(fs, ((fs->e2fs_bsize>1024)?0:1)+i+1), + fs->e2fs_bsize, 0, 0); + bcopy(&fs->e2fs_gd[i* fs->e2fs_bsize / sizeof(struct ext2_gd)], + bp->b_data, fs->e2fs_bsize); + if (waitfor == MNT_WAIT) + error = bwrite(bp); + else + bawrite(bp); + } + + if (!allerror && error) + allerror = error; + return (allerror); +} diff --git a/sys/ufs/ext2fs/ext2fs_vnops.c b/sys/ufs/ext2fs/ext2fs_vnops.c new file mode 100644 index 00000000000..1ce4efaf3bc --- /dev/null +++ b/sys/ufs/ext2fs/ext2fs_vnops.c @@ -0,0 +1,1541 @@ +/* $NetBSD: ufs_vnops.c,v 1.22 1997/01/30 09:52:27 tls Exp $ */ + +/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */ + +/* + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ufs_vnops.c 8.14 (Berkeley) 10/26/94 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/resourcevar.h> +#include <sys/kernel.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/conf.h> +#include <sys/mount.h> +#include <sys/namei.h> +#include <sys/vnode.h> +#include <sys/lockf.h> +#include <sys/malloc.h> +#include <sys/signalvar.h> + +#include <vm/vm.h> + +#include <miscfs/fifofs/fifo.h> +#include <miscfs/genfs/genfs.h> +#include <miscfs/specfs/specdev.h> + +#include <ufs/ufs/quota.h> +#include <ufs/ufs/inode.h> +#include <ufs/ufs/ufs_extern.h> +#include <ufs/ufs/ufsmount.h> + +#include <ufs/ext2fs/ext2fs.h> +#include <ufs/ext2fs/ext2fs_extern.h> +#include <ufs/ext2fs/ext2fs_dir.h> + + +static int ext2fs_chmod + __P((struct vnode *, int, struct ucred *, struct proc *)); +static int ext2fs_chown + __P((struct vnode *, uid_t, gid_t, struct ucred *, struct proc *)); + +union _qcvt { + int64_t qcvt; + int32_t val[2]; +}; +#define SETHIGH(q, h) { \ + union _qcvt tmp; \ + tmp.qcvt = (q); \ + tmp.val[_QUAD_HIGHWORD] = (h); \ + (q) = tmp.qcvt; \ +} +#define SETLOW(q, l) { \ + union _qcvt tmp; \ + tmp.qcvt = (q); \ + tmp.val[_QUAD_LOWWORD] = (l); \ + (q) = tmp.qcvt; \ +} + +/* + * Create a regular file + */ +int +ext2fs_create(v) + void *v; +{ + struct vop_create_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap = v; + return + ext2fs_makeinode(MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode), + ap->a_dvp, ap->a_vpp, ap->a_cnp); +} + +/* + * Mknod vnode call + */ +/* ARGSUSED */ +int +ext2fs_mknod(v) + void *v; +{ + struct vop_mknod_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap = v; + register struct vattr *vap = ap->a_vap; + register struct vnode **vpp = ap->a_vpp; + register struct inode *ip; + int error; + + if ((error = + ext2fs_makeinode(MAKEIMODE(vap->va_type, vap->va_mode), + ap->a_dvp, vpp, ap->a_cnp)) != 0) + return (error); + ip = VTOI(*vpp); + ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; + if (vap->va_rdev != VNOVAL) { + /* + * Want to be able to use this to make badblock + * inodes, so don't truncate the dev number. + */ + ip->i_din.e2fs_din.e2di_rdev = vap->va_rdev; + } + /* + * Remove inode so that it will be reloaded by VFS_VGET and + * checked to see if it is an alias of an existing entry in + * the inode cache. + */ + vput(*vpp); + (*vpp)->v_type = VNON; + vgone(*vpp); + *vpp = 0; + return (0); +} + +/* + * Open called. + * + * Just check the APPEND flag. + */ +/* ARGSUSED */ +int +ext2fs_open(v) + void *v; +{ + struct vop_open_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap = v; + + /* + * Files marked append-only must be opened for appending. + */ + if ((VTOI(ap->a_vp)->i_e2fs_flags & EXT2_APPEND) && + (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE) + return (EPERM); + return (0); +} + +int +ext2fs_access(v) + void *v; +{ + struct vop_access_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap = v; + register struct vnode *vp = ap->a_vp; + register struct inode *ip = VTOI(vp); + mode_t mode = ap->a_mode; + +#ifdef DIAGNOSTIC + if (!VOP_ISLOCKED(vp)) { + vprint("ext2fs_access: not locked", vp); + panic("ext2fs_access: not locked"); + } +#endif + + /* If immutable bit set, nobody gets to write it. */ + if ((mode & VWRITE) && (ip->i_e2fs_flags & EXT2_IMMUTABLE)) + return (EPERM); + + return (vaccess(ip->i_e2fs_mode, ip->i_e2fs_uid, ip->i_e2fs_gid, mode, + ap->a_cred)); +} + +/* ARGSUSED */ +int +ext2fs_getattr(v) + void *v; +{ + struct vop_getattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap = v; + register struct vnode *vp = ap->a_vp; + register struct inode *ip = VTOI(vp); + register struct vattr *vap = ap->a_vap; + struct timespec ts; + + TIMEVAL_TO_TIMESPEC(&time, &ts); + EXT2FS_ITIMES(ip, &ts, &ts, &ts); + /* + * Copy from inode table + */ + vap->va_fsid = ip->i_dev; + vap->va_fileid = ip->i_number; + vap->va_mode = ip->i_e2fs_mode & ~IFMT; + vap->va_nlink = ip->i_e2fs_nlink; + vap->va_uid = ip->i_e2fs_uid; + vap->va_gid = ip->i_e2fs_gid; + vap->va_rdev = (dev_t)ip->i_din.e2fs_din.e2di_rdev; + vap->va_size = ip->i_e2fs_size; + vap->va_atime.tv_sec = ip->i_e2fs_atime; + vap->va_atime.tv_nsec = 0; + vap->va_mtime.tv_sec = ip->i_e2fs_mtime; + vap->va_mtime.tv_nsec = 0; + vap->va_ctime.tv_sec = ip->i_e2fs_ctime; + vap->va_ctime.tv_nsec = 0; +#ifdef EXT2FS_SYSTEM_FLAGS + vap->va_flags = (ip->i_e2fs_flags & EXT2_APPEND) ? SF_APPEND : 0; + vap->va_flags |= (ip->i_e2fs_flags & EXT2_IMMUTABLE) ? SF_IMMUTABLE : 0; +#else + vap->va_flags = (ip->i_e2fs_flags & EXT2_APPEND) ? UF_APPEND : 0; + vap->va_flags |= (ip->i_e2fs_flags & EXT2_IMMUTABLE) ? UF_IMMUTABLE : 0; +#endif + vap->va_gen = ip->i_e2fs_gen; + /* this doesn't belong here */ + if (vp->v_type == VBLK) + vap->va_blocksize = BLKDEV_IOSIZE; + else if (vp->v_type == VCHR) + vap->va_blocksize = MAXBSIZE; + else + vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize; + vap->va_bytes = dbtob(ip->i_e2fs_nblock); + vap->va_type = vp->v_type; + vap->va_filerev = ip->i_modrev; + return (0); +} + +/* + * Set attribute vnode op. called from several syscalls + */ +int +ext2fs_setattr(v) + void *v; +{ + struct vop_setattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap = v; + register struct vattr *vap = ap->a_vap; + register struct vnode *vp = ap->a_vp; + register struct inode *ip = VTOI(vp); + register struct ucred *cred = ap->a_cred; + register struct proc *p = ap->a_p; + int error; + + /* + * Check for unsettable attributes. + */ + if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) || + (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || + (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || + ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { + return (EINVAL); + } + if (vap->va_flags != VNOVAL) { + if (cred->cr_uid != ip->i_e2fs_uid && + (error = suser(cred, &p->p_acflag))) + return (error); +#ifdef EXT2FS_SYSTEM_FLAGS + if (cred->cr_uid == 0) { + if ((ip->i_e2fs_flags & (EXT2_APPEND | EXT2_IMMUTABLE)) && + securelevel > 0) + return (EPERM); + ip->i_e2fs_flags &= ~(EXT2_APPEND | EXT2_IMMUTABLE); + ip->i_e2fs_flags |= (vap->va_flags & SF_APPEND) ? EXT2_APPEND : 0 | + (vap->va_flags & SF_IMMUTABLE) ? EXT2_IMMUTABLE: 0; + } else { + return (EPERM); + } +#else + ip->i_e2fs_flags &= ~(EXT2_APPEND | EXT2_IMMUTABLE); + ip->i_e2fs_flags |= (vap->va_flags & UF_APPEND) ? EXT2_APPEND : 0 | + (vap->va_flags & UF_IMMUTABLE) ? EXT2_IMMUTABLE: 0; +#endif + ip->i_flag |= IN_CHANGE; + if (vap->va_flags & (IMMUTABLE | APPEND)) + return (0); + } + if (ip->i_e2fs_flags & (EXT2_APPEND | EXT2_IMMUTABLE)) + return (EPERM); + /* + * Go through the fields and update iff not VNOVAL. + */ + if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) { + error = ext2fs_chown(vp, vap->va_uid, vap->va_gid, cred, p); + if (error) + return (error); + } + if (vap->va_size != VNOVAL) { + if (vp->v_type == VDIR) + return (EISDIR); + error = VOP_TRUNCATE(vp, vap->va_size, 0, cred, p); + if (error) + return (error); + } + ip = VTOI(vp); + if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) { + if (cred->cr_uid != ip->i_e2fs_uid && + (error = suser(cred, &p->p_acflag)) && + ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || + (error = VOP_ACCESS(vp, VWRITE, cred, p)))) + return (error); + if (vap->va_atime.tv_sec != VNOVAL) + if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) + ip->i_flag |= IN_ACCESS; + if (vap->va_mtime.tv_sec != VNOVAL) + ip->i_flag |= IN_CHANGE | IN_UPDATE; + error = VOP_UPDATE(vp, &vap->va_atime, &vap->va_mtime, 1); + if (error) + return (error); + } + error = 0; + if (vap->va_mode != (mode_t)VNOVAL) + error = ext2fs_chmod(vp, (int)vap->va_mode, cred, p); + return (error); +} + +/* + * Change the mode on a file. + * Inode must be locked before calling. + */ +static int +ext2fs_chmod(vp, mode, cred, p) + register struct vnode *vp; + register int mode; + register struct ucred *cred; + struct proc *p; +{ + register struct inode *ip = VTOI(vp); + int error; + + if (cred->cr_uid != ip->i_e2fs_uid && + (error = suser(cred, &p->p_acflag))) + return (error); + if (cred->cr_uid) { + if (vp->v_type != VDIR && (mode & S_ISTXT)) + return (EFTYPE); + if (!groupmember(ip->i_e2fs_gid, cred) && (mode & ISGID)) + return (EPERM); + } + ip->i_e2fs_mode &= ~ALLPERMS; + ip->i_e2fs_mode |= (mode & ALLPERMS); + ip->i_flag |= IN_CHANGE; + if ((vp->v_flag & VTEXT) && (ip->i_e2fs_mode & S_ISTXT) == 0) + (void) vnode_pager_uncache(vp); + return (0); +} + +/* + * Perform chown operation on inode ip; + * inode must be locked prior to call. + */ +static int +ext2fs_chown(vp, uid, gid, cred, p) + register struct vnode *vp; + uid_t uid; + gid_t gid; + struct ucred *cred; + struct proc *p; +{ + register struct inode *ip = VTOI(vp); + uid_t ouid; + gid_t ogid; + int error = 0; + + if (uid == (uid_t)VNOVAL) + uid = ip->i_e2fs_uid; + if (gid == (gid_t)VNOVAL) + gid = ip->i_e2fs_gid; + /* + * If we don't own the file, are trying to change the owner + * of the file, or are not a member of the target group, + * the caller must be superuser or the call fails. + */ + if ((cred->cr_uid != ip->i_e2fs_uid || uid != ip->i_e2fs_uid || + (gid != ip->i_e2fs_gid && !groupmember((gid_t)gid, cred))) && + (error = suser(cred, &p->p_acflag))) + return (error); + ogid = ip->i_e2fs_gid; + ouid = ip->i_e2fs_uid; + + ip->i_e2fs_gid = gid; + ip->i_e2fs_uid = uid; + if (ouid != uid || ogid != gid) + ip->i_flag |= IN_CHANGE; + if (ouid != uid && cred->cr_uid != 0) + ip->i_e2fs_mode &= ~ISUID; + if (ogid != gid && cred->cr_uid != 0) + ip->i_e2fs_mode &= ~ISGID; + return (0); +} + +int +ext2fs_remove(v) + void *v; +{ + struct vop_remove_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap = v; + register struct inode *ip; + register struct vnode *vp = ap->a_vp; + register struct vnode *dvp = ap->a_dvp; + int error; + + if (vp->v_type == VDIR) { + error = EISDIR; + goto out; + } + ip = VTOI(vp); + if ((ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) || + (VTOI(dvp)->i_e2fs_flags & EXT2_APPEND)) { + error = EPERM; + goto out; + } + error = ext2fs_dirremove(dvp, ap->a_cnp); + if (error == 0) { + ip->i_e2fs_nlink--; + ip->i_flag |= IN_CHANGE; + } +out: + if (dvp == vp) + vrele(vp); + else + vput(vp); + vput(dvp); + return (error); +} + +/* + * link vnode call + */ +int +ext2fs_link(v) + void *v; +{ + struct vop_link_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap = v; + register struct vnode *dvp = ap->a_dvp; + register struct vnode *vp = ap->a_vp; + register struct componentname *cnp = ap->a_cnp; + register struct inode *ip; + struct timespec ts; + int error; + +#ifdef DIAGNOSTIC + if ((cnp->cn_flags & HASBUF) == 0) + panic("ext2fs_link: no name"); +#endif + if (vp->v_type == VDIR) { + VOP_ABORTOP(dvp, cnp); + error = EISDIR; + goto out2; + } + if (dvp->v_mount != vp->v_mount) { + VOP_ABORTOP(dvp, cnp); + error = EXDEV; + goto out2; + } + if (dvp != vp && (error = VOP_LOCK(vp))) { + VOP_ABORTOP(dvp, cnp); + goto out2; + } + ip = VTOI(vp); + if ((nlink_t)ip->i_e2fs_nlink >= LINK_MAX) { + VOP_ABORTOP(dvp, cnp); + error = EMLINK; + goto out1; + } + if (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) { + VOP_ABORTOP(dvp, cnp); + error = EPERM; + goto out1; + } + ip->i_e2fs_nlink++; + ip->i_flag |= IN_CHANGE; + TIMEVAL_TO_TIMESPEC(&time, &ts); + error = VOP_UPDATE(vp, &ts, &ts, 1); + if (!error) + error = ext2fs_direnter(ip, dvp, cnp); + if (error) { + ip->i_e2fs_nlink--; + ip->i_flag |= IN_CHANGE; + } + FREE(cnp->cn_pnbuf, M_NAMEI); +out1: + if (dvp != vp) + VOP_UNLOCK(vp); +out2: + vput(dvp); + return (error); +} + +/* + * Rename system call. + * rename("foo", "bar"); + * is essentially + * unlink("bar"); + * link("foo", "bar"); + * unlink("foo"); + * but ``atomically''. Can't do full commit without saving state in the + * inode on disk which isn't feasible at this time. Best we can do is + * always guarantee the target exists. + * + * Basic algorithm is: + * + * 1) Bump link count on source while we're linking it to the + * target. This also ensure the inode won't be deleted out + * from underneath us while we work (it may be truncated by + * a concurrent `trunc' or `open' for creation). + * 2) Link source to destination. If destination already exists, + * delete it first. + * 3) Unlink source reference to inode if still around. If a + * directory was moved and the parent of the destination + * is different from the source, patch the ".." entry in the + * directory. + */ +int +ext2fs_rename(v) + void *v; +{ + struct vop_rename_args /* { + struct vnode *a_fdvp; + struct vnode *a_fvp; + struct componentname *a_fcnp; + struct vnode *a_tdvp; + struct vnode *a_tvp; + struct componentname *a_tcnp; + } */ *ap = v; + struct vnode *tvp = ap->a_tvp; + register struct vnode *tdvp = ap->a_tdvp; + struct vnode *fvp = ap->a_fvp; + register struct vnode *fdvp = ap->a_fdvp; + register struct componentname *tcnp = ap->a_tcnp; + register struct componentname *fcnp = ap->a_fcnp; + register struct inode *ip, *xp, *dp; + struct ext2fs_dirtemplate dirbuf; + struct timespec ts; + int doingdirectory = 0, oldparent = 0, newparent = 0; + int error = 0; + u_char namlen; + +#ifdef DIAGNOSTIC + if ((tcnp->cn_flags & HASBUF) == 0 || + (fcnp->cn_flags & HASBUF) == 0) + panic("ext2fs_rename: no name"); +#endif + /* + * Check for cross-device rename. + */ + if ((fvp->v_mount != tdvp->v_mount) || + (tvp && (fvp->v_mount != tvp->v_mount))) { + error = EXDEV; +abortit: + VOP_ABORTOP(tdvp, tcnp); /* XXX, why not in NFS? */ + if (tdvp == tvp) + vrele(tdvp); + else + vput(tdvp); + if (tvp) + vput(tvp); + VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */ + vrele(fdvp); + vrele(fvp); + return (error); + } + + /* + * Check if just deleting a link name. + */ + if (tvp && ((VTOI(tvp)->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) || + (VTOI(tdvp)->i_e2fs_flags & EXT2_APPEND))) { + error = EPERM; + goto abortit; + } + if (fvp == tvp) { + if (fvp->v_type == VDIR) { + error = EINVAL; + goto abortit; + } + + /* Release destination completely. */ + VOP_ABORTOP(tdvp, tcnp); + vput(tdvp); + vput(tvp); + + /* Delete source. */ + vrele(fdvp); + vrele(fvp); + fcnp->cn_flags &= ~MODMASK; + fcnp->cn_flags |= LOCKPARENT | LOCKLEAF; + if ((fcnp->cn_flags & SAVESTART) == 0) + panic("ext2fs_rename: lost from startdir"); + fcnp->cn_nameiop = DELETE; + (void) relookup(fdvp, &fvp, fcnp); + return (VOP_REMOVE(fdvp, fvp, fcnp)); + } + if ((error = VOP_LOCK(fvp)) != 0) + goto abortit; + dp = VTOI(fdvp); + ip = VTOI(fvp); + if ((ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) || + (dp->i_e2fs_flags & EXT2_APPEND)) { + VOP_UNLOCK(fvp); + error = EPERM; + goto abortit; + } + if ((ip->i_e2fs_mode & IFMT) == IFDIR) { + error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc); + if (!error && tvp) + error = VOP_ACCESS(tvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc); + if (error) { + VOP_UNLOCK(fvp); + error = EACCES; + goto abortit; + } + /* + * Avoid ".", "..", and aliases of "." for obvious reasons. + */ + if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') || + dp == ip || (fcnp->cn_flags&ISDOTDOT) || + (ip->i_flag & IN_RENAME)) { + VOP_UNLOCK(fvp); + error = EINVAL; + goto abortit; + } + ip->i_flag |= IN_RENAME; + oldparent = dp->i_number; + doingdirectory++; + } + vrele(fdvp); + + /* + * When the target exists, both the directory + * and target vnodes are returned locked. + */ + dp = VTOI(tdvp); + xp = NULL; + if (tvp) + xp = VTOI(tvp); + + /* + * 1) Bump link count while we're moving stuff + * around. If we crash somewhere before + * completing our work, the link count + * may be wrong, but correctable. + */ + ip->i_e2fs_nlink++; + ip->i_flag |= IN_CHANGE; + TIMEVAL_TO_TIMESPEC(&time, &ts); + if ((error = VOP_UPDATE(fvp, &ts, &ts, 1)) != 0) { + VOP_UNLOCK(fvp); + goto bad; + } + + /* + * If ".." must be changed (ie the directory gets a new + * parent) then the source directory must not be in the + * directory heirarchy above the target, as this would + * orphan everything below the source directory. Also + * the user must have write permission in the source so + * as to be able to change "..". We must repeat the call + * to namei, as the parent directory is unlocked by the + * call to checkpath(). + */ + error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc); + VOP_UNLOCK(fvp); + if (oldparent != dp->i_number) + newparent = dp->i_number; + if (doingdirectory && newparent) { + if (error) /* write access check above */ + goto bad; + if (xp != NULL) + vput(tvp); + error = ext2fs_checkpath(ip, dp, tcnp->cn_cred); + if (error != 0) + goto out; + if ((tcnp->cn_flags & SAVESTART) == 0) + panic("ext2fs_rename: lost to startdir"); + if ((error = relookup(tdvp, &tvp, tcnp)) != 0) + goto out; + dp = VTOI(tdvp); + xp = NULL; + if (tvp) + xp = VTOI(tvp); + } + /* + * 2) If target doesn't exist, link the target + * to the source and unlink the source. + * Otherwise, rewrite the target directory + * entry to reference the source inode and + * expunge the original entry's existence. + */ + if (xp == NULL) { + if (dp->i_dev != ip->i_dev) + panic("rename: EXDEV"); + /* + * Account for ".." in new directory. + * When source and destination have the same + * parent we don't fool with the link count. + */ + if (doingdirectory && newparent) { + if ((nlink_t)dp->i_e2fs_nlink >= LINK_MAX) { + error = EMLINK; + goto bad; + } + dp->i_e2fs_nlink++; + dp->i_flag |= IN_CHANGE; + if ((error = VOP_UPDATE(tdvp, &ts, &ts, 1)) != 0) + goto bad; + } + error = ext2fs_direnter(ip, tdvp, tcnp); + if (error != 0) { + if (doingdirectory && newparent) { + dp->i_e2fs_nlink--; + dp->i_flag |= IN_CHANGE; + (void)VOP_UPDATE(tdvp, &ts, &ts, 1); + } + goto bad; + } + vput(tdvp); + } else { + if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev) + panic("rename: EXDEV"); + /* + * Short circuit rename(foo, foo). + */ + if (xp->i_number == ip->i_number) + panic("rename: same file"); + /* + * If the parent directory is "sticky", then the user must + * own the parent directory, or the destination of the rename, + * otherwise the destination may not be changed (except by + * root). This implements append-only directories. + */ + if ((dp->i_e2fs_mode & S_ISTXT) && tcnp->cn_cred->cr_uid != 0 && + tcnp->cn_cred->cr_uid != dp->i_e2fs_uid && + xp->i_e2fs_uid != tcnp->cn_cred->cr_uid) { + error = EPERM; + goto bad; + } + /* + * Target must be empty if a directory and have no links + * to it. Also, ensure source and target are compatible + * (both directories, or both not directories). + */ + if ((xp->i_e2fs_mode & IFMT) == IFDIR) { + if (!ext2fs_dirempty(xp, dp->i_number, tcnp->cn_cred) || + xp->i_e2fs_nlink > 2) { + error = ENOTEMPTY; + goto bad; + } + if (!doingdirectory) { + error = ENOTDIR; + goto bad; + } + cache_purge(tdvp); + } else if (doingdirectory) { + error = EISDIR; + goto bad; + } + error = ext2fs_dirrewrite(dp, ip, tcnp); + if (error != 0) + goto bad; + /* + * If the target directory is in the same + * directory as the source directory, + * decrement the link count on the parent + * of the target directory. + */ + if (doingdirectory && !newparent) { + dp->i_e2fs_nlink--; + dp->i_flag |= IN_CHANGE; + } + vput(tdvp); + /* + * Adjust the link count of the target to + * reflect the dirrewrite above. If this is + * a directory it is empty and there are + * no links to it, so we can squash the inode and + * any space associated with it. We disallowed + * renaming over top of a directory with links to + * it above, as the remaining link would point to + * a directory without "." or ".." entries. + */ + xp->i_e2fs_nlink--; + if (doingdirectory) { + if (--xp->i_e2fs_nlink != 0) + panic("rename: linked directory"); + error = VOP_TRUNCATE(tvp, (off_t)0, IO_SYNC, + tcnp->cn_cred, tcnp->cn_proc); + } + xp->i_flag |= IN_CHANGE; + vput(tvp); + xp = NULL; + } + + /* + * 3) Unlink the source. + */ + fcnp->cn_flags &= ~MODMASK; + fcnp->cn_flags |= LOCKPARENT | LOCKLEAF; + if ((fcnp->cn_flags & SAVESTART) == 0) + panic("ext2fs_rename: lost from startdir"); + (void) relookup(fdvp, &fvp, fcnp); + if (fvp != NULL) { + xp = VTOI(fvp); + dp = VTOI(fdvp); + } else { + /* + * From name has disappeared. + */ + if (doingdirectory) + panic("ext2fs_rename: lost dir entry"); + vrele(ap->a_fvp); + return (0); + } + /* + * Ensure that the directory entry still exists and has not + * changed while the new name has been entered. If the source is + * a file then the entry may have been unlinked or renamed. In + * either case there is no further work to be done. If the source + * is a directory then it cannot have been rmdir'ed; its link + * count of three would cause a rmdir to fail with ENOTEMPTY. + * The IRENAME flag ensures that it cannot be moved by another + * rename. + */ + if (xp != ip) { + if (doingdirectory) + panic("ext2fs_rename: lost dir entry"); + } else { + /* + * If the source is a directory with a + * new parent, the link count of the old + * parent directory must be decremented + * and ".." set to point to the new parent. + */ + if (doingdirectory && newparent) { + dp->i_e2fs_nlink--; + dp->i_flag |= IN_CHANGE; + error = vn_rdwr(UIO_READ, fvp, (caddr_t)&dirbuf, + sizeof (struct ext2fs_dirtemplate), (off_t)0, + UIO_SYSSPACE, IO_NODELOCKED, + tcnp->cn_cred, (int *)0, (struct proc *)0); + if (error == 0) { + namlen = dirbuf.dotdot_namlen; + if (namlen != 2 || + dirbuf.dotdot_name[0] != '.' || + dirbuf.dotdot_name[1] != '.') { + ufs_dirbad(xp, (doff_t)12, + "ext2fs_rename: mangled dir"); + } else { + dirbuf.dotdot_ino = newparent; + (void) vn_rdwr(UIO_WRITE, fvp, + (caddr_t)&dirbuf, + sizeof (struct dirtemplate), + (off_t)0, UIO_SYSSPACE, + IO_NODELOCKED|IO_SYNC, + tcnp->cn_cred, (int *)0, + (struct proc *)0); + cache_purge(fdvp); + } + } + } + error = ext2fs_dirremove(fdvp, fcnp); + if (!error) { + xp->i_e2fs_nlink--; + xp->i_flag |= IN_CHANGE; + } + xp->i_flag &= ~IN_RENAME; + } + if (dp) + vput(fdvp); + if (xp) + vput(fvp); + vrele(ap->a_fvp); + return (error); + +bad: + if (xp) + vput(ITOV(xp)); + vput(ITOV(dp)); +out: + if (doingdirectory) + ip->i_flag &= ~IN_RENAME; + if (VOP_LOCK(fvp) == 0) { + ip->i_e2fs_nlink--; + ip->i_flag |= IN_CHANGE; + vput(fvp); + } else + vrele(fvp); + return (error); +} + +/* + * A virgin directory (no blushing please). + */ +static struct ext2fs_dirtemplate mastertemplate = { + 0, 12, 1, ".", + 0, - 12, 2, ".." /* XXX -12 should be e2fs_bsize-12 */ +}; + +/* + * Mkdir system call + */ +int +ext2fs_mkdir(v) + void *v; +{ + struct vop_mkdir_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap = v; + register struct vnode *dvp = ap->a_dvp; + register struct vattr *vap = ap->a_vap; + register struct componentname *cnp = ap->a_cnp; + register struct inode *ip, *dp; + struct vnode *tvp; + struct ext2fs_dirtemplate dirtemplate, *dtp; + struct timespec ts; + int error, dmode; + +#ifdef DIAGNOSTIC + if ((cnp->cn_flags & HASBUF) == 0) + panic("ext2fs_mkdir: no name"); +#endif + dp = VTOI(dvp); + if ((nlink_t)dp->i_e2fs_nlink >= LINK_MAX) { + error = EMLINK; + goto out; + } + dmode = vap->va_mode & 0777; + dmode |= IFDIR; + /* + * Must simulate part of ext2fs_makeinode here to acquire the inode, + * but not have it entered in the parent directory. The entry is + * made later after writing "." and ".." entries. + */ + if ((error = VOP_VALLOC(dvp, dmode, cnp->cn_cred, &tvp)) != 0) + goto out; + ip = VTOI(tvp); + ip->i_e2fs_uid = cnp->cn_cred->cr_uid; + ip->i_e2fs_gid = dp->i_e2fs_gid; + ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; + ip->i_e2fs_mode = dmode; + tvp->v_type = VDIR; /* Rest init'd in getnewvnode(). */ + ip->i_e2fs_nlink = 2; + TIMEVAL_TO_TIMESPEC(&time, &ts); + error = VOP_UPDATE(tvp, &ts, &ts, 1); + + /* + * Bump link count in parent directory + * to reflect work done below. Should + * be done before reference is created + * so reparation is possible if we crash. + */ + dp->i_e2fs_nlink++; + dp->i_flag |= IN_CHANGE; + if ((error = VOP_UPDATE(dvp, &ts, &ts, 1)) != 0) + goto bad; + + /* Initialize directory with "." and ".." from static template. */ + dtp = &mastertemplate; + dirtemplate = *dtp; + dirtemplate.dot_ino = ip->i_number; + dirtemplate.dotdot_ino = dp->i_number; + /* Correct reclen of second entry */ + dirtemplate.dotdot_reclen = VTOI(dvp)->i_e2fs->e2fs_bsize - 12; + error = vn_rdwr(UIO_WRITE, tvp, (caddr_t)&dirtemplate, + sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE, + IO_NODELOCKED|IO_SYNC, cnp->cn_cred, (int *)0, (struct proc *)0); + if (error) { + dp->i_e2fs_nlink--; + dp->i_flag |= IN_CHANGE; + goto bad; + } + if (VTOI(dvp)->i_e2fs->e2fs_bsize > + VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize) + panic("ext2fs_mkdir: blksize"); /* XXX should grow with balloc() */ + else { + ip->i_e2fs_size = VTOI(dvp)->i_e2fs->e2fs_bsize; + ip->i_flag |= IN_CHANGE; + } + + /* Directory set up, now install it's entry in the parent directory. */ + error = ext2fs_direnter(ip, dvp, cnp); + if (error != 0) { + dp->i_e2fs_nlink--; + dp->i_flag |= IN_CHANGE; + } +bad: + /* + * No need to do an explicit VOP_TRUNCATE here, vrele will do this + * for us because we set the link count to 0. + */ + if (error) { + ip->i_e2fs_nlink = 0; + ip->i_flag |= IN_CHANGE; + vput(tvp); + } else + *ap->a_vpp = tvp; +out: + FREE(cnp->cn_pnbuf, M_NAMEI); + vput(dvp); + return (error); +} + +/* + * Rmdir system call. + */ +int +ext2fs_rmdir(v) + void *v; +{ + struct vop_rmdir_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap = v; + register struct vnode *vp = ap->a_vp; + register struct vnode *dvp = ap->a_dvp; + register struct componentname *cnp = ap->a_cnp; + register struct inode *ip, *dp; + int error; + + ip = VTOI(vp); + dp = VTOI(dvp); + /* + * No rmdir "." please. + */ + if (dp == ip) { + vrele(dvp); + vput(vp); + return (EINVAL); + } + /* + * Verify the directory is empty (and valid). + * (Rmdir ".." won't be valid since + * ".." will contain a reference to + * the current directory and thus be + * non-empty.) + */ + error = 0; + if (ip->i_e2fs_nlink != 2 || + !ext2fs_dirempty(ip, dp->i_number, cnp->cn_cred)) { + error = ENOTEMPTY; + goto out; + } + if ((dp->i_e2fs_flags & EXT2_APPEND) || + (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND))) { + error = EPERM; + goto out; + } + /* + * Delete reference to directory before purging + * inode. If we crash in between, the directory + * will be reattached to lost+found, + */ + error = ext2fs_dirremove(dvp, cnp); + if (error != 0) + goto out; + dp->i_e2fs_nlink--; + dp->i_flag |= IN_CHANGE; + cache_purge(dvp); + vput(dvp); + dvp = NULL; + /* + * Truncate inode. The only stuff left + * in the directory is "." and "..". The + * "." reference is inconsequential since + * we're quashing it. The ".." reference + * has already been adjusted above. We've + * removed the "." reference and the reference + * in the parent directory, but there may be + * other hard links so decrement by 2 and + * worry about them later. + */ + ip->i_e2fs_nlink -= 2; + error = VOP_TRUNCATE(vp, (off_t)0, IO_SYNC, cnp->cn_cred, + cnp->cn_proc); + cache_purge(ITOV(ip)); +out: + if (dvp) + vput(dvp); + vput(vp); + return (error); +} + +/* + * symlink -- make a symbolic link + */ +int +ext2fs_symlink(v) + void *v; +{ + struct vop_symlink_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + char *a_target; + } */ *ap = v; + register struct vnode *vp, **vpp = ap->a_vpp; + register struct inode *ip; + int len, error; + + error = ext2fs_makeinode(IFLNK | ap->a_vap->va_mode, ap->a_dvp, + vpp, ap->a_cnp); + if (error) + return (error); + vp = *vpp; + len = strlen(ap->a_target); + if (len < vp->v_mount->mnt_maxsymlinklen) { + ip = VTOI(vp); + bcopy(ap->a_target, (char *)ip->i_din.e2fs_din.e2di_shortlink, len); + ip->i_e2fs_size = len; + ip->i_flag |= IN_CHANGE | IN_UPDATE; + } else + error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0, + UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, (int *)0, + (struct proc *)0); + vput(vp); + return (error); +} + +/* + * Return target name of a symbolic link + */ +int +ext2fs_readlink(v) + void *v; +{ + struct vop_readlink_args /* { + struct vnode *a_vp; + struct uio *a_uio; + struct ucred *a_cred; + } */ *ap = v; + register struct vnode *vp = ap->a_vp; + register struct inode *ip = VTOI(vp); + int isize; + + isize = ip->i_e2fs_size; + if (isize < vp->v_mount->mnt_maxsymlinklen || + (vp->v_mount->mnt_maxsymlinklen == 0 && ip->i_e2fs_nblock == 0)) { + uiomove((char *)ip->i_din.e2fs_din.e2di_shortlink, isize, ap->a_uio); + return (0); + } + return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred)); +} + +/* + * Advisory record locking support + */ +int +ext2fs_advlock(v) + void *v; +{ + struct vop_advlock_args /* { + struct vnode *a_vp; + caddr_t a_id; + int a_op; + struct flock *a_fl; + int a_flags; + } */ *ap = v; + register struct inode *ip = VTOI(ap->a_vp); + + return (lf_advlock(&ip->i_lockf, ip->i_e2fs_size, ap->a_id, ap->a_op, + ap->a_fl, ap->a_flags)); +} + +/* + * Initialize the vnode associated with a new inode, handle aliased + * vnodes. + */ +int +ext2fs_vinit(mntp, specops, fifoops, vpp) + struct mount *mntp; + int (**specops) __P((void *)); + int (**fifoops) __P((void *)); + struct vnode **vpp; +{ + struct inode *ip; + struct vnode *vp, *nvp; + + vp = *vpp; + ip = VTOI(vp); + switch(vp->v_type = IFTOVT(ip->i_e2fs_mode)) { + case VCHR: + case VBLK: + vp->v_op = specops; + if ((nvp = checkalias(vp, ip->i_din.e2fs_din.e2di_rdev, mntp)) + != NULL) { + /* + * Discard unneeded vnode, but save its inode. + */ + ufs_ihashrem(ip); + VOP_UNLOCK(vp); + nvp->v_data = vp->v_data; + vp->v_data = NULL; + vp->v_op = spec_vnodeop_p; + vrele(vp); + vgone(vp); + /* + * Reinitialize aliased inode. + */ + vp = nvp; + ip->i_vnode = vp; + ufs_ihashins(ip); + } + break; + case VFIFO: +#ifdef FIFO + vp->v_op = fifoops; + break; +#else + return (EOPNOTSUPP); +#endif + case VNON: + case VBAD: + case VSOCK: + case VLNK: + case VDIR: + case VREG: + break; + } + if (ip->i_number == ROOTINO) + vp->v_flag |= VROOT; + /* + * Initialize modrev times + */ + SETHIGH(ip->i_modrev, mono_time.tv_sec); + SETLOW(ip->i_modrev, mono_time.tv_usec * 4294); + *vpp = vp; + return (0); +} + +/* + * Allocate a new inode. + */ +int +ext2fs_makeinode(mode, dvp, vpp, cnp) + int mode; + struct vnode *dvp; + struct vnode **vpp; + struct componentname *cnp; +{ + register struct inode *ip, *pdir; + struct timespec ts; + struct vnode *tvp; + int error; + + pdir = VTOI(dvp); +#ifdef DIAGNOSTIC + if ((cnp->cn_flags & HASBUF) == 0) + panic("ext2fs_makeinode: no name"); +#endif + *vpp = NULL; + if ((mode & IFMT) == 0) + mode |= IFREG; + + if ((error = VOP_VALLOC(dvp, mode, cnp->cn_cred, &tvp)) != 0) { + free(cnp->cn_pnbuf, M_NAMEI); + vput(dvp); + return (error); + } + ip = VTOI(tvp); + ip->i_e2fs_gid = pdir->i_e2fs_gid; + ip->i_e2fs_uid = cnp->cn_cred->cr_uid; + ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; + ip->i_e2fs_mode = mode; + tvp->v_type = IFTOVT(mode); /* Rest init'd in getnewvnode(). */ + ip->i_e2fs_nlink = 1; + if ((ip->i_e2fs_mode & ISGID) && + !groupmember(ip->i_e2fs_gid, cnp->cn_cred) && + suser(cnp->cn_cred, NULL)) + ip->i_e2fs_mode &= ~ISGID; + + /* + * Make sure inode goes to disk before directory entry. + */ + TIMEVAL_TO_TIMESPEC(&time, &ts); + if ((error = VOP_UPDATE(tvp, &ts, &ts, 1)) != 0) + goto bad; + error = ext2fs_direnter(ip, dvp, cnp); + if (error != 0) + goto bad; + if ((cnp->cn_flags & SAVESTART) == 0) + FREE(cnp->cn_pnbuf, M_NAMEI); + vput(dvp); + *vpp = tvp; + return (0); + +bad: + /* + * Write error occurred trying to update the inode + * or the directory so must deallocate the inode. + */ + free(cnp->cn_pnbuf, M_NAMEI); + vput(dvp); + ip->i_e2fs_nlink = 0; + ip->i_flag |= IN_CHANGE; + vput(tvp); + return (error); +} + +/* + * Reclaim an inode so that it can be used for other purposes. + */ +int +ext2fs_reclaim(v) + void *v; +{ + struct vop_reclaim_args /* { + struct vnode *a_vp; + } */ *ap = v; + register struct vnode *vp = ap->a_vp; + struct inode *ip; + extern int prtactive; + + if (prtactive && vp->v_usecount != 0) + vprint("ext2fs_reclaim: pushing active", vp); + /* + * Remove the inode from its hash chain. + */ + ip = VTOI(vp); + ufs_ihashrem(ip); + /* + * Purge old data structures associated with the inode. + */ + cache_purge(vp); + if (ip->i_devvp) { + vrele(ip->i_devvp); + ip->i_devvp = 0; + } + + FREE(vp->v_data, M_EXT2FSNODE); + vp->v_data = NULL; + return (0); +} + +/* Global vfs data structures for ext2fs. */ +int (**ext2fs_vnodeop_p) __P((void *)); +struct vnodeopv_entry_desc ext2fs_vnodeop_entries[] = { + { &vop_default_desc, vn_default_error }, + { &vop_lookup_desc, ext2fs_lookup }, /* lookup */ + { &vop_create_desc, ext2fs_create }, /* create */ + { &vop_mknod_desc, ext2fs_mknod }, /* mknod */ + { &vop_open_desc, ext2fs_open }, /* open */ + { &vop_close_desc, ufs_close }, /* close */ + { &vop_access_desc, ext2fs_access }, /* access */ + { &vop_getattr_desc, ext2fs_getattr }, /* getattr */ + { &vop_setattr_desc, ext2fs_setattr }, /* setattr */ + { &vop_read_desc, ext2fs_read }, /* read */ + { &vop_write_desc, ext2fs_write }, /* write */ + { &vop_lease_desc, ufs_lease_check }, /* lease */ + { &vop_ioctl_desc, ufs_ioctl }, /* ioctl */ + { &vop_poll_desc, ufs_poll }, /* poll */ + { &vop_mmap_desc, ufs_mmap }, /* mmap */ + { &vop_fsync_desc, ext2fs_fsync }, /* fsync */ + { &vop_seek_desc, ufs_seek }, /* seek */ + { &vop_remove_desc, ext2fs_remove }, /* remove */ + { &vop_link_desc, ext2fs_link }, /* link */ + { &vop_rename_desc, ext2fs_rename }, /* rename */ + { &vop_mkdir_desc, ext2fs_mkdir }, /* mkdir */ + { &vop_rmdir_desc, ext2fs_rmdir }, /* rmdir */ + { &vop_symlink_desc, ext2fs_symlink }, /* symlink */ + { &vop_readdir_desc, ext2fs_readdir }, /* readdir */ + { &vop_readlink_desc, ext2fs_readlink },/* readlink */ + { &vop_abortop_desc, ufs_abortop }, /* abortop */ + { &vop_inactive_desc, ext2fs_inactive },/* inactive */ + { &vop_reclaim_desc, ext2fs_reclaim }, /* reclaim */ + { &vop_lock_desc, ufs_lock }, /* lock */ + { &vop_unlock_desc, ufs_unlock }, /* unlock */ + { &vop_bmap_desc, ext2fs_bmap }, /* bmap */ + { &vop_strategy_desc, ufs_strategy }, /* strategy */ + { &vop_print_desc, ufs_print }, /* print */ + { &vop_islocked_desc, ufs_islocked }, /* islocked */ + { &vop_pathconf_desc, ufs_pathconf }, /* pathconf */ + { &vop_advlock_desc, ext2fs_advlock }, /* advlock */ + { &vop_blkatoff_desc, ext2fs_blkatoff },/* blkatoff */ + { &vop_valloc_desc, ext2fs_valloc }, /* valloc */ + { &vop_vfree_desc, ext2fs_vfree }, /* vfree */ + { &vop_truncate_desc, ext2fs_truncate },/* truncate */ + { &vop_update_desc, ext2fs_update }, /* update */ + { &vop_bwrite_desc, vn_bwrite }, /* bwrite */ + { (struct vnodeop_desc*)NULL, (int(*) __P((void*)))NULL } +}; +struct vnodeopv_desc ext2fs_vnodeop_opv_desc = + { &ext2fs_vnodeop_p, ext2fs_vnodeop_entries }; + +int (**ext2fs_specop_p) __P((void *)); +struct vnodeopv_entry_desc ext2fs_specop_entries[] = { + { &vop_default_desc, vn_default_error }, + { &vop_lookup_desc, spec_lookup }, /* lookup */ + { &vop_create_desc, spec_create }, /* create */ + { &vop_mknod_desc, spec_mknod }, /* mknod */ + { &vop_open_desc, spec_open }, /* open */ + { &vop_close_desc, ufsspec_close }, /* close */ + { &vop_access_desc, ext2fs_access }, /* access */ + { &vop_getattr_desc, ext2fs_getattr }, /* getattr */ + { &vop_setattr_desc, ext2fs_setattr }, /* setattr */ + { &vop_read_desc, ufsspec_read }, /* read */ + { &vop_write_desc, ufsspec_write }, /* write */ + { &vop_lease_desc, spec_lease_check }, /* lease */ + { &vop_ioctl_desc, spec_ioctl }, /* ioctl */ + { &vop_poll_desc, spec_poll }, /* poll */ + { &vop_mmap_desc, spec_mmap }, /* mmap */ + { &vop_fsync_desc, ext2fs_fsync }, /* fsync */ + { &vop_seek_desc, spec_seek }, /* seek */ + { &vop_remove_desc, spec_remove }, /* remove */ + { &vop_link_desc, spec_link }, /* link */ + { &vop_rename_desc, spec_rename }, /* rename */ + { &vop_mkdir_desc, spec_mkdir }, /* mkdir */ + { &vop_rmdir_desc, spec_rmdir }, /* rmdir */ + { &vop_symlink_desc, spec_symlink }, /* symlink */ + { &vop_readdir_desc, spec_readdir }, /* readdir */ + { &vop_readlink_desc, spec_readlink }, /* readlink */ + { &vop_abortop_desc, spec_abortop }, /* abortop */ + { &vop_inactive_desc, ext2fs_inactive },/* inactive */ + { &vop_reclaim_desc, ext2fs_reclaim }, /* reclaim */ + { &vop_lock_desc, ufs_lock }, /* lock */ + { &vop_unlock_desc, ufs_unlock }, /* unlock */ + { &vop_bmap_desc, spec_bmap }, /* bmap */ + { &vop_strategy_desc, spec_strategy }, /* strategy */ + { &vop_print_desc, ufs_print }, /* print */ + { &vop_islocked_desc, ufs_islocked }, /* islocked */ + { &vop_pathconf_desc, spec_pathconf }, /* pathconf */ + { &vop_advlock_desc, spec_advlock }, /* advlock */ + { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */ + { &vop_valloc_desc, spec_valloc }, /* valloc */ + { &vop_vfree_desc, ext2fs_vfree }, /* vfree */ + { &vop_truncate_desc, spec_truncate }, /* truncate */ + { &vop_update_desc, ext2fs_update }, /* update */ + { &vop_bwrite_desc, vn_bwrite }, /* bwrite */ + { (struct vnodeop_desc*)NULL, (int(*) __P((void *)))NULL } +}; +struct vnodeopv_desc ext2fs_specop_opv_desc = + { &ext2fs_specop_p, ext2fs_specop_entries }; + +#ifdef FIFO +int (**ext2fs_fifoop_p) __P((void *)); +struct vnodeopv_entry_desc ext2fs_fifoop_entries[] = { + { &vop_default_desc, vn_default_error }, + { &vop_lookup_desc, fifo_lookup }, /* lookup */ + { &vop_create_desc, fifo_create }, /* create */ + { &vop_mknod_desc, fifo_mknod }, /* mknod */ + { &vop_open_desc, fifo_open }, /* open */ + { &vop_close_desc, ufsfifo_close }, /* close */ + { &vop_access_desc, ext2fs_access }, /* access */ + { &vop_getattr_desc, ext2fs_getattr }, /* getattr */ + { &vop_setattr_desc, ext2fs_setattr }, /* setattr */ + { &vop_read_desc, ufsfifo_read }, /* read */ + { &vop_write_desc, ufsfifo_write }, /* write */ + { &vop_lease_desc, fifo_lease_check }, /* lease */ + { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */ + { &vop_poll_desc, fifo_poll }, /* poll */ + { &vop_mmap_desc, fifo_mmap }, /* mmap */ + { &vop_fsync_desc, ext2fs_fsync }, /* fsync */ + { &vop_seek_desc, fifo_seek }, /* seek */ + { &vop_remove_desc, fifo_remove }, /* remove */ + { &vop_link_desc, fifo_link }, /* link */ + { &vop_rename_desc, fifo_rename }, /* rename */ + { &vop_mkdir_desc, fifo_mkdir }, /* mkdir */ + { &vop_rmdir_desc, fifo_rmdir }, /* rmdir */ + { &vop_symlink_desc, fifo_symlink }, /* symlink */ + { &vop_readdir_desc, fifo_readdir }, /* readdir */ + { &vop_readlink_desc, fifo_readlink }, /* readlink */ + { &vop_abortop_desc, fifo_abortop }, /* abortop */ + { &vop_inactive_desc, ext2fs_inactive },/* inactive */ + { &vop_reclaim_desc, ext2fs_reclaim }, /* reclaim */ + { &vop_lock_desc, ufs_lock }, /* lock */ + { &vop_unlock_desc, ufs_unlock }, /* unlock */ + { &vop_bmap_desc, fifo_bmap }, /* bmap */ + { &vop_strategy_desc, fifo_strategy }, /* strategy */ + { &vop_print_desc, ufs_print }, /* print */ + { &vop_islocked_desc, ufs_islocked }, /* islocked */ + { &vop_pathconf_desc, fifo_pathconf }, /* pathconf */ + { &vop_advlock_desc, fifo_advlock }, /* advlock */ + { &vop_blkatoff_desc, fifo_blkatoff }, /* blkatoff */ + { &vop_valloc_desc, fifo_valloc }, /* valloc */ + { &vop_vfree_desc, ext2fs_vfree }, /* vfree */ + { &vop_truncate_desc, fifo_truncate }, /* truncate */ + { &vop_update_desc, ext2fs_update }, /* update */ + { &vop_bwrite_desc, vn_bwrite }, /* bwrite */ + { (struct vnodeop_desc*)NULL, (int(*) __P((void *)))NULL } +}; +struct vnodeopv_desc ext2fs_fifoop_opv_desc = + { &ext2fs_fifoop_p, ext2fs_fifoop_entries }; +#endif /* FIFO */ |