summaryrefslogtreecommitdiffstats
path: root/sys/gnu/ext2fs
diff options
context:
space:
mode:
Diffstat (limited to 'sys/gnu/ext2fs')
-rw-r--r--sys/gnu/ext2fs/COPYRIGHT.INFO35
-rw-r--r--sys/gnu/ext2fs/alpha-bitops.h255
-rw-r--r--sys/gnu/ext2fs/ext2_alloc.c539
-rw-r--r--sys/gnu/ext2fs/ext2_balloc.c314
-rw-r--r--sys/gnu/ext2fs/ext2_bitmap.c27
-rw-r--r--sys/gnu/ext2fs/ext2_bmap.c334
-rw-r--r--sys/gnu/ext2fs/ext2_extern.h116
-rw-r--r--sys/gnu/ext2fs/ext2_fs.h169
-rw-r--r--sys/gnu/ext2fs/ext2_fs_i.h42
-rw-r--r--sys/gnu/ext2fs/ext2_fs_sb.h35
-rw-r--r--sys/gnu/ext2fs/ext2_ihash.c173
-rw-r--r--sys/gnu/ext2fs/ext2_inode.c552
-rw-r--r--sys/gnu/ext2fs/ext2_inode_cnv.c131
-rw-r--r--sys/gnu/ext2fs/ext2_linux_balloc.c680
-rw-r--r--sys/gnu/ext2fs/ext2_linux_ialloc.c613
-rw-r--r--sys/gnu/ext2fs/ext2_lookup.c1110
-rw-r--r--sys/gnu/ext2fs/ext2_mount.h74
-rw-r--r--sys/gnu/ext2fs/ext2_readwrite.c312
-rw-r--r--sys/gnu/ext2fs/ext2_subr.c127
-rw-r--r--sys/gnu/ext2fs/ext2_super.c802
-rw-r--r--sys/gnu/ext2fs/ext2_vfsops.c1230
-rw-r--r--sys/gnu/ext2fs/ext2_vnops.c1962
-rw-r--r--sys/gnu/ext2fs/fs.h180
-rw-r--r--sys/gnu/ext2fs/i386-bitops.h203
-rw-r--r--sys/gnu/ext2fs/inode.h177
25 files changed, 8305 insertions, 1887 deletions
diff --git a/sys/gnu/ext2fs/COPYRIGHT.INFO b/sys/gnu/ext2fs/COPYRIGHT.INFO
new file mode 100644
index 0000000..699f519
--- /dev/null
+++ b/sys/gnu/ext2fs/COPYRIGHT.INFO
@@ -0,0 +1,35 @@
+$FreeBSD$
+
+Most of the files in this directory are written by Godmar Back or modified
+by him using the CSRG sources. Those files are covered by the Berkeley-style
+copyright. However the following files are covered by GPL. Since the policy
+of the FreeBSD project is to keep the files with the more restrictive
+copyright in the gnu tree and it is a good idea to keep the filesystem code
+all together, the EXT2FS in its entirety resides under the gnu tree. Note
+that only the files below are under the GPL. In the eventuality that these
+files are redesigned or rewritten, this tree can be moved back into the less
+restrictive FreeBSD tree.
+
+ ext2_bitmap.c (in the cvs attic)
+ ext2_fs.h
+ ext2_fs_i.h
+ ext2_fs_sb.h
+ ext2_linux_balloc.c
+ ext2_linux_ialloc.c
+ ext2_super.c (in the cvs attic)
+ ext2_vfsops.c (has some GPL'ed code from ext2_super.c)
+ i386-bitops.h
+
+PS.
+ THANKS GODMAR!!!
+
+Note that this port has been modified by John Dyson and others on
+the FreeBSD team, and it is best to send the bug reports to the FreeBSD
+team. If there are any non-FreeBSD specific bugs, fixes will be sent to
+Godmar to help him fix the original code base. It is also our intention
+to send Godmar any FreeBSD specific porting changes so that he can keep
+control of his code....
+
+John
+dyson@freebsd.org
+
diff --git a/sys/gnu/ext2fs/alpha-bitops.h b/sys/gnu/ext2fs/alpha-bitops.h
new file mode 100644
index 0000000..3910da8
--- /dev/null
+++ b/sys/gnu/ext2fs/alpha-bitops.h
@@ -0,0 +1,255 @@
+/* $FreeBSD$ */
+#ifndef _ALPHA_BITOPS_H
+#define _ALPHA_BITOPS_H
+
+/*
+ * Copyright 1994, Linus Torvalds.
+ */
+
+/*
+ * These have to be done with inline assembly: that way the bit-setting
+ * is guaranteed to be atomic. All bit operations return 0 if the bit
+ * was cleared before the operation and != 0 if it was not.
+ *
+ * To get proper branch prediction for the main line, we must branch
+ * forward to code at the end of this object's .text section, then
+ * branch back to restart the operation.
+ *
+ * bit 0 is the LSB of addr; bit 64 is the LSB of (addr+1).
+ */
+static __inline unsigned int set_bit(unsigned long, volatile void *);
+static __inline unsigned int clear_bit(unsigned long, volatile void *);
+static __inline unsigned int change_bit(unsigned long, volatile void *);
+static __inline unsigned int test_bit(int, volatile void *);
+static __inline unsigned long ffz_b(unsigned long x);
+static __inline unsigned long ffz(unsigned long);
+/* static __inline int ffs(int); */
+static __inline void * memscan(void *, int, size_t);
+#ifdef __alpha_cix__
+static __inline unsigned long hweight64(unsigned long);
+#endif
+static __inline unsigned long
+find_next_zero_bit(void *, unsigned long, unsigned long);
+
+static __inline unsigned int set_bit(unsigned long nr, volatile void * addr)
+{
+ unsigned long oldbit;
+ unsigned long temp;
+ volatile unsigned int *m = ((volatile unsigned int *) addr) + (nr >> 5);
+
+ __asm__ __volatile__(
+ "1: ldl_l %0,%1\n"
+ " and %0,%3,%2\n"
+ " bne %2,2f\n"
+ " xor %0,%3,%0\n"
+ " stl_c %0,%1\n"
+ " beq %0,3f\n"
+ "2:\n"
+ ".section .text2,\"ax\"\n"
+ "3: br 1b\n"
+ ".previous"
+ :"=&r" (temp), "=m" (*m), "=&r" (oldbit)
+ :"Ir" (1UL << (nr & 31)), "m" (*m));
+ return oldbit;
+}
+
+static __inline unsigned int clear_bit(unsigned long nr, volatile void * addr)
+{
+ unsigned long oldbit;
+ unsigned long temp;
+ volatile unsigned int *m = ((volatile unsigned int *) addr) + (nr >> 5);
+
+ __asm__ __volatile__(
+ "1: ldl_l %0,%1\n"
+ " and %0,%3,%2\n"
+ " beq %2,2f\n"
+ " xor %0,%3,%0\n"
+ " stl_c %0,%1\n"
+ " beq %0,3f\n"
+ "2:\n"
+ ".section .text2,\"ax\"\n"
+ "3: br 1b\n"
+ ".previous"
+ :"=&r" (temp), "=m" (*m), "=&r" (oldbit)
+ :"Ir" (1UL << (nr & 31)), "m" (*m));
+ return oldbit;
+}
+
+static __inline unsigned int change_bit(unsigned long nr, volatile void * addr)
+{
+ unsigned long oldbit;
+ unsigned long temp;
+ volatile unsigned int *m = ((volatile unsigned int *) addr) + (nr >> 5);
+
+ __asm__ __volatile__(
+ "1: ldl_l %0,%1\n"
+ " xor %0,%2,%0\n"
+ " stl_c %0,%1\n"
+ " beq %0,3f\n"
+ ".section .text2,\"ax\"\n"
+ "3: br 1b\n"
+ ".previous"
+ :"=&r" (temp), "=m" (*m), "=&r" (oldbit)
+ :"Ir" (1UL << (nr & 31)), "m" (*m));
+ return oldbit;
+}
+
+static __inline unsigned int test_bit(int nr, volatile void * addr)
+{
+ return 1UL & (((volatile int *) addr)[nr >> 5] >> (nr & 31));
+}
+
+/*
+ * ffz = Find First Zero in word. Undefined if no zero exists,
+ * so code should check against ~0UL first..
+ *
+ * Do a binary search on the bits. Due to the nature of large
+ * constants on the alpha, it is worthwhile to split the search.
+ */
+static __inline unsigned long ffz_b(unsigned long x)
+{
+ unsigned long sum = 0;
+
+ x = ~x & -~x; /* set first 0 bit, clear others */
+ if (x & 0xF0) sum += 4;
+ if (x & 0xCC) sum += 2;
+ if (x & 0xAA) sum += 1;
+
+ return sum;
+}
+
+static __inline unsigned long ffz(unsigned long word)
+{
+#ifdef __alpha_cix__
+ /* Whee. EV6 can calculate it directly. */
+ unsigned long result;
+ __asm__("ctlz %1,%0" : "=r"(result) : "r"(~word));
+ return result;
+#else
+ unsigned long bits, qofs, bofs;
+
+ __asm__("cmpbge %1,%2,%0" : "=r"(bits) : "r"(word), "r"(~0UL));
+ qofs = ffz_b(bits);
+ __asm__("extbl %1,%2,%0" : "=r"(bits) : "r"(word), "r"(qofs));
+ bofs = ffz_b(bits);
+
+ return qofs*8 + bofs;
+#endif
+}
+
+#ifdef __KERNEL__
+#if 0
+/*
+ * ffs: find first bit set. This is defined the same way as
+ * the libc and compiler builtin ffs routines, therefore
+ * differs in spirit from the above ffz (man ffs).
+ */
+
+static __inline int ffs(int word)
+{
+ int result = ffz(~word);
+ return word ? result+1 : 0;
+}
+#endif
+
+/*
+ * hweightN: returns the hamming weight (i.e. the number
+ * of bits set) of a N-bit word
+ */
+
+#ifdef __alpha_cix__
+/* Whee. EV6 can calculate it directly. */
+static __inline unsigned long hweight64(unsigned long w)
+{
+ unsigned long result;
+ __asm__("ctpop %1,%0" : "=r"(result) : "r"(w));
+ return result;
+}
+
+#define hweight32(x) hweight64((x) & 0xfffffffful)
+#define hweight16(x) hweight64((x) & 0xfffful)
+#define hweight8(x) hweight64((x) & 0xfful)
+#else
+#define hweight32(x) generic_hweight32(x)
+#define hweight16(x) generic_hweight16(x)
+#define hweight8(x) generic_hweight8(x)
+#endif
+
+#endif /* __alpha_cix__ */
+
+/* from lib/string.c */
+static __inline void * memscan(void * addr, int c, size_t size)
+{
+ unsigned char * p = (unsigned char *) addr;
+
+ while (size) {
+ if (*p == c)
+ return (void *) p;
+ p++;
+ size--;
+ }
+ return (void *) p;
+}
+
+
+/*
+ * Find next zero bit in a bitmap reasonably efficiently..
+ */
+static __inline unsigned long find_next_zero_bit(void * addr, unsigned long size, unsigned long offset)
+{
+ unsigned long * p = ((unsigned long *) addr) + (offset >> 6);
+ unsigned long result = offset & ~63UL;
+ unsigned long tmp;
+
+ if (offset >= size)
+ return size;
+ size -= result;
+ offset &= 63UL;
+ if (offset) {
+ tmp = *(p++);
+ tmp |= ~0UL >> (64-offset);
+ if (size < 64)
+ goto found_first;
+ if (~tmp)
+ goto found_middle;
+ size -= 64;
+ result += 64;
+ }
+ while (size & ~63UL) {
+ if (~(tmp = *(p++)))
+ goto found_middle;
+ result += 64;
+ size -= 64;
+ }
+ if (!size)
+ return result;
+ tmp = *p;
+found_first:
+ tmp |= ~0UL << size;
+found_middle:
+ return result + ffz(tmp);
+}
+
+/*
+ * The optimizer actually does good code for this case..
+ */
+#define find_first_zero_bit(addr, size) \
+ find_next_zero_bit((addr), (size), 0)
+
+#ifdef __KERNEL__
+
+#define ext2_set_bit test_and_set_bit
+#define ext2_clear_bit test_and_clear_bit
+#define ext2_test_bit test_bit
+#define ext2_find_first_zero_bit find_first_zero_bit
+#define ext2_find_next_zero_bit find_next_zero_bit
+
+/* Bitmap functions for the minix filesystem. */
+#define minix_set_bit(nr,addr) test_and_set_bit(nr,addr)
+#define minix_clear_bit(nr,addr) test_and_clear_bit(nr,addr)
+#define minix_test_bit(nr,addr) test_bit(nr,addr)
+#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size)
+
+#endif /* __KERNEL__ */
+
+#endif /* _ALPHA_BITOPS_H */
diff --git a/sys/gnu/ext2fs/ext2_alloc.c b/sys/gnu/ext2fs/ext2_alloc.c
new file mode 100644
index 0000000..a99a8ee
--- /dev/null
+++ b/sys/gnu/ext2fs/ext2_alloc.c
@@ -0,0 +1,539 @@
+/*
+ * modified for Lites 1.1
+ *
+ * Aug 1995, Godmar Back (gback@cs.utah.edu)
+ * University of Utah, Department of Computer Science
+ */
+/*
+ * 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.8 (Berkeley) 2/21/94
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/vnode.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <sys/syslog.h>
+
+#include <gnu/ext2fs/inode.h>
+#include <gnu/ext2fs/ext2_mount.h>
+#include <gnu/ext2fs/ext2_fs.h>
+#include <gnu/ext2fs/ext2_fs_sb.h>
+#include <gnu/ext2fs/fs.h>
+#include <gnu/ext2fs/ext2_extern.h>
+
+static void ext2_fserr(struct ext2_sb_info *, u_int, char *);
+
+/*
+ * Linux calls this functions at the following locations:
+ * (1) the inode is freed
+ * (2) a preallocation miss occurs
+ * (3) truncate is called
+ * (4) release_file is called and f_mode & 2
+ *
+ * I call it in ext2_inactive, ext2_truncate, ext2_vfree and in (2)
+ * the call in vfree might be redundant
+ */
+void
+ext2_discard_prealloc(ip)
+ struct inode * ip;
+{
+#ifdef EXT2_PREALLOCATE
+ if (ip->i_prealloc_count) {
+ int i = ip->i_prealloc_count;
+ ip->i_prealloc_count = 0;
+ ext2_free_blocks (ITOV(ip)->v_mount,
+ ip->i_prealloc_block,
+ i);
+ }
+#endif
+}
+
+/*
+ * Allocate a block in the file system.
+ *
+ * this takes the framework from ffs_alloc. To implement the
+ * actual allocation, it calls ext2_new_block, the ported version
+ * of the same Linux routine.
+ *
+ * we note that this is always called in connection with ext2_blkpref
+ *
+ * preallocation is done as Linux does it
+ */
+int
+ext2_alloc(ip, lbn, bpref, size, cred, bnp)
+ struct inode *ip;
+ int32_t lbn, bpref;
+ int size;
+ struct ucred *cred;
+ int32_t *bnp;
+{
+ struct ext2_sb_info *fs;
+ int32_t bno;
+
+ *bnp = 0;
+ fs = ip->i_e2fs;
+#if DIAGNOSTIC
+ if ((u_int)size > fs->s_blocksize || blkoff(fs, size) != 0) {
+ printf("dev = %s, bsize = %lu, size = %d, fs = %s\n",
+ devtoname(ip->i_dev), fs->s_blocksize, size, fs->fs_fsmnt);
+ panic("ext2_alloc: bad size");
+ }
+ if (cred == NOCRED)
+ panic("ext2_alloc: missing credential");
+#endif /* DIAGNOSTIC */
+ if (size == fs->s_blocksize && fs->s_es->s_free_blocks_count == 0)
+ goto nospace;
+ if (cred->cr_uid != 0 &&
+ fs->s_es->s_free_blocks_count < fs->s_es->s_r_blocks_count)
+ goto nospace;
+ if (bpref >= fs->s_es->s_blocks_count)
+ bpref = 0;
+ /* call the Linux code */
+#ifdef EXT2_PREALLOCATE
+ /* To have a preallocation hit, we must
+ * - have at least one block preallocated
+ * - and our preferred block must have that block number or one below
+ */
+ if (ip->i_prealloc_count &&
+ (bpref == ip->i_prealloc_block ||
+ bpref + 1 == ip->i_prealloc_block))
+ {
+ bno = ip->i_prealloc_block++;
+ ip->i_prealloc_count--;
+ /* ext2_debug ("preallocation hit (%lu/%lu).\n",
+ ++alloc_hits, ++alloc_attempts); */
+
+ /* Linux gets, clears, and releases the buffer at this
+ point - we don't have to that; we leave it to the caller
+ */
+ } else {
+ ext2_discard_prealloc (ip);
+ /* ext2_debug ("preallocation miss (%lu/%lu).\n",
+ alloc_hits, ++alloc_attempts); */
+ if (S_ISREG(ip->i_mode))
+ bno = ext2_new_block
+ (ITOV(ip)->v_mount, bpref,
+ &ip->i_prealloc_count,
+ &ip->i_prealloc_block);
+ else
+ bno = (int32_t)ext2_new_block(ITOV(ip)->v_mount,
+ bpref, 0, 0);
+ }
+#else
+ bno = (int32_t)ext2_new_block(ITOV(ip)->v_mount, bpref, 0, 0);
+#endif
+
+ if (bno > 0) {
+ /* set next_alloc fields as done in block_getblk */
+ ip->i_next_alloc_block = lbn;
+ ip->i_next_alloc_goal = bno;
+
+ ip->i_blocks += btodb(size);
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ *bnp = bno;
+ return (0);
+ }
+nospace:
+ ext2_fserr(fs, cred->cr_uid, "file system full");
+ uprintf("\n%s: write failed, file system is full\n", fs->fs_fsmnt);
+ return (ENOSPC);
+}
+
+/*
+ * Reallocate a sequence of blocks into a contiguous sequence of blocks.
+ *
+ * The vnode and an array of buffer pointers for a range of sequential
+ * logical blocks to be made contiguous is given. The allocator attempts
+ * to find a range of sequential blocks starting as close as possible to
+ * an fs_rotdelay offset from the end of the allocation for the logical
+ * block immediately preceding the current range. If successful, the
+ * physical block numbers in the buffer pointers and in the inode are
+ * changed to reflect the new allocation. If unsuccessful, the allocation
+ * is left unchanged. The success in doing the reallocation is returned.
+ * Note that the error return is not reflected back to the user. Rather
+ * the previous block allocation will be used.
+ */
+
+#ifdef FANCY_REALLOC
+#include <sys/sysctl.h>
+static int doasyncfree = 1;
+#ifdef OPT_DEBUG
+SYSCTL_INT(_debug, 14, doasyncfree, CTLFLAG_RW, &doasyncfree, 0, "");
+#endif /* OPT_DEBUG */
+#endif
+
+int
+ext2_reallocblks(ap)
+ struct vop_reallocblks_args /* {
+ struct vnode *a_vp;
+ struct cluster_save *a_buflist;
+ } */ *ap;
+{
+#ifndef FANCY_REALLOC
+/* printf("ext2_reallocblks not implemented\n"); */
+return ENOSPC;
+#else
+
+ struct ext2_sb_info *fs;
+ struct inode *ip;
+ struct vnode *vp;
+ struct buf *sbp, *ebp;
+ int32_t *bap, *sbap, *ebap;
+ struct cluster_save *buflist;
+ int32_t start_lbn, end_lbn, soff, eoff, newblk, blkno;
+ struct indir start_ap[NIADDR + 1], end_ap[NIADDR + 1], *idp;
+ int i, len, start_lvl, end_lvl, pref, ssize;
+
+ vp = ap->a_vp;
+ ip = VTOI(vp);
+ fs = ip->i_e2fs;
+#ifdef UNKLAR
+ if (fs->fs_contigsumsize <= 0)
+ return (ENOSPC);
+#endif
+ buflist = ap->a_buflist;
+ len = buflist->bs_nchildren;
+ start_lbn = buflist->bs_children[0]->b_lblkno;
+ end_lbn = start_lbn + len - 1;
+#if DIAGNOSTIC
+ for (i = 1; i < len; i++)
+ if (buflist->bs_children[i]->b_lblkno != start_lbn + i)
+ panic("ext2_reallocblks: non-cluster");
+#endif
+ /*
+ * If the latest allocation is in a new cylinder group, assume that
+ * the filesystem has decided to move and do not force it back to
+ * the previous cylinder group.
+ */
+ if (dtog(fs, dbtofsb(fs, buflist->bs_children[0]->b_blkno)) !=
+ dtog(fs, dbtofsb(fs, buflist->bs_children[len - 1]->b_blkno)))
+ return (ENOSPC);
+ if (ufs_getlbns(vp, start_lbn, start_ap, &start_lvl) ||
+ ufs_getlbns(vp, end_lbn, end_ap, &end_lvl))
+ return (ENOSPC);
+ /*
+ * Get the starting offset and block map for the first block.
+ */
+ if (start_lvl == 0) {
+ sbap = &ip->i_db[0];
+ soff = start_lbn;
+ } else {
+ idp = &start_ap[start_lvl - 1];
+ if (bread(vp, idp->in_lbn, (int)fs->s_blocksize, NOCRED, &sbp)) {
+ brelse(sbp);
+ return (ENOSPC);
+ }
+ sbap = (int32_t *)sbp->b_data;
+ soff = idp->in_off;
+ }
+ /*
+ * Find the preferred location for the cluster.
+ */
+ pref = ext2_blkpref(ip, start_lbn, soff, sbap);
+ /*
+ * If the block range spans two block maps, get the second map.
+ */
+ if (end_lvl == 0 || (idp = &end_ap[end_lvl - 1])->in_off + 1 >= len) {
+ ssize = len;
+ } else {
+#if DIAGNOSTIC
+ if (start_ap[start_lvl-1].in_lbn == idp->in_lbn)
+ panic("ext2_reallocblk: start == end");
+#endif
+ ssize = len - (idp->in_off + 1);
+ if (bread(vp, idp->in_lbn, (int)fs->s_blocksize, NOCRED, &ebp))
+ goto fail;
+ ebap = (int32_t *)ebp->b_data;
+ }
+ /*
+ * Search the block map looking for an allocation of the desired size.
+ */
+ if ((newblk = (int32_t)ext2_hashalloc(ip, dtog(fs, pref), (long)pref,
+ len, (u_long (*)())ext2_clusteralloc)) == 0)
+ goto fail;
+ /*
+ * We have found a new contiguous block.
+ *
+ * First we have to replace the old block pointers with the new
+ * block pointers in the inode and indirect blocks associated
+ * with the file.
+ */
+ blkno = newblk;
+ for (bap = &sbap[soff], i = 0; i < len; i++, blkno += fs->s_frags_per_block) {
+ if (i == ssize)
+ bap = ebap;
+#if DIAGNOSTIC
+ if (buflist->bs_children[i]->b_blkno != fsbtodb(fs, *bap))
+ panic("ext2_reallocblks: alloc mismatch");
+#endif
+ *bap++ = blkno;
+ }
+ /*
+ * Next we must write out the modified inode and indirect blocks.
+ * For strict correctness, the writes should be synchronous since
+ * the old block values may have been written to disk. In practise
+ * they are almost never written, but if we are concerned about
+ * strict correctness, the `doasyncfree' flag should be set to zero.
+ *
+ * The test on `doasyncfree' should be changed to test a flag
+ * that shows whether the associated buffers and inodes have
+ * been written. The flag should be set when the cluster is
+ * started and cleared whenever the buffer or inode is flushed.
+ * We can then check below to see if it is set, and do the
+ * synchronous write only when it has been cleared.
+ */
+ if (sbap != &ip->i_db[0]) {
+ if (doasyncfree)
+ bdwrite(sbp);
+ else
+ bwrite(sbp);
+ } else {
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ if (!doasyncfree)
+ ext2_update(vp, 1);
+ }
+ if (ssize < len)
+ if (doasyncfree)
+ bdwrite(ebp);
+ else
+ bwrite(ebp);
+ /*
+ * Last, free the old blocks and assign the new blocks to the buffers.
+ */
+ for (blkno = newblk, i = 0; i < len; i++, blkno += fs->s_frags_per_block) {
+ ext2_blkfree(ip, dbtofsb(fs, buflist->bs_children[i]->b_blkno),
+ fs->s_blocksize);
+ buflist->bs_children[i]->b_blkno = fsbtodb(fs, blkno);
+ }
+ return (0);
+
+fail:
+ if (ssize < len)
+ brelse(ebp);
+ if (sbap != &ip->i_db[0])
+ brelse(sbp);
+ return (ENOSPC);
+
+#endif /* FANCY_REALLOC */
+}
+
+/*
+ * Allocate an inode in the file system.
+ *
+ * we leave the actual allocation strategy to the (modified)
+ * ext2_new_inode(), to make sure we get the policies right
+ */
+int
+ext2_valloc(pvp, mode, cred, vpp)
+ struct vnode *pvp;
+ int mode;
+ struct ucred *cred;
+ struct vnode **vpp;
+{
+ struct inode *pip;
+ struct ext2_sb_info *fs;
+ struct inode *ip;
+ ino_t ino;
+ int i, error;
+
+ *vpp = NULL;
+ pip = VTOI(pvp);
+ fs = pip->i_e2fs;
+ if (fs->s_es->s_free_inodes_count == 0)
+ goto noinodes;
+
+ /* call the Linux routine - it returns the inode number only */
+ ino = ext2_new_inode(pip, mode);
+
+ if (ino == 0)
+ goto noinodes;
+ error = VFS_VGET(pvp->v_mount, ino, LK_EXCLUSIVE, vpp);
+ if (error) {
+ ext2_vfree(pvp, ino, mode);
+ return (error);
+ }
+ ip = VTOI(*vpp);
+
+ /*
+ the question is whether using VGET was such good idea at all -
+ Linux doesn't read the old inode in when it's allocating a
+ new one. I will set at least i_size & i_blocks the zero.
+ */
+ ip->i_mode = 0;
+ ip->i_size = 0;
+ ip->i_blocks = 0;
+ ip->i_flags = 0;
+ /* now we want to make sure that the block pointers are zeroed out */
+ for (i = 0; i < NDADDR; i++)
+ ip->i_db[i] = 0;
+ for (i = 0; i < NIADDR; i++)
+ ip->i_ib[i] = 0;
+
+ /*
+ * Set up a new generation number for this inode.
+ * XXX check if this makes sense in ext2
+ */
+ if (ip->i_gen == 0 || ++ip->i_gen == 0)
+ ip->i_gen = random() / 2 + 1;
+/*
+printf("ext2_valloc: allocated inode %d\n", ino);
+*/
+ return (0);
+noinodes:
+ ext2_fserr(fs, cred->cr_uid, "out of inodes");
+ uprintf("\n%s: create/symlink failed, no inodes free\n", fs->fs_fsmnt);
+ return (ENOSPC);
+}
+
+/*
+ * Select the desired position for the next block in a file.
+ *
+ * we try to mimic what Remy does in inode_getblk/block_getblk
+ *
+ * we note: blocknr == 0 means that we're about to allocate either
+ * a direct block or a pointer block at the first level of indirection
+ * (In other words, stuff that will go in i_db[] or i_ib[])
+ *
+ * blocknr != 0 means that we're allocating a block that is none
+ * of the above. Then, blocknr tells us the number of the block
+ * that will hold the pointer
+ */
+int32_t
+ext2_blkpref(ip, lbn, indx, bap, blocknr)
+ struct inode *ip;
+ int32_t lbn;
+ int indx;
+ int32_t *bap;
+ int32_t blocknr;
+{
+ int tmp;
+
+ /* if the next block is actually what we thought it is,
+ then set the goal to what we thought it should be
+ */
+ if(ip->i_next_alloc_block == lbn)
+ return ip->i_next_alloc_goal;
+
+ /* now check whether we were provided with an array that basically
+ tells us previous blocks to which we want to stay closeby
+ */
+ if(bap)
+ for (tmp = indx - 1; tmp >= 0; tmp--)
+ if (bap[tmp])
+ return bap[tmp];
+
+ /* else let's fall back to the blocknr, or, if there is none,
+ follow the rule that a block should be allocated near its inode
+ */
+ return blocknr ? blocknr :
+ (int32_t)(ip->i_block_group *
+ EXT2_BLOCKS_PER_GROUP(ip->i_e2fs)) +
+ ip->i_e2fs->s_es->s_first_data_block;
+}
+
+/*
+ * Free a block or fragment.
+ *
+ * pass on to the Linux code
+ */
+void
+ext2_blkfree(ip, bno, size)
+ struct inode *ip;
+ int32_t bno;
+ long size;
+{
+ struct ext2_sb_info *fs;
+
+ fs = ip->i_e2fs;
+ /*
+ * call Linux code with mount *, block number, count
+ */
+ ext2_free_blocks(ITOV(ip)->v_mount, bno, size / fs->s_frag_size);
+}
+
+/*
+ * Free an inode.
+ *
+ * the maintenance of the actual bitmaps is again up to the linux code
+ */
+int
+ext2_vfree(pvp, ino, mode)
+ struct vnode *pvp;
+ ino_t ino;
+ int mode;
+{
+ struct ext2_sb_info *fs;
+ struct inode *pip;
+ mode_t save_i_mode;
+
+ pip = VTOI(pvp);
+ fs = pip->i_e2fs;
+ if ((u_int)ino > fs->s_inodes_per_group * fs->s_groups_count)
+ panic("ext2_vfree: range: dev = (%d, %d), ino = %d, fs = %s",
+ major(pip->i_dev), minor(pip->i_dev), ino, fs->fs_fsmnt);
+
+/* ext2_debug("ext2_vfree (%d, %d) called\n", pip->i_number, mode);
+ */
+ ext2_discard_prealloc(pip);
+
+ /* we need to make sure that ext2_free_inode can adjust the
+ used_dir_counts in the group summary information - I'd
+ really like to know what the rationale behind this
+ 'set i_mode to zero to denote an unused inode' is
+ */
+ save_i_mode = pip->i_mode;
+ pip->i_mode = mode;
+ ext2_free_inode(pip);
+ pip->i_mode = save_i_mode;
+ return (0);
+}
+
+/*
+ * Fserr prints the name of a file system with an error diagnostic.
+ *
+ * The form of the error message is:
+ * fs: error message
+ */
+static void
+ext2_fserr(fs, uid, cp)
+ struct ext2_sb_info *fs;
+ u_int uid;
+ char *cp;
+{
+
+ log(LOG_ERR, "uid %d on %s: %s\n", uid, fs->fs_fsmnt, cp);
+}
diff --git a/sys/gnu/ext2fs/ext2_balloc.c b/sys/gnu/ext2fs/ext2_balloc.c
new file mode 100644
index 0000000..62a94c1
--- /dev/null
+++ b/sys/gnu/ext2fs/ext2_balloc.c
@@ -0,0 +1,314 @@
+/*
+ * modified for Lites 1.1
+ *
+ * Aug 1995, Godmar Back (gback@cs.utah.edu)
+ * University of Utah, Department of Computer Science
+ */
+/*
+ * 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
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bio.h>
+#include <sys/buf.h>
+#include <sys/lock.h>
+#include <sys/ucred.h>
+#include <sys/vnode.h>
+
+#include <gnu/ext2fs/inode.h>
+#include <gnu/ext2fs/ext2_fs.h>
+#include <gnu/ext2fs/ext2_fs_sb.h>
+#include <gnu/ext2fs/fs.h>
+#include <gnu/ext2fs/ext2_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
+ext2_balloc(ip, bn, size, cred, bpp, flags)
+ struct inode *ip;
+ int32_t bn;
+ int size;
+ struct ucred *cred;
+ struct buf **bpp;
+ int flags;
+{
+ struct ext2_sb_info *fs;
+ int32_t nb;
+ struct buf *bp, *nbp;
+ struct vnode *vp = ITOV(ip);
+ struct indir indirs[NIADDR + 2];
+ int32_t newb, lbn, *bap, pref;
+ int osize, nsize, num, i, error;
+/*
+ext2_debug("ext2_balloc called (%d, %d, %d)\n",
+ ip->i_number, (int)bn, (int)size);
+*/
+ *bpp = NULL;
+ if (bn < 0)
+ return (EFBIG);
+ fs = ip->i_e2fs;
+ lbn = bn;
+
+ /*
+ * check if this is a sequential block allocation.
+ * If so, increment next_alloc fields to allow ext2_blkpref
+ * to make a good guess
+ */
+ if (lbn == ip->i_next_alloc_block + 1) {
+ ip->i_next_alloc_block++;
+ ip->i_next_alloc_goal++;
+ }
+
+ /*
+ * The first NDADDR blocks are direct blocks
+ */
+ if (bn < NDADDR) {
+ nb = ip->i_db[bn];
+ /* no new block is to be allocated, and no need to expand
+ the file */
+ if (nb != 0 && ip->i_size >= (bn + 1) * fs->s_blocksize) {
+ error = bread(vp, bn, fs->s_blocksize, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ bp->b_blkno = fsbtodb(fs, nb);
+ *bpp = bp;
+ return (0);
+ }
+ if (nb != 0) {
+ /*
+ * Consider need to reallocate a fragment.
+ */
+ osize = fragroundup(fs, blkoff(fs, ip->i_size));
+ nsize = fragroundup(fs, size);
+ if (nsize <= osize) {
+ error = bread(vp, bn, osize, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ bp->b_blkno = fsbtodb(fs, nb);
+ } else {
+ /* Godmar thinks: this shouldn't happen w/o fragments */
+ printf("nsize %d(%d) > osize %d(%d) nb %d\n",
+ (int)nsize, (int)size, (int)osize,
+ (int)ip->i_size, (int)nb);
+ panic(
+ "ext2_balloc: Something is terribly wrong");
+/*
+ * please note there haven't been any changes from here on -
+ * FFS seems to work.
+ */
+ }
+ } else {
+ if (ip->i_size < (bn + 1) * fs->s_blocksize)
+ nsize = fragroundup(fs, size);
+ else
+ nsize = fs->s_blocksize;
+ error = ext2_alloc(ip, bn,
+ ext2_blkpref(ip, bn, (int)bn, &ip->i_db[0], 0),
+ nsize, cred, &newb);
+ if (error)
+ return (error);
+ bp = getblk(vp, bn, nsize, 0, 0);
+ bp->b_blkno = fsbtodb(fs, newb);
+ if (flags & B_CLRBUF)
+ vfs_bio_clrbuf(bp);
+ }
+ ip->i_db[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 = ext2_getlbns(vp, bn, indirs, &num)) != 0)
+ return(error);
+#if DIAGNOSTIC
+ if (num < 1)
+ panic ("ext2_balloc: ext2_getlbns returned indirect block");
+#endif
+ /*
+ * Fetch the first indirect block allocating if necessary.
+ */
+ --num;
+ nb = ip->i_ib[indirs[0].in_off];
+ if (nb == 0) {
+#if 0
+ pref = ext2_blkpref(ip, lbn, 0, (int32_t *)0, 0);
+#else
+ /* see the comment by ext2_blkpref. What we do here is
+ to pretend that it'd be good for a block holding indirect
+ pointers to be allocated near its predecessor in terms
+ of indirection, or the last direct block.
+ We shamelessly exploit the fact that i_ib immediately
+ follows i_db.
+ Godmar thinks it make sense to allocate i_ib[0] immediately
+ after i_db[11], but it's not utterly clear whether this also
+ applies to i_ib[1] and i_ib[0]
+ */
+
+ pref = ext2_blkpref(ip, lbn, indirs[0].in_off +
+ EXT2_NDIR_BLOCKS, &ip->i_db[0], 0);
+#endif
+ if ((error = ext2_alloc(ip, lbn, pref, (int)fs->s_blocksize,
+ cred, &newb)) != 0)
+ return (error);
+ nb = newb;
+ bp = getblk(vp, indirs[1].in_lbn, fs->s_blocksize, 0, 0);
+ bp->b_blkno = fsbtodb(fs, newb);
+ vfs_bio_clrbuf(bp);
+ /*
+ * Write synchronously so that indirect blocks
+ * never point at garbage.
+ */
+ if ((error = bwrite(bp)) != 0) {
+ ext2_blkfree(ip, nb, fs->s_blocksize);
+ return (error);
+ }
+ ip->i_ib[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->s_blocksize, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ bap = (int32_t *)bp->b_data;
+ nb = bap[indirs[i].in_off];
+ if (i == num)
+ break;
+ i += 1;
+ if (nb != 0) {
+ brelse(bp);
+ continue;
+ }
+ if (pref == 0)
+#if 1
+ /* see the comment above and by ext2_blkpref
+ * I think this implements Linux policy, but
+ * does it really make sense to allocate to
+ * block containing pointers together ?
+ * Also, will it ever succeed ?
+ */
+ pref = ext2_blkpref(ip, lbn, indirs[i].in_off, bap,
+ bp->b_lblkno);
+#else
+ pref = ext2_blkpref(ip, lbn, 0, (int32_t *)0, 0);
+#endif
+ if ((error =
+ ext2_alloc(ip, lbn, pref, (int)fs->s_blocksize, cred, &newb)) != 0) {
+ brelse(bp);
+ return (error);
+ }
+ nb = newb;
+ nbp = getblk(vp, indirs[i].in_lbn, fs->s_blocksize, 0, 0);
+ nbp->b_blkno = fsbtodb(fs, nb);
+ vfs_bio_clrbuf(nbp);
+ /*
+ * Write synchronously so that indirect blocks
+ * never point at garbage.
+ */
+ if ((error = bwrite(nbp)) != 0) {
+ ext2_blkfree(ip, nb, fs->s_blocksize);
+ 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 = ext2_blkpref(ip, lbn, indirs[i].in_off, &bap[0],
+ bp->b_lblkno);
+ if ((error = ext2_alloc(ip,
+ lbn, pref, (int)fs->s_blocksize, cred, &newb)) != 0) {
+ brelse(bp);
+ return (error);
+ }
+ nb = newb;
+ nbp = getblk(vp, lbn, fs->s_blocksize, 0, 0);
+ nbp->b_blkno = fsbtodb(fs, nb);
+ if (flags & B_CLRBUF)
+ vfs_bio_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->s_blocksize, NOCRED, &nbp);
+ if (error) {
+ brelse(nbp);
+ return (error);
+ }
+ } else {
+ nbp = getblk(vp, lbn, fs->s_blocksize, 0, 0);
+ nbp->b_blkno = fsbtodb(fs, nb);
+ }
+ *bpp = nbp;
+ return (0);
+}
diff --git a/sys/gnu/ext2fs/ext2_bitmap.c b/sys/gnu/ext2fs/ext2_bitmap.c
deleted file mode 100644
index c0ffb68c..0000000
--- a/sys/gnu/ext2fs/ext2_bitmap.c
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * linux/fs/ext2/bitmap.c
- *
- * Copyright (C) 1992, 1993, 1994, 1995
- * Remy Card (card@masi.ibp.fr)
- * Laboratoire MASI - Institut Blaise Pascal
- * Universite Pierre et Marie Curie (Paris VI)
- */
-
-#include <linux/fs.h>
-
-
-
-static int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0};
-
-unsigned long ext2_count_free (struct buffer_head * map, unsigned int numchars)
-{
- unsigned int i;
- unsigned long sum = 0;
-
- if (!map)
- return (0);
- for (i = 0; i < numchars; i++)
- sum += nibblemap[map->b_data[i] & 0xf] +
- nibblemap[(map->b_data[i] >> 4) & 0xf];
- return (sum);
-}
diff --git a/sys/gnu/ext2fs/ext2_bmap.c b/sys/gnu/ext2fs/ext2_bmap.c
new file mode 100644
index 0000000..69a069f
--- /dev/null
+++ b/sys/gnu/ext2fs/ext2_bmap.c
@@ -0,0 +1,334 @@
+/*
+ * 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.7 (Berkeley) 3/21/95
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bio.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/resourcevar.h>
+#include <sys/stat.h>
+
+#include <gnu/ext2fs/inode.h>
+#include <gnu/ext2fs/ext2_mount.h>
+#include <gnu/ext2fs/ext2_extern.h>
+
+/*
+ * 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
+ext2_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ int *a_runp;
+ int *a_runb;
+ } */ *ap;
+{
+ int32_t blkno;
+ int error;
+
+ /*
+ * 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);
+
+ error = ext2_bmaparray(ap->a_vp, ap->a_bn, &blkno,
+ ap->a_runp, ap->a_runb);
+ *ap->a_bnp = blkno;
+ return (error);
+}
+
+/*
+ * 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.
+ *
+ * ufs_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
+ext2_bmaparray(vp, bn, bnp, runp, runb)
+ struct vnode *vp;
+ int32_t bn;
+ int32_t *bnp;
+ int *runp;
+ int *runb;
+{
+ struct inode *ip;
+ struct buf *bp;
+ struct ext2mount *ump;
+ struct mount *mp;
+ struct vnode *devvp;
+ struct indir a[NIADDR+1], *ap;
+ int32_t daddr;
+ long metalbn;
+ int error, num, maxrun = 0;
+ int *nump;
+
+ ap = NULL;
+ ip = VTOI(vp);
+ mp = vp->v_mount;
+ ump = VFSTOEXT2(mp);
+ devvp = ump->um_devvp;
+
+ if (runp) {
+ maxrun = mp->mnt_iosize_max / mp->mnt_stat.f_iosize - 1;
+ *runp = 0;
+ }
+
+ if (runb) {
+ *runb = 0;
+ }
+
+
+ ap = a;
+ nump = &num;
+ error = ext2_getlbns(vp, bn, ap, nump);
+ if (error)
+ return (error);
+
+ num = *nump;
+ if (num == 0) {
+ *bnp = blkptrtodb(ump, ip->i_db[bn]);
+ if (*bnp == 0) {
+ *bnp = -1;
+ } else if (runp) {
+ int32_t bnb = bn;
+ for (++bn; bn < NDADDR && *runp < maxrun &&
+ is_sequential(ump, ip->i_db[bn - 1], ip->i_db[bn]);
+ ++bn, ++*runp);
+ bn = bnb;
+ if (runb && (bn > 0)) {
+ for (--bn; (bn >= 0) && (*runb < maxrun) &&
+ is_sequential(ump, ip->i_db[bn],
+ ip->i_db[bn+1]);
+ --bn, ++*runb);
+ }
+ }
+ return (0);
+ }
+
+
+ /* Get disk address out of indirect block array */
+ daddr = ip->i_ib[ap->in_off];
+
+ for (bp = NULL, ++ap; --num; ++ap) {
+ /*
+ * 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 = ap->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)
+ bqrelse(bp);
+
+ ap->in_exists = 1;
+ bp = getblk(vp, metalbn, mp->mnt_stat.f_iosize, 0, 0);
+ if ((bp->b_flags & B_CACHE) == 0) {
+#ifdef DIAGNOSTIC
+ if (!daddr)
+ panic("ufs_bmaparray: indirect block not in cache");
+#endif
+ bp->b_blkno = blkptrtodb(ump, daddr);
+ bp->b_iocmd = BIO_READ;
+ bp->b_flags &= ~B_INVAL;
+ bp->b_ioflags &= ~BIO_ERROR;
+ vfs_busy_pages(bp, 0);
+ VOP_STRATEGY(bp->b_vp, bp);
+ curproc->p_stats->p_ru.ru_inblock++; /* XXX */
+ error = bufwait(bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ }
+
+ daddr = ((int32_t *)bp->b_data)[ap->in_off];
+ if (num == 1 && daddr && runp) {
+ for (bn = ap->in_off + 1;
+ bn < MNINDIR(ump) && *runp < maxrun &&
+ is_sequential(ump,
+ ((int32_t *)bp->b_data)[bn - 1],
+ ((int32_t *)bp->b_data)[bn]);
+ ++bn, ++*runp);
+ bn = ap->in_off;
+ if (runb && bn) {
+ for(--bn; bn >= 0 && *runb < maxrun &&
+ is_sequential(ump, ((int32_t *)bp->b_data)[bn],
+ ((int32_t *)bp->b_data)[bn+1]);
+ --bn, ++*runb);
+ }
+ }
+ }
+ if (bp)
+ bqrelse(bp);
+
+ /*
+ * Since this is FFS independent code, we are out of scope for the
+ * definitions of BLK_NOCOPY and BLK_SNAP, but we do know that they
+ * will fall in the range 1..um_seqinc, so we use that test and
+ * return a request for a zeroed out buffer if attempts are made
+ * to read a BLK_NOCOPY or BLK_SNAP block.
+ */
+ if ((ip->i_flags & SF_SNAPSHOT) && daddr > 0 && daddr < ump->um_seqinc){
+ *bnp = -1;
+ return (0);
+ }
+ *bnp = blkptrtodb(ump, daddr);
+ if (*bnp == 0) {
+ *bnp = -1;
+ }
+ return (0);
+}
+
+/*
+ * Create an array of logical block number/offset pairs which represent the
+ * path of indirect blocks required to access a data block. The first "pair"
+ * contains the logical block number of the appropriate single, double or
+ * triple indirect block and the offset into the inode indirect block array.
+ * Note, the logical block number of the inode single/double/triple indirect
+ * block appears twice in the array, once with the offset into the i_ib and
+ * once with the offset into the page itself.
+ */
+int
+ext2_getlbns(vp, bn, ap, nump)
+ struct vnode *vp;
+ int32_t bn;
+ struct indir *ap;
+ int *nump;
+{
+ long blockcnt, metalbn, realbn;
+ struct ext2mount *ump;
+ int i, numlevels, off;
+ int64_t qblockcnt;
+
+ ump = VFSTOEXT2(vp->v_mount);
+ if (nump)
+ *nump = 0;
+ numlevels = 0;
+ realbn = bn;
+ if ((long)bn < 0)
+ bn = -(long)bn;
+
+ /* The first NDADDR blocks are direct blocks. */
+ if (bn < NDADDR)
+ return (0);
+
+ /*
+ * Determine the number of levels of indirection. After this loop
+ * is done, blockcnt indicates the number of data blocks possible
+ * at the previous level of indirection, and NIADDR - i is the number
+ * of levels of indirection needed to locate the requested block.
+ */
+ for (blockcnt = 1, i = NIADDR, bn -= NDADDR;; i--, bn -= blockcnt) {
+ if (i == 0)
+ return (EFBIG);
+ /*
+ * Use int64_t's here to avoid overflow for triple indirect
+ * blocks when longs have 32 bits and the block size is more
+ * than 4K.
+ */
+ qblockcnt = (int64_t)blockcnt * MNINDIR(ump);
+ if (bn < qblockcnt)
+ break;
+ blockcnt = qblockcnt;
+ }
+
+ /* Calculate the address of the first meta-block. */
+ if (realbn >= 0)
+ metalbn = -(realbn - bn + NIADDR - i);
+ else
+ metalbn = -(-realbn - bn + NIADDR - i);
+
+ /*
+ * At each iteration, off is the offset into the bap array which is
+ * an array of disk addresses at the current level of indirection.
+ * The logical block number and the offset in that block are stored
+ * into the argument array.
+ */
+ ap->in_lbn = metalbn;
+ ap->in_off = off = NIADDR - i;
+ ap->in_exists = 0;
+ ap++;
+ for (++numlevels; i <= NIADDR; i++) {
+ /* If searching for a meta-data block, quit when found. */
+ if (metalbn == realbn)
+ break;
+
+ off = (bn / blockcnt) % MNINDIR(ump);
+
+ ++numlevels;
+ ap->in_lbn = metalbn;
+ ap->in_off = off;
+ ap->in_exists = 0;
+ ++ap;
+
+ metalbn -= -1 + off * blockcnt;
+ blockcnt /= MNINDIR(ump);
+ }
+ if (nump)
+ *nump = numlevels;
+ return (0);
+}
diff --git a/sys/gnu/ext2fs/ext2_extern.h b/sys/gnu/ext2fs/ext2_extern.h
new file mode 100644
index 0000000..a8fcf09
--- /dev/null
+++ b/sys/gnu/ext2fs/ext2_extern.h
@@ -0,0 +1,116 @@
+/*
+ * modified for EXT2FS support in Lites 1.1
+ *
+ * Aug 1995, Godmar Back (gback@cs.utah.edu)
+ * University of Utah, Department of Computer Science
+ */
+/*-
+ * 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
+ * $FreeBSD$
+ */
+
+#ifndef _SYS_GNU_EXT2FS_EXT2_EXTERN_H_
+#define _SYS_GNU_EXT2FS_EXT2_EXTERN_H_
+
+struct ext2_inode;
+struct indir;
+struct inode;
+struct mount;
+struct vfsconf;
+struct vnode;
+
+int ext2_alloc(struct inode *,
+ int32_t, int32_t, int, struct ucred *, int32_t *);
+int ext2_balloc(struct inode *,
+ int32_t, int, struct ucred *, struct buf **, int);
+int ext2_blkatoff(struct vnode *, off_t, char **, struct buf **);
+void ext2_blkfree(struct inode *, int32_t, long);
+int32_t ext2_blkpref(struct inode *, int32_t, int, int32_t *, int32_t);
+int ext2_bmap(struct vop_bmap_args *);
+int ext2_bmaparray(struct vnode *, int32_t, int32_t *, int *, int *);
+void ext2_dirbad(struct inode *ip, doff_t offset, char *how);
+void ext2_ei2i(struct ext2_inode *, struct inode *);
+int ext2_getlbns(struct vnode *, int32_t, struct indir *, int *);
+void ext2_i2ei(struct inode *, struct ext2_inode *);
+int ext2_ihashget(dev_t, ino_t, int, struct vnode **);
+void ext2_ihashinit(void);
+void ext2_ihashins(struct inode *);
+struct vnode *
+ ext2_ihashlookup(dev_t, ino_t);
+void ext2_ihashrem(struct inode *);
+void ext2_ihashuninit(void);
+void ext2_itimes(struct vnode *vp);
+int ext2_reallocblks(struct vop_reallocblks_args *);
+int ext2_reclaim(struct vop_reclaim_args *);
+void ext2_setblock(struct ext2_sb_info *, u_char *, int32_t);
+int ext2_truncate(struct vnode *, off_t, int, struct ucred *, struct thread *);
+int ext2_update(struct vnode *, int);
+int ext2_valloc(struct vnode *, int, struct ucred *, struct vnode **);
+int ext2_vfree(struct vnode *, ino_t, int);
+int ext2_vinit(struct mount *, vop_t **, vop_t **, struct vnode **vpp);
+int ext2_lookup(struct vop_cachedlookup_args *);
+int ext2_readdir(struct vop_readdir_args *);
+void ext2_print_inode(struct inode *);
+int ext2_direnter(struct inode *,
+ struct vnode *, struct componentname *);
+int ext2_dirremove(struct vnode *, struct componentname *);
+int ext2_dirrewrite(struct inode *,
+ struct inode *, struct componentname *);
+int ext2_dirempty(struct inode *, ino_t, struct ucred *);
+int ext2_checkpath(struct inode *, struct inode *, struct ucred *);
+struct ext2_group_desc * get_group_desc(struct mount * ,
+ unsigned int , struct buf ** );
+int ext2_group_sparse(int group);
+void ext2_discard_prealloc(struct inode *);
+int ext2_inactive(struct vop_inactive_args *);
+int ext2_new_block(struct mount * mp, unsigned long goal,
+ u_int32_t *prealloc_count, u_int32_t *prealloc_block);
+ino_t ext2_new_inode(const struct inode * dir, int mode);
+unsigned long ext2_count_free(struct buf *map, unsigned int numchars);
+void ext2_free_blocks(struct mount *mp, unsigned long block,
+ unsigned long count);
+void ext2_free_inode(struct inode * inode);
+void mark_buffer_dirty(struct buf *bh);
+
+/* Flags to low-level allocation routines. */
+#define B_CLRBUF 0x01 /* Request allocated buffer be cleared. */
+#define B_SYNC 0x02 /* Do all allocations synchronously. */
+#define B_METAONLY 0x04 /* Return indirect block buffer. */
+#define B_NOWAIT 0x08 /* do not sleep to await lock */
+
+extern vop_t **ext2_vnodeop_p;
+extern vop_t **ext2_specop_p;
+extern vop_t **ext2_fifoop_p;
+
+#endif /* !_SYS_GNU_EXT2FS_EXT2_EXTERN_H_ */
diff --git a/sys/gnu/ext2fs/ext2_fs.h b/sys/gnu/ext2fs/ext2_fs.h
index 836038e..2943593 100644
--- a/sys/gnu/ext2fs/ext2_fs.h
+++ b/sys/gnu/ext2fs/ext2_fs.h
@@ -1,4 +1,12 @@
/*
+ * modified for EXT2FS support in Lites 1.1
+ *
+ * Aug 1995, Godmar Back (gback@cs.utah.edu)
+ * University of Utah, Department of Computer Science
+ *
+ * $FreeBSD$
+ */
+/*
* linux/include/linux/ext2_fs.h
*
* Copyright (C) 1992, 1993, 1994, 1995
@@ -16,7 +24,19 @@
#ifndef _LINUX_EXT2_FS_H
#define _LINUX_EXT2_FS_H
-#include <linux/types.h>
+#include <sys/types.h>
+
+#define __u32 u_int32_t
+#define u32 u_int32_t
+#define __u16 u_int16_t
+#define __u8 u_int8_t
+
+#define __s32 int32_t
+#define __s16 int16_t
+#define __s8 int8_t
+
+#define umode_t mode_t
+#define loff_t off_t
/*
* The second extended filesystem constants/structures
@@ -44,9 +64,9 @@
*/
#ifdef EXT2FS_DEBUG
# define ext2_debug(f, a...) { \
- printk ("EXT2-fs DEBUG (%s, %d): %s:", \
- __FILE__, __LINE__, __FUNCTION__); \
- printk (f, ## a); \
+ printf ("EXT2-fs DEBUG (%s, %d): %s:", \
+ __FILE__, __LINE__, __func__); \
+ printf (f, ## a); \
}
#else
# define ext2_debug(f, a...) /**/
@@ -76,12 +96,19 @@
#define EXT2_LINK_MAX 32000
/*
+ * Note: under FreeBSD, the "user" versions of the following macros are
+ * used (and must be used) in most cases, because ((s)->u.ext2_sb.s_es is
+ * not accessible. This depends on __KERNEL__ not being defined for
+ * kernel builds under FreeBSD.
+ */
+
+/*
* Macro-instructions used to manage several block sizes
*/
#define EXT2_MIN_BLOCK_SIZE 1024
#define EXT2_MAX_BLOCK_SIZE 4096
#define EXT2_MIN_BLOCK_LOG_SIZE 10
-#ifdef __KERNEL__
+#if defined(__KERNEL__) || (defined(__FreeBSD__) && defined(_KERNEL))
# define EXT2_BLOCK_SIZE(s) ((s)->s_blocksize)
#else
# define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size)
@@ -93,6 +120,7 @@
#else
# define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10)
#endif
+#ifdef notyet
#ifdef __KERNEL__
#define EXT2_ADDR_PER_BLOCK_BITS(s) ((s)->u.ext2_sb.s_addr_per_block_bits)
#define EXT2_INODE_SIZE(s) ((s)->u.ext2_sb.s_inode_size)
@@ -105,6 +133,12 @@
EXT2_GOOD_OLD_FIRST_INO : \
(s)->s_first_ino)
#endif
+#else /* !notyet */
+#define EXT2_INODES_PER_BLOCK(s) ((s)->s_inodes_per_block)
+/* Should be sizeof(struct ext2_inode): */
+#define EXT2_INODE_SIZE 128
+#define EXT2_FIRST_INO 11
+#endif /* notyet */
/*
* Macro-instructions used to manage fragments
@@ -116,7 +150,11 @@
# define EXT2_FRAG_SIZE(s) ((s)->u.ext2_sb.s_frag_size)
# define EXT2_FRAGS_PER_BLOCK(s) ((s)->u.ext2_sb.s_frags_per_block)
#else
+# if defined(_KERNEL) && defined(__FreeBSD__)
+# define EXT2_FRAG_SIZE(s) ((s)->s_frag_size)
+# else
# define EXT2_FRAG_SIZE(s) (EXT2_MIN_FRAG_SIZE << (s)->s_log_frag_size)
+# endif
# define EXT2_FRAGS_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s))
#endif
@@ -179,6 +217,7 @@ struct ext2_group_desc
#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)
#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
+#define EXT2_MAXSYMLINKLEN (EXT2_N_BLOCKS * sizeof (__u32))
/*
* Inode flags
@@ -442,9 +481,13 @@ struct ext2_super_block {
#define EXT2_FEATURE_COMPAT_SUPP 0
#define EXT2_FEATURE_INCOMPAT_SUPP EXT2_FEATURE_INCOMPAT_FILETYPE
+#ifdef notyet
#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
+#else
+#define EXT2_FEATURE_RO_COMPAT_SUPP EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
+#endif
/*
* Default values for user and/or group using reserved blocks
@@ -503,120 +546,4 @@ struct ext2_dir_entry_2 {
#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \
~EXT2_DIR_ROUND)
-#ifdef __KERNEL__
-/*
- * Function prototypes
- */
-
-/*
- * Ok, these declarations are also in <linux/kernel.h> but none of the
- * ext2 source programs needs to include it so they are duplicated here.
- */
-# define NORET_TYPE /**/
-# define ATTRIB_NORET __attribute__((noreturn))
-# define NORET_AND noreturn,
-
-/* acl.c */
-extern int ext2_permission (struct inode *, int);
-
-/* balloc.c */
-extern int ext2_group_sparse(int group);
-extern int ext2_new_block (const struct inode *, unsigned long,
- __u32 *, __u32 *, int *);
-extern void ext2_free_blocks (const struct inode *, unsigned long,
- unsigned long);
-extern unsigned long ext2_count_free_blocks (struct super_block *);
-extern void ext2_check_blocks_bitmap (struct super_block *);
-extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb,
- unsigned int block_group,
- struct buffer_head ** bh);
-
-/* bitmap.c */
-extern unsigned long ext2_count_free (struct buffer_head *, unsigned);
-
-/* dir.c */
-extern int ext2_check_dir_entry (const char *, struct inode *,
- struct ext2_dir_entry_2 *, struct buffer_head *,
- unsigned long);
-
-/* file.c */
-extern int ext2_read (struct inode *, struct file *, char *, int);
-extern int ext2_write (struct inode *, struct file *, char *, int);
-
-/* fsync.c */
-extern int ext2_sync_file (struct file *, struct dentry *);
-
-/* ialloc.c */
-extern struct inode * ext2_new_inode (const struct inode *, int, int *);
-extern void ext2_free_inode (struct inode *);
-extern unsigned long ext2_count_free_inodes (struct super_block *);
-extern void ext2_check_inodes_bitmap (struct super_block *);
-
-/* inode.c */
-extern long ext2_bmap (struct inode *, long);
-extern int ext2_get_block (struct inode *, long, struct buffer_head *, int);
-
-extern struct buffer_head * ext2_getblk (struct inode *, long, int, int *);
-extern int ext2_getblk_block (struct inode *, long, int, int *, int *);
-extern struct buffer_head * ext2_bread (struct inode *, int, int, int *);
-
-extern int ext2_getcluster (struct inode * inode, long block);
-extern void ext2_read_inode (struct inode *);
-extern void ext2_write_inode (struct inode *);
-extern void ext2_put_inode (struct inode *);
-extern void ext2_delete_inode (struct inode *);
-extern int ext2_sync_inode (struct inode *);
-extern void ext2_discard_prealloc (struct inode *);
-
-/* ioctl.c */
-extern int ext2_ioctl (struct inode *, struct file *, unsigned int,
- unsigned long);
-
-/* namei.c */
-extern void ext2_release (struct inode *, struct file *);
-extern struct dentry *ext2_lookup (struct inode *, struct dentry *);
-extern int ext2_create (struct inode *,struct dentry *,int);
-extern int ext2_mkdir (struct inode *,struct dentry *,int);
-extern int ext2_rmdir (struct inode *,struct dentry *);
-extern int ext2_unlink (struct inode *,struct dentry *);
-extern int ext2_symlink (struct inode *,struct dentry *,const char *);
-extern int ext2_link (struct dentry *, struct inode *, struct dentry *);
-extern int ext2_mknod (struct inode *, struct dentry *, int, int);
-extern int ext2_rename (struct inode *, struct dentry *,
- struct inode *, struct dentry *);
-
-/* super.c */
-extern void ext2_error (struct super_block *, const char *, const char *, ...)
- __attribute__ ((format (printf, 3, 4)));
-extern NORET_TYPE void ext2_panic (struct super_block *, const char *,
- const char *, ...)
- __attribute__ ((NORET_AND format (printf, 3, 4)));
-extern void ext2_warning (struct super_block *, const char *, const char *, ...)
- __attribute__ ((format (printf, 3, 4)));
-extern void ext2_put_super (struct super_block *);
-extern void ext2_write_super (struct super_block *);
-extern int ext2_remount (struct super_block *, int *, char *);
-extern struct super_block * ext2_read_super (struct super_block *,void *,int);
-extern int init_ext2_fs(void);
-extern int ext2_statfs (struct super_block *, struct statfs *, int);
-
-/* truncate.c */
-extern void ext2_truncate (struct inode *);
-
-/*
- * Inodes and files operations
- */
-
-/* dir.c */
-extern struct inode_operations ext2_dir_inode_operations;
-
-/* file.c */
-extern struct inode_operations ext2_file_inode_operations;
-
-/* symlink.c */
-extern struct inode_operations ext2_symlink_inode_operations;
-extern struct inode_operations ext2_fast_symlink_inode_operations;
-
-#endif /* __KERNEL__ */
-
#endif /* _LINUX_EXT2_FS_H */
diff --git a/sys/gnu/ext2fs/ext2_fs_i.h b/sys/gnu/ext2fs/ext2_fs_i.h
deleted file mode 100644
index 72bcd5c..0000000
--- a/sys/gnu/ext2fs/ext2_fs_i.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * linux/include/linux/ext2_fs_i.h
- *
- * Copyright (C) 1992, 1993, 1994, 1995
- * Remy Card (card@masi.ibp.fr)
- * Laboratoire MASI - Institut Blaise Pascal
- * Universite Pierre et Marie Curie (Paris VI)
- *
- * from
- *
- * linux/include/linux/minix_fs_i.h
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- */
-
-#ifndef _LINUX_EXT2_FS_I
-#define _LINUX_EXT2_FS_I
-
-/*
- * second extended file system inode data in memory
- */
-struct ext2_inode_info {
- __u32 i_data[15];
- __u32 i_flags;
- __u32 i_faddr;
- __u8 i_frag_no;
- __u8 i_frag_size;
- __u16 i_osync;
- __u32 i_file_acl;
- __u32 i_dir_acl;
- __u32 i_dtime;
- __u32 not_used_1; /* FIX: not used/ 2.2 placeholder */
- __u32 i_block_group;
- __u32 i_next_alloc_block;
- __u32 i_next_alloc_goal;
- __u32 i_prealloc_block;
- __u32 i_prealloc_count;
- __u32 i_high_size;
- int i_new_inode:1; /* Is a freshly allocated inode */
-};
-
-#endif /* _LINUX_EXT2_FS_I */
diff --git a/sys/gnu/ext2fs/ext2_fs_sb.h b/sys/gnu/ext2fs/ext2_fs_sb.h
index 2072163..85f71bf 100644
--- a/sys/gnu/ext2fs/ext2_fs_sb.h
+++ b/sys/gnu/ext2fs/ext2_fs_sb.h
@@ -1,4 +1,10 @@
/*
+ * modified for EXT2FS support in Lites 1.1
+ *
+ * Aug 1995, Godmar Back (gback@cs.utah.edu)
+ * University of Utah, Department of Computer Science
+ */
+/*
* linux/include/linux/ext2_fs_sb.h
*
* Copyright (C) 1992, 1993, 1994, 1995
@@ -16,8 +22,6 @@
#ifndef _LINUX_EXT2_FS_SB
#define _LINUX_EXT2_FS_SB
-#include <linux/ext2_fs.h>
-
/*
* The following is not needed anymore since the descriptors buffer
* heads are now dynamically allocated
@@ -26,6 +30,9 @@
#define EXT2_MAX_GROUP_LOADED 8
+#define buffer_head buf
+#define MAXMNTLEN 512
+
/*
* second extended-fs super-block data in memory
*/
@@ -49,18 +56,26 @@ struct ext2_sb_info {
struct buffer_head * s_inode_bitmap[EXT2_MAX_GROUP_LOADED];
unsigned long s_block_bitmap_number[EXT2_MAX_GROUP_LOADED];
struct buffer_head * s_block_bitmap[EXT2_MAX_GROUP_LOADED];
+ int s_rename_lock;
unsigned long s_mount_opt;
unsigned short s_resuid;
unsigned short s_resgid;
unsigned short s_mount_state;
- unsigned short s_pad;
- int s_addr_per_block_bits;
- int s_desc_per_block_bits;
- int s_inode_size;
- int s_first_ino;
- int s_feature_compat;
- int s_feature_incompat;
- int s_feature_ro_compat;
+ /*
+ stuff that FFS keeps in its super block or that linux
+ has in its non-ext2 specific super block and which is
+ generally considered useful
+ */
+ unsigned long s_blocksize;
+ unsigned long s_blocksize_bits;
+ unsigned int s_bshift; /* = log2(s_blocksize) */
+ quad_t s_qbmask; /* = s_blocksize - 1 */
+ unsigned int s_fsbtodb; /* shift to get disk block */
+ char s_rd_only; /* read-only */
+ char s_dirt; /* fs modified flag */
+ char s_wasvalid; /* valid at mount time */
+
+ char fs_fsmnt[MAXMNTLEN]; /* name mounted on */
};
#endif /* _LINUX_EXT2_FS_SB */
diff --git a/sys/gnu/ext2fs/ext2_ihash.c b/sys/gnu/ext2fs/ext2_ihash.c
new file mode 100644
index 0000000..df2bd98
--- /dev/null
+++ b/sys/gnu/ext2fs/ext2_ihash.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1991, 1993, 1995
+ * 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_ihash.c 8.7 (Berkeley) 5/17/95
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/mutex.h>
+
+#include <gnu/ext2fs/inode.h>
+#include <gnu/ext2fs/ext2_extern.h>
+
+static MALLOC_DEFINE(M_EXT2IHASH, "EXT2 ihash", "EXT2 Inode hash tables");
+/*
+ * Structures associated with inode cacheing.
+ */
+static LIST_HEAD(ihashhead, inode) *ihashtbl;
+static u_long ihash; /* size of hash table - 1 */
+#define INOHASH(device, inum) (&ihashtbl[(minor(device) + (inum)) & ihash])
+static struct mtx ext2_ihash_mtx;
+
+/*
+ * Initialize inode hash table.
+ */
+void
+ext2_ihashinit()
+{
+
+ KASSERT(ihashtbl == NULL, ("ext2_ihashinit called twice"));
+ ihashtbl = hashinit(desiredvnodes, M_EXT2IHASH, &ihash);
+ mtx_init(&ext2_ihash_mtx, "ext2 ihash", NULL, MTX_DEF);
+}
+
+/*
+ * Destroy the inode hash table.
+ */
+void
+ext2_ihashuninit()
+{
+
+ hashdestroy(ihashtbl, M_EXT2IHASH, ihash);
+ mtx_destroy(&ext2_ihash_mtx);
+}
+
+/*
+ * Use the device/inum pair to find the incore inode, and return a pointer
+ * to it. If it is in core, return it, even if it is locked.
+ */
+struct vnode *
+ext2_ihashlookup(dev, inum)
+ dev_t dev;
+ ino_t inum;
+{
+ struct inode *ip;
+
+ mtx_lock(&ext2_ihash_mtx);
+ LIST_FOREACH(ip, INOHASH(dev, inum), i_hash)
+ if (inum == ip->i_number && dev == ip->i_dev)
+ break;
+ mtx_unlock(&ext2_ihash_mtx);
+
+ if (ip)
+ return (ITOV(ip));
+ return (NULLVP);
+}
+
+/*
+ * Use the device/inum pair to find the incore inode, and return a pointer
+ * to it. If it is in core, but locked, wait for it.
+ */
+int
+ext2_ihashget(dev, inum, flags, vpp)
+ dev_t dev;
+ ino_t inum;
+ int flags;
+ struct vnode **vpp;
+{
+ struct thread *td = curthread; /* XXX */
+ struct inode *ip;
+ struct vnode *vp;
+ int error;
+
+ *vpp = NULL;
+loop:
+ mtx_lock(&ext2_ihash_mtx);
+ LIST_FOREACH(ip, INOHASH(dev, inum), i_hash) {
+ if (inum == ip->i_number && dev == ip->i_dev) {
+ vp = ITOV(ip);
+ mtx_lock(&vp->v_interlock);
+ mtx_unlock(&ext2_ihash_mtx);
+ error = vget(vp, flags | LK_INTERLOCK, td);
+ if (error == ENOENT)
+ goto loop;
+ if (error)
+ return (error);
+ *vpp = vp;
+ return (0);
+ }
+ }
+ mtx_unlock(&ext2_ihash_mtx);
+ return (0);
+}
+
+/*
+ * Insert the inode into the hash table, and return it locked.
+ */
+void
+ext2_ihashins(ip)
+ struct inode *ip;
+{
+ struct thread *td = curthread; /* XXX */
+ struct ihashhead *ipp;
+
+ /* lock the inode, then put it on the appropriate hash list */
+ vn_lock(ITOV(ip), LK_EXCLUSIVE | LK_RETRY, td);
+
+ mtx_lock(&ext2_ihash_mtx);
+ ipp = INOHASH(ip->i_dev, ip->i_number);
+ LIST_INSERT_HEAD(ipp, ip, i_hash);
+ ip->i_flag |= IN_HASHED;
+ mtx_unlock(&ext2_ihash_mtx);
+}
+
+/*
+ * Remove the inode from the hash table.
+ */
+void
+ext2_ihashrem(ip)
+ struct inode *ip;
+{
+ mtx_lock(&ext2_ihash_mtx);
+ if (ip->i_flag & IN_HASHED) {
+ ip->i_flag &= ~IN_HASHED;
+ LIST_REMOVE(ip, i_hash);
+ }
+ mtx_unlock(&ext2_ihash_mtx);
+}
diff --git a/sys/gnu/ext2fs/ext2_inode.c b/sys/gnu/ext2fs/ext2_inode.c
new file mode 100644
index 0000000..fc14dba
--- /dev/null
+++ b/sys/gnu/ext2fs/ext2_inode.c
@@ -0,0 +1,552 @@
+/*
+ * modified for Lites 1.1
+ *
+ * Aug 1995, Godmar Back (gback@cs.utah.edu)
+ * University of Utah, Department of Computer Science
+ */
+/*
+ * 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.5 (Berkeley) 12/30/93
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mount.h>
+#include <sys/bio.h>
+#include <sys/buf.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+
+#include <gnu/ext2fs/inode.h>
+#include <gnu/ext2fs/ext2_mount.h>
+#include <gnu/ext2fs/ext2_fs.h>
+#include <gnu/ext2fs/ext2_fs_sb.h>
+#include <gnu/ext2fs/fs.h>
+#include <gnu/ext2fs/ext2_extern.h>
+
+static int ext2_indirtrunc(struct inode *, int32_t, int32_t, int32_t, int,
+ long *);
+
+/*
+ * Update the access, modified, and inode change times as specified by the
+ * IN_ACCESS, IN_UPDATE, and IN_CHANGE flags respectively. Write the inode
+ * to disk if the IN_MODIFIED flag is set (it may be set initially, or by
+ * the timestamp update). The IN_LAZYMOD flag is set to force a write
+ * later if not now. If we write now, then clear both IN_MODIFIED and
+ * IN_LAZYMOD to reflect the presumably successful write, and if waitfor is
+ * set, then wait for the write to complete.
+ */
+int
+ext2_update(vp, waitfor)
+ struct vnode *vp;
+ int waitfor;
+{
+ struct ext2_sb_info *fs;
+ struct buf *bp;
+ struct inode *ip;
+ int error;
+
+ ext2_itimes(vp);
+ ip = VTOI(vp);
+ if ((ip->i_flag & IN_MODIFIED) == 0)
+ return (0);
+ ip->i_flag &= ~(IN_LAZYMOD | IN_MODIFIED);
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ return (0);
+ fs = ip->i_e2fs;
+ if ((error = bread(ip->i_devvp,
+ fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
+ (int)fs->s_blocksize, NOCRED, &bp)) != 0) {
+ brelse(bp);
+ return (error);
+ }
+ ext2_i2ei(ip, (struct ext2_inode *)((char *)bp->b_data +
+ EXT2_INODE_SIZE * ino_to_fsbo(fs, ip->i_number)));
+/*
+ if (waitfor && (vp->v_mount->mnt_flag & MNT_ASYNC) == 0)
+ 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
+ext2_truncate(vp, length, flags, cred, td)
+ struct vnode *vp;
+ off_t length;
+ int flags;
+ struct ucred *cred;
+ struct thread *td;
+{
+ struct vnode *ovp = vp;
+ int32_t lastblock;
+ struct inode *oip;
+ int32_t bn, lbn, lastiblock[NIADDR], indir_lbn[NIADDR];
+ int32_t oldblks[NDADDR + NIADDR], newblks[NDADDR + NIADDR];
+ struct ext2_sb_info *fs;
+ struct buf *bp;
+ int offset, size, level;
+ long count, nblocks, blocksreleased = 0;
+ int aflags, error, i, allerror;
+ off_t osize;
+/*
+printf("ext2_truncate called %d to %d\n", VTOI(ovp)->i_number, length);
+*/ /*
+ * negative file sizes will totally break the code below and
+ * are not meaningful anyways.
+ */
+ if (length < 0)
+ return EFBIG;
+
+ oip = VTOI(ovp);
+ if (ovp->v_type == VLNK &&
+ oip->i_size < ovp->v_mount->mnt_maxsymlinklen) {
+#if DIAGNOSTIC
+ if (length != 0)
+ panic("ext2_truncate: partial truncate of symlink");
+#endif
+ bzero((char *)&oip->i_shortlink, (u_int)oip->i_size);
+ oip->i_size = 0;
+ oip->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (ext2_update(ovp, 1));
+ }
+ if (oip->i_size == length) {
+ oip->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (ext2_update(ovp, 0));
+ }
+ fs = oip->i_e2fs;
+ osize = oip->i_size;
+ ext2_discard_prealloc(oip);
+ /*
+ * Lengthen the size of the file. We must ensure that the
+ * last byte of the file is allocated. Since the smallest
+ * value of oszie is 0, length will be at least 1.
+ */
+ if (osize < length) {
+ offset = blkoff(fs, length - 1);
+ lbn = lblkno(fs, length - 1);
+ aflags = B_CLRBUF;
+ if (flags & IO_SYNC)
+ aflags |= B_SYNC;
+ vnode_pager_setsize(ovp, length);
+ if ((error = ext2_balloc(oip, lbn, offset + 1, cred, &bp,
+ aflags)) != 0)
+ return (error);
+ oip->i_size = length;
+ if (aflags & IO_SYNC)
+ bwrite(bp);
+ else
+ bawrite(bp);
+ oip->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (ext2_update(ovp, 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 accessible again because
+ * of subsequent file growth.
+ */
+ /* I don't understand the comment above */
+ offset = blkoff(fs, length);
+ if (offset == 0) {
+ oip->i_size = length;
+ } else {
+ lbn = lblkno(fs, length);
+ aflags = B_CLRBUF;
+ if (flags & IO_SYNC)
+ aflags |= B_SYNC;
+ if ((error = ext2_balloc(oip, lbn, offset, cred, &bp,
+ aflags)) != 0)
+ return (error);
+ oip->i_size = length;
+ size = blksize(fs, oip, lbn);
+ bzero((char *)bp->b_data + offset, (u_int)(size - offset));
+ allocbuf(bp, size);
+ if (aflags & IO_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->s_blocksize - 1) - 1;
+ lastiblock[SINGLE] = lastblock - NDADDR;
+ lastiblock[DOUBLE] = lastiblock[SINGLE] - NINDIR(fs);
+ lastiblock[TRIPLE] = lastiblock[DOUBLE] - NINDIR(fs) * NINDIR(fs);
+ nblocks = btodb(fs->s_blocksize);
+ /*
+ * 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 ext2_indirtrunc below.
+ */
+ bcopy((caddr_t)&oip->i_db[0], (caddr_t)oldblks, sizeof oldblks);
+ for (level = TRIPLE; level >= SINGLE; level--)
+ if (lastiblock[level] < 0) {
+ oip->i_ib[level] = 0;
+ lastiblock[level] = -1;
+ }
+ for (i = NDADDR - 1; i > lastblock; i--)
+ oip->i_db[i] = 0;
+ oip->i_flag |= IN_CHANGE | IN_UPDATE;
+ allerror = ext2_update(ovp, 1);
+
+ /*
+ * 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_db[0], (caddr_t)newblks, sizeof newblks);
+ bcopy((caddr_t)oldblks, (caddr_t)&oip->i_db[0], sizeof oldblks);
+ oip->i_size = osize;
+ error = vtruncbuf(ovp, cred, td, length, (int)fs->s_blocksize);
+ if (error && (allerror == 0))
+ allerror = error;
+
+ /*
+ * 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_ib[level];
+ if (bn != 0) {
+ error = ext2_indirtrunc(oip, indir_lbn[level],
+ fsbtodb(fs, bn), lastiblock[level], level, &count);
+ if (error)
+ allerror = error;
+ blocksreleased += count;
+ if (lastiblock[level] < 0) {
+ oip->i_ib[level] = 0;
+ ext2_blkfree(oip, bn, fs->s_frag_size);
+ blocksreleased += nblocks;
+ }
+ }
+ if (lastiblock[level] >= 0)
+ goto done;
+ }
+
+ /*
+ * All whole direct blocks or frags.
+ */
+ for (i = NDADDR - 1; i > lastblock; i--) {
+ long bsize;
+
+ bn = oip->i_db[i];
+ if (bn == 0)
+ continue;
+ oip->i_db[i] = 0;
+ bsize = blksize(fs, oip, i);
+ ext2_blkfree(oip, bn, bsize);
+ blocksreleased += btodb(bsize);
+ }
+ if (lastblock < 0)
+ goto done;
+
+ /*
+ * Finally, look for a change in size of the
+ * last direct block; release any frags.
+ */
+ bn = oip->i_db[lastblock];
+ if (bn != 0) {
+ long oldspace, newspace;
+
+ /*
+ * Calculate amount of space we're giving
+ * back as old block size minus new block size.
+ */
+ oldspace = blksize(fs, oip, lastblock);
+ oip->i_size = length;
+ newspace = blksize(fs, oip, lastblock);
+ if (newspace == 0)
+ panic("itrunc: newspace");
+ if (oldspace - newspace > 0) {
+ /*
+ * Block number of space to be free'd is
+ * the old block # plus the number of frags
+ * required for the storage we're keeping.
+ */
+ bn += numfrags(fs, newspace);
+ ext2_blkfree(oip, bn, oldspace - newspace);
+ blocksreleased += btodb(oldspace - newspace);
+ }
+ }
+done:
+#if DIAGNOSTIC
+ for (level = SINGLE; level <= TRIPLE; level++)
+ if (newblks[NDADDR + level] != oip->i_ib[level])
+ panic("itrunc1");
+ for (i = 0; i < NDADDR; i++)
+ if (newblks[i] != oip->i_db[i])
+ panic("itrunc2");
+ VI_LOCK(ovp);
+ if (length == 0 && (!TAILQ_EMPTY(&ovp->v_dirtyblkhd) ||
+ !TAILQ_EMPTY(&ovp->v_cleanblkhd)))
+ panic("itrunc3");
+ VI_UNLOCK(ovp);
+#endif /* DIAGNOSTIC */
+ /*
+ * Put back the real size.
+ */
+ oip->i_size = length;
+ oip->i_blocks -= blocksreleased;
+ if (oip->i_blocks < 0) /* sanity */
+ oip->i_blocks = 0;
+ oip->i_flag |= IN_CHANGE;
+ vnode_pager_setsize(ovp, length);
+ 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
+ext2_indirtrunc(ip, lbn, dbn, lastbn, level, countp)
+ struct inode *ip;
+ int32_t lbn, lastbn;
+ int32_t dbn;
+ int level;
+ long *countp;
+{
+ struct buf *bp;
+ struct ext2_sb_info *fs = ip->i_e2fs;
+ struct vnode *vp;
+ int32_t *bap, *copy, nb, nlbn, last;
+ long blkcount, factor;
+ int i, 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->s_blocksize);
+ /*
+ * 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->s_blocksize, 0, 0);
+ if (bp->b_flags & (B_DONE | B_DELWRI)) {
+ } else {
+ bp->b_iocmd = BIO_READ;
+ if (bp->b_bcount > bp->b_bufsize)
+ panic("ext2_indirtrunc: bad buffer size");
+ bp->b_blkno = dbn;
+ vfs_busy_pages(bp, 0);
+ VOP_STRATEGY(vp, bp);
+ error = bufwait(bp);
+ }
+ if (error) {
+ brelse(bp);
+ *countp = 0;
+ return (error);
+ }
+
+ bap = (int32_t *)bp->b_data;
+ MALLOC(copy, int32_t *, fs->s_blocksize, M_TEMP, M_WAITOK);
+ bcopy((caddr_t)bap, (caddr_t)copy, (u_int)fs->s_blocksize);
+ bzero((caddr_t)&bap[last + 1],
+ (u_int)(NINDIR(fs) - (last + 1)) * sizeof (int32_t));
+ if (last == -1)
+ bp->b_flags |= B_INVAL;
+ 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) {
+ if ((error = ext2_indirtrunc(ip, nlbn,
+ fsbtodb(fs, nb), (int32_t)-1, level - 1, &blkcount)) != 0)
+ allerror = error;
+ blocksreleased += blkcount;
+ }
+ ext2_blkfree(ip, nb, fs->s_blocksize);
+ blocksreleased += nblocks;
+ }
+
+ /*
+ * Recursively free last partial block.
+ */
+ if (level > SINGLE && lastbn >= 0) {
+ last = lastbn % factor;
+ nb = bap[i];
+ if (nb != 0) {
+ if ((error = ext2_indirtrunc(ip, nlbn, fsbtodb(fs, nb),
+ last, level - 1, &blkcount)) != 0)
+ allerror = error;
+ blocksreleased += blkcount;
+ }
+ }
+ FREE(copy, M_TEMP);
+ *countp = blocksreleased;
+ return (allerror);
+}
+
+/*
+ * discard preallocated blocks
+ */
+int
+ext2_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ struct thread *a_td;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct inode *ip = VTOI(vp);
+ struct thread *td = ap->a_td;
+ int mode, error = 0;
+
+ ext2_discard_prealloc(ip);
+ if (prtactive && vrefcnt(vp) != 0)
+ vprint("ext2_inactive: pushing active", vp);
+
+ /*
+ * Ignore inodes related to stale file handles.
+ */
+ if (ip->i_mode == 0)
+ goto out;
+ if (ip->i_nlink <= 0) {
+ (void) vn_write_suspend_wait(vp, NULL, V_WAIT);
+ error = ext2_truncate(vp, (off_t)0, 0, NOCRED, td);
+ ip->i_rdev = 0;
+ mode = ip->i_mode;
+ ip->i_mode = 0;
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ ext2_vfree(vp, ip->i_number, mode);
+ }
+ if (ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) {
+ if ((ip->i_flag & (IN_CHANGE | IN_UPDATE | IN_MODIFIED)) == 0 &&
+ vn_write_suspend_wait(vp, NULL, V_NOWAIT)) {
+ ip->i_flag &= ~IN_ACCESS;
+ } else {
+ (void) vn_write_suspend_wait(vp, NULL, V_WAIT);
+ ext2_update(vp, 0);
+ }
+ }
+out:
+ VOP_UNLOCK(vp, 0, td);
+ /*
+ * If we are done with the inode, reclaim it
+ * so that it can be reused immediately.
+ */
+ if (ip->i_mode == 0)
+ vrecycle(vp, NULL, td);
+ return (error);
+}
+
+/*
+ * Reclaim an inode so that it can be used for other purposes.
+ */
+int
+ext2_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ struct thread *a_td;
+ } */ *ap;
+{
+ struct inode *ip;
+ struct vnode *vp = ap->a_vp;
+
+ if (prtactive && vrefcnt(vp) != 0)
+ vprint("ufs_reclaim: pushing active", vp);
+ ip = VTOI(vp);
+ if (ip->i_flag & IN_LAZYMOD) {
+ ip->i_flag |= IN_MODIFIED;
+ ext2_update(vp, 0);
+ }
+ /*
+ * Remove the inode from its hash chain.
+ */
+ ext2_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_EXT2NODE);
+ vp->v_data = 0;
+ return (0);
+}
diff --git a/sys/gnu/ext2fs/ext2_inode_cnv.c b/sys/gnu/ext2fs/ext2_inode_cnv.c
new file mode 100644
index 0000000..d64e549
--- /dev/null
+++ b/sys/gnu/ext2fs/ext2_inode_cnv.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 1995 The University of Utah and
+ * the Computer Systems Laboratory at the University of Utah (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software is hereby
+ * granted provided that (1) source code retains these copyright, permission,
+ * and disclaimer notices, and (2) redistributions including binaries
+ * reproduce the notices in supporting documentation, and (3) all advertising
+ * materials mentioning features or use of this software display the following
+ * acknowledgement: ``This product includes software developed by the
+ * Computer Systems Laboratory at the University of Utah.''
+ *
+ * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
+ * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
+ * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * CSL requests users of this software to return to csl-dist@cs.utah.edu any
+ * improvements that they make and grant CSL redistribution rights.
+ *
+ * Utah $Hdr$
+ * $FreeBSD$
+ */
+
+/*
+ * routines to convert on disk ext2 inodes into inodes and back
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/lock.h>
+#include <sys/stat.h>
+#include <sys/vnode.h>
+
+#include <gnu/ext2fs/inode.h>
+#include <gnu/ext2fs/ext2_fs.h>
+#include <gnu/ext2fs/ext2_extern.h>
+
+void
+ext2_print_inode( in )
+ struct inode *in;
+{
+ int i;
+
+ printf( "Inode: %5d", in->i_number);
+ printf( /* "Inode: %5d" */
+ " Type: %10s Mode: 0x%o Flags: 0x%x Version: %d\n",
+ "n/a", in->i_mode, in->i_flags, in->i_gen);
+ printf( "User: %5lu Group: %5lu Size: %lu\n",
+ (unsigned long)in->i_uid, (unsigned long)in->i_gid,
+ (unsigned long)in->i_size);
+ printf( "Links: %3d Blockcount: %d\n",
+ in->i_nlink, in->i_blocks);
+ printf( "ctime: 0x%x", in->i_ctime);
+ printf( "atime: 0x%x", in->i_atime);
+ printf( "mtime: 0x%x", in->i_mtime);
+ printf( "BLOCKS: ");
+ for(i=0; i < (in->i_blocks <= 24 ? ((in->i_blocks+1)/2): 12); i++)
+ printf("%d ", in->i_db[i]);
+ printf("\n");
+}
+
+/*
+ * raw ext2 inode to inode
+ */
+void
+ext2_ei2i(ei, ip)
+ struct ext2_inode *ei;
+ struct inode *ip;
+{
+ int i;
+
+ ip->i_nlink = ei->i_links_count;
+ /* Godmar thinks - if the link count is zero, then the inode is
+ unused - according to ext2 standards. Ufs marks this fact
+ by setting i_mode to zero - why ?
+ I can see that this might lead to problems in an undelete.
+ */
+ ip->i_mode = ei->i_links_count ? ei->i_mode : 0;
+ ip->i_size = ei->i_size;
+ ip->i_atime = ei->i_atime;
+ ip->i_mtime = ei->i_mtime;
+ ip->i_ctime = ei->i_ctime;
+ ip->i_flags = 0;
+ ip->i_flags |= (ei->i_flags & EXT2_APPEND_FL) ? APPEND : 0;
+ ip->i_flags |= (ei->i_flags & EXT2_IMMUTABLE_FL) ? IMMUTABLE : 0;
+ ip->i_blocks = ei->i_blocks;
+ ip->i_gen = ei->i_generation;
+ ip->i_uid = ei->i_uid;
+ ip->i_gid = ei->i_gid;
+ /* XXX use memcpy */
+ for(i = 0; i < NDADDR; i++)
+ ip->i_db[i] = ei->i_block[i];
+ for(i = 0; i < NIADDR; i++)
+ ip->i_ib[i] = ei->i_block[EXT2_NDIR_BLOCKS + i];
+}
+
+/*
+ * inode to raw ext2 inode
+ */
+void
+ext2_i2ei(ip, ei)
+ struct inode *ip;
+ struct ext2_inode *ei;
+{
+ int i;
+
+ ei->i_mode = ip->i_mode;
+ ei->i_links_count = ip->i_nlink;
+ /*
+ Godmar thinks: if dtime is nonzero, ext2 says this inode
+ has been deleted, this would correspond to a zero link count
+ */
+ ei->i_dtime = ei->i_links_count ? 0 : ip->i_mtime;
+ ei->i_size = ip->i_size;
+ ei->i_atime = ip->i_atime;
+ ei->i_mtime = ip->i_mtime;
+ ei->i_ctime = ip->i_ctime;
+ ei->i_flags = ip->i_flags;
+ ei->i_flags = 0;
+ ei->i_flags |= (ip->i_flags & APPEND) ? EXT2_APPEND_FL: 0;
+ ei->i_flags |= (ip->i_flags & IMMUTABLE) ? EXT2_IMMUTABLE_FL: 0;
+ ei->i_blocks = ip->i_blocks;
+ ei->i_generation = ip->i_gen;
+ ei->i_uid = ip->i_uid;
+ ei->i_gid = ip->i_gid;
+ /* XXX use memcpy */
+ for(i = 0; i < NDADDR; i++)
+ ei->i_block[i] = ip->i_db[i];
+ for(i = 0; i < NIADDR; i++)
+ ei->i_block[EXT2_NDIR_BLOCKS + i] = ip->i_ib[i];
+}
diff --git a/sys/gnu/ext2fs/ext2_linux_balloc.c b/sys/gnu/ext2fs/ext2_linux_balloc.c
index 43a425b..435661b 100644
--- a/sys/gnu/ext2fs/ext2_linux_balloc.c
+++ b/sys/gnu/ext2fs/ext2_linux_balloc.c
@@ -1,4 +1,12 @@
/*
+ * modified for Lites 1.1
+ *
+ * Aug 1995, Godmar Back (gback@cs.utah.edu)
+ * University of Utah, Department of Computer Science
+ *
+ * $FreeBSD$
+ */
+/*
* linux/fs/ext2/balloc.c
*
* Copyright (C) 1992, 1993, 1994, 1995
@@ -7,17 +15,6 @@
* Universite Pierre et Marie Curie (Paris VI)
*
* Enhanced block allocation by Stephen Tweedie (sct@dcs.ed.ac.uk), 1993
- * Big-endian to little-endian byte-swapping/bitmaps by
- * David S. Miller (davem@caip.rutgers.edu), 1995
- */
-
-#include <linux/fs.h>
-#include <linux/locks.h>
-#include <linux/quotaops.h>
-
-
-/*
- * balloc.c contains the blocks allocation and deallocation routines
*/
/*
@@ -31,78 +28,53 @@
* when a file system is mounted (see ext2_read_super).
*/
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bio.h>
+#include <sys/buf.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+
+#include <gnu/ext2fs/inode.h>
+#include <gnu/ext2fs/ext2_mount.h>
+#include <gnu/ext2fs/ext2_extern.h>
+#include <gnu/ext2fs/ext2_fs.h>
+#include <gnu/ext2fs/ext2_fs_sb.h>
+#include <gnu/ext2fs/fs.h>
+
+#ifdef __i386__
+#include <gnu/ext2fs/i386-bitops.h>
+#elif __alpha__
+#include <gnu/ext2fs/alpha-bitops.h>
+#else
+#error Provide a bitops.h file, please!
+#endif
#define in_range(b, first, len) ((b) >= (first) && (b) <= (first) + (len) - 1)
-struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb,
- unsigned int block_group,
- struct buffer_head ** bh)
-{
- unsigned long group_desc;
- unsigned long desc;
- struct ext2_group_desc * gdp;
-
- if (block_group >= sb->u.ext2_sb.s_groups_count) {
- ext2_error (sb, "ext2_get_group_desc",
- "block_group >= groups_count - "
- "block_group = %d, groups_count = %lu",
- block_group, sb->u.ext2_sb.s_groups_count);
-
- return NULL;
- }
-
- group_desc = block_group / EXT2_DESC_PER_BLOCK(sb);
- desc = block_group % EXT2_DESC_PER_BLOCK(sb);
- if (!sb->u.ext2_sb.s_group_desc[group_desc]) {
- ext2_error (sb, "ext2_get_group_desc",
- "Group descriptor not loaded - "
- "block_group = %d, group_desc = %lu, desc = %lu",
- block_group, group_desc, desc);
- return NULL;
- }
-
- gdp = (struct ext2_group_desc *)
- sb->u.ext2_sb.s_group_desc[group_desc]->b_data;
- if (bh)
- *bh = sb->u.ext2_sb.s_group_desc[group_desc];
- return gdp + desc;
-}
-
-/*
- * Read the bitmap for a given block_group, reading into the specified
- * slot in the superblock's bitmap cache.
- *
- * Return >=0 on success or a -ve error code.
+/* got rid of get_group_desc since it can already be found in
+ * ext2_linux_ialloc.c
*/
-static int read_block_bitmap (struct super_block * sb,
+static void read_block_bitmap (struct mount * mp,
unsigned int block_group,
unsigned long bitmap_nr)
{
+ struct ext2_sb_info *sb = VFSTOEXT2(mp)->um_e2fs;
struct ext2_group_desc * gdp;
- struct buffer_head * bh = NULL;
- int retval = -EIO;
+ struct buffer_head * bh;
+ int error;
- gdp = ext2_get_group_desc (sb, block_group, NULL);
- if (!gdp)
- goto error_out;
- retval = 0;
- bh = bread (sb->s_dev, le32_to_cpu(gdp->bg_block_bitmap), sb->s_blocksize);
- if (!bh) {
- ext2_error (sb, "read_block_bitmap",
+ gdp = get_group_desc (mp, block_group, NULL);
+ if ((error = bread (VFSTOEXT2(mp)->um_devvp,
+ fsbtodb(sb, gdp->bg_block_bitmap),sb->s_blocksize, NOCRED, &bh)) != 0)
+ panic ( "read_block_bitmap: "
"Cannot read block bitmap - "
"block_group = %d, block_bitmap = %lu",
block_group, (unsigned long) gdp->bg_block_bitmap);
- retval = -EIO;
- }
- /*
- * On IO error, just leave a zero in the superblock's block pointer for
- * this group. The IO will be retried next time.
- */
-error_out:
- sb->u.ext2_sb.s_block_bitmap_number[bitmap_nr] = block_group;
- sb->u.ext2_sb.s_block_bitmap[bitmap_nr] = bh;
- return retval;
+ sb->s_block_bitmap_number[bitmap_nr] = block_group;
+ sb->s_block_bitmap[bitmap_nr] = bh;
+ LCK_BUF(bh)
}
/*
@@ -115,233 +87,157 @@ error_out:
* 1/ There is one cache per mounted file system.
* 2/ If the file system contains less than EXT2_MAX_GROUP_LOADED groups,
* this function reads the bitmap without maintaining a LRU cache.
- *
- * Return the slot used to store the bitmap, or a -ve error code.
*/
-static int __load_block_bitmap (struct super_block * sb,
- unsigned int block_group)
+static int load__block_bitmap (struct mount * mp,
+ unsigned int block_group)
{
- int i, j, retval = 0;
+ int i, j;
+ struct ext2_sb_info *sb = VFSTOEXT2(mp)->um_e2fs;
unsigned long block_bitmap_number;
struct buffer_head * block_bitmap;
- if (block_group >= sb->u.ext2_sb.s_groups_count)
- ext2_panic (sb, "load_block_bitmap",
+ if (block_group >= sb->s_groups_count)
+ panic ( "load_block_bitmap: "
"block_group >= groups_count - "
"block_group = %d, groups_count = %lu",
- block_group, sb->u.ext2_sb.s_groups_count);
+ block_group, sb->s_groups_count);
- if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED) {
- if (sb->u.ext2_sb.s_block_bitmap[block_group]) {
- if (sb->u.ext2_sb.s_block_bitmap_number[block_group] ==
+ if (sb->s_groups_count <= EXT2_MAX_GROUP_LOADED) {
+ if (sb->s_block_bitmap[block_group]) {
+ if (sb->s_block_bitmap_number[block_group] !=
block_group)
+ panic ( "load_block_bitmap: "
+ "block_group != block_bitmap_number");
+ else
return block_group;
- ext2_error (sb, "__load_block_bitmap",
- "block_group != block_bitmap_number");
+ } else {
+ read_block_bitmap (mp, block_group, block_group);
+ return block_group;
}
- retval = read_block_bitmap (sb, block_group, block_group);
- if (retval < 0)
- return retval;
- return block_group;
}
- for (i = 0; i < sb->u.ext2_sb.s_loaded_block_bitmaps &&
- sb->u.ext2_sb.s_block_bitmap_number[i] != block_group; i++)
+ for (i = 0; i < sb->s_loaded_block_bitmaps &&
+ sb->s_block_bitmap_number[i] != block_group; i++)
;
- if (i < sb->u.ext2_sb.s_loaded_block_bitmaps &&
- sb->u.ext2_sb.s_block_bitmap_number[i] == block_group) {
- block_bitmap_number = sb->u.ext2_sb.s_block_bitmap_number[i];
- block_bitmap = sb->u.ext2_sb.s_block_bitmap[i];
+ if (i < sb->s_loaded_block_bitmaps &&
+ sb->s_block_bitmap_number[i] == block_group) {
+ block_bitmap_number = sb->s_block_bitmap_number[i];
+ block_bitmap = sb->s_block_bitmap[i];
for (j = i; j > 0; j--) {
- sb->u.ext2_sb.s_block_bitmap_number[j] =
- sb->u.ext2_sb.s_block_bitmap_number[j - 1];
- sb->u.ext2_sb.s_block_bitmap[j] =
- sb->u.ext2_sb.s_block_bitmap[j - 1];
+ sb->s_block_bitmap_number[j] =
+ sb->s_block_bitmap_number[j - 1];
+ sb->s_block_bitmap[j] =
+ sb->s_block_bitmap[j - 1];
}
- sb->u.ext2_sb.s_block_bitmap_number[0] = block_bitmap_number;
- sb->u.ext2_sb.s_block_bitmap[0] = block_bitmap;
-
- /*
- * There's still one special case here --- if block_bitmap == 0
- * then our last attempt to read the bitmap failed and we have
- * just ended up caching that failure. Try again to read it.
- */
- if (!block_bitmap)
- retval = read_block_bitmap (sb, block_group, 0);
+ sb->s_block_bitmap_number[0] = block_bitmap_number;
+ sb->s_block_bitmap[0] = block_bitmap;
} else {
- if (sb->u.ext2_sb.s_loaded_block_bitmaps < EXT2_MAX_GROUP_LOADED)
- sb->u.ext2_sb.s_loaded_block_bitmaps++;
+ if (sb->s_loaded_block_bitmaps < EXT2_MAX_GROUP_LOADED)
+ sb->s_loaded_block_bitmaps++;
else
- brelse (sb->u.ext2_sb.s_block_bitmap[EXT2_MAX_GROUP_LOADED - 1]);
- for (j = sb->u.ext2_sb.s_loaded_block_bitmaps - 1; j > 0; j--) {
- sb->u.ext2_sb.s_block_bitmap_number[j] =
- sb->u.ext2_sb.s_block_bitmap_number[j - 1];
- sb->u.ext2_sb.s_block_bitmap[j] =
- sb->u.ext2_sb.s_block_bitmap[j - 1];
+ ULCK_BUF(sb->s_block_bitmap[EXT2_MAX_GROUP_LOADED - 1])
+
+ for (j = sb->s_loaded_block_bitmaps - 1; j > 0; j--) {
+ sb->s_block_bitmap_number[j] =
+ sb->s_block_bitmap_number[j - 1];
+ sb->s_block_bitmap[j] =
+ sb->s_block_bitmap[j - 1];
}
- retval = read_block_bitmap (sb, block_group, 0);
+ read_block_bitmap (mp, block_group, 0);
}
- return retval;
+ return 0;
}
-/*
- * Load the block bitmap for a given block group. First of all do a couple
- * of fast lookups for common cases and then pass the request onto the guts
- * of the bitmap loader.
- *
- * Return the slot number of the group in the superblock bitmap cache's on
- * success, or a -ve error code.
- *
- * There is still one inconsistency here --- if the number of groups in this
- * filesystems is <= EXT2_MAX_GROUP_LOADED, then we have no way of
- * differentiating between a group for which we have never performed a bitmap
- * IO request, and a group for which the last bitmap read request failed.
- */
-static inline int load_block_bitmap (struct super_block * sb,
- unsigned int block_group)
+static __inline int load_block_bitmap (struct mount * mp,
+ unsigned int block_group)
{
- int slot;
-
- /*
- * Do the lookup for the slot. First of all, check if we're asking
- * for the same slot as last time, and did we succeed that last time?
- */
- if (sb->u.ext2_sb.s_loaded_block_bitmaps > 0 &&
- sb->u.ext2_sb.s_block_bitmap_number[0] == block_group &&
- sb->u.ext2_sb.s_block_bitmap[block_group]) {
+ struct ext2_sb_info *sb = VFSTOEXT2(mp)->um_e2fs;
+ if (sb->s_loaded_block_bitmaps > 0 &&
+ sb->s_block_bitmap_number[0] == block_group)
return 0;
- }
- /*
- * Or can we do a fast lookup based on a loaded group on a filesystem
- * small enough to be mapped directly into the superblock?
- */
- else if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED &&
- sb->u.ext2_sb.s_block_bitmap_number[block_group] == block_group &&
- sb->u.ext2_sb.s_block_bitmap[block_group]) {
- slot = block_group;
- }
- /*
- * If not, then do a full lookup for this block group.
- */
- else {
- slot = __load_block_bitmap (sb, block_group);
- }
-
- /*
- * <0 means we just got an error
- */
- if (slot < 0)
- return slot;
- /*
- * If it's a valid slot, we may still have cached a previous IO error,
- * in which case the bh in the superblock cache will be zero.
- */
- if (!sb->u.ext2_sb.s_block_bitmap[slot])
- return -EIO;
-
- /*
- * Must have been read in OK to get this far.
- */
- return slot;
+ if (sb->s_groups_count <= EXT2_MAX_GROUP_LOADED &&
+ sb->s_block_bitmap_number[block_group] == block_group &&
+ sb->s_block_bitmap[block_group])
+ return block_group;
+
+ return load__block_bitmap (mp, block_group);
}
-void ext2_free_blocks (const struct inode * inode, unsigned long block,
+void ext2_free_blocks (struct mount * mp, unsigned long block,
unsigned long count)
{
+ struct ext2_sb_info *sb = VFSTOEXT2(mp)->um_e2fs;
struct buffer_head * bh;
struct buffer_head * bh2;
unsigned long block_group;
unsigned long bit;
unsigned long i;
int bitmap_nr;
- unsigned long overflow;
- struct super_block * sb;
struct ext2_group_desc * gdp;
- struct ext2_super_block * es;
+ struct ext2_super_block * es = sb->s_es;
- sb = inode->i_sb;
if (!sb) {
- printk ("ext2_free_blocks: nonexistent device");
+ printf ("ext2_free_blocks: nonexistent device");
return;
}
- lock_super (sb);
- es = sb->u.ext2_sb.s_es;
- if (block < le32_to_cpu(es->s_first_data_block) ||
- (block + count) > le32_to_cpu(es->s_blocks_count)) {
- ext2_error (sb, "ext2_free_blocks",
+ lock_super (VFSTOEXT2(mp)->um_devvp);
+ if (block < es->s_first_data_block ||
+ (block + count) > es->s_blocks_count) {
+ printf ( "ext2_free_blocks: "
"Freeing blocks not in datazone - "
"block = %lu, count = %lu", block, count);
- goto error_return;
+ unlock_super (VFSTOEXT2(mp)->um_devvp);
+ return;
}
- ext2_debug ("freeing block %lu\n", block);
+ ext2_debug ("freeing blocks %lu to %lu\n", block, block+count-1);
-do_more:
- overflow = 0;
- block_group = (block - le32_to_cpu(es->s_first_data_block)) /
- EXT2_BLOCKS_PER_GROUP(sb);
- bit = (block - le32_to_cpu(es->s_first_data_block)) %
+ block_group = (block - es->s_first_data_block) /
EXT2_BLOCKS_PER_GROUP(sb);
- /*
- * Check to see if we are freeing blocks across a group
- * boundary.
- */
- if (bit + count > EXT2_BLOCKS_PER_GROUP(sb)) {
- overflow = bit + count - EXT2_BLOCKS_PER_GROUP(sb);
- count -= overflow;
- }
- bitmap_nr = load_block_bitmap (sb, block_group);
- if (bitmap_nr < 0)
- goto error_return;
-
- bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
- gdp = ext2_get_group_desc (sb, block_group, &bh2);
- if (!gdp)
- goto error_return;
-
- if (test_opt (sb, CHECK_STRICT) &&
- (in_range (le32_to_cpu(gdp->bg_block_bitmap), block, count) ||
- in_range (le32_to_cpu(gdp->bg_inode_bitmap), block, count) ||
- in_range (block, le32_to_cpu(gdp->bg_inode_table),
- sb->u.ext2_sb.s_itb_per_group) ||
- in_range (block + count - 1, le32_to_cpu(gdp->bg_inode_table),
- sb->u.ext2_sb.s_itb_per_group)))
- ext2_panic (sb, "ext2_free_blocks",
+ bit = (block - es->s_first_data_block) % EXT2_BLOCKS_PER_GROUP(sb);
+ if (bit + count > EXT2_BLOCKS_PER_GROUP(sb))
+ panic ( "ext2_free_blocks: "
+ "Freeing blocks across group boundary - "
+ "Block = %lu, count = %lu",
+ block, count);
+ bitmap_nr = load_block_bitmap (mp, block_group);
+ bh = sb->s_block_bitmap[bitmap_nr];
+ gdp = get_group_desc (mp, block_group, &bh2);
+
+ if (/* test_opt (sb, CHECK_STRICT) && assume always strict ! */
+ (in_range (gdp->bg_block_bitmap, block, count) ||
+ in_range (gdp->bg_inode_bitmap, block, count) ||
+ in_range (block, gdp->bg_inode_table,
+ sb->s_itb_per_group) ||
+ in_range (block + count - 1, gdp->bg_inode_table,
+ sb->s_itb_per_group)))
+ panic ( "ext2_free_blocks: "
"Freeing blocks in system zones - "
"Block = %lu, count = %lu",
block, count);
for (i = 0; i < count; i++) {
- if (!ext2_clear_bit (bit + i, bh->b_data))
- ext2_warning (sb, "ext2_free_blocks",
+ if (!clear_bit (bit + i, bh->b_data))
+ printf ("ext2_free_blocks: "
"bit already cleared for block %lu",
block);
else {
- DQUOT_FREE_BLOCK(sb, inode, 1);
- gdp->bg_free_blocks_count =
- cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count)+1);
- es->s_free_blocks_count =
- cpu_to_le32(le32_to_cpu(es->s_free_blocks_count)+1);
+ gdp->bg_free_blocks_count++;
+ es->s_free_blocks_count++;
}
}
-
- mark_buffer_dirty(bh2, 1);
- mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
- mark_buffer_dirty(bh, 1);
+ mark_buffer_dirty(bh2);
+ mark_buffer_dirty(bh);
+/****
if (sb->s_flags & MS_SYNCHRONOUS) {
ll_rw_block (WRITE, 1, &bh);
wait_on_buffer (bh);
}
- if (overflow) {
- block += count;
- count = overflow;
- goto do_more;
- }
+****/
sb->s_dirt = 1;
-error_return:
- unlock_super (sb);
+ unlock_super (VFSTOEXT2(mp)->um_devvp);
return;
}
@@ -352,67 +248,50 @@ error_return:
* each block group the search first looks for an entire free byte in the block
* bitmap, and then for any free bit if that fails.
*/
-int ext2_new_block (const struct inode * inode, unsigned long goal,
- u32 * prealloc_count, u32 * prealloc_block, int * err)
+int ext2_new_block (struct mount * mp, unsigned long goal,
+ u_int32_t * prealloc_count,
+ u_int32_t * prealloc_block)
{
+ struct ext2_sb_info *sb = VFSTOEXT2(mp)->um_e2fs;
struct buffer_head * bh;
struct buffer_head * bh2;
char * p, * r;
int i, j, k, tmp;
int bitmap_nr;
- struct super_block * sb;
struct ext2_group_desc * gdp;
- struct ext2_super_block * es;
+ struct ext2_super_block * es = sb->s_es;
+
#ifdef EXT2FS_DEBUG
static int goal_hits = 0, goal_attempts = 0;
#endif
- *err = -ENOSPC;
- sb = inode->i_sb;
if (!sb) {
- printk ("ext2_new_block: nonexistent device");
- return 0;
- }
-
- lock_super (sb);
- es = sb->u.ext2_sb.s_es;
- if (le32_to_cpu(es->s_free_blocks_count) <= le32_to_cpu(es->s_r_blocks_count) &&
- ((sb->u.ext2_sb.s_resuid != current->fsuid) &&
- (sb->u.ext2_sb.s_resgid == 0 ||
- !in_group_p (sb->u.ext2_sb.s_resgid)) &&
- !capable(CAP_SYS_RESOURCE))) {
- unlock_super (sb);
+ printf ("ext2_new_block: nonexistent device");
return 0;
}
+ lock_super (VFSTOEXT2(mp)->um_devvp);
- ext2_debug ("goal=%lu.\n", goal);
+ ext2_debug ("goal=%lu.\n", goal);
repeat:
/*
* First, test whether the goal block is free.
*/
- if (goal < le32_to_cpu(es->s_first_data_block) ||
- goal >= le32_to_cpu(es->s_blocks_count))
- goal = le32_to_cpu(es->s_first_data_block);
- i = (goal - le32_to_cpu(es->s_first_data_block)) / EXT2_BLOCKS_PER_GROUP(sb);
- gdp = ext2_get_group_desc (sb, i, &bh2);
- if (!gdp)
- goto io_error;
-
- if (le16_to_cpu(gdp->bg_free_blocks_count) > 0) {
- j = ((goal - le32_to_cpu(es->s_first_data_block)) % EXT2_BLOCKS_PER_GROUP(sb));
+ if (goal < es->s_first_data_block || goal >= es->s_blocks_count)
+ goal = es->s_first_data_block;
+ i = (goal - es->s_first_data_block) / EXT2_BLOCKS_PER_GROUP(sb);
+ gdp = get_group_desc (mp, i, &bh2);
+ if (gdp->bg_free_blocks_count > 0) {
+ j = ((goal - es->s_first_data_block) % EXT2_BLOCKS_PER_GROUP(sb));
#ifdef EXT2FS_DEBUG
if (j)
goal_attempts++;
#endif
- bitmap_nr = load_block_bitmap (sb, i);
- if (bitmap_nr < 0)
- goto io_error;
-
- bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
+ bitmap_nr = load_block_bitmap (mp, i);
+ bh = sb->s_block_bitmap[bitmap_nr];
- ext2_debug ("goal is at %d:%d.\n", i, j);
+ ext2_debug ("goal is at %d:%d.\n", i, j);
- if (!ext2_test_bit(j, bh->b_data)) {
+ if (!test_bit(j, bh->b_data)) {
#ifdef EXT2FS_DEBUG
goal_hits++;
ext2_debug ("goal bit allocated.\n");
@@ -429,7 +308,7 @@ repeat:
* next 64-bit boundary is simple..
*/
int end_goal = (j + 63) & ~63;
- j = ext2_find_next_zero_bit(bh->b_data, end_goal, j);
+ j = find_next_zero_bit(bh->b_data, end_goal, j);
if (j < end_goal)
goto got_block;
}
@@ -452,8 +331,7 @@ repeat:
j = k;
goto search_back;
}
-
- k = ext2_find_next_zero_bit ((unsigned long *) bh->b_data,
+ k = find_next_zero_bit ((unsigned long *) bh->b_data,
EXT2_BLOCKS_PER_GROUP(sb),
j);
if (k < EXT2_BLOCKS_PER_GROUP(sb)) {
@@ -462,45 +340,38 @@ repeat:
}
}
- ext2_debug ("Bit not found in block group %d.\n", i);
+ ext2_debug ("Bit not found in block group %d.\n", i);
/*
* Now search the rest of the groups. We assume that
* i and gdp correctly point to the last group visited.
*/
- for (k = 0; k < sb->u.ext2_sb.s_groups_count; k++) {
+ for (k = 0; k < sb->s_groups_count; k++) {
i++;
- if (i >= sb->u.ext2_sb.s_groups_count)
+ if (i >= sb->s_groups_count)
i = 0;
- gdp = ext2_get_group_desc (sb, i, &bh2);
- if (!gdp) {
- *err = -EIO;
- unlock_super (sb);
- return 0;
- }
- if (le16_to_cpu(gdp->bg_free_blocks_count) > 0)
+ gdp = get_group_desc (mp, i, &bh2);
+ if (gdp->bg_free_blocks_count > 0)
break;
}
- if (k >= sb->u.ext2_sb.s_groups_count) {
- unlock_super (sb);
+ if (k >= sb->s_groups_count) {
+ unlock_super (VFSTOEXT2(mp)->um_devvp);
return 0;
}
- bitmap_nr = load_block_bitmap (sb, i);
- if (bitmap_nr < 0)
- goto io_error;
-
- bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
+ bitmap_nr = load_block_bitmap (mp, i);
+ bh = sb->s_block_bitmap[bitmap_nr];
r = memscan(bh->b_data, 0, EXT2_BLOCKS_PER_GROUP(sb) >> 3);
j = (r - bh->b_data) << 3;
+
if (j < EXT2_BLOCKS_PER_GROUP(sb))
goto search_back;
else
- j = ext2_find_first_zero_bit ((unsigned long *) bh->b_data,
+ j = find_first_zero_bit ((unsigned long *) bh->b_data,
EXT2_BLOCKS_PER_GROUP(sb));
if (j >= EXT2_BLOCKS_PER_GROUP(sb)) {
- ext2_error (sb, "ext2_new_block",
- "Free blocks count corrupted for block group %d", i);
- unlock_super (sb);
+ printf ( "ext2_new_block: "
+ "Free blocks count corrupted for block group %d", i);
+ unlock_super (VFSTOEXT2(mp)->um_devvp);
return 0;
}
@@ -510,35 +381,25 @@ search_back:
* bitmap. Now search backwards up to 7 bits to find the
* start of this group of free blocks.
*/
- for (k = 0; k < 7 && j > 0 && !ext2_test_bit (j - 1, bh->b_data); k++, j--);
+ for (k = 0; k < 7 && j > 0 && !test_bit (j - 1, bh->b_data); k++, j--);
got_block:
ext2_debug ("using block group %d(%d)\n", i, gdp->bg_free_blocks_count);
- /*
- * Check quota for allocation of this block.
- */
- if(DQUOT_ALLOC_BLOCK(sb, inode, 1)) {
- unlock_super(sb);
- *err = -EDQUOT;
- return 0;
- }
+ tmp = j + i * EXT2_BLOCKS_PER_GROUP(sb) + es->s_first_data_block;
- tmp = j + i * EXT2_BLOCKS_PER_GROUP(sb) + le32_to_cpu(es->s_first_data_block);
-
- if (test_opt (sb, CHECK_STRICT) &&
- (tmp == le32_to_cpu(gdp->bg_block_bitmap) ||
- tmp == le32_to_cpu(gdp->bg_inode_bitmap) ||
- in_range (tmp, le32_to_cpu(gdp->bg_inode_table), sb->u.ext2_sb.s_itb_per_group)))
- ext2_panic (sb, "ext2_new_block",
+ if (/* test_opt (sb, CHECK_STRICT) && we are always strict. */
+ (tmp == gdp->bg_block_bitmap ||
+ tmp == gdp->bg_inode_bitmap ||
+ in_range (tmp, gdp->bg_inode_table, sb->s_itb_per_group)))
+ panic ( "ext2_new_block: "
"Allocating block in system zone - "
- "block = %u", tmp);
+ "%dth block = %u in group %u", j, tmp, i);
- if (ext2_set_bit (j, bh->b_data)) {
- ext2_warning (sb, "ext2_new_block",
- "bit already set for block %d", j);
- DQUOT_FREE_BLOCK(sb, inode, 1);
+ if (set_bit (j, bh->b_data)) {
+ printf ( "ext2_new_block: "
+ "bit already set for block %d", j);
goto repeat;
}
@@ -549,73 +410,53 @@ got_block:
*/
#ifdef EXT2_PREALLOCATE
if (prealloc_block) {
- int prealloc_goal;
-
- prealloc_goal = es->s_prealloc_blocks ?
- es->s_prealloc_blocks : EXT2_DEFAULT_PREALLOC_BLOCKS;
-
*prealloc_count = 0;
*prealloc_block = tmp + 1;
for (k = 1;
- k < prealloc_goal && (j + k) < EXT2_BLOCKS_PER_GROUP(sb);
- k++) {
- if (DQUOT_PREALLOC_BLOCK(sb, inode, 1))
+ k < 8 && (j + k) < EXT2_BLOCKS_PER_GROUP(sb); k++) {
+ if (set_bit (j + k, bh->b_data))
break;
- if (ext2_set_bit (j + k, bh->b_data)) {
- DQUOT_FREE_BLOCK(sb, inode, 1);
- break;
- }
(*prealloc_count)++;
}
- gdp->bg_free_blocks_count =
- cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count) -
- *prealloc_count);
- es->s_free_blocks_count =
- cpu_to_le32(le32_to_cpu(es->s_free_blocks_count) -
- *prealloc_count);
+ gdp->bg_free_blocks_count -= *prealloc_count;
+ es->s_free_blocks_count -= *prealloc_count;
ext2_debug ("Preallocated a further %lu bits.\n",
- *prealloc_count);
+ *prealloc_count);
}
#endif
j = tmp;
- mark_buffer_dirty(bh, 1);
+ mark_buffer_dirty(bh);
+/****
if (sb->s_flags & MS_SYNCHRONOUS) {
ll_rw_block (WRITE, 1, &bh);
wait_on_buffer (bh);
}
-
- if (j >= le32_to_cpu(es->s_blocks_count)) {
- ext2_error (sb, "ext2_new_block",
- "block(%d) >= blocks count(%d) - "
- "block_group = %d, es == %p ",j,
- le32_to_cpu(es->s_blocks_count), i, es);
- unlock_super (sb);
+****/
+ if (j >= es->s_blocks_count) {
+ printf ( "ext2_new_block: "
+ "block >= blocks count - "
+ "block_group = %d, block=%d", i, j);
+ unlock_super (VFSTOEXT2(mp)->um_devvp);
return 0;
}
ext2_debug ("allocating block %d. "
"Goal hits %d of %d.\n", j, goal_hits, goal_attempts);
- gdp->bg_free_blocks_count = cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count) - 1);
- mark_buffer_dirty(bh2, 1);
- es->s_free_blocks_count = cpu_to_le32(le32_to_cpu(es->s_free_blocks_count) - 1);
- mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
+ gdp->bg_free_blocks_count--;
+ mark_buffer_dirty(bh2);
+ es->s_free_blocks_count--;
sb->s_dirt = 1;
- unlock_super (sb);
- *err = 0;
+ unlock_super (VFSTOEXT2(mp)->um_devvp);
return j;
-
-io_error:
- *err = -EIO;
- unlock_super (sb);
- return 0;
-
}
-unsigned long ext2_count_free_blocks (struct super_block * sb)
+#ifdef unused
+static unsigned long ext2_count_free_blocks (struct mount * mp)
{
+ struct ext2_sb_info *sb = VFSTOEXT2(mp)->um_e2fs;
#ifdef EXT2FS_DEBUG
struct ext2_super_block * es;
unsigned long desc_count, bitmap_count, x;
@@ -623,40 +464,36 @@ unsigned long ext2_count_free_blocks (struct super_block * sb)
struct ext2_group_desc * gdp;
int i;
- lock_super (sb);
- es = sb->u.ext2_sb.s_es;
+ lock_super (VFSTOEXT2(mp)->um_devvp);
+ es = sb->s_es;
desc_count = 0;
bitmap_count = 0;
gdp = NULL;
- for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
- gdp = ext2_get_group_desc (sb, i, NULL);
- if (!gdp)
- continue;
- desc_count += le16_to_cpu(gdp->bg_free_blocks_count);
- bitmap_nr = load_block_bitmap (sb, i);
- if (bitmap_nr < 0)
- continue;
-
- x = ext2_count_free (sb->u.ext2_sb.s_block_bitmap[bitmap_nr],
+ for (i = 0; i < sb->s_groups_count; i++) {
+ gdp = get_group_desc (mp, i, NULL);
+ desc_count += gdp->bg_free_blocks_count;
+ bitmap_nr = load_block_bitmap (mp, i);
+ x = ext2_count_free (sb->s_block_bitmap[bitmap_nr],
sb->s_blocksize);
- printk ("group %d: stored = %d, counted = %lu\n",
- i, le16_to_cpu(gdp->bg_free_blocks_count), x);
+ ext2_debug ("group %d: stored = %d, counted = %lu\n",
+ i, gdp->bg_free_blocks_count, x);
bitmap_count += x;
}
- printk("ext2_count_free_blocks: stored = %lu, computed = %lu, %lu\n",
- le32_to_cpu(es->s_free_blocks_count), desc_count, bitmap_count);
- unlock_super (sb);
+ ext2_debug( "stored = %lu, computed = %lu, %lu\n",
+ es->s_free_blocks_count, desc_count, bitmap_count);
+ unlock_super (VFSTOEXT2(mp)->um_devvp);
return bitmap_count;
#else
- return le32_to_cpu(sb->u.ext2_sb.s_es->s_free_blocks_count);
+ return sb->s_es->s_free_blocks_count;
#endif
}
+#endif /* unused */
-static inline int block_in_use (unsigned long block,
- struct super_block * sb,
- unsigned char * map)
+static __inline int block_in_use (unsigned long block,
+ struct ext2_sb_info * sb,
+ unsigned char * map)
{
- return ext2_test_bit ((block - le32_to_cpu(sb->u.ext2_sb.s_es->s_first_data_block)) %
+ return test_bit ((block - sb->s_es->s_first_data_block) %
EXT2_BLOCKS_PER_GROUP(sb), map);
}
@@ -679,8 +516,10 @@ int ext2_group_sparse(int group)
test_root(group, 7));
}
-void ext2_check_blocks_bitmap (struct super_block * sb)
+#ifdef unused
+static void ext2_check_blocks_bitmap (struct mount * mp)
{
+ struct ext2_sb_info *sb = VFSTOEXT2(mp)->um_e2fs;
struct buffer_head * bh;
struct ext2_super_block * es;
unsigned long desc_count, bitmap_count, x;
@@ -689,68 +528,83 @@ void ext2_check_blocks_bitmap (struct super_block * sb)
struct ext2_group_desc * gdp;
int i, j;
- lock_super (sb);
- es = sb->u.ext2_sb.s_es;
+ lock_super (VFSTOEXT2(mp)->um_devvp);
+ es = sb->s_es;
desc_count = 0;
bitmap_count = 0;
gdp = NULL;
- desc_blocks = (sb->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) /
+ desc_blocks = (sb->s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) /
EXT2_DESC_PER_BLOCK(sb);
- for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
- gdp = ext2_get_group_desc (sb, i, NULL);
- if (!gdp)
- continue;
- desc_count += le16_to_cpu(gdp->bg_free_blocks_count);
- bitmap_nr = load_block_bitmap (sb, i);
- if (bitmap_nr < 0)
- continue;
-
- bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
-
- if (!(sb->u.ext2_sb.s_feature_ro_compat &
+ for (i = 0; i < sb->s_groups_count; i++) {
+ gdp = get_group_desc (mp, i, NULL);
+ desc_count += gdp->bg_free_blocks_count;
+ bitmap_nr = load_block_bitmap (mp, i);
+ bh = sb->s_block_bitmap[bitmap_nr];
+
+ if (!(es->s_feature_ro_compat &
EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) ||
ext2_group_sparse(i)) {
- if (!ext2_test_bit (0, bh->b_data))
- ext2_error (sb, "ext2_check_blocks_bitmap",
+ if (!test_bit (0, bh->b_data))
+ printf ("ext2_check_blocks_bitmap: "
"Superblock in group %d "
"is marked free", i);
for (j = 0; j < desc_blocks; j++)
- if (!ext2_test_bit (j + 1, bh->b_data))
- ext2_error (sb,
- "ext2_check_blocks_bitmap",
+ if (!test_bit (j + 1, bh->b_data))
+ printf ("ext2_check_blocks_bitmap: "
"Descriptor block #%d in group "
"%d is marked free", j, i);
}
- if (!block_in_use (le32_to_cpu(gdp->bg_block_bitmap), sb, bh->b_data))
- ext2_error (sb, "ext2_check_blocks_bitmap",
+ if (!block_in_use (gdp->bg_block_bitmap, sb, bh->b_data))
+ printf ("ext2_check_blocks_bitmap: "
"Block bitmap for group %d is marked free",
i);
- if (!block_in_use (le32_to_cpu(gdp->bg_inode_bitmap), sb, bh->b_data))
- ext2_error (sb, "ext2_check_blocks_bitmap",
+ if (!block_in_use (gdp->bg_inode_bitmap, sb, bh->b_data))
+ printf ("ext2_check_blocks_bitmap: "
"Inode bitmap for group %d is marked free",
i);
- for (j = 0; j < sb->u.ext2_sb.s_itb_per_group; j++)
- if (!block_in_use (le32_to_cpu(gdp->bg_inode_table) + j, sb, bh->b_data))
- ext2_error (sb, "ext2_check_blocks_bitmap",
+ for (j = 0; j < sb->s_itb_per_group; j++)
+ if (!block_in_use (gdp->bg_inode_table + j, sb, bh->b_data))
+ printf ("ext2_check_blocks_bitmap: "
"Block #%d of the inode table in "
"group %d is marked free", j, i);
x = ext2_count_free (bh, sb->s_blocksize);
- if (le16_to_cpu(gdp->bg_free_blocks_count) != x)
- ext2_error (sb, "ext2_check_blocks_bitmap",
+ if (gdp->bg_free_blocks_count != x)
+ printf ("ext2_check_blocks_bitmap: "
"Wrong free blocks count for group %d, "
"stored = %d, counted = %lu", i,
- le16_to_cpu(gdp->bg_free_blocks_count), x);
+ gdp->bg_free_blocks_count, x);
bitmap_count += x;
}
- if (le32_to_cpu(es->s_free_blocks_count) != bitmap_count)
- ext2_error (sb, "ext2_check_blocks_bitmap",
+ if (es->s_free_blocks_count != bitmap_count)
+ printf ("ext2_check_blocks_bitmap: "
"Wrong free blocks count in super block, "
"stored = %lu, counted = %lu",
- (unsigned long) le32_to_cpu(es->s_free_blocks_count), bitmap_count);
- unlock_super (sb);
+ (unsigned long) es->s_free_blocks_count, bitmap_count);
+ unlock_super (VFSTOEXT2(mp)->um_devvp);
+}
+#endif /* unused */
+
+/*
+ * this function is taken from
+ * linux/fs/ext2/bitmap.c
+ */
+
+static int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0};
+
+unsigned long ext2_count_free (struct buffer_head * map, unsigned int numchars)
+{
+ unsigned int i;
+ unsigned long sum = 0;
+
+ if (!map)
+ return (0);
+ for (i = 0; i < numchars; i++)
+ sum += nibblemap[map->b_data[i] & 0xf] +
+ nibblemap[(map->b_data[i] >> 4) & 0xf];
+ return (sum);
}
diff --git a/sys/gnu/ext2fs/ext2_linux_ialloc.c b/sys/gnu/ext2fs/ext2_linux_ialloc.c
index d519f60..64d96b2 100644
--- a/sys/gnu/ext2fs/ext2_linux_ialloc.c
+++ b/sys/gnu/ext2fs/ext2_linux_ialloc.c
@@ -1,4 +1,12 @@
/*
+ * modified for Lites 1.1
+ *
+ * Aug 1995, Godmar Back (gback@cs.utah.edu)
+ * University of Utah, Department of Computer Science
+ *
+ * $FreeBSD$
+ */
+/*
* linux/fs/ext2/ialloc.c
*
* Copyright (C) 1992, 1993, 1994, 1995
@@ -8,17 +16,6 @@
*
* BSD ufs-inspired inode and directory allocation by
* Stephen Tweedie (sct@dcs.ed.ac.uk), 1993
- * Big-endian to little-endian byte-swapping/bitmaps by
- * David S. Miller (davem@caip.rutgers.edu), 1995
- */
-
-#include <linux/fs.h>
-#include <linux/locks.h>
-#include <linux/quotaops.h>
-
-
-/*
- * ialloc.c contains the inodes allocation and deallocation routines
*/
/*
@@ -32,42 +29,90 @@
* when a file system is mounted (see ext2_read_super).
*/
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bio.h>
+#include <sys/buf.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+
+#include <gnu/ext2fs/inode.h>
+#include <gnu/ext2fs/ext2_mount.h>
+#include <gnu/ext2fs/ext2_extern.h>
+#include <gnu/ext2fs/ext2_fs.h>
+#include <gnu/ext2fs/ext2_fs_sb.h>
+#include <gnu/ext2fs/fs.h>
+#include <sys/stat.h>
+
+#ifdef __i386__
+#include <gnu/ext2fs/i386-bitops.h>
+#elif __alpha__
+#include <gnu/ext2fs/alpha-bitops.h>
+#else
+#error please provide bit operation functions
+#endif
-/*
- * Read the inode allocation bitmap for a given block_group, reading
- * into the specified slot in the superblock's bitmap cache.
- *
- * Return >=0 on success or a -ve error code.
+/* this is supposed to mark a buffer dirty on ready for delayed writing
*/
-static int read_inode_bitmap (struct super_block * sb,
+void mark_buffer_dirty(struct buf *bh)
+{
+ int s;
+
+ s = splbio();
+ bh->b_flags |= B_DIRTY;
+ splx(s);
+}
+
+struct ext2_group_desc * get_group_desc (struct mount * mp,
+ unsigned int block_group,
+ struct buffer_head ** bh)
+{
+ struct ext2_sb_info *sb = VFSTOEXT2(mp)->um_e2fs;
+ unsigned long group_desc;
+ unsigned long desc;
+ struct ext2_group_desc * gdp;
+
+ if (block_group >= sb->s_groups_count)
+ panic ("get_group_desc: "
+ "block_group >= groups_count - "
+ "block_group = %d, groups_count = %lu",
+ block_group, sb->s_groups_count);
+
+ group_desc = block_group / EXT2_DESC_PER_BLOCK(sb);
+ desc = block_group % EXT2_DESC_PER_BLOCK(sb);
+ if (!sb->s_group_desc[group_desc])
+ panic ( "get_group_desc:"
+ "Group descriptor not loaded - "
+ "block_group = %d, group_desc = %lu, desc = %lu",
+ block_group, group_desc, desc);
+ gdp = (struct ext2_group_desc *)
+ sb->s_group_desc[group_desc]->b_data;
+ if (bh)
+ *bh = sb->s_group_desc[group_desc];
+ return gdp + desc;
+}
+
+static void read_inode_bitmap (struct mount * mp,
unsigned long block_group,
unsigned int bitmap_nr)
{
+ struct ext2_sb_info *sb = VFSTOEXT2(mp)->um_e2fs;
struct ext2_group_desc * gdp;
- struct buffer_head * bh = NULL;
- int retval = 0;
-
- gdp = ext2_get_group_desc (sb, block_group, NULL);
- if (!gdp) {
- retval = -EIO;
- goto error_out;
- }
- bh = bread (sb->s_dev, le32_to_cpu(gdp->bg_inode_bitmap), sb->s_blocksize);
- if (!bh) {
- ext2_error (sb, "read_inode_bitmap",
+ struct buffer_head * bh;
+ int error;
+
+ gdp = get_group_desc (mp, block_group, NULL);
+ if ((error = bread (VFSTOEXT2(mp)->um_devvp,
+ fsbtodb(sb, gdp->bg_inode_bitmap),
+ sb->s_blocksize,
+ NOCRED, &bh)) != 0)
+ panic ( "read_inode_bitmap:"
"Cannot read inode bitmap - "
"block_group = %lu, inode_bitmap = %lu",
block_group, (unsigned long) gdp->bg_inode_bitmap);
- retval = -EIO;
- }
- /*
- * On IO error, just leave a zero in the superblock's block pointer for
- * this group. The IO will be retried next time.
- */
-error_out:
- sb->u.ext2_sb.s_inode_bitmap_number[bitmap_nr] = block_group;
- sb->u.ext2_sb.s_inode_bitmap[bitmap_nr] = bh;
- return retval;
+ sb->s_inode_bitmap_number[bitmap_nr] = block_group;
+ sb->s_inode_bitmap[bitmap_nr] = bh;
+ LCK_BUF(bh)
}
/*
@@ -80,103 +125,73 @@ error_out:
* 1/ There is one cache per mounted file system.
* 2/ If the file system contains less than EXT2_MAX_GROUP_LOADED groups,
* this function reads the bitmap without maintaining a LRU cache.
- *
- * Return the slot used to store the bitmap, or a -ve error code.
*/
-static int load_inode_bitmap (struct super_block * sb,
+static int load_inode_bitmap (struct mount * mp,
unsigned int block_group)
{
- int i, j, retval = 0;
+ struct ext2_sb_info *sb = VFSTOEXT2(mp)->um_e2fs;
+ int i, j;
unsigned long inode_bitmap_number;
struct buffer_head * inode_bitmap;
- if (block_group >= sb->u.ext2_sb.s_groups_count)
- ext2_panic (sb, "load_inode_bitmap",
+ if (block_group >= sb->s_groups_count)
+ panic ("load_inode_bitmap:"
"block_group >= groups_count - "
"block_group = %d, groups_count = %lu",
- block_group, sb->u.ext2_sb.s_groups_count);
- if (sb->u.ext2_sb.s_loaded_inode_bitmaps > 0 &&
- sb->u.ext2_sb.s_inode_bitmap_number[0] == block_group &&
- sb->u.ext2_sb.s_inode_bitmap[0] != NULL)
+ block_group, sb->s_groups_count);
+ if (sb->s_loaded_inode_bitmaps > 0 &&
+ sb->s_inode_bitmap_number[0] == block_group)
return 0;
- if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED) {
- if (sb->u.ext2_sb.s_inode_bitmap[block_group]) {
- if (sb->u.ext2_sb.s_inode_bitmap_number[block_group] != block_group)
- ext2_panic (sb, "load_inode_bitmap",
- "block_group != inode_bitmap_number");
+ if (sb->s_groups_count <= EXT2_MAX_GROUP_LOADED) {
+ if (sb->s_inode_bitmap[block_group]) {
+ if (sb->s_inode_bitmap_number[block_group] !=
+ block_group)
+ panic ( "load_inode_bitmap:"
+ "block_group != inode_bitmap_number");
else
return block_group;
} else {
- retval = read_inode_bitmap (sb, block_group,
- block_group);
- if (retval < 0)
- return retval;
+ read_inode_bitmap (mp, block_group, block_group);
return block_group;
}
}
- for (i = 0; i < sb->u.ext2_sb.s_loaded_inode_bitmaps &&
- sb->u.ext2_sb.s_inode_bitmap_number[i] != block_group;
+ for (i = 0; i < sb->s_loaded_inode_bitmaps &&
+ sb->s_inode_bitmap_number[i] != block_group;
i++)
;
- if (i < sb->u.ext2_sb.s_loaded_inode_bitmaps &&
- sb->u.ext2_sb.s_inode_bitmap_number[i] == block_group) {
- inode_bitmap_number = sb->u.ext2_sb.s_inode_bitmap_number[i];
- inode_bitmap = sb->u.ext2_sb.s_inode_bitmap[i];
+ if (i < sb->s_loaded_inode_bitmaps &&
+ sb->s_inode_bitmap_number[i] == block_group) {
+ inode_bitmap_number = sb->s_inode_bitmap_number[i];
+ inode_bitmap = sb->s_inode_bitmap[i];
for (j = i; j > 0; j--) {
- sb->u.ext2_sb.s_inode_bitmap_number[j] =
- sb->u.ext2_sb.s_inode_bitmap_number[j - 1];
- sb->u.ext2_sb.s_inode_bitmap[j] =
- sb->u.ext2_sb.s_inode_bitmap[j - 1];
+ sb->s_inode_bitmap_number[j] =
+ sb->s_inode_bitmap_number[j - 1];
+ sb->s_inode_bitmap[j] =
+ sb->s_inode_bitmap[j - 1];
}
- sb->u.ext2_sb.s_inode_bitmap_number[0] = inode_bitmap_number;
- sb->u.ext2_sb.s_inode_bitmap[0] = inode_bitmap;
-
- /*
- * There's still one special case here --- if inode_bitmap == 0
- * then our last attempt to read the bitmap failed and we have
- * just ended up caching that failure. Try again to read it.
- */
- if (!inode_bitmap)
- retval = read_inode_bitmap (sb, block_group, 0);
-
+ sb->s_inode_bitmap_number[0] = inode_bitmap_number;
+ sb->s_inode_bitmap[0] = inode_bitmap;
} else {
- if (sb->u.ext2_sb.s_loaded_inode_bitmaps < EXT2_MAX_GROUP_LOADED)
- sb->u.ext2_sb.s_loaded_inode_bitmaps++;
+ if (sb->s_loaded_inode_bitmaps < EXT2_MAX_GROUP_LOADED)
+ sb->s_loaded_inode_bitmaps++;
else
- brelse (sb->u.ext2_sb.s_inode_bitmap[EXT2_MAX_GROUP_LOADED - 1]);
- for (j = sb->u.ext2_sb.s_loaded_inode_bitmaps - 1; j > 0; j--) {
- sb->u.ext2_sb.s_inode_bitmap_number[j] =
- sb->u.ext2_sb.s_inode_bitmap_number[j - 1];
- sb->u.ext2_sb.s_inode_bitmap[j] =
- sb->u.ext2_sb.s_inode_bitmap[j - 1];
+ ULCK_BUF(sb->s_inode_bitmap[EXT2_MAX_GROUP_LOADED - 1])
+ for (j = sb->s_loaded_inode_bitmaps - 1; j > 0; j--) {
+ sb->s_inode_bitmap_number[j] =
+ sb->s_inode_bitmap_number[j - 1];
+ sb->s_inode_bitmap[j] =
+ sb->s_inode_bitmap[j - 1];
}
- retval = read_inode_bitmap (sb, block_group, 0);
+ read_inode_bitmap (mp, block_group, 0);
}
- return retval;
+ return 0;
}
-/*
- * NOTE! When we get the inode, we're the only people
- * that have access to it, and as such there are no
- * race conditions we have to worry about. The inode
- * is not on the hash-lists, and it cannot be reached
- * through the filesystem because the directory entry
- * has been deleted earlier.
- *
- * HOWEVER: we must make sure that we get no aliases,
- * which means that we have to call "clear_inode()"
- * _before_ we mark the inode not in use in the inode
- * bitmaps. Otherwise a newly created file might use
- * the same inode number (not actually the same pointer
- * though), and then we'd have two inodes sharing the
- * same inode number and space on the harddisk.
- */
+
void ext2_free_inode (struct inode * inode)
{
- struct super_block * sb = inode->i_sb;
- int is_directory;
- unsigned long ino;
+ struct ext2_sb_info * sb;
struct buffer_head * bh;
struct buffer_head * bh2;
unsigned long block_group;
@@ -185,83 +200,89 @@ void ext2_free_inode (struct inode * inode)
struct ext2_group_desc * gdp;
struct ext2_super_block * es;
- if (!inode->i_dev) {
- printk ("ext2_free_inode: inode has no device\n");
+ if (!inode)
return;
- }
- if (inode->i_count > 1) {
- printk ("ext2_free_inode: inode has count=%d\n", inode->i_count);
- return;
- }
+
if (inode->i_nlink) {
- printk ("ext2_free_inode: inode has nlink=%d\n",
+ printf ("ext2_free_inode: inode has nlink=%d\n",
inode->i_nlink);
return;
}
- if (!sb) {
- printk("ext2_free_inode: inode on nonexistent device\n");
- return;
- }
- ino = inode->i_ino;
- ext2_debug ("freeing inode %lu\n", ino);
+ ext2_debug ("freeing inode %lu\n", inode->i_number);
- /*
- * Note: we must free any quota before locking the superblock,
- * as writing the quota to disk may need the lock as well.
- */
- DQUOT_FREE_INODE(sb, inode);
- DQUOT_DROP(inode);
-
- lock_super (sb);
- es = sb->u.ext2_sb.s_es;
- if (ino < EXT2_FIRST_INO(sb) ||
- ino > le32_to_cpu(es->s_inodes_count)) {
- ext2_error (sb, "free_inode",
- "reserved inode or nonexistent inode");
- goto error_return;
+ sb = inode->i_e2fs;
+ lock_super (DEVVP(inode));
+ if (inode->i_number < EXT2_FIRST_INO ||
+ inode->i_number > sb->s_es->s_inodes_count) {
+ printf ("free_inode reserved inode or nonexistent inode");
+ unlock_super (DEVVP(inode));
+ return;
}
- block_group = (ino - 1) / EXT2_INODES_PER_GROUP(sb);
- bit = (ino - 1) % EXT2_INODES_PER_GROUP(sb);
- bitmap_nr = load_inode_bitmap (sb, block_group);
- if (bitmap_nr < 0)
- goto error_return;
-
- bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr];
-
- is_directory = S_ISDIR(inode->i_mode);
-
- /* Do this BEFORE marking the inode not in use */
- clear_inode (inode);
-
- /* Ok, now we can actually update the inode bitmaps.. */
- if (!ext2_clear_bit (bit, bh->b_data))
- ext2_warning (sb, "ext2_free_inode",
- "bit already cleared for inode %lu", ino);
+ es = sb->s_es;
+ block_group = (inode->i_number - 1) / EXT2_INODES_PER_GROUP(sb);
+ bit = (inode->i_number - 1) % EXT2_INODES_PER_GROUP(sb);
+ bitmap_nr = load_inode_bitmap (ITOV(inode)->v_mount, block_group);
+ bh = sb->s_inode_bitmap[bitmap_nr];
+ if (!clear_bit (bit, bh->b_data))
+ printf ( "ext2_free_inode:"
+ "bit already cleared for inode %lu",
+ (unsigned long)inode->i_number);
else {
- gdp = ext2_get_group_desc (sb, block_group, &bh2);
- if (gdp) {
- gdp->bg_free_inodes_count =
- cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) + 1);
- if (is_directory)
- gdp->bg_used_dirs_count =
- cpu_to_le16(le16_to_cpu(gdp->bg_used_dirs_count) - 1);
- }
- mark_buffer_dirty(bh2, 1);
- es->s_free_inodes_count =
- cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) + 1);
- mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
+ gdp = get_group_desc (ITOV(inode)->v_mount, block_group, &bh2);
+ gdp->bg_free_inodes_count++;
+ if (S_ISDIR(inode->i_mode))
+ gdp->bg_used_dirs_count--;
+ mark_buffer_dirty(bh2);
+ es->s_free_inodes_count++;
}
- mark_buffer_dirty(bh, 1);
+ mark_buffer_dirty(bh);
+/*** XXX
if (sb->s_flags & MS_SYNCHRONOUS) {
ll_rw_block (WRITE, 1, &bh);
wait_on_buffer (bh);
}
+***/
sb->s_dirt = 1;
-error_return:
- unlock_super (sb);
+ unlock_super (DEVVP(inode));
+}
+
+#if linux
+/*
+ * This function increments the inode version number
+ *
+ * This may be used one day by the NFS server
+ */
+static void inc_inode_version (struct inode * inode,
+ struct ext2_group_desc *gdp,
+ int mode)
+{
+ unsigned long inode_block;
+ struct buffer_head * bh;
+ struct ext2_inode * raw_inode;
+
+ inode_block = gdp->bg_inode_table + (((inode->i_number - 1) %
+ EXT2_INODES_PER_GROUP(inode->i_sb)) /
+ EXT2_INODES_PER_BLOCK(inode->i_sb));
+ bh = bread (inode->i_sb->s_dev, inode_block, inode->i_sb->s_blocksize);
+ if (!bh) {
+ printf ("inc_inode_version Cannot load inode table block - "
+ "inode=%lu, inode_block=%lu\n",
+ inode->i_number, inode_block);
+ inode->u.ext2_i.i_version = 1;
+ return;
+ }
+ raw_inode = ((struct ext2_inode *) bh->b_data) +
+ (((inode->i_number - 1) %
+ EXT2_INODES_PER_GROUP(inode->i_sb)) %
+ EXT2_INODES_PER_BLOCK(inode->i_sb));
+ raw_inode->i_version++;
+ inode->u.ext2_i.i_version = raw_inode->i_version;
+ bdwrite (bh);
}
+#endif /* linux */
+
/*
* There are two policies for allocating an inode. If the new inode is
* a directory, then a forward search is made for a block group with both
@@ -272,49 +293,38 @@ error_return:
* For other inodes, search forward from the parent directory\'s block
* group to find a free inode.
*/
-struct inode * ext2_new_inode (const struct inode * dir, int mode, int * err)
+/*
+ * this functino has been reduced to the actual 'find the inode number' part
+ */
+ino_t ext2_new_inode (const struct inode * dir, int mode)
{
- struct super_block * sb;
+ struct ext2_sb_info * sb;
struct buffer_head * bh;
struct buffer_head * bh2;
int i, j, avefreei;
- struct inode * inode;
int bitmap_nr;
struct ext2_group_desc * gdp;
struct ext2_group_desc * tmp;
struct ext2_super_block * es;
- /* Cannot create files in a deleted directory */
- if (!dir || !dir->i_nlink) {
- *err = -EPERM;
- return NULL;
- }
-
- inode = get_empty_inode ();
- if (!inode) {
- *err = -ENOMEM;
- return NULL;
- }
+ if (!dir)
+ return 0;
+ sb = dir->i_e2fs;
- sb = dir->i_sb;
- inode->i_sb = sb;
- inode->i_flags = 0;
- lock_super (sb);
- es = sb->u.ext2_sb.s_es;
+ lock_super (DEVVP(dir));
+ es = sb->s_es;
repeat:
- gdp = NULL; i=0;
-
- *err = -ENOSPC;
- if (S_ISDIR(mode)) {
- avefreei = le32_to_cpu(es->s_free_inodes_count) /
- sb->u.ext2_sb.s_groups_count;
+ gdp = NULL; i=0;
+
+ if (S_ISDIR(mode)) {
+ avefreei = es->s_free_inodes_count /
+ sb->s_groups_count;
/* I am not yet convinced that this next bit is necessary.
i = dir->u.ext2_i.i_block_group;
for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) {
- tmp = ext2_get_group_desc (sb, i, &bh2);
- if (tmp &&
- (le16_to_cpu(tmp->bg_used_dirs_count) << 8) <
- le16_to_cpu(tmp->bg_free_inodes_count)) {
+ tmp = get_group_desc (sb, i, &bh2);
+ if ((tmp->bg_used_dirs_count << 8) <
+ tmp->bg_free_inodes_count) {
gdp = tmp;
break;
}
@@ -323,14 +333,13 @@ repeat:
}
*/
if (!gdp) {
- for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) {
- tmp = ext2_get_group_desc (sb, j, &bh2);
- if (tmp &&
- le16_to_cpu(tmp->bg_free_inodes_count) &&
- le16_to_cpu(tmp->bg_free_inodes_count) >= avefreei) {
+ for (j = 0; j < sb->s_groups_count; j++) {
+ tmp = get_group_desc(ITOV(dir)->v_mount,j,&bh2);
+ if (tmp->bg_free_inodes_count &&
+ tmp->bg_free_inodes_count >= avefreei) {
if (!gdp ||
- (le16_to_cpu(tmp->bg_free_blocks_count) >
- le16_to_cpu(gdp->bg_free_blocks_count))) {
+ (tmp->bg_free_blocks_count >
+ gdp->bg_free_blocks_count)) {
i = j;
gdp = tmp;
}
@@ -343,9 +352,9 @@ repeat:
/*
* Try to place the inode in its parent directory
*/
- i = dir->u.ext2_i.i_block_group;
- tmp = ext2_get_group_desc (sb, i, &bh2);
- if (tmp && le16_to_cpu(tmp->bg_free_inodes_count))
+ i = dir->i_block_group;
+ tmp = get_group_desc (ITOV(dir)->v_mount, i, &bh2);
+ if (tmp->bg_free_inodes_count)
gdp = tmp;
else
{
@@ -353,13 +362,12 @@ repeat:
* Use a quadratic hash to find a group with a
* free inode
*/
- for (j = 1; j < sb->u.ext2_sb.s_groups_count; j <<= 1) {
+ for (j = 1; j < sb->s_groups_count; j <<= 1) {
i += j;
- if (i >= sb->u.ext2_sb.s_groups_count)
- i -= sb->u.ext2_sb.s_groups_count;
- tmp = ext2_get_group_desc (sb, i, &bh2);
- if (tmp &&
- le16_to_cpu(tmp->bg_free_inodes_count)) {
+ if (i >= sb->s_groups_count)
+ i -= sb->s_groups_count;
+ tmp = get_group_desc(ITOV(dir)->v_mount,i,&bh2);
+ if (tmp->bg_free_inodes_count) {
gdp = tmp;
break;
}
@@ -369,13 +377,12 @@ repeat:
/*
* That failed: try linear search for a free inode
*/
- i = dir->u.ext2_i.i_block_group + 1;
- for (j = 2; j < sb->u.ext2_sb.s_groups_count; j++) {
- if (++i >= sb->u.ext2_sb.s_groups_count)
+ i = dir->i_block_group + 1;
+ for (j = 2; j < sb->s_groups_count; j++) {
+ if (++i >= sb->s_groups_count)
i = 0;
- tmp = ext2_get_group_desc (sb, i, &bh2);
- if (tmp &&
- le16_to_cpu(tmp->bg_free_inodes_count)) {
+ tmp = get_group_desc(ITOV(dir)->v_mount,i,&bh2);
+ if (tmp->bg_free_inodes_count) {
gdp = tmp;
break;
}
@@ -384,151 +391,94 @@ repeat:
}
if (!gdp) {
- unlock_super (sb);
- iput(inode);
- return NULL;
- }
- bitmap_nr = load_inode_bitmap (sb, i);
- if (bitmap_nr < 0) {
- unlock_super (sb);
- iput(inode);
- *err = -EIO;
- return NULL;
+ unlock_super (DEVVP(dir));
+ return 0;
}
-
- bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr];
- if ((j = ext2_find_first_zero_bit ((unsigned long *) bh->b_data,
+ bitmap_nr = load_inode_bitmap (ITOV(dir)->v_mount, i);
+ bh = sb->s_inode_bitmap[bitmap_nr];
+ if ((j = find_first_zero_bit ((unsigned long *) bh->b_data,
EXT2_INODES_PER_GROUP(sb))) <
EXT2_INODES_PER_GROUP(sb)) {
- if (ext2_set_bit (j, bh->b_data)) {
- ext2_warning (sb, "ext2_new_inode",
+ if (set_bit (j, bh->b_data)) {
+ printf ( "ext2_new_inode:"
"bit already set for inode %d", j);
goto repeat;
}
- mark_buffer_dirty(bh, 1);
+/* Linux now does the following:
+ mark_buffer_dirty(bh);
if (sb->s_flags & MS_SYNCHRONOUS) {
ll_rw_block (WRITE, 1, &bh);
wait_on_buffer (bh);
}
+*/
+ mark_buffer_dirty(bh);
} else {
- if (le16_to_cpu(gdp->bg_free_inodes_count) != 0) {
- ext2_error (sb, "ext2_new_inode",
+ if (gdp->bg_free_inodes_count != 0) {
+ printf ( "ext2_new_inode:"
"Free inodes count corrupted in group %d",
i);
- unlock_super (sb);
- iput (inode);
- return NULL;
+ unlock_super (DEVVP(dir));
+ return 0;
}
goto repeat;
}
j += i * EXT2_INODES_PER_GROUP(sb) + 1;
- if (j < EXT2_FIRST_INO(sb) || j > le32_to_cpu(es->s_inodes_count)) {
- ext2_error (sb, "ext2_new_inode",
+ if (j < EXT2_FIRST_INO || j > es->s_inodes_count) {
+ printf ( "ext2_new_inode:"
"reserved inode or inode > inodes count - "
"block_group = %d,inode=%d", i, j);
- unlock_super (sb);
- iput (inode);
- return NULL;
+ unlock_super (DEVVP(dir));
+ return 0;
}
- gdp->bg_free_inodes_count =
- cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) - 1);
+ gdp->bg_free_inodes_count--;
if (S_ISDIR(mode))
- gdp->bg_used_dirs_count =
- cpu_to_le16(le16_to_cpu(gdp->bg_used_dirs_count) + 1);
- mark_buffer_dirty(bh2, 1);
- es->s_free_inodes_count =
- cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) - 1);
- mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
+ gdp->bg_used_dirs_count++;
+ mark_buffer_dirty(bh2);
+ es->s_free_inodes_count--;
+ /* mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); */
sb->s_dirt = 1;
- inode->i_mode = mode;
- inode->i_sb = sb;
- inode->i_nlink = 1;
- inode->i_dev = sb->s_dev;
- inode->i_uid = current->fsuid;
- if (test_opt (sb, GRPID))
- inode->i_gid = dir->i_gid;
- else if (dir->i_mode & S_ISGID) {
- inode->i_gid = dir->i_gid;
- if (S_ISDIR(mode))
- mode |= S_ISGID;
- } else
- inode->i_gid = current->fsgid;
-
- inode->i_ino = j;
- inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */
- inode->i_blocks = 0;
- inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
- inode->u.ext2_i.i_new_inode = 1;
- inode->u.ext2_i.i_flags = dir->u.ext2_i.i_flags;
- if (S_ISLNK(mode))
- inode->u.ext2_i.i_flags &= ~(EXT2_IMMUTABLE_FL | EXT2_APPEND_FL);
- inode->u.ext2_i.i_faddr = 0;
- inode->u.ext2_i.i_frag_no = 0;
- inode->u.ext2_i.i_frag_size = 0;
- inode->u.ext2_i.i_file_acl = 0;
- inode->u.ext2_i.i_dir_acl = 0;
- inode->u.ext2_i.i_dtime = 0;
- inode->u.ext2_i.i_block_group = i;
- inode->i_op = NULL;
- if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL)
- inode->i_flags |= MS_SYNCHRONOUS;
- insert_inode_hash(inode);
- inode->i_generation = event++;
- mark_inode_dirty(inode);
-
- unlock_super (sb);
- if(DQUOT_ALLOC_INODE(sb, inode)) {
- sb->dq_op->drop(inode);
- inode->i_nlink = 0;
- iput(inode);
- *err = -EDQUOT;
- return NULL;
- }
- ext2_debug ("allocating inode %lu\n", inode->i_ino);
-
- *err = 0;
- return inode;
+ unlock_super (DEVVP(dir));
+ return j;
}
-unsigned long ext2_count_free_inodes (struct super_block * sb)
+#ifdef unused
+static unsigned long ext2_count_free_inodes (struct mount * mp)
{
#ifdef EXT2FS_DEBUG
+ struct ext2_sb_info *sb = VFSTOEXT2(mp)->um_e2fs;
struct ext2_super_block * es;
unsigned long desc_count, bitmap_count, x;
int bitmap_nr;
struct ext2_group_desc * gdp;
int i;
- lock_super (sb);
- es = sb->u.ext2_sb.s_es;
+ lock_super (VFSTOEXT2(mp)->um_devvp);
+ es = sb->s_es;
desc_count = 0;
bitmap_count = 0;
gdp = NULL;
- for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
- gdp = ext2_get_group_desc (sb, i, NULL);
- if (!gdp)
- continue;
- desc_count += le16_to_cpu(gdp->bg_free_inodes_count);
- bitmap_nr = load_inode_bitmap (sb, i);
- if (bitmap_nr < 0)
- continue;
-
- x = ext2_count_free (sb->u.ext2_sb.s_inode_bitmap[bitmap_nr],
+ for (i = 0; i < sb->s_groups_count; i++) {
+ gdp = get_group_desc (mp, i, NULL);
+ desc_count += gdp->bg_free_inodes_count;
+ bitmap_nr = load_inode_bitmap (mp, i);
+ x = ext2_count_free (sb->s_inode_bitmap[bitmap_nr],
EXT2_INODES_PER_GROUP(sb) / 8);
- printk ("group %d: stored = %d, counted = %lu\n",
- i, le16_to_cpu(gdp->bg_free_inodes_count), x);
+ ext2_debug ("group %d: stored = %d, counted = %lu\n",
+ i, gdp->bg_free_inodes_count, x);
bitmap_count += x;
}
- printk("ext2_count_free_inodes: stored = %lu, computed = %lu, %lu\n",
- le32_to_cpu(es->s_free_inodes_count), desc_count, bitmap_count);
- unlock_super (sb);
+ ext2_debug("stored = %lu, computed = %lu, %lu\n",
+ es->s_free_inodes_count, desc_count, bitmap_count);
+ unlock_super (VFSTOEXT2(mp)->um_devvp);
return desc_count;
#else
- return le32_to_cpu(sb->u.ext2_sb.s_es->s_free_inodes_count);
+ return VFSTOEXT2(mp)->um_e2fsb->s_free_inodes_count;
#endif
}
+#endif /* unused */
-void ext2_check_inodes_bitmap (struct super_block * sb)
+#ifdef LATER
+void ext2_check_inodes_bitmap (struct mount * mp)
{
struct ext2_super_block * es;
unsigned long desc_count, bitmap_count, x;
@@ -542,28 +492,23 @@ void ext2_check_inodes_bitmap (struct super_block * sb)
bitmap_count = 0;
gdp = NULL;
for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
- gdp = ext2_get_group_desc (sb, i, NULL);
- if (!gdp)
- continue;
- desc_count += le16_to_cpu(gdp->bg_free_inodes_count);
+ gdp = get_group_desc (sb, i, NULL);
+ desc_count += gdp->bg_free_inodes_count;
bitmap_nr = load_inode_bitmap (sb, i);
- if (bitmap_nr < 0)
- continue;
-
x = ext2_count_free (sb->u.ext2_sb.s_inode_bitmap[bitmap_nr],
EXT2_INODES_PER_GROUP(sb) / 8);
- if (le16_to_cpu(gdp->bg_free_inodes_count) != x)
- ext2_error (sb, "ext2_check_inodes_bitmap",
+ if (gdp->bg_free_inodes_count != x)
+ printf ( "ext2_check_inodes_bitmap:"
"Wrong free inodes count in group %d, "
"stored = %d, counted = %lu", i,
- le16_to_cpu(gdp->bg_free_inodes_count), x);
+ gdp->bg_free_inodes_count, x);
bitmap_count += x;
}
- if (le32_to_cpu(es->s_free_inodes_count) != bitmap_count)
- ext2_error (sb, "ext2_check_inodes_bitmap",
+ if (es->s_free_inodes_count != bitmap_count)
+ printf ( "ext2_check_inodes_bitmap:"
"Wrong free inodes count in super block, "
"stored = %lu, counted = %lu",
- (unsigned long) le32_to_cpu(es->s_free_inodes_count),
- bitmap_count);
+ (unsigned long) es->s_free_inodes_count, bitmap_count);
unlock_super (sb);
}
+#endif
diff --git a/sys/gnu/ext2fs/ext2_lookup.c b/sys/gnu/ext2fs/ext2_lookup.c
new file mode 100644
index 0000000..49caccf
--- /dev/null
+++ b/sys/gnu/ext2fs/ext2_lookup.c
@@ -0,0 +1,1110 @@
+/*
+ * 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
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/bio.h>
+#include <sys/buf.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/sysctl.h>
+
+#include <ufs/ufs/dir.h>
+
+#include <gnu/ext2fs/inode.h>
+#include <gnu/ext2fs/ext2_mount.h>
+#include <gnu/ext2fs/ext2_extern.h>
+#include <gnu/ext2fs/ext2_fs.h>
+#include <gnu/ext2fs/ext2_fs_sb.h>
+
+#ifdef DIAGNOSTIC
+static int dirchk = 1;
+#else
+static int dirchk = 0;
+#endif
+
+SYSCTL_NODE(_vfs, OID_AUTO, e2fs, CTLFLAG_RD, 0, "EXT2FS filesystem");
+SYSCTL_INT(_vfs_e2fs, OID_AUTO, dircheck, CTLFLAG_RW, &dirchk, 0, "");
+
+/*
+ DIRBLKSIZE in ffs is DEV_BSIZE (in most cases 512)
+ while it is the native blocksize in ext2fs - thus, a #define
+ is no longer appropriate
+*/
+#undef DIRBLKSIZ
+
+static u_char ext2_ft_to_dt[] = {
+ DT_UNKNOWN, /* EXT2_FT_UNKNOWN */
+ DT_REG, /* EXT2_FT_REG_FILE */
+ DT_DIR, /* EXT2_FT_DIR */
+ DT_CHR, /* EXT2_FT_CHRDEV */
+ DT_BLK, /* EXT2_FT_BLKDEV */
+ DT_FIFO, /* EXT2_FT_FIFO */
+ DT_SOCK, /* EXT2_FT_SOCK */
+ DT_LNK, /* EXT2_FT_SYMLINK */
+};
+#define FTTODT(ft) \
+ ((ft) > sizeof(ext2_ft_to_dt) / sizeof(ext2_ft_to_dt[0]) ? \
+ DT_UNKNOWN : ext2_ft_to_dt[(ft)])
+
+static u_char dt_to_ext2_ft[] = {
+ EXT2_FT_UNKNOWN, /* DT_UNKNOWN */
+ EXT2_FT_FIFO, /* DT_FIFO */
+ EXT2_FT_CHRDEV, /* DT_CHR */
+ EXT2_FT_UNKNOWN, /* unused */
+ EXT2_FT_DIR, /* DT_DIR */
+ EXT2_FT_UNKNOWN, /* unused */
+ EXT2_FT_BLKDEV, /* DT_BLK */
+ EXT2_FT_UNKNOWN, /* unused */
+ EXT2_FT_REG_FILE, /* DT_REG */
+ EXT2_FT_UNKNOWN, /* unused */
+ EXT2_FT_SYMLINK, /* DT_LNK */
+ EXT2_FT_UNKNOWN, /* unused */
+ EXT2_FT_SOCK, /* DT_SOCK */
+ EXT2_FT_UNKNOWN, /* unused */
+ EXT2_FT_UNKNOWN, /* DT_WHT */
+};
+#define DTTOFT(dt) \
+ ((dt) > sizeof(dt_to_ext2_ft) / sizeof(dt_to_ext2_ft[0]) ? \
+ EXT2_FT_UNKNOWN : dt_to_ext2_ft[(dt)])
+
+static int ext2_dirbadentry(struct vnode *dp, struct ext2_dir_entry_2 *de,
+ int entryoffsetinblock);
+
+/*
+ * Vnode op for reading directories.
+ *
+ * The routine below assumes that the on-disk format of a directory
+ * is the same as that defined by <sys/dirent.h>. If the on-disk
+ * format changes, then it will be necessary to do a conversion
+ * from the on-disk format that read returns to the format defined
+ * by <sys/dirent.h>.
+ */
+/*
+ * this is exactly what we do here - 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
+ext2_readdir(ap)
+ struct vop_readdir_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ struct uio *uio = ap->a_uio;
+ int count, error;
+
+ struct ext2_dir_entry_2 *edp, *dp;
+ int ncookies;
+ struct dirent dstdp;
+ struct uio auio;
+ struct iovec aiov;
+ caddr_t dirbuf;
+ int DIRBLKSIZ = VTOI(ap->a_vp)->i_e2fs->s_blocksize;
+ int readcnt;
+ off_t startoffset = uio->uio_offset;
+
+ count = uio->uio_resid;
+ /*
+ * Avoid complications for partial directory entries by adjusting
+ * the i/o to end at a block boundary. Don't give up (like ufs
+ * does) if the initial adjustment gives a negative count, since
+ * many callers don't supply a large enough buffer. The correct
+ * size is a little larger than DIRBLKSIZ to allow for expansion
+ * of directory entries, but some callers just use 512.
+ */
+ count -= (uio->uio_offset + count) & (DIRBLKSIZ -1);
+ if (count <= 0)
+ count += DIRBLKSIZ;
+
+#ifdef EXT2FS_DEBUG
+ printf("ext2_readdir: uio_offset = %lld, uio_resid = %d, count = %d\n",
+ uio->uio_offset, uio->uio_resid, count);
+#endif
+
+ auio = *uio;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_resid = count;
+ auio.uio_segflg = UIO_SYSSPACE;
+ aiov.iov_len = count;
+ MALLOC(dirbuf, caddr_t, count, M_TEMP, M_WAITOK);
+ aiov.iov_base = dirbuf;
+ error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred);
+ if (error == 0) {
+ readcnt = count - auio.uio_resid;
+ edp = (struct ext2_dir_entry_2 *)&dirbuf[readcnt];
+ ncookies = 0;
+ bzero(&dstdp, offsetof(struct dirent, d_name));
+ for (dp = (struct ext2_dir_entry_2 *)dirbuf;
+ !error && uio->uio_resid > 0 && dp < edp; ) {
+ /*-
+ * "New" ext2fs directory entries differ in 3 ways
+ * from ufs on-disk ones:
+ * - the name is not necessarily NUL-terminated.
+ * - the file type field always exists and always
+ * follows the name length field.
+ * - the file type is encoded in a different way.
+ *
+ * "Old" ext2fs directory entries need no special
+ * conversions, since they binary compatible with
+ * "new" entries having a file type of 0 (i.e.,
+ * EXT2_FT_UNKNOWN). Splitting the old name length
+ * field didn't make a mess like it did in ufs,
+ * because ext2fs uses a machine-dependent disk
+ * layout.
+ */
+ dstdp.d_fileno = dp->inode;
+ dstdp.d_type = FTTODT(dp->file_type);
+ dstdp.d_namlen = dp->name_len;
+ dstdp.d_reclen = GENERIC_DIRSIZ(&dstdp);
+ bcopy(dp->name, dstdp.d_name, dstdp.d_namlen);
+ bzero(dstdp.d_name + dstdp.d_namlen,
+ dstdp.d_reclen - offsetof(struct dirent, d_name) -
+ dstdp.d_namlen);
+
+ if (dp->rec_len > 0) {
+ if(dstdp.d_reclen <= uio->uio_resid) {
+ /* advance dp */
+ dp = (struct ext2_dir_entry_2 *)
+ ((char *)dp + dp->rec_len);
+ error =
+ uiomove((caddr_t)&dstdp,
+ dstdp.d_reclen, uio);
+ if (!error)
+ ncookies++;
+ } else
+ break;
+ } else {
+ error = EIO;
+ break;
+ }
+ }
+ /* we need to correct uio_offset */
+ uio->uio_offset = startoffset + (caddr_t)dp - dirbuf;
+
+ if (!error && ap->a_ncookies != NULL) {
+ u_long *cookiep, *cookies, *ecookies;
+ off_t off;
+
+ if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1)
+ panic("ext2fs_readdir: unexpected uio from NFS server");
+ MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP,
+ M_WAITOK);
+ off = startoffset;
+ for (dp = (struct ext2_dir_entry_2 *)dirbuf,
+ cookiep = cookies, ecookies = cookies + ncookies;
+ cookiep < ecookies;
+ dp = (struct ext2_dir_entry_2 *)((caddr_t) dp + dp->rec_len)) {
+ off += dp->rec_len;
+ *cookiep++ = (u_long) off;
+ }
+ *ap->a_ncookies = ncookies;
+ *ap->a_cookies = cookies;
+ }
+ }
+ FREE(dirbuf, M_TEMP);
+ if (ap->a_eofflag)
+ *ap->a_eofflag = VTOI(ap->a_vp)->i_size <= uio->uio_offset;
+ 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 ufs_lookup:
+ *
+ * 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
+ext2_lookup(ap)
+ struct vop_cachedlookup_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ struct vnode *vdp; /* vnode for directory being searched */
+ struct inode *dp; /* inode for directory being searched */
+ struct buf *bp; /* a buffer of directory entries */
+ struct ext2_dir_entry_2 *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;
+ struct thread *td = cnp->cn_thread;
+
+ int DIRBLKSIZ = VTOI(ap->a_dvp)->i_e2fs->s_blocksize;
+
+ bp = NULL;
+ slotoffset = -1;
+ *vpp = NULL;
+ vdp = ap->a_dvp;
+ dp = VTOI(vdp);
+ lockparent = flags & LOCKPARENT;
+ wantparent = flags & (LOCKPARENT|WANTPARENT);
+
+ /*
+ * We now have a segment name to search for, and a directory to search.
+ */
+
+ /*
+ * 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 = EXT2_DIR_REC_LEN(cnp->cn_namelen);
+ /* was
+ slotneeded = (sizeof(struct direct) - MAXNAMLEN +
+ cnp->cn_namelen + 3) &~ 3; */
+ }
+
+ /*
+ * 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 = VFSTOEXT2(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
+ if (nameiop != LOOKUP || dp->i_diroff == 0 ||
+ dp->i_diroff > dp->i_size) {
+ entryoffsetinblock = 0;
+ dp->i_offset = 0;
+ numdirpasses = 1;
+ } else {
+ dp->i_offset = dp->i_diroff;
+ if ((entryoffsetinblock = dp->i_offset & bmask) &&
+ (error = ext2_blkatoff(vdp, (off_t)dp->i_offset, NULL,
+ &bp)))
+ return (error);
+ numdirpasses = 2;
+ nchstats.ncs_2passes++;
+ }
+ prevoff = dp->i_offset;
+ endsearch = roundup(dp->i_size, DIRBLKSIZ);
+ 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);
+ if ((error =
+ ext2_blkatoff(vdp, (off_t)dp->i_offset, NULL,
+ &bp)) != 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 & (DIRBLKSIZ - 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 setting
+ * "vfs.e2fs.dirchk" to be true.
+ */
+ ep = (struct ext2_dir_entry_2 *)
+ ((char *)bp->b_data + entryoffsetinblock);
+ if (ep->rec_len == 0 ||
+ (dirchk && ext2_dirbadentry(vdp, ep, entryoffsetinblock))) {
+ int i;
+ ext2_dirbad(dp, dp->i_offset, "mangled entry");
+ i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 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->rec_len;
+
+ if (ep->inode != 0)
+ size -= EXT2_DIR_REC_LEN(ep->name_len);
+ if (size > 0) {
+ if (size >= slotneeded) {
+ slotstatus = FOUND;
+ slotoffset = dp->i_offset;
+ slotsize = ep->rec_len;
+ } else if (slotstatus == NONE) {
+ slotfreespace += size;
+ if (slotoffset == -1)
+ slotoffset = dp->i_offset;
+ if (slotfreespace >= slotneeded) {
+ slotstatus = COMPACT;
+ slotsize = dp->i_offset +
+ ep->rec_len - slotoffset;
+ }
+ }
+ }
+ }
+
+ /*
+ * Check for a name match.
+ */
+ if (ep->inode) {
+ namlen = ep->name_len;
+ if (namlen == cnp->cn_namelen &&
+ !bcmp(cnp->cn_nameptr, ep->name,
+ (unsigned)namlen)) {
+ /*
+ * Save directory entry's inode number and
+ * reclen in ndp->ni_ufs area, and release
+ * directory buffer.
+ */
+ dp->i_ino = ep->inode;
+ dp->i_reclen = ep->rec_len;
+ goto found;
+ }
+ }
+ prevoff = dp->i_offset;
+ dp->i_offset += ep->rec_len;
+ entryoffsetinblock += ep->rec_len;
+ if (ep->inode)
+ 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_nlink != 0) {
+ /*
+ * Access for write is interpreted as allowing
+ * creation of files in the directory.
+ */
+ if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 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_size, DIRBLKSIZ);
+ 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, DIRBLKSIZ);
+ 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, 0, td);
+ 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:
+ if (numdirpasses == 2)
+ nchstats.ncs_pass2++;
+ /*
+ * Check that directory length properly reflects presence
+ * of this entry.
+ */
+ if (entryoffsetinblock + EXT2_DIR_REC_LEN(ep->name_len)
+ > dp->i_size) {
+ ext2_dirbad(dp, dp->i_offset, "i_size too small");
+ dp->i_size = entryoffsetinblock+EXT2_DIR_REC_LEN(ep->name_len);
+ dp->i_flag |= IN_CHANGE | IN_UPDATE;
+ }
+ brelse(bp);
+
+ /*
+ * 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 &~ (DIRBLKSIZ - 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_thread)) != 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 & (DIRBLKSIZ - 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, LK_EXCLUSIVE,
+ &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_mode & ISVTX) &&
+ cred->cr_uid != 0 &&
+ cred->cr_uid != dp->i_uid &&
+ VTOI(tdp)->i_uid != cred->cr_uid) {
+ vput(tdp);
+ return (EPERM);
+ }
+ *vpp = tdp;
+ if (!lockparent)
+ VOP_UNLOCK(vdp, 0, td);
+ 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_thread)) != 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, LK_EXCLUSIVE,
+ &tdp)) != 0)
+ return (error);
+ *vpp = tdp;
+ cnp->cn_flags |= SAVENAME;
+ if (!lockparent)
+ VOP_UNLOCK(vdp, 0, td);
+ 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, 0, td); /* race to get the inode */
+ if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, LK_EXCLUSIVE,
+ &tdp)) != 0) {
+ vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, td);
+ return (error);
+ }
+ if (lockparent && (flags & ISLASTCN) &&
+ (error = vn_lock(pdp, LK_EXCLUSIVE, td))) {
+ 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, LK_EXCLUSIVE,
+ &tdp)) != 0)
+ return (error);
+ if (!lockparent || !(flags & ISLASTCN))
+ VOP_UNLOCK(pdp, 0, td);
+ *vpp = tdp;
+ }
+
+ /*
+ * Insert name into cache if appropriate.
+ */
+ if (cnp->cn_flags & MAKEENTRY)
+ cache_enter(vdp, *vpp, cnp);
+ return (0);
+}
+
+void
+ext2_dirbad(ip, offset, how)
+ struct inode *ip;
+ doff_t offset;
+ char *how;
+{
+ struct mount *mp;
+
+ mp = ITOV(ip)->v_mount;
+ (void)printf("%s: bad dir ino %lu at offset %ld: %s\n",
+ mp->mnt_stat.f_mntonname, (u_long)ip->i_number, (long)offset, how);
+ if ((mp->mnt_flag & MNT_RDONLY) == 0)
+ panic("ext2_dirbad: bad dir");
+}
+
+/*
+ * Do consistency checking on a directory entry:
+ * record length must be multiple of 4
+ * entry must fit in rest of its DIRBLKSIZ 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 ext2_check_dir_entry
+ */
+static int
+ext2_dirbadentry(dp, de, entryoffsetinblock)
+ struct vnode *dp;
+ struct ext2_dir_entry_2 *de;
+ int entryoffsetinblock;
+{
+ int DIRBLKSIZ = VTOI(dp)->i_e2fs->s_blocksize;
+
+ char * error_msg = NULL;
+
+ if (de->rec_len < EXT2_DIR_REC_LEN(1))
+ error_msg = "rec_len is smaller than minimal";
+ else if (de->rec_len % 4 != 0)
+ error_msg = "rec_len % 4 != 0";
+ else if (de->rec_len < EXT2_DIR_REC_LEN(de->name_len))
+ error_msg = "reclen is too small for name_len";
+ else if (entryoffsetinblock + de->rec_len > DIRBLKSIZ)
+ error_msg = "directory entry across blocks";
+ /* else LATER
+ if (de->inode > dir->i_sb->u.ext2_sb.s_es->s_inodes_count)
+ error_msg = "inode out of bounds";
+ */
+
+ if (error_msg != NULL) {
+ printf("bad directory entry: %s\n", error_msg);
+ printf("offset=%d, inode=%lu, rec_len=%u, name_len=%u\n",
+ entryoffsetinblock, (unsigned long)de->inode,
+ de->rec_len, de->name_len);
+ }
+ 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
+ext2_direnter(ip, dvp, cnp)
+ struct inode *ip;
+ struct vnode *dvp;
+ struct componentname *cnp;
+{
+ struct ext2_dir_entry_2 *ep, *nep;
+ struct inode *dp;
+ struct buf *bp;
+ struct ext2_dir_entry_2 newdir;
+ struct iovec aiov;
+ struct uio auio;
+ u_int dsize;
+ int error, loc, newentrysize, spacefree;
+ char *dirbuf;
+ int DIRBLKSIZ = ip->i_e2fs->s_blocksize;
+
+
+#if DIAGNOSTIC
+ if ((cnp->cn_flags & SAVENAME) == 0)
+ panic("direnter: missing name");
+#endif
+ dp = VTOI(dvp);
+ newdir.inode = ip->i_number;
+ newdir.name_len = cnp->cn_namelen;
+ if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs->s_es,
+ EXT2_FEATURE_INCOMPAT_FILETYPE))
+ newdir.file_type = DTTOFT(IFTODT(ip->i_mode));
+ else
+ newdir.file_type = EXT2_FT_UNKNOWN;
+ bcopy(cnp->cn_nameptr, newdir.name, (unsigned)cnp->cn_namelen + 1);
+ newentrysize = EXT2_DIR_REC_LEN(newdir.name_len);
+ 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 & (DIRBLKSIZ - 1))
+ panic("ext2_direnter: newblk");
+ auio.uio_offset = dp->i_offset;
+ newdir.rec_len = DIRBLKSIZ;
+ 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_td = (struct thread *)0;
+ error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred);
+ if (DIRBLKSIZ >
+ VFSTOEXT2(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
+ /* XXX should grow with balloc() */
+ panic("ext2_direnter: frag size");
+ else if (!error) {
+ dp->i_size = roundup(dp->i_size, DIRBLKSIZ);
+ 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.
+ */
+
+ /*
+ * Increase size of directory if entry eats into new space.
+ * This should never push the size past a new multiple of
+ * DIRBLKSIZE.
+ *
+ * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
+ */
+ if (dp->i_offset + dp->i_count > dp->i_size)
+ dp->i_size = dp->i_offset + dp->i_count;
+ /*
+ * Get the block containing the space for the new directory entry.
+ */
+ if ((error = ext2_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 ext2_dir_entry_2 *)dirbuf;
+ dsize = EXT2_DIR_REC_LEN(ep->name_len);
+ spacefree = ep->rec_len - dsize;
+ for (loc = ep->rec_len; loc < dp->i_count; ) {
+ nep = (struct ext2_dir_entry_2 *)(dirbuf + loc);
+ if (ep->inode) {
+ /* trim the existing slot */
+ ep->rec_len = dsize;
+ ep = (struct ext2_dir_entry_2 *)((char *)ep + dsize);
+ } else {
+ /* overwrite; nothing there; header is ours */
+ spacefree += dsize;
+ }
+ dsize = EXT2_DIR_REC_LEN(nep->name_len);
+ spacefree += nep->rec_len - dsize;
+ loc += nep->rec_len;
+ 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->inode == 0) {
+ if (spacefree + dsize < newentrysize)
+ panic("ext2_direnter: compact1");
+ newdir.rec_len = spacefree + dsize;
+ } else {
+ if (spacefree < newentrysize)
+ panic("ext2_direnter: compact2");
+ newdir.rec_len = spacefree;
+ ep->rec_len = dsize;
+ ep = (struct ext2_dir_entry_2 *)((char *)ep + dsize);
+ }
+ bcopy((caddr_t)&newdir, (caddr_t)ep, (u_int)newentrysize);
+ error = BUF_WRITE(bp);
+ dp->i_flag |= IN_CHANGE | IN_UPDATE;
+ if (!error && dp->i_endoff && dp->i_endoff < dp->i_size)
+ error = ext2_truncate(dvp, (off_t)dp->i_endoff, IO_SYNC,
+ cnp->cn_cred, cnp->cn_thread);
+ 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
+ext2_dirremove(dvp, cnp)
+ struct vnode *dvp;
+ struct componentname *cnp;
+{
+ struct inode *dp;
+ struct ext2_dir_entry_2 *ep;
+ struct buf *bp;
+ int error;
+
+ dp = VTOI(dvp);
+ if (dp->i_count == 0) {
+ /*
+ * First entry in block: set d_ino to zero.
+ */
+ if ((error =
+ ext2_blkatoff(dvp, (off_t)dp->i_offset, (char **)&ep,
+ &bp)) != 0)
+ return (error);
+ ep->inode = 0;
+ error = BUF_WRITE(bp);
+ dp->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (error);
+ }
+ /*
+ * Collapse new free space into previous entry.
+ */
+ if ((error = ext2_blkatoff(dvp, (off_t)(dp->i_offset - dp->i_count),
+ (char **)&ep, &bp)) != 0)
+ return (error);
+ ep->rec_len += dp->i_reclen;
+ error = BUF_WRITE(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
+ext2_dirrewrite(dp, ip, cnp)
+ struct inode *dp, *ip;
+ struct componentname *cnp;
+{
+ struct buf *bp;
+ struct ext2_dir_entry_2 *ep;
+ struct vnode *vdp = ITOV(dp);
+ int error;
+
+ if ((error = ext2_blkatoff(vdp, (off_t)dp->i_offset, (char **)&ep,
+ &bp)) != 0)
+ return (error);
+ ep->inode = ip->i_number;
+ if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs->s_es,
+ EXT2_FEATURE_INCOMPAT_FILETYPE))
+ ep->file_type = DTTOFT(IFTODT(ip->i_mode));
+ else
+ ep->file_type = EXT2_FT_UNKNOWN;
+ error = BUF_WRITE(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 direct.
+ *
+ * NB: does not handle corrupted directories.
+ */
+int
+ext2_dirempty(ip, parentino, cred)
+ struct inode *ip;
+ ino_t parentino;
+ struct ucred *cred;
+{
+ off_t off;
+ struct dirtemplate dbuf;
+ struct ext2_dir_entry_2 *dp = (struct ext2_dir_entry_2 *)&dbuf;
+ int error, count, namlen;
+
+#define MINDIRSIZ (sizeof (struct dirtemplate) / 2)
+
+ for (off = 0; off < ip->i_size; off += dp->rec_len) {
+ error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ,
+ off, UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK, cred,
+ NOCRED, &count, (struct thread *)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->rec_len == 0)
+ return (0);
+ /* skip empty entries */
+ if (dp->inode == 0)
+ continue;
+ /* accept only "." and ".." */
+ namlen = dp->name_len;
+ if (namlen > 2)
+ return (0);
+ if (dp->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->name[1] == '.' && dp->inode == 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
+ext2_checkpath(source, target, cred)
+ struct inode *source, *target;
+ struct ucred *cred;
+{
+ struct vnode *vp;
+ int error, rootino, namlen;
+ struct 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 dirtemplate), (off_t)0, UIO_SYSSPACE,
+ IO_NODELOCKED | IO_NOMACCHECK, cred, NOCRED, (int *)0,
+ (struct thread *)0);
+ if (error != 0)
+ break;
+ namlen = dirbuf.dotdot_type; /* like ufs little-endian */
+ 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);
+ if ((error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino,
+ LK_EXCLUSIVE, &vp)) != 0) {
+ vp = NULL;
+ break;
+ }
+ }
+
+out:
+ if (error == ENOTDIR)
+ printf("checkpath: .. not a directory\n");
+ if (vp != NULL)
+ vput(vp);
+ return (error);
+}
+
diff --git a/sys/gnu/ext2fs/ext2_mount.h b/sys/gnu/ext2fs/ext2_mount.h
new file mode 100644
index 0000000..4e4894f
--- /dev/null
+++ b/sys/gnu/ext2fs/ext2_mount.h
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ *
+ * @(#)ufsmount.h 8.6 (Berkeley) 3/30/95
+ * $FreeBSD$
+ */
+
+#ifndef _SYS_GNU_EXT2FS_EXT2_MOUNT_H_
+#define _SYS_GNU_EXT2FS_EXT2_MOUNT_H_
+
+#ifdef _KERNEL
+
+#ifdef MALLOC_DECLARE
+MALLOC_DECLARE(M_EXT2NODE);
+#endif
+
+struct vnode;
+
+/* This structure describes the ext2fs specific mount structure data. */
+struct ext2mount {
+ struct mount *um_mountp; /* filesystem vfs structure */
+ dev_t um_dev; /* device mounted */
+ struct vnode *um_devvp; /* block device mounted vnode */
+
+ struct ext2_sb_info *um_e2fs; /* EXT2FS */
+#define em_e2fsb um_e2fs->s_es
+
+ u_long um_nindir; /* indirect ptrs per block */
+ u_long um_bptrtodb; /* indir ptr to disk block */
+ u_long um_seqinc; /* inc between seq blocks */
+};
+
+/* Convert mount ptr to ext2fsmount ptr. */
+#define VFSTOEXT2(mp) ((struct ext2mount *)((mp)->mnt_data))
+
+/*
+ * Macros to access file system parameters in the ufsmount structure.
+ * Used by ufs_bmap.
+ */
+#define MNINDIR(ump) ((ump)->um_nindir)
+#define blkptrtodb(ump, b) ((b) << (ump)->um_bptrtodb)
+#define is_sequential(ump, a, b) ((b) == (a) + ump->um_seqinc)
+#endif /* _KERNEL */
+
+#endif
diff --git a/sys/gnu/ext2fs/ext2_readwrite.c b/sys/gnu/ext2fs/ext2_readwrite.c
new file mode 100644
index 0000000..232e46f
--- /dev/null
+++ b/sys/gnu/ext2fs/ext2_readwrite.c
@@ -0,0 +1,312 @@
+/*
+ * modified for Lites 1.1
+ *
+ * Aug 1995, Godmar Back (gback@cs.utah.edu)
+ * University of Utah, Department of Computer Science
+ */
+/*
+ * 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.7 (Berkeley) 1/21/94
+ * $FreeBSD$
+ */
+
+#define BLKSIZE(a, b, c) blksize(a, b, c)
+#define FS struct ext2_sb_info
+#define I_FS i_e2fs
+#define READ ext2_read
+#define READ_S "ext2_read"
+#define WRITE ext2_write
+#define WRITE_S "ext2_write"
+
+/*
+ * Vnode op for reading.
+ */
+/* ARGSUSED */
+static int
+READ(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ struct vnode *vp;
+ struct inode *ip;
+ struct uio *uio;
+ FS *fs;
+ struct buf *bp;
+ daddr_t lbn, nextlbn;
+ off_t bytesinfile;
+ long size, xfersize, blkoffset;
+ int error, orig_resid;
+ int seqcount = ap->a_ioflag >> 16;
+ u_short mode;
+
+ vp = ap->a_vp;
+ ip = VTOI(vp);
+ mode = ip->i_mode;
+ uio = ap->a_uio;
+
+#ifdef DIAGNOSTIC
+ if (uio->uio_rw != UIO_READ)
+ panic("%s: mode", READ_S);
+
+ if (vp->v_type == VLNK) {
+ if ((int)ip->i_size < vp->v_mount->mnt_maxsymlinklen)
+ panic("%s: short symlink", READ_S);
+ } else if (vp->v_type != VREG && vp->v_type != VDIR)
+ panic("%s: type %d", READ_S, vp->v_type);
+#endif
+ fs = ip->I_FS;
+#if 0
+ if ((u_quad_t)uio->uio_offset > fs->fs_maxfilesize)
+ return (EFBIG);
+#endif
+
+ orig_resid = uio->uio_resid;
+ for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) {
+ if ((bytesinfile = ip->i_size - uio->uio_offset) <= 0)
+ break;
+ lbn = lblkno(fs, uio->uio_offset);
+ nextlbn = lbn + 1;
+ size = BLKSIZE(fs, ip, lbn);
+ blkoffset = blkoff(fs, uio->uio_offset);
+
+ xfersize = fs->s_frag_size - blkoffset;
+ if (uio->uio_resid < xfersize)
+ xfersize = uio->uio_resid;
+ if (bytesinfile < xfersize)
+ xfersize = bytesinfile;
+
+ if (lblktosize(fs, nextlbn) >= ip->i_size)
+ error = bread(vp, lbn, size, NOCRED, &bp);
+ else if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERR) == 0)
+ error = cluster_read(vp,
+ ip->i_size, lbn, size, NOCRED,
+ uio->uio_resid, (ap->a_ioflag >> 16), &bp);
+ else if (seqcount > 1) {
+ int nextsize = BLKSIZE(fs, ip, nextlbn);
+ error = breadn(vp, lbn,
+ size, &nextlbn, &nextsize, 1, NOCRED, &bp);
+ } else
+ error = bread(vp, lbn, size, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ bp = NULL;
+ break;
+ }
+
+ /*
+ * 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;
+
+ bqrelse(bp);
+ }
+ if (bp != NULL)
+ bqrelse(bp);
+ if (orig_resid > 0 && (error == 0 || uio->uio_resid != orig_resid) &&
+ (vp->v_mount->mnt_flag & MNT_NOATIME) == 0)
+ ip->i_flag |= IN_ACCESS;
+ return (error);
+}
+
+/*
+ * Vnode op for writing.
+ */
+static int
+WRITE(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ struct vnode *vp;
+ struct uio *uio;
+ struct inode *ip;
+ FS *fs;
+ struct buf *bp;
+ struct thread *td;
+ daddr_t lbn;
+ off_t osize;
+ int seqcount;
+ int blkoffset, error, flags, ioflag, resid, size, xfersize;
+
+ ioflag = ap->a_ioflag;
+ seqcount = ap->a_ioflag >> 16;
+ uio = ap->a_uio;
+ vp = ap->a_vp;
+ ip = VTOI(vp);
+
+#ifdef DIAGNOSTIC
+ if (uio->uio_rw != UIO_WRITE)
+ panic("%s: mode", WRITE_S);
+#endif
+
+ switch (vp->v_type) {
+ case VREG:
+ if (ioflag & IO_APPEND)
+ uio->uio_offset = ip->i_size;
+ if ((ip->i_flags & APPEND) && uio->uio_offset != ip->i_size)
+ return (EPERM);
+ /* FALLTHROUGH */
+ case VLNK:
+ break;
+ case VDIR:
+ if ((ioflag & IO_SYNC) == 0)
+ panic("%s: nonsync dir write", WRITE_S);
+ break;
+ default:
+ panic("%s: type", WRITE_S);
+ }
+
+ fs = ip->I_FS;
+#if 0
+ if (uio->uio_offset < 0 ||
+ (u_quad_t)uio->uio_offset + uio->uio_resid > fs->fs_maxfilesize)
+ return (EFBIG);
+#endif
+ /*
+ * Maybe this should be above the vnode op call, but so long as
+ * file servers have no limits, I don't think it matters.
+ */
+ td = uio->uio_td;
+ /* For p_rlimit. */
+ mtx_assert(&Giant, MA_OWNED);
+ if (vp->v_type == VREG && td &&
+ uio->uio_offset + uio->uio_resid >
+ td->td_proc->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
+ PROC_LOCK(td->td_proc);
+ psignal(td->td_proc, SIGXFSZ);
+ PROC_UNLOCK(td->td_proc);
+ return (EFBIG);
+ }
+
+ resid = uio->uio_resid;
+ osize = ip->i_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->s_frag_size - blkoffset;
+ if (uio->uio_resid < xfersize)
+ xfersize = uio->uio_resid;
+
+ if (uio->uio_offset + xfersize > ip->i_size)
+ vnode_pager_setsize(vp, uio->uio_offset + xfersize);
+
+ /*
+ * Avoid a data-consistency race between write() and mmap()
+ * by ensuring that newly allocated blocks are zerod. The
+ * race can occur even in the case where the write covers
+ * the entire block.
+ */
+ flags |= B_CLRBUF;
+#if 0
+ if (fs->s_frag_size > xfersize)
+ flags |= B_CLRBUF;
+ else
+ flags &= ~B_CLRBUF;
+#endif
+
+ error = ext2_balloc(ip,
+ lbn, blkoffset + xfersize, ap->a_cred, &bp, flags);
+ if (error)
+ break;
+
+ if (uio->uio_offset + xfersize > ip->i_size) {
+ ip->i_size = uio->uio_offset + xfersize;
+ }
+
+ size = BLKSIZE(fs, ip, lbn) - bp->b_resid;
+ if (size < xfersize)
+ xfersize = size;
+
+ error =
+ uiomove((char *)bp->b_data + blkoffset, (int)xfersize, uio);
+ if ((ioflag & IO_VMIO) &&
+ (LIST_FIRST(&bp->b_dep) == NULL)) /* in ext2fs? */
+ bp->b_flags |= B_RELBUF;
+
+ if (ioflag & IO_SYNC) {
+ (void)bwrite(bp);
+ } else if (xfersize + blkoffset == fs->s_frag_size) {
+ if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0) {
+ bp->b_flags |= B_CLUSTEROK;
+ cluster_write(bp, ip->i_size, seqcount);
+ } else {
+ bawrite(bp);
+ }
+ } else {
+ bp->b_flags |= B_CLUSTEROK;
+ 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_mode &= ~(ISUID | ISGID);
+ if (error) {
+ if (ioflag & IO_UNIT) {
+ (void)ext2_truncate(vp, osize,
+ ioflag & IO_SYNC, ap->a_cred, uio->uio_td);
+ uio->uio_offset -= resid - uio->uio_resid;
+ uio->uio_resid = resid;
+ }
+ } else if (resid > uio->uio_resid && (ioflag & IO_SYNC))
+ error = ext2_update(vp, 1);
+ return (error);
+}
diff --git a/sys/gnu/ext2fs/ext2_subr.c b/sys/gnu/ext2fs/ext2_subr.c
new file mode 100644
index 0000000..e762b38
--- /dev/null
+++ b/sys/gnu/ext2fs/ext2_subr.c
@@ -0,0 +1,127 @@
+/*
+ * modified for Lites 1.1
+ *
+ * Aug 1995, Godmar Back (gback@cs.utah.edu)
+ * University of Utah, Department of Computer Science
+ */
+/*
+ * 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
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/bio.h>
+#include <sys/buf.h>
+#include <sys/lock.h>
+#include <sys/ucred.h>
+#include <sys/vnode.h>
+
+#include <gnu/ext2fs/inode.h>
+#include <gnu/ext2fs/ext2_extern.h>
+#include <gnu/ext2fs/ext2_fs_sb.h>
+#include <gnu/ext2fs/fs.h>
+
+#include "opt_ddb.h"
+
+#ifdef DDB
+void ext2_checkoverlap(struct buf *, struct inode *);
+#endif
+
+/*
+ * 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
+ext2_blkatoff(vp, offset, res, bpp)
+ struct vnode *vp;
+ off_t offset;
+ char **res;
+ struct buf **bpp;
+{
+ struct inode *ip;
+ struct ext2_sb_info *fs;
+ struct buf *bp;
+ int32_t lbn;
+ int bsize, error;
+
+ ip = VTOI(vp);
+ fs = ip->i_e2fs;
+ lbn = lblkno(fs, offset);
+ bsize = blksize(fs, ip, lbn);
+
+ *bpp = NULL;
+ if ((error = bread(vp, lbn, bsize, NOCRED, &bp)) != 0) {
+ brelse(bp);
+ return (error);
+ }
+ if (res)
+ *res = (char *)bp->b_data + blkoff(fs, offset);
+ *bpp = bp;
+ return (0);
+}
+
+#ifdef DDB
+void
+ext2_checkoverlap(bp, ip)
+ struct buf *bp;
+ struct inode *ip;
+{
+ struct buf *ebp, *ep;
+ int32_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;
+ vp = ip->i_devvp;
+ /* 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);
+ (void)printf("\tstart %d, end %d overlap start %lld, end %ld\n",
+ start, last, (long long)ep->b_blkno,
+ (long)(ep->b_blkno + btodb(ep->b_bcount) - 1));
+ panic("Disk buffer overlap");
+ }
+}
+#endif /* DDB */
diff --git a/sys/gnu/ext2fs/ext2_super.c b/sys/gnu/ext2fs/ext2_super.c
deleted file mode 100644
index 11f99b4..0000000
--- a/sys/gnu/ext2fs/ext2_super.c
+++ /dev/null
@@ -1,802 +0,0 @@
-/*
- * linux/fs/ext2/super.c
- *
- * Copyright (C) 1992, 1993, 1994, 1995
- * Remy Card (card@masi.ibp.fr)
- * Laboratoire MASI - Institut Blaise Pascal
- * Universite Pierre et Marie Curie (Paris VI)
- *
- * from
- *
- * linux/fs/minix/inode.c
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- *
- * Big-endian to little-endian byte-swapping/bitmaps by
- * David S. Miller (davem@caip.rutgers.edu), 1995
- */
-
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/fs.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/locks.h>
-#include <asm/uaccess.h>
-
-
-
-static char error_buf[1024];
-
-void ext2_error (struct super_block * sb, const char * function,
- const char * fmt, ...)
-{
- va_list args;
-
- if (!(sb->s_flags & MS_RDONLY)) {
- sb->u.ext2_sb.s_mount_state |= EXT2_ERROR_FS;
- sb->u.ext2_sb.s_es->s_state =
- cpu_to_le16(le16_to_cpu(sb->u.ext2_sb.s_es->s_state) | EXT2_ERROR_FS);
- mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
- sb->s_dirt = 1;
- }
- va_start (args, fmt);
- vsprintf (error_buf, fmt, args);
- va_end (args);
- if (test_opt (sb, ERRORS_PANIC) ||
- (le16_to_cpu(sb->u.ext2_sb.s_es->s_errors) == EXT2_ERRORS_PANIC &&
- !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_RO)))
- panic ("EXT2-fs panic (device %s): %s: %s\n",
- bdevname(sb->s_dev), function, error_buf);
- printk (KERN_CRIT "EXT2-fs error (device %s): %s: %s\n",
- bdevname(sb->s_dev), function, error_buf);
- if (test_opt (sb, ERRORS_RO) ||
- (le16_to_cpu(sb->u.ext2_sb.s_es->s_errors) == EXT2_ERRORS_RO &&
- !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_PANIC))) {
- printk ("Remounting filesystem read-only\n");
- sb->s_flags |= MS_RDONLY;
- }
-}
-
-NORET_TYPE void ext2_panic (struct super_block * sb, const char * function,
- const char * fmt, ...)
-{
- va_list args;
-
- if (!(sb->s_flags & MS_RDONLY)) {
- sb->u.ext2_sb.s_mount_state |= EXT2_ERROR_FS;
- sb->u.ext2_sb.s_es->s_state =
- cpu_to_le16(le16_to_cpu(sb->u.ext2_sb.s_es->s_state) | EXT2_ERROR_FS);
- mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
- sb->s_dirt = 1;
- }
- va_start (args, fmt);
- vsprintf (error_buf, fmt, args);
- va_end (args);
- /* this is to prevent panic from syncing this filesystem */
- if (sb->s_lock)
- sb->s_lock=0;
- sb->s_flags |= MS_RDONLY;
- panic ("EXT2-fs panic (device %s): %s: %s\n",
- bdevname(sb->s_dev), function, error_buf);
-}
-
-void ext2_warning (struct super_block * sb, const char * function,
- const char * fmt, ...)
-{
- va_list args;
-
- va_start (args, fmt);
- vsprintf (error_buf, fmt, args);
- va_end (args);
- printk (KERN_WARNING "EXT2-fs warning (device %s): %s: %s\n",
- bdevname(sb->s_dev), function, error_buf);
-}
-
-void ext2_put_super (struct super_block * sb)
-{
- int db_count;
- int i;
-
- if (!(sb->s_flags & MS_RDONLY)) {
- sb->u.ext2_sb.s_es->s_state = le16_to_cpu(sb->u.ext2_sb.s_mount_state);
- mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
- }
- db_count = sb->u.ext2_sb.s_db_per_group;
- for (i = 0; i < db_count; i++)
- if (sb->u.ext2_sb.s_group_desc[i])
- brelse (sb->u.ext2_sb.s_group_desc[i]);
- kfree_s (sb->u.ext2_sb.s_group_desc,
- db_count * sizeof (struct buffer_head *));
- for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++)
- if (sb->u.ext2_sb.s_inode_bitmap[i])
- brelse (sb->u.ext2_sb.s_inode_bitmap[i]);
- for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++)
- if (sb->u.ext2_sb.s_block_bitmap[i])
- brelse (sb->u.ext2_sb.s_block_bitmap[i]);
- brelse (sb->u.ext2_sb.s_sbh);
-
- MOD_DEC_USE_COUNT;
- return;
-}
-
-static struct super_operations ext2_sops = {
- ext2_read_inode,
- ext2_write_inode,
- ext2_put_inode,
- ext2_delete_inode,
- NULL,
- ext2_put_super,
- ext2_write_super,
- ext2_statfs,
- ext2_remount
-};
-
-/*
- * This function has been shamelessly adapted from the msdos fs
- */
-static int parse_options (char * options, unsigned long * sb_block,
- unsigned short *resuid, unsigned short * resgid,
- unsigned long * mount_options)
-{
- char * this_char;
- char * value;
-
- if (!options)
- return 1;
- for (this_char = strtok (options, ",");
- this_char != NULL;
- this_char = strtok (NULL, ",")) {
- if ((value = strchr (this_char, '=')) != NULL)
- *value++ = 0;
- if (!strcmp (this_char, "bsddf"))
- clear_opt (*mount_options, MINIX_DF);
- else if (!strcmp (this_char, "check")) {
- if (!value || !*value)
- set_opt (*mount_options, CHECK_NORMAL);
- else if (!strcmp (value, "none")) {
- clear_opt (*mount_options, CHECK_NORMAL);
- clear_opt (*mount_options, CHECK_STRICT);
- }
- else if (!strcmp (value, "normal"))
- set_opt (*mount_options, CHECK_NORMAL);
- else if (!strcmp (value, "strict")) {
- set_opt (*mount_options, CHECK_NORMAL);
- set_opt (*mount_options, CHECK_STRICT);
- }
- else {
- printk ("EXT2-fs: Invalid check option: %s\n",
- value);
- return 0;
- }
- }
- else if (!strcmp (this_char, "debug"))
- set_opt (*mount_options, DEBUG);
- else if (!strcmp (this_char, "errors")) {
- if (!value || !*value) {
- printk ("EXT2-fs: the errors option requires "
- "an argument");
- return 0;
- }
- if (!strcmp (value, "continue")) {
- clear_opt (*mount_options, ERRORS_RO);
- clear_opt (*mount_options, ERRORS_PANIC);
- set_opt (*mount_options, ERRORS_CONT);
- }
- else if (!strcmp (value, "remount-ro")) {
- clear_opt (*mount_options, ERRORS_CONT);
- clear_opt (*mount_options, ERRORS_PANIC);
- set_opt (*mount_options, ERRORS_RO);
- }
- else if (!strcmp (value, "panic")) {
- clear_opt (*mount_options, ERRORS_CONT);
- clear_opt (*mount_options, ERRORS_RO);
- set_opt (*mount_options, ERRORS_PANIC);
- }
- else {
- printk ("EXT2-fs: Invalid errors option: %s\n",
- value);
- return 0;
- }
- }
- else if (!strcmp (this_char, "grpid") ||
- !strcmp (this_char, "bsdgroups"))
- set_opt (*mount_options, GRPID);
- else if (!strcmp (this_char, "minixdf"))
- set_opt (*mount_options, MINIX_DF);
- else if (!strcmp (this_char, "nocheck")) {
- clear_opt (*mount_options, CHECK_NORMAL);
- clear_opt (*mount_options, CHECK_STRICT);
- }
- else if (!strcmp (this_char, "nogrpid") ||
- !strcmp (this_char, "sysvgroups"))
- clear_opt (*mount_options, GRPID);
- else if (!strcmp (this_char, "resgid")) {
- if (!value || !*value) {
- printk ("EXT2-fs: the resgid option requires "
- "an argument");
- return 0;
- }
- *resgid = simple_strtoul (value, &value, 0);
- if (*value) {
- printk ("EXT2-fs: Invalid resgid option: %s\n",
- value);
- return 0;
- }
- }
- else if (!strcmp (this_char, "resuid")) {
- if (!value || !*value) {
- printk ("EXT2-fs: the resuid option requires "
- "an argument");
- return 0;
- }
- *resuid = simple_strtoul (value, &value, 0);
- if (*value) {
- printk ("EXT2-fs: Invalid resuid option: %s\n",
- value);
- return 0;
- }
- }
- else if (!strcmp (this_char, "sb")) {
- if (!value || !*value) {
- printk ("EXT2-fs: the sb option requires "
- "an argument");
- return 0;
- }
- *sb_block = simple_strtoul (value, &value, 0);
- if (*value) {
- printk ("EXT2-fs: Invalid sb option: %s\n",
- value);
- return 0;
- }
- }
- /* Silently ignore the quota options */
- else if (!strcmp (this_char, "grpquota")
- || !strcmp (this_char, "noquota")
- || !strcmp (this_char, "quota")
- || !strcmp (this_char, "usrquota"))
- /* Don't do anything ;-) */ ;
- else {
- printk ("EXT2-fs: Unrecognized mount option %s\n", this_char);
- return 0;
- }
- }
- return 1;
-}
-
-static void ext2_setup_super (struct super_block * sb,
- struct ext2_super_block * es)
-{
- if (le32_to_cpu(es->s_rev_level) > EXT2_MAX_SUPP_REV) {
- printk ("EXT2-fs warning: revision level too high, "
- "forcing read/only mode\n");
- sb->s_flags |= MS_RDONLY;
- }
- if (!(sb->s_flags & MS_RDONLY)) {
- if (!(sb->u.ext2_sb.s_mount_state & EXT2_VALID_FS))
- printk ("EXT2-fs warning: mounting unchecked fs, "
- "running e2fsck is recommended\n");
- else if ((sb->u.ext2_sb.s_mount_state & EXT2_ERROR_FS))
- printk ("EXT2-fs warning: mounting fs with errors, "
- "running e2fsck is recommended\n");
- else if ((__s16) le16_to_cpu(es->s_max_mnt_count) >= 0 &&
- le16_to_cpu(es->s_mnt_count) >=
- (unsigned short) (__s16) le16_to_cpu(es->s_max_mnt_count))
- printk ("EXT2-fs warning: maximal mount count reached, "
- "running e2fsck is recommended\n");
- else if (le32_to_cpu(es->s_checkinterval) &&
- (le32_to_cpu(es->s_lastcheck) + le32_to_cpu(es->s_checkinterval) <= CURRENT_TIME))
- printk ("EXT2-fs warning: checktime reached, "
- "running e2fsck is recommended\n");
- es->s_state = cpu_to_le16(le16_to_cpu(es->s_state) & ~EXT2_VALID_FS);
- if (!(__s16) le16_to_cpu(es->s_max_mnt_count))
- es->s_max_mnt_count = (__s16) cpu_to_le16(EXT2_DFL_MAX_MNT_COUNT);
- es->s_mnt_count=cpu_to_le16(le16_to_cpu(es->s_mnt_count) + 1);
- es->s_mtime = cpu_to_le32(CURRENT_TIME);
- mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
- sb->s_dirt = 1;
- if (test_opt (sb, DEBUG))
- printk ("[EXT II FS %s, %s, bs=%lu, fs=%lu, gc=%lu, "
- "bpg=%lu, ipg=%lu, mo=%04lx]\n",
- EXT2FS_VERSION, EXT2FS_DATE, sb->s_blocksize,
- sb->u.ext2_sb.s_frag_size,
- sb->u.ext2_sb.s_groups_count,
- EXT2_BLOCKS_PER_GROUP(sb),
- EXT2_INODES_PER_GROUP(sb),
- sb->u.ext2_sb.s_mount_opt);
- if (test_opt (sb, CHECK)) {
- ext2_check_blocks_bitmap (sb);
- ext2_check_inodes_bitmap (sb);
- }
- }
-#if 0 /* ibasket's still have unresolved bugs... -DaveM */
-
- /* [T. Schoebel-Theuer] This limit should be maintained on disk.
- * This is just provisionary.
- */
- sb->s_ibasket_max = 100;
-#endif
-}
-
-static int ext2_check_descriptors (struct super_block * sb)
-{
- int i;
- int desc_block = 0;
- unsigned long block = le32_to_cpu(sb->u.ext2_sb.s_es->s_first_data_block);
- struct ext2_group_desc * gdp = NULL;
-
- ext2_debug ("Checking group descriptors");
-
- for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++)
- {
- if ((i % EXT2_DESC_PER_BLOCK(sb)) == 0)
- gdp = (struct ext2_group_desc *) sb->u.ext2_sb.s_group_desc[desc_block++]->b_data;
- if (le32_to_cpu(gdp->bg_block_bitmap) < block ||
- le32_to_cpu(gdp->bg_block_bitmap) >= block + EXT2_BLOCKS_PER_GROUP(sb))
- {
- ext2_error (sb, "ext2_check_descriptors",
- "Block bitmap for group %d"
- " not in group (block %lu)!",
- i, (unsigned long) le32_to_cpu(gdp->bg_block_bitmap));
- return 0;
- }
- if (le32_to_cpu(gdp->bg_inode_bitmap) < block ||
- le32_to_cpu(gdp->bg_inode_bitmap) >= block + EXT2_BLOCKS_PER_GROUP(sb))
- {
- ext2_error (sb, "ext2_check_descriptors",
- "Inode bitmap for group %d"
- " not in group (block %lu)!",
- i, (unsigned long) le32_to_cpu(gdp->bg_inode_bitmap));
- return 0;
- }
- if (le32_to_cpu(gdp->bg_inode_table) < block ||
- le32_to_cpu(gdp->bg_inode_table) + sb->u.ext2_sb.s_itb_per_group >=
- block + EXT2_BLOCKS_PER_GROUP(sb))
- {
- ext2_error (sb, "ext2_check_descriptors",
- "Inode table for group %d"
- " not in group (block %lu)!",
- i, (unsigned long) le32_to_cpu(gdp->bg_inode_table));
- return 0;
- }
- block += EXT2_BLOCKS_PER_GROUP(sb);
- gdp++;
- }
- return 1;
-}
-
-#define log2(n) ffz(~(n))
-
-struct super_block * ext2_read_super (struct super_block * sb, void * data,
- int silent)
-{
- struct buffer_head * bh;
- struct ext2_super_block * es;
- unsigned long sb_block = 1;
- unsigned short resuid = EXT2_DEF_RESUID;
- unsigned short resgid = EXT2_DEF_RESGID;
- unsigned long logic_sb_block = 1;
- unsigned long offset = 0;
- kdev_t dev = sb->s_dev;
- int blocksize = BLOCK_SIZE;
- int hblock;
- int db_count;
- int i, j;
-
- /*
- * See what the current blocksize for the device is, and
- * use that as the blocksize. Otherwise (or if the blocksize
- * is smaller than the default) use the default.
- * This is important for devices that have a hardware
- * sectorsize that is larger than the default.
- */
- blocksize = get_hardblocksize(dev);
- if( blocksize == 0 || blocksize < BLOCK_SIZE )
- {
- blocksize = BLOCK_SIZE;
- }
-
- sb->u.ext2_sb.s_mount_opt = 0;
- set_opt (sb->u.ext2_sb.s_mount_opt, CHECK_NORMAL);
- if (!parse_options ((char *) data, &sb_block, &resuid, &resgid,
- &sb->u.ext2_sb.s_mount_opt)) {
- sb->s_dev = 0;
- return NULL;
- }
-
- MOD_INC_USE_COUNT;
- lock_super (sb);
- set_blocksize (dev, blocksize);
-
- /*
- * If the superblock doesn't start on a sector boundary,
- * calculate the offset. FIXME(eric) this doesn't make sense
- * that we would have to do this.
- */
- if (blocksize != BLOCK_SIZE) {
- logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize;
- offset = (sb_block*BLOCK_SIZE) % blocksize;
- }
-
- if (!(bh = bread (dev, logic_sb_block, blocksize))) {
- sb->s_dev = 0;
- unlock_super (sb);
- printk ("EXT2-fs: unable to read superblock\n");
- MOD_DEC_USE_COUNT;
- return NULL;
- }
- /*
- * Note: s_es must be initialized s_es as soon as possible because
- * some ext2 macro-instructions depend on its value
- */
- es = (struct ext2_super_block *) (((char *)bh->b_data) + offset);
- sb->u.ext2_sb.s_es = es;
- sb->s_magic = le16_to_cpu(es->s_magic);
- if (sb->s_magic != EXT2_SUPER_MAGIC) {
- if (!silent)
- printk ("VFS: Can't find an ext2 filesystem on dev "
- "%s.\n", bdevname(dev));
- failed_mount:
- sb->s_dev = 0;
- unlock_super (sb);
- if (bh)
- brelse(bh);
- MOD_DEC_USE_COUNT;
- return NULL;
- }
- if (le32_to_cpu(es->s_rev_level) > EXT2_GOOD_OLD_REV) {
- if (le32_to_cpu(es->s_feature_incompat) & ~EXT2_FEATURE_INCOMPAT_SUPP) {
- printk("EXT2-fs: %s: couldn't mount because of "
- "unsupported optional features.\n",
- bdevname(dev));
- goto failed_mount;
- }
- if (!(sb->s_flags & MS_RDONLY) &&
- (le32_to_cpu(es->s_feature_ro_compat) & ~EXT2_FEATURE_RO_COMPAT_SUPP)) {
- printk("EXT2-fs: %s: couldn't mount RDWR because of "
- "unsupported optional features.\n",
- bdevname(dev));
- goto failed_mount;
- }
- }
- sb->s_blocksize_bits = le32_to_cpu(sb->u.ext2_sb.s_es->s_log_block_size) + 10;
- sb->s_blocksize = 1 << sb->s_blocksize_bits;
- if (sb->s_blocksize != BLOCK_SIZE &&
- (sb->s_blocksize == 1024 || sb->s_blocksize == 2048 ||
- sb->s_blocksize == 4096)) {
- /*
- * Make sure the blocksize for the filesystem is larger
- * than the hardware sectorsize for the machine.
- */
- hblock = get_hardblocksize(dev);
- if( (hblock != 0)
- && (sb->s_blocksize < hblock) )
- {
- printk("EXT2-fs: blocksize too small for device.\n");
- goto failed_mount;
- }
-
- brelse (bh);
- set_blocksize (dev, sb->s_blocksize);
- logic_sb_block = (sb_block*BLOCK_SIZE) / sb->s_blocksize;
- offset = (sb_block*BLOCK_SIZE) % sb->s_blocksize;
- bh = bread (dev, logic_sb_block, sb->s_blocksize);
- if(!bh) {
- printk("EXT2-fs: Couldn't read superblock on "
- "2nd try.\n");
- goto failed_mount;
- }
- es = (struct ext2_super_block *) (((char *)bh->b_data) + offset);
- sb->u.ext2_sb.s_es = es;
- if (es->s_magic != le16_to_cpu(EXT2_SUPER_MAGIC)) {
- printk ("EXT2-fs: Magic mismatch, very weird !\n");
- goto failed_mount;
- }
- }
- if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV) {
- sb->u.ext2_sb.s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;
- sb->u.ext2_sb.s_first_ino = EXT2_GOOD_OLD_FIRST_INO;
- } else {
- sb->u.ext2_sb.s_inode_size = le16_to_cpu(es->s_inode_size);
- sb->u.ext2_sb.s_first_ino = le32_to_cpu(es->s_first_ino);
- if (sb->u.ext2_sb.s_inode_size != EXT2_GOOD_OLD_INODE_SIZE) {
- printk ("EXT2-fs: unsupported inode size: %d\n",
- sb->u.ext2_sb.s_inode_size);
- goto failed_mount;
- }
- }
- sb->u.ext2_sb.s_feature_compat = le32_to_cpu(es->s_feature_compat);
- sb->u.ext2_sb.s_feature_incompat = le32_to_cpu(es->s_feature_incompat);
- sb->u.ext2_sb.s_feature_ro_compat = le32_to_cpu(es->s_feature_ro_compat);
- sb->u.ext2_sb.s_frag_size = EXT2_MIN_FRAG_SIZE <<
- le32_to_cpu(es->s_log_frag_size);
- if (sb->u.ext2_sb.s_frag_size)
- sb->u.ext2_sb.s_frags_per_block = sb->s_blocksize /
- sb->u.ext2_sb.s_frag_size;
- else
- sb->s_magic = 0;
- sb->u.ext2_sb.s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group);
- sb->u.ext2_sb.s_frags_per_group = le32_to_cpu(es->s_frags_per_group);
- sb->u.ext2_sb.s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group);
- sb->u.ext2_sb.s_inodes_per_block = sb->s_blocksize /
- EXT2_INODE_SIZE(sb);
- sb->u.ext2_sb.s_itb_per_group = sb->u.ext2_sb.s_inodes_per_group /
- sb->u.ext2_sb.s_inodes_per_block;
- sb->u.ext2_sb.s_desc_per_block = sb->s_blocksize /
- sizeof (struct ext2_group_desc);
- sb->u.ext2_sb.s_sbh = bh;
- if (resuid != EXT2_DEF_RESUID)
- sb->u.ext2_sb.s_resuid = resuid;
- else
- sb->u.ext2_sb.s_resuid = le16_to_cpu(es->s_def_resuid);
- if (resgid != EXT2_DEF_RESGID)
- sb->u.ext2_sb.s_resgid = resgid;
- else
- sb->u.ext2_sb.s_resgid = le16_to_cpu(es->s_def_resgid);
- sb->u.ext2_sb.s_mount_state = le16_to_cpu(es->s_state);
- sb->u.ext2_sb.s_addr_per_block_bits =
- log2 (EXT2_ADDR_PER_BLOCK(sb));
- sb->u.ext2_sb.s_desc_per_block_bits =
- log2 (EXT2_DESC_PER_BLOCK(sb));
- if (sb->s_magic != EXT2_SUPER_MAGIC) {
- if (!silent)
- printk ("VFS: Can't find an ext2 filesystem on dev "
- "%s.\n",
- bdevname(dev));
- goto failed_mount;
- }
- if (sb->s_blocksize != bh->b_size) {
- if (!silent)
- printk ("VFS: Unsupported blocksize on dev "
- "%s.\n", bdevname(dev));
- goto failed_mount;
- }
-
- if (sb->s_blocksize != sb->u.ext2_sb.s_frag_size) {
- printk ("EXT2-fs: fragsize %lu != blocksize %lu (not supported yet)\n",
- sb->u.ext2_sb.s_frag_size, sb->s_blocksize);
- goto failed_mount;
- }
-
- if (sb->u.ext2_sb.s_blocks_per_group > sb->s_blocksize * 8) {
- printk ("EXT2-fs: #blocks per group too big: %lu\n",
- sb->u.ext2_sb.s_blocks_per_group);
- goto failed_mount;
- }
- if (sb->u.ext2_sb.s_frags_per_group > sb->s_blocksize * 8) {
- printk ("EXT2-fs: #fragments per group too big: %lu\n",
- sb->u.ext2_sb.s_frags_per_group);
- goto failed_mount;
- }
- if (sb->u.ext2_sb.s_inodes_per_group > sb->s_blocksize * 8) {
- printk ("EXT2-fs: #inodes per group too big: %lu\n",
- sb->u.ext2_sb.s_inodes_per_group);
- goto failed_mount;
- }
-
- sb->u.ext2_sb.s_groups_count = (le32_to_cpu(es->s_blocks_count) -
- le32_to_cpu(es->s_first_data_block) +
- EXT2_BLOCKS_PER_GROUP(sb) - 1) /
- EXT2_BLOCKS_PER_GROUP(sb);
- db_count = (sb->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) /
- EXT2_DESC_PER_BLOCK(sb);
- sb->u.ext2_sb.s_group_desc = kmalloc (db_count * sizeof (struct buffer_head *), GFP_KERNEL);
- if (sb->u.ext2_sb.s_group_desc == NULL) {
- printk ("EXT2-fs: not enough memory\n");
- goto failed_mount;
- }
- for (i = 0; i < db_count; i++) {
- sb->u.ext2_sb.s_group_desc[i] = bread (dev, logic_sb_block + i + 1,
- sb->s_blocksize);
- if (!sb->u.ext2_sb.s_group_desc[i]) {
- for (j = 0; j < i; j++)
- brelse (sb->u.ext2_sb.s_group_desc[j]);
- kfree_s (sb->u.ext2_sb.s_group_desc,
- db_count * sizeof (struct buffer_head *));
- printk ("EXT2-fs: unable to read group descriptors\n");
- goto failed_mount;
- }
- }
- if (!ext2_check_descriptors (sb)) {
- for (j = 0; j < db_count; j++)
- brelse (sb->u.ext2_sb.s_group_desc[j]);
- kfree_s (sb->u.ext2_sb.s_group_desc,
- db_count * sizeof (struct buffer_head *));
- printk ("EXT2-fs: group descriptors corrupted !\n");
- goto failed_mount;
- }
- for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++) {
- sb->u.ext2_sb.s_inode_bitmap_number[i] = 0;
- sb->u.ext2_sb.s_inode_bitmap[i] = NULL;
- sb->u.ext2_sb.s_block_bitmap_number[i] = 0;
- sb->u.ext2_sb.s_block_bitmap[i] = NULL;
- }
- sb->u.ext2_sb.s_loaded_inode_bitmaps = 0;
- sb->u.ext2_sb.s_loaded_block_bitmaps = 0;
- sb->u.ext2_sb.s_db_per_group = db_count;
- unlock_super (sb);
- /*
- * set up enough so that it can read an inode
- */
- sb->s_dev = dev;
- sb->s_op = &ext2_sops;
- sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO));
- if (!sb->s_root) {
- sb->s_dev = 0;
- for (i = 0; i < db_count; i++)
- if (sb->u.ext2_sb.s_group_desc[i])
- brelse (sb->u.ext2_sb.s_group_desc[i]);
- kfree_s (sb->u.ext2_sb.s_group_desc,
- db_count * sizeof (struct buffer_head *));
- brelse (bh);
- printk ("EXT2-fs: get root inode failed\n");
- MOD_DEC_USE_COUNT;
- return NULL;
- }
- ext2_setup_super (sb, es);
- return sb;
-}
-
-static void ext2_commit_super (struct super_block * sb,
- struct ext2_super_block * es)
-{
- es->s_wtime = cpu_to_le32(CURRENT_TIME);
- mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
- sb->s_dirt = 0;
-}
-
-/*
- * In the second extended file system, it is not necessary to
- * write the super block since we use a mapping of the
- * disk super block in a buffer.
- *
- * However, this function is still used to set the fs valid
- * flags to 0. We need to set this flag to 0 since the fs
- * may have been checked while mounted and e2fsck may have
- * set s_state to EXT2_VALID_FS after some corrections.
- */
-
-void ext2_write_super (struct super_block * sb)
-{
- struct ext2_super_block * es;
-
- if (!(sb->s_flags & MS_RDONLY)) {
- es = sb->u.ext2_sb.s_es;
-
- ext2_debug ("setting valid to 0\n");
-
- if (le16_to_cpu(es->s_state) & EXT2_VALID_FS) {
- es->s_state = cpu_to_le16(le16_to_cpu(es->s_state) & ~EXT2_VALID_FS);
- es->s_mtime = cpu_to_le32(CURRENT_TIME);
- }
- ext2_commit_super (sb, es);
- }
- sb->s_dirt = 0;
-}
-
-int ext2_remount (struct super_block * sb, int * flags, char * data)
-{
- struct ext2_super_block * es;
- unsigned short resuid = sb->u.ext2_sb.s_resuid;
- unsigned short resgid = sb->u.ext2_sb.s_resgid;
- unsigned long new_mount_opt;
- unsigned long tmp;
-
- /*
- * Allow the "check" option to be passed as a remount option.
- */
- new_mount_opt = EXT2_MOUNT_CHECK_NORMAL;
- if (!parse_options (data, &tmp, &resuid, &resgid,
- &new_mount_opt))
- return -EINVAL;
-
- sb->u.ext2_sb.s_mount_opt = new_mount_opt;
- sb->u.ext2_sb.s_resuid = resuid;
- sb->u.ext2_sb.s_resgid = resgid;
- es = sb->u.ext2_sb.s_es;
- if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
- return 0;
- if (*flags & MS_RDONLY) {
- if (le16_to_cpu(es->s_state) & EXT2_VALID_FS ||
- !(sb->u.ext2_sb.s_mount_state & EXT2_VALID_FS))
- return 0;
- /*
- * OK, we are remounting a valid rw partition rdonly, so set
- * the rdonly flag and then mark the partition as valid again.
- */
- es->s_state = cpu_to_le16(sb->u.ext2_sb.s_mount_state);
- es->s_mtime = cpu_to_le32(CURRENT_TIME);
- mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
- sb->s_dirt = 1;
- ext2_commit_super (sb, es);
- }
- else {
- /*
- * Mounting a RDONLY partition read-write, so reread and
- * store the current valid flag. (It may have been changed
- * by e2fsck since we originally mounted the partition.)
- */
- sb->u.ext2_sb.s_mount_state = le16_to_cpu(es->s_state);
- sb->s_flags &= ~MS_RDONLY;
- ext2_setup_super (sb, es);
- }
- return 0;
-}
-
-int ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz)
-{
- unsigned long overhead;
- struct statfs tmp;
- int ngroups, i;
-
- if (test_opt (sb, MINIX_DF))
- overhead = 0;
- else {
- /*
- * Compute the overhead (FS structures)
- */
-
- /*
- * All of the blocks before first_data_block are
- * overhead
- */
- overhead = le32_to_cpu(sb->u.ext2_sb.s_es->s_first_data_block);
-
- /*
- * Add the overhead attributed to the superblock and
- * block group descriptors. If this is sparse
- * superblocks is turned on, then not all groups have
- * this.
- */
- if (sb->u.ext2_sb.s_feature_ro_compat &
- EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) {
- ngroups = 0;
- for (i=0 ; i < sb->u.ext2_sb.s_groups_count; i++)
- if (ext2_group_sparse(i))
- ngroups++;
- } else
- ngroups = sb->u.ext2_sb.s_groups_count;
- overhead += ngroups * (1 + sb->u.ext2_sb.s_db_per_group);
-
- /*
- * Every block group has an inode bitmap, a block
- * bitmap, and an inode table.
- */
- overhead += (sb->u.ext2_sb.s_groups_count *
- (2 + sb->u.ext2_sb.s_itb_per_group));
- }
-
- tmp.f_type = EXT2_SUPER_MAGIC;
- tmp.f_bsize = sb->s_blocksize;
- tmp.f_blocks = le32_to_cpu(sb->u.ext2_sb.s_es->s_blocks_count) - overhead;
- tmp.f_bfree = ext2_count_free_blocks (sb);
- tmp.f_bavail = tmp.f_bfree - le32_to_cpu(sb->u.ext2_sb.s_es->s_r_blocks_count);
- if (tmp.f_bfree < le32_to_cpu(sb->u.ext2_sb.s_es->s_r_blocks_count))
- tmp.f_bavail = 0;
- tmp.f_files = le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count);
- tmp.f_ffree = ext2_count_free_inodes (sb);
- tmp.f_namelen = EXT2_NAME_LEN;
- return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
-}
-
-static struct file_system_type ext2_fs_type = {
- "ext2",
- FS_REQUIRES_DEV /* | FS_IBASKET */, /* ibaskets have unresolved bugs */
- ext2_read_super,
- NULL
-};
-
-static int __init init_ext2_fs(void)
-{
- return register_filesystem(&ext2_fs_type);
-}
-
-static int __exit exit_ext2_fs(void)
-{
- unregister_filesystem(&ext2_fs_type);
-}
-
-EXPORT_NO_SYMBOLS;
-
-module_init(init_ext2_fs)
-module_exit(exit_ext2_fs)
diff --git a/sys/gnu/ext2fs/ext2_vfsops.c b/sys/gnu/ext2fs/ext2_vfsops.c
new file mode 100644
index 0000000..d91a92e
--- /dev/null
+++ b/sys/gnu/ext2fs/ext2_vfsops.c
@@ -0,0 +1,1230 @@
+/*
+ * modified for EXT2FS support in Lites 1.1
+ *
+ * Aug 1995, Godmar Back (gback@cs.utah.edu)
+ * University of Utah, Department of Computer Science
+ */
+/*
+ * 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.8 (Berkeley) 4/18/94
+ * $FreeBSD$
+ */
+
+#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/mount.h>
+#include <sys/bio.h>
+#include <sys/buf.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/malloc.h>
+#include <sys/stat.h>
+#include <sys/mutex.h>
+
+#include <gnu/ext2fs/ext2_mount.h>
+#include <gnu/ext2fs/inode.h>
+
+#include <gnu/ext2fs/fs.h>
+#include <gnu/ext2fs/ext2_extern.h>
+#include <gnu/ext2fs/ext2_fs.h>
+#include <gnu/ext2fs/ext2_fs_sb.h>
+
+static int ext2_fhtovp(struct mount *, struct fid *, struct vnode **);
+static int ext2_flushfiles(struct mount *mp, int flags, struct thread *td);
+static int ext2_init(struct vfsconf *);
+static int ext2_mount(struct mount *, struct nameidata *, struct thread *);
+static int ext2_mountfs(struct vnode *, struct mount *, struct thread *);
+static int ext2_reload(struct mount *mountp, struct ucred *cred,
+ struct thread *td);
+static int ext2_root(struct mount *, struct vnode **vpp);
+static int ext2_sbupdate(struct ext2mount *, int);
+static int ext2_statfs(struct mount *, struct statfs *, struct thread *);
+static int ext2_sync(struct mount *, int, struct ucred *, struct thread *);
+static int ext2_uninit(struct vfsconf *);
+static int ext2_unmount(struct mount *, int, struct thread *);
+static int ext2_vget(struct mount *, ino_t, int, struct vnode **);
+static int ext2_vptofh(struct vnode *, struct fid *);
+
+MALLOC_DEFINE(M_EXT2NODE, "EXT2 node", "EXT2 vnode private part");
+static MALLOC_DEFINE(M_EXT2MNT, "EXT2 mount", "EXT2 mount structure");
+
+static struct vfsops ext2fs_vfsops = {
+ NULL,
+ vfs_stdstart,
+ ext2_unmount,
+ ext2_root, /* root inode via vget */
+ vfs_stdquotactl,
+ ext2_statfs,
+ ext2_sync,
+ ext2_vget,
+ ext2_fhtovp,
+ vfs_stdcheckexp,
+ ext2_vptofh,
+ ext2_init,
+ ext2_uninit,
+ vfs_stdextattrctl,
+ ext2_mount,
+};
+
+VFS_SET(ext2fs_vfsops, ext2fs, 0);
+#define bsd_malloc malloc
+#define bsd_free free
+
+static int ext2fs_inode_hash_lock;
+
+static int ext2_check_sb_compat(struct ext2_super_block *es, dev_t dev,
+ int ronly);
+static int compute_sb_data(struct vnode * devvp,
+ struct ext2_super_block * es, struct ext2_sb_info * fs);
+
+#ifdef notyet
+static int ext2_mountroot(void);
+
+/*
+ * Called by main() when ext2fs is going to be mounted as root.
+ *
+ * Name is updated by mount(8) after booting.
+ */
+#define ROOTNAME "root_device"
+
+static int
+ext2_mountroot()
+{
+ struct ext2_sb_info *fs;
+ struct mount *mp;
+ struct thread *td = curthread;
+ struct ext2mount *ump;
+ u_int size;
+ int error;
+
+ if ((error = bdevvp(rootdev, &rootvp))) {
+ printf("ext2_mountroot: can't find rootvp\n");
+ return (error);
+ }
+ mp = bsd_malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK);
+ bzero((char *)mp, (u_long)sizeof(struct mount));
+ TAILQ_INIT(&mp->mnt_nvnodelist);
+ TAILQ_INIT(&mp->mnt_reservedvnlist);
+ mp->mnt_op = &ext2fs_vfsops;
+ mp->mnt_flag = MNT_RDONLY;
+ if (error = ext2_mountfs(rootvp, mp, td)) {
+ bsd_free(mp, M_MOUNT);
+ return (error);
+ }
+ if (error = vfs_lock(mp)) {
+ (void)ext2_unmount(mp, 0, td);
+ bsd_free(mp, M_MOUNT);
+ return (error);
+ }
+ TAILQ_INSERT_HEAD(&mountlist, mp, mnt_list);
+ mp->mnt_flag |= MNT_ROOTFS;
+ mp->mnt_vnodecovered = NULLVP;
+ ump = VFSTOEXT2(mp);
+ fs = ump->um_e2fs;
+ bzero(fs->fs_fsmnt, sizeof(fs->fs_fsmnt));
+ fs->fs_fsmnt[0] = '/';
+ bcopy((caddr_t)fs->fs_fsmnt, (caddr_t)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)ext2_statfs(mp, &mp->mnt_stat, td);
+ vfs_unlock(mp);
+ inittodr(fs->s_es->s_wtime); /* this helps to set the time */
+ return (0);
+}
+#endif
+
+/*
+ * VFS Operations.
+ *
+ * mount system call
+ */
+static int
+ext2_mount(mp, ndp, td)
+ struct mount *mp;
+ struct nameidata *ndp;
+ struct thread *td;
+{
+ struct export_args *export;
+ struct vfsoptlist *opts;
+ struct vnode *devvp;
+ struct ext2mount *ump = 0;
+ struct ext2_sb_info *fs;
+ char *path, *fspec;
+ size_t size;
+ int error, flags, len;
+ mode_t accessmode;
+
+ opts = mp->mnt_optnew;
+
+ vfs_getopt(opts, "fspath", (void **)&path, NULL);
+ /* Double-check the length of path.. */
+ if (strlen(path) >= MAXMNTLEN - 1)
+ return (ENAMETOOLONG);
+
+ fspec = NULL;
+ error = vfs_getopt(opts, "from", (void **)&fspec, &len);
+ if (!error && fspec[len - 1] != '\0')
+ return (EINVAL);
+
+ /*
+ * 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 = VFSTOEXT2(mp);
+ fs = ump->um_e2fs;
+ error = 0;
+ if (fs->s_rd_only == 0 && (mp->mnt_flag & MNT_RDONLY)) {
+ flags = WRITECLOSE;
+ if (mp->mnt_flag & MNT_FORCE)
+ flags |= FORCECLOSE;
+ if (vfs_busy(mp, LK_NOWAIT, 0, td))
+ return (EBUSY);
+ error = ext2_flushfiles(mp, flags, td);
+ vfs_unbusy(mp, td);
+ if (!error && fs->s_wasvalid) {
+ fs->s_es->s_state |= EXT2_VALID_FS;
+ ext2_sbupdate(ump, MNT_WAIT);
+ }
+ fs->s_rd_only = 1;
+ }
+ if (!error && (mp->mnt_flag & MNT_RELOAD))
+ error = ext2_reload(mp, ndp->ni_cnd.cn_cred, td);
+ if (error)
+ return (error);
+ devvp = ump->um_devvp;
+ if (ext2_check_sb_compat(fs->s_es, devvp->v_rdev,
+ (mp->mnt_kern_flag & MNTK_WANTRDWR) == 0) != 0)
+ return (EPERM);
+ if (fs->s_rd_only && (mp->mnt_kern_flag & MNTK_WANTRDWR)) {
+ /*
+ * If upgrade to read-write by non-root, then verify
+ * that user has necessary permissions on the device.
+ */
+ if (suser(td)) {
+ vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td);
+ if ((error = VOP_ACCESS(devvp, VREAD | VWRITE,
+ td->td_ucred, td)) != 0) {
+ VOP_UNLOCK(devvp, 0, td);
+ return (error);
+ }
+ VOP_UNLOCK(devvp, 0, td);
+ }
+
+ if ((fs->s_es->s_state & EXT2_VALID_FS) == 0 ||
+ (fs->s_es->s_state & EXT2_ERROR_FS)) {
+ if (mp->mnt_flag & MNT_FORCE) {
+ printf(
+"WARNING: %s was not properly dismounted\n",
+ fs->fs_fsmnt);
+ } else {
+ printf(
+"WARNING: R/W mount of %s denied. Filesystem is not clean - run fsck\n",
+ fs->fs_fsmnt);
+ return (EPERM);
+ }
+ }
+ fs->s_es->s_state &= ~EXT2_VALID_FS;
+ ext2_sbupdate(ump, MNT_WAIT);
+ fs->s_rd_only = 0;
+ }
+ if (fspec == NULL) {
+ error = vfs_getopt(opts, "export", (void **)&export,
+ &len);
+ if (error || len != sizeof(struct export_args))
+ return (EINVAL);
+ /* Process export requests. */
+ return (vfs_export(mp, export));
+ }
+ }
+ /*
+ * Not an update, or updating the name: look up the name
+ * and verify that it refers to a sensible block device.
+ */
+ if (fspec == NULL)
+ return (EINVAL);
+ NDINIT(ndp, LOOKUP, FOLLOW, UIO_SYSSPACE, fspec, td);
+ if ((error = namei(ndp)) != 0)
+ return (error);
+ NDFREE(ndp, NDF_ONLY_PNBUF);
+ devvp = ndp->ni_vp;
+
+ if (!vn_isdisk(devvp, &error)) {
+ vrele(devvp);
+ return (error);
+ }
+
+ /*
+ * If mount by non-root, then verify that user has necessary
+ * permissions on the device.
+ */
+ if (suser(td)) {
+ accessmode = VREAD;
+ if ((mp->mnt_flag & MNT_RDONLY) == 0)
+ accessmode |= VWRITE;
+ vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td);
+ if ((error = VOP_ACCESS(devvp, accessmode, td->td_ucred, td)) != 0) {
+ vput(devvp);
+ return (error);
+ }
+ VOP_UNLOCK(devvp, 0, td);
+ }
+
+ if ((mp->mnt_flag & MNT_UPDATE) == 0) {
+ error = ext2_mountfs(devvp, mp, td);
+ } else {
+ if (devvp != ump->um_devvp)
+ error = EINVAL; /* needs translation */
+ else
+ vrele(devvp);
+ }
+ if (error) {
+ vrele(devvp);
+ return (error);
+ }
+ ump = VFSTOEXT2(mp);
+ fs = ump->um_e2fs;
+ /*
+ * Note that this strncpy() is ok because of a check at the start
+ * of ext2_mount().
+ */
+ strncpy(fs->fs_fsmnt, path, MAXMNTLEN);
+ fs->fs_fsmnt[MAXMNTLEN - 1] = '\0';
+ (void)copystr(fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+ (void)ext2_statfs(mp, &mp->mnt_stat, td);
+ return (0);
+}
+
+/*
+ * checks that the data in the descriptor blocks make sense
+ * this is taken from ext2/super.c
+ */
+static int ext2_check_descriptors (struct ext2_sb_info * sb)
+{
+ int i;
+ int desc_block = 0;
+ unsigned long block = sb->s_es->s_first_data_block;
+ struct ext2_group_desc * gdp = NULL;
+
+ /* ext2_debug ("Checking group descriptors"); */
+
+ for (i = 0; i < sb->s_groups_count; i++)
+ {
+ /* examine next descriptor block */
+ if ((i % EXT2_DESC_PER_BLOCK(sb)) == 0)
+ gdp = (struct ext2_group_desc *)
+ sb->s_group_desc[desc_block++]->b_data;
+ if (gdp->bg_block_bitmap < block ||
+ gdp->bg_block_bitmap >= block + EXT2_BLOCKS_PER_GROUP(sb))
+ {
+ printf ("ext2_check_descriptors: "
+ "Block bitmap for group %d"
+ " not in group (block %lu)!\n",
+ i, (unsigned long) gdp->bg_block_bitmap);
+ return 0;
+ }
+ if (gdp->bg_inode_bitmap < block ||
+ gdp->bg_inode_bitmap >= block + EXT2_BLOCKS_PER_GROUP(sb))
+ {
+ printf ("ext2_check_descriptors: "
+ "Inode bitmap for group %d"
+ " not in group (block %lu)!\n",
+ i, (unsigned long) gdp->bg_inode_bitmap);
+ return 0;
+ }
+ if (gdp->bg_inode_table < block ||
+ gdp->bg_inode_table + sb->s_itb_per_group >=
+ block + EXT2_BLOCKS_PER_GROUP(sb))
+ {
+ printf ("ext2_check_descriptors: "
+ "Inode table for group %d"
+ " not in group (block %lu)!\n",
+ i, (unsigned long) gdp->bg_inode_table);
+ return 0;
+ }
+ block += EXT2_BLOCKS_PER_GROUP(sb);
+ gdp++;
+ }
+ return 1;
+}
+
+static int
+ext2_check_sb_compat(es, dev, ronly)
+ struct ext2_super_block *es;
+ dev_t dev;
+ int ronly;
+{
+
+ if (es->s_magic != EXT2_SUPER_MAGIC) {
+ printf("ext2fs: %s: wrong magic number %#x (expected %#x)\n",
+ devtoname(dev), es->s_magic, EXT2_SUPER_MAGIC);
+ return (1);
+ }
+ if (es->s_rev_level > EXT2_GOOD_OLD_REV) {
+ if (es->s_feature_incompat & ~EXT2_FEATURE_INCOMPAT_SUPP) {
+ printf(
+"WARNING: mount of %s denied due to unsupported optional features\n",
+ devtoname(dev));
+ return (1);
+ }
+ if (!ronly &&
+ (es->s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPP)) {
+ printf(
+"WARNING: R/W mount of %s denied due to unsupported optional features\n",
+ devtoname(dev));
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * this computes the fields of the ext2_sb_info structure from the
+ * data in the ext2_super_block structure read in
+ */
+static int compute_sb_data(devvp, es, fs)
+ struct vnode * devvp;
+ struct ext2_super_block * es;
+ struct ext2_sb_info * fs;
+{
+ int db_count, error;
+ int i, j;
+ int logic_sb_block = 1; /* XXX for now */
+
+#if 1
+#define V(v)
+#else
+#define V(v) printf(#v"= %d\n", fs->v);
+#endif
+
+ fs->s_blocksize = EXT2_MIN_BLOCK_SIZE << es->s_log_block_size;
+ V(s_blocksize)
+ fs->s_bshift = EXT2_MIN_BLOCK_LOG_SIZE + es->s_log_block_size;
+ V(s_bshift)
+ fs->s_fsbtodb = es->s_log_block_size + 1;
+ V(s_fsbtodb)
+ fs->s_qbmask = fs->s_blocksize - 1;
+ V(s_bmask)
+ fs->s_blocksize_bits = EXT2_BLOCK_SIZE_BITS(es);
+ V(s_blocksize_bits)
+ fs->s_frag_size = EXT2_MIN_FRAG_SIZE << es->s_log_frag_size;
+ V(s_frag_size)
+ if (fs->s_frag_size)
+ fs->s_frags_per_block = fs->s_blocksize / fs->s_frag_size;
+ V(s_frags_per_block)
+ fs->s_blocks_per_group = es->s_blocks_per_group;
+ V(s_blocks_per_group)
+ fs->s_frags_per_group = es->s_frags_per_group;
+ V(s_frags_per_group)
+ fs->s_inodes_per_group = es->s_inodes_per_group;
+ V(s_inodes_per_group)
+ fs->s_inodes_per_block = fs->s_blocksize / EXT2_INODE_SIZE;
+ V(s_inodes_per_block)
+ fs->s_itb_per_group = fs->s_inodes_per_group /fs->s_inodes_per_block;
+ V(s_itb_per_group)
+ fs->s_desc_per_block = fs->s_blocksize / sizeof (struct ext2_group_desc);
+ V(s_desc_per_block)
+ /* s_resuid / s_resgid ? */
+ fs->s_groups_count = (es->s_blocks_count -
+ es->s_first_data_block +
+ EXT2_BLOCKS_PER_GROUP(fs) - 1) /
+ EXT2_BLOCKS_PER_GROUP(fs);
+ V(s_groups_count)
+ db_count = (fs->s_groups_count + EXT2_DESC_PER_BLOCK(fs) - 1) /
+ EXT2_DESC_PER_BLOCK(fs);
+ fs->s_db_per_group = db_count;
+ V(s_db_per_group)
+
+ fs->s_group_desc = bsd_malloc(db_count * sizeof (struct buf *),
+ M_EXT2MNT, M_WAITOK);
+
+ /* adjust logic_sb_block */
+ if(fs->s_blocksize > SBSIZE)
+ /* Godmar thinks: if the blocksize is greater than 1024, then
+ the superblock is logically part of block zero.
+ */
+ logic_sb_block = 0;
+
+ for (i = 0; i < db_count; i++) {
+ error = bread(devvp , fsbtodb(fs, logic_sb_block + i + 1),
+ fs->s_blocksize, NOCRED, &fs->s_group_desc[i]);
+ if(error) {
+ for (j = 0; j < i; j++)
+ brelse(fs->s_group_desc[j]);
+ bsd_free(fs->s_group_desc, M_EXT2MNT);
+ printf("EXT2-fs: unable to read group descriptors (%d)\n", error);
+ return EIO;
+ }
+ /* Set the B_LOCKED flag on the buffer, then brelse() it */
+ LCK_BUF(fs->s_group_desc[i])
+ }
+ if(!ext2_check_descriptors(fs)) {
+ for (j = 0; j < db_count; j++)
+ ULCK_BUF(fs->s_group_desc[j])
+ bsd_free(fs->s_group_desc, M_EXT2MNT);
+ printf("EXT2-fs: (ext2_check_descriptors failure) "
+ "unable to read group descriptors\n");
+ return EIO;
+ }
+
+ for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++) {
+ fs->s_inode_bitmap_number[i] = 0;
+ fs->s_inode_bitmap[i] = NULL;
+ fs->s_block_bitmap_number[i] = 0;
+ fs->s_block_bitmap[i] = NULL;
+ }
+ fs->s_loaded_inode_bitmaps = 0;
+ fs->s_loaded_block_bitmaps = 0;
+ 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.
+ */
+static int
+ext2_reload(mountp, cred, td)
+ struct mount *mountp;
+ struct ucred *cred;
+ struct thread *td;
+{
+ struct vnode *vp, *nvp, *devvp;
+ struct inode *ip;
+ struct buf *bp;
+ struct ext2_super_block * es;
+ struct ext2_sb_info *fs;
+ int error;
+
+ if ((mountp->mnt_flag & MNT_RDONLY) == 0)
+ return (EINVAL);
+ /*
+ * Step 1: invalidate all cached meta-data.
+ */
+ devvp = VFSTOEXT2(mountp)->um_devvp;
+ if (vinvalbuf(devvp, 0, cred, td, 0, 0))
+ panic("ext2_reload: dirty1");
+ /*
+ * Step 2: re-read superblock from disk.
+ * constants have been adjusted for ext2
+ */
+ if ((error = bread(devvp, SBLOCK, SBSIZE, NOCRED, &bp)) != 0)
+ return (error);
+ es = (struct ext2_super_block *)bp->b_data;
+ if (ext2_check_sb_compat(es, devvp->v_rdev, 0) != 0) {
+ brelse(bp);
+ return (EIO); /* XXX needs translation */
+ }
+ fs = VFSTOEXT2(mountp)->um_e2fs;
+ bcopy(bp->b_data, fs->s_es, sizeof(struct ext2_super_block));
+
+ if((error = compute_sb_data(devvp, es, fs)) != 0) {
+ brelse(bp);
+ return error;
+ }
+#ifdef UNKLAR
+ if (fs->fs_sbsize < SBSIZE)
+ bp->b_flags |= B_INVAL;
+#endif
+ brelse(bp);
+
+loop:
+ mtx_lock(&mntvnode_mtx);
+ for (vp = TAILQ_FIRST(&mountp->mnt_nvnodelist); vp != NULL; vp = nvp) {
+ if (vp->v_mount != mountp) {
+ mtx_unlock(&mntvnode_mtx);
+ goto loop;
+ }
+ nvp = TAILQ_NEXT(vp, v_nmntvnodes);
+ mtx_unlock(&mntvnode_mtx);
+ /*
+ * Step 4: invalidate all inactive vnodes.
+ */
+ if (vrecycle(vp, NULL, td))
+ goto loop;
+ /*
+ * Step 5: invalidate all cached file data.
+ */
+ mtx_lock(&vp->v_interlock);
+ if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
+ goto loop;
+ }
+ if (vinvalbuf(vp, 0, cred, td, 0, 0))
+ panic("ext2_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->s_blocksize, NOCRED, &bp);
+ if (error) {
+ vput(vp);
+ return (error);
+ }
+ ext2_ei2i((struct ext2_inode *) ((char *)bp->b_data +
+ EXT2_INODE_SIZE * ino_to_fsbo(fs, ip->i_number)), ip);
+ brelse(bp);
+ vput(vp);
+ mtx_lock(&mntvnode_mtx);
+ }
+ mtx_unlock(&mntvnode_mtx);
+ return (0);
+}
+
+/*
+ * Common code for mount and mountroot
+ */
+static int
+ext2_mountfs(devvp, mp, td)
+ struct vnode *devvp;
+ struct mount *mp;
+ struct thread *td;
+{
+ struct ext2mount *ump;
+ struct buf *bp;
+ struct ext2_sb_info *fs;
+ struct ext2_super_block * es;
+ dev_t dev = devvp->v_rdev;
+ int error;
+ int ronly;
+
+ /*
+ * 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, td->td_ucred, td, 0, 0)) != 0)
+ return (error);
+#ifdef READONLY
+/* turn on this to force it to be read-only */
+ mp->mnt_flag |= MNT_RDONLY;
+#endif
+
+ ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
+ vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td);
+ error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, td);
+ VOP_UNLOCK(devvp, 0, td);
+ if (error)
+ return (error);
+ if (devvp->v_rdev->si_iosize_max != 0)
+ mp->mnt_iosize_max = devvp->v_rdev->si_iosize_max;
+ if (mp->mnt_iosize_max > MAXPHYS)
+ mp->mnt_iosize_max = MAXPHYS;
+
+ bp = NULL;
+ ump = NULL;
+ if ((error = bread(devvp, SBLOCK, SBSIZE, NOCRED, &bp)) != 0)
+ goto out;
+ es = (struct ext2_super_block *)bp->b_data;
+ if (ext2_check_sb_compat(es, dev, ronly) != 0) {
+ error = EINVAL; /* XXX needs translation */
+ goto out;
+ }
+ if ((es->s_state & EXT2_VALID_FS) == 0 ||
+ (es->s_state & EXT2_ERROR_FS)) {
+ if (ronly || (mp->mnt_flag & MNT_FORCE)) {
+ printf(
+"WARNING: Filesystem was not properly dismounted\n");
+ } else {
+ printf(
+"WARNING: R/W mount denied. Filesystem is not clean - run fsck\n");
+ error = EPERM;
+ goto out;
+ }
+ }
+ ump = bsd_malloc(sizeof *ump, M_EXT2MNT, M_WAITOK);
+ bzero((caddr_t)ump, sizeof *ump);
+ /* I don't know whether this is the right strategy. Note that
+ we dynamically allocate both an ext2_sb_info and an ext2_super_block
+ while Linux keeps the super block in a locked buffer
+ */
+ ump->um_e2fs = bsd_malloc(sizeof(struct ext2_sb_info),
+ M_EXT2MNT, M_WAITOK);
+ ump->um_e2fs->s_es = bsd_malloc(sizeof(struct ext2_super_block),
+ M_EXT2MNT, M_WAITOK);
+ bcopy(es, ump->um_e2fs->s_es, (u_int)sizeof(struct ext2_super_block));
+ if ((error = compute_sb_data(devvp, ump->um_e2fs->s_es, ump->um_e2fs)))
+ goto out;
+ /*
+ * We don't free the group descriptors allocated by compute_sb_data()
+ * until ext2_unmount(). This is OK since the mount will succeed.
+ */
+ brelse(bp);
+ bp = NULL;
+ fs = ump->um_e2fs;
+ fs->s_rd_only = ronly; /* ronly is set according to mnt_flags */
+ /* if the fs is not mounted read-only, make sure the super block is
+ always written back on a sync()
+ */
+ fs->s_wasvalid = fs->s_es->s_state & EXT2_VALID_FS ? 1 : 0;
+ if (ronly == 0) {
+ fs->s_dirt = 1; /* mark it modified */
+ fs->s_es->s_state &= ~EXT2_VALID_FS; /* set fs invalid */
+ }
+ mp->mnt_data = (qaddr_t)ump;
+ mp->mnt_stat.f_fsid.val[0] = dev2udev(dev);
+ mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
+ mp->mnt_maxsymlinklen = EXT2_MAXSYMLINKLEN;
+ mp->mnt_flag |= MNT_LOCAL;
+ ump->um_mountp = mp;
+ ump->um_dev = dev;
+ ump->um_devvp = devvp;
+ /* setting those two parameters allowed us to use
+ ufs_bmap w/o changse !
+ */
+ ump->um_nindir = EXT2_ADDR_PER_BLOCK(fs);
+ ump->um_bptrtodb = fs->s_es->s_log_block_size + 1;
+ ump->um_seqinc = EXT2_FRAGS_PER_BLOCK(fs);
+ devvp->v_rdev->si_mountpoint = mp;
+ if (ronly == 0)
+ ext2_sbupdate(ump, MNT_WAIT);
+ return (0);
+out:
+ if (bp)
+ brelse(bp);
+ (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, NOCRED, td);
+ if (ump) {
+ bsd_free(ump->um_e2fs->s_es, M_EXT2MNT);
+ bsd_free(ump->um_e2fs, M_EXT2MNT);
+ bsd_free(ump, M_EXT2MNT);
+ mp->mnt_data = (qaddr_t)0;
+ }
+ return (error);
+}
+
+/*
+ * unmount system call
+ */
+static int
+ext2_unmount(mp, mntflags, td)
+ struct mount *mp;
+ int mntflags;
+ struct thread *td;
+{
+ struct ext2mount *ump;
+ struct ext2_sb_info *fs;
+ int error, flags, ronly, i;
+
+ flags = 0;
+ if (mntflags & MNT_FORCE) {
+ if (mp->mnt_flag & MNT_ROOTFS)
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+ if ((error = ext2_flushfiles(mp, flags, td)) != 0)
+ return (error);
+ ump = VFSTOEXT2(mp);
+ fs = ump->um_e2fs;
+ ronly = fs->s_rd_only;
+ if (ronly == 0) {
+ if (fs->s_wasvalid)
+ fs->s_es->s_state |= EXT2_VALID_FS;
+ ext2_sbupdate(ump, MNT_WAIT);
+ }
+
+ /* release buffers containing group descriptors */
+ for(i = 0; i < fs->s_db_per_group; i++)
+ ULCK_BUF(fs->s_group_desc[i])
+ bsd_free(fs->s_group_desc, M_EXT2MNT);
+
+ /* release cached inode/block bitmaps */
+ for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++)
+ if (fs->s_inode_bitmap[i])
+ ULCK_BUF(fs->s_inode_bitmap[i])
+
+ for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++)
+ if (fs->s_block_bitmap[i])
+ ULCK_BUF(fs->s_block_bitmap[i])
+
+ ump->um_devvp->v_rdev->si_mountpoint = NULL;
+ error = VOP_CLOSE(ump->um_devvp, ronly ? FREAD : FREAD|FWRITE,
+ NOCRED, td);
+ vrele(ump->um_devvp);
+ bsd_free(fs->s_es, M_EXT2MNT);
+ bsd_free(fs, M_EXT2MNT);
+ bsd_free(ump, M_EXT2MNT);
+ mp->mnt_data = (qaddr_t)0;
+ mp->mnt_flag &= ~MNT_LOCAL;
+ return (error);
+}
+
+/*
+ * Flush out all the files in a filesystem.
+ */
+static int
+ext2_flushfiles(mp, flags, td)
+ struct mount *mp;
+ int flags;
+ struct thread *td;
+{
+ int error;
+
+ error = vflush(mp, 0, flags);
+ return (error);
+}
+
+/*
+ * Get file system statistics.
+ * taken from ext2/super.c ext2_statfs
+ */
+static int
+ext2_statfs(mp, sbp, td)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct thread *td;
+{
+ unsigned long overhead;
+ struct ext2mount *ump;
+ struct ext2_sb_info *fs;
+ struct ext2_super_block *es;
+ int i, nsb;
+
+ ump = VFSTOEXT2(mp);
+ fs = ump->um_e2fs;
+ es = fs->s_es;
+
+ if (es->s_magic != EXT2_SUPER_MAGIC)
+ panic("ext2_statfs - magic number spoiled");
+
+ /*
+ * Compute the overhead (FS structures)
+ */
+ if (es->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) {
+ nsb = 0;
+ for (i = 0 ; i < fs->s_groups_count; i++)
+ if (ext2_group_sparse(i))
+ nsb++;
+ } else
+ nsb = fs->s_groups_count;
+ overhead = es->s_first_data_block +
+ /* Superblocks and block group descriptors: */
+ nsb * (1 + fs->s_db_per_group) +
+ /* Inode bitmap, block bitmap, and inode table: */
+ fs->s_groups_count * (1 + 1 + fs->s_itb_per_group);
+
+ sbp->f_bsize = EXT2_FRAG_SIZE(fs);
+ sbp->f_iosize = EXT2_BLOCK_SIZE(fs);
+ sbp->f_blocks = es->s_blocks_count - overhead;
+ sbp->f_bfree = es->s_free_blocks_count;
+ sbp->f_bavail = sbp->f_bfree - es->s_r_blocks_count;
+ sbp->f_files = es->s_inodes_count;
+ sbp->f_ffree = es->s_free_inodes_count;
+ if (sbp != &mp->mnt_stat) {
+ sbp->f_type = mp->mnt_vfc->vfc_typenum;
+ bcopy((caddr_t)mp->mnt_stat.f_mntonname,
+ (caddr_t)&sbp->f_mntonname[0], MNAMELEN);
+ bcopy((caddr_t)mp->mnt_stat.f_mntfromname,
+ (caddr_t)&sbp->f_mntfromname[0], MNAMELEN);
+ }
+ 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'.
+ */
+static int
+ext2_sync(mp, waitfor, cred, td)
+ struct mount *mp;
+ int waitfor;
+ struct ucred *cred;
+ struct thread *td;
+{
+ struct vnode *nvp, *vp;
+ struct inode *ip;
+ struct ext2mount *ump = VFSTOEXT2(mp);
+ struct ext2_sb_info *fs;
+ int error, allerror = 0;
+
+ fs = ump->um_e2fs;
+ if (fs->s_dirt != 0 && fs->s_rd_only != 0) { /* XXX */
+ printf("fs = %s\n", fs->fs_fsmnt);
+ panic("ext2_sync: rofs mod");
+ }
+ /*
+ * Write back each (modified) inode.
+ */
+ mtx_lock(&mntvnode_mtx);
+loop:
+ for (vp = TAILQ_FIRST(&mp->mnt_nvnodelist); vp != NULL; vp = nvp) {
+ /*
+ * 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;
+ nvp = TAILQ_NEXT(vp, v_nmntvnodes);
+ mtx_unlock(&mntvnode_mtx);
+ VI_LOCK(vp);
+ ip = VTOI(vp);
+ if (vp->v_type == VNON ||
+ ((ip->i_flag &
+ (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0 &&
+ (TAILQ_EMPTY(&vp->v_dirtyblkhd) || waitfor == MNT_LAZY))) {
+ VI_UNLOCK(vp);
+ mtx_lock(&mntvnode_mtx);
+ continue;
+ }
+ error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, td);
+ if (error) {
+ mtx_lock(&mntvnode_mtx);
+ if (error == ENOENT)
+ goto loop;
+ continue;
+ }
+ if ((error = VOP_FSYNC(vp, cred, waitfor, td)) != 0)
+ allerror = error;
+ VOP_UNLOCK(vp, 0, td);
+ vrele(vp);
+ mtx_lock(&mntvnode_mtx);
+ }
+ mtx_unlock(&mntvnode_mtx);
+ /*
+ * Force stale file system control information to be flushed.
+ */
+ if (waitfor != MNT_LAZY) {
+ vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY, td);
+ if ((error = VOP_FSYNC(ump->um_devvp, cred, waitfor, td)) != 0)
+ allerror = error;
+ VOP_UNLOCK(ump->um_devvp, 0, td);
+ }
+ /*
+ * Write back modified superblock.
+ */
+ if (fs->s_dirt != 0) {
+ fs->s_dirt = 0;
+ fs->s_es->s_wtime = time_second;
+ if ((error = ext2_sbupdate(ump, waitfor)) != 0)
+ allerror = error;
+ }
+ return (allerror);
+}
+
+/*
+ * Look up an 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.
+ */
+static int
+ext2_vget(mp, ino, flags, vpp)
+ struct mount *mp;
+ ino_t ino;
+ int flags;
+ struct vnode **vpp;
+{
+ struct ext2_sb_info *fs;
+ struct inode *ip;
+ struct ext2mount *ump;
+ struct buf *bp;
+ struct vnode *vp;
+ dev_t dev;
+ int i, error;
+ int used_blocks;
+
+ ump = VFSTOEXT2(mp);
+ dev = ump->um_dev;
+restart:
+ if ((error = ext2_ihashget(dev, ino, flags, vpp)) != 0)
+ return (error);
+ if (*vpp != NULL)
+ return (0);
+
+ /*
+ * Lock out the creation of new entries in the FFS hash table in
+ * case getnewvnode() or MALLOC() blocks, otherwise a duplicate
+ * may occur!
+ */
+ if (ext2fs_inode_hash_lock) {
+ while (ext2fs_inode_hash_lock) {
+ ext2fs_inode_hash_lock = -1;
+ tsleep(&ext2fs_inode_hash_lock, PVM, "e2vget", 0);
+ }
+ goto restart;
+ }
+ ext2fs_inode_hash_lock = 1;
+
+ /*
+ * If this MALLOC() is performed after the getnewvnode()
+ * it might block, leaving a vnode with a NULL v_data to be
+ * found by ext2_sync() if a sync happens to fire right then,
+ * which will cause a panic because ext2_sync() blindly
+ * dereferences vp->v_data (as well it should).
+ */
+ MALLOC(ip, struct inode *, sizeof(struct inode), M_EXT2NODE, M_WAITOK);
+
+ /* Allocate a new vnode/inode. */
+ if ((error = getnewvnode("ext2fs", mp, ext2_vnodeop_p, &vp)) != 0) {
+ if (ext2fs_inode_hash_lock < 0)
+ wakeup(&ext2fs_inode_hash_lock);
+ ext2fs_inode_hash_lock = 0;
+ *vpp = NULL;
+ FREE(ip, M_EXT2NODE);
+ return (error);
+ }
+ 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;
+ /*
+ * 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.
+ */
+ ext2_ihashins(ip);
+
+ if (ext2fs_inode_hash_lock < 0)
+ wakeup(&ext2fs_inode_hash_lock);
+ ext2fs_inode_hash_lock = 0;
+
+ /* Read in the disk contents for the inode, copy into the inode. */
+#if 0
+printf("ext2_vget(%d) dbn= %d ", ino, fsbtodb(fs, ino_to_fsba(fs, ino)));
+#endif
+ if ((error = bread(ump->um_devvp, fsbtodb(fs, ino_to_fsba(fs, ino)),
+ (int)fs->s_blocksize, NOCRED, &bp)) != 0) {
+ /*
+ * 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);
+ }
+ /* convert ext2 inode to dinode */
+ ext2_ei2i((struct ext2_inode *) ((char *)bp->b_data + EXT2_INODE_SIZE *
+ ino_to_fsbo(fs, ino)), ip);
+ ip->i_block_group = ino_to_cg(fs, ino);
+ ip->i_next_alloc_block = 0;
+ ip->i_next_alloc_goal = 0;
+ ip->i_prealloc_count = 0;
+ ip->i_prealloc_block = 0;
+ /* now we want to make sure that block pointers for unused
+ blocks are zeroed out - ext2_balloc depends on this
+ although for regular files and directories only
+ */
+ if(S_ISDIR(ip->i_mode) || S_ISREG(ip->i_mode)) {
+ used_blocks = (ip->i_size+fs->s_blocksize-1) / fs->s_blocksize;
+ for(i = used_blocks; i < EXT2_NDIR_BLOCKS; i++)
+ ip->i_db[i] = 0;
+ }
+/*
+ ext2_print_inode(ip);
+*/
+ brelse(bp);
+
+ /*
+ * Initialize the vnode from the inode, check for aliases.
+ * Note that the underlying vnode may have changed.
+ */
+ if ((error = ext2_vinit(mp, ext2_specop_p, ext2_fifoop_p, &vp)) != 0) {
+ 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_gen == 0) {
+ ip->i_gen = random() / 2 + 1;
+ 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 ext2_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
+ */
+static int
+ext2_fhtovp(mp, fhp, vpp)
+ struct mount *mp;
+ struct fid *fhp;
+ struct vnode **vpp;
+{
+ struct inode *ip;
+ struct ufid *ufhp;
+ struct vnode *nvp;
+ struct ext2_sb_info *fs;
+ int error;
+
+ ufhp = (struct ufid *)fhp;
+ fs = VFSTOEXT2(mp)->um_e2fs;
+ if (ufhp->ufid_ino < ROOTINO ||
+ ufhp->ufid_ino > fs->s_groups_count * fs->s_es->s_inodes_per_group)
+ return (ESTALE);
+
+ error = VFS_VGET(mp, ufhp->ufid_ino, LK_EXCLUSIVE, &nvp);
+ if (error) {
+ *vpp = NULLVP;
+ return (error);
+ }
+ ip = VTOI(nvp);
+ if (ip->i_mode == 0 ||
+ ip->i_gen != ufhp->ufid_gen || ip->i_nlink <= 0) {
+ vput(nvp);
+ *vpp = NULLVP;
+ return (ESTALE);
+ }
+ *vpp = nvp;
+ return (0);
+}
+
+/*
+ * Vnode pointer to File handle
+ */
+/* ARGSUSED */
+static int
+ext2_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+ struct inode *ip;
+ 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_gen;
+ return (0);
+}
+
+/*
+ * Write a superblock and associated information back to disk.
+ */
+static int
+ext2_sbupdate(mp, waitfor)
+ struct ext2mount *mp;
+ int waitfor;
+{
+ struct ext2_sb_info *fs = mp->um_e2fs;
+ struct ext2_super_block *es = fs->s_es;
+ struct buf *bp;
+ int error = 0;
+/*
+printf("\nupdating superblock, waitfor=%s\n", waitfor == MNT_WAIT ? "yes":"no");
+*/
+ bp = getblk(mp->um_devvp, SBLOCK, SBSIZE, 0, 0);
+ bcopy((caddr_t)es, bp->b_data, (u_int)sizeof(struct ext2_super_block));
+ if (waitfor == MNT_WAIT)
+ error = bwrite(bp);
+ else
+ bawrite(bp);
+
+ /*
+ * The buffers for group descriptors, inode bitmaps and block bitmaps
+ * are not busy at this point and are (hopefully) written by the
+ * usual sync mechanism. No need to write them here
+ */
+
+ return (error);
+}
+
+/*
+ * Return the root of a filesystem.
+ */
+static int
+ext2_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct vnode *nvp;
+ int error;
+
+ error = VFS_VGET(mp, (ino_t)ROOTINO, LK_EXCLUSIVE, &nvp);
+ if (error)
+ return (error);
+ *vpp = nvp;
+ return (0);
+}
+
+static int
+ext2_init(struct vfsconf *vfsp)
+{
+
+ ext2_ihashinit();
+ return (0);
+}
+
+static int
+ext2_uninit(struct vfsconf *vfsp)
+{
+
+ ext2_ihashuninit();
+ return (0);
+}
diff --git a/sys/gnu/ext2fs/ext2_vnops.c b/sys/gnu/ext2fs/ext2_vnops.c
new file mode 100644
index 0000000..e030b7c
--- /dev/null
+++ b/sys/gnu/ext2fs/ext2_vnops.c
@@ -0,0 +1,1962 @@
+/*
+ * modified for EXT2FS support in Lites 1.1
+ *
+ * Aug 1995, Godmar Back (gback@cs.utah.edu)
+ * University of Utah, Department of Computer Science
+ */
+/*
+ * 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.7 (Berkeley) 2/3/94
+ * @(#)ufs_vnops.c 8.27 (Berkeley) 5/27/95
+ * $FreeBSD$
+ */
+
+#include "opt_suiddir.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/resourcevar.h>
+#include <sys/kernel.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/bio.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/unistd.h>
+#include <sys/time.h>
+#include <sys/vnode.h>
+#include <sys/namei.h>
+#include <sys/lockf.h>
+#include <sys/event.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vnode_pager.h>
+
+#include <fs/fifofs/fifo.h>
+
+#include <sys/signalvar.h>
+#include <ufs/ufs/dir.h>
+
+#include <gnu/ext2fs/inode.h>
+#include <gnu/ext2fs/ext2_mount.h>
+#include <gnu/ext2fs/ext2_fs_sb.h>
+#include <gnu/ext2fs/fs.h>
+#include <gnu/ext2fs/ext2_extern.h>
+#include <gnu/ext2fs/ext2_fs.h>
+
+static int ext2_makeinode(int mode, struct vnode *, struct vnode **, struct componentname *);
+
+static int ext2_access(struct vop_access_args *);
+static int ext2_advlock(struct vop_advlock_args *);
+static int ext2_chmod(struct vnode *, int, struct ucred *, struct thread *);
+static int ext2_chown(struct vnode *, uid_t, gid_t, struct ucred *,
+ struct thread *);
+static int ext2_close(struct vop_close_args *);
+static int ext2_create(struct vop_create_args *);
+static int ext2_fsync(struct vop_fsync_args *);
+static int ext2_getattr(struct vop_getattr_args *);
+static int ext2_kqfilter(struct vop_kqfilter_args *ap);
+static int ext2_link(struct vop_link_args *);
+static int ext2_mkdir(struct vop_mkdir_args *);
+static int ext2_mknod(struct vop_mknod_args *);
+static int ext2_open(struct vop_open_args *);
+static int ext2_pathconf(struct vop_pathconf_args *);
+static int ext2_print(struct vop_print_args *);
+static int ext2_read(struct vop_read_args *);
+static int ext2_readlink(struct vop_readlink_args *);
+static int ext2_remove(struct vop_remove_args *);
+static int ext2_rename(struct vop_rename_args *);
+static int ext2_rmdir(struct vop_rmdir_args *);
+static int ext2_setattr(struct vop_setattr_args *);
+static int ext2_strategy(struct vop_strategy_args *);
+static int ext2_symlink(struct vop_symlink_args *);
+static int ext2_write(struct vop_write_args *);
+static int ext2fifo_close(struct vop_close_args *);
+static int ext2fifo_kqfilter(struct vop_kqfilter_args *);
+static int ext2fifo_read(struct vop_read_args *);
+static int ext2fifo_write(struct vop_write_args *);
+static int ext2spec_close(struct vop_close_args *);
+static int ext2spec_read(struct vop_read_args *);
+static int ext2spec_write(struct vop_write_args *);
+static int filt_ext2read(struct knote *kn, long hint);
+static int filt_ext2write(struct knote *kn, long hint);
+static int filt_ext2vnode(struct knote *kn, long hint);
+static void filt_ext2detach(struct knote *kn);
+
+/* Global vfs data structures for ext2. */
+vop_t **ext2_vnodeop_p;
+static struct vnodeopv_entry_desc ext2_vnodeop_entries[] = {
+ { &vop_default_desc, (vop_t *) vop_defaultop },
+ { &vop_access_desc, (vop_t *) ext2_access },
+ { &vop_advlock_desc, (vop_t *) ext2_advlock },
+ { &vop_bmap_desc, (vop_t *) ext2_bmap },
+ { &vop_cachedlookup_desc, (vop_t *) ext2_lookup },
+ { &vop_close_desc, (vop_t *) ext2_close },
+ { &vop_create_desc, (vop_t *) ext2_create },
+ { &vop_fsync_desc, (vop_t *) ext2_fsync },
+ { &vop_getattr_desc, (vop_t *) ext2_getattr },
+ { &vop_getwritemount_desc, (vop_t *) vop_stdgetwritemount },
+ { &vop_inactive_desc, (vop_t *) ext2_inactive },
+ { &vop_link_desc, (vop_t *) ext2_link },
+ { &vop_lookup_desc, (vop_t *) vfs_cache_lookup },
+ { &vop_mkdir_desc, (vop_t *) ext2_mkdir },
+ { &vop_mknod_desc, (vop_t *) ext2_mknod },
+ { &vop_open_desc, (vop_t *) ext2_open },
+ { &vop_pathconf_desc, (vop_t *) ext2_pathconf },
+ { &vop_poll_desc, (vop_t *) vop_stdpoll },
+ { &vop_kqfilter_desc, (vop_t *) ext2_kqfilter },
+ { &vop_print_desc, (vop_t *) ext2_print },
+ { &vop_read_desc, (vop_t *) ext2_read },
+ { &vop_readdir_desc, (vop_t *) ext2_readdir },
+ { &vop_readlink_desc, (vop_t *) ext2_readlink },
+ { &vop_reallocblks_desc, (vop_t *) ext2_reallocblks },
+ { &vop_reclaim_desc, (vop_t *) ext2_reclaim },
+ { &vop_remove_desc, (vop_t *) ext2_remove },
+ { &vop_rename_desc, (vop_t *) ext2_rename },
+ { &vop_rmdir_desc, (vop_t *) ext2_rmdir },
+ { &vop_setattr_desc, (vop_t *) ext2_setattr },
+ { &vop_strategy_desc, (vop_t *) ext2_strategy },
+ { &vop_symlink_desc, (vop_t *) ext2_symlink },
+ { &vop_write_desc, (vop_t *) ext2_write },
+ { NULL, NULL }
+};
+static struct vnodeopv_desc ext2fs_vnodeop_opv_desc =
+ { &ext2_vnodeop_p, ext2_vnodeop_entries };
+
+vop_t **ext2_specop_p;
+static struct vnodeopv_entry_desc ext2_specop_entries[] = {
+ { &vop_default_desc, (vop_t *) spec_vnoperate },
+ { &vop_access_desc, (vop_t *) ext2_access },
+ { &vop_close_desc, (vop_t *) ext2spec_close },
+ { &vop_fsync_desc, (vop_t *) ext2_fsync },
+ { &vop_getattr_desc, (vop_t *) ext2_getattr },
+ { &vop_inactive_desc, (vop_t *) ext2_inactive },
+ { &vop_print_desc, (vop_t *) ext2_print },
+ { &vop_read_desc, (vop_t *) ext2spec_read },
+ { &vop_reclaim_desc, (vop_t *) ext2_reclaim },
+ { &vop_setattr_desc, (vop_t *) ext2_setattr },
+ { &vop_write_desc, (vop_t *) ext2spec_write },
+ { NULL, NULL }
+};
+static struct vnodeopv_desc ext2fs_specop_opv_desc =
+ { &ext2_specop_p, ext2_specop_entries };
+
+vop_t **ext2_fifoop_p;
+static struct vnodeopv_entry_desc ext2_fifoop_entries[] = {
+ { &vop_default_desc, (vop_t *) fifo_vnoperate },
+ { &vop_access_desc, (vop_t *) ext2_access },
+ { &vop_close_desc, (vop_t *) ext2fifo_close },
+ { &vop_fsync_desc, (vop_t *) ext2_fsync },
+ { &vop_getattr_desc, (vop_t *) ext2_getattr },
+ { &vop_inactive_desc, (vop_t *) ext2_inactive },
+ { &vop_kqfilter_desc, (vop_t *) ext2fifo_kqfilter },
+ { &vop_print_desc, (vop_t *) ext2_print },
+ { &vop_read_desc, (vop_t *) ext2fifo_read },
+ { &vop_reclaim_desc, (vop_t *) ext2_reclaim },
+ { &vop_setattr_desc, (vop_t *) ext2_setattr },
+ { &vop_write_desc, (vop_t *) ext2fifo_write },
+ { NULL, NULL }
+};
+static struct vnodeopv_desc ext2fs_fifoop_opv_desc =
+ { &ext2_fifoop_p, ext2_fifoop_entries };
+
+ VNODEOP_SET(ext2fs_vnodeop_opv_desc);
+ VNODEOP_SET(ext2fs_specop_opv_desc);
+ VNODEOP_SET(ext2fs_fifoop_opv_desc);
+
+#include <gnu/ext2fs/ext2_readwrite.c>
+
+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; \
+}
+
+/*
+ * A virgin directory (no blushing please).
+ * Note that the type and namlen fields are reversed relative to ext2.
+ * Also, we don't use `struct odirtemplate', since it would just cause
+ * endianness problems.
+ */
+static struct dirtemplate mastertemplate = {
+ 0, 12, 1, EXT2_FT_DIR, ".",
+ 0, DIRBLKSIZ - 12, 2, EXT2_FT_DIR, ".."
+};
+static struct dirtemplate omastertemplate = {
+ 0, 12, 1, EXT2_FT_UNKNOWN, ".",
+ 0, DIRBLKSIZ - 12, 2, EXT2_FT_UNKNOWN, ".."
+};
+
+void
+ext2_itimes(vp)
+ struct vnode *vp;
+{
+ struct inode *ip;
+ struct timespec ts;
+
+ ip = VTOI(vp);
+ if ((ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_UPDATE)) == 0)
+ return;
+ if ((vp->v_type == VBLK || vp->v_type == VCHR))
+ ip->i_flag |= IN_LAZYMOD;
+ else
+ ip->i_flag |= IN_MODIFIED;
+ if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
+ vfs_timestamp(&ts);
+ if (ip->i_flag & IN_ACCESS) {
+ ip->i_atime = ts.tv_sec;
+ ip->i_atimensec = ts.tv_nsec;
+ }
+ if (ip->i_flag & IN_UPDATE) {
+ ip->i_mtime = ts.tv_sec;
+ ip->i_mtimensec = ts.tv_nsec;
+ ip->i_modrev++;
+ }
+ if (ip->i_flag & IN_CHANGE) {
+ ip->i_ctime = ts.tv_sec;
+ ip->i_ctimensec = ts.tv_nsec;
+ }
+ }
+ ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE);
+}
+
+/*
+ * Create a regular file
+ */
+static int
+ext2_create(ap)
+ struct vop_create_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ int error;
+
+ error =
+ ext2_makeinode(MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode),
+ ap->a_dvp, ap->a_vpp, ap->a_cnp);
+ if (error)
+ return (error);
+ return (0);
+}
+
+/*
+ * Open called.
+ *
+ * Nothing to do.
+ */
+static int
+ext2_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct thread *a_td;
+ } */ *ap;
+{
+
+ /*
+ * Files marked append-only must be opened for appending.
+ */
+ if ((VTOI(ap->a_vp)->i_flags & APPEND) &&
+ (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
+ return (EPERM);
+ return (0);
+}
+
+/*
+ * Close called.
+ *
+ * Update the times on the inode.
+ */
+static int
+ext2_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct thread *a_td;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct mount *mp;
+
+ VI_LOCK(vp);
+ if (vp->v_usecount > 1) {
+ ext2_itimes(vp);
+ VI_UNLOCK(vp);
+ } else {
+ VI_UNLOCK(vp);
+ /*
+ * If we are closing the last reference to an unlinked
+ * file, then it will be freed by the inactive routine.
+ * Because the freeing causes a the filesystem to be
+ * modified, it must be held up during periods when the
+ * filesystem is suspended.
+ *
+ * XXX - EAGAIN is returned to prevent vn_close from
+ * repeating the vrele operation.
+ */
+ if (vp->v_type == VREG && VTOI(vp)->i_nlink == 0) {
+ (void) vn_start_write(vp, &mp, V_WAIT);
+ vrele(vp);
+ vn_finished_write(mp);
+ return (EAGAIN);
+ }
+ }
+ return (0);
+}
+
+static int
+ext2_access(ap)
+ struct vop_access_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct thread *a_td;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct inode *ip = VTOI(vp);
+ mode_t mode = ap->a_mode;
+ int error;
+
+ /*
+ * Disallow write attempts on read-only file systems;
+ * unless the file is a socket, fifo, or a block or
+ * character device resident on the file system.
+ */
+ if (mode & VWRITE) {
+ switch (vp->v_type) {
+ case VDIR:
+ case VLNK:
+ case VREG:
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ return (EROFS);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* If immutable bit set, nobody gets to write it. */
+ if ((mode & VWRITE) && (ip->i_flags & (IMMUTABLE | SF_SNAPSHOT)))
+ return (EPERM);
+
+ error = vaccess(vp->v_type, ip->i_mode, ip->i_uid, ip->i_gid,
+ ap->a_mode, ap->a_cred, NULL);
+ return (error);
+}
+
+static int
+ext2_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct thread *a_td;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct inode *ip = VTOI(vp);
+ struct vattr *vap = ap->a_vap;
+
+ ext2_itimes(vp);
+ /*
+ * Copy from inode table
+ */
+ vap->va_fsid = dev2udev(ip->i_dev);
+ vap->va_fileid = ip->i_number;
+ vap->va_mode = ip->i_mode & ~IFMT;
+ vap->va_nlink = ip->i_nlink;
+ vap->va_uid = ip->i_uid;
+ vap->va_gid = ip->i_gid;
+ vap->va_rdev = ip->i_rdev;
+ vap->va_size = ip->i_size;
+ vap->va_atime.tv_sec = ip->i_atime;
+ vap->va_atime.tv_nsec = ip->i_atimensec;
+ vap->va_mtime.tv_sec = ip->i_mtime;
+ vap->va_mtime.tv_nsec = ip->i_mtimensec;
+ vap->va_ctime.tv_sec = ip->i_ctime;
+ vap->va_ctime.tv_nsec = ip->i_ctimensec;
+ vap->va_flags = ip->i_flags;
+ vap->va_gen = ip->i_gen;
+ vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize;
+ vap->va_bytes = dbtob((u_quad_t)ip->i_blocks);
+ vap->va_type = IFTOVT(ip->i_mode);
+ vap->va_filerev = ip->i_modrev;
+ return (0);
+}
+
+/*
+ * Set attribute vnode op. called from several syscalls
+ */
+static int
+ext2_setattr(ap)
+ struct vop_setattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct thread *a_td;
+ } */ *ap;
+{
+ struct vattr *vap = ap->a_vap;
+ struct vnode *vp = ap->a_vp;
+ struct inode *ip = VTOI(vp);
+ struct ucred *cred = ap->a_cred;
+ struct thread *td = ap->a_td;
+ 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 (vp->v_mount->mnt_flag & MNT_RDONLY)
+ return (EROFS);
+ /*
+ * Callers may only modify the file flags on objects they
+ * have VADMIN rights for.
+ */
+ if ((error = VOP_ACCESS(vp, VADMIN, cred, td)))
+ return (error);
+ /*
+ * Unprivileged processes and privileged processes in
+ * jail() are not permitted to unset system flags, or
+ * modify flags if any system flags are set.
+ * Privileged non-jail processes may not modify system flags
+ * if securelevel > 0 and any existing system flags are set.
+ */
+ if (!suser_cred(cred, PRISON_ROOT)) {
+ if (ip->i_flags
+ & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) {
+ error = securelevel_gt(cred, 0);
+ if (error)
+ return (error);
+ }
+ ip->i_flags = vap->va_flags;
+ } else {
+ if (ip->i_flags
+ & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND) ||
+ (vap->va_flags & UF_SETTABLE) != vap->va_flags)
+ return (EPERM);
+ ip->i_flags &= SF_SETTABLE;
+ ip->i_flags |= (vap->va_flags & UF_SETTABLE);
+ }
+ ip->i_flag |= IN_CHANGE;
+ if (vap->va_flags & (IMMUTABLE | APPEND))
+ return (0);
+ }
+ if (ip->i_flags & (IMMUTABLE | APPEND))
+ return (EPERM);
+ /*
+ * Go through the fields and update iff not VNOVAL.
+ */
+ if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ return (EROFS);
+ if ((error = ext2_chown(vp, vap->va_uid, vap->va_gid, cred,
+ td)) != 0)
+ return (error);
+ }
+ if (vap->va_size != VNOVAL) {
+ /*
+ * Disallow write attempts on read-only file systems;
+ * unless the file is a socket, fifo, or a block or
+ * character device resident on the file system.
+ */
+ switch (vp->v_type) {
+ case VDIR:
+ return (EISDIR);
+ case VLNK:
+ case VREG:
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ return (EROFS);
+ break;
+ default:
+ break;
+ }
+ if ((error = ext2_truncate(vp, vap->va_size, 0, cred, td)) != 0)
+ return (error);
+ }
+ if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ return (EROFS);
+ /*
+ * From utimes(2):
+ * If times is NULL, ... The caller must be the owner of
+ * the file, have permission to write the file, or be the
+ * super-user.
+ * If times is non-NULL, ... The caller must be the owner of
+ * the file or be the super-user.
+ */
+ if ((error = VOP_ACCESS(vp, VADMIN, cred, td)) &&
+ ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
+ (error = VOP_ACCESS(vp, VWRITE, cred, td))))
+ return (error);
+ if (vap->va_atime.tv_sec != VNOVAL)
+ ip->i_flag |= IN_ACCESS;
+ if (vap->va_mtime.tv_sec != VNOVAL)
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ ext2_itimes(vp);
+ if (vap->va_atime.tv_sec != VNOVAL) {
+ ip->i_atime = vap->va_atime.tv_sec;
+ ip->i_atimensec = vap->va_atime.tv_nsec;
+ }
+ if (vap->va_mtime.tv_sec != VNOVAL) {
+ ip->i_mtime = vap->va_mtime.tv_sec;
+ ip->i_mtimensec = vap->va_mtime.tv_nsec;
+ }
+ error = ext2_update(vp, 0);
+ if (error)
+ return (error);
+ }
+ error = 0;
+ if (vap->va_mode != (mode_t)VNOVAL) {
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ return (EROFS);
+ error = ext2_chmod(vp, (int)vap->va_mode, cred, td);
+ }
+ VN_KNOTE(vp, NOTE_ATTRIB);
+ return (error);
+}
+
+/*
+ * Change the mode on a file.
+ * Inode must be locked before calling.
+ */
+static int
+ext2_chmod(vp, mode, cred, td)
+ struct vnode *vp;
+ int mode;
+ struct ucred *cred;
+ struct thread *td;
+{
+ struct inode *ip = VTOI(vp);
+ int error;
+
+ /*
+ * To modify the permissions on a file, must possess VADMIN
+ * for that file.
+ */
+ if ((error = VOP_ACCESS(vp, VADMIN, cred, td)))
+ return (error);
+ /*
+ * Privileged processes may set the sticky bit on non-directories,
+ * as well as set the setgid bit on a file with a group that the
+ * process is not a member of.
+ */
+ if (suser_cred(cred, PRISON_ROOT)) {
+ if (vp->v_type != VDIR && (mode & S_ISTXT))
+ return (EFTYPE);
+ if (!groupmember(ip->i_gid, cred) && (mode & ISGID))
+ return (EPERM);
+ }
+ ip->i_mode &= ~ALLPERMS;
+ ip->i_mode |= (mode & ALLPERMS);
+ ip->i_flag |= IN_CHANGE;
+ return (0);
+}
+
+/*
+ * Perform chown operation on inode ip;
+ * inode must be locked prior to call.
+ */
+static int
+ext2_chown(vp, uid, gid, cred, td)
+ struct vnode *vp;
+ uid_t uid;
+ gid_t gid;
+ struct ucred *cred;
+ struct thread *td;
+{
+ struct inode *ip = VTOI(vp);
+ uid_t ouid;
+ gid_t ogid;
+ int error = 0;
+
+ if (uid == (uid_t)VNOVAL)
+ uid = ip->i_uid;
+ if (gid == (gid_t)VNOVAL)
+ gid = ip->i_gid;
+ /*
+ * To modify the ownership of a file, must possess VADMIN
+ * for that file.
+ */
+ if ((error = VOP_ACCESS(vp, VADMIN, cred, td)))
+ return (error);
+ /*
+ * To change the owner of a file, or change the group of a file
+ * to a group of which we are not a member, the caller must
+ * have privilege.
+ */
+ if ((uid != ip->i_uid ||
+ (gid != ip->i_gid && !groupmember(gid, cred))) &&
+ (error = suser_cred(cred, PRISON_ROOT)))
+ return (error);
+ ogid = ip->i_gid;
+ ouid = ip->i_uid;
+ ip->i_gid = gid;
+ ip->i_uid = uid;
+ ip->i_flag |= IN_CHANGE;
+ if (suser_cred(cred, PRISON_ROOT) && (ouid != uid || ogid != gid))
+ ip->i_mode &= ~(ISUID | ISGID);
+ return (0);
+}
+
+/*
+ * Synch an open file.
+ */
+/* ARGSUSED */
+static int
+ext2_fsync(ap)
+ struct vop_fsync_args /* {
+ struct vnode *a_vp;
+ struct ucred *a_cred;
+ int a_waitfor;
+ struct thread *a_td;
+ } */ *ap;
+{
+ /*
+ * Flush all dirty buffers associated with a vnode.
+ */
+ ext2_discard_prealloc(VTOI(ap->a_vp));
+
+ vop_stdfsync(ap);
+
+ return (ext2_update(ap->a_vp, ap->a_waitfor == MNT_WAIT));
+}
+
+/*
+ * Mknod vnode call
+ */
+/* ARGSUSED */
+static int
+ext2_mknod(ap)
+ struct vop_mknod_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ struct vattr *vap = ap->a_vap;
+ struct vnode **vpp = ap->a_vpp;
+ struct inode *ip;
+ ino_t ino;
+ int error;
+
+ error = ext2_makeinode(MAKEIMODE(vap->va_type, vap->va_mode),
+ ap->a_dvp, vpp, ap->a_cnp);
+ if (error)
+ 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_rdev = vap->va_rdev;
+ }
+ /*
+ * Remove inode, then reload it through VFS_VGET so it is
+ * checked to see if it is an alias of an existing entry in
+ * the inode cache.
+ */
+ vput(*vpp);
+ (*vpp)->v_type = VNON;
+ ino = ip->i_number; /* Save this before vgone() invalidates ip. */
+ vgone(*vpp);
+ error = VFS_VGET(ap->a_dvp->v_mount, ino, LK_EXCLUSIVE, vpp);
+ if (error) {
+ *vpp = NULL;
+ return (error);
+ }
+ return (0);
+}
+
+static int
+ext2_remove(ap)
+ struct vop_remove_args /* {
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ struct inode *ip;
+ struct vnode *vp = ap->a_vp;
+ struct vnode *dvp = ap->a_dvp;
+ int error;
+
+ ip = VTOI(vp);
+ if ((ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
+ (VTOI(dvp)->i_flags & APPEND)) {
+ error = EPERM;
+ goto out;
+ }
+ error = ext2_dirremove(dvp, ap->a_cnp);
+ if (error == 0) {
+ ip->i_nlink--;
+ ip->i_flag |= IN_CHANGE;
+ }
+out:
+ return (error);
+}
+
+/*
+ * link vnode call
+ */
+static int
+ext2_link(ap)
+ struct vop_link_args /* {
+ struct vnode *a_tdvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct vnode *tdvp = ap->a_tdvp;
+ struct componentname *cnp = ap->a_cnp;
+ struct inode *ip;
+ int error;
+
+#ifdef DIAGNOSTIC
+ if ((cnp->cn_flags & HASBUF) == 0)
+ panic("ext2_link: no name");
+#endif
+ if (tdvp->v_mount != vp->v_mount) {
+ error = EXDEV;
+ goto out;
+ }
+ ip = VTOI(vp);
+ if ((nlink_t)ip->i_nlink >= LINK_MAX) {
+ error = EMLINK;
+ goto out;
+ }
+ if (ip->i_flags & (IMMUTABLE | APPEND)) {
+ error = EPERM;
+ goto out;
+ }
+ ip->i_nlink++;
+ ip->i_flag |= IN_CHANGE;
+ error = ext2_update(vp, 1);
+ if (!error)
+ error = ext2_direnter(ip, tdvp, cnp);
+ if (error) {
+ ip->i_nlink--;
+ ip->i_flag |= IN_CHANGE;
+ }
+out:
+ return (error);
+}
+
+/*
+ * Rename system call.
+ * See comments in sys/ufs/ufs/ufs_vnops.c
+ */
+static int
+ext2_rename(ap)
+ 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;
+{
+ struct vnode *tvp = ap->a_tvp;
+ struct vnode *tdvp = ap->a_tdvp;
+ struct vnode *fvp = ap->a_fvp;
+ struct vnode *fdvp = ap->a_fdvp;
+ struct componentname *tcnp = ap->a_tcnp;
+ struct componentname *fcnp = ap->a_fcnp;
+ struct thread *td = fcnp->cn_thread;
+ struct inode *ip, *xp, *dp;
+ struct dirtemplate dirbuf;
+ 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("ext2_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:
+ if (tdvp == tvp)
+ vrele(tdvp);
+ else
+ vput(tdvp);
+ if (tvp)
+ vput(tvp);
+ vrele(fdvp);
+ vrele(fvp);
+ return (error);
+ }
+
+ if (tvp && ((VTOI(tvp)->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
+ (VTOI(tdvp)->i_flags & APPEND))) {
+ error = EPERM;
+ goto abortit;
+ }
+
+ /*
+ * Renaming a file to itself has no effect. The upper layers should
+ * not call us in that case. Temporarily just warn if they do.
+ */
+ if (fvp == tvp) {
+ printf("ext2_rename: fvp == tvp (can't happen)\n");
+ error = 0;
+ goto abortit;
+ }
+
+ if ((error = vn_lock(fvp, LK_EXCLUSIVE, td)) != 0)
+ goto abortit;
+ dp = VTOI(fdvp);
+ ip = VTOI(fvp);
+ if (ip->i_nlink >= LINK_MAX) {
+ VOP_UNLOCK(fvp, 0, td);
+ error = EMLINK;
+ goto abortit;
+ }
+ if ((ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND))
+ || (dp->i_flags & APPEND)) {
+ VOP_UNLOCK(fvp, 0, td);
+ error = EPERM;
+ goto abortit;
+ }
+ if ((ip->i_mode & IFMT) == IFDIR) {
+ /*
+ * Avoid ".", "..", and aliases of "." for obvious reasons.
+ */
+ if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
+ dp == ip || (fcnp->cn_flags | tcnp->cn_flags) & ISDOTDOT ||
+ (ip->i_flag & IN_RENAME)) {
+ VOP_UNLOCK(fvp, 0, td);
+ 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_nlink++;
+ ip->i_flag |= IN_CHANGE;
+ if ((error = ext2_update(fvp, 1)) != 0) {
+ VOP_UNLOCK(fvp, 0, td);
+ 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_thread);
+ VOP_UNLOCK(fvp, 0, td);
+ 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 = ext2_checkpath(ip, dp, tcnp->cn_cred);
+ if (error)
+ goto out;
+ VREF(tdvp);
+ error = relookup(tdvp, &tvp, tcnp);
+ if (error)
+ goto out;
+ vrele(tdvp);
+ 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("ext2_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_nlink >= LINK_MAX) {
+ error = EMLINK;
+ goto bad;
+ }
+ dp->i_nlink++;
+ dp->i_flag |= IN_CHANGE;
+ error = ext2_update(tdvp, 1);
+ if (error)
+ goto bad;
+ }
+ error = ext2_direnter(ip, tdvp, tcnp);
+ if (error) {
+ if (doingdirectory && newparent) {
+ dp->i_nlink--;
+ dp->i_flag |= IN_CHANGE;
+ (void)ext2_update(tdvp, 1);
+ }
+ goto bad;
+ }
+ vput(tdvp);
+ } else {
+ if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev)
+ panic("ext2_rename: EXDEV");
+ /*
+ * Short circuit rename(foo, foo).
+ */
+ if (xp->i_number == ip->i_number)
+ panic("ext2_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_mode & S_ISTXT) && tcnp->cn_cred->cr_uid != 0 &&
+ tcnp->cn_cred->cr_uid != dp->i_uid &&
+ xp->i_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_mode&IFMT) == IFDIR) {
+ if (! ext2_dirempty(xp, dp->i_number, tcnp->cn_cred) ||
+ xp->i_nlink > 2) {
+ error = ENOTEMPTY;
+ goto bad;
+ }
+ if (!doingdirectory) {
+ error = ENOTDIR;
+ goto bad;
+ }
+ cache_purge(tdvp);
+ } else if (doingdirectory) {
+ error = EISDIR;
+ goto bad;
+ }
+ error = ext2_dirrewrite(dp, ip, tcnp);
+ if (error)
+ 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_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_nlink--;
+ if (doingdirectory) {
+ if (--xp->i_nlink != 0)
+ panic("ext2_rename: linked directory");
+ error = ext2_truncate(tvp, (off_t)0, IO_SYNC,
+ tcnp->cn_cred, tcnp->cn_thread);
+ }
+ xp->i_flag |= IN_CHANGE;
+ vput(tvp);
+ xp = NULL;
+ }
+
+ /*
+ * 3) Unlink the source.
+ */
+ fcnp->cn_flags &= ~MODMASK;
+ fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
+ VREF(fdvp);
+ error = relookup(fdvp, &fvp, fcnp);
+ if (error == 0)
+ vrele(fdvp);
+ if (fvp != NULL) {
+ xp = VTOI(fvp);
+ dp = VTOI(fdvp);
+ } else {
+ /*
+ * From name has disappeared.
+ */
+ if (doingdirectory)
+ panic("ext2_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 IN_RENAME flag ensures that it cannot be moved by another
+ * rename.
+ */
+ if (xp != ip) {
+ if (doingdirectory)
+ panic("ext2_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_nlink--;
+ dp->i_flag |= IN_CHANGE;
+ error = vn_rdwr(UIO_READ, fvp, (caddr_t)&dirbuf,
+ sizeof (struct dirtemplate), (off_t)0,
+ UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK,
+ tcnp->cn_cred, NOCRED, (int *)0,
+ (struct thread *)0);
+ if (error == 0) {
+ /* Like ufs little-endian: */
+ namlen = dirbuf.dotdot_type;
+ if (namlen != 2 ||
+ dirbuf.dotdot_name[0] != '.' ||
+ dirbuf.dotdot_name[1] != '.') {
+ ext2_dirbad(xp, (doff_t)12,
+ "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 |
+ IO_NOMACCHECK, tcnp->cn_cred,
+ NOCRED, (int *)0,
+ (struct thread *)0);
+ cache_purge(fdvp);
+ }
+ }
+ }
+ error = ext2_dirremove(fdvp, fcnp);
+ if (!error) {
+ xp->i_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 (vn_lock(fvp, LK_EXCLUSIVE, td) == 0) {
+ ip->i_nlink--;
+ ip->i_flag |= IN_CHANGE;
+ ip->i_flag &= ~IN_RENAME;
+ vput(fvp);
+ } else
+ vrele(fvp);
+ return (error);
+}
+
+/*
+ * Mkdir system call
+ */
+static int
+ext2_mkdir(ap)
+ struct vop_mkdir_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ struct vnode *dvp = ap->a_dvp;
+ struct vattr *vap = ap->a_vap;
+ struct componentname *cnp = ap->a_cnp;
+ struct inode *ip, *dp;
+ struct vnode *tvp;
+ struct dirtemplate dirtemplate, *dtp;
+ int error, dmode;
+
+#ifdef DIAGNOSTIC
+ if ((cnp->cn_flags & HASBUF) == 0)
+ panic("ext2_mkdir: no name");
+#endif
+ dp = VTOI(dvp);
+ if ((nlink_t)dp->i_nlink >= LINK_MAX) {
+ error = EMLINK;
+ goto out;
+ }
+ dmode = vap->va_mode & 0777;
+ dmode |= IFDIR;
+ /*
+ * Must simulate part of ext2_makeinode here to acquire the inode,
+ * but not have it entered in the parent directory. The entry is
+ * made later after writing "." and ".." entries.
+ */
+ error = ext2_valloc(dvp, dmode, cnp->cn_cred, &tvp);
+ if (error)
+ goto out;
+ ip = VTOI(tvp);
+ ip->i_gid = dp->i_gid;
+#ifdef SUIDDIR
+ {
+ /*
+ * if we are hacking owners here, (only do this where told to)
+ * and we are not giving it TOO root, (would subvert quotas)
+ * then go ahead and give it to the other user.
+ * The new directory also inherits the SUID bit.
+ * If user's UID and dir UID are the same,
+ * 'give it away' so that the SUID is still forced on.
+ */
+ if ( (dvp->v_mount->mnt_flag & MNT_SUIDDIR) &&
+ (dp->i_mode & ISUID) && dp->i_uid) {
+ dmode |= ISUID;
+ ip->i_uid = dp->i_uid;
+ } else {
+ ip->i_uid = cnp->cn_cred->cr_uid;
+ }
+ }
+#else
+ ip->i_uid = cnp->cn_cred->cr_uid;
+#endif
+ ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
+ ip->i_mode = dmode;
+ tvp->v_type = VDIR; /* Rest init'd in getnewvnode(). */
+ ip->i_nlink = 2;
+ if (cnp->cn_flags & ISWHITEOUT)
+ ip->i_flags |= UF_OPAQUE;
+ error = ext2_update(tvp, 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_nlink++;
+ dp->i_flag |= IN_CHANGE;
+ error = ext2_update(dvp, 1);
+ if (error)
+ goto bad;
+
+ /* Initialize directory with "." and ".." from static template. */
+ if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs->s_es,
+ EXT2_FEATURE_INCOMPAT_FILETYPE))
+ dtp = &mastertemplate;
+ else
+ dtp = &omastertemplate;
+ dirtemplate = *dtp;
+ dirtemplate.dot_ino = ip->i_number;
+ dirtemplate.dotdot_ino = dp->i_number;
+ /* note that in ext2 DIRBLKSIZ == blocksize, not DEV_BSIZE
+ * so let's just redefine it - for this function only
+ */
+#undef DIRBLKSIZ
+#define DIRBLKSIZ VTOI(dvp)->i_e2fs->s_blocksize
+ dirtemplate.dotdot_reclen = DIRBLKSIZ - 12;
+ error = vn_rdwr(UIO_WRITE, tvp, (caddr_t)&dirtemplate,
+ sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE,
+ IO_NODELOCKED | IO_SYNC | IO_NOMACCHECK, cnp->cn_cred, NOCRED,
+ (int *)0, (struct thread *)0);
+ if (error) {
+ dp->i_nlink--;
+ dp->i_flag |= IN_CHANGE;
+ goto bad;
+ }
+ if (DIRBLKSIZ > VFSTOEXT2(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
+ /* XXX should grow with balloc() */
+ panic("ext2_mkdir: blksize");
+ else {
+ ip->i_size = DIRBLKSIZ;
+ ip->i_flag |= IN_CHANGE;
+ }
+
+ /* Directory set up, now install its entry in the parent directory. */
+ error = ext2_direnter(ip, dvp, cnp);
+ if (error) {
+ dp->i_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_nlink = 0;
+ ip->i_flag |= IN_CHANGE;
+ vput(tvp);
+ } else
+ *ap->a_vpp = tvp;
+out:
+ return (error);
+#undef DIRBLKSIZ
+#define DIRBLKSIZ DEV_BSIZE
+}
+
+/*
+ * Rmdir system call.
+ */
+static int
+ext2_rmdir(ap)
+ struct vop_rmdir_args /* {
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct vnode *dvp = ap->a_dvp;
+ struct componentname *cnp = ap->a_cnp;
+ struct thread *td = cnp->cn_thread;
+ struct inode *ip, *dp;
+ int error;
+
+ ip = VTOI(vp);
+ dp = VTOI(dvp);
+
+ /*
+ * 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_nlink != 2 || !ext2_dirempty(ip, dp->i_number, cnp->cn_cred)) {
+ error = ENOTEMPTY;
+ goto out;
+ }
+ if ((dp->i_flags & APPEND)
+ || (ip->i_flags & (NOUNLINK | IMMUTABLE | 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 = ext2_dirremove(dvp, cnp);
+ if (error)
+ goto out;
+ dp->i_nlink--;
+ dp->i_flag |= IN_CHANGE;
+ cache_purge(dvp);
+ VOP_UNLOCK(dvp, 0, td);
+ /*
+ * 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_nlink -= 2;
+ error = ext2_truncate(vp, (off_t)0, IO_SYNC, cnp->cn_cred, td);
+ cache_purge(ITOV(ip));
+ vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td);
+out:
+ return (error);
+}
+
+/*
+ * symlink -- make a symbolic link
+ */
+static int
+ext2_symlink(ap)
+ struct vop_symlink_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ char *a_target;
+ } */ *ap;
+{
+ struct vnode *vp, **vpp = ap->a_vpp;
+ struct inode *ip;
+ int len, error;
+
+ error = ext2_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_shortlink, len);
+ ip->i_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 | IO_NOMACCHECK,
+ ap->a_cnp->cn_cred, NOCRED, (int *)0, (struct thread *)0);
+ if (error)
+ vput(vp);
+ return (error);
+}
+
+/*
+ * Return target name of a symbolic link
+ */
+static int
+ext2_readlink(ap)
+ struct vop_readlink_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct inode *ip = VTOI(vp);
+ int isize;
+
+ isize = ip->i_size;
+ if (isize < vp->v_mount->mnt_maxsymlinklen) {
+ uiomove((char *)ip->i_shortlink, isize, ap->a_uio);
+ return (0);
+ }
+ return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred));
+}
+
+/*
+ * Calculate the logical to physical mapping if not done already,
+ * then call the device strategy routine.
+ *
+ * In order to be able to swap to a file, the ext2_bmaparray() operation may not
+ * deadlock on memory. See ext2_bmap() for details.
+ */
+static int
+ext2_strategy(ap)
+ struct vop_strategy_args /* {
+ struct vnode *a_vp;
+ struct buf *a_bp;
+ } */ *ap;
+{
+ struct buf *bp = ap->a_bp;
+ struct vnode *vp = ap->a_vp;
+ struct inode *ip;
+ int32_t blkno;
+ int error;
+
+ ip = VTOI(vp);
+ if (vp->v_type == VBLK || vp->v_type == VCHR)
+ panic("ext2_strategy: spec");
+ if (bp->b_blkno == bp->b_lblkno) {
+ error = ext2_bmaparray(vp, bp->b_lblkno, &blkno, NULL, NULL);
+ bp->b_blkno = blkno;
+ if (error) {
+ bp->b_error = error;
+ bp->b_ioflags |= BIO_ERROR;
+ bufdone(bp);
+ return (error);
+ }
+ if ((long)bp->b_blkno == -1)
+ vfs_bio_clrbuf(bp);
+ }
+ if ((long)bp->b_blkno == -1) {
+ bufdone(bp);
+ return (0);
+ }
+ vp = ip->i_devvp;
+ bp->b_dev = vp->v_rdev;
+ VOP_SPECSTRATEGY(vp, bp);
+ return (0);
+}
+
+/*
+ * Print out the contents of an inode.
+ */
+static int
+ext2_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct inode *ip = VTOI(vp);
+
+ printf("ino %lu, on dev %s (%d, %d)", (u_long)ip->i_number,
+ devtoname(ip->i_dev), major(ip->i_dev), minor(ip->i_dev));
+ if (vp->v_type == VFIFO)
+ fifo_printinfo(vp);
+ printf("\n");
+ return (0);
+}
+
+/*
+ * Read wrapper for special devices.
+ */
+static int
+ext2spec_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int error, resid;
+ struct inode *ip;
+ struct uio *uio;
+
+ uio = ap->a_uio;
+ resid = uio->uio_resid;
+ error = VOCALL(spec_vnodeop_p, VOFFSET(vop_read), ap);
+ /*
+ * The inode may have been revoked during the call, so it must not
+ * be accessed blindly here or in the other wrapper functions.
+ */
+ ip = VTOI(ap->a_vp);
+ if (ip != NULL && (uio->uio_resid != resid || (error == 0 && resid != 0)))
+ ip->i_flag |= IN_ACCESS;
+ return (error);
+}
+
+/*
+ * Write wrapper for special devices.
+ */
+static int
+ext2spec_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int error, resid;
+ struct inode *ip;
+ struct uio *uio;
+
+ uio = ap->a_uio;
+ resid = uio->uio_resid;
+ error = VOCALL(spec_vnodeop_p, VOFFSET(vop_write), ap);
+ ip = VTOI(ap->a_vp);
+ if (ip != NULL && (uio->uio_resid != resid || (error == 0 && resid != 0)))
+ VTOI(ap->a_vp)->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (error);
+}
+
+/*
+ * Close wrapper for special devices.
+ *
+ * Update the times on the inode then do device close.
+ */
+static int
+ext2spec_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct thread *a_td;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+
+ VI_LOCK(vp);
+ if (vp->v_usecount > 1)
+ ext2_itimes(vp);
+ VI_UNLOCK(vp);
+ return (VOCALL(spec_vnodeop_p, VOFFSET(vop_close), ap));
+}
+
+/*
+ * Read wrapper for fifos.
+ */
+static int
+ext2fifo_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int error, resid;
+ struct inode *ip;
+ struct uio *uio;
+
+ uio = ap->a_uio;
+ resid = uio->uio_resid;
+ error = VOCALL(fifo_vnodeop_p, VOFFSET(vop_read), ap);
+ ip = VTOI(ap->a_vp);
+ if ((ap->a_vp->v_mount->mnt_flag & MNT_NOATIME) == 0 && ip != NULL &&
+ (uio->uio_resid != resid || (error == 0 && resid != 0)))
+ VTOI(ap->a_vp)->i_flag |= IN_ACCESS;
+ return (error);
+}
+
+/*
+ * Write wrapper for fifos.
+ */
+static int
+ext2fifo_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int error, resid;
+ struct inode *ip;
+ struct uio *uio;
+
+ uio = ap->a_uio;
+ resid = uio->uio_resid;
+ error = VOCALL(fifo_vnodeop_p, VOFFSET(vop_write), ap);
+ ip = VTOI(ap->a_vp);
+ if (ip != NULL && (uio->uio_resid != resid || (error == 0 && resid != 0)))
+ VTOI(ap->a_vp)->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (error);
+}
+
+/*
+ * Close wrapper for fifos.
+ *
+ * Update the times on the inode then do device close.
+ */
+static int
+ext2fifo_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct thread *a_td;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+
+ VI_LOCK(vp);
+ if (vp->v_usecount > 1)
+ ext2_itimes(vp);
+ VI_UNLOCK(vp);
+ return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_close), ap));
+}
+
+/*
+ * Kqfilter wrapper for fifos.
+ *
+ * Fall through to ext2 kqfilter routines if needed
+ */
+static int
+ext2fifo_kqfilter(ap)
+ struct vop_kqfilter_args *ap;
+{
+ int error;
+
+ error = VOCALL(fifo_vnodeop_p, VOFFSET(vop_kqfilter), ap);
+ if (error)
+ error = ext2_kqfilter(ap);
+ return (error);
+}
+
+/*
+ * Return POSIX pathconf information applicable to ext2 filesystems.
+ */
+static int
+ext2_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ int *a_retval;
+ } */ *ap;
+{
+
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = LINK_MAX;
+ return (0);
+ case _PC_NAME_MAX:
+ *ap->a_retval = NAME_MAX;
+ return (0);
+ case _PC_PATH_MAX:
+ *ap->a_retval = PATH_MAX;
+ return (0);
+ case _PC_PIPE_BUF:
+ *ap->a_retval = PIPE_BUF;
+ return (0);
+ case _PC_CHOWN_RESTRICTED:
+ *ap->a_retval = 1;
+ return (0);
+ case _PC_NO_TRUNC:
+ *ap->a_retval = 1;
+ return (0);
+ default:
+ return (EINVAL);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Advisory record locking support
+ */
+static int
+ext2_advlock(ap)
+ struct vop_advlock_args /* {
+ struct vnode *a_vp;
+ caddr_t a_id;
+ int a_op;
+ struct flock *a_fl;
+ int a_flags;
+ } */ *ap;
+{
+ struct inode *ip = VTOI(ap->a_vp);
+
+ return (lf_advlock(ap, &(ip->i_lockf), ip->i_size));
+}
+
+/*
+ * Initialize the vnode associated with a new inode, handle aliased
+ * vnodes.
+ */
+int
+ext2_vinit(mntp, specops, fifoops, vpp)
+ struct mount *mntp;
+ vop_t **specops;
+ vop_t **fifoops;
+ struct vnode **vpp;
+{
+ struct inode *ip;
+ struct vnode *vp;
+ struct timeval tv;
+
+ vp = *vpp;
+ ip = VTOI(vp);
+ switch(vp->v_type = IFTOVT(ip->i_mode)) {
+ case VCHR:
+ case VBLK:
+ vp->v_op = specops;
+ vp = addaliasu(vp, ip->i_rdev);
+ ip->i_vnode = vp;
+ break;
+ case VFIFO:
+ vp->v_op = fifoops;
+ break;
+ default:
+ break;
+
+ }
+ if (ip->i_number == ROOTINO)
+ vp->v_vflag |= VV_ROOT;
+ /*
+ * Initialize modrev times
+ */
+ getmicrouptime(&tv);
+ SETHIGH(ip->i_modrev, tv.tv_sec);
+ SETLOW(ip->i_modrev, tv.tv_usec * 4294);
+ *vpp = vp;
+ return (0);
+}
+
+/*
+ * Allocate a new inode.
+ */
+static int
+ext2_makeinode(mode, dvp, vpp, cnp)
+ int mode;
+ struct vnode *dvp;
+ struct vnode **vpp;
+ struct componentname *cnp;
+{
+ struct inode *ip, *pdir;
+ struct vnode *tvp;
+ int error;
+
+ pdir = VTOI(dvp);
+#ifdef DIAGNOSTIC
+ if ((cnp->cn_flags & HASBUF) == 0)
+ panic("ext2_makeinode: no name");
+#endif
+ *vpp = NULL;
+ if ((mode & IFMT) == 0)
+ mode |= IFREG;
+
+ error = ext2_valloc(dvp, mode, cnp->cn_cred, &tvp);
+ if (error) {
+ return (error);
+ }
+ ip = VTOI(tvp);
+ ip->i_gid = pdir->i_gid;
+#ifdef SUIDDIR
+ {
+ /*
+ * if we are
+ * not the owner of the directory,
+ * and we are hacking owners here, (only do this where told to)
+ * and we are not giving it TOO root, (would subvert quotas)
+ * then go ahead and give it to the other user.
+ * Note that this drops off the execute bits for security.
+ */
+ if ( (dvp->v_mount->mnt_flag & MNT_SUIDDIR) &&
+ (pdir->i_mode & ISUID) &&
+ (pdir->i_uid != cnp->cn_cred->cr_uid) && pdir->i_uid) {
+ ip->i_uid = pdir->i_uid;
+ mode &= ~07111;
+ } else {
+ ip->i_uid = cnp->cn_cred->cr_uid;
+ }
+ }
+#else
+ ip->i_uid = cnp->cn_cred->cr_uid;
+#endif
+ ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
+ ip->i_mode = mode;
+ tvp->v_type = IFTOVT(mode); /* Rest init'd in getnewvnode(). */
+ ip->i_nlink = 1;
+ if ((ip->i_mode & ISGID) && !groupmember(ip->i_gid, cnp->cn_cred) &&
+ suser_cred(cnp->cn_cred, PRISON_ROOT))
+ ip->i_mode &= ~ISGID;
+
+ if (cnp->cn_flags & ISWHITEOUT)
+ ip->i_flags |= UF_OPAQUE;
+
+ /*
+ * Make sure inode goes to disk before directory entry.
+ */
+ error = ext2_update(tvp, 1);
+ if (error)
+ goto bad;
+ error = ext2_direnter(ip, dvp, cnp);
+ if (error)
+ goto bad;
+
+ *vpp = tvp;
+ return (0);
+
+bad:
+ /*
+ * Write error occurred trying to update the inode
+ * or the directory so must deallocate the inode.
+ */
+ ip->i_nlink = 0;
+ ip->i_flag |= IN_CHANGE;
+ vput(tvp);
+ return (error);
+}
+
+static struct filterops ext2read_filtops =
+ { 1, NULL, filt_ext2detach, filt_ext2read };
+static struct filterops ext2write_filtops =
+ { 1, NULL, filt_ext2detach, filt_ext2write };
+static struct filterops ext2vnode_filtops =
+ { 1, NULL, filt_ext2detach, filt_ext2vnode };
+
+static int
+ext2_kqfilter(ap)
+ struct vop_kqfilter_args /* {
+ struct vnode *a_vp;
+ struct knote *a_kn;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct knote *kn = ap->a_kn;
+
+ switch (kn->kn_filter) {
+ case EVFILT_READ:
+ kn->kn_fop = &ext2read_filtops;
+ break;
+ case EVFILT_WRITE:
+ kn->kn_fop = &ext2write_filtops;
+ break;
+ case EVFILT_VNODE:
+ kn->kn_fop = &ext2vnode_filtops;
+ break;
+ default:
+ return (1);
+ }
+
+ kn->kn_hook = (caddr_t)vp;
+
+ if (vp->v_pollinfo == NULL)
+ v_addpollinfo(vp);
+ mtx_lock(&vp->v_pollinfo->vpi_lock);
+ SLIST_INSERT_HEAD(&vp->v_pollinfo->vpi_selinfo.si_note, kn, kn_selnext);
+ mtx_unlock(&vp->v_pollinfo->vpi_lock);
+
+ return (0);
+}
+
+static void
+filt_ext2detach(struct knote *kn)
+{
+ struct vnode *vp = (struct vnode *)kn->kn_hook;
+
+ KASSERT(vp->v_pollinfo != NULL, ("Mising v_pollinfo"));
+ mtx_lock(&vp->v_pollinfo->vpi_lock);
+ SLIST_REMOVE(&vp->v_pollinfo->vpi_selinfo.si_note,
+ kn, knote, kn_selnext);
+ mtx_unlock(&vp->v_pollinfo->vpi_lock);
+}
+
+/*ARGSUSED*/
+static int
+filt_ext2read(struct knote *kn, long hint)
+{
+ struct vnode *vp = (struct vnode *)kn->kn_hook;
+ struct inode *ip = VTOI(vp);
+
+ /*
+ * filesystem is gone, so set the EOF flag and schedule
+ * the knote for deletion.
+ */
+ if (hint == NOTE_REVOKE) {
+ kn->kn_flags |= (EV_EOF | EV_ONESHOT);
+ return (1);
+ }
+
+ kn->kn_data = ip->i_size - kn->kn_fp->f_offset;
+ return (kn->kn_data != 0);
+}
+
+/*ARGSUSED*/
+static int
+filt_ext2write(struct knote *kn, long hint)
+{
+
+ /*
+ * filesystem is gone, so set the EOF flag and schedule
+ * the knote for deletion.
+ */
+ if (hint == NOTE_REVOKE)
+ kn->kn_flags |= (EV_EOF | EV_ONESHOT);
+
+ kn->kn_data = 0;
+ return (1);
+}
+
+static int
+filt_ext2vnode(struct knote *kn, long hint)
+{
+
+ if (kn->kn_sfflags & hint)
+ kn->kn_fflags |= hint;
+ if (hint == NOTE_REVOKE) {
+ kn->kn_flags |= EV_EOF;
+ return (1);
+ }
+ return (kn->kn_fflags != 0);
+}
diff --git a/sys/gnu/ext2fs/fs.h b/sys/gnu/ext2fs/fs.h
new file mode 100644
index 0000000..50fb711
--- /dev/null
+++ b/sys/gnu/ext2fs/fs.h
@@ -0,0 +1,180 @@
+/*
+ * modified for EXT2FS support in Lites 1.1
+ *
+ * Aug 1995, Godmar Back (gback@cs.utah.edu)
+ * University of Utah, Department of Computer Science
+ */
+/*
+ * 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.7 (Berkeley) 4/19/94
+ * $FreeBSD$
+ */
+
+/*
+ * 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 SBSIZE 1024
+#define SBLOCK 2
+
+/*
+ * 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
+
+/*
+ * Macros for access to superblock array structures
+ */
+
+/*
+ * Convert cylinder group to base address of its global summary info.
+ */
+#define fs_cs(fs, cgindx) (((struct ext2_group_desc *) \
+ (fs->s_group_desc[cgindx / EXT2_DESC_PER_BLOCK(fs)]->b_data)) \
+ [cgindx % EXT2_DESC_PER_BLOCK(fs)])
+
+/*
+ * Turn file system block numbers into disk block addresses.
+ * This maps file system blocks to device size blocks.
+ */
+#define fsbtodb(fs, b) ((b) << ((fs)->s_fsbtodb))
+#define dbtofsb(fs, b) ((b) >> ((fs)->s_fsbtodb))
+
+/* get group containing inode */
+#define ino_to_cg(fs, x) (((x) - 1) / EXT2_INODES_PER_GROUP(fs))
+
+/* get block containing inode from its number x */
+#define ino_to_fsba(fs, x) fs_cs(fs, ino_to_cg(fs, x)).bg_inode_table + \
+ (((x)-1) % EXT2_INODES_PER_GROUP(fs))/EXT2_INODES_PER_BLOCK(fs)
+
+/* get offset for inode in block */
+#define ino_to_fsbo(fs, x) ((x-1) % EXT2_INODES_PER_BLOCK(fs))
+
+/*
+ * 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->s_es->s_first_data_block) / \
+ EXT2_BLOCKS_PER_GROUP(fs))
+#define dtogd(fs, d) (((d) - fs->s_es->s_first_data_block) % \
+ EXT2_BLOCKS_PER_GROUP(fs))
+
+/*
+ * 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->fs_bsize) */ \
+ ((loc) & (fs)->s_qbmask)
+
+#define lblktosize(fs, blk) /* calculates (blk * fs->fs_bsize) */ \
+ ((blk) << (fs->s_bshift))
+
+#define lblkno(fs, loc) /* calculates (loc / fs->fs_bsize) */ \
+ ((loc) >> (fs->s_bshift))
+
+/* no fragments -> logical block number equal # of frags */
+#define numfrags(fs, loc) /* calculates (loc / fs->fs_fsize) */ \
+ ((loc) >> (fs->s_bshift))
+
+#define fragroundup(fs, size) /* calculates roundup(size, fs->fs_fsize) */ \
+ roundup(size, fs->s_frag_size)
+ /* was (((size) + (fs)->fs_qfmask) & (fs)->fs_fmask) */
+
+/*
+ * Determining the size of a file block in the file system.
+ * easy w/o fragments
+ */
+#define blksize(fs, ip, lbn) ((fs)->s_frag_size)
+
+/*
+ * INOPB is the number of inodes in a secondary storage block.
+ */
+#define INOPB(fs) EXT2_INODES_PER_BLOCK(fs)
+
+/*
+ * NINDIR is the number of indirects in a file system block.
+ */
+#define NINDIR(fs) (EXT2_ADDR_PER_BLOCK(fs))
+
+extern int inside[], around[];
+extern u_char *fragtbl[];
+
+/* a few remarks about superblock locking/unlocking
+ * Linux provides special routines for doing so
+ * I haven't figured out yet what BSD does
+ * I think I'll try a VOP_LOCK/VOP_UNLOCK on the device vnode
+ */
+#define DEVVP(inode) (VFSTOEXT2(ITOV(inode)->v_mount)->um_devvp)
+#define lock_super(devvp) vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, curthread)
+#define unlock_super(devvp) VOP_UNLOCK(devvp, 0, curthread)
+
+/*
+ * To lock a buffer, set the B_LOCKED flag and then brelse() it. To unlock,
+ * reset the B_LOCKED flag and brelse() the buffer back on the LRU list
+ */
+#define LCK_BUF(bp) { \
+ int s; \
+ s = splbio(); \
+ (bp)->b_flags |= B_LOCKED; \
+ splx(s); \
+ brelse(bp); \
+}
+
+#define ULCK_BUF(bp) { \
+ long flags; \
+ int s; \
+ s = splbio(); \
+ flags = (bp)->b_flags; \
+ (bp)->b_flags &= ~(B_DIRTY | B_LOCKED); \
+ BUF_LOCK(bp, LK_EXCLUSIVE); \
+ bremfree(bp); \
+ splx(s); \
+ if (flags & B_DIRTY) \
+ bwrite(bp); \
+ else \
+ brelse(bp); \
+}
diff --git a/sys/gnu/ext2fs/i386-bitops.h b/sys/gnu/ext2fs/i386-bitops.h
index 86068d0..595e870 100644
--- a/sys/gnu/ext2fs/i386-bitops.h
+++ b/sys/gnu/ext2fs/i386-bitops.h
@@ -1,5 +1,12 @@
-#ifndef _I386_BITOPS_H
-#define _I386_BITOPS_H
+/* $FreeBSD$ */
+/*
+ * this is mixture of i386/bitops.h and asm/string.h
+ * taken from the Linux source tree
+ *
+ * XXX replace with Mach routines or reprogram in C
+ */
+#ifndef _SYS_GNU_EXT2FS_I386_BITOPS_H_
+#define _SYS_GNU_EXT2FS_I386_BITOPS_H_
/*
* Copyright 1992, Linus Torvalds.
@@ -13,141 +20,85 @@
* bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1).
*/
-#ifdef __SMP__
-#define LOCK_PREFIX "lock ; "
-#else
-#define LOCK_PREFIX ""
-#endif
-
-/*
- * Function prototypes to keep gcc -Wall happy
- */
-extern void set_bit(int nr, volatile void * addr);
-extern void clear_bit(int nr, volatile void * addr);
-extern void change_bit(int nr, volatile void * addr);
-extern int test_and_set_bit(int nr, volatile void * addr);
-extern int test_and_clear_bit(int nr, volatile void * addr);
-extern int test_and_change_bit(int nr, volatile void * addr);
-extern int __constant_test_bit(int nr, const volatile void * addr);
-extern int __test_bit(int nr, volatile void * addr);
-extern int find_first_zero_bit(void * addr, unsigned size);
-extern int find_next_zero_bit (void * addr, int size, int offset);
-extern unsigned long ffz(unsigned long word);
-
/*
* Some hacks to defeat gcc over-optimizations..
*/
struct __dummy { unsigned long a[100]; };
-#define ADDR (*(volatile struct __dummy *) addr)
-#define CONST_ADDR (*(volatile const struct __dummy *) addr)
+#define ADDR (*(struct __dummy *) addr)
-extern __inline__ void set_bit(int nr, volatile void * addr)
-{
- __asm__ __volatile__( LOCK_PREFIX
- "btsl %1,%0"
- :"=m" (ADDR)
- :"Ir" (nr));
-}
-
-extern __inline__ void clear_bit(int nr, volatile void * addr)
-{
- __asm__ __volatile__( LOCK_PREFIX
- "btrl %1,%0"
- :"=m" (ADDR)
- :"Ir" (nr));
-}
-
-extern __inline__ void change_bit(int nr, volatile void * addr)
-{
- __asm__ __volatile__( LOCK_PREFIX
- "btcl %1,%0"
- :"=m" (ADDR)
- :"Ir" (nr));
-}
-
-extern __inline__ int test_and_set_bit(int nr, volatile void * addr)
+static __inline__ int set_bit(int nr, void * addr)
{
int oldbit;
- __asm__ __volatile__( LOCK_PREFIX
- "btsl %2,%1\n\tsbbl %0,%0"
+ __asm__ __volatile__("btsl %2,%1\n\tsbbl %0,%0"
:"=r" (oldbit),"=m" (ADDR)
- :"Ir" (nr));
+ :"ir" (nr));
return oldbit;
}
-extern __inline__ int test_and_clear_bit(int nr, volatile void * addr)
+static __inline__ int clear_bit(int nr, void * addr)
{
int oldbit;
- __asm__ __volatile__( LOCK_PREFIX
- "btrl %2,%1\n\tsbbl %0,%0"
+ __asm__ __volatile__("btrl %2,%1\n\tsbbl %0,%0"
:"=r" (oldbit),"=m" (ADDR)
- :"Ir" (nr));
+ :"ir" (nr));
return oldbit;
}
-extern __inline__ int test_and_change_bit(int nr, volatile void * addr)
+static __inline__ int change_bit(int nr, void * addr)
{
int oldbit;
- __asm__ __volatile__( LOCK_PREFIX
- "btcl %2,%1\n\tsbbl %0,%0"
+ __asm__ __volatile__("btcl %2,%1\n\tsbbl %0,%0"
:"=r" (oldbit),"=m" (ADDR)
- :"Ir" (nr));
+ :"ir" (nr));
return oldbit;
}
/*
- * This routine doesn't need to be atomic.
+ * This routine doesn't need to be atomic, but it's faster to code it
+ * this way.
*/
-extern __inline__ int __constant_test_bit(int nr, const volatile void * addr)
-{
- return ((1UL << (nr & 31)) & (((const volatile unsigned int *) addr)[nr >> 5])) != 0;
-}
-
-extern __inline__ int __test_bit(int nr, volatile void * addr)
+static __inline__ int test_bit(int nr, void * addr)
{
int oldbit;
- __asm__ __volatile__(
- "btl %2,%1\n\tsbbl %0,%0"
+ __asm__ __volatile__("btl %2,%1\n\tsbbl %0,%0"
:"=r" (oldbit)
- :"m" (ADDR),"Ir" (nr));
+ :"m" (ADDR),"ir" (nr));
return oldbit;
}
-#define test_bit(nr,addr) \
-(__builtin_constant_p(nr) ? \
- __constant_test_bit((nr),(addr)) : \
- __test_bit((nr),(addr)))
-
/*
* Find-bit routines..
*/
-extern __inline__ int find_first_zero_bit(void * addr, unsigned size)
+static __inline__ int find_first_zero_bit(void * addr, unsigned size)
{
- int d0, d1, d2;
int res;
+ int _count = (size + 31) >> 5;
if (!size)
return 0;
- __asm__("movl $-1,%%eax\n\t"
- "xorl %%edx,%%edx\n\t"
- "repe; scasl\n\t"
- "je 1f\n\t"
- "xorl -4(%%edi),%%eax\n\t"
- "subl $4,%%edi\n\t"
- "bsfl %%eax,%%edx\n"
- "1:\tsubl %%ebx,%%edi\n\t"
- "shll $3,%%edi\n\t"
- "addl %%edi,%%edx"
- :"=d" (res), "=&c" (d0), "=&D" (d1), "=&a" (d2)
- :"1" ((size + 31) >> 5), "2" (addr), "b" (addr));
+ __asm__(" \n\
+ cld \n\
+ movl $-1,%%eax \n\
+ xorl %%edx,%%edx \n\
+ repe; scasl \n\
+ je 1f \n\
+ xorl -4(%%edi),%%eax \n\
+ subl $4,%%edi \n\
+ bsfl %%eax,%%edx \n\
+1: subl %%ebx,%%edi \n\
+ shll $3,%%edi \n\
+ addl %%edi,%%edx"
+ : "=c" (_count), "=D" (addr), "=d" (res)
+ : "0" (_count), "1" (addr), "b" (addr)
+ : "ax");
return res;
}
-extern __inline__ int find_next_zero_bit (void * addr, int size, int offset)
+static __inline__ int find_next_zero_bit (void * addr, int size, int offset)
{
unsigned long * p = ((unsigned long *) addr) + (offset >> 5);
int set = 0, bit = offset & 31, res;
@@ -156,10 +107,11 @@ extern __inline__ int find_next_zero_bit (void * addr, int size, int offset)
/*
* Look for zero in first byte
*/
- __asm__("bsfl %1,%0\n\t"
- "jne 1f\n\t"
- "movl $32, %0\n"
- "1:"
+ __asm__(" \n\
+ bsfl %1,%0 \n\
+ jne 1f \n\
+ movl $32, %0 \n\
+1: "
: "=r" (set)
: "r" (~(*p >> bit)));
if (set < (32 - bit))
@@ -178,7 +130,7 @@ extern __inline__ int find_next_zero_bit (void * addr, int size, int offset)
* ffz = Find First Zero in word. Undefined if no zero exists,
* so code should check against ~0UL first..
*/
-extern __inline__ unsigned long ffz(unsigned long word)
+static __inline__ unsigned long ffz(unsigned long word)
{
__asm__("bsfl %1,%0"
:"=r" (word)
@@ -186,50 +138,25 @@ extern __inline__ unsigned long ffz(unsigned long word)
return word;
}
-#ifdef __KERNEL__
-
+/*
+ * memscan() taken from linux asm/string.h
+ */
/*
- * ffs: find first bit set. This is defined the same way as
- * the libc and compiler builtin ffs routines, therefore
- * differs in spirit from the above ffz (man ffs).
+ * find the first occurrence of byte 'c', or 1 past the area if none
*/
-
-extern __inline__ int ffs(int x)
+static __inline__ char * memscan(void * addr, unsigned char c, int size)
{
- int r;
-
- __asm__("bsfl %1,%0\n\t"
- "jnz 1f\n\t"
- "movl $-1,%0\n"
- "1:" : "=r" (r) : "g" (x));
- return r+1;
+ if (!size)
+ return addr;
+ __asm__(" \n\
+ cld \n\
+ repnz; scasb \n\
+ jnz 1f \n\
+ dec %%edi \n\
+1: "
+ : "=D" (addr), "=c" (size)
+ : "0" (addr), "1" (size), "a" (c));
+ return addr;
}
-/*
- * hweightN: returns the hamming weight (i.e. the number
- * of bits set) of a N-bit word
- */
-
-#define hweight32(x) generic_hweight32(x)
-#define hweight16(x) generic_hweight16(x)
-#define hweight8(x) generic_hweight8(x)
-
-#endif /* __KERNEL__ */
-
-#ifdef __KERNEL__
-
-#define ext2_set_bit test_and_set_bit
-#define ext2_clear_bit test_and_clear_bit
-#define ext2_test_bit test_bit
-#define ext2_find_first_zero_bit find_first_zero_bit
-#define ext2_find_next_zero_bit find_next_zero_bit
-
-/* Bitmap functions for the minix filesystem. */
-#define minix_set_bit(nr,addr) test_and_set_bit(nr,addr)
-#define minix_clear_bit(nr,addr) test_and_clear_bit(nr,addr)
-#define minix_test_bit(nr,addr) test_bit(nr,addr)
-#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size)
-
-#endif /* __KERNEL__ */
-
-#endif /* _I386_BITOPS_H */
+#endif /* !_SYS_GNU_EXT2FS_I386_BITOPS_H_ */
diff --git a/sys/gnu/ext2fs/inode.h b/sys/gnu/ext2fs/inode.h
new file mode 100644
index 0000000..4704619
--- /dev/null
+++ b/sys/gnu/ext2fs/inode.h
@@ -0,0 +1,177 @@
+/*
+ * 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.
+ *
+ * @(#)inode.h 8.9 (Berkeley) 5/14/95
+ * $FreeBSD$
+ */
+
+#ifndef _SYS_GNU_EXT2FS_INODE_H_
+#define _SYS_GNU_EXT2FS_INODE_H_
+
+#include <sys/lock.h>
+#include <sys/queue.h>
+
+#define ROOTINO ((ino_t)2)
+
+#define NDADDR 12 /* Direct addresses in inode. */
+#define NIADDR 3 /* Indirect addresses in inode. */
+
+/*
+ * This must agree with the definition in <ufs/ufs/dir.h>.
+ */
+#define doff_t int32_t
+
+/*
+ * The inode is used to describe each active (or recently active) file in the
+ * EXT2FS filesystem. It is composed of two types of information. The first
+ * part is the information that is needed only while the file is active (such
+ * as the identity of the file and linkage to speed its lookup). The second
+ * part is the permanent meta-data associated with the file which is read in
+ * from the permanent dinode from long term storage when the file becomes
+ * active, and is put back when the file is no longer being used.
+ */
+struct inode {
+ LIST_ENTRY(inode) i_hash;/* Hash chain. */
+ struct vnode *i_vnode;/* Vnode associated with this inode. */
+ struct vnode *i_devvp;/* Vnode for block I/O. */
+ u_int32_t i_flag; /* flags, see below */
+ dev_t i_dev; /* Device associated with the inode. */
+ ino_t i_number; /* The identity of the inode. */
+
+ struct ext2_sb_info *i_e2fs; /* EXT2FS */
+ u_quad_t i_modrev; /* Revision level for NFS lease. */
+ struct lockf *i_lockf;/* Head of byte-level lock list. */
+ /*
+ * Side effects; used during directory lookup.
+ */
+ int32_t i_count; /* Size of free slot in directory. */
+ doff_t i_endoff; /* End of useful stuff in directory. */
+ doff_t i_diroff; /* Offset in dir, where we found last entry. */
+ doff_t i_offset; /* Offset of free space in directory. */
+ ino_t i_ino; /* Inode number of found directory. */
+ u_int32_t i_reclen; /* Size of found directory entry. */
+
+ u_int32_t i_block_group;
+ u_int32_t i_next_alloc_block;
+ u_int32_t i_next_alloc_goal;
+ u_int32_t i_prealloc_block;
+ u_int32_t i_prealloc_count;
+
+ /* Fields from struct dinode in UFS. */
+ u_int16_t i_mode; /* IFMT, permissions; see below. */
+ int16_t i_nlink; /* File link count. */
+ u_int64_t i_size; /* File byte count. */
+ int32_t i_atime; /* Last access time. */
+ int32_t i_atimensec; /* Last access time. */
+ int32_t i_mtime; /* Last modified time. */
+ int32_t i_mtimensec; /* Last modified time. */
+ int32_t i_ctime; /* Last inode change time. */
+ int32_t i_ctimensec; /* Last inode change time. */
+ int32_t i_db[NDADDR]; /* Direct disk blocks. */
+ int32_t i_ib[NIADDR]; /* Indirect disk blocks. */
+ u_int32_t i_flags; /* Status flags (chflags). */
+ int32_t i_blocks; /* Blocks actually held. */
+ int32_t i_gen; /* Generation number. */
+ u_int32_t i_uid; /* File owner. */
+ u_int32_t i_gid; /* File group. */
+};
+
+/*
+ * The di_db 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 i_shortlink i_db
+#define i_rdev i_db[0]
+#define MAXSYMLINKLEN ((NDADDR + NIADDR) * sizeof(int32_t))
+
+/* File permissions. */
+#define IEXEC 0000100 /* Executable. */
+#define IWRITE 0000200 /* Writeable. */
+#define IREAD 0000400 /* Readable. */
+#define ISVTX 0001000 /* Sticky bit. */
+#define ISGID 0002000 /* Set-gid. */
+#define ISUID 0004000 /* Set-uid. */
+
+/* File types. */
+#define IFMT 0170000 /* Mask of file type. */
+#define IFIFO 0010000 /* Named pipe (fifo). */
+#define IFCHR 0020000 /* Character device. */
+#define IFDIR 0040000 /* Directory file. */
+#define IFBLK 0060000 /* Block device. */
+#define IFREG 0100000 /* Regular file. */
+#define IFLNK 0120000 /* Symbolic link. */
+#define IFSOCK 0140000 /* UNIX domain socket. */
+#define IFWHT 0160000 /* Whiteout. */
+
+/* These flags are kept in i_flag. */
+#define IN_ACCESS 0x0001 /* Access time update request. */
+#define IN_CHANGE 0x0002 /* Inode change time update request. */
+#define IN_UPDATE 0x0004 /* Modification time update request. */
+#define IN_MODIFIED 0x0008 /* Inode has been modified. */
+#define IN_RENAME 0x0010 /* Inode is being renamed. */
+#define IN_HASHED 0x0020 /* Inode is on hash list */
+#define IN_LAZYMOD 0x0040 /* Modified, but don't write yet. */
+#define IN_SPACECOUNTED 0x0080 /* Blocks to be freed in free count. */
+
+#ifdef _KERNEL
+/*
+ * Structure used to pass around logical block paths generated by
+ * ext2_getlbns and used by truncate and bmap code.
+ */
+struct indir {
+ int32_t in_lbn; /* Logical block number. */
+ int in_off; /* Offset in buffer. */
+ int in_exists; /* Flag if the block exists. */
+};
+
+/* Convert between inode pointers and vnode pointers. */
+#define VTOI(vp) ((struct inode *)(vp)->v_data)
+#define ITOV(ip) ((ip)->i_vnode)
+
+/* This overlays the fid structure (see mount.h). */
+struct ufid {
+ u_int16_t ufid_len; /* Length of structure. */
+ u_int16_t ufid_pad; /* Force 32-bit alignment. */
+ ino_t ufid_ino; /* File number (ino). */
+ int32_t ufid_gen; /* Generation number. */
+};
+#endif /* _KERNEL */
+
+#endif /* !_SYS_GNU_EXT2FS_INODE_H_ */
OpenPOWER on IntegriCloud