--- linux-2.2.18pre17.ext3-0.0.5a/Makefile.~1~	Wed Oct 25 19:09:54 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/Makefile	Wed Oct 25 19:11:34 2000
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 2
 SUBLEVEL = 18
-EXTRAVERSION = pre17
+EXTRAVERSION = pre17ext3
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 
@@ -423,6 +423,7 @@
 		-o -regex '.*ksymoops/.*' \) -print`
 	rm -f core `find . -type f -name 'core' -print`
 	rm -f core `find . -name '.*.flags' -print`
+	rm -f map map.out
 	rm -f ksym.[ch] dummy_sym.c System.map.sv
 	rm -f vmlinux System.map
 	rm -f .tmp*
--- linux-2.2.18pre17.ext3-0.0.5a/arch/i386/kdb/kdb.c.~1~	Wed Oct 25 19:09:54 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/arch/i386/kdb/kdb.c	Wed Oct 25 19:10:56 2000
@@ -1517,7 +1517,7 @@
 kdb_ll(int argc, const char **argv, const char **envp, struct pt_regs *regs)
 {
 	int diag;
-	unsigned long addr;
+	unsigned long addr, firstaddr;
 	long 	      offset = 0;
 	unsigned long va;
 	unsigned long linkoffset;
@@ -1537,12 +1537,13 @@
 		return diag;
 
 	/*
-	 * Using the starting address as 
-	 * the first element in the list, and assuming that
-	 * the list ends with a null pointer.
+	 * Using the starting address as the first element in the list,
+	 * and assuming that the list ends with a null pointer or with
+	 * the first element again (do the right thing for both
+	 * null-terminated and circular lists).
 	 */
 
-	va = addr;
+	va = firstaddr = addr;
 
 	while (va) {
 		char buf[80];
@@ -1558,6 +1559,8 @@
 			kdb_flags &= ~KDB_FLAG_SUPRESS;
 			return 0;
 		}
+		if (va == firstaddr)
+			break;
 	}
 
 	return 0;
--- linux-2.2.18pre17.ext3-0.0.5a/arch/i386/kdb/modules/Makefile.~1~	Wed Oct 25 19:09:54 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/arch/i386/kdb/modules/Makefile	Wed Oct 25 19:10:56 2000
@@ -12,6 +12,6 @@
 
 #O_TARGET     := kdbm_vm.o
 
-M_OBJS       := kdbm_vm.o
+M_OBJS       := kdbm_vm.o kdbm_jfs.o
 
 include $(TOPDIR)/Rules.make
--- linux-2.2.18pre17.ext3-0.0.5a/arch/i386/kdb/modules/kdbm_jfs.c.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/arch/i386/kdb/modules/kdbm_jfs.c	Wed Oct 25 19:10:56 2000
@@ -0,0 +1,311 @@
+#include <linux/kdb.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/jfs.h>
+
+#define FETCH(dest, source) \
+do {							\
+	int i;						\
+	unsigned char *p = (unsigned char *)(dest);	\
+							\
+	for (i=0; i<sizeof(*(dest)); i++)		\
+		*p++ = kdbgetword((source)+i, 1);	\
+} while (0)
+
+const char *bh_listnames[] = {
+	"BUF_CLEAN",
+	"BUF_LOCKED",
+	"BUF_DIRTY",
+	"BUF_JOURNAL"
+};
+
+const char *jfs_listnames[] = {
+	"BJ_None",
+	"BJ_Data",
+	"BJ_Metadata",
+	"BJ_Forget",
+	"BJ_IO",
+	"BJ_Shadow",
+	"BJ_LogCtl",
+	"BJ_Reserved"
+};
+
+const char *statenames[] = {
+	"T_RUNNING",
+	"T_LOCKED",
+	"T_RUNDOWN",
+	"T_FLUSH",
+	"T_COMMIT",
+	"T_FINISH"
+};
+
+const char *bh_statenames = "UDLRPTJW";
+int bh_states[] = {0,1,2,3,6,7,8,9,-1};
+
+const char *bh_listname(int list)
+{
+	if (list >= NR_LIST)
+		return "*INVALID*";
+	return bh_listnames[list];
+}
+
+const char *jfs_listname(int list)
+{
+	if (list >= BJ_Types)
+		return "*INVALID*";
+	return jfs_listnames[list];
+}
+
+const char *transaction_state(int state)
+{
+	static char statenum[16];
+	if (state > T_FINISHED) {
+		sprintf(statenum, "[%d]", state);
+		return statenum;
+	}
+	return statenames[state];
+}
+
+const char *bh_state(int state)
+{
+	static char buffer[9];
+	char *result = buffer;
+	int i = 0;
+	do {
+		if (state & (1<<bh_states[i]))
+			result[i] = bh_statenames[i];
+		else
+			result[i] = '.';
+		i++;
+	} while (bh_states[i] >= 0);
+	result[i] = 0;
+	return result;
+}
+
+
+int
+kdbm_bh(int argc, const char **argv, const char **envp, struct pt_regs *regs)
+{
+	struct buffer_head bh;
+	unsigned long addr;
+	long	offset=0;
+	int nextarg;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+
+	nextarg = 1;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	addr += offset;
+
+	FETCH(&bh, addr);
+
+	kdb_printf("struct buffer_head 0x%lx for %d bytes\n", addr, sizeof(bh));
+	kdb_printf("  dev   = %-11s          blk   = 0x%08lx   size  = 0x%04lx\n",
+	       kdevname(bh.b_dev), bh.b_blocknr, bh.b_size);
+	kdb_printf("  data  = %p\n", bh.b_data);
+	kdb_printf("  list  = %-3d (%-11s)    state = %7s       count = %-5d\n",
+	       bh.b_list, bh_listname(bh.b_list),
+	       bh_state(bh.b_state),
+	       bh.b_count);
+	kdb_printf("  jlist = %-3d (%-11s)    transaction      = %p\n",
+	       bh.b_jlist, jfs_listname(bh.b_jlist), bh.b_transaction);
+	kdb_printf("  frozen data       = %p    cp_transaction   = %p\n",
+	       bh.b_frozen_data, bh.b_cp_transaction);
+	kdb_printf("  committed data    = %p    next_transaction = %p\n",
+	       bh.b_committed_data, bh.b_next_transaction);
+	kdb_printf("  forget transacion = %p\n", bh.b_forget_transaction);
+	return 0;
+}
+
+int
+kdbm_bhi(int argc, const char **argv, const char **envp, struct pt_regs *regs)
+{
+	show_buffers();
+	return 0;
+}
+
+
+static void show_journal(journal_t *journal, unsigned long where)
+{
+	kdb_printf("journal at %08lx for %d bytes:\n", where, sizeof(*journal));
+
+	kdb_printf(" State: ");
+	if (journal->j_flags & JFS_UNMOUNT)
+		kdb_printf("Unmount ");
+	if (journal->j_flags & JFS_SYNC)
+		kdb_printf("Sync ");
+	if (journal->j_locked)
+		kdb_printf("Locked ");
+	kdb_printf("\n");
+
+	kdb_printf(" Log head: %ld  Tail: %ld  Free: %ld     "
+	       "Valid: %ld to %ld (+%d)\n",
+	       journal->j_head, journal->j_tail, journal->j_free,
+	       journal->j_first, journal->j_last, journal->j_maxlen);
+
+	kdb_printf(" Transactions IDs:\n");
+	kdb_printf("  Oldest:     %-8ld          Next:       %-8ld\n",
+		   journal->j_tail_sequence,
+		   journal->j_transaction_sequence);
+	kdb_printf("    Committed:  %-8ld          Req commit: %-8ld\n",
+		   journal->j_commit_sequence,
+		   journal->j_commit_request);
+	kdb_printf("\n");
+}
+
+static void show_transaction_brief(transaction_t *transaction,
+				   unsigned long where,
+				   const char *kind)
+{
+	kdb_printf("%s%s at 0x%08lx (journal 0x%08x) for %d bytes:\n",
+	       kind ? kind : "", kind ? " transaction" : "Transaction",
+	       where, transaction->t_journal, sizeof(*transaction));
+
+	kdb_printf(" ID: %-5ld   State: %9s   "
+	       "Buffers: %-5d Log at: %-5ld\n",
+	       transaction->t_tid, transaction_state(transaction->t_state),
+	       transaction->t_nr_buffers, transaction->t_log_start);
+	kdb_printf(" Updates: %-5d   Credits: %-5d\n",
+	       transaction->t_updates, transaction->t_outstanding_credits);
+}
+
+static void show_transaction_list(unsigned long where, const char *kind,
+				  unsigned long offset)
+{
+	unsigned long bp, first, count=0;
+
+	if (!where)
+		return;
+
+	first = where;
+	
+	do {
+		FETCH(&bp, where+offset);
+		where = bp;
+		count++;
+	} while (bp != first);
+
+	kdb_printf (" %9s list: %5ld buffers\n", kind, count);
+}
+
+#define SHOW_BUFS(bh, kind, field) \
+do {								    \
+	unsigned long offset, bp;				    \
+	bp = (unsigned long) (bh);				    \
+	offset = ((char *) &((struct buffer_head *)(bp))->field) -  \
+		 ((char *) (bp));				    \
+	show_transaction_list((bp), kind, offset);		    \
+} while (0)
+
+static void show_transaction_full(transaction_t *transaction,
+				  unsigned long where,
+				  const char *kind)
+{
+	show_transaction_brief(transaction, where, kind);
+
+	SHOW_BUFS(transaction->t_datalist,        "Data",      b_tnext);
+	SHOW_BUFS(transaction->t_buffers,         "Metadata",  b_tnext);
+	SHOW_BUFS(transaction->t_forget,          "Forget",    b_tnext);
+	SHOW_BUFS(transaction->t_checkpoint_list, "Checkpoint",b_cpnext);
+	SHOW_BUFS(transaction->t_iobuf_list,      "Iobuf",     b_tnext);
+	SHOW_BUFS(transaction->t_shadow_list,     "Shadow",    b_tnext);
+	SHOW_BUFS(transaction->t_log_list,        "Log",       b_tnext);
+}
+
+int
+kdbm_jfs(int argc, const char **argv, const char **envp, struct pt_regs *regs)
+{
+	journal_t journal;
+	transaction_t transaction;
+	unsigned long jaddr, taddr;
+
+	long	offset=0;
+	int nextarg;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+
+	nextarg = 1;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &jaddr, &offset, NULL,regs);
+	if (diag)
+		return diag;
+
+	jaddr += offset;
+
+	FETCH(&journal, jaddr);
+	show_journal(&journal, jaddr);
+
+	taddr = (unsigned long) journal.j_running_transaction;
+	if (taddr) {
+		FETCH(&transaction, taddr);
+		show_transaction_brief(&transaction, taddr, "Running");
+	}
+
+	taddr = (unsigned long) journal.j_committing_transaction;
+	if (taddr) {
+		FETCH(&transaction, taddr);
+		show_transaction_brief(&transaction, taddr, "Committing");
+	}
+
+	taddr = (unsigned long) journal.j_checkpoint_transactions;
+	if (taddr) do {
+		FETCH(&transaction, taddr);
+		show_transaction_brief(&transaction, taddr, "Checkpointed");
+		taddr = (unsigned long) transaction.t_cpnext;
+	} while (taddr != (unsigned long) journal.j_checkpoint_transactions);
+
+	return 0;
+}
+
+int
+kdbm_tr(int argc, const char **argv, const char **envp, struct pt_regs *regs)
+{
+	transaction_t transaction;
+	unsigned long taddr;
+
+	long	offset=0;
+	int nextarg;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+
+	nextarg = 1;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &taddr, &offset, NULL,regs);
+	if (diag)
+		return diag;
+
+	taddr += offset;
+
+	FETCH(&transaction, taddr);
+	show_transaction_full(&transaction, taddr, NULL);
+
+	return 0;
+}
+
+int
+init_module(void)
+{
+	kdb_register("bh",  kdbm_bh,  "<vaddr>", "Display buffer_head", 0);
+	kdb_register("bhi", kdbm_bhi, "", "Summarise buffer_head info", 0);
+	kdb_register("jfs", kdbm_jfs, "<vaddr>", "Display journal_t", 0);
+	kdb_register("tr",  kdbm_tr,  "<vaddr>", "Display transaction_t", 0);
+
+	return 0;
+}
+
+void
+cleanup_module(void)
+{
+	kdb_unregister("bh");
+	kdb_unregister("bhi");
+	kdb_unregister("jfs");
+	kdb_unregister("tr");
+}
--- linux-2.2.18pre17.ext3-0.0.5a/fs/Config.in.~1~	Wed Oct 25 18:57:52 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/Config.in	Wed Oct 25 19:10:56 2000
@@ -49,6 +49,8 @@
 fi
 tristate 'ROM filesystem support' CONFIG_ROMFS_FS
 tristate 'Second extended fs support' CONFIG_EXT2_FS
+tristate 'Second extended fs development code' CONFIG_EXT3_FS
+
 tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS
 tristate 'UFS filesystem support' CONFIG_UFS_FS
 if [ "$CONFIG_UFS_FS" != "n" ]; then
--- linux-2.2.18pre17.ext3-0.0.5a/fs/Makefile.~1~	Thu Aug 26 01:29:49 1999
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/Makefile	Wed Oct 25 19:10:56 2000
@@ -16,9 +16,9 @@
 		dcache.o inode.o attr.o bad_inode.o file.o $(BINFMTS) 
 
 MOD_LIST_NAME := FS_MODULES
-ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \
+ALL_SUB_DIRS = coda minix ext2 ext3 fat msdos vfat proc isofs nfs umsdos ntfs \
 		hpfs sysv smbfs ncpfs ufs affs romfs autofs hfs lockd \
-		nfsd nls devpts adfs qnx4 efs
+		nfsd nls devpts adfs qnx4 efs jfs
 
 ifeq ($(CONFIG_QUOTA),y)
 O_OBJS += dquot.o
@@ -47,6 +47,14 @@
 else
   ifeq ($(CONFIG_EXT2_FS),m)
   MOD_SUB_DIRS += ext2
+  endif
+endif
+
+ifeq ($(CONFIG_EXT3_FS),y)
+SUB_DIRS += ext3 jfs
+else
+  ifeq ($(CONFIG_EXT3_FS),m)
+  MOD_SUB_DIRS += ext3 jfs
   endif
 endif
 
--- linux-2.2.18pre17.ext3-0.0.5a/fs/buffer.c.~1~	Mon Sep  4 18:39:22 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/buffer.c	Wed Oct 25 19:10:56 2000
@@ -43,6 +43,8 @@
 #include <linux/file.h>
 #include <linux/init.h>
 #include <linux/quotaops.h>
+#include <linux/buffer.h>
+#include <linux/jfs.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -79,7 +81,8 @@
 
 static struct buffer_head * unused_list = NULL;
 static struct buffer_head * reuse_list = NULL;
-static struct wait_queue * buffer_wait = NULL;
+
+struct wait_queue * buffer_wait = NULL;
 
 static int nr_buffers = 0;
 static int nr_buffers_type[NR_LIST] = {0,};
@@ -155,6 +158,15 @@
 	bh->b_count--;
 }
 
+/* @@@ ext3 debugging only. */
+static void check_buffer_flushable(struct buffer_head *bh)
+{
+	if (bh->b_jlist == 0)
+		J_ASSERT (bh->b_transaction == NULL);
+	else
+		J_ASSERT (bh->b_jlist == BJ_Data);
+}
+
 /* Call sync_buffers with wait!=0 to ensure that the call does not
  * return until all buffer writes have completed.  Sync() may return
  * before the writes have finished; fsync() may not.
@@ -165,7 +177,7 @@
  * We will ultimately want to put these in a separate list, but for
  * now we search all of the lists for dirty buffers.
  */
-static int sync_buffers(kdev_t dev, int wait)
+int sync_buffers(kdev_t dev, int wait)
 {
 	int i, retry, pass = 0, err = 0;
 	struct buffer_head * bh, *next;
@@ -187,6 +199,7 @@
 		for (i = nr_buffers_type[BUF_DIRTY]*2 ; i-- > 0 ; bh = next) {
 			if (bh->b_list != BUF_DIRTY)
 				goto repeat;
+			check_buffer_flushable(bh);
 			next = bh->b_next_free;
 			if (!lru_list[BUF_DIRTY])
 				break;
@@ -229,6 +242,7 @@
 			bh->b_count++;
 			next->b_count++;
 			bh->b_flushtime = 0;
+			check_buffer_flushable(bh);
 			ll_rw_block(WRITE, 1, &bh);
 			bh->b_count--;
 			next->b_count--;
@@ -271,9 +285,9 @@
 
 void sync_dev(kdev_t dev)
 {
-	sync_supers(dev);
 	sync_inodes(dev);
 	DQUOT_SYNC(dev);
+	sync_supers(dev);
 	/* sync all the dirty buffers out to disk only _after_ all the
 	   high level layers finished generated buffer dirty data
 	   (or we'll return with some buffer still dirty on the blockdevice
@@ -295,8 +309,8 @@
 int fsync_dev(kdev_t dev)
 {
 	sync_buffers(dev, 0);
-	sync_supers(dev);
 	sync_inodes(dev);
+	sync_supers(dev);
 	DQUOT_SYNC(dev);
 	return sync_buffers(dev, 1);
 }
@@ -734,7 +748,7 @@
 	bh->b_dev_id = dev_id;
 }
 
-static void end_buffer_io_sync(struct buffer_head *bh, int uptodate)
+void end_buffer_io_sync(struct buffer_head *bh, int uptodate)
 {
 	mark_buffer_uptodate(bh, uptodate);
 	unlock_buffer(bh);
@@ -826,14 +840,18 @@
 
 	if(buf->b_dev == B_FREE) {
 		printk("Attempt to refile free buffer\n");
+		*((char *) 0) = 0;
 		return;
 	}
-	if (buffer_dirty(buf))
+	if (buf->b_jlist != BJ_None && buf->b_jlist != BJ_Data)
+		dispose = BUF_JOURNAL;
+	else if (buffer_dirty(buf))
 		dispose = BUF_DIRTY;
 	else if (buffer_locked(buf))
 		dispose = BUF_LOCKED;
 	else
 		dispose = BUF_CLEAN;
+
 	if(dispose != buf->b_list) {
 		file_buffer(buf, dispose);
 		if(dispose == BUF_DIRTY) {
@@ -854,6 +872,13 @@
 				wakeup_bdflush(1);
 		}
 	}
+
+	/* If we have just written a checkpointed jfs buffer, then we
+	   can now unlink it from its original transaction: there's no
+	   need to keep the transaction pinned in the log once the datat
+	   is safely on disk. */
+	if (dispose == BUF_CLEAN && buf->b_cp_transaction)
+		journal_remove_checkpoint(buf);
 }
 
 /*
@@ -868,9 +893,13 @@
 
 	if (buf->b_count) {
 		buf->b_count--;
+		if (!buf->b_count &&
+		    (buf->b_jlist != BJ_None && buf->b_jlist != BJ_Shadow && buf->b_jlist != BJ_Data))
+			J_ASSERT (!test_bit(BH_JWrite, &buf->b_state));
 		return;
 	}
 	printk("VFS: brelse: Trying to free free buffer\n");
+	J_ASSERT(buf->b_count > 0);
 }
 
 /*
@@ -881,7 +910,10 @@
  */
 void __bforget(struct buffer_head * buf)
 {
-	if (buf->b_count != 1 || buffer_locked(buf)) {
+	/* @@@ TODO (non-urgent): Put checkpointed buffers on the
+           current transaction's forget list here. */
+	if (buf->b_count != 1 || buffer_locked(buf) ||
+	    buffer_journaled(buf)) {
 		__brelse(buf);
 		return;
 	}
@@ -976,7 +1008,7 @@
 /*
  * Note: the caller should wake up the buffer_wait list if needed.
  */
-static void put_unused_buffer_head(struct buffer_head * bh)
+void put_unused_buffer_head(struct buffer_head * bh)
 {
 	if (nr_unused_buffer_heads >= MAX_UNUSED_BUFFERS) {
 		nr_buffer_heads--;
@@ -1024,7 +1056,7 @@
  * no-buffer-head deadlock.  Return NULL on failure; waiting for
  * buffer heads is now handled in create_buffers().
  */ 
-static struct buffer_head * get_unused_buffer_head(int async)
+struct buffer_head * get_unused_buffer_head(int async)
 {
 	struct buffer_head * bh;
 
@@ -1466,7 +1498,20 @@
  * Can the buffer be thrown out?
  */
 #define BUFFER_BUSY_BITS	((1<<BH_Dirty) | (1<<BH_Lock) | (1<<BH_Protected))
-#define buffer_busy(bh)		((bh)->b_count || ((bh)->b_state & BUFFER_BUSY_BITS))
+static inline int buffer_busy(struct buffer_head *bh) 
+{
+	if (bh->b_count)
+		return 1;
+	if (bh->b_state & BUFFER_BUSY_BITS)
+		return 1;
+	if (bh->b_jlist != BJ_None || bh->b_cp_transaction != NULL)
+		return 1;
+	/* @@@ Expensive debugging code, eliminate at some point */
+	J_ASSERT(bh->b_transaction == NULL);
+	J_ASSERT(!test_bit(BH_JWrite, &bh->b_state));
+	return 0;
+}
+
 
 static int sync_page_buffers(struct buffer_head *bh, int wait)
 {
@@ -1478,7 +1523,7 @@
 		if (buffer_locked(p)) {
 			if (wait)
 				__wait_on_buffer(p);
-		} else if (buffer_dirty(p))
+		} else if (buffer_dirty(p) && !p->b_jlist)
 			ll_rw_block(WRITE, 1, &p);
 	} while (tmp != bh);
 
@@ -1561,7 +1606,7 @@
 	int found = 0, locked = 0, dirty = 0, used = 0, lastused = 0;
 	int protected = 0;
 	int nlist;
-	static char *buf_types[NR_LIST] = {"CLEAN","LOCKED","DIRTY"};
+	static char *buf_types[NR_LIST] = {"CLEAN","LOCKED","DIRTY","JFS"};
 
 	printk("Buffer memory:   %8ldkB\n",buffermem>>10);
 	printk("Buffer heads:    %6d\n",nr_buffer_heads);
@@ -1708,6 +1753,9 @@
 	for(nlist = BUF_LOCKED; nlist <= BUF_DIRTY; nlist++)
 #endif
 	{
+		if (nlist == BUF_JOURNAL)
+			continue;	/* Never sync journaled buffers, even
+					   when debugging! */
 		ndirty = 0;
 		nwritten = 0;
 	repeat:
@@ -1717,6 +1765,7 @@
 			 for (i = nr_buffers_type[nlist]; i-- > 0; bh = next) {
 				 /* We may have stalled while waiting for I/O to complete. */
 				 if(bh->b_list != nlist) goto repeat;
+				 check_buffer_flushable(bh);
 				 next = bh->b_next_free;
 				 if(!lru_list[nlist]) {
 					 printk("Dirty list empty %d\n", i);
@@ -1747,6 +1796,7 @@
 #ifdef DEBUG
 				 if(nlist != BUF_DIRTY) ncount++;
 #endif
+				 check_buffer_flushable(bh);
 				 ll_rw_block(WRITE, 1, &bh);
 				 bh->b_count--;
 				 next->b_count--;
@@ -1863,6 +1913,12 @@
 #endif
 		 {
 			 ndirty = 0;
+
+			 /* Never sync journaled buffers, even when
+			  * debugging! */
+			 if (nlist == BUF_JOURNAL)
+				 continue;	
+
 		 repeat:
 
 			 bh = lru_list[nlist];
@@ -1871,6 +1927,7 @@
 				       bh = next) {
 					  /* We may have stalled while waiting for I/O to complete. */
 					  if(bh->b_list != nlist) goto repeat;
+					  check_buffer_flushable(bh);
 					  next = bh->b_next_free;
 					  if(!lru_list[nlist]) {
 						  printk("Dirty list empty %d\n", i);
@@ -1898,6 +1955,7 @@
 					  bh->b_count++;
 					  ndirty++;
 					  bh->b_flushtime = 0;
+					  check_buffer_flushable(bh);
 					  if (major == LOOP_MAJOR) {
 						  ll_rw_block(wrta_cmd,1, &bh);
 						  wrta_cmd = WRITEA;
--- linux-2.2.18pre17.ext3-0.0.5a/fs/dquot.c.~1~	Wed Oct 25 18:57:52 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/dquot.c	Wed Oct 25 19:10:56 2000
@@ -102,6 +102,14 @@
 
 void dqput(struct dquot *);
 static struct dquot *dqduplicate(struct dquot *);
+static void do_write_dquot(struct dquot *dquot);
+
+static inline void mark_dquot_dirty(struct dquot *dquot)
+{
+	dquot->dq_flags |= DQ_MOD;
+	if (dquot->dq_mnt->mnt_flags & DQUOT_WRITETHROUGH)
+		do_write_dquot(dquot);
+}
 
 static inline char is_enabled(struct vfsmount *vfsmnt, short type)
 {
@@ -251,7 +259,7 @@
 /*
  *	We don't have to be afraid of deadlocks as we never have quotas on quota files...
  */
-static void write_dquot(struct dquot *dquot)
+static void do_write_dquot(struct dquot *dquot)
 {
 	short type = dquot->dq_type;
 	struct file *filp;
@@ -260,11 +268,8 @@
 	ssize_t ret;
 	struct semaphore *sem = &dquot->dq_mnt->mnt_dquot.dqio_sem;
 
-	lock_dquot(dquot);
-	if (!dquot->dq_mnt) {	/* Invalidated quota? */
-		unlock_dquot(dquot);
+	if (!dquot->dq_mnt)	/* Invalidated quota? */
 		return;
-	}
 	down(sem);
 	filp = dquot->dq_mnt->mnt_dquot.files[type];
 	offset = dqoff(dquot->dq_id);
@@ -286,11 +291,17 @@
 
 	set_fs(fs);
 	up(sem);
-	unlock_dquot(dquot);
 
 	dqstats.writes++;
 }
 
+static void write_dquot(struct dquot *dquot)
+{
+	lock_dquot(dquot);
+	do_write_dquot(dquot);
+	unlock_dquot(dquot);
+}
+
 static void read_dquot(struct dquot *dquot)
 {
 	short type = dquot->dq_type;
@@ -792,13 +803,13 @@
 static inline void dquot_incr_inodes(struct dquot *dquot, unsigned long number)
 {
 	dquot->dq_curinodes += number;
-	dquot->dq_flags |= DQ_MOD;
+	mark_dquot_dirty(dquot);
 }
 
 static inline void dquot_incr_blocks(struct dquot *dquot, unsigned long number)
 {
 	dquot->dq_curblocks += number;
-	dquot->dq_flags |= DQ_MOD;
+	mark_dquot_dirty(dquot);
 }
 
 static inline void dquot_decr_inodes(struct dquot *dquot, unsigned long number)
@@ -810,7 +821,7 @@
 	if (dquot->dq_curinodes < dquot->dq_isoftlimit)
 		dquot->dq_itime = (time_t) 0;
 	dquot->dq_flags &= ~DQ_INODES;
-	dquot->dq_flags |= DQ_MOD;
+	mark_dquot_dirty(dquot);
 }
 
 static inline void dquot_decr_blocks(struct dquot *dquot, unsigned long number)
@@ -822,7 +833,7 @@
 	if (dquot->dq_curblocks < dquot->dq_bsoftlimit)
 		dquot->dq_btime = (time_t) 0;
 	dquot->dq_flags &= ~DQ_BLKS;
-	dquot->dq_flags |= DQ_MOD;
+	mark_dquot_dirty(dquot);
 }
 
 static inline char need_print_warning(short type, uid_t initiator, struct dquot *dquot)
@@ -988,7 +999,7 @@
 		else
 			dquot->dq_flags &= ~DQ_FAKE;
 
-		dquot->dq_flags |= DQ_MOD;
+		mark_dquot_dirty(dquot);
 		unlock_dquot(dquot);
 		dqput(dquot);
 	}
--- linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/CHANGES.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/CHANGES	Wed Oct 25 19:10:56 2000
@@ -0,0 +1,157 @@
+Changes from version 0.5a to version 0.5b
+=========================================
+	- Now that we have sysctl(), the immutable flag cannot be changed when
+	  the system is running at security level > 0.
+	- Some cleanups in the code.
+	- More consistency checks on directories.
+	- The ext2.diff patch from Tom May <ftom@netcom.com> has been
+	  integrated.  This patch replaces expensive "/" and "%" with
+	  cheap ">>" and "&" where possible.
+
+Changes from version 0.5 to version 0.5a
+========================================
+	- Zero the partial block following the end of the file when a file
+	  is truncated.
+	- Dates updated in the copyright.
+	- More checks when the filesystem is mounted: the count of blocks,
+	  fragments, and inodes per group is checked against the block size.
+	- The buffers used by the error routines are now static variables, to
+	  avoid using space on the kernel stack, as requested by Linus.
+	- Some cleanups in the error messages (some versions of syslog contain
+	  a bug which truncates an error message if it contains '\n').
+	- Check that no data can be written to a file past the 2GB limit.
+	- The famous readdir() bug has been fixed by Stephen Tweedie.
+	- Added a revision level in the superblock.
+	- Full support for O_SYNC flag of the open system call.
+	- New mount options: `resuid=#uid' and `resgid=#gid'.  `resuid' causes
+	  ext2fs to consider user #uid like root for the reserved blocks.
+	  `resgid' acts the same way with group #gid.  New fields in the
+	  superblock contain default values for resuid and resgid and can
+	  be modified by tune2fs.
+	  Idea comes from Rene Cougnenc <cougnenc@renux.frmug.fr.net>.
+	- New mount options: `bsddf' and `minixdf'.  `bsddf' causes ext2fs
+	  to remove the blocks used for FS structures from the total block
+	  count in statfs.  With `minixdf', ext2fs mimics Minix behavior
+	  in statfs (i.e. it returns the total number of blocks on the
+	  partition).  This is intended to make bde happy :-)
+	- New file attributes:
+	  - Immutable files cannot be modified.  Data cannot be written to
+	    these files.  They cannot be removed, renamed and new links cannot
+	    be created.  Even root cannot modify the files.  He has to remove
+	    the immutable attribute first.
+	  - Append-only files: can only be written in append-mode when writing.
+	    They cannot be removed, renamed and new links cannot be created.
+	    Note: files may only be added to an append-only directory.
+	  - No-dump files: the attribute is not used by the kernel.  My port
+	    of dump uses it to avoid backing up files which are not important.
+	- New check in ext2_check_dir_entry: the inode number is checked.
+	- Support for big file systems: the copy of the FS descriptor is now
+	  dynamically allocated (previous versions used a fixed size array).
+	  This allows to mount 2GB+ FS.
+	- Reorganization of the ext2_inode structure to allow other operating
+	  systems to create specific fields if they use ext2fs as their native
+	  file system.  Currently, ext2fs is only implemented in Linux but
+	  will soon be part of Gnu Hurd and of Masix.
+
+Changes from version 0.4b to version 0.5
+========================================
+	- New superblock fields: s_lastcheck and s_checkinterval added
+	  by Uwe Ohse <uwe@tirka.gun.de> to implement timedependent checks
+	  of the file system
+	- Real random numbers for secure rm added by Pierre del Perugia
+	  <delperug@gla.ecoledoc.ibp.fr>
+	- The mount warnings related to the state of a fs are not printed
+	  if the fs is mounted read-only, idea by Nick Holloway
+	  <alfie@dcs.warwick.ac.uk>
+
+Changes from version 0.4a to version 0.4b
+=========================================
+	- Copyrights changed to include the name of my laboratory.
+	- Clean up of balloc.c and ialloc.c.
+	- More consistency checks.
+	- Block preallocation added by Stephen Tweedie.
+	- Direct reads of directories disallowed.
+	- Readahead implemented in readdir by Stephen Tweedie.
+	- Bugs in block and inodes allocation fixed.
+	- Readahead implemented in ext2_find_entry by Chip Salzenberg.
+	- New mount options:
+	  `check=none|normal|strict'
+	  `debug'
+	  `errors=continue|remount-ro|panic'
+	  `grpid', `bsdgroups'
+	  `nocheck'
+	  `nogrpid', `sysvgroups'
+	- truncate() now tries to deallocate contiguous blocks in a single call
+	  to ext2_free_blocks().
+	- lots of cosmetic changes.
+
+Changes from version 0.4 to version 0.4a
+========================================
+        - the `sync' option support is now complete.  Version 0.4 was not
+          supporting it when truncating a file.  I have tested the synchronous
+          writes and they work but they make the system very slow :-(  I have
+          to work again on this to make it faster.
+        - when detecting an error on a mounted filesystem, version 0.4 used
+          to try to write a flag in the super block even if the filesystem had
+          been mounted read-only.  This is fixed.
+        - the `sb=#' option now causes the kernel code to use the filesystem
+          descriptors located at block #+1.  Version 0.4 used the superblock
+          backup located at block # but used the main copy of the descriptors.
+        - a new file attribute `S' is supported.  This attribute causes
+          synchronous writes but is applied to a file not to the entire file
+          system (thanks to Michael Kraehe <kraehe@bakunin.north.de> for
+          suggesting it).
+        - the directory cache is inhibited by default.  The cache management
+          code seems to be buggy and I have to look at it carefully before
+          using it again.
+        - deleting a file with the `s' attribute (secure deletion) causes its
+          blocks to be overwritten with random values not with zeros (thanks to
+          Michael A. Griffith <grif@cs.ucr.edu> for suggesting it).
+        - lots of cosmetic changes have been made.
+
+Changes from version 0.3 to version 0.4
+=======================================
+        - Three new mount options are supported: `check', `sync' and `sb=#'.
+          `check' tells the kernel code to make more consistency checks
+          when the file system is mounted.  Currently, the kernel code checks
+          that the blocks and inodes bitmaps are consistent with the free
+          blocks and inodes counts.  More checks will be added in future
+          releases.
+          `sync' tells the kernel code to use synchronous writes when updating
+          an inode, a bitmap, a directory entry or an indirect block.  This
+          can make the file system much slower but can be a big win for files
+          recovery in case of a crash (and we can now say to the BSD folks
+          that Linux also supports synchronous updates :-).
+          `sb=#' tells the kernel code to use an alternate super block instead
+          of its master copy.  `#' is the number of the block (counted in
+          1024 bytes blocks) which contains the alternate super block.
+          An ext2 file system typically contains backups of the super block
+          at blocks 8193, 16385, and so on.
+        - I have change the meaning of the valid flag used by e2fsck.  it
+          now contains the state of the file system.  If the kernel code
+          detects an inconsistency while the file system is mounted, it flags
+          it as erroneous and e2fsck will detect that on next run.
+        - The super block now contains a mount counter.  This counter is
+          incremented each time the file system is mounted read/write.   When
+          this counter becomes bigger than a maximal mount counts (also stored
+          in the super block), e2fsck checks the file system, even if it had
+          been unmounted cleanly, and resets this counter to 0.
+        - File attributes are now supported.  One can associate a set of
+          attributes to a file.  Three attributes are defined:
+          `c': the file is marked for automatic compression,
+          `s': the file is marked for secure deletion: when the file is
+          deleted, its blocks are zeroed and written back to the disk,
+          `u': the file is marked for undeletion: when the file is deleted,
+          its contents are saved to allow a future undeletion.
+          Currently, only the `s' attribute is implemented in the kernel
+          code.  Support for the other attributes will be added in a future
+          release.
+        - a few bugs related to times updates have been fixed by Bruce
+          Evans and me.
+        - a bug related to the links count of deleted inodes has been fixed.
+          Previous versions used to keep the links count set to 1 when a file
+          was deleted.  The new version now sets links_count to 0 when deleting
+          the last link.
+        - a race condition when deallocating an inode has been fixed by
+          Stephen Tweedie.
+
--- linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/Makefile.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/Makefile	Wed Oct 25 19:10:56 2000
@@ -0,0 +1,15 @@
+#
+# Makefile for the linux ext2-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := ext3.o
+O_OBJS   := acl.o balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \
+		ioctl.o namei.o super.o symlink.o truncate.o
+M_OBJS   := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
--- linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/TODO.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/TODO	Wed Oct 25 19:10:56 2000
@@ -0,0 +1,102 @@
+In the middle of doing:
+
+namei.c: got as far as mknod + create
+
+Next:
+
+ext3_new_inode()
+
+* jfs/commit.c:	Free up b_committed_data if appropriate
+
+Longer term:
+
+
+* fs/buffer.c: on IO completion, remove BJ_Data buffers from their
+		transaction list.  Need to think about the locking
+		implications of this one!
+
+		Perhaps use a call into the journaling code to scan
+		the current transactions from bdflushd?
+
+* All:		Think about error recovery a bit more.
+
+* ext3/super.c:	Set up superblock journaling flags on mount/dismount
+		
+		Think about how to mount a journaled filesystem readonly!
+
+* fs/journal.c:	Cache the entire bmap data for the journal for performance
+
+* fs/journal.c: Redundant copies of the journal superblock.
+
+* All:		When deleting metadata, we need a special transaction
+		record to obsolete older copies of that data in the
+		log.  This is necessary to avoid recovering that old
+		data if we subsequently reallocate the block for data.
+		Only required for non-journaled data.
+
+* ext3/balloc.c:Think about preallocation.  Disable it for now, but we
+		may be able to deal with it by recording preallocation
+		in separate bitmap buffers outside of recovery.  That 
+		pins buffers in cache, tho: an undo mechanism would be
+		the most general-purpose solution.
+
+* All: 		Ensure that we preserve lock ranking.  Journal lock is of
+		higher rank than the buffer lock.  (Done, I think;
+		still need to audit it.)
+
+* fs/transaction.c:
+		Make sure commit does the right things if we don't do a
+		journal_dirty_* on a bh after getting write access.
+
+* ext3/ialloc.c, balloc.c:
+		What happens if we drop the superblock locks here?
+		Getting write access on the bitmap buffers may block.
+		Ugh.  Return -EGAIN and restart the transaction??
+
+* All:		Test out readonly mode (especially wrt. null handles!)
+
+* ext3/fsync.c:	Add transactions.  ext3_sync_inode doesn't complete the 
+		transaction on its own.
+
+* fs.h:		Remove BH_Temp?
+ 
+* ext3/super.c:	Add remount of JFS_SYNC, maybe?
+
+Done:
+
+* fs/buffer.c:	Add infrastructure for journaled sync().  Hmm.
+
+* ext3/fsync.c:	Add transactions.  ext3_sync_inode doesn't complete the 
+		transaction on its own.
+
+* fs/journal.c:	Get the endianness right!
+
+* ext3/inode.c:	Prevent ext3_write_inode from flushing a buffer which is
+		on a journal list.  Journal the update if necessary.
+
+* ext3/super.c:	Make ext3_put_super do something sane.
+
+* ext3/namei.c:	Whoops, fixed ext3_mknod: it was doing an
+		ext3_mark_iloc_dirty on the wrong inode.  Directory
+		corrupter!
+
+* jfs/checkpoint.c:
+		Fixed a stupid bug in the management of the checkpoint
+		buffer wait queue.  Checkpoint has a chance of being
+		reliable now.
+
+* ext3/super.c:	Don't load the journal if the _HAS_JOURNAL feature is
+		clear, even if there is a journal inum in the
+		superblock.  If the user has used debugfs or e2fsck to
+		restore the fs to ext2, we want to be absolutely sure
+		we don't replay the journal by accident!
+
+* ext3/super.c: Lots of fixes in the setting/processing of the
+		NEEDS_RECOVERY flag.
+
+* ext3/super.c: Release the journal on umount even on a readonly filesystem.
+
+* fs/buffer.c:	Be a little more cautious about bforget()ing
+		checkpointed buffers, in case other software (such as
+		background soft raid resync) is trying to access the
+		buffer cache behind our backs. 
--- linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/acl.c.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/acl.c	Wed Oct 25 19:10:56 2000
@@ -0,0 +1,61 @@
+/*
+ * linux/fs/ext3/acl.c
+ *
+ * Copyright (C) 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ */
+
+/*
+ * This file will contain the Access Control Lists management for the
+ * second extended file system.
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ext3_fs.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+
+/*
+ * ext3_permission ()
+ *
+ * Check for access rights
+ */
+int ext3_permission (struct inode * inode, int mask)
+{
+	unsigned short mode = inode->i_mode;
+
+	/*
+	 * Nobody gets write access to a file on a readonly-fs
+	 */
+	if ((mask & S_IWOTH) && 
+            (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) &&
+            IS_RDONLY(inode))
+		return -EROFS;
+	/*
+	 * Nobody gets write access to an immutable file
+	 */
+	if ((mask & S_IWOTH) && IS_IMMUTABLE(inode))
+		return -EACCES;
+
+	/*
+	 * If no ACL, checks using the file mode
+	 */
+	else if (current->fsuid == inode->i_uid)
+		mode >>= 6;
+	else if (in_group_p (inode->i_gid))
+		mode >>= 3;
+	/*
+	 * Access is always granted for root. We now check last,
+         * though, for BSD process accounting correctness
+	 */
+	if (((mode & mask & S_IRWXO) == mask) || capable(CAP_DAC_OVERRIDE))
+		return 0;
+	if ((mask == S_IROTH) ||
+	    (S_ISDIR(mode)  && !(mask & ~(S_IROTH | S_IXOTH))))
+		if (capable(CAP_DAC_READ_SEARCH))
+			return 0;
+	return -EACCES;
+}
--- linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/balloc.c.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/balloc.c	Wed Oct 25 19:10:56 2000
@@ -0,0 +1,851 @@
+/*
+ *  linux/fs/ext3/balloc.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)
+ *
+ *  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
+ */
+
+/*
+ * balloc.c contains the blocks allocation and deallocation routines
+ */
+
+/*
+ * The free blocks are managed by bitmaps.  A file system contains several
+ * blocks groups.  Each group contains 1 bitmap block for blocks, 1 bitmap
+ * block for inodes, N blocks for the inode table and data blocks.
+ *
+ * The file system contains group descriptors which are located after the
+ * super block.  Each descriptor contains the number of the bitmap block and
+ * the free blocks count in the block.  The descriptors are loaded in memory
+ * when a file system is mounted (see ext3_read_super).
+ */
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/ext3_fs.h>
+#include <linux/ext3_jfs.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/quotaops.h>
+
+#include <asm/bitops.h>
+#include <asm/byteorder.h>
+
+#define in_range(b, first, len)		((b) >= (first) && (b) <= (first) + (len) - 1)
+
+struct ext3_group_desc * ext3_get_group_desc(struct super_block * sb,
+					     unsigned int block_group,
+					     struct buffer_head ** bh)
+{
+	unsigned long group_desc;
+	unsigned long desc;
+	struct ext3_group_desc * gdp;
+
+	if (block_group >= sb->u.ext3_sb.s_groups_count) {
+		ext3_error (sb, "ext3_get_group_desc",
+			    "block_group >= groups_count - "
+			    "block_group = %d, groups_count = %lu",
+			    block_group, sb->u.ext3_sb.s_groups_count);
+
+		return NULL;
+	}
+	
+	group_desc = block_group / EXT3_DESC_PER_BLOCK(sb);
+	desc = block_group % EXT3_DESC_PER_BLOCK(sb);
+	if (!sb->u.ext3_sb.s_group_desc[group_desc]) {
+		ext3_error (sb, "ext3_get_group_desc",
+			    "Group descriptor not loaded - "
+			    "block_group = %d, group_desc = %lu, desc = %lu",
+			     block_group, group_desc, desc);
+		return NULL;
+	}
+	
+	gdp = (struct ext3_group_desc *) 
+	      sb->u.ext3_sb.s_group_desc[group_desc]->b_data;
+	if (bh)
+		*bh = sb->u.ext3_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.
+ */
+
+static int read_block_bitmap (struct super_block * sb,
+			       unsigned int block_group,
+			       unsigned long bitmap_nr)
+{
+	struct ext3_group_desc * gdp;
+	struct buffer_head * bh = NULL;
+	int retval = -EIO;
+	
+	gdp = ext3_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) {
+		ext3_error (sb, "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.ext3_sb.s_block_bitmap_number[bitmap_nr] = block_group;
+	sb->u.ext3_sb.s_block_bitmap[bitmap_nr] = bh;
+	return retval;
+}
+
+/*
+ * load_block_bitmap loads the block bitmap for a blocks group
+ *
+ * It maintains a cache for the last bitmaps loaded.  This cache is managed
+ * with a LRU algorithm.
+ *
+ * Notes:
+ * 1/ There is one cache per mounted file system.
+ * 2/ If the file system contains less than EXT3_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)
+{
+	int i, j, retval = 0;
+	unsigned long block_bitmap_number;
+	struct buffer_head * block_bitmap;
+
+	if (block_group >= sb->u.ext3_sb.s_groups_count)
+		ext3_panic (sb, "load_block_bitmap",
+			    "block_group >= groups_count - "
+			    "block_group = %d, groups_count = %lu",
+			    block_group, sb->u.ext3_sb.s_groups_count);
+
+	if (sb->u.ext3_sb.s_groups_count <= EXT3_MAX_GROUP_LOADED) {
+		if (sb->u.ext3_sb.s_block_bitmap[block_group]) {
+			if (sb->u.ext3_sb.s_block_bitmap_number[block_group] ==
+			    block_group)
+				return block_group;
+			ext3_error (sb, "load_block_bitmap",
+				    "block_group != block_bitmap_number");
+		}
+		retval = read_block_bitmap (sb, block_group, block_group);
+		if (retval < 0)
+			return retval;
+		return block_group;
+	}
+
+	for (i = 0; i < sb->u.ext3_sb.s_loaded_block_bitmaps &&
+		    sb->u.ext3_sb.s_block_bitmap_number[i] != block_group; i++)
+		;
+	if (i < sb->u.ext3_sb.s_loaded_block_bitmaps &&
+  	    sb->u.ext3_sb.s_block_bitmap_number[i] == block_group) {
+		block_bitmap_number = sb->u.ext3_sb.s_block_bitmap_number[i];
+		block_bitmap = sb->u.ext3_sb.s_block_bitmap[i];
+		for (j = i; j > 0; j--) {
+			sb->u.ext3_sb.s_block_bitmap_number[j] =
+				sb->u.ext3_sb.s_block_bitmap_number[j - 1];
+			sb->u.ext3_sb.s_block_bitmap[j] =
+				sb->u.ext3_sb.s_block_bitmap[j - 1];
+		}
+		sb->u.ext3_sb.s_block_bitmap_number[0] = block_bitmap_number;
+		sb->u.ext3_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);
+	} else {
+		if (sb->u.ext3_sb.s_loaded_block_bitmaps < EXT3_MAX_GROUP_LOADED)
+			sb->u.ext3_sb.s_loaded_block_bitmaps++;
+		else
+			brelse (sb->u.ext3_sb.s_block_bitmap[EXT3_MAX_GROUP_LOADED - 1]);
+		for (j = sb->u.ext3_sb.s_loaded_block_bitmaps - 1; j > 0;  j--) {
+			sb->u.ext3_sb.s_block_bitmap_number[j] =
+				sb->u.ext3_sb.s_block_bitmap_number[j - 1];
+			sb->u.ext3_sb.s_block_bitmap[j] =
+				sb->u.ext3_sb.s_block_bitmap[j - 1];
+		}
+		retval = read_block_bitmap (sb, block_group, 0);
+	}
+	return retval;
+}
+
+/*
+ * 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 inconsistancy here --- if the number of groups in this
+ * filesystems is <= EXT3_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)
+{
+	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.ext3_sb.s_loaded_block_bitmaps > 0 &&
+	    sb->u.ext3_sb.s_block_bitmap_number[0] == block_group &&
+	    sb->u.ext3_sb.s_block_bitmap[0]) {
+		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.ext3_sb.s_groups_count <= EXT3_MAX_GROUP_LOADED && 
+		 sb->u.ext3_sb.s_block_bitmap_number[block_group] == block_group &&
+		 sb->u.ext3_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.ext3_sb.s_block_bitmap[slot])
+		return -EIO;
+	
+	/*
+	 * Must have been read in OK to get this far.
+	 */
+	return slot;
+}
+
+void ext3_free_blocks (handle_t *handle,
+		       const struct inode * inode, unsigned long block,
+		       unsigned long count)
+{
+	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 ext3_group_desc * gdp;
+	struct ext3_super_block * es;
+
+	sb = inode->i_sb;
+	if (!sb) {
+		printk ("ext3_free_blocks: nonexistent device");
+		return;
+	}
+	lock_super (sb);
+	es = sb->u.ext3_sb.s_es;
+	if (block < le32_to_cpu(es->s_first_data_block) || 
+	    (block + count) > le32_to_cpu(es->s_blocks_count)) {
+		ext3_error (sb, "ext3_free_blocks",
+			    "Freeing blocks not in datazone - "
+			    "block = %lu, count = %lu", block, count);
+		goto error_return;
+	}
+
+	ext3_debug ("freeing block %lu\n", block);
+
+do_more:
+	overflow = 0;
+	block_group = (block - le32_to_cpu(es->s_first_data_block)) /
+		      EXT3_BLOCKS_PER_GROUP(sb);
+	bit = (block - le32_to_cpu(es->s_first_data_block)) %
+		      EXT3_BLOCKS_PER_GROUP(sb);
+	/*
+	 * Check to see if we are freeing blocks across a group
+	 * boundary.
+	 */
+	if (bit + count > EXT3_BLOCKS_PER_GROUP(sb)) {
+		overflow = bit + count - EXT3_BLOCKS_PER_GROUP(sb);
+		count -= overflow;
+	}
+	bitmap_nr = load_block_bitmap (sb, block_group);
+	if (bitmap_nr < 0)
+		goto error_return;
+	
+	bh = sb->u.ext3_sb.s_block_bitmap[bitmap_nr];
+	gdp = ext3_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.ext3_sb.s_itb_per_group) ||
+	     in_range (block + count - 1, le32_to_cpu(gdp->bg_inode_table),
+		       sb->u.ext3_sb.s_itb_per_group)))
+		ext3_panic (sb, "ext3_free_blocks",
+			    "Freeing blocks in system zones - "
+			    "Block = %lu, count = %lu",
+			    block, count);
+
+	/* @@@ Check error returns! */
+	journal_get_undo_access(handle, bh);
+	journal_get_write_access(handle, bh2);
+	journal_get_write_access(handle, sb->u.ext3_sb.s_sbh);
+	
+	for (i = 0; i < count; i++) {
+		if (!ext3_clear_bit (bit + i, bh->b_data))
+			ext3_warning (sb, "ext3_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);
+		}
+		
+		/* @@@ This prevents newly-allocated data from being
+		 * freed and then reallocated within the same
+		 * transaction. 
+		 * 
+		 * Ideally we would want to allow that to happen, but to
+		 * do so requires making journal_forget() capable of
+		 * revoking the queued write of a data block, which
+		 * implies blocking on the journal lock.  *forget()
+		 * cannot block due to truncate races.
+		 *
+		 * Eventually we can fix this by making journal_forget()
+		 * return a status indicating whether or not it was able
+		 * to revoke the buffer.  On successful revoke, it is
+		 * safe not to set the allocation bit in the committed
+		 * bitmap, because we know that there is no outstanding
+		 * activity on the buffer any more and so it is safe to
+		 * reallocate it.  
+		 */
+		J_ASSERT(bh->b_committed_data != NULL);
+		ext3_set_bit (bit + i, bh->b_committed_data);
+	}
+	
+	journal_dirty_metadata(handle, bh);
+	journal_dirty_metadata(handle, bh2);
+	journal_dirty_metadata(handle, sb->u.ext3_sb.s_sbh);
+
+	if (sb->s_flags & MS_SYNCHRONOUS)
+		handle->h_sync = 1;
+
+	if (overflow) {
+		block += count;
+		count = overflow;
+		goto do_more;
+	}
+	sb->s_dirt = 1;
+error_return:
+	unlock_super (sb);
+	return;
+}
+
+/* For ext3 allocations, we must not reuse any blocks which are
+ * allocated in the bitmap buffer's "last committed data" copy.  This
+ * prevents deletes from freeing up the page for reuse until we have
+ * committed the delete transaction.
+ *
+ * If we didn't do this, then deleting something and reallocating it as
+ * data would allow the old block to be overwritten before the
+ * transaction committed (because we force data to disk before commit).
+ * This would lead to corruption if we crashed between overwriting the
+ * data and committing the delete. 
+ *
+ * @@@ We may want to make this allocation behaviour conditional on
+ * data-writes at some point, and disable it for metadata allocations or
+ * sync-data inodes.
+ */
+static int ext3_test_allocatable(int nr, struct buffer_head *bh)
+{
+	if (ext3_test_bit(nr, bh->b_data))
+		return 0;
+	if (!bh->b_committed_data)
+		return 1;
+	return !ext3_test_bit(nr, bh->b_committed_data);
+}
+
+/*
+ * Find an allocatable block in a bitmap.  We honour both the bitmap and
+ * its last-committed copy (if that exists), and perform the "most
+ * appropriate allocation" algorithm of looking for a free block near
+ * the initial goal; then for a free byte somewhere in the bitmap; then
+ * for any free bit in the bitmap.
+ */
+static int find_next_usable_block(int start, struct buffer_head *bh, int maxblocks)
+{
+	int here, next;
+	char *p, *r;
+	
+	if (start > 0) {
+		/*
+		 * The goal was occupied; search forward for a free 
+		 * block within the next XX blocks.
+		 *
+		 * end_goal is more or less random, but it has to be
+		 * less than EXT3_BLOCKS_PER_GROUP. Aligning up to the
+		 * next 64-bit boundary is simple..
+		 */
+		int end_goal = (start + 63) & ~63;
+		here = ext3_find_next_zero_bit(bh->b_data, end_goal, start);
+		if (here < end_goal && ext3_test_allocatable(here, bh))
+			return here;
+		
+		ext3_debug ("Bit not found near goal\n");
+		
+	}
+	
+	here = start;
+	if (here < 0)
+		here = 0;
+	
+	/*
+	 * There has been no free block found in the near vicinity of
+	 * the goal: do a search forward through the block groups,
+	 * searching in each group first for an entire free byte in the
+	 * bitmap and then for any free bit.
+	 * 
+	 * Search first in the remainder of the current group 
+	 */
+	p = ((char *) bh->b_data) + (here >> 3);
+	r = memscan(p, 0, (maxblocks - here + 7) >> 3);
+	next = (r - ((char *) bh->b_data)) << 3;
+	
+	if (next < maxblocks && ext3_test_allocatable(next, bh))
+		return next;
+	
+	/* The bitmap search --- search forward alternately
+	 * through the actual bitmap and the last-committed copy
+	 * until we find a bit free in both. */
+
+	while (here < maxblocks) {
+		next  = ext3_find_next_zero_bit ((unsigned long *) bh->b_data, 
+						 maxblocks, here);
+		if (next >= maxblocks)
+			return -1;
+		if (ext3_test_allocatable(next, bh))
+			return next;
+
+		J_ASSERT (bh->b_committed_data);
+		here = ext3_find_next_zero_bit
+			((unsigned long *) bh->b_committed_data, 
+			 maxblocks, next);
+	}
+	return -1;
+}
+
+/*
+ * ext3_new_block uses a goal block to assist allocation.  If the goal is
+ * free, or there is a free block within 32 blocks of the goal, that block
+ * is allocated.  Otherwise a forward search is made for a free block; within 
+ * 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.
+ */
+struct buffer_head * ext3_new_block (handle_t *handle,
+				     const struct inode * inode, 
+				     unsigned long goal,
+				     u32 * prealloc_count, 
+				     u32 * prealloc_block, int * err)
+{
+	struct buffer_head * bh;
+	struct buffer_head * bh2;
+	int i, j, k, tmp;
+	int bitmap_nr;
+	struct super_block * sb;
+	struct ext3_group_desc * gdp;
+	struct ext3_super_block * es;
+#ifdef EXT3FS_DEBUG
+	static int goal_hits = 0, goal_attempts = 0;
+#endif
+	*err = -ENOSPC;
+	sb = inode->i_sb;
+	if (!sb) {
+		printk ("ext3_new_block: nonexistent device");
+		return NULL;
+	}
+
+	lock_super (sb);
+	es = sb->u.ext3_sb.s_es;
+	if (le32_to_cpu(es->s_free_blocks_count) <= le32_to_cpu(es->s_r_blocks_count) &&
+	    ((sb->u.ext3_sb.s_resuid != current->fsuid) &&
+	     (sb->u.ext3_sb.s_resgid == 0 ||
+	      !in_group_p (sb->u.ext3_sb.s_resgid)) && 
+	     !capable(CAP_SYS_RESOURCE))) {
+		unlock_super (sb);
+		return NULL;
+	}
+
+	ext3_debug ("goal=%lu.\n", goal);
+
+	/*
+	 * 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)) / EXT3_BLOCKS_PER_GROUP(sb);
+	gdp = ext3_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)) % EXT3_BLOCKS_PER_GROUP(sb));
+#ifdef EXT3FS_DEBUG
+		if (j)
+			goal_attempts++;
+#endif
+		bitmap_nr = load_block_bitmap (sb, i);
+		if (bitmap_nr < 0)
+			goto io_error;
+		
+		bh = sb->u.ext3_sb.s_block_bitmap[bitmap_nr];
+
+		ext3_debug ("goal is at %d:%d.\n", i, j);
+
+		if (ext3_test_allocatable(j, bh)) {
+#ifdef EXT3FS_DEBUG
+			goal_hits++;
+			ext3_debug ("goal bit allocated.\n");
+#endif
+			goto got_block;
+		}
+
+		j = find_next_usable_block(j, bh, EXT3_BLOCKS_PER_GROUP(sb));
+		if (j >= 0)
+			goto search_back;
+	}
+
+	ext3_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.ext3_sb.s_groups_count; k++) {
+		i++;
+		if (i >= sb->u.ext3_sb.s_groups_count)
+			i = 0;
+		gdp = ext3_get_group_desc (sb, i, &bh2);
+		if (!gdp) {
+			*err = -EIO;
+			unlock_super (sb);
+			return NULL;
+		}
+		if (le16_to_cpu(gdp->bg_free_blocks_count) > 0) {
+			bitmap_nr = load_block_bitmap (sb, i);
+			if (bitmap_nr < 0)
+				goto io_error;
+	
+			bh = sb->u.ext3_sb.s_block_bitmap[bitmap_nr];
+			j = find_next_usable_block(-1, bh, 
+						   EXT3_BLOCKS_PER_GROUP(sb));
+			if (j >= 0) 
+				goto search_back;
+		}
+	}
+
+	/* No space left on the device */
+	unlock_super (sb);
+	return NULL;
+
+search_back:
+	/* 
+	 * We have succeeded in finding a free byte in the block
+	 * 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 && ext3_test_allocatable (j - 1, bh); k++, j--);
+	
+got_block:
+
+	ext3_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 NULL;
+	}
+
+	journal_get_write_access(handle, bh);
+	journal_get_write_access(handle, bh2);
+	journal_get_write_access(handle, sb->u.ext3_sb.s_sbh);
+	
+	tmp = j + i * EXT3_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.ext3_sb.s_itb_per_group)))
+		ext3_panic (sb, "ext3_new_block",
+			    "Allocating block in system zone - "
+			    "block = %u", tmp);
+
+	/* The superblock lock should guard against anybody else beating
+	 * us to this point! */
+	J_ASSERT (!ext3_set_bit (j, bh->b_data));
+
+	if (bh->b_committed_data)
+		J_ASSERT(!ext3_test_bit(j, bh->b_committed_data));
+
+	ext3_debug ("found bit %d\n", j);
+
+	/*
+	 * Do block preallocation now if required.
+	 */
+#ifdef EXT3_PREALLOCATE
+	if (prealloc_block) {
+		int	prealloc_goal;
+
+		prealloc_goal = es->s_prealloc_blocks ?
+			es->s_prealloc_blocks : EXT3_DEFAULT_PREALLOC_BLOCKS;
+
+		*prealloc_count = 0;
+		*prealloc_block = tmp + 1;
+		for (k = 1;
+		     k < prealloc_goal && (j + k) < EXT3_BLOCKS_PER_GROUP(sb);
+		     k++) {
+			if (DQUOT_PREALLOC_BLOCK(sb, inode, 1))
+				break;
+			if (ext3_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);
+		ext3_debug ("Preallocated a further %lu bits.\n",
+			    *prealloc_count);
+	}
+#endif
+
+	j = tmp;
+
+	journal_dirty_metadata(handle, bh);
+	if (sb->s_flags & MS_SYNCHRONOUS)
+		handle->h_sync = 1;
+
+	if (j >= le32_to_cpu(es->s_blocks_count)) {
+		ext3_error (sb, "ext3_new_block",
+			    "block >= blocks count - "
+			    "block_group = %d, block=%d", i, j);
+		unlock_super (sb);
+		return NULL;
+	}
+	if (!(bh = getblk (sb->s_dev, j, sb->s_blocksize))) {
+		ext3_error (sb, "ext3_new_block", "cannot get block %d", j);
+		unlock_super (sb);
+		return NULL;
+	}
+	if (!buffer_uptodate(bh))
+		wait_on_buffer(bh);
+
+	memset(bh->b_data, 0, sb->s_blocksize);
+	mark_buffer_uptodate(bh, 1);
+	/* It is up to the caller to add the new buffer to a journal
+	 * list of some description.  We don't know in advance whether
+	 * the caller wants to use it as metadata or data. */
+	mark_buffer_dirty(bh, 0);
+	
+	ext3_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);
+	es->s_free_blocks_count = cpu_to_le32(le32_to_cpu(es->s_free_blocks_count) - 1);
+	journal_dirty_metadata(handle, bh2);
+	journal_dirty_metadata(handle, sb->u.ext3_sb.s_sbh);
+	sb->s_dirt = 1;
+	unlock_super (sb);
+	*err = 0;
+	return bh;
+	
+io_error:
+	*err = -EIO;
+	unlock_super (sb);
+	return NULL;
+	
+}
+
+unsigned long ext3_count_free_blocks (struct super_block * sb)
+{
+#ifdef EXT3FS_DEBUG
+	struct ext3_super_block * es;
+	unsigned long desc_count, bitmap_count, x;
+	int bitmap_nr;
+	struct ext3_group_desc * gdp;
+	int i;
+	
+	lock_super (sb);
+	es = sb->u.ext3_sb.s_es;
+	desc_count = 0;
+	bitmap_count = 0;
+	gdp = NULL;
+	for (i = 0; i < sb->u.ext3_sb.s_groups_count; i++) {
+		gdp = ext3_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 = ext3_count_free (sb->u.ext3_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);
+		bitmap_count += x;
+	}
+	printk("ext3_count_free_blocks: stored = %lu, computed = %lu, %lu\n",
+	       le32_to_cpu(es->s_free_blocks_count), desc_count, bitmap_count);
+	unlock_super (sb);
+	return bitmap_count;
+#else
+	return le32_to_cpu(sb->u.ext3_sb.s_es->s_free_blocks_count);
+#endif
+}
+
+static inline int block_in_use (unsigned long block,
+				struct super_block * sb,
+				unsigned char * map)
+{
+	return ext3_test_bit ((block - le32_to_cpu(sb->u.ext3_sb.s_es->s_first_data_block)) %
+			 EXT3_BLOCKS_PER_GROUP(sb), map);
+}
+
+static int test_root(int a, int b)
+{
+	if (a == 0)
+		return 1;
+	while (1) {
+		if (a == 1)
+			return 1;
+		if (a % b)
+			return 0;
+		a = a / b;
+	}
+}
+
+int ext3_group_sparse(int group)
+{
+	return (test_root(group, 3) || test_root(group, 5) ||
+		test_root(group, 7));
+}
+
+void ext3_check_blocks_bitmap (struct super_block * sb)
+{
+	struct buffer_head * bh;
+	struct ext3_super_block * es;
+	unsigned long desc_count, bitmap_count, x;
+	unsigned long desc_blocks;
+	int bitmap_nr;
+	struct ext3_group_desc * gdp;
+	int i, j;
+
+	lock_super (sb);
+	es = sb->u.ext3_sb.s_es;
+	desc_count = 0;
+	bitmap_count = 0;
+	gdp = NULL;
+	desc_blocks = (sb->u.ext3_sb.s_groups_count + EXT3_DESC_PER_BLOCK(sb) - 1) /
+		      EXT3_DESC_PER_BLOCK(sb);
+	for (i = 0; i < sb->u.ext3_sb.s_groups_count; i++) {
+		gdp = ext3_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.ext3_sb.s_block_bitmap[bitmap_nr];
+
+		if (!(sb->u.ext3_sb.s_feature_ro_compat &
+		     EXT3_FEATURE_RO_COMPAT_SPARSE_SUPER) ||
+		    ext3_group_sparse(i)) {
+			if (!ext3_test_bit (0, bh->b_data))
+				ext3_error (sb, "ext3_check_blocks_bitmap",
+					    "Superblock in group %d "
+					    "is marked free", i);
+
+			for (j = 0; j < desc_blocks; j++)
+				if (!ext3_test_bit (j + 1, bh->b_data))
+					ext3_error (sb,
+					    "ext3_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))
+			ext3_error (sb, "ext3_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))
+			ext3_error (sb, "ext3_check_blocks_bitmap",
+				    "Inode bitmap for group %d is marked free",
+				    i);
+
+		for (j = 0; j < sb->u.ext3_sb.s_itb_per_group; j++)
+			if (!block_in_use (le32_to_cpu(gdp->bg_inode_table) + j, sb, bh->b_data))
+				ext3_error (sb, "ext3_check_blocks_bitmap",
+					    "Block #%d of the inode table in "
+					    "group %d is marked free", j, i);
+
+		x = ext3_count_free (bh, sb->s_blocksize);
+		if (le16_to_cpu(gdp->bg_free_blocks_count) != x)
+			ext3_error (sb, "ext3_check_blocks_bitmap",
+				    "Wrong free blocks count for group %d, "
+				    "stored = %d, counted = %lu", i,
+				    le16_to_cpu(gdp->bg_free_blocks_count), x);
+		bitmap_count += x;
+	}
+	if (le32_to_cpu(es->s_free_blocks_count) != bitmap_count)
+		ext3_error (sb, "ext3_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);
+}
--- linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/bitmap.c.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/bitmap.c	Wed Oct 25 19:10:56 2000
@@ -0,0 +1,26 @@
+/*
+ *  linux/fs/ext3/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>
+#include <linux/ext3_fs.h>
+
+static int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0};
+
+unsigned long ext3_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);
+}
--- linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/dir.c.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/dir.c	Wed Oct 25 19:10:56 2000
@@ -0,0 +1,221 @@
+/*
+ *  linux/fs/ext3/dir.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/dir.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  ext3 directory handling functions
+ *
+ *  Big-endian to little-endian byte-swapping/bitmaps by
+ *        David S. Miller (davem@caip.rutgers.edu), 1995
+ */
+
+#include <asm/uaccess.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ext3_fs.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+
+static ssize_t ext3_dir_read (struct file * filp, char * buf,
+			      size_t count, loff_t *ppos)
+{
+	return -EISDIR;
+}
+
+static int ext3_readdir(struct file *, void *, filldir_t);
+
+static struct file_operations ext3_dir_operations = {
+	NULL,			/* lseek - default */
+	ext3_dir_read,		/* read */
+	NULL,			/* write - bad */
+	ext3_readdir,		/* readdir */
+	NULL,			/* poll - default */
+	ext3_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	NULL,			/* no special open code */
+	NULL,			/* flush */
+	NULL,			/* no special release code */
+	ext3_sync_file,		/* fsync */
+	NULL,			/* fasync */
+	NULL,			/* check_media_change */
+	NULL			/* revalidate */
+};
+
+/*
+ * directories can handle most operations...
+ */
+struct inode_operations ext3_dir_inode_operations = {
+	&ext3_dir_operations,	/* default directory file-ops */
+	ext3_create,		/* create */
+	ext3_lookup,		/* lookup */
+	ext3_link,		/* link */
+	ext3_unlink,		/* unlink */
+	ext3_symlink,		/* symlink */
+	ext3_mkdir,		/* mkdir */
+	ext3_rmdir,		/* rmdir */
+	ext3_mknod,		/* mknod */
+	ext3_rename,		/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	ext3_permission,	/* permission */
+	NULL			/* smap */
+};
+
+int ext3_check_dir_entry (const char * function, struct inode * dir,
+			  struct ext3_dir_entry_2 * de,
+			  struct buffer_head * bh,
+			  unsigned long offset)
+{
+	const char * error_msg = NULL;
+
+	if (le16_to_cpu(de->rec_len) < EXT3_DIR_REC_LEN(1))
+		error_msg = "rec_len is smaller than minimal";
+	else if (le16_to_cpu(de->rec_len) % 4 != 0)
+		error_msg = "rec_len % 4 != 0";
+	else if (le16_to_cpu(de->rec_len) < EXT3_DIR_REC_LEN(de->name_len))
+		error_msg = "rec_len is too small for name_len";
+	else if (dir && ((char *) de - bh->b_data) + le16_to_cpu(de->rec_len) >
+		 dir->i_sb->s_blocksize)
+		error_msg = "directory entry across blocks";
+	else if (dir && le32_to_cpu(de->inode) > le32_to_cpu(dir->i_sb->u.ext3_sb.s_es->s_inodes_count))
+		error_msg = "inode out of bounds";
+
+	if (error_msg != NULL)
+		ext3_error (dir->i_sb, function, "bad entry in directory #%lu: %s - "
+			    "offset=%lu, inode=%lu, rec_len=%d, name_len=%d",
+			    dir->i_ino, error_msg, offset,
+			    (unsigned long) le32_to_cpu(de->inode),
+			    le16_to_cpu(de->rec_len), de->name_len);
+	return error_msg == NULL ? 1 : 0;
+}
+
+static int ext3_readdir(struct file * filp,
+			 void * dirent, filldir_t filldir)
+{
+	int error = 0;
+	unsigned long offset, blk;
+	int i, num, stored;
+	struct buffer_head * bh, * tmp, * bha[16];
+	struct ext3_dir_entry_2 * de;
+	struct super_block * sb;
+	int err;
+	struct inode *inode = filp->f_dentry->d_inode;
+
+	sb = inode->i_sb;
+
+	stored = 0;
+	bh = NULL;
+	offset = filp->f_pos & (sb->s_blocksize - 1);
+
+	while (!error && !stored && filp->f_pos < inode->i_size) {
+		blk = (filp->f_pos) >> EXT3_BLOCK_SIZE_BITS(sb);
+		bh = ext3_bread (0, inode, blk, 0, &err);
+		if (!bh) {
+			ext3_error (sb, "ext3_readdir",
+				    "directory #%lu contains a hole at offset %lu",
+				    inode->i_ino, (unsigned long)filp->f_pos);
+			filp->f_pos += sb->s_blocksize - offset;
+			continue;
+		}
+
+		/*
+		 * Do the readahead
+		 */
+		if (!offset) {
+			for (i = 16 >> (EXT3_BLOCK_SIZE_BITS(sb) - 9), num = 0;
+			     i > 0; i--) {
+				tmp = ext3_getblk (NULL, inode, ++blk, 0, &err);
+				if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
+					bha[num++] = tmp;
+				else
+					brelse (tmp);
+			}
+			if (num) {
+				ll_rw_block (READA, num, bha);
+				for (i = 0; i < num; i++)
+					brelse (bha[i]);
+			}
+		}
+		
+revalidate:
+		/* If the dir block has changed since the last call to
+		 * readdir(2), then we might be pointing to an invalid
+		 * dirent right now.  Scan from the start of the block
+		 * to make sure. */
+		if (filp->f_version != inode->i_version) {
+			for (i = 0; i < sb->s_blocksize && i < offset; ) {
+				de = (struct ext3_dir_entry_2 *) 
+					(bh->b_data + i);
+				/* It's too expensive to do a full
+				 * dirent test each time round this
+				 * loop, but we do have to test at
+				 * least that it is non-zero.  A
+				 * failure will be detected in the
+				 * dirent test below. */
+				if (le16_to_cpu(de->rec_len) < EXT3_DIR_REC_LEN(1))
+					break;
+				i += le16_to_cpu(de->rec_len);
+			}
+			offset = i;
+			filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1))
+				| offset;
+			filp->f_version = inode->i_version;
+		}
+		
+		while (!error && filp->f_pos < inode->i_size 
+		       && offset < sb->s_blocksize) {
+			de = (struct ext3_dir_entry_2 *) (bh->b_data + offset);
+			if (!ext3_check_dir_entry ("ext3_readdir", inode, de,
+						   bh, offset)) {
+				/* On error, skip the f_pos to the
+                                   next block. */
+				filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1))
+					      + sb->s_blocksize;
+				brelse (bh);
+				return stored;
+			}
+			offset += le16_to_cpu(de->rec_len);
+			if (le32_to_cpu(de->inode)) {
+				/* We might block in the next section
+				 * if the data destination is
+				 * currently swapped out.  So, use a
+				 * version stamp to detect whether or
+				 * not the directory has been modified
+				 * during the copy operation.
+				 * AV: It can't be modified, but it fscking
+				 * can be seeked by another process that shares
+				 * the descriptor.
+				 */
+				unsigned long version = filp->f_version;
+
+				error = filldir(dirent, de->name,
+						de->name_len,
+						filp->f_pos, le32_to_cpu(de->inode));
+				if (error)
+					break;
+				if (version != filp->f_version)
+					goto revalidate;
+				stored ++;
+			}
+			filp->f_pos += le16_to_cpu(de->rec_len);
+		}
+		offset = 0;
+		brelse (bh);
+	}
+	UPDATE_ATIME(inode);
+	return 0;
+}
--- linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/file.c.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/file.c	Wed Oct 25 23:21:36 2000
@@ -0,0 +1,491 @@
+/*
+ *  linux/fs/ext3/file.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/file.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  ext3 fs regular file handling primitives
+ *
+ *  64-bit file support on 64-bit platforms by Jakub Jelinek
+ * 	(jj@sunsite.ms.mff.cuni.cz)
+ */
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/ext3_fs.h>
+#include <linux/ext3_jfs.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+
+#define	NBUF	32
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#define MAX(a,b) (((a)>(b))?(a):(b))
+
+static long long ext3_file_lseek(struct file *, long long, int);
+static ssize_t ext3_file_write (struct file *, const char *, size_t, loff_t *);
+static int ext3_release_file (struct inode *, struct file *);
+#if BITS_PER_LONG < 64
+static int ext3_open_file (struct inode *, struct file *);
+
+#else
+
+#define EXT3_MAX_SIZE(bits)							\
+	(((EXT3_NDIR_BLOCKS + (1LL << (bits - 2)) + 				\
+	   (1LL << (bits - 2)) * (1LL << (bits - 2)) + 				\
+	   (1LL << (bits - 2)) * (1LL << (bits - 2)) * (1LL << (bits - 2))) * 	\
+	  (1LL << bits)) - 1)
+
+long long ext3_max_sizes[] = {
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+EXT3_MAX_SIZE(10), EXT3_MAX_SIZE(11), EXT3_MAX_SIZE(12), EXT3_MAX_SIZE(13)
+};
+
+#endif
+
+/*
+ * We have mostly NULL's here: the current defaults are ok for
+ * the ext3 filesystem.
+ */
+static struct file_operations ext3_file_operations = {
+	ext3_file_lseek,	/* lseek */
+	generic_file_read,	/* read */
+	ext3_file_write,	/* write */
+	NULL,			/* readdir - bad */
+	NULL,			/* poll - default */
+	ext3_ioctl,		/* ioctl */
+	generic_file_mmap,	/* mmap */
+#if BITS_PER_LONG == 64	
+	NULL,			/* no special open is needed */
+#else
+	ext3_open_file,
+#endif
+	NULL,			/* flush */
+	ext3_release_file,	/* release */
+	ext3_sync_file,		/* fsync */
+	NULL,			/* fasync */
+	NULL,			/* check_media_change */
+	NULL			/* revalidate */
+};
+
+struct inode_operations ext3_file_inode_operations = {
+	&ext3_file_operations,/* default file operations */
+	NULL,			/* create */
+	NULL,			/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	generic_readpage,	/* readpage */
+	NULL,			/* writepage */
+	ext3_bmap,		/* bmap */
+	ext3_truncate,		/* truncate */
+	ext3_permission,	/* permission */
+	NULL			/* smap */
+};
+
+
+static inline int ext3_should_journal_data(struct inode *inode)
+{
+	if (!S_ISREG(inode->i_mode))
+		return 1;
+	if (test_opt(inode->i_sb, DATA_FLAGS) == EXT3_MOUNT_JOURNAL_DATA)
+		return 1;
+	return 0;
+}
+
+
+/*
+ * Make sure the offset never goes beyond the 32-bit mark..
+ */
+static long long ext3_file_lseek(
+	struct file *file,
+	long long offset,
+	int origin)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+
+	switch (origin) {
+		case 2:
+			offset += inode->i_size;
+			break;
+		case 1:
+			offset += file->f_pos;
+	}
+	if (((unsigned long long) offset >> 32) != 0) {
+#if BITS_PER_LONG < 64
+		return -EINVAL;
+#else
+		if (offset > ext3_max_sizes[EXT3_BLOCK_SIZE_BITS(inode->i_sb)])
+			return -EINVAL;
+#endif
+	} 
+	if (offset != file->f_pos) {
+		file->f_pos = offset;
+		file->f_reada = 0;
+		file->f_version = ++global_event;
+	}
+	return offset;
+}
+
+static inline void remove_suid(struct inode *inode)
+{
+	unsigned int mode;
+
+	/* set S_IGID if S_IXGRP is set, and always set S_ISUID */
+	mode = (inode->i_mode & S_IXGRP)*(S_ISGID/S_IXGRP) | S_ISUID;
+
+	/* was any of the uid bits set? */
+	mode &= inode->i_mode;
+	if (mode && !capable(CAP_FSETID)) {
+		inode->i_mode &= ~mode;
+		mark_inode_dirty(inode);
+	}
+}
+
+static ssize_t ext3_file_write (struct file * filp, const char * buf,
+				size_t count, loff_t *ppos)
+{
+	struct inode * inode = filp->f_dentry->d_inode;
+	off_t pos;
+	long block;
+	int offset;
+	int written, c, needed, err;
+	struct buffer_head * bh, *bufferlist[NBUF];
+	struct super_block * sb;
+	int i, buffercount, write_error, new_buffer;
+	unsigned long limit;
+	handle_t *handle;
+	int will_journal_data;
+	
+	/* POSIX: mtime/ctime may not change for 0 count */
+	if (!count)
+		return 0;
+	/* This makes the bounds-checking arithmetic later on much more
+	 * sane. */
+	if (((signed) count) < 0)
+ 		return -EINVAL;
+	
+	write_error = buffercount = 0;
+	if (!inode) {
+		printk("ext3_file_write: inode = NULL\n");
+		return -EINVAL;
+	}
+	sb = inode->i_sb;
+	if (sb->s_flags & MS_RDONLY)
+		/*
+		 * This fs has been automatically remounted ro because
+		 * of errors 
+		 */
+		return -ENOSPC;
+
+	if (!S_ISREG(inode->i_mode)) {
+		ext3_warning (sb, "ext3_file_write", "mode = %07o",
+			      inode->i_mode);
+		return -EINVAL;
+	}
+	remove_suid(inode);
+
+	if (filp->f_flags & O_APPEND)
+		pos = inode->i_size;
+	else {
+		pos = *ppos;
+		if (pos != *ppos)
+			return -EINVAL;
+#if BITS_PER_LONG >= 64
+		if (pos > ext3_max_sizes[EXT3_BLOCK_SIZE_BITS(sb)])
+			return -EINVAL;
+#endif
+	}
+
+	block = pos >> EXT3_BLOCK_SIZE_BITS(sb);
+	offset = pos & (sb->s_blocksize - 1);
+	c = sb->s_blocksize - offset;
+
+	/* Record this now so that we don't get confused if the user
+	 * changes the flag half-way through! */
+	will_journal_data = ext3_should_journal_data(inode);
+	
+	/* How large a transaction might we need?  We can always
+	 * underestimate and grow later for really large writes */
+	if (will_journal_data) {
+		needed = (count >> EXT3_BLOCK_SIZE_BITS(sb)) + 1;
+		if (needed > EXT3_MAX_TRANS_DATA)
+			needed = EXT3_MAX_TRANS_DATA;
+	} else 
+		needed = 0;
+	
+	handle = ext3_journal_start(inode, EXT3_DATA_TRANS_BLOCKS + needed);
+		
+	
+	/* Check for overflow.. */
+
+#if BITS_PER_LONG < 64
+	/* If the fd's pos is already greater than or equal to the file
+	 * descriptor's offset maximum, then we need to return EFBIG for
+	 * any non-zero count (and we already tested for zero above). */
+	if (((unsigned) pos) >= 0x7FFFFFFFUL)  {
+		written = -EFBIG;
+		goto error_out;
+	}
+	
+	/* If we are about to overflow the maximum file size, we also
+	 * need to return the error, but only if no bytes can be written
+	 * successfully. */
+	if (((unsigned) pos + count) > 0x7FFFFFFFUL) {
+		count = 0x7FFFFFFFL - pos;
+		if (((signed) count) < 0)  {
+			written = -EFBIG;
+			goto error_out;
+		}
+	}
+#else
+	{
+		off_t max = ext3_max_sizes[EXT3_BLOCK_SIZE_BITS(sb)];
+
+		if (pos >= max) {
+			written = -EFBIG;
+			goto error_out;
+		}
+		
+		if (pos + count > max) {
+			count = max - pos;
+			if (!count) {
+				written = -EFBIG;
+				goto error_out;
+			}
+		}
+		if (((pos + count) >> 31) && 
+		    !(sb->u.ext3_sb.s_es->s_feature_ro_compat &
+		      cpu_to_le32(EXT3_FEATURE_RO_COMPAT_LARGE_FILE))) {
+			/* If this is the first large file created, add a flag
+			   to the superblock */
+			journal_get_write_access(handle, sb->u.ext3_sb.s_sbh);
+			sb->u.ext3_sb.s_es->s_feature_ro_compat |=
+				cpu_to_le32(EXT3_FEATURE_RO_COMPAT_LARGE_FILE);
+			journal_dirty_metadata(handle, sb->u.ext3_sb.s_sbh);
+		}
+	}
+#endif
+
+	/* From SUS: We must generate a SIGXFSZ for file size overflow
+	 * only if no bytes were actually written to the file. --sct */
+
+	limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
+	if (limit < RLIM_INFINITY) {
+		if (((unsigned) pos+count) >= limit) {
+			count = limit - pos;
+			if (((signed) count) <= 0) {
+				send_sig(SIGXFSZ, current, 0);
+				written = -EFBIG;
+				goto error_out;
+			}
+		}
+	}
+
+	written = 0;
+	do {
+		/* Each time round the loop, work out whether we can
+		 * continue as part of the current transaction or
+		 * whether we need to start another one.  This obviously
+		 * matters only for large writes. */
+		if (handle->h_buffer_credits < EXT3_RESERVE_TRANS_BLOCKS) {
+			needed = (count >> EXT3_BLOCK_SIZE_BITS(sb)) + 1;
+			if (needed > EXT3_MAX_TRANS_DATA)
+				needed = EXT3_MAX_TRANS_DATA;
+			if (journal_extend(handle, needed)) {
+				/* Couldn't extend: OK, commit the current
+				 * transaction and start a new one. */
+				if (pos > inode->u.ext3_i.i_disksize)
+					inode->u.ext3_i.i_disksize = pos;
+				inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+				ext3_mark_inode_dirty(handle, inode);
+				err = ext3_journal_stop(handle, inode);
+				if (err) {
+					written = err;
+					break;
+				}
+				
+				handle = ext3_journal_start
+				    (inode, EXT3_DATA_TRANS_BLOCKS + needed);
+			}
+		}
+
+		bh = ext3_getblk (handle, inode, block, 1, &err);
+		if (!bh) {
+			if (!written)
+				written = err;
+			break;
+		}
+		if (c > count)
+			c = count;
+
+		/* Tricky: what happens if we are writing the complete
+		 * contents of a block which is not currently
+		 * initialised?  We have to obey the same
+		 * synchronisation rules as the IO code, to prevent some
+		 * other process from stomping on the buffer contents by
+		 * refreshing them from disk while we are setting up the
+		 * buffer.  The copy_from_user() can page fault, after
+		 * all.  We also have to throw away partially successful
+		 * copy_from_users to such buffers, since we can't trust
+		 * the rest of the buffer_head in that case.  --sct */
+
+		new_buffer = (!buffer_uptodate(bh) && !buffer_locked(bh) &&
+			      c == sb->s_blocksize);
+
+		if (new_buffer) {
+			set_bit(BH_Lock, &bh->b_state);
+			if (will_journal_data)
+				journal_get_create_access(handle, bh);
+			c -= copy_from_user (bh->b_data + offset, buf, c);
+			if (c != sb->s_blocksize) {
+				/* On getting an EFAULT mid-copy, we end
+				 * up throwing away the whole block.
+				 * Too bad.  */
+				c = 0;
+				unlock_buffer(bh);
+				if (will_journal_data)
+					journal_release_buffer(handle, bh);
+				brelse(bh);
+				if (!written)
+					written = -EFAULT;
+				break;
+			}
+			mark_buffer_uptodate(bh, 1);
+			unlock_buffer(bh);
+		} else {
+			if (!buffer_uptodate(bh)) {
+				ll_rw_block (READ, 1, &bh);
+				wait_on_buffer (bh);
+				if (!buffer_uptodate(bh)) {
+					brelse (bh);
+					if (!written)
+						written = -EIO;
+					break;
+				}
+			}
+			if (will_journal_data)
+				journal_get_write_access(handle, bh);
+			c -= copy_from_user (bh->b_data + offset, buf, c);
+		}
+		if (!c) {
+			journal_release_buffer(handle, bh);
+			brelse(bh);
+			if (!written)
+				written = -EFAULT;
+			break;
+		}
+
+		if (will_journal_data)
+			journal_dirty_metadata(handle, bh);
+		else if (test_opt(inode->i_sb, DATA_FLAGS) == EXT3_MOUNT_ORDERED_DATA)
+			journal_dirty_data(handle, bh);
+		else 
+			mark_buffer_dirty(bh, 0);
+
+		update_vm_cache(inode, pos, bh->b_data + offset, c);
+		pos += c;
+		written += c;
+		buf += c;
+		count -= c;
+		brelse(bh);
+
+		/* @@@ For O_SYNC, we'll need to restore this bit once
+		 * we are doing separate non-journaled data updates. */
+#if 0
+		if (buffercount == NBUF){
+			ll_rw_block(WRITE, buffercount, bufferlist);
+			for(i=0; i<buffercount; i++){
+				wait_on_buffer(bufferlist[i]);
+				if (!buffer_uptodate(bufferlist[i]))
+					write_error=1;
+				brelse(bufferlist[i]);
+			}
+			buffercount=0;
+		}
+		if(write_error)
+			break;
+
+#endif
+		block++;
+		offset = 0;
+		c = sb->s_blocksize;
+	} while (count);
+
+#if 0
+	if ( buffercount ){
+		ll_rw_block(WRITE, buffercount, bufferlist);
+		for(i=0; i<buffercount; i++){
+			wait_on_buffer(bufferlist[i]);
+			if (!buffer_uptodate(bufferlist[i]))
+				write_error=1;
+			brelse(bufferlist[i]);
+		}
+	}		
+#endif
+
+	/*
+	 * If a file has been opened in synchronous mode, we have to ensure
+	 * that meta-data will also be written synchronously.  
+	 */
+	if (filp->f_flags & O_SYNC)
+		handle->h_sync = 1;
+
+	if (pos > inode->i_size) {
+		inode->i_size = pos;
+		inode->u.ext3_i.i_disksize = pos;
+	}
+	
+	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+	*ppos = pos;
+	ext3_mark_inode_dirty(handle, inode);
+error_out:
+	ext3_journal_stop(handle, inode);
+	return written;
+}
+
+/*
+ * Called when an inode is released. Note that this is different
+ * from ext3_file_open: open gets called at every open, but release
+ * gets called only when /all/ the files are closed.
+ */
+static int ext3_release_file (struct inode * inode, struct file * filp)
+{
+	if (filp->f_mode & FMODE_WRITE)
+		ext3_discard_prealloc (inode);
+	return 0;
+}
+
+#if BITS_PER_LONG < 64
+/*
+ * Called when an inode is about to be open.
+ * We use this to disallow opening RW large files on 32bit systems.
+ */
+static int ext3_open_file (struct inode * inode, struct file * filp)
+{
+	if (inode->u.ext3_i.i_high_size && (filp->f_mode & FMODE_WRITE))
+		return -EFBIG;
+	return 0;
+}
+#endif
--- linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/fsync.c.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/fsync.c	Wed Oct 25 19:10:56 2000
@@ -0,0 +1,308 @@
+/*
+ *  linux/fs/ext3/fsync.c
+ *
+ *  Copyright (C) 1993  Stephen Tweedie (sct@dcs.ed.ac.uk)
+ *  from
+ *  Copyright (C) 1992  Remy Card (card@masi.ibp.fr)
+ *                      Laboratoire MASI - Institut Blaise Pascal
+ *                      Universite Pierre et Marie Curie (Paris VI)
+ *  from
+ *  linux/fs/minix/truncate.c   Copyright (C) 1991, 1992  Linus Torvalds
+ * 
+ *  ext3fs fsync primitive
+ *
+ *  Big-endian to little-endian byte-swapping/bitmaps by
+ *        David S. Miller (davem@caip.rutgers.edu), 1995
+ * 
+ *  Removed unnecessary code duplication for little endian machines
+ *  and excessive __inline__s. 
+ *        Andi Kleen, 1997
+ */
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ext3_fs.h>
+#include <linux/ext3_jfs.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+
+
+#define blocksize (EXT3_BLOCK_SIZE(inode->i_sb))
+#define addr_per_block (EXT3_ADDR_PER_BLOCK(inode->i_sb))
+
+static int sync_block (struct inode * inode, u32 * block, int wait)
+{
+	struct buffer_head * bh;
+	
+	if (!*block)
+		return 0;
+	bh = get_hash_table (inode->i_dev, *block, blocksize);
+	if (!bh)
+		return 0;
+	if (wait && buffer_req(bh) && !buffer_uptodate(bh)) {
+		/* There can be a parallel read(2) that started read-I/O
+		   on the buffer so we can't assume that there's been
+		   an I/O error without first waiting I/O completation. */
+		wait_on_buffer(bh);
+		if (!buffer_uptodate(bh))
+		{
+			brelse (bh);
+			return -1;
+		}
+	}
+	if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) {
+		if (wait)
+			/* when we return from fsync all the blocks
+			   must be _just_ stored on disk */
+			wait_on_buffer(bh);
+		brelse (bh);
+		return 0;
+	}
+	ll_rw_block (WRITE, 1, &bh);
+	bh->b_count--;
+	return 0;
+}
+
+#ifndef __LITTLE_ENDIAN
+static int sync_block_swab32 (struct inode * inode, u32 * block, int wait)
+{
+	struct buffer_head * bh;
+	
+	if (!le32_to_cpu(*block))
+		return 0;
+	bh = get_hash_table (inode->i_dev, le32_to_cpu(*block), blocksize);
+	if (!bh)
+		return 0;
+	if (wait && buffer_req(bh) && !buffer_uptodate(bh)) {
+		brelse (bh);
+		return -1;
+	}
+	if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) {
+		brelse (bh);
+		return 0;
+	}
+	ll_rw_block (WRITE, 1, &bh);
+	bh->b_count--;
+	return 0;
+}
+#else
+#define sync_block_swab32 sync_block
+#endif
+
+
+static int sync_iblock (struct inode * inode, u32 * iblock, 
+			struct buffer_head ** bh, int wait) 
+{
+	int rc, tmp;
+	
+	*bh = NULL;
+	tmp = *iblock;
+	if (!tmp)
+		return 0;
+	rc = sync_block (inode, iblock, wait);
+	if (rc)
+		return rc;
+	*bh = bread (inode->i_dev, tmp, blocksize);
+	if (!*bh)
+		return -1;
+	return 0;
+}
+
+#ifndef __LITTLE_ENDIAN
+static int sync_iblock_swab32 (struct inode * inode, u32 * iblock, 
+			       struct buffer_head ** bh, int wait) 
+{
+	int rc, tmp;
+	
+	*bh = NULL;
+	tmp = le32_to_cpu(*iblock);
+	if (!tmp)
+		return 0;
+	rc = sync_block_swab32 (inode, iblock, wait);
+	if (rc)
+		return rc;
+	*bh = bread (inode->i_dev, tmp, blocksize);
+	if (!*bh)
+		return -1;
+	return 0;
+}
+#else
+#define sync_iblock_swab32 sync_iblock
+#endif
+
+static int sync_direct (struct inode * inode, int wait)
+{
+	int i;
+	int rc, err = 0;
+
+	for (i = 0; i < EXT3_NDIR_BLOCKS; i++) {
+		rc = sync_block (inode, inode->u.ext3_i.i_data_u.i_data + i, wait);
+		if (rc)
+			err = rc;
+	}
+	return err;
+}
+
+static int sync_indirect (struct inode * inode, u32 * iblock, int wait)
+{
+	int i;
+	struct buffer_head * ind_bh;
+	int rc, err = 0;
+
+	rc = sync_iblock (inode, iblock, &ind_bh, wait);
+	if (rc || !ind_bh)
+		return rc;
+	
+	for (i = 0; i < addr_per_block; i++) {
+		rc = sync_block_swab32 (inode, 
+					((u32 *) ind_bh->b_data) + i,
+					wait);
+		if (rc)
+			err = rc;
+	}
+	brelse (ind_bh);
+	return err;
+}
+
+#ifndef __LITTLE_ENDIAN
+static __inline__ int sync_indirect_swab32 (struct inode * inode, u32 * iblock, int wait)
+{
+	int i;
+	struct buffer_head * ind_bh;
+	int rc, err = 0;
+
+	rc = sync_iblock_swab32 (inode, iblock, &ind_bh, wait);
+	if (rc || !ind_bh)
+		return rc;
+	
+	for (i = 0; i < addr_per_block; i++) {
+		rc = sync_block_swab32 (inode, 
+					((u32 *) ind_bh->b_data) + i,
+					wait);
+		if (rc)
+			err = rc;
+	}
+	brelse (ind_bh);
+	return err;
+}
+#else
+#define sync_indirect_swab32 sync_indirect
+#endif
+
+static int sync_dindirect (struct inode * inode, u32 * diblock, int wait)
+{
+	int i;
+	struct buffer_head * dind_bh;
+	int rc, err = 0;
+
+	rc = sync_iblock (inode, diblock, &dind_bh, wait);
+	if (rc || !dind_bh)
+		return rc;
+	
+	for (i = 0; i < addr_per_block; i++) {
+		rc = sync_indirect_swab32 (inode,
+					   ((u32 *) dind_bh->b_data) + i,
+					   wait);
+		if (rc)
+			err = rc;
+	}
+	brelse (dind_bh);
+	return err;
+}
+
+#ifndef __LITTLE_ENDIAN
+static __inline__ int sync_dindirect_swab32 (struct inode * inode, u32 * diblock, int wait)
+{
+	int i;
+	struct buffer_head * dind_bh;
+	int rc, err = 0;
+
+	rc = sync_iblock_swab32 (inode, diblock, &dind_bh, wait);
+	if (rc || !dind_bh)
+		return rc;
+	
+	for (i = 0; i < addr_per_block; i++) {
+		rc = sync_indirect_swab32 (inode,
+					   ((u32 *) dind_bh->b_data) + i,
+					   wait);
+		if (rc)
+			err = rc;
+	}
+	brelse (dind_bh);
+	return err;
+}
+#else
+#define sync_dindirect_swab32 sync_dindirect
+#endif
+
+static int sync_tindirect (struct inode * inode, u32 * tiblock, int wait)
+{
+	int i;
+	struct buffer_head * tind_bh;
+	int rc, err = 0;
+
+	rc = sync_iblock (inode, tiblock, &tind_bh, wait);
+	if (rc || !tind_bh)
+		return rc;
+	
+	for (i = 0; i < addr_per_block; i++) {
+		rc = sync_dindirect_swab32 (inode,
+					    ((u32 *) tind_bh->b_data) + i,
+					    wait);
+		if (rc)
+			err = rc;
+	}
+	brelse (tind_bh);
+	return err;
+}
+
+/*
+ *	File may be NULL when we are called. Perhaps we shouldn't
+ *	even pass file to fsync ?
+ */
+
+int ext3_sync_file(struct file * file, struct dentry *dentry)
+{
+	int wait, err = 0;
+	struct inode *inode = dentry->d_inode;
+	handle_t *handle;
+
+ 	handle = ext3_journal_start(inode, 1);
+ 	if (IS_ERR(handle))
+ 		return PTR_ERR(handle);
+ 	
+	handle->h_sync = 1;
+	
+	if (S_ISLNK(inode->i_mode) && !(inode->i_blocks))
+		/*
+		 * Don't sync fast links!
+		 */
+		goto skip;
+
+#if 0 /* Non-journaled data needs to be taken care of here @@@ */
+	for (wait=0; wait<=1; wait++)
+	{
+		err |= sync_direct (inode, wait);
+		err |= sync_indirect (inode,
+				      inode->u.ext3_i.i_data_u.i_data+EXT3_IND_BLOCK,
+				      wait);
+		err |= sync_dindirect (inode,
+				       inode->u.ext3_i.i_data_u.i_data+EXT3_DIND_BLOCK, 
+				       wait);
+		err |= sync_tindirect (inode, 
+				       inode->u.ext3_i.i_data_u.i_data+EXT3_TIND_BLOCK, 
+				       wait);
+	}
+#endif
+
+skip:
+	err |= ext3_mark_inode_dirty (handle, inode);
+	ext3_journal_stop(handle, inode);
+	return err ? -EIO : 0;
+}
--- linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/ialloc.c.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/ialloc.c	Wed Oct 25 19:10:56 2000
@@ -0,0 +1,630 @@
+/*
+ *  linux/fs/ext3/ialloc.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)
+ *
+ *  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
+ */
+
+/*
+ * ialloc.c contains the inodes allocation and deallocation routines
+ */
+
+/*
+ * The free inodes are managed by bitmaps.  A file system contains several
+ * blocks groups.  Each group contains 1 bitmap block for blocks, 1 bitmap
+ * block for inodes, N blocks for the inode table and data blocks.
+ *
+ * The file system contains group descriptors which are located after the
+ * super block.  Each descriptor contains the number of the bitmap block and
+ * the free blocks count in the block.  The descriptors are loaded in memory
+ * when a file system is mounted (see ext3_read_super).
+ */
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/ext3_fs.h>
+#include <linux/ext3_jfs.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/quotaops.h>
+
+#include <asm/bitops.h>
+#include <asm/byteorder.h>
+
+/*
+ * 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.
+ */
+static int read_inode_bitmap (struct super_block * sb,
+			       unsigned long block_group,
+			       unsigned int bitmap_nr)
+{
+	struct ext3_group_desc * gdp;
+	struct buffer_head * bh = NULL;
+	int retval = 0;
+
+	gdp = ext3_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) {
+		ext3_error (sb, "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.ext3_sb.s_inode_bitmap_number[bitmap_nr] = block_group;
+	sb->u.ext3_sb.s_inode_bitmap[bitmap_nr] = bh;
+	return retval;
+}
+
+/*
+ * load_inode_bitmap loads the inode bitmap for a blocks group
+ *
+ * It maintains a cache for the last bitmaps loaded.  This cache is managed
+ * with a LRU algorithm.
+ *
+ * Notes:
+ * 1/ There is one cache per mounted file system.
+ * 2/ If the file system contains less than EXT3_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,
+			      unsigned int block_group)
+{
+	int i, j, retval = 0;
+	unsigned long inode_bitmap_number;
+	struct buffer_head * inode_bitmap;
+
+	if (block_group >= sb->u.ext3_sb.s_groups_count)
+		ext3_panic (sb, "load_inode_bitmap",
+			    "block_group >= groups_count - "
+			    "block_group = %d, groups_count = %lu",
+			     block_group, sb->u.ext3_sb.s_groups_count);
+	if (sb->u.ext3_sb.s_loaded_inode_bitmaps > 0 &&
+	    sb->u.ext3_sb.s_inode_bitmap_number[0] == block_group &&
+	    sb->u.ext3_sb.s_inode_bitmap[0] != NULL)
+		return 0;
+	if (sb->u.ext3_sb.s_groups_count <= EXT3_MAX_GROUP_LOADED) {
+		if (sb->u.ext3_sb.s_inode_bitmap[block_group]) {
+			if (sb->u.ext3_sb.s_inode_bitmap_number[block_group] != block_group)
+				ext3_panic (sb, "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;
+			return block_group;
+		}
+	}
+
+	for (i = 0; i < sb->u.ext3_sb.s_loaded_inode_bitmaps &&
+		    sb->u.ext3_sb.s_inode_bitmap_number[i] != block_group;
+	     i++)
+		;
+	if (i < sb->u.ext3_sb.s_loaded_inode_bitmaps &&
+  	    sb->u.ext3_sb.s_inode_bitmap_number[i] == block_group) {
+		inode_bitmap_number = sb->u.ext3_sb.s_inode_bitmap_number[i];
+		inode_bitmap = sb->u.ext3_sb.s_inode_bitmap[i];
+		for (j = i; j > 0; j--) {
+			sb->u.ext3_sb.s_inode_bitmap_number[j] =
+				sb->u.ext3_sb.s_inode_bitmap_number[j - 1];
+			sb->u.ext3_sb.s_inode_bitmap[j] =
+				sb->u.ext3_sb.s_inode_bitmap[j - 1];
+		}
+		sb->u.ext3_sb.s_inode_bitmap_number[0] = inode_bitmap_number;
+		sb->u.ext3_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);
+		
+	} else {
+		if (sb->u.ext3_sb.s_loaded_inode_bitmaps < EXT3_MAX_GROUP_LOADED)
+			sb->u.ext3_sb.s_loaded_inode_bitmaps++;
+		else
+			brelse (sb->u.ext3_sb.s_inode_bitmap[EXT3_MAX_GROUP_LOADED - 1]);
+		for (j = sb->u.ext3_sb.s_loaded_inode_bitmaps - 1; j > 0; j--) {
+			sb->u.ext3_sb.s_inode_bitmap_number[j] =
+				sb->u.ext3_sb.s_inode_bitmap_number[j - 1];
+			sb->u.ext3_sb.s_inode_bitmap[j] =
+				sb->u.ext3_sb.s_inode_bitmap[j - 1];
+		}
+		retval = read_inode_bitmap (sb, block_group, 0);
+	}
+	return retval;
+}
+
+/*
+ * 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 ext3_free_inode (handle_t *handle, struct inode * inode)
+{
+	struct super_block * sb = inode->i_sb;
+	int is_directory;
+	unsigned long ino;
+	struct buffer_head * bh;
+	struct buffer_head * bh2;
+	unsigned long block_group;
+	unsigned long bit;
+	int bitmap_nr;
+	struct ext3_group_desc * gdp;
+	struct ext3_super_block * es;
+
+	if (!inode->i_dev) {
+		printk ("ext3_free_inode: inode has no device\n");
+		return;
+	}
+	if (inode->i_count > 1) {
+		printk ("ext3_free_inode: inode has count=%d\n", inode->i_count);
+		return;
+	}
+	if (inode->i_nlink) {
+		printk ("ext3_free_inode: inode has nlink=%d\n",
+			inode->i_nlink);
+		return;
+	}
+	if (!sb) {
+		printk("ext3_free_inode: inode on nonexistent device\n");
+		return;
+	}
+
+	ino = inode->i_ino;
+	ext3_debug ("freeing inode %lu\n", ino);
+
+	/*
+	 * 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.ext3_sb.s_es;
+	if (ino < EXT3_FIRST_INO(sb) || 
+	    ino > le32_to_cpu(es->s_inodes_count)) {
+		ext3_error (sb, "free_inode",
+			    "reserved inode or nonexistent inode");
+		goto error_return;
+	}
+	block_group = (ino - 1) / EXT3_INODES_PER_GROUP(sb);
+	bit = (ino - 1) % EXT3_INODES_PER_GROUP(sb);
+	bitmap_nr = load_inode_bitmap (sb, block_group);
+	if (bitmap_nr < 0)
+		goto error_return;
+	
+	bh = sb->u.ext3_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);
+
+	journal_get_write_access(handle, bh);
+	/* Ok, now we can actually update the inode bitmaps.. */
+	if (!ext3_clear_bit (bit, bh->b_data))
+		ext3_warning (sb, "ext3_free_inode",
+			      "bit already cleared for inode %lu", ino);
+	else {
+		gdp = ext3_get_group_desc (sb, block_group, &bh2);
+		journal_get_write_access(handle, bh2);
+		journal_get_write_access(handle, sb->u.ext3_sb.s_sbh);
+		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);
+		}
+		journal_dirty_metadata(handle, bh2);
+		es->s_free_inodes_count =
+			cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) + 1);
+		journal_dirty_metadata(handle, sb->u.ext3_sb.s_sbh);
+	}
+	journal_dirty_metadata(handle, bh);
+	if (sb->s_flags & MS_SYNCHRONOUS) 
+		handle->h_sync = 1;
+	sb->s_dirt = 1;
+error_return:
+	unlock_super (sb);
+}
+
+/*
+ * 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
+ * free space and a low directory-to-inode ratio; if that fails, then of
+ * the groups with above-average free space, that group with the fewest
+ * directories already is chosen.
+ *
+ * For other inodes, search forward from the parent directory\'s block
+ * group to find a free inode.
+ */
+struct inode * ext3_new_inode (handle_t *handle, const struct inode * dir, 
+			       int mode, int * err)
+{
+	struct super_block * sb;
+	struct buffer_head * bh;
+	struct buffer_head * bh2;
+	int i, j, avefreei;
+	struct inode * inode;
+	int bitmap_nr;
+	struct ext3_group_desc * gdp;
+	struct ext3_group_desc * tmp;
+	struct ext3_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;
+	}
+
+	sb = dir->i_sb;
+	inode->i_sb = sb;
+	inode->i_flags = 0;
+	lock_super (sb);
+	es = sb->u.ext3_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.ext3_sb.s_groups_count;
+/* I am not yet convinced that this next bit is necessary.
+		i = dir->u.ext3_i.i_block_group;
+		for (j = 0; j < sb->u.ext3_sb.s_groups_count; j++) {
+			tmp = ext3_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)) {
+				gdp = tmp;
+				break;
+			}
+			else
+			i = ++i % sb->u.ext3_sb.s_groups_count;
+		}
+*/
+		if (!gdp) {
+			for (j = 0; j < sb->u.ext3_sb.s_groups_count; j++) {
+				tmp = ext3_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) {
+					if (!gdp || 
+					    (le16_to_cpu(tmp->bg_free_blocks_count) >
+					     le16_to_cpu(gdp->bg_free_blocks_count))) {
+						i = j;
+						gdp = tmp;
+					}
+				}
+			}
+		}
+	}
+	else 
+	{
+		/*
+		 * Try to place the inode in its parent directory
+		 */
+		i = dir->u.ext3_i.i_block_group;
+		tmp = ext3_get_group_desc (sb, i, &bh2);
+		if (tmp && le16_to_cpu(tmp->bg_free_inodes_count))
+			gdp = tmp;
+		else
+		{
+			/*
+			 * Use a quadratic hash to find a group with a
+			 * free inode
+			 */
+			for (j = 1; j < sb->u.ext3_sb.s_groups_count; j <<= 1) {
+				i += j;
+				if (i >= sb->u.ext3_sb.s_groups_count)
+					i -= sb->u.ext3_sb.s_groups_count;
+				tmp = ext3_get_group_desc (sb, i, &bh2);
+				if (tmp &&
+				    le16_to_cpu(tmp->bg_free_inodes_count)) {
+					gdp = tmp;
+					break;
+				}
+			}
+		}
+		if (!gdp) {
+			/*
+			 * That failed: try linear search for a free inode
+			 */
+			i = dir->u.ext3_i.i_block_group + 1;
+			for (j = 2; j < sb->u.ext3_sb.s_groups_count; j++) {
+				if (++i >= sb->u.ext3_sb.s_groups_count)
+					i = 0;
+				tmp = ext3_get_group_desc (sb, i, &bh2);
+				if (tmp &&
+				    le16_to_cpu(tmp->bg_free_inodes_count)) {
+					gdp = tmp;
+					break;
+				}
+			}
+		}
+	}
+
+	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;
+	}
+
+	bh = sb->u.ext3_sb.s_inode_bitmap[bitmap_nr];
+
+	if (sb->s_flags & MS_SYNCHRONOUS) 
+		handle->h_sync = 1;
+	
+	if ((j = ext3_find_first_zero_bit ((unsigned long *) bh->b_data,
+				      EXT3_INODES_PER_GROUP(sb))) <
+	    EXT3_INODES_PER_GROUP(sb)) {
+		journal_get_write_access(handle, bh);
+		if (ext3_set_bit (j, bh->b_data)) {
+			ext3_warning (sb, "ext3_new_inode",
+				      "bit already set for inode %d", j);
+			goto repeat;
+		}
+		journal_dirty_metadata(handle, bh);
+	} else {
+		if (le16_to_cpu(gdp->bg_free_inodes_count) != 0) {
+			ext3_error (sb, "ext3_new_inode",
+				    "Free inodes count corrupted in group %d",
+				    i);
+			unlock_super (sb);
+			iput (inode);
+			return NULL;
+		}
+		goto repeat;
+	}
+	j += i * EXT3_INODES_PER_GROUP(sb) + 1;
+	if (j < EXT3_FIRST_INO(sb) || j > le32_to_cpu(es->s_inodes_count)) {
+		ext3_error (sb, "ext3_new_inode",
+			    "reserved inode or inode > inodes count - "
+			    "block_group = %d,inode=%d", i, j);
+		unlock_super (sb);
+		iput (inode);
+		return NULL;
+	}
+
+	journal_get_write_access(handle, bh2);
+	gdp->bg_free_inodes_count =
+		cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) - 1);
+	if (S_ISDIR(mode))
+		gdp->bg_used_dirs_count =
+			cpu_to_le16(le16_to_cpu(gdp->bg_used_dirs_count) + 1);
+	journal_dirty_metadata(handle, bh2);
+
+	journal_get_write_access(handle, sb->u.ext3_sb.s_sbh);
+	es->s_free_inodes_count =
+		cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) - 1);
+	journal_dirty_metadata(handle, sb->u.ext3_sb.s_sbh);
+	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.ext3_i.i_new_inode = 1;
+	inode->u.ext3_i.i_flags = dir->u.ext3_i.i_flags;
+	if (S_ISLNK(mode))
+		inode->u.ext3_i.i_flags &= ~(EXT3_IMMUTABLE_FL | EXT3_APPEND_FL);
+	inode->u.ext3_i.i_faddr = 0;
+	inode->u.ext3_i.i_frag_no = 0;
+	inode->u.ext3_i.i_frag_size = 0;
+	inode->u.ext3_i.i_file_acl = 0;
+	inode->u.ext3_i.i_dir_acl = 0;
+	inode->u.ext3_i.i_dtime = 0;
+	INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan);
+	inode->u.ext3_i.i_block_group = i;
+	inode->i_op = NULL;
+	if (inode->u.ext3_i.i_flags & EXT3_SYNC_FL)
+		inode->i_flags |= MS_SYNCHRONOUS;
+	insert_inode_hash(inode);
+	inode->i_generation = inode_generation_count++;
+	inode->u.ext3_i.i_version = inode->i_generation;
+	ext3_mark_inode_dirty(handle, 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;
+	}
+	ext3_debug ("allocating inode %lu\n", inode->i_ino);
+
+	*err = 0;
+	return inode;
+}
+
+/* Verify that we are loading a valid orphan from disk */
+struct inode *ext3_orphan_get (struct super_block * sb, ino_t ino)
+{
+	ino_t max_ino = le32_to_cpu(EXT3_SB(sb)->s_es->s_inodes_count);
+	unsigned long block_group;
+	unsigned long bit;
+	int bitmap_nr;
+	struct buffer_head *bh;
+	struct inode *inode = NULL;
+
+	/* Error cases - e2fsck has already cleaned up for us */
+	if (ino > max_ino) {
+		ext3_warning(sb, __FUNCTION__,
+			     "bad orphan ino %ld!  e2fsck was run?\n", ino);
+		return NULL;
+	}
+
+	block_group = (ino - 1) / EXT3_INODES_PER_GROUP(sb);
+	bit = (ino - 1) % EXT3_INODES_PER_GROUP(sb);
+	if ((bitmap_nr = load_inode_bitmap(sb, block_group)) < 0 ||
+	    !(bh = EXT3_SB(sb)->s_inode_bitmap[bitmap_nr])) {
+		ext3_warning(sb, __FUNCTION__,
+			     "inode bitmap error for orphan %ld\n", ino);
+		return NULL;
+	}
+
+	/* Having the inode bit set should be a 100% indicator that this
+	 * is a valid orphan (no e2fsck run on fs).  Orphans also include
+	 * inodes that were being truncated, so we can't check i_nlink==0.
+	 */
+	if (!ext3_test_bit(bit, bh->b_data) || !(inode = iget(sb, ino)) ||
+	    is_bad_inode(inode) || NEXT_ORPHAN(inode) > max_ino) {
+		ext3_warning(sb, __FUNCTION__,
+			     "bad orphan inode %ld!  e2fsck was run?\n", ino);
+
+		/* Avoid freeing blocks if we got a bad deleted inode */
+		if (inode && inode->i_nlink == 0)
+			inode->i_blocks = 0;
+		iput(inode);
+		return NULL;
+	}
+
+	return inode;
+}
+
+unsigned long ext3_count_free_inodes (struct super_block * sb)
+{
+#ifdef EXT3FS_DEBUG
+	struct ext3_super_block * es;
+	unsigned long desc_count, bitmap_count, x;
+	int bitmap_nr;
+	struct ext3_group_desc * gdp;
+	int i;
+
+	lock_super (sb);
+	es = sb->u.ext3_sb.s_es;
+	desc_count = 0;
+	bitmap_count = 0;
+	gdp = NULL;
+	for (i = 0; i < sb->u.ext3_sb.s_groups_count; i++) {
+		gdp = ext3_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 = ext3_count_free (sb->u.ext3_sb.s_inode_bitmap[bitmap_nr],
+				     EXT3_INODES_PER_GROUP(sb) / 8);
+		printk ("group %d: stored = %d, counted = %lu\n",
+			i, le16_to_cpu(gdp->bg_free_inodes_count), x);
+		bitmap_count += x;
+	}
+	printk("ext3_count_free_inodes: stored = %lu, computed = %lu, %lu\n",
+		le32_to_cpu(es->s_free_inodes_count), desc_count, bitmap_count);
+	unlock_super (sb);
+	return desc_count;
+#else
+	return le32_to_cpu(sb->u.ext3_sb.s_es->s_free_inodes_count);
+#endif
+}
+
+void ext3_check_inodes_bitmap (struct super_block * sb)
+{
+	struct ext3_super_block * es;
+	unsigned long desc_count, bitmap_count, x;
+	int bitmap_nr;
+	struct ext3_group_desc * gdp;
+	int i;
+
+	lock_super (sb);
+	es = sb->u.ext3_sb.s_es;
+	desc_count = 0;
+	bitmap_count = 0;
+	gdp = NULL;
+	for (i = 0; i < sb->u.ext3_sb.s_groups_count; i++) {
+		gdp = ext3_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 = ext3_count_free (sb->u.ext3_sb.s_inode_bitmap[bitmap_nr],
+				     EXT3_INODES_PER_GROUP(sb) / 8);
+		if (le16_to_cpu(gdp->bg_free_inodes_count) != x)
+			ext3_error (sb, "ext3_check_inodes_bitmap",
+				    "Wrong free inodes count in group %d, "
+				    "stored = %d, counted = %lu", i,
+				    le16_to_cpu(gdp->bg_free_inodes_count), x);
+		bitmap_count += x;
+	}
+	if (le32_to_cpu(es->s_free_inodes_count) != bitmap_count)
+		ext3_error (sb, "ext3_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);
+	unlock_super (sb);
+}
--- linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/inode.c.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/inode.c	Wed Oct 25 19:10:56 2000
@@ -0,0 +1,935 @@
+/*
+ *  linux/fs/ext3/inode.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
+ *
+ *  Goal-directed block allocation by Stephen Tweedie
+ * 	(sct@dcs.ed.ac.uk), 1993, 1998
+ *  Big-endian to little-endian byte-swapping/bitmaps by
+ *        David S. Miller (davem@caip.rutgers.edu), 1995
+ *  64-bit file support on 64-bit platforms by Jakub Jelinek
+ * 	(jj@sunsite.ms.mff.cuni.cz)
+ */
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ext3_fs.h>
+#include <linux/ext3_jfs.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/mm.h>
+
+/*
+ * Called at each iput()
+ */
+void ext3_put_inode (struct inode * inode)
+{
+	ext3_discard_prealloc (inode);
+}
+
+/*
+ * ext3_orphan_del() removes an unlinked or truncated inode from the list
+ * of such inodes stored on disk, because it is finally being cleaned up.
+ */
+void ext3_orphan_del(handle_t *handle, struct inode *inode)
+{
+	struct list_head *prev = inode->u.ext3_i.i_orphan.prev;
+	struct ext3_sb_info *sbi = EXT3_SB(inode->i_sb);
+	ino_t ino_next = NEXT_ORPHAN(inode);
+
+	if (list_empty(&inode->u.ext3_i.i_orphan))
+		return;
+
+	jfs_debug(4, "remove inode %ld from orphan list\n", inode->i_ino);
+	lock_super(inode->i_sb);
+	list_del(&inode->u.ext3_i.i_orphan);
+	INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan);
+
+	if (prev == &sbi->s_orphan) {
+		jfs_debug(4, "superblock will point to %ld\n", ino_next);
+		journal_get_write_access(handle, sbi->s_sbh);
+		sbi->s_es->s_last_orphan = cpu_to_le32(ino_next);
+		journal_dirty_metadata(handle, sbi->s_sbh);
+	} else {
+		struct inode *i_prev =
+			list_entry(prev, struct inode, u.ext3_i.i_orphan);
+
+		jfs_debug(4, "orphan inode %ld will point to %ld\n",
+			  i_prev->i_ino, ino_next);
+		NEXT_ORPHAN(i_prev) = ino_next;
+		ext3_mark_inode_dirty(handle, i_prev);
+	}
+	unlock_super(inode->i_sb);
+}
+
+/*
+ * Called at the last iput() if i_nlink is zero.
+ */
+void ext3_delete_inode (struct inode * inode)
+{
+	handle_t *handle;
+
+	if (inode->i_ino == EXT3_ACL_IDX_INO ||
+	    inode->i_ino == EXT3_ACL_DATA_INO)
+		return;
+
+	/* When we delete an inode, we increment its i_version. If it
+	   is ever read in from disk again, it will have a different
+	   i_version. */
+	inode->u.ext3_i.i_version++;
+
+	handle = ext3_journal_start(inode, EXT3_DELETE_TRANS_BLOCKS);
+	if (IS_ERR(handle))
+		return;
+	
+	if (IS_SYNC(inode))
+		handle->h_sync = 1;
+	inode->i_size = 0;
+	if (inode->i_blocks)
+		ext3_truncate (inode);
+	ext3_orphan_del(handle, inode);
+	inode->u.ext3_i.i_dtime	= CURRENT_TIME;
+	ext3_mark_inode_dirty(handle, inode);
+	ext3_free_inode(handle, inode);
+	ext3_journal_stop(handle, inode);
+}
+
+#define inode_bmap(inode, nr) ((inode)->u.ext3_i.i_data_u.i_data[(nr)])
+
+static inline int block_bmap (struct buffer_head * bh, int nr)
+{
+	int tmp;
+
+	if (!bh)
+		return 0;
+	tmp = le32_to_cpu(((u32 *) bh->b_data)[nr]);
+	brelse (bh);
+	return tmp;
+}
+
+/* 
+ * ext3_discard_prealloc and ext3_alloc_block are atomic wrt. the
+ * superblock in the same manner as are ext3_free_blocks and
+ * ext3_new_block.  We just wait on the super rather than locking it
+ * here, since ext3_new_block will do the necessary locking and we
+ * can't block until then.
+ */
+void ext3_discard_prealloc (struct inode * inode)
+{
+#ifdef EXT3_PREALLOCATE
+	unsigned short total;
+
+	if (inode->u.ext3_i.i_prealloc_count) {
+		total = inode->u.ext3_i.i_prealloc_count;
+		inode->u.ext3_i.i_prealloc_count = 0;
+		ext3_free_blocks (inode, inode->u.ext3_i.i_prealloc_block, total);
+	}
+#endif
+}
+
+static struct buffer_head * ext3_alloc_block (handle_t *handle, 
+					      struct inode * inode, 
+					      unsigned long goal, int * err)
+{
+#ifdef EXT3FS_DEBUG
+	static unsigned long alloc_hits = 0, alloc_attempts = 0;
+#endif
+	struct buffer_head * bh;
+
+	wait_on_super (inode->i_sb);
+
+#ifdef EXT3_PREALLOCATE
+	if (inode->u.ext3_i.i_prealloc_count &&
+	    (goal == inode->u.ext3_i.i_prealloc_block ||
+	     goal + 1 == inode->u.ext3_i.i_prealloc_block))
+	{		
+		result = inode->u.ext3_i.i_prealloc_block++;
+		inode->u.ext3_i.i_prealloc_count--;
+		ext3_debug ("preallocation hit (%lu/%lu).\n",
+			    ++alloc_hits, ++alloc_attempts);
+
+		/* It doesn't matter if we block in getblk() since
+		   we have already atomically allocated the block, and
+		   are only clearing it now. */
+		if (!(bh = getblk (inode->i_sb->s_dev, result,
+				   inode->i_sb->s_blocksize))) {
+			ext3_error (inode->i_sb, "ext3_alloc_block",
+				    "cannot get block %lu", result);
+			return 0;
+		}
+		if (!buffer_uptodate(bh))
+			wait_on_buffer(bh);
+		/* @@@ Once we start journaling data separately, this
+                   needs to become dependent on the type of inode we
+                   are allocating inside */
+		journal_get_write_access(handle, bh);
+		memset(bh->b_data, 0, inode->i_sb->s_blocksize);
+		mark_buffer_uptodate(bh, 1);
+		journal_dirty_metadata(handle, bh);
+		brelse (bh);
+	} else {
+		ext3_discard_prealloc (inode);
+		ext3_debug ("preallocation miss (%lu/%lu).\n",
+			    alloc_hits, ++alloc_attempts);
+		if (S_ISREG(inode->i_mode))
+			result = ext3_new_block (inode, goal, 
+				 &inode->u.ext3_i.i_prealloc_count,
+				 &inode->u.ext3_i.i_prealloc_block, err);
+		else
+			result = ext3_new_block (handle, inode, goal, 0, 0, err);
+	}
+#else
+	bh = ext3_new_block (handle, inode, goal, 0, 0, err);
+#endif
+
+	return bh;
+}
+
+
+int ext3_bmap (struct inode * inode, int block)
+{
+	int i;
+	int addr_per_block = EXT3_ADDR_PER_BLOCK(inode->i_sb);
+	int addr_per_block_bits = EXT3_ADDR_PER_BLOCK_BITS(inode->i_sb);
+
+	if (block < 0) {
+		ext3_warning (inode->i_sb, "ext3_bmap", "block < 0");
+		return 0;
+	}
+	if (block >= EXT3_NDIR_BLOCKS + addr_per_block +
+		(1 << (addr_per_block_bits * 2)) +
+		((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) {
+		ext3_warning (inode->i_sb, "ext3_bmap", "block > big");
+		return 0;
+	}
+	if (block < EXT3_NDIR_BLOCKS)
+		return inode_bmap (inode, block);
+	block -= EXT3_NDIR_BLOCKS;
+	if (block < addr_per_block) {
+		i = inode_bmap (inode, EXT3_IND_BLOCK);
+		if (!i)
+			return 0;
+		return block_bmap (bread (inode->i_dev, i,
+					  inode->i_sb->s_blocksize), block);
+	}
+	block -= addr_per_block;
+	if (block < (1 << (addr_per_block_bits * 2))) {
+		i = inode_bmap (inode, EXT3_DIND_BLOCK);
+		if (!i)
+			return 0;
+		i = block_bmap (bread (inode->i_dev, i,
+				       inode->i_sb->s_blocksize),
+				block >> addr_per_block_bits);
+		if (!i)
+			return 0;
+		return block_bmap (bread (inode->i_dev, i,
+					  inode->i_sb->s_blocksize),
+				   block & (addr_per_block - 1));
+	}
+	block -= (1 << (addr_per_block_bits * 2));
+	i = inode_bmap (inode, EXT3_TIND_BLOCK);
+	if (!i)
+		return 0;
+	i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
+			block >> (addr_per_block_bits * 2));
+	if (!i)
+		return 0;
+	i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
+			(block >> addr_per_block_bits) & (addr_per_block - 1));
+	if (!i)
+		return 0;
+	return block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
+			   block & (addr_per_block - 1));
+}
+
+static struct buffer_head * inode_getblk (handle_t *handle,
+					  struct inode * inode, int nr,
+					  int create, int new_block, int * err)
+{
+	u32 * p;
+	int tmp, goal = 0;
+	struct buffer_head * result;
+	int blocks = inode->i_sb->s_blocksize / 512;
+
+	p = inode->u.ext3_i.i_data_u.i_data + nr;
+repeat:
+	tmp = *p;
+	if (tmp) {
+		struct buffer_head * result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
+		if (tmp == *p)
+			return result;
+		brelse (result);
+		goto repeat;
+	}
+	*err = -EFBIG;
+	if (!create)
+		return NULL;
+
+	if (inode->u.ext3_i.i_next_alloc_block == new_block)
+		goal = inode->u.ext3_i.i_next_alloc_goal;
+
+	ext3_debug ("hint = %d,", goal);
+
+	if (!goal) {
+		for (tmp = nr - 1; tmp >= 0; tmp--) {
+			if (inode->u.ext3_i.i_data_u.i_data[tmp]) {
+				goal = inode->u.ext3_i.i_data_u.i_data[tmp];
+				break;
+			}
+		}
+		if (!goal)
+			goal = (inode->u.ext3_i.i_block_group * 
+				EXT3_BLOCKS_PER_GROUP(inode->i_sb)) +
+			       le32_to_cpu(inode->i_sb->u.ext3_sb.s_es->s_first_data_block);
+	}
+
+	ext3_debug ("goal = %d.\n", goal);
+
+	result = ext3_alloc_block (handle, inode, goal, err);
+	if (!result)
+		return NULL;
+	tmp = result->b_blocknr;
+	
+	if (*p) {
+		ext3_free_blocks (handle, inode, tmp, 1);
+		brelse (result);
+		goto repeat;
+	}
+	*p = tmp;
+	inode->u.ext3_i.i_next_alloc_block = new_block;
+	inode->u.ext3_i.i_next_alloc_goal = tmp;
+	inode->i_ctime = CURRENT_TIME;
+	inode->i_blocks += blocks;
+	if (IS_SYNC(inode) || inode->u.ext3_i.i_osync)
+		handle->h_sync = 1;
+	ext3_mark_inode_dirty(handle, inode);
+	return result;
+}
+
+static struct buffer_head * block_getblk (handle_t *handle, 
+					  struct inode * inode,
+					  struct buffer_head * bh, int nr,
+					  int create, int blocksize, 
+					  int new_block, int * err)
+{
+	int tmp, goal = 0;
+	u32 * p;
+	struct buffer_head * result;
+	int blocks = inode->i_sb->s_blocksize / 512;
+	
+	if (!bh)
+		return NULL;
+	if (!buffer_uptodate(bh)) {
+		ll_rw_block (READ, 1, &bh);
+		wait_on_buffer (bh);
+		if (!buffer_uptodate(bh)) {
+			brelse (bh);
+			return NULL;
+		}
+	}
+	p = (u32 *) bh->b_data + nr;
+repeat:
+	tmp = le32_to_cpu(*p);
+	if (tmp) {
+		result = getblk (bh->b_dev, tmp, blocksize);
+		if (tmp == le32_to_cpu(*p)) {
+			brelse (bh);
+			return result;
+		}
+		brelse (result);
+		goto repeat;
+	}
+	*err = -EFBIG;
+	if (!create) {
+		brelse (bh);
+		return NULL;
+	}
+
+	if (inode->u.ext3_i.i_next_alloc_block == new_block)
+		goal = inode->u.ext3_i.i_next_alloc_goal;
+	if (!goal) {
+		for (tmp = nr - 1; tmp >= 0; tmp--) {
+			if (le32_to_cpu(((u32 *) bh->b_data)[tmp])) {
+				goal = le32_to_cpu(((u32 *)bh->b_data)[tmp]);
+				break;
+			}
+		}
+		if (!goal)
+			goal = bh->b_blocknr;
+	}
+	result = ext3_alloc_block (handle, inode, goal, err);
+	if (!result)
+		return NULL;
+	tmp = result->b_blocknr;
+
+	journal_get_write_access(handle, bh);
+	if (le32_to_cpu(*p)) {
+		journal_dirty_metadata(handle, bh); /* release bh */
+
+		/* @@@ Major danger here: we are using up more and more
+		 * buffer credits against this transaction by undoing
+		 * the allocation when we get a collision here.  We need
+		 * to be able to extend the transaction if possible, and
+		 * return a retry failure code if we can't. */
+		ext3_free_blocks (handle, inode, tmp, 1);
+		brelse (result);
+		goto repeat;
+	}
+	*p = le32_to_cpu(tmp);
+	journal_dirty_metadata(handle, bh);
+	if (IS_SYNC(inode) || inode->u.ext3_i.i_osync)
+		handle->h_sync = 1;
+	inode->i_ctime = CURRENT_TIME;
+	inode->i_blocks += blocks;
+	inode->u.ext3_i.i_next_alloc_block = new_block;
+	inode->u.ext3_i.i_next_alloc_goal = tmp;
+	ext3_mark_inode_dirty(handle, inode);
+	brelse (bh);
+	return result;
+}
+
+struct buffer_head * ext3_getblk (handle_t *handle, 
+				  struct inode * inode, long block,
+				  int create, int * err)
+{
+	struct buffer_head * bh;
+	unsigned long b;
+	unsigned long addr_per_block = EXT3_ADDR_PER_BLOCK(inode->i_sb);
+	int addr_per_block_bits = EXT3_ADDR_PER_BLOCK_BITS(inode->i_sb);
+
+	*err = -EIO;
+	if (block < 0) {
+		ext3_warning (inode->i_sb, "ext3_getblk", "block < 0");
+		return NULL;
+	}
+	if (block > EXT3_NDIR_BLOCKS + addr_per_block +
+		(1 << (addr_per_block_bits * 2)) +
+		((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) {
+		ext3_warning (inode->i_sb, "ext3_getblk", "block > big");
+		return NULL;
+	}
+	/*
+	 * If this is a sequential block allocation, set the next_alloc_block
+	 * to this block now so that all the indblock and data block
+	 * allocations use the same goal zone
+	 */
+
+	ext3_debug ("block %lu, next %lu, goal %lu.\n", block, 
+		    inode->u.ext3_i.i_next_alloc_block,
+		    inode->u.ext3_i.i_next_alloc_goal);
+
+	if (block == inode->u.ext3_i.i_next_alloc_block + 1) {
+		inode->u.ext3_i.i_next_alloc_block++;
+		inode->u.ext3_i.i_next_alloc_goal++;
+	}
+
+	*err = -ENOSPC;
+	b = block;
+	if (block < EXT3_NDIR_BLOCKS)
+		return inode_getblk (handle, inode, block, create, b, err);
+	block -= EXT3_NDIR_BLOCKS;
+	if (block < addr_per_block) {
+		bh = inode_getblk (handle, inode, EXT3_IND_BLOCK, create, b, err);
+		return block_getblk (handle, inode, bh, block, create,
+				     inode->i_sb->s_blocksize, b, err);
+	}
+	block -= addr_per_block;
+	if (block < (1 << (addr_per_block_bits * 2))) {
+		bh = inode_getblk (handle, inode, EXT3_DIND_BLOCK, create, b, err);
+		bh = block_getblk (handle, inode, bh, block >> addr_per_block_bits,
+				   create, inode->i_sb->s_blocksize, b, err);
+		return block_getblk (handle, inode, bh, block & (addr_per_block - 1),
+				     create, inode->i_sb->s_blocksize, b, err);
+	}
+	block -= (1 << (addr_per_block_bits * 2));
+	bh = inode_getblk (handle, inode, EXT3_TIND_BLOCK, create, b, err);
+	bh = block_getblk (handle, inode, bh, block >> (addr_per_block_bits * 2),
+			   create, inode->i_sb->s_blocksize, b, err);
+	bh = block_getblk (handle, inode, bh, (block >> addr_per_block_bits) & (addr_per_block - 1),
+			   create, inode->i_sb->s_blocksize, b, err);
+	return block_getblk (handle, inode, bh, block & (addr_per_block - 1), create,
+			     inode->i_sb->s_blocksize, b, err);
+}
+
+/* For ext3_bread, we need a transaction handle iff create is true. */
+
+struct buffer_head * ext3_bread (handle_t *handle, 
+				 struct inode * inode, int block, 
+				 int create, int *err)
+{
+	struct buffer_head * bh;
+	int prev_blocks;
+	
+	prev_blocks = inode->i_blocks;
+	
+	bh = ext3_getblk (handle, inode, block, create, err);
+	if (!bh)
+		return bh;
+	
+	/*
+	 * If the inode has grown, and this is a directory, then perform
+	 * preallocation of a few more blocks to try to keep directory
+	 * fragmentation down.
+	 */
+	if (create && 
+	    S_ISDIR(inode->i_mode) && 
+	    inode->i_blocks > prev_blocks &&
+	    EXT3_HAS_COMPAT_FEATURE(inode->i_sb,
+				    EXT3_FEATURE_COMPAT_DIR_PREALLOC)) {
+		int i;
+		struct buffer_head *tmp_bh;
+		
+		for (i = 1;
+		     i < EXT3_SB(inode->i_sb)->s_es->s_prealloc_dir_blocks;
+		     i++) {
+			/* 
+			 * ext3_getblk will zero out the contents of the
+			 * directory for us
+			 */
+			tmp_bh = ext3_getblk(handle, inode, block+i, create, err);
+			if (!tmp_bh) {
+				brelse (bh);
+				return 0;
+			}
+			brelse (tmp_bh);
+		}
+	}
+	
+	if (buffer_uptodate(bh))
+		return bh;
+	ll_rw_block (READ, 1, &bh);
+	wait_on_buffer (bh);
+	if (buffer_uptodate(bh))
+		return bh;
+	brelse (bh);
+	*err = -EIO;
+	return NULL;
+}
+
+/* 
+ * ext3_get_inode_loc returns with an extra refcount against the
+ * inode's underlying buffer_head on success. 
+ */
+
+int ext3_get_inode_loc (struct inode *inode, struct ext3_iloc *iloc)
+{
+	struct buffer_head *bh = 0;
+	unsigned long block;
+	unsigned long block_group;
+	unsigned long group_desc;
+	unsigned long desc;
+	unsigned long offset;
+	struct ext3_group_desc * gdp;
+		
+	if ((inode->i_ino != EXT3_ROOT_INO && inode->i_ino != EXT3_ACL_IDX_INO &&
+	     inode->i_ino != EXT3_ACL_DATA_INO &&
+	     inode->i_ino < EXT3_FIRST_INO(inode->i_sb)) ||
+	    inode->i_ino > le32_to_cpu(inode->i_sb->u.ext3_sb.s_es->s_inodes_count)) {
+		ext3_error (inode->i_sb, "ext3_get_inode_loc",
+			    "bad inode number: %lu", inode->i_ino);
+		goto bad_inode;
+	}
+	block_group = (inode->i_ino - 1) / EXT3_INODES_PER_GROUP(inode->i_sb);
+	if (block_group >= inode->i_sb->u.ext3_sb.s_groups_count) {
+		ext3_error (inode->i_sb, "ext3_get_inode_loc",
+			    "group >= groups count");
+		goto bad_inode;
+	}
+	group_desc = block_group >> EXT3_DESC_PER_BLOCK_BITS(inode->i_sb);
+	desc = block_group & (EXT3_DESC_PER_BLOCK(inode->i_sb) - 1);
+	bh = inode->i_sb->u.ext3_sb.s_group_desc[group_desc];
+	if (!bh) {
+		ext3_error (inode->i_sb, "ext3_get_inode_loc",
+			    "Descriptor not loaded");
+		goto bad_inode;
+	}
+
+	gdp = (struct ext3_group_desc *) bh->b_data;
+	/*
+	 * Figure out the offset within the block group inode table
+	 */
+	offset = ((inode->i_ino - 1) % EXT3_INODES_PER_GROUP(inode->i_sb)) *
+		EXT3_INODE_SIZE(inode->i_sb);
+	block = le32_to_cpu(gdp[desc].bg_inode_table) +
+		(offset >> EXT3_BLOCK_SIZE_BITS(inode->i_sb));
+	if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize))) {
+		ext3_error (inode->i_sb, "ext3_get_inode_loc",
+			    "unable to read inode block - "
+			    "inode=%lu, block=%lu", inode->i_ino, block);
+		goto bad_inode;
+	}
+	offset &= (EXT3_BLOCK_SIZE(inode->i_sb) - 1);
+
+	iloc->bh = bh;
+	iloc->raw_inode = (struct ext3_inode *) (bh->b_data + offset);
+	iloc->block_group = block_group;
+	
+	return 0;
+	
+ bad_inode:
+	return -EIO;
+}
+
+void ext3_read_inode (struct inode * inode)
+{
+	struct ext3_iloc iloc;
+	int block;
+	
+	if(ext3_get_inode_loc(inode, &iloc))
+		goto bad_inode;
+
+	inode->i_mode = le16_to_cpu(iloc.raw_inode->i_mode);
+	inode->i_uid = le16_to_cpu(iloc.raw_inode->i_uid);
+	inode->i_gid = le16_to_cpu(iloc.raw_inode->i_gid);
+	inode->i_nlink = le16_to_cpu(iloc.raw_inode->i_links_count);
+	inode->i_size = le32_to_cpu(iloc.raw_inode->i_size);
+	inode->i_atime = le32_to_cpu(iloc.raw_inode->i_atime);
+	inode->i_ctime = le32_to_cpu(iloc.raw_inode->i_ctime);
+	inode->i_mtime = le32_to_cpu(iloc.raw_inode->i_mtime);
+	inode->u.ext3_i.i_dtime = le32_to_cpu(iloc.raw_inode->i_dtime);
+	inode->i_blksize = PAGE_SIZE;	/* This is the optimal IO size (for stat), not the fs block size */
+	inode->i_blocks = le32_to_cpu(iloc.raw_inode->i_blocks);
+	inode->i_version = ++global_event;
+	inode->u.ext3_i.i_new_inode = 0;
+	inode->u.ext3_i.i_flags = le32_to_cpu(iloc.raw_inode->i_flags);
+	inode->u.ext3_i.i_faddr = le32_to_cpu(iloc.raw_inode->i_faddr);
+	inode->u.ext3_i.i_frag_no = iloc.raw_inode->i_frag;
+	inode->u.ext3_i.i_frag_size = iloc.raw_inode->i_fsize;
+	inode->u.ext3_i.i_osync = 0;
+	inode->u.ext3_i.i_file_acl = le32_to_cpu(iloc.raw_inode->i_file_acl);
+	if (S_ISDIR(inode->i_mode))
+		inode->u.ext3_i.i_dir_acl = le32_to_cpu(iloc.raw_inode->i_dir_acl);
+	else {
+		inode->u.ext3_i.i_dir_acl = 0;
+		inode->u.ext3_i.i_high_size =
+			le32_to_cpu(iloc.raw_inode->i_size_high);
+#if BITS_PER_LONG < 64
+		if (iloc.raw_inode->i_size_high)
+			inode->i_size = (__u32)-1;
+#else
+		inode->i_size |= ((__u64)le32_to_cpu(iloc.raw_inode->i_size_high))
+			<< 32;
+#endif
+	}
+	inode->u.ext3_i.i_disksize = inode->i_size;
+	inode->u.ext3_i.i_version = le32_to_cpu(iloc.raw_inode->i_version);
+	inode->i_generation = inode->u.ext3_i.i_version;
+	inode->u.ext3_i.i_block_group = iloc.block_group;
+	inode->u.ext3_i.i_next_alloc_block = 0;
+	inode->u.ext3_i.i_next_alloc_goal = 0;
+	INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan);
+	if (inode->u.ext3_i.i_prealloc_count)
+		ext3_error (inode->i_sb, "ext3_read_inode",
+			    "New inode has non-zero prealloc count!");
+	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+		inode->i_rdev = to_kdev_t(le32_to_cpu(iloc.raw_inode->i_block[0]));
+	else if (S_ISLNK(inode->i_mode) && !inode->i_blocks)
+		for (block = 0; block < EXT3_N_BLOCKS; block++)
+			inode->u.ext3_i.i_data_u.i_data[block] = iloc.raw_inode->i_block[block];
+	else for (block = 0; block < EXT3_N_BLOCKS; block++)
+		inode->u.ext3_i.i_data_u.i_data[block] = le32_to_cpu(iloc.raw_inode->i_block[block]);
+	brelse (iloc.bh);
+	inode->i_op = NULL;
+	if (inode->i_ino == EXT3_ACL_IDX_INO ||
+	    inode->i_ino == EXT3_ACL_DATA_INO)
+		/* Nothing to do */ ;
+	else if (S_ISREG(inode->i_mode))
+		inode->i_op = &ext3_file_inode_operations;
+	else if (S_ISDIR(inode->i_mode))
+		inode->i_op = &ext3_dir_inode_operations;
+	else if (S_ISLNK(inode->i_mode))
+		inode->i_op = &ext3_symlink_inode_operations;
+	else if (S_ISCHR(inode->i_mode))
+		inode->i_op = &chrdev_inode_operations;
+	else if (S_ISBLK(inode->i_mode))
+		inode->i_op = &blkdev_inode_operations;
+	else if (S_ISFIFO(inode->i_mode))
+		init_fifo(inode);
+	inode->i_attr_flags = 0;
+	if (inode->u.ext3_i.i_flags & EXT3_SYNC_FL) {
+		inode->i_attr_flags |= ATTR_FLAG_SYNCRONOUS;
+		inode->i_flags |= MS_SYNCHRONOUS;
+	}
+	if (inode->u.ext3_i.i_flags & EXT3_APPEND_FL) {
+		inode->i_attr_flags |= ATTR_FLAG_APPEND;
+		inode->i_flags |= S_APPEND;
+	}
+	if (inode->u.ext3_i.i_flags & EXT3_IMMUTABLE_FL) {
+		inode->i_attr_flags |= ATTR_FLAG_IMMUTABLE;
+		inode->i_flags |= S_IMMUTABLE;
+	}
+	if (inode->u.ext3_i.i_flags & EXT3_NOATIME_FL) {
+		inode->i_attr_flags |= ATTR_FLAG_NOATIME;
+		inode->i_flags |= MS_NOATIME;
+	}
+	return;
+	
+bad_inode:
+	make_bad_inode(inode);
+	return;
+}
+
+/*
+ * Post the struct inode info into an on-disk inode location in the
+ * buffer-cache.  This gobbles the caller's reference to the
+ * buffer_head in the inode location struct.  
+ */
+
+int ext3_do_update_inode(handle_t *handle, 
+			 struct inode *inode, 
+			 struct ext3_iloc *iloc,
+			 int do_sync)
+{
+	struct ext3_inode *raw_inode = iloc->raw_inode;
+	struct buffer_head *bh = iloc->bh;
+	int block;
+	int err = 0;
+
+	if (handle)
+		journal_get_write_access(handle, bh);
+	raw_inode->i_mode = cpu_to_le16(inode->i_mode);
+	raw_inode->i_uid = cpu_to_le16(inode->i_uid);
+	raw_inode->i_gid = cpu_to_le16(inode->i_gid);
+	raw_inode->i_links_count = cpu_to_le16(inode->i_nlink);
+	raw_inode->i_size = cpu_to_le32(inode->u.ext3_i.i_disksize);
+	raw_inode->i_atime = cpu_to_le32(inode->i_atime);
+	raw_inode->i_ctime = cpu_to_le32(inode->i_ctime);
+	raw_inode->i_mtime = cpu_to_le32(inode->i_mtime);
+	raw_inode->i_blocks = cpu_to_le32(inode->i_blocks);
+	raw_inode->i_dtime = cpu_to_le32(inode->u.ext3_i.i_dtime);
+	raw_inode->i_flags = cpu_to_le32(inode->u.ext3_i.i_flags);
+	raw_inode->i_faddr = cpu_to_le32(inode->u.ext3_i.i_faddr);
+	raw_inode->i_frag = inode->u.ext3_i.i_frag_no;
+	raw_inode->i_fsize = inode->u.ext3_i.i_frag_size;
+	raw_inode->i_file_acl = cpu_to_le32(inode->u.ext3_i.i_file_acl);
+	if (S_ISDIR(inode->i_mode))
+		raw_inode->i_dir_acl = cpu_to_le32(inode->u.ext3_i.i_dir_acl);
+	else { 
+#if BITS_PER_LONG < 64
+		raw_inode->i_size_high =
+			cpu_to_le32(inode->u.ext3_i.i_high_size);
+#else
+		raw_inode->i_size_high = cpu_to_le32(inode->u.ext3_i.i_disksize >> 32);
+#endif
+	}
+	raw_inode->i_version = cpu_to_le32(inode->u.ext3_i.i_version);
+	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+		raw_inode->i_block[0] = cpu_to_le32(kdev_t_to_nr(inode->i_rdev));
+	else if (S_ISLNK(inode->i_mode) && !inode->i_blocks)
+		for (block = 0; block < EXT3_N_BLOCKS; block++)
+			raw_inode->i_block[block] = inode->u.ext3_i.i_data_u.i_data[block];
+	else for (block = 0; block < EXT3_N_BLOCKS; block++)
+		raw_inode->i_block[block] = cpu_to_le32(inode->u.ext3_i.i_data_u.i_data[block]);
+
+	if (handle) {
+		journal_dirty_metadata(handle, bh);
+		if (do_sync) 
+			handle->h_sync = 1;
+	} else {
+		mark_buffer_dirty(bh, 1);
+		if (do_sync) {
+			ll_rw_block (WRITE, 1, &bh);
+			wait_on_buffer (bh);
+			if (buffer_req(bh) && !buffer_uptodate(bh)) {
+				printk ("IO error syncing ext3 inode ["
+					"%s:%08lx]\n",
+					bdevname(inode->i_dev), inode->i_ino);
+				err = -EIO;
+			}
+		}
+	}
+		
+	brelse (bh);
+	return err;
+}
+
+/* ext3_write_inode is only used for flushing in-core un-journaled
+ * dirty inodes from the inode cache to the buffer cache when
+ * reclaiming memory or during a sync().
+ *
+ * We don't actually have to flush the resulting buffer cache entry to
+ * disk: for sync(), that will happen during the write_super() or the
+ * sync_buffers() (depending on whether or not we perform the write as
+ * a journaled operation or not).
+ *
+ * CAUTION: This routine has the potential to interact badly with
+ * journaling.  An existing transaction may call get_empty_inode() in
+ * the VFS, and that may in turn result in a call to ext3_write_inode if
+ * there are too many dirty inodes already in the inode cache.  We
+ * cannot use the existing transaction handle here: it may not be for
+ * the correct filesystem.
+ *
+ * Neither can we start a new transaction: that may deadlock as we
+ * already have a lock on the inode by this point, and starting a new
+ * transaction may require us to wait for the existing one to complete
+ * (which our inode lock may obstruct).
+ *
+ * However, a dirty ext3 inode can only result from an update to a
+ * timestamp or something equally benign: any inode update which
+ * requires journaling will be journaled immediately rather than being
+ * flushed asynchronously from the inode cache.  We are saved: it is
+ * safe to simply update the buffer cache directly: if the buffer is
+ * already being journaled then the existing journaling write ordering
+ * will do the right thing, but if it is not, then it can safely be
+ * flushed to disk immediately.
+ */
+
+void ext3_write_inode(struct inode * inode)
+{
+	struct ext3_iloc iloc;
+	struct buffer_head *bh;
+	journal_t *journal = NULL;
+	int err;
+	
+	err = ext3_get_inode_loc(inode, &iloc);
+	if (err)
+		return;
+
+	bh = iloc.bh;
+	if (bh->b_transaction != NULL) {
+		journal = bh->b_transaction->t_journal;
+		/* We aren't going to mess with the journal, but let's
+                   just be safe and make sure that nobody else is doing
+                   something critical with the buffer right now either */
+		lock_journal(journal);
+	}
+	
+	ext3_do_update_inode(NULL, inode, &iloc, 0);
+
+	if (journal)
+		unlock_journal(journal);
+	return;
+}
+
+int ext3_notify_change(struct dentry *dentry, struct iattr *iattr)
+{
+	struct inode *inode = dentry->d_inode;
+	int		retval;
+	unsigned int	flags;
+	handle_t	*handle = 0;
+	struct ext3_iloc iloc;
+	
+	retval = -EPERM;
+	if (iattr->ia_valid & ATTR_ATTR_FLAG) {
+		if ((iattr->ia_attr_flags &
+		    (ATTR_FLAG_APPEND | ATTR_FLAG_IMMUTABLE)) ^
+		    (inode->u.ext3_i.i_flags &
+		     (EXT3_APPEND_FL | EXT3_IMMUTABLE_FL))) {
+			if (!capable(CAP_LINUX_IMMUTABLE))
+				goto out;
+		}
+	}
+	if (iattr->ia_valid & ATTR_UID) {
+		if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+			goto out;
+	}
+	if (iattr->ia_valid & ATTR_SIZE) {
+		off_t size = iattr->ia_size;
+		unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
+
+		if (size < 0)
+			return -EINVAL;
+#if BITS_PER_LONG == 64	
+		if (size > ext3_max_sizes[EXT3_BLOCK_SIZE_BITS(inode->i_sb)])
+			return -EFBIG;
+#endif
+ 		if (limit < RLIM_INFINITY && size > limit) {
+			send_sig(SIGXFSZ, current, 0);
+			return -EFBIG;
+		}
+
+#if BITS_PER_LONG == 64	
+		if (size >> 33) {
+			struct super_block *sb = inode->i_sb;
+			struct ext3_super_block *es = sb->u.ext3_sb.s_es;
+
+			if (!(es->s_feature_ro_compat &
+			      cpu_to_le32(EXT3_FEATURE_RO_COMPAT_LARGE_FILE))){
+				struct buffer_head *bh = sb->u.ext3_sb.s_sbh;
+				
+				handle = ext3_journal_start(inode, 1);
+				if (IS_ERR(handle))
+					return PTR_ERR(handle);
+
+				/* If this is the first large file
+				 * created, add a flag to the superblock */
+				es->s_feature_ro_compat |=
+				cpu_to_le32(EXT3_FEATURE_RO_COMPAT_LARGE_FILE);
+				journal_dirty_metadata(handle, bh); /*@@@err*/
+				ext3_journal_stop(handle, inode);
+			}
+		}
+#endif
+	}
+	
+
+	retval = inode_change_ok(inode, iattr);
+	if (retval != 0)
+		goto out;
+
+	/* Notify-change transaction.  The maximum number of buffers
+	 * required is one. */
+
+	handle = ext3_journal_start(inode, 1);
+	if (IS_ERR(handle))
+		return PTR_ERR(handle);
+	retval = ext3_reserve_inode_write(handle, inode, &iloc);
+	if (retval) 			
+		goto out_stop;
+	
+	inode_setattr(inode, iattr);
+
+	if (iattr->ia_valid & ATTR_ATTR_FLAG) {
+		flags = iattr->ia_attr_flags;
+		if (flags & ATTR_FLAG_SYNCRONOUS) {
+			inode->i_flags |= MS_SYNCHRONOUS;
+			inode->u.ext3_i.i_flags = EXT3_SYNC_FL;
+		} else {
+			inode->i_flags &= ~MS_SYNCHRONOUS;
+			inode->u.ext3_i.i_flags &= ~EXT3_SYNC_FL;
+		}
+		if (flags & ATTR_FLAG_NOATIME) {
+			inode->i_flags |= MS_NOATIME;
+			inode->u.ext3_i.i_flags = EXT3_NOATIME_FL;
+		} else {
+			inode->i_flags &= ~MS_NOATIME;
+			inode->u.ext3_i.i_flags &= ~EXT3_NOATIME_FL;
+		}
+		if (flags & ATTR_FLAG_APPEND) {
+			inode->i_flags |= S_APPEND;
+			inode->u.ext3_i.i_flags = EXT3_APPEND_FL;
+		} else {
+			inode->i_flags &= ~S_APPEND;
+			inode->u.ext3_i.i_flags &= ~EXT3_APPEND_FL;
+		}
+		if (flags & ATTR_FLAG_IMMUTABLE) {
+			inode->i_flags |= S_IMMUTABLE;
+			inode->u.ext3_i.i_flags = EXT3_IMMUTABLE_FL;
+		} else {
+			inode->i_flags &= ~S_IMMUTABLE;
+			inode->u.ext3_i.i_flags &= ~EXT3_IMMUTABLE_FL;
+		}
+	}
+	
+	retval = ext3_mark_iloc_dirty(handle, inode, &iloc);
+
+out_stop:
+	ext3_journal_stop(handle, inode);
+out:
+	return retval;
+}
+
--- linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/ioctl.c.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/ioctl.c	Wed Oct 25 19:10:56 2000
@@ -0,0 +1,86 @@
+/*
+ * linux/fs/ext3/ioctl.c
+ *
+ * Copyright (C) 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ */
+
+#include <asm/uaccess.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ext3_fs.h>
+#include <linux/ioctl.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+
+int ext3_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
+		unsigned long arg)
+{
+	unsigned int flags;
+
+	ext3_debug ("cmd = %u, arg = %lu\n", cmd, arg);
+
+	switch (cmd) {
+	case EXT3_IOC_GETFLAGS:
+		flags = inode->u.ext3_i.i_flags & EXT3_FL_USER_VISIBLE;
+		return put_user(inode->u.ext3_i.i_flags, (int *) arg);
+	case EXT3_IOC_SETFLAGS:
+		if (get_user(flags, (int *) arg))
+			return -EFAULT;
+		flags = flags & EXT3_FL_USER_MODIFIABLE;
+		/*
+		 * The IMMUTABLE and APPEND_ONLY flags can only be changed by
+		 * a process with the relevent capability.
+		 */
+		if ((flags & (EXT3_APPEND_FL | EXT3_IMMUTABLE_FL)) ^
+		    (inode->u.ext3_i.i_flags &
+		     (EXT3_APPEND_FL | EXT3_IMMUTABLE_FL)))
+			/* This test looks nicer. Thanks to Pauline Middelink */
+			if (!capable(CAP_LINUX_IMMUTABLE))
+				return -EPERM;
+
+		if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+			return -EPERM;
+		if (IS_RDONLY(inode))
+			return -EROFS;
+		inode->u.ext3_i.i_flags = (inode->u.ext3_i.i_flags &
+					   ~EXT3_FL_USER_MODIFIABLE) | flags;
+		if (flags & EXT3_SYNC_FL)
+			inode->i_flags |= MS_SYNCHRONOUS;
+		else
+			inode->i_flags &= ~MS_SYNCHRONOUS;
+		if (flags & EXT3_APPEND_FL)
+			inode->i_flags |= S_APPEND;
+		else
+			inode->i_flags &= ~S_APPEND;
+		if (flags & EXT3_IMMUTABLE_FL)
+			inode->i_flags |= S_IMMUTABLE;
+		else
+			inode->i_flags &= ~S_IMMUTABLE;
+		if (flags & EXT3_NOATIME_FL)
+			inode->i_flags |= MS_NOATIME;
+		else
+			inode->i_flags &= ~MS_NOATIME;
+		inode->i_ctime = CURRENT_TIME;
+		mark_inode_dirty(inode);
+		return 0;
+	case EXT3_IOC_GETVERSION:
+		return put_user(inode->u.ext3_i.i_version, (int *) arg);
+	case EXT3_IOC_SETVERSION:
+		if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+			return -EPERM;
+		if (IS_RDONLY(inode))
+			return -EROFS;
+		if (get_user(inode->u.ext3_i.i_version, (int *) arg))
+			return -EFAULT;	
+		inode->i_ctime = CURRENT_TIME;
+		inode->i_generation = inode->u.ext3_i.i_version;
+		mark_inode_dirty(inode);
+		return 0;
+	default:
+		return -ENOTTY;
+	}
+}
--- linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/namei.c.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/namei.c	Wed Oct 25 19:10:56 2000
@@ -0,0 +1,1026 @@
+/*
+ *  linux/fs/ext3/namei.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/namei.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  Big-endian to little-endian byte-swapping/bitmaps by
+ *        David S. Miller (davem@caip.rutgers.edu), 1995
+ *  Directory entry file type support and forward compatibility hooks
+ *  	for B-tree directories by Theodore Ts'o (tytso@mit.edu), 1998
+ */
+
+#include <asm/uaccess.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ext3_fs.h>
+#include <linux/ext3_jfs.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/quotaops.h>
+
+
+/*
+ * define how far ahead to read directories while searching them.
+ */
+#define NAMEI_RA_CHUNKS  2
+#define NAMEI_RA_BLOCKS  4
+#define NAMEI_RA_SIZE        (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS)
+#define NAMEI_RA_INDEX(c,b)  (((c) * NAMEI_RA_BLOCKS) + (b))
+
+/*
+ * NOTE! unlike strncmp, ext3_match returns 1 for success, 0 for failure.
+ *
+ * `len <= EXT3_NAME_LEN' is guaranteed by caller.
+ * `de != NULL' is guaranteed by caller.
+ */
+static inline int ext3_match (int len, const char * const name,
+		       struct ext3_dir_entry_2 * de)
+{
+	if (len != de->name_len)
+		return 0;
+	if (!de->inode)
+		return 0;
+	return !memcmp(name, de->name, len);
+}
+
+/*
+ *	ext3_find_entry()
+ *
+ * finds an entry in the specified directory with the wanted name. It
+ * returns the cache buffer in which the entry was found, and the entry
+ * itself (as a parameter - res_dir). It does NOT read the inode of the
+ * entry - you'll have to do that yourself if you want to.
+ */
+static struct buffer_head * ext3_find_entry (struct inode * dir,
+					     const char * const name, int namelen,
+					     struct ext3_dir_entry_2 ** res_dir)
+{
+	struct super_block * sb;
+	struct buffer_head * bh_use[NAMEI_RA_SIZE];
+	struct buffer_head * bh_read[NAMEI_RA_SIZE];
+	unsigned long offset;
+	int block, toread, i, err;
+
+	*res_dir = NULL;
+	sb = dir->i_sb;
+
+	if (namelen > EXT3_NAME_LEN)
+		return NULL;
+
+	memset (bh_use, 0, sizeof (bh_use));
+	toread = 0;
+	for (block = 0; block < NAMEI_RA_SIZE; ++block) {
+		struct buffer_head * bh;
+
+		if ((block << EXT3_BLOCK_SIZE_BITS (sb)) >= dir->i_size)
+			break;
+		bh = ext3_getblk (NULL, dir, block, 0, &err);
+		bh_use[block] = bh;
+		if (bh && !buffer_uptodate(bh))
+			bh_read[toread++] = bh;
+	}
+
+	for (block = 0, offset = 0; offset < dir->i_size; block++) {
+		struct buffer_head * bh;
+		struct ext3_dir_entry_2 * de;
+		char * dlimit;
+
+		if ((block % NAMEI_RA_BLOCKS) == 0 && toread) {
+			ll_rw_block (READ, toread, bh_read);
+			toread = 0;
+		}
+		bh = bh_use[block % NAMEI_RA_SIZE];
+		if (!bh) {
+#if 0
+			ext3_error (sb, "ext3_find_entry",
+				    "directory #%lu contains a hole at offset %lu",
+				    dir->i_ino, offset);
+#endif
+			offset += sb->s_blocksize;
+			continue;
+		}
+		wait_on_buffer (bh);
+		if (!buffer_uptodate(bh)) {
+			/*
+			 * read error: all bets are off
+			 */
+			break;
+		}
+
+		de = (struct ext3_dir_entry_2 *) bh->b_data;
+		dlimit = bh->b_data + sb->s_blocksize;
+		while ((char *) de < dlimit) {
+			/* this code is executed quadratically often */
+			/* do minimal checking `by hand' */
+			int de_len;
+
+			if ((char *) de + namelen <= dlimit &&
+			    ext3_match (namelen, name, de)) {
+				/* found a match -
+				   just to be sure, do a full check */
+				if (!ext3_check_dir_entry("ext3_find_entry",
+							  dir, de, bh, offset))
+					goto failure;
+				for (i = 0; i < NAMEI_RA_SIZE; ++i) {
+					if (bh_use[i] != bh)
+						brelse (bh_use[i]);
+				}
+				*res_dir = de;
+				return bh;
+			}
+			/* prevent looping on a bad block */
+			de_len = le16_to_cpu(de->rec_len);
+			if (de_len <= 0)
+				goto failure;
+			offset += de_len;
+			de = (struct ext3_dir_entry_2 *)
+				((char *) de + de_len);
+		}
+
+		brelse (bh);
+		if (((block + NAMEI_RA_SIZE) << EXT3_BLOCK_SIZE_BITS (sb)) >=
+		    dir->i_size)
+			bh = NULL;
+		else
+			bh = ext3_getblk (NULL, dir, block + NAMEI_RA_SIZE, 0, &err);
+		bh_use[block % NAMEI_RA_SIZE] = bh;
+		if (bh && !buffer_uptodate(bh))
+			bh_read[toread++] = bh;
+	}
+
+failure:
+	for (i = 0; i < NAMEI_RA_SIZE; ++i)
+		brelse (bh_use[i]);
+	return NULL;
+}
+
+struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry)
+{
+	struct inode * inode;
+	struct ext3_dir_entry_2 * de;
+	struct buffer_head * bh;
+
+	if (dentry->d_name.len > EXT3_NAME_LEN)
+		return ERR_PTR(-ENAMETOOLONG);
+
+	bh = ext3_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de);
+	inode = NULL;
+	if (bh) {
+		unsigned long ino = le32_to_cpu(de->inode);
+		brelse (bh);
+		inode = iget(dir->i_sb, ino);
+
+		if (!inode)
+			return ERR_PTR(-EACCES);
+	}
+	d_add(dentry, inode);
+	return NULL;
+}
+
+/*
+ *	ext3_add_entry()
+ *
+ * adds a file entry to the specified directory, using the same
+ * semantics as ext3_find_entry(). It returns NULL if it failed.
+ *
+ * NOTE!! The inode part of 'de' is left at 0 - which means you
+ * may not sleep between calling this and putting something into
+ * the entry, as someone else might have used it while you slept.
+ */
+static struct buffer_head * ext3_add_entry (handle_t * handle,
+					    struct inode * dir,
+					    const char * name, int namelen,
+					    struct ext3_dir_entry_2 ** res_dir,
+					    int *err)
+{
+	unsigned long offset;
+	unsigned short rec_len;
+	struct buffer_head * bh;
+	struct ext3_dir_entry_2 * de, * de1;
+	struct super_block * sb;
+
+	*err = -EINVAL;
+	*res_dir = NULL;
+	if (!dir || !dir->i_nlink)
+		return NULL;
+	sb = dir->i_sb;
+
+	if (!namelen)
+		return NULL;
+	/*
+	 * Is this a busy deleted directory?  Can't create new files if so
+	 */
+	if (dir->i_size == 0)
+	{
+		*err = -ENOENT;
+		return NULL;
+	}
+	bh = ext3_bread (0, dir, 0, 0, err);
+	if (!bh)
+		return NULL;
+	rec_len = EXT3_DIR_REC_LEN(namelen);
+	offset = 0;
+	de = (struct ext3_dir_entry_2 *) bh->b_data;
+	*err = -ENOSPC;
+	while (1) {
+		if ((char *)de >= sb->s_blocksize + bh->b_data) {
+			brelse (bh);
+			bh = NULL;
+			bh = ext3_bread (handle, dir, offset >> EXT3_BLOCK_SIZE_BITS(sb), 1, err);
+			if (!bh)
+				return NULL;
+			if (dir->i_size <= offset) {
+				if (dir->i_size == 0) {
+					*err = -ENOENT;
+					return NULL;
+				}
+
+				ext3_debug ("creating next block\n");
+
+				journal_get_write_access(handle, bh);
+				de = (struct ext3_dir_entry_2 *) bh->b_data;
+				de->inode = 0;
+				de->rec_len = le16_to_cpu(sb->s_blocksize);
+				dir->i_size = offset + sb->s_blocksize;
+				dir->u.ext3_i.i_disksize = dir->i_size;
+				dir->u.ext3_i.i_flags &= ~EXT3_BTREE_FL;
+				ext3_mark_inode_dirty(handle, dir);
+				/* Just keep the buffer reserved for now. */
+				goto got_buffer;
+			} else {
+
+				ext3_debug ("skipping to next block\n");
+
+				de = (struct ext3_dir_entry_2 *) bh->b_data;
+			}
+		}
+		if (!ext3_check_dir_entry ("ext3_add_entry", dir, de, bh,
+					   offset)) {
+			*err = -ENOENT;
+			brelse (bh);
+			return NULL;
+		}
+		if (ext3_match (namelen, name, de)) {
+				*err = -EEXIST;
+				brelse (bh);
+				return NULL;
+		}
+		if ((le32_to_cpu(de->inode) == 0 && le16_to_cpu(de->rec_len) >= rec_len) ||
+		    (le16_to_cpu(de->rec_len) >= EXT3_DIR_REC_LEN(de->name_len) + rec_len)) {
+			journal_get_write_access(handle, bh);
+got_buffer:		/* By now the buffer is marked for journaling */
+			offset += le16_to_cpu(de->rec_len);
+			if (le32_to_cpu(de->inode)) {
+				de1 = (struct ext3_dir_entry_2 *) ((char *) de +
+					EXT3_DIR_REC_LEN(de->name_len));
+				de1->rec_len = cpu_to_le16(le16_to_cpu(de->rec_len) -
+					EXT3_DIR_REC_LEN(de->name_len));
+				de->rec_len = cpu_to_le16(EXT3_DIR_REC_LEN(de->name_len));
+				de = de1;
+			}
+			de->inode = 0;
+			de->name_len = namelen;
+			de->file_type = 0;
+			memcpy (de->name, name, namelen);
+			/*
+			 * XXX shouldn't update any times until successful
+			 * completion of syscall, but too many callers depend
+			 * on this.
+			 *
+			 * XXX similarly, too many callers depend on
+			 * ext3_new_inode() setting the times, but error
+			 * recovery deletes the inode, so the worst that can
+			 * happen is that the times are slightly out of date
+			 * and/or different from the directory change time.
+			 */
+			dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+			dir->u.ext3_i.i_flags &= ~EXT3_BTREE_FL;
+			dir->i_version = ++global_event;
+			ext3_mark_inode_dirty(handle, dir);
+			journal_dirty_metadata(handle, bh);
+			*res_dir = de;
+			*err = 0;
+			return bh;
+		}
+		offset += le16_to_cpu(de->rec_len);
+		de = (struct ext3_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len));
+	}
+	brelse (bh);
+	return NULL;
+}
+
+/*
+ * ext3_delete_entry deletes a directory entry by merging it with the
+ * previous entry
+ */
+static int ext3_delete_entry (handle_t *handle, 
+			      struct ext3_dir_entry_2 * dir,
+			      struct buffer_head * bh)
+{
+	struct ext3_dir_entry_2 * de, * pde;
+	int i;
+
+	i = 0;
+	pde = NULL;
+	de = (struct ext3_dir_entry_2 *) bh->b_data;
+	while (i < bh->b_size) {
+		if (!ext3_check_dir_entry ("ext3_delete_entry", NULL, 
+					   de, bh, i))
+			return -EIO;
+		if (de == dir)  {
+			journal_get_write_access(handle, bh);
+			if (pde)
+				pde->rec_len =
+					cpu_to_le16(le16_to_cpu(pde->rec_len) +
+						    le16_to_cpu(dir->rec_len));
+			else
+				dir->inode = 0;
+			journal_dirty_metadata(handle, bh);
+			return 0;
+		}
+		i += le16_to_cpu(de->rec_len);
+		pde = de;
+		de = (struct ext3_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len));
+	}
+	return -ENOENT;
+}
+
+/*
+ * By the time this is called, we already have created
+ * the directory cache entry for the new file, but it
+ * is so far negative - it has no inode.
+ *
+ * If the create succeeds, we fill in the inode information
+ * with d_instantiate(). 
+ */
+int ext3_create (struct inode * dir, struct dentry * dentry, int mode)
+{
+	struct inode * inode;
+	struct buffer_head * bh;
+	struct ext3_dir_entry_2 * de;
+	handle_t *handle;
+	int err = -EIO;
+
+	handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + 3);
+	if (IS_ERR(handle))
+		return PTR_ERR(handle);
+
+	/*
+	 * N.B. Several error exits in ext3_new_inode don't set err.
+	 */
+	inode = ext3_new_inode (handle, dir, mode, &err);
+	if (!inode)
+		goto out;
+
+	err = 0;
+	
+	inode->i_op = &ext3_file_inode_operations;
+	inode->i_mode = mode;
+	ext3_mark_inode_dirty(handle, inode); /* @@@ Error? */
+	bh = ext3_add_entry (handle, dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
+	if (!bh) {
+		inode->i_nlink--;
+		ext3_mark_inode_dirty(handle, inode);
+		iput (inode);
+		goto out;
+	}
+	de->inode = cpu_to_le32(inode->i_ino);
+	if (EXT3_HAS_INCOMPAT_FEATURE(dir->i_sb,
+				      EXT3_FEATURE_INCOMPAT_FILETYPE))
+		de->file_type = EXT3_FT_REG_FILE;
+	dir->i_version = ++global_event;
+	if (IS_SYNC(dir))
+		handle->h_sync = 1;
+	brelse (bh);
+	d_instantiate(dentry, inode);
+
+out:
+	ext3_journal_stop(handle, dir);
+	return err;
+}
+
+int ext3_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev)
+{
+	struct inode * inode;
+	struct buffer_head * bh;
+	struct ext3_dir_entry_2 * de;
+	int err = -EIO;
+	handle_t *handle = 0;
+
+	handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + 3);
+	if (IS_ERR(handle))
+		return PTR_ERR(handle);
+	
+	inode = ext3_new_inode (handle, dir, mode, &err);
+	if (!inode)
+		goto out_stop;
+
+	inode->i_uid = current->fsuid;
+	inode->i_mode = mode;
+	inode->i_op = NULL;
+	bh = ext3_add_entry (handle, dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
+	if (!bh)
+		goto out_no_entry;
+	de->inode = cpu_to_le32(inode->i_ino);
+	dir->i_version = ++global_event;
+	if (S_ISREG(inode->i_mode)) {
+		inode->i_op = &ext3_file_inode_operations;
+		if (EXT3_HAS_INCOMPAT_FEATURE(dir->i_sb,
+					      EXT3_FEATURE_INCOMPAT_FILETYPE))
+			de->file_type = EXT3_FT_REG_FILE;
+	} else if (S_ISSOCK(inode->i_mode)) {
+		if (EXT3_HAS_INCOMPAT_FEATURE(dir->i_sb,
+					      EXT3_FEATURE_INCOMPAT_FILETYPE))
+			de->file_type = EXT3_FT_SOCK;
+	} else if (S_ISCHR(inode->i_mode)) {
+		inode->i_op = &chrdev_inode_operations;
+		if (EXT3_HAS_INCOMPAT_FEATURE(dir->i_sb,
+					      EXT3_FEATURE_INCOMPAT_FILETYPE))
+			de->file_type = EXT3_FT_CHRDEV;
+	} else if (S_ISBLK(inode->i_mode)) {
+		inode->i_op = &blkdev_inode_operations;
+		if (EXT3_HAS_INCOMPAT_FEATURE(dir->i_sb,
+					      EXT3_FEATURE_INCOMPAT_FILETYPE))
+			de->file_type = EXT3_FT_BLKDEV;
+	} else if (S_ISFIFO(inode->i_mode))  {
+		init_fifo(inode);
+		if (EXT3_HAS_INCOMPAT_FEATURE(dir->i_sb,
+					      EXT3_FEATURE_INCOMPAT_FILETYPE))
+			de->file_type = EXT3_FT_FIFO;
+	}
+	if (S_ISBLK(mode) || S_ISCHR(mode))
+		inode->i_rdev = to_kdev_t(rdev);
+	ext3_mark_inode_dirty(handle, inode);
+	journal_dirty_metadata(handle, bh);
+	if (IS_SYNC(dir))
+		handle->h_sync = 1;
+	d_instantiate(dentry, inode);
+	brelse(bh);
+out_stop:
+	ext3_journal_stop(handle, dir);
+	return err;
+
+out_no_entry:
+	inode->i_nlink--;
+	ext3_mark_inode_dirty(handle, inode);
+	iput(inode);
+	goto out_stop;
+}
+
+int ext3_mkdir(struct inode * dir, struct dentry * dentry, int mode)
+{
+	struct inode * inode;
+	struct buffer_head * bh, * dir_block;
+	struct ext3_dir_entry_2 * de;
+	handle_t *handle = 0;
+	int err;
+
+	err = -EMLINK;
+	if (dir->i_nlink >= EXT3_LINK_MAX)
+		goto out;
+
+	handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + 4);
+	if (IS_ERR(handle))
+		return PTR_ERR(handle);
+
+	err = -EIO;
+	inode = ext3_new_inode (handle, dir, S_IFDIR, &err);
+	if (!inode)
+		goto out_stop;
+
+	inode->i_op = &ext3_dir_inode_operations;
+	inode->i_size = inode->u.ext3_i.i_disksize = inode->i_sb->s_blocksize;
+	inode->i_blocks = 0;	
+	dir_block = ext3_bread (handle, inode, 0, 1, &err);
+	if (!dir_block) {
+		inode->i_nlink--; /* is this nlink == 0? */
+		ext3_mark_inode_dirty(handle, inode);
+		iput (inode);
+		goto out_stop;
+	}
+	journal_get_write_access(handle, dir_block);
+	de = (struct ext3_dir_entry_2 *) dir_block->b_data;
+	de->inode = cpu_to_le32(inode->i_ino);
+	de->name_len = 1;
+	de->rec_len = cpu_to_le16(EXT3_DIR_REC_LEN(de->name_len));
+	strcpy (de->name, ".");
+	if (EXT3_HAS_INCOMPAT_FEATURE(dir->i_sb,
+				      EXT3_FEATURE_INCOMPAT_FILETYPE))
+		de->file_type = EXT3_FT_DIR;
+	de = (struct ext3_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len));
+	de->inode = cpu_to_le32(dir->i_ino);
+	de->rec_len = cpu_to_le16(inode->i_sb->s_blocksize - EXT3_DIR_REC_LEN(1));
+	de->name_len = 2;
+	strcpy (de->name, "..");
+	if (EXT3_HAS_INCOMPAT_FEATURE(dir->i_sb,
+				      EXT3_FEATURE_INCOMPAT_FILETYPE))
+		de->file_type = EXT3_FT_DIR;
+	inode->i_nlink = 2;
+	journal_dirty_metadata(handle, dir_block);
+	brelse (dir_block);
+	inode->i_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask);
+	if (dir->i_mode & S_ISGID)
+		inode->i_mode |= S_ISGID;
+	ext3_mark_inode_dirty(handle, inode);
+	bh = ext3_add_entry (handle, dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
+	if (!bh)
+		goto out_no_entry;
+	de->inode = cpu_to_le32(inode->i_ino);
+	if (EXT3_HAS_INCOMPAT_FEATURE(dir->i_sb,
+				      EXT3_FEATURE_INCOMPAT_FILETYPE))
+		de->file_type = EXT3_FT_DIR;
+	dir->i_version = ++global_event;
+	journal_dirty_metadata(handle, bh);
+	dir->i_nlink++;
+	dir->u.ext3_i.i_flags &= ~EXT3_BTREE_FL;
+	ext3_mark_inode_dirty(handle, dir);
+	if (IS_SYNC(dir)) 
+		handle->h_sync = 1;
+	d_instantiate(dentry, inode);
+	brelse (bh);
+	err = 0;
+
+out_stop:
+	ext3_journal_stop(handle, dir);
+out:
+	return err;
+
+out_no_entry:
+	inode->i_nlink = 0;
+	ext3_mark_inode_dirty(handle, inode);
+	/* The implicit delete in the iput() will be dealt with as a
+	 * recursive transaction. */
+	iput (inode);
+	goto out_stop;
+}
+
+/*
+ * routine to check that the specified directory is empty (for rmdir)
+ */
+static int empty_dir (struct inode * inode)
+{
+	unsigned long offset;
+	struct buffer_head * bh;
+	struct ext3_dir_entry_2 * de, * de1;
+	struct super_block * sb;
+	int err;
+
+	sb = inode->i_sb;
+	if (inode->i_size < EXT3_DIR_REC_LEN(1) + EXT3_DIR_REC_LEN(2) ||
+	    !(bh = ext3_bread (NULL, inode, 0, 0, &err))) {
+	    	ext3_warning (inode->i_sb, "empty_dir",
+			      "bad directory (dir #%lu) - no data block",
+			      inode->i_ino);
+		return 1;
+	}
+	de = (struct ext3_dir_entry_2 *) bh->b_data;
+	de1 = (struct ext3_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len));
+	if (le32_to_cpu(de->inode) != inode->i_ino || !le32_to_cpu(de1->inode) || 
+	    strcmp (".", de->name) || strcmp ("..", de1->name)) {
+	    	ext3_warning (inode->i_sb, "empty_dir",
+			      "bad directory (dir #%lu) - no `.' or `..'",
+			      inode->i_ino);
+		brelse (bh);
+		return 1;
+	}
+	offset = le16_to_cpu(de->rec_len) + le16_to_cpu(de1->rec_len);
+	de = (struct ext3_dir_entry_2 *) ((char *) de1 + le16_to_cpu(de1->rec_len));
+	while (offset < inode->i_size ) {
+		if (!bh || (void *) de >= (void *) (bh->b_data + sb->s_blocksize)) {
+			brelse (bh);
+			bh = ext3_bread (NULL, inode, offset >> EXT3_BLOCK_SIZE_BITS(sb), 0, &err);
+			if (!bh) {
+#if 0
+				ext3_error (sb, "empty_dir",
+					    "directory #%lu contains a hole at offset %lu",
+					    inode->i_ino, offset);
+#endif
+				offset += sb->s_blocksize;
+				continue;
+			}
+			de = (struct ext3_dir_entry_2 *) bh->b_data;
+		}
+		if (!ext3_check_dir_entry ("empty_dir", inode, de, bh,
+					   offset)) {
+			brelse (bh);
+			return 1;
+		}
+		if (le32_to_cpu(de->inode)) {
+			brelse (bh);
+			return 0;
+		}
+		offset += le16_to_cpu(de->rec_len);
+		de = (struct ext3_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len));
+	}
+	brelse (bh);
+	return 1;
+}
+
+int ext3_rmdir (struct inode * dir, struct dentry *dentry)
+{
+	int retval;
+	struct inode * inode;
+	struct buffer_head * bh;
+	struct ext3_dir_entry_2 * de;
+	handle_t *handle;
+	
+	handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS);
+	if (IS_ERR(handle))
+		return PTR_ERR(handle);
+
+	retval = -ENOENT;
+	bh = ext3_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de);
+	if (!bh)
+		goto end_rmdir;
+
+	inode = dentry->d_inode;
+	DQUOT_INIT(inode);
+
+	retval = -EIO;
+	if (le32_to_cpu(de->inode) != inode->i_ino)
+		goto end_rmdir;
+
+	retval = -ENOTEMPTY;
+	if (!empty_dir (inode))
+		goto end_rmdir;
+
+	retval = ext3_delete_entry (handle, de, bh); /* @@@ handle */
+	dir->i_version = ++global_event;
+
+	if (retval)
+		goto end_rmdir;
+	if (inode->i_nlink != 2)
+		ext3_warning (inode->i_sb, "ext3_rmdir",
+			      "empty directory has nlink!=2 (%d)",
+			      inode->i_nlink);
+	inode->i_version = ++global_event;
+	inode->i_nlink = 0;
+	inode->i_size = 0;
+	ext3_orphan_add(handle, inode);
+	ext3_mark_inode_dirty(handle, inode);
+	dir->i_nlink--;
+	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+	dir->u.ext3_i.i_flags &= ~EXT3_BTREE_FL;
+	ext3_mark_inode_dirty(handle, dir);
+	d_delete(dentry);
+	if (IS_SYNC(dir)) 
+		handle->h_sync = 1;
+
+end_rmdir:
+	ext3_journal_stop(handle, dir);
+	brelse (bh);
+	return retval;
+}
+
+/* ext3_orphan_add() links a unlinked or truncated inode into a list of
+ * such inodes, starting at the superblock, in case we crash before the
+ * file is closed/deleted, or in case the inode truncate spans multiple
+ * transactions and the last transaction is not recovered after a crash.
+ *
+ * At filesystem recovery time, we walk this list deleting unlinked
+ * inodes and truncating linked inodes in ext3_orphan_cleanup().
+ */
+void ext3_orphan_add(handle_t *handle, struct inode *inode)
+{
+	struct super_block *sb = inode->i_sb;
+
+	lock_super(sb);
+	if (!list_empty(&inode->u.ext3_i.i_orphan)) {
+		unlock_super(sb);
+		return;
+	}
+	journal_get_write_access(handle, sb->u.ext3_sb.s_sbh);
+	NEXT_ORPHAN(inode) = le32_to_cpu(EXT3_SB(sb)->s_es->s_last_orphan);
+	EXT3_SB(sb)->s_es->s_last_orphan = cpu_to_le32(inode->i_ino);
+	journal_dirty_metadata(handle, sb->u.ext3_sb.s_sbh);
+	list_add(&inode->u.ext3_i.i_orphan, &EXT3_SB(sb)->s_orphan);
+	ext3_mark_inode_dirty(handle, inode);
+	unlock_super(sb);
+
+	jfs_debug(4, "superblock will point to %ld\n", inode->i_ino);
+	jfs_debug(4, "orphan inode %ld will point to %d\n",
+		  inode->i_ino, NEXT_ORPHAN(inode));
+}
+
+int ext3_unlink(struct inode * dir, struct dentry *dentry)
+{
+	int retval;
+	struct inode * inode;
+	struct buffer_head * bh;
+	struct ext3_dir_entry_2 * de;
+	handle_t *handle;
+	
+	handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS);
+	if (IS_ERR(handle))
+		return PTR_ERR(handle);
+
+	retval = -ENOENT;
+	bh = ext3_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de);
+	if (!bh)
+		goto end_unlink;
+
+	inode = dentry->d_inode;
+	DQUOT_INIT(inode);
+
+	retval = -EIO;
+	if (le32_to_cpu(de->inode) != inode->i_ino)
+		goto end_unlink;
+	
+	if (!inode->i_nlink) {
+		ext3_warning (inode->i_sb, "ext3_unlink",
+			      "Deleting nonexistent file (%lu), %d",
+			      inode->i_ino, inode->i_nlink);
+		inode->i_nlink = 1;
+	}
+	retval = ext3_delete_entry (handle, de, bh);
+	if (retval)
+		goto end_unlink;
+	dir->i_version = ++global_event;
+	if (IS_SYNC(dir))
+		handle->h_sync = 1;
+	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+	dir->u.ext3_i.i_flags &= ~EXT3_BTREE_FL;
+	ext3_mark_inode_dirty(handle, dir);
+	inode->i_nlink--;
+	if (!inode->i_nlink)
+		ext3_orphan_add(handle, inode);
+	ext3_mark_inode_dirty(handle, inode);
+	inode->i_ctime = dir->i_ctime;
+	retval = 0;
+	d_delete(dentry);	/* This also frees the inode */
+
+end_unlink:
+	ext3_journal_stop(handle, dir);
+	brelse (bh);
+	return retval;
+}
+
+int ext3_symlink (struct inode * dir, struct dentry *dentry, const char * symname)
+{
+	struct ext3_dir_entry_2 * de;
+	struct inode * inode;
+	struct buffer_head * bh = NULL, * name_block = NULL;
+	char * link;
+	int i, l, err = -EIO;
+	char c;
+	handle_t *handle;
+	
+	handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + 5);
+	if (IS_ERR(handle))
+		return PTR_ERR(handle);
+
+	if (!(inode = ext3_new_inode (handle, dir, S_IFLNK, &err)))
+		goto out;
+	inode->i_mode = S_IFLNK | S_IRWXUGO;
+	inode->i_op = &ext3_symlink_inode_operations;
+	for (l = 0; l < inode->i_sb->s_blocksize - 1 &&
+	     symname [l]; l++)
+		;
+	if (l >= sizeof (inode->u.ext3_i.i_data_u.i_data)) {
+
+		ext3_debug ("l=%d, normal symlink\n", l);
+
+		name_block = ext3_bread (handle, inode, 0, 1, &err);
+		if (!name_block) {
+			inode->i_nlink--;
+			ext3_mark_inode_dirty(handle, inode);
+			iput (inode);
+			goto out;
+		}
+		link = name_block->b_data;
+		journal_get_write_access(handle, name_block);
+	} else {
+		link = (char *) inode->u.ext3_i.i_data_u.i_data;
+
+		ext3_debug ("l=%d, fast symlink\n", l);
+
+	}
+	i = 0;
+	while (i < inode->i_sb->s_blocksize - 1 && (c = *(symname++)))
+		link[i++] = c;
+	link[i] = 0;
+	if (name_block) {
+		journal_dirty_metadata(handle, name_block);
+		brelse (name_block);
+	}
+	inode->i_size = inode->u.ext3_i.i_disksize = i;
+	ext3_mark_inode_dirty(handle, inode);
+
+	bh = ext3_add_entry (handle, dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
+	if (!bh)
+		goto out_no_entry;
+	de->inode = cpu_to_le32(inode->i_ino);
+	if (EXT3_HAS_INCOMPAT_FEATURE(dir->i_sb,
+				      EXT3_FEATURE_INCOMPAT_FILETYPE))
+		de->file_type = EXT3_FT_SYMLINK;
+	dir->i_version = ++global_event;
+	if (IS_SYNC(dir)) 
+		handle->h_sync = 1;
+	brelse (bh);
+	d_instantiate(dentry, inode);
+	err = 0;
+out:
+	ext3_journal_stop(handle, dir);
+	return err;
+
+out_no_entry:
+	inode->i_nlink--;
+	ext3_mark_inode_dirty(handle, inode);
+	iput (inode);
+	goto out;
+}
+
+int ext3_link (struct dentry * old_dentry,
+		struct inode * dir, struct dentry *dentry)
+{
+	struct inode *inode = old_dentry->d_inode;
+	struct ext3_dir_entry_2 * de;
+	struct buffer_head * bh;
+	int err;
+	handle_t *handle;
+
+	if (S_ISDIR(inode->i_mode))
+		return -EPERM;
+
+	if (inode->i_nlink >= EXT3_LINK_MAX)
+		return -EMLINK;
+
+	handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS);
+	if (IS_ERR(handle))
+		return PTR_ERR(handle);
+	
+	bh = ext3_add_entry (handle, dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
+	if (!bh)
+		goto out;
+
+	de->inode = cpu_to_le32(inode->i_ino);
+	if (EXT3_HAS_INCOMPAT_FEATURE(inode->i_sb,
+				      EXT3_FEATURE_INCOMPAT_FILETYPE)) {
+		if (S_ISREG(inode->i_mode))
+			de->file_type = EXT3_FT_REG_FILE;
+		else if (S_ISDIR(inode->i_mode))
+			de->file_type = EXT3_FT_DIR;
+		else if (S_ISLNK(inode->i_mode))
+			de->file_type = EXT3_FT_SYMLINK;
+		else if (S_ISSOCK(inode->i_mode))
+			de->file_type = EXT3_FT_SOCK;
+		else if (S_ISCHR(inode->i_mode))
+			de->file_type = EXT3_FT_CHRDEV;
+		else if (S_ISBLK(inode->i_mode))
+			de->file_type = EXT3_FT_BLKDEV;
+		else if (S_ISFIFO(inode->i_mode))  
+			de->file_type = EXT3_FT_FIFO;
+	}
+	dir->i_version = ++global_event;
+	if (IS_SYNC(dir))
+		handle->h_sync = 1;
+	brelse (bh);
+	inode->i_nlink++;
+	inode->i_ctime = CURRENT_TIME;
+	ext3_mark_inode_dirty(handle, inode);
+	inode->i_count++;
+	d_instantiate(dentry, inode);
+	err = 0;
+out:
+	ext3_journal_stop(handle, dir);
+	return err;
+}
+
+#define PARENT_INO(buffer) \
+	((struct ext3_dir_entry_2 *) ((char *) buffer + \
+	le16_to_cpu(((struct ext3_dir_entry_2 *) buffer)->rec_len)))->inode
+
+/*
+ * Anybody can rename anything with this: the permission checks are left to the
+ * higher-level routines.
+ */
+int ext3_rename (struct inode * old_dir, struct dentry *old_dentry,
+			   struct inode * new_dir,struct dentry *new_dentry)
+{
+	struct inode * old_inode, * new_inode;
+	struct buffer_head * old_bh, * new_bh, * dir_bh;
+	struct ext3_dir_entry_2 * old_de, * new_de;
+	int retval;
+	handle_t *handle;
+	
+	old_bh = new_bh = dir_bh = NULL;
+
+	handle = ext3_journal_start(old_dir, 2 * EXT3_DATA_TRANS_BLOCKS + 2);
+	if (IS_ERR(handle))
+		return PTR_ERR(handle);
+
+	old_bh = ext3_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de);
+	/*
+	 *  Check for inode number is _not_ due to possible IO errors.
+	 *  We might rmdir the source, keep it as pwd of some process
+	 *  and merrily kill the link to whatever was created under the
+	 *  same name. Goodbye sticky bit ;-<
+	 */
+	old_inode = old_dentry->d_inode;
+	retval = -ENOENT;
+	if (!old_bh || le32_to_cpu(old_de->inode) != old_inode->i_ino)
+		goto end_rename;
+
+	new_inode = new_dentry->d_inode;
+	new_bh = ext3_find_entry (new_dir, new_dentry->d_name.name,
+				new_dentry->d_name.len, &new_de);
+	if (new_bh) {
+		if (!new_inode) {
+			brelse (new_bh);
+			new_bh = NULL;
+		} else {
+			DQUOT_INIT(new_inode);
+		}
+	}
+	if (S_ISDIR(old_inode->i_mode)) {
+		if (new_inode) {
+			retval = -ENOTEMPTY;
+			if (!empty_dir (new_inode))
+				goto end_rename;
+		}
+		retval = -EIO;
+		dir_bh = ext3_bread (NULL, old_inode, 0, 0, &retval);
+		if (!dir_bh)
+			goto end_rename;
+		if (le32_to_cpu(PARENT_INO(dir_bh->b_data)) != old_dir->i_ino)
+			goto end_rename;
+		retval = -EMLINK;
+		if (!new_inode && new_dir!=old_dir &&
+				new_dir->i_nlink >= EXT3_LINK_MAX)
+			goto end_rename;
+	}
+	if (!new_bh) {
+		new_bh = ext3_add_entry (handle, new_dir, new_dentry->d_name.name,
+					new_dentry->d_name.len, &new_de,
+					&retval);
+		if (!new_bh)
+			goto end_rename;
+	}
+	new_dir->i_version = ++global_event;
+
+	/*
+	 * ok, that's it
+	 */
+
+	journal_get_write_access(handle, old_bh);
+	journal_get_write_access(handle, new_bh);
+	if (dir_bh)
+		journal_get_write_access(handle, dir_bh);
+
+	new_de->inode = le32_to_cpu(old_inode->i_ino);
+	if (EXT3_HAS_INCOMPAT_FEATURE(new_dir->i_sb,
+				      EXT3_FEATURE_INCOMPAT_FILETYPE))
+		new_de->file_type = old_de->file_type;
+	
+	ext3_delete_entry (handle, old_de, old_bh); /* @@@ HANDLE */
+
+	old_dir->i_version = ++global_event;
+	if (new_inode) {
+		new_inode->i_nlink--;
+		new_inode->i_ctime = CURRENT_TIME;
+		ext3_mark_inode_dirty(handle, new_inode);
+	}
+	old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+	old_dir->u.ext3_i.i_flags &= ~EXT3_BTREE_FL;
+	ext3_mark_inode_dirty(handle, old_dir);
+	if (dir_bh) {
+		PARENT_INO(dir_bh->b_data) = le32_to_cpu(new_dir->i_ino);
+		journal_dirty_metadata(handle, dir_bh);
+		old_dir->i_nlink--;
+		ext3_mark_inode_dirty(handle, old_dir);
+		if (new_inode) {
+			new_inode->i_nlink--;
+			ext3_mark_inode_dirty(handle, new_inode);
+		} else {
+			new_dir->i_nlink++;
+			new_dir->u.ext3_i.i_flags &= ~EXT3_BTREE_FL;
+			ext3_mark_inode_dirty(handle, new_dir);
+		}
+	}
+	journal_dirty_metadata(handle, old_bh);
+	journal_dirty_metadata(handle, new_bh);
+	if (IS_SYNC(old_dir) || IS_SYNC(new_dir))
+		handle->h_sync = 1;
+
+	retval = 0;
+
+end_rename:
+	brelse (dir_bh);
+	brelse (old_bh);
+	brelse (new_bh);
+	ext3_journal_stop(handle, old_dir);
+	return retval;
+}
--- linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/super.c.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/super.c	Wed Oct 25 22:27:07 2000
@@ -0,0 +1,1273 @@
+/*
+ *  linux/fs/ext3/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 <stdarg.h>
+
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/jfs.h>
+#include <linux/ext3_fs.h>
+#include <linux/malloc.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
+
+static char error_buf[1024];
+
+static int ext3_load_journal(struct super_block *, struct ext3_super_block *);
+static int ext3_create_journal(struct super_block *, struct ext3_super_block *,
+			       int);
+static void ext3_commit_super (struct super_block * sb,
+			       struct ext3_super_block * es, 
+			       int sync);
+static void ext3_mark_recovery_complete(struct super_block * sb,
+					struct ext3_super_block * es);
+static void ext3_clear_journal_err(struct super_block * sb,
+				   struct ext3_super_block * es);
+
+/* Deal with the reporting of failure conditions on a filesystem such as
+ * inconsistencies detected or read IO failures. 
+ *
+ * On ext2, we can store the error state of the filesystem in the
+ * superblock.  That is not possible on ext3, because we may have other
+ * write ordering constraints on the superblock which prevent us from
+ * writing it out straight away; and given that the journal is about to
+ * be aborted, we can't rely on the current, or future, transactions to
+ * write out the superblock safely.
+ *
+ * We'll just use the journal_abort() error code to record an error in
+ * the journal instead.  On recovery, the journal will compain about
+ * that error until we've noted it down and cleared it.
+ */
+
+void ext3_error (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);
+	if (test_opt (sb, ERRORS_PANIC) ||
+	    (le16_to_cpu(sb->u.ext3_sb.s_es->s_errors) == EXT3_ERRORS_PANIC &&
+	     !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_RO)))
+		panic ("EXT3-fs panic (device %s): %s: %s\n",
+		       bdevname(sb->s_dev), function, error_buf);
+	printk (KERN_CRIT "EXT3-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.ext3_sb.s_es->s_errors) == EXT3_ERRORS_RO &&
+	     !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_PANIC))) {
+		printk ("Remounting filesystem read-only\n");
+		sb->s_flags |= MS_RDONLY;
+		sb->u.ext3_sb.s_mount_opt |= EXT3_MOUNT_ABORT;
+		
+		if (!(sb->s_flags & MS_RDONLY))
+			journal_abort(EXT3_SB(sb)->s_journal, -EIO);
+	}
+}
+
+/* 
+ * ext3_abort is a much stronger failure handler than ext3_error.  The
+ * abort function may be used to deal with unrecoverable failures such
+ * as journal IO errors or ENOMEM at a critical moment in log
+ * management. 
+ * 
+ * We unconditionally force the filesystem into an ABORT|READONLY state,
+ * unless the error response on the fs has been set to panic in which we
+ * take the easy way out and panic immediately.
+ */
+
+void ext3_abort (struct super_block * sb, const char * function,
+		 const char * fmt, ...)
+{
+	va_list args;
+
+	printk (KERN_CRIT "ext3_abort called.\n");
+
+	sb->u.ext3_sb.s_mount_state |= EXT3_ERROR_FS;
+
+	va_start (args, fmt);
+	vsprintf (error_buf, fmt, args);
+	va_end (args);
+
+	if (test_opt (sb, ERRORS_PANIC) ||
+	    (le16_to_cpu(sb->u.ext3_sb.s_es->s_errors) ==
+	     EXT3_ERRORS_PANIC))
+		panic ("EXT3-fs panic (device %s): %s: %s\n",
+		       bdevname(sb->s_dev), function, error_buf);
+
+	printk (KERN_CRIT "EXT3-fs error (device %s): %s: %s\n",
+		bdevname(sb->s_dev), function, error_buf);
+	if (!(sb->s_flags & MS_RDONLY)) {
+		printk ("Remounting filesystem read-only\n");
+		sb->s_flags |= MS_RDONLY;
+	}
+	sb->u.ext3_sb.s_mount_opt |= EXT3_MOUNT_ABORT;
+	journal_abort(EXT3_SB(sb)->s_journal, -EIO);
+}
+
+NORET_TYPE void ext3_panic (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);
+	/* this is to prevent panic from syncing this filesystem */
+	if (sb->s_lock)
+		sb->s_lock=0;
+	sb->s_flags |= MS_RDONLY;
+	panic ("EXT3-fs panic (device %s): %s: %s\n",
+	       bdevname(sb->s_dev), function, error_buf);
+}
+
+void ext3_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 "EXT3-fs warning (device %s): %s: %s\n",
+		bdevname(sb->s_dev), function, error_buf);
+}
+
+void ext3_put_super (struct super_block * sb)
+{
+	int db_count;
+	int i;
+	struct ext3_super_block *es = EXT3_SB(sb)->s_es;
+
+	journal_release(EXT3_SB(sb)->s_journal);
+	if (!(sb->s_flags & MS_RDONLY)) {
+		es->s_feature_incompat &= cpu_to_le32(~EXT3_FEATURE_INCOMPAT_RECOVER);
+		es->s_state = le16_to_cpu(EXT3_SB(sb)->s_mount_state);
+		mark_buffer_dirty(EXT3_SB(sb)->s_sbh, 1);
+	}
+
+	db_count = EXT3_SB(sb)->s_db_per_group;
+	for (i = 0; i < db_count; i++)
+		if (EXT3_SB(sb)->s_group_desc[i])
+			brelse (EXT3_SB(sb)->s_group_desc[i]);
+	kfree_s (EXT3_SB(sb)->s_group_desc,
+		 db_count * sizeof (struct buffer_head *));
+	for (i = 0; i < EXT3_MAX_GROUP_LOADED; i++)
+		if (EXT3_SB(sb)->s_inode_bitmap[i])
+			brelse (EXT3_SB(sb)->s_inode_bitmap[i]);
+	for (i = 0; i < EXT3_MAX_GROUP_LOADED; i++)
+		if (EXT3_SB(sb)->s_block_bitmap[i])
+			brelse (EXT3_SB(sb)->s_block_bitmap[i]);
+	brelse (EXT3_SB(sb)->s_sbh);
+
+	J_ASSERT (list_empty(&EXT3_SB(sb)->s_orphan));
+
+	MOD_DEC_USE_COUNT;
+	return;
+}
+
+static struct super_operations ext3_sops = {
+	ext3_read_inode,
+	ext3_write_inode,
+	ext3_put_inode,
+	ext3_delete_inode,
+	ext3_notify_change,
+	ext3_put_super,
+	ext3_write_super,
+	ext3_statfs,
+	ext3_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, 
+			  unsigned long * inum,
+			  int is_remount)
+{
+	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, "abort"))
+			set_opt (*mount_options, ABORT);
+		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 ("EXT3-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 ("EXT3-fs: the errors option requires "
+					"an argument\n");
+				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 ("EXT3-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 ("EXT3-fs: the resgid option requires "
+					"an argument\n");
+				return 0;
+			}
+			*resgid = simple_strtoul (value, &value, 0);
+			if (*value) {
+				printk ("EXT3-fs: Invalid resgid option: %s\n",
+					value);
+				return 0;
+			}
+		}
+		else if (!strcmp (this_char, "resuid")) {
+			if (!value || !*value) {
+				printk ("EXT3-fs: the resuid option requires "
+					"an argument");
+				return 0;
+			}
+			*resuid = simple_strtoul (value, &value, 0);
+			if (*value) {
+				printk ("EXT3-fs: Invalid resuid option: %s\n",
+					value);
+				return 0;
+			}
+		}
+		else if (!strcmp (this_char, "sb")) {
+			if (!value || !*value) {
+				printk ("EXT3-fs: the sb option requires "
+					"an argument\n");
+				return 0;
+			}
+			*sb_block = simple_strtoul (value, &value, 0);
+			if (*value) {
+				printk ("EXT3-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 if (!strcmp (this_char, "journal")) {
+			/* @@@ FIXME */
+			/* Eventually we will want to be able to create
+                           a journal file here.  For now, only allow the
+                           user to specify an existing inode to be the
+                           journal file. */
+			if (!value || !*value) {
+				printk ("EXT3-fs: the journal option requires "
+					"an argument\n");
+				return 0;
+			}
+			if (is_remount) {
+				printk ("EXT3-fs: cannot specify journal on remount\n");
+				return 0;
+			}
+
+			if (!strcmp (value, "update")) {
+				set_opt (*mount_options, UPDATE_JOURNAL);
+			} else {
+				*inum = simple_strtoul (value, &value, 0);
+				if (*value) {
+					printk ("EXT3-fs: Invalid journal option: "
+						"%s\n", value);
+					return 0;
+				}
+			}
+		} 
+		else if (!strcmp (this_char, "noload"))
+			set_opt (*mount_options, NOLOAD);
+		else if (!strcmp (this_char, "data")) {
+			int data_opt = 0;
+
+			if (!value || !*value) {
+				printk ("EXT3-fs: the data option requires "
+					"an argument\n");
+				return 0;
+			}
+			if (is_remount) {
+				printk ("EXT3-fs: cannot change data mode on remount\n");
+				return 0;
+			}
+
+			if (!strcmp (value, "journal"))
+				data_opt = EXT3_MOUNT_JOURNAL_DATA;
+			else if (!strcmp (value, "ordered"))
+				data_opt = EXT3_MOUNT_ORDERED_DATA;
+			else if (!strcmp (value, "writeback"))
+				data_opt = EXT3_MOUNT_WRITEBACK_DATA;
+			else { 
+				printk ("EXT3-fs: Invalid data option: %s\n",
+					value);
+				return 0;
+			}
+			*mount_options &= ~EXT3_MOUNT_DATA_FLAGS;
+			*mount_options |= data_opt;
+		} else {
+			printk ("EXT3-fs: Unrecognized mount option %s\n", this_char);
+			return 0;
+		}
+	}
+	return 1;
+}
+
+static void ext3_setup_super (struct super_block * sb,
+			      struct ext3_super_block * es)
+{
+	if (le32_to_cpu(es->s_rev_level) > EXT3_MAX_SUPP_REV) {
+			printk ("EXT3-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.ext3_sb.s_mount_state & EXT3_VALID_FS))
+			printk ("EXT3-fs warning: mounting unchecked fs, "
+				"running e2fsck is recommended\n");
+		else if ((sb->u.ext3_sb.s_mount_state & EXT3_ERROR_FS))
+			printk ("EXT3-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 ("EXT3-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 ("EXT3-fs warning: checktime reached, "
+				"running e2fsck is recommended\n");
+
+#if 0
+		/* @@@ We _will_ want to clear the valid bit if we find
+                   inconsistencies, to force a fsck at reboot.  But for
+                   a plain journaled filesystem we can keep it set as
+                   valid forever! :) */
+		es->s_state = cpu_to_le16(le16_to_cpu(es->s_state) & ~EXT3_VALID_FS);
+#endif
+		if (!(__s16) le16_to_cpu(es->s_max_mnt_count))
+			es->s_max_mnt_count = (__s16) cpu_to_le16(EXT3_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);
+		es->s_feature_incompat |= cpu_to_le32(EXT3_FEATURE_INCOMPAT_RECOVER);
+		ext3_commit_super (sb, es, 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",
+				EXT3FS_VERSION, EXT3FS_DATE, sb->s_blocksize,
+				sb->u.ext3_sb.s_frag_size,
+				sb->u.ext3_sb.s_groups_count,
+				EXT3_BLOCKS_PER_GROUP(sb),
+				EXT3_INODES_PER_GROUP(sb),
+				sb->u.ext3_sb.s_mount_opt);
+		if (test_opt (sb, CHECK)) {
+			ext3_check_blocks_bitmap (sb);
+			ext3_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 ext3_check_descriptors (struct super_block * sb)
+{
+	int i;
+	int desc_block = 0;
+	unsigned long block = le32_to_cpu(sb->u.ext3_sb.s_es->s_first_data_block);
+	struct ext3_group_desc * gdp = NULL;
+
+	ext3_debug ("Checking group descriptors");
+
+	for (i = 0; i < sb->u.ext3_sb.s_groups_count; i++)
+	{
+		if ((i % EXT3_DESC_PER_BLOCK(sb)) == 0)
+			gdp = (struct ext3_group_desc *) sb->u.ext3_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 + EXT3_BLOCKS_PER_GROUP(sb))
+		{
+			ext3_error (sb, "ext3_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 + EXT3_BLOCKS_PER_GROUP(sb))
+		{
+			ext3_error (sb, "ext3_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.ext3_sb.s_itb_per_group >=
+		    block + EXT3_BLOCKS_PER_GROUP(sb))
+		{
+			ext3_error (sb, "ext3_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 += EXT3_BLOCKS_PER_GROUP(sb);
+		gdp++;
+	}
+	return 1;
+}
+
+/* ext3_orphan_cleanup() walks a singly-linked list of inodes (starting at
+ * the superblock) which were deleted from all directories, but held open by
+ * a process at the time of a crash.  We walk the list and try to delete these
+ * inodes at recovery time (only with a read-write filesystem).
+ *
+ * In order to keep the orphan inode chain consistent during traversal (in
+ * case of crash during recovery), we link each inode into the superblock
+ * orphan list_head and handle it the same way as an inode deletion during
+ * normal operation (which journals the operations for us).
+ *
+ * We only do an iget() and an iput() on each inode, which is very safe if we
+ * accidentally point at an in-use or already deleted inode.  The worst that
+ * can happen in this case is that we get a "bit already cleared" message from
+ * ext3_free_inode().  The only reason we would point at a wrong inode is if
+ * e2fsck was run on this filesystem, and it must have already done the orphan
+ * inode cleanup for us, so we can safely abort without any further action.
+ */
+static void ext3_orphan_cleanup (struct super_block * sb,
+				 struct ext3_super_block * es)
+{
+	unsigned int s_flags = sb->s_flags;
+	int nr_orphans = 0, nr_truncates = 0;
+	if (!es->s_last_orphan) {
+		jfs_debug(4, "no orphan inodes to clean up\n");
+		return;
+	}
+	
+	if (s_flags & MS_RDONLY) {
+		printk(KERN_INFO "EXT3-fs: %s: orphan cleanup on read-only fs\n",
+		       kdevname(sb->s_dev));
+		sb->s_flags &= ~MS_RDONLY;
+	}
+
+	while (es->s_last_orphan) {
+		struct inode *inode;
+
+		if (!(inode =
+		      ext3_orphan_get(sb, le32_to_cpu(es->s_last_orphan)))) {
+			es->s_last_orphan = 0;
+			break;
+		}
+
+		list_add(&inode->u.ext3_i.i_orphan, &EXT3_SB(sb)->s_orphan);
+		if (inode->i_nlink) {
+			jfs_debug(2, "truncating inode %ld to %ld bytes\n",
+				  inode->i_ino, inode->i_size);
+			ext3_truncate(inode);
+			nr_truncates++;
+		} else {
+			jfs_debug(2, "deleting unreferenced inode %ld\n",
+				  inode->i_ino);
+			nr_orphans++;
+		}
+		iput(inode);  /* The delete magic happens here! */
+	}
+
+#define PLURAL(x) (x), ((x)==1) ? "" : "s"
+
+	if (nr_orphans)
+		printk(KERN_INFO "EXT3-fs: %s: %d orphan inode%s deleted\n", 
+		       kdevname(sb->s_dev), PLURAL(nr_orphans));
+	if (nr_truncates)
+		printk(KERN_INFO "EXT3-fs: %s: %d truncate%s cleaned up\n", 
+		       kdevname(sb->s_dev), PLURAL(nr_truncates));
+	sb->s_flags = s_flags; /* Restore MS_RDONLY status */
+}
+
+#define log2(n) ffz(~(n))
+
+struct super_block * ext3_read_super (struct super_block * sb, void * data,
+				      int silent)
+{
+	struct buffer_head * bh;
+	struct ext3_super_block * es;
+	unsigned long sb_block = 1;
+	unsigned short resuid = EXT3_DEF_RESUID;
+	unsigned short resgid = EXT3_DEF_RESGID;
+	unsigned long logic_sb_block = 1;
+	unsigned long offset = 0;
+	unsigned long journal_inum = 0;
+	
+	kdev_t dev = sb->s_dev;
+	int blocksize = BLOCK_SIZE;
+	int hblock;
+	int db_count;
+	int i, j, err;
+
+	/*
+	 * 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->s_default_dquot_flags = DQUOT_WRITETHROUGH;
+	sb->u.ext3_sb.s_mount_opt = 0;
+	set_opt (sb->u.ext3_sb.s_mount_opt, CHECK_NORMAL);
+
+	if (!parse_options ((char *) data, &sb_block, &resuid, &resgid,
+	    &sb->u.ext3_sb.s_mount_opt, &journal_inum, 0)) {
+		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 ("EXT3-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 ext3 macro-instructions depend on its value
+	 */
+	es = (struct ext3_super_block *) (((char *)bh->b_data) + offset);
+	sb->u.ext3_sb.s_es = es;
+	sb->s_magic = le16_to_cpu(es->s_magic);
+	if (sb->s_magic != EXT3_SUPER_MAGIC) {
+		if (!silent)
+			printk ("VFS: Can't find an ext3 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) > EXT3_GOOD_OLD_REV) {
+		if (le32_to_cpu(es->s_feature_incompat) & ~EXT3_FEATURE_INCOMPAT_SUPP) {
+			printk("EXT3-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) & ~EXT3_FEATURE_RO_COMPAT_SUPP)) {
+			printk("EXT3-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.ext3_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("EXT3-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("EXT3-fs: Couldn't read superblock on "
+			       "2nd try.\n");
+			goto failed_mount;
+		}
+		es = (struct ext3_super_block *) (((char *)bh->b_data) + offset);
+		sb->u.ext3_sb.s_es = es;
+		if (es->s_magic != le16_to_cpu(EXT3_SUPER_MAGIC)) {
+			printk ("EXT3-fs: Magic mismatch, very weird !\n");
+			goto failed_mount;
+		}
+	}
+	if (le32_to_cpu(es->s_rev_level) == EXT3_GOOD_OLD_REV) {
+		sb->u.ext3_sb.s_inode_size = EXT3_GOOD_OLD_INODE_SIZE;
+		sb->u.ext3_sb.s_first_ino = EXT3_GOOD_OLD_FIRST_INO;
+	} else {
+		sb->u.ext3_sb.s_inode_size = le16_to_cpu(es->s_inode_size);
+		sb->u.ext3_sb.s_first_ino = le32_to_cpu(es->s_first_ino);
+		if (sb->u.ext3_sb.s_inode_size != EXT3_GOOD_OLD_INODE_SIZE) {
+			printk ("EXT3-fs: unsupported inode size: %d\n",
+				sb->u.ext3_sb.s_inode_size);
+			goto failed_mount;
+		}
+	}
+	sb->u.ext3_sb.s_feature_compat = le32_to_cpu(es->s_feature_compat);
+	sb->u.ext3_sb.s_feature_incompat = le32_to_cpu(es->s_feature_incompat);
+	sb->u.ext3_sb.s_feature_ro_compat = le32_to_cpu(es->s_feature_ro_compat);
+	sb->u.ext3_sb.s_frag_size = EXT3_MIN_FRAG_SIZE <<
+				   le32_to_cpu(es->s_log_frag_size);
+	if (sb->u.ext3_sb.s_frag_size)
+		sb->u.ext3_sb.s_frags_per_block = sb->s_blocksize /
+						  sb->u.ext3_sb.s_frag_size;
+	else
+		sb->s_magic = 0;
+	sb->u.ext3_sb.s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group);
+	sb->u.ext3_sb.s_frags_per_group = le32_to_cpu(es->s_frags_per_group);
+	sb->u.ext3_sb.s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group);
+	sb->u.ext3_sb.s_inodes_per_block = sb->s_blocksize /
+					   EXT3_INODE_SIZE(sb);
+	sb->u.ext3_sb.s_itb_per_group = sb->u.ext3_sb.s_inodes_per_group /
+				        sb->u.ext3_sb.s_inodes_per_block;
+	sb->u.ext3_sb.s_desc_per_block = sb->s_blocksize /
+					 sizeof (struct ext3_group_desc);
+	sb->u.ext3_sb.s_sbh = bh;
+	if (resuid != EXT3_DEF_RESUID)
+		sb->u.ext3_sb.s_resuid = resuid;
+	else
+		sb->u.ext3_sb.s_resuid = le16_to_cpu(es->s_def_resuid);
+	if (resgid != EXT3_DEF_RESGID)
+		sb->u.ext3_sb.s_resgid = resgid;
+	else
+		sb->u.ext3_sb.s_resgid = le16_to_cpu(es->s_def_resgid);
+	sb->u.ext3_sb.s_mount_state = le16_to_cpu(es->s_state);
+	sb->u.ext3_sb.s_addr_per_block_bits =
+		log2 (EXT3_ADDR_PER_BLOCK(sb));
+	sb->u.ext3_sb.s_desc_per_block_bits =
+		log2 (EXT3_DESC_PER_BLOCK(sb));
+	if (sb->s_magic != EXT3_SUPER_MAGIC) {
+		if (!silent)
+			printk ("VFS: Can't find an ext3 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.ext3_sb.s_frag_size) {
+		printk ("EXT3-fs: fragsize %lu != blocksize %lu (not supported yet)\n",
+			sb->u.ext3_sb.s_frag_size, sb->s_blocksize);
+		goto failed_mount;
+	}
+
+	if (sb->u.ext3_sb.s_blocks_per_group > sb->s_blocksize * 8) {
+		printk ("EXT3-fs: #blocks per group too big: %lu\n",
+			sb->u.ext3_sb.s_blocks_per_group);
+		goto failed_mount;
+	}
+	if (sb->u.ext3_sb.s_frags_per_group > sb->s_blocksize * 8) {
+		printk ("EXT3-fs: #fragments per group too big: %lu\n",
+			sb->u.ext3_sb.s_frags_per_group);
+		goto failed_mount;
+	}
+	if (sb->u.ext3_sb.s_inodes_per_group > sb->s_blocksize * 8) {
+		printk ("EXT3-fs: #inodes per group too big: %lu\n",
+			sb->u.ext3_sb.s_inodes_per_group);
+		goto failed_mount;
+	}
+
+	sb->u.ext3_sb.s_groups_count = (le32_to_cpu(es->s_blocks_count) -
+				        le32_to_cpu(es->s_first_data_block) +
+				       EXT3_BLOCKS_PER_GROUP(sb) - 1) /
+				       EXT3_BLOCKS_PER_GROUP(sb);
+	db_count = (sb->u.ext3_sb.s_groups_count + EXT3_DESC_PER_BLOCK(sb) - 1) /
+		   EXT3_DESC_PER_BLOCK(sb);
+	sb->u.ext3_sb.s_group_desc = kmalloc (db_count * sizeof (struct buffer_head *), GFP_KERNEL);
+	if (sb->u.ext3_sb.s_group_desc == NULL) {
+		printk ("EXT3-fs: not enough memory\n");
+		goto failed_mount;
+	}
+	for (i = 0; i < db_count; i++) {
+		sb->u.ext3_sb.s_group_desc[i] = bread (dev, logic_sb_block + i + 1,
+						       sb->s_blocksize);
+		if (!sb->u.ext3_sb.s_group_desc[i]) {
+			for (j = 0; j < i; j++)
+				brelse (sb->u.ext3_sb.s_group_desc[j]);
+			kfree_s (sb->u.ext3_sb.s_group_desc,
+				 db_count * sizeof (struct buffer_head *));
+			printk ("EXT3-fs: unable to read group descriptors\n");
+			goto failed_mount;
+		}
+	}
+	if (!ext3_check_descriptors (sb)) {
+		for (j = 0; j < db_count; j++)
+			brelse (sb->u.ext3_sb.s_group_desc[j]);
+		kfree_s (sb->u.ext3_sb.s_group_desc,
+			 db_count * sizeof (struct buffer_head *));
+		printk ("EXT3-fs: group descriptors corrupted !\n");
+		goto failed_mount;
+	}
+	for (i = 0; i < EXT3_MAX_GROUP_LOADED; i++) {
+		sb->u.ext3_sb.s_inode_bitmap_number[i] = 0;
+		sb->u.ext3_sb.s_inode_bitmap[i] = NULL;
+		sb->u.ext3_sb.s_block_bitmap_number[i] = 0;
+		sb->u.ext3_sb.s_block_bitmap[i] = NULL;
+	}
+	sb->u.ext3_sb.s_loaded_inode_bitmaps = 0;
+	sb->u.ext3_sb.s_loaded_block_bitmaps = 0;
+	sb->u.ext3_sb.s_db_per_group = db_count;
+	/*
+	 * set up enough so that it can read an inode
+	 */
+	sb->s_dev = dev;
+	sb->s_op = &ext3_sops;
+	INIT_LIST_HEAD(&sb->u.ext3_sb.s_orphan); /* unlinked but open files */
+	unlock_super (sb);
+
+	err = 0;
+	sb->s_root = 0;
+
+	/*
+	 * The first inode we look at is the journal inode.  Don't try
+	 * root first: it may be modified in the journal!
+	 */
+	if (!test_opt(sb, NOLOAD) &&
+	    (sb->u.ext3_sb.s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
+		if (ext3_load_journal(sb, es))
+			goto error_out;
+	} else if (journal_inum) {
+		if (ext3_create_journal(sb, es, journal_inum))
+			goto error_out;
+	} else {
+		printk (KERN_ERR "ext3: No journal on filesystem on %s\n",
+			kdevname(dev));
+		goto error_out;
+	}
+
+	/* We have now updated the journal if required, so we can
+	 * validate the data journaling mode. */
+	switch (test_opt(sb, DATA_FLAGS)) {
+	case 0:
+		/* No mode set, assume a default based on the journal
+                   capabilities: WRITEBACK_DATA if the journal can
+                   cope, else JOURNAL_DATA */
+		if (journal_check_features(EXT3_SB(sb)->s_journal, 0, 0, 
+					   JFS_FEATURE_INCOMPAT_REVOKE))
+			set_opt(EXT3_SB(sb)->s_mount_opt, WRITEBACK_DATA);
+		else 
+			set_opt(EXT3_SB(sb)->s_mount_opt, JOURNAL_DATA);
+		break;
+	case EXT3_MOUNT_ORDERED_DATA:
+	case EXT3_MOUNT_WRITEBACK_DATA:
+		if (!journal_check_features(EXT3_SB(sb)->s_journal, 0, 0, 
+					    JFS_FEATURE_INCOMPAT_REVOKE)) {
+			printk(KERN_ERR "EXT3-fs: Journal does not support "
+			       "requested data journaling mode\n");
+			journal_release(EXT3_SB(sb)->s_journal);
+			goto error_out;
+		}
+	default:
+	}
+		
+	/*
+	 * The journal_load will have done any necessary log recovery,
+	 * so we can safely mount the rest of the filesystem now.  
+	 */
+	sb->s_root = d_alloc_root(iget(sb, EXT3_ROOT_INO), NULL);
+	if (!sb->s_root) {
+		journal_release(EXT3_SB(sb)->s_journal);
+		goto error_out;
+	}
+			
+	ext3_setup_super(sb, es);
+	ext3_orphan_cleanup(sb, es);
+	ext3_mark_recovery_complete(sb, es);
+	printk (KERN_INFO "EXT3-fs: recovery complete.\n");
+	return sb;
+
+ error_out:
+	sb->s_dev = 0;
+
+	for (i = 0; i < db_count; i++)
+		if (sb->u.ext3_sb.s_group_desc[i])
+			brelse (sb->u.ext3_sb.s_group_desc[i]);
+	kfree_s (sb->u.ext3_sb.s_group_desc,
+		 db_count * sizeof (struct buffer_head *));
+	brelse (bh);
+	printk ("EXT3-fs: get root inode failed\n");
+	MOD_DEC_USE_COUNT;
+	return NULL;
+}
+
+static journal_t *ext3_get_journal(struct super_block *sb, int journal_inum)
+{
+	struct inode *journal_inode;
+	journal_t *journal;
+
+	/* First, test for the existence of a valid inode on disk.  Bad
+	 * things happen if we iget() an unused inode, as the subsequent
+	 * iput() will try to delete it. */
+
+	journal_inode = iget(sb, journal_inum);
+	if (!journal_inode) {
+		printk("EXT3-fs: no journal found.\n");
+		return NULL;
+	}
+	if (!journal_inode->i_nlink) {
+		make_bad_inode(journal_inode);
+		iput(journal_inode);
+		printk("EXT3-fs: journal inode is deleted.\n");
+		return NULL;
+	}
+
+	jfs_debug(2, "Journal inode found at %p: %ld bytes\n",
+		  journal_inode, journal_inode->i_size);
+	if (is_bad_inode(journal_inode) || !S_ISREG(journal_inode->i_mode)) {
+		printk("EXT3-fs: invalid journal inode.\n");
+		iput(journal_inode);
+		return NULL;
+	}
+
+	journal = journal_init_inode(journal_inode);
+	if (!journal)
+		iput(journal_inode);
+	if (sb->s_flags & MS_SYNCHRONOUS)
+		journal->j_flags |= JFS_SYNC;
+	return journal;
+}
+
+static int ext3_load_journal(struct super_block * sb,
+			     struct ext3_super_block * es)
+{
+	journal_t *journal;
+	int journal_inum = le32_to_cpu(es->s_journal_inum);
+	int err;
+
+	/*
+	 * Are we loading a blank journal or performing recovery after a
+	 * crash?  For recovery, we need to check in advance whether we
+	 * can get read-write access to the device.
+	 */
+
+	if (es->s_feature_incompat & cpu_to_le32(EXT3_FEATURE_INCOMPAT_RECOVER)) {
+		if (sb->s_flags & MS_RDONLY) {
+			printk(KERN_ERR "EXT3-fs: WARNING: recovery required on readonly filesystem.\n");
+			if (is_read_only(sb->s_dev)) {
+				printk(KERN_ERR "EXT3-fs: write access unavailable, cannot proceed.\n");
+				return -EROFS;
+			}
+			printk (KERN_ERR "EXT3-fs: write access will be enabled during recovery.\n");
+		}
+	}
+	
+	if (!(journal = ext3_get_journal(sb, journal_inum)))
+		return -EINVAL;
+
+	if (test_opt(sb, UPDATE_JOURNAL)) {
+		err = journal_update_format(journal);
+		if (err)  {
+			printk(KERN_ERR "EXT3-fs: error updating journal.\n");
+			journal_release(journal);
+			return err;
+		}
+	}
+
+	err = journal_load(journal);
+	if (err) {
+		printk(KERN_ERR "EXT3-fs: error loading journal.\n");
+		journal_release(journal);
+		return err;
+	}
+
+	EXT3_SB(sb)->s_journal = journal;
+	ext3_clear_journal_err(sb, es);
+	return 0;
+}
+
+static int ext3_create_journal(struct super_block * sb,
+			       struct ext3_super_block * es,
+			       int journal_inum)
+{
+	journal_t *journal;
+		
+	if (sb->s_flags & MS_RDONLY) {
+		printk("EXT3-fs: readonly filesystem when trying to create journal.\n");
+		return -EROFS;
+	}
+	
+	if (!(journal = ext3_get_journal(sb, journal_inum)))
+		return -EINVAL;
+
+	if (journal_create(journal)) {
+		printk("EXT3-fs: error creating journal.\n");
+		journal_release(journal);
+		return -EIO;
+	}
+
+	EXT3_SB(sb)->s_journal = journal;
+	if (sb->s_flags & MS_SYNCHRONOUS)
+		journal->j_flags |= JFS_SYNC;
+	
+	sb->u.ext3_sb.s_feature_incompat |= EXT3_FEATURE_INCOMPAT_RECOVER;
+	es->s_feature_incompat = cpu_to_le32(sb->u.ext3_sb.s_feature_incompat);
+	
+	sb->u.ext3_sb.s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+	es->s_feature_compat = cpu_to_le32(sb->u.ext3_sb.s_feature_compat);
+	
+	es->s_journal_inum = cpu_to_le32(journal_inum);
+	sb->s_dirt = 1;
+
+	/* Make sure we flush the recovery flag to disk. */
+	ext3_commit_super(sb, es, 1);
+	
+	return 0;
+}
+
+static void ext3_commit_super (struct super_block * sb,
+			       struct ext3_super_block * es, 
+			       int sync)
+{
+	es->s_wtime = cpu_to_le32(CURRENT_TIME);
+	mark_buffer_dirty(sb->u.ext3_sb.s_sbh, 1);
+	if (sync) {
+		ll_rw_block(WRITE, 1, &sb->u.ext3_sb.s_sbh);
+		wait_on_buffer(sb->u.ext3_sb.s_sbh);
+	}
+}
+
+
+/* 
+ * Have we just finished recovery?  If so, and if we are mounting the
+ * filesystem readonly, then we will end up with a consistent fs on
+ * disk.  Record that fact if so.  
+ */
+static void ext3_mark_recovery_complete(struct super_block * sb,
+					struct ext3_super_block * es)
+{
+	journal_flush(EXT3_SB(sb)->s_journal);
+	if (EXT3_HAS_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER)) {
+		if (sb->s_flags & MS_RDONLY) {
+			EXT3_SB(sb)->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER;
+			es->s_feature_incompat = cpu_to_le32(EXT3_SB(sb)->s_feature_incompat);
+			es->s_mtime = cpu_to_le32(CURRENT_TIME);
+			ext3_commit_super(sb, es, 1);
+			sb->s_dirt = 0;
+		}
+	}
+}
+
+/*
+ * If we are mounting (or read-write remounting) a filesystem whose journal
+ * has recorded an error from a previous lifetime, move that error to the
+ * main filesystem now. 
+ */
+static void ext3_clear_journal_err(struct super_block * sb,
+				   struct ext3_super_block * es)
+{
+	journal_t *journal;
+	int j_errno;
+	
+	journal = EXT3_SB(sb)->s_journal;
+	
+	/*
+	 * Now check for any error status which may have been recorded in the 
+	 * journal by a prior ext3_panic()
+	 */
+
+	j_errno = journal_errno(journal);
+	if (j_errno) {
+		ext3_warning(sb, __FUNCTION__, 
+			     "detected error %d from previous mount", 
+			     j_errno);
+
+		sb->u.ext3_sb.s_mount_state |= EXT3_ERROR_FS;
+		sb->u.ext3_sb.s_es->s_state |= cpu_to_le16(EXT3_ERROR_FS);
+		ext3_commit_super (sb, es, 1);
+
+		journal_clear_err(journal);
+	}
+}
+	
+
+/*
+ * 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 EXT3_VALID_FS after some corrections.
+ */
+
+void ext3_write_super (struct super_block * sb)
+{
+	tid_t wait_tid;
+
+	sb->s_dirt = 0;
+	
+	if (!(sb->s_flags & MS_RDONLY)) {
+		journal_t *journal;
+		
+		journal = EXT3_SB(sb)->s_journal;
+
+		if (journal->j_running_transaction) {
+			wait_tid = journal->j_running_transaction->t_tid;
+			log_start_commit(journal, journal->j_running_transaction);
+			log_wait_commit(journal, wait_tid);
+		} else if (journal->j_committing_transaction)
+			log_wait_commit(journal, journal->j_committing_transaction->t_tid);
+	}
+}
+
+int ext3_remount (struct super_block * sb, int * flags, char * data)
+{
+	struct ext3_super_block * es;
+	unsigned short resuid = sb->u.ext3_sb.s_resuid;
+	unsigned short resgid = sb->u.ext3_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 = sb->u.ext3_sb.s_mount_opt;
+	if (!parse_options (data, &tmp, &resuid, &resgid,
+			    &new_mount_opt, &tmp, 1))
+		return -EINVAL;
+
+	if (new_mount_opt & EXT3_MOUNT_ABORT)
+		ext3_abort(sb, __FUNCTION__, "Abort forced by user");
+	
+	sb->u.ext3_sb.s_mount_opt = new_mount_opt;
+	sb->u.ext3_sb.s_resuid = resuid;
+	sb->u.ext3_sb.s_resgid = resgid;
+	es = sb->u.ext3_sb.s_es;
+	if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
+		return 0;
+
+	if (sb->u.ext3_sb.s_mount_opt & EXT3_MOUNT_ABORT)
+		return -EROFS;
+	
+	if (*flags & MS_RDONLY) {
+		/* 
+		 * First of all, the unconditional stuff we have to do
+		 * to disable replay of the journal when we next remount
+		 */
+		sb->s_flags |= MS_RDONLY;
+
+		/*
+		 * OK, test if we are remounting a valid rw partition
+		 * rdonly, and if so set the rdonly flag and then mark the
+		 * partition as valid again.  
+		 */
+		if (!(le16_to_cpu(es->s_state) & EXT3_VALID_FS) &&
+		    (sb->u.ext3_sb.s_mount_state & EXT3_VALID_FS))
+			es->s_state = cpu_to_le16(sb->u.ext3_sb.s_mount_state);
+
+		ext3_mark_recovery_complete(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.)
+		 */
+		ext3_clear_journal_err(sb, es);
+		sb->u.ext3_sb.s_mount_state = le16_to_cpu(es->s_state);
+		sb->s_flags &= ~MS_RDONLY;
+		ext3_setup_super (sb, es);
+		sb->s_dirt = 1;
+	}
+	return 0;
+}
+
+static struct file_system_type ext3_fs_type = {
+	"ext3", 
+	FS_REQUIRES_DEV /* | FS_IBASKET */,	/* ibaskets have unresolved bugs */
+        ext3_read_super, 
+	NULL
+};
+
+__initfunc(int init_ext3_fs(void))
+{
+        return register_filesystem(&ext3_fs_type);
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+	return init_ext3_fs();
+}
+
+void cleanup_module(void)
+{
+        unregister_filesystem(&ext3_fs_type);
+}
+
+#endif
+
+int ext3_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.ext3_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.ext3_sb.s_feature_ro_compat &
+		    EXT3_FEATURE_RO_COMPAT_SPARSE_SUPER) {
+			ngroups = 0;
+			for (i=0 ; i < sb->u.ext3_sb.s_groups_count; i++)
+				if (ext3_group_sparse(i))
+					ngroups++;
+		} else
+			ngroups = sb->u.ext3_sb.s_groups_count;
+		overhead += ngroups * (1 + sb->u.ext3_sb.s_db_per_group);
+
+		/*
+		 * Every block group has an inode bitmap, a block
+		 * bitmap, and an inode table.
+		 */
+		overhead += (sb->u.ext3_sb.s_groups_count *
+			     (2 + sb->u.ext3_sb.s_itb_per_group));
+	}
+
+	tmp.f_type = EXT3_SUPER_MAGIC;
+	tmp.f_bsize = sb->s_blocksize;
+	tmp.f_blocks = le32_to_cpu(sb->u.ext3_sb.s_es->s_blocks_count) - overhead;
+	tmp.f_bfree = ext3_count_free_blocks (sb);
+	tmp.f_bavail = tmp.f_bfree - le32_to_cpu(sb->u.ext3_sb.s_es->s_r_blocks_count);
+	if (tmp.f_bfree < le32_to_cpu(sb->u.ext3_sb.s_es->s_r_blocks_count))
+		tmp.f_bavail = 0;
+	tmp.f_files = le32_to_cpu(sb->u.ext3_sb.s_es->s_inodes_count);
+	tmp.f_ffree = ext3_count_free_inodes (sb);
+	tmp.f_namelen = EXT3_NAME_LEN;
+	return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+}
--- linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/symlink.c.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/symlink.c	Wed Oct 25 19:10:56 2000
@@ -0,0 +1,109 @@
+/*
+ *  linux/fs/ext3/symlink.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/symlink.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  ext3 symlink handling code
+ */
+
+#include <asm/uaccess.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ext3_fs.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/stat.h>
+
+static int ext3_readlink (struct dentry *, char *, int);
+static struct dentry *ext3_follow_link(struct dentry *, struct dentry *, unsigned int);
+
+/*
+ * symlinks can't do much...
+ */
+struct inode_operations ext3_symlink_inode_operations = {
+	NULL,			/* no file-operations */
+	NULL,			/* create */
+	NULL,			/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	ext3_readlink,		/* readlink */
+	ext3_follow_link,	/* follow_link */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL,			/* permission */
+	NULL			/* smap */
+};
+
+static struct dentry * ext3_follow_link(struct dentry * dentry,
+					struct dentry *base,
+					unsigned int follow)
+{
+	struct inode *inode = dentry->d_inode;
+	struct buffer_head * bh = NULL;
+	int error;
+	char * link;
+
+	link = (char *) inode->u.ext3_i.i_data_u.i_data;
+	if (inode->i_blocks) {
+		if (!(bh = ext3_bread (NULL, inode, 0, 0, &error))) {
+			dput(base);
+			return ERR_PTR(-EIO);
+		}
+		link = bh->b_data;
+	}
+	UPDATE_ATIME(inode);
+	base = lookup_dentry(link, base, follow);
+	if (bh)
+		brelse(bh);
+	return base;
+}
+
+static int ext3_readlink (struct dentry * dentry, char * buffer, int buflen)
+{
+	struct inode *inode = dentry->d_inode;
+	struct buffer_head * bh = NULL;
+	char * link;
+	int i;
+
+	if (buflen > inode->i_sb->s_blocksize - 1)
+		buflen = inode->i_sb->s_blocksize - 1;
+
+	link = (char *) inode->u.ext3_i.i_data_u.i_data;
+	if (inode->i_blocks) {
+		int err;
+		bh = ext3_bread (NULL, inode, 0, 0, &err);
+		if (!bh) {
+			if(err < 0) /* indicate type of error */
+				return err;
+			return 0;
+		}
+		link = bh->b_data;
+	}
+
+	i = 0;
+	while (i < buflen && link[i])
+		i++;
+	if (copy_to_user(buffer, link, i))
+		i = -EFAULT;
+ 	UPDATE_ATIME(inode);
+	if (bh)
+		brelse (bh);
+	return i;
+}
--- linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/truncate.c.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/ext3/truncate.c	Wed Oct 25 19:10:56 2000
@@ -0,0 +1,543 @@
+/*
+ *  linux/fs/ext3/truncate.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/truncate.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  Big-endian to little-endian byte-swapping/bitmaps by
+ *        David S. Miller (davem@caip.rutgers.edu), 1995
+ *
+ *  General cleanup and race fixes, wsh, 1998
+ */
+
+/*
+ * Real random numbers for secure rm added 94/02/18
+ * Idea from Pierre del Perugia <delperug@gla.ecoledoc.ibp.fr>
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/ext3_fs.h>
+#include <linux/ext3_jfs.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+#include <linux/string.h>
+
+#if 0
+
+/*
+ * Secure deletion currently doesn't work. It interacts very badly
+ * with buffers shared with memory mappings, and for that reason
+ * can't be done in the truncate() routines. It should instead be
+ * done separately in "release()" before calling the truncate routines
+ * that will release the actual file blocks.
+ *
+ *		Linus
+ */
+static int ext3_secrm_seed = 152;	/* Random generator base */
+
+#define RANDOM_INT (ext3_secrm_seed = ext3_secrm_seed * 69069l +1)
+#endif
+
+/*
+ * Macros to return the block number for the inode size and offset.
+ * Currently we always hold the inode semaphore during truncate, so
+ * there's no need to test for changes during the operation.
+ */
+#define DIRECT_BLOCK(inode) \
+	((inode->i_size + inode->i_sb->s_blocksize - 1) / \
+			  inode->i_sb->s_blocksize)
+#define INDIRECT_BLOCK(inode,offset) ((int)DIRECT_BLOCK(inode) - offset)
+#define DINDIRECT_BLOCK(inode,offset) \
+	(INDIRECT_BLOCK(inode,offset) / addr_per_block)
+#define TINDIRECT_BLOCK(inode,offset) \
+	(INDIRECT_BLOCK(inode,offset) / (addr_per_block*addr_per_block))
+
+/*
+ * Truncate has the most races in the whole filesystem: coding it is
+ * a pain in the a**. Especially as I don't do any locking...
+ *
+ * The code may look a bit weird, but that's just because I've tried to
+ * handle things like file-size changes in a somewhat graceful manner.
+ * Anyway, truncating a file at the same time somebody else writes to it
+ * is likely to result in pretty weird behaviour...
+ *
+ * The new code handles normal truncates (size = 0) as well as the more
+ * general case (size = XXX). I hope.
+ *
+ *
+ * Truncate operations have been rewritten to avoid various races. The
+ * previous code was allowing blocking operations to precede a call to
+ * bforget(), possible allowing the buffer to be used again.
+ *
+ * We now ensure that b_count == 1 before calling bforget() and that the
+ * parent buffer (if any) is unlocked before clearing the block pointer.
+ * The operations are always performed in this order:
+ *	(1) Make sure that the parent buffer is unlocked.
+ *	(2) Use find_buffer() to find the block buffer without blocking,
+ *	    and set 'retry' if the buffer is locked or b_count > 1.
+ *	(3) Clear the block pointer in the parent (buffer or inode).
+ *	(4) Update the inode block count and mark the inode dirty.
+ *	(5) Forget the block buffer, if any. This call won't block, as
+ *	    we know the buffer is unlocked from (2).
+ *	(6) If the block pointer is in a (parent) buffer, mark the buffer
+ *	    dirty. (Note that this can block on a loop device.)
+ *	(7) Accumulate the blocks to free and/or update the block bitmap.
+ *	    (This operation will frequently block.)
+ *
+ * The requirement that parent buffers be unlocked follows from the general
+ * principle of not modifying a buffer that may be undergoing I/O. With the
+ * the present kernels there's no problem with modifying a locked inode, as
+ * the I_DIRTY bit is cleared before setting I_LOCK.
+ *		-- WSH, 1998
+ */
+
+
+/* 
+ * The journaling doesn't have to break the rules above, as long as we
+ * do a journal_get_write_access() on the appropriate indirect blocks
+ * before entering the find_buffer().   
+ * 
+ * However, there are some new complications.  truncate transactions can
+ * be complex and unbounded in length, so we need to be able to restart
+ * the transaction at a conventient checkpoint to make sure we don't
+ * overflow the journal.
+ *
+ * start_transaction gets us a new handle for a truncate transaction,
+ * and extend_transaction tries to extend the existing one a bit.  If
+ * extend fails, we need to propagate the failure up and restart the
+ * transaction in the top-level truncate loop. --sct 
+ */
+
+static handle_t *start_transaction(struct inode *inode) 
+{
+	long needed;
+	needed = inode->i_blocks;
+	if (needed > EXT3_MAX_TRANS_DATA) 
+		needed = EXT3_MAX_TRANS_DATA;
+
+	return ext3_journal_start(inode, EXT3_DATA_TRANS_BLOCKS + needed);
+}
+
+static int extend_transaction(handle_t *handle, struct inode *inode)
+{
+	long needed;
+	
+	if (handle->h_buffer_credits > EXT3_RESERVE_TRANS_BLOCKS)
+		return 0;
+	needed = inode->i_blocks;
+	if (needed > EXT3_MAX_TRANS_DATA) 
+		needed = EXT3_MAX_TRANS_DATA;
+	if (!journal_extend(handle, EXT3_RESERVE_TRANS_BLOCKS + needed))
+		return 0;
+
+	jfs_debug(2, "extending handle %p\n", handle);
+	journal_restart(handle, EXT3_DATA_TRANS_BLOCKS + needed);
+	return 1;
+}
+
+	
+
+/*
+ * Check whether any of the slots in an indirect block are
+ * still in use, and if not free the block.
+ */
+static int check_block_empty(handle_t *handle, 
+			     struct inode *inode, struct buffer_head *bh,
+			     u32 *p, struct buffer_head *ind_bh)
+{
+	int addr_per_block = EXT3_ADDR_PER_BLOCK(inode->i_sb);
+	u32 * ind = (u32 *) bh->b_data;
+	int i, retry;
+
+	if (ind_bh)
+		journal_get_write_access(handle, ind_bh);
+	
+	/* Make sure both buffers are unlocked */
+	do {
+		retry = 0;
+		if (buffer_locked(bh)) {
+			__wait_on_buffer(bh);
+			retry = 1;
+		}
+		if (ind_bh && buffer_locked(ind_bh)) {
+			__wait_on_buffer(ind_bh);
+			retry = 1;
+		}
+	} while (retry);
+
+	for (i = 0; i < addr_per_block; i++)
+		if (*(ind++))
+			goto in_use;
+
+	if (!journal_is_buffer_shared(bh)) {
+		int tmp;
+		if (ind_bh)
+			tmp = le32_to_cpu(*p);
+		else
+			tmp = *p;
+		*p = 0;
+		inode->i_blocks -= (inode->i_sb->s_blocksize / 512);
+
+		/*
+		 * Forget the buffer, then mark the parent buffer dirty.
+		 */
+		journal_forget(handle, bh);
+		if (ind_bh)
+			journal_dirty_metadata(handle, ind_bh);
+		ext3_free_blocks (handle, inode, tmp, 1);
+		ext3_mark_inode_dirty(handle, inode);
+		goto out;
+	}
+	jfs_debug(4, "retry\n");
+	retry = 1;
+
+in_use:
+	brelse (bh);
+	if (ind_bh)
+		journal_release_buffer(handle, ind_bh);
+	
+out:
+	return retry;
+}
+
+static int trunc_direct (handle_t *handle, struct inode * inode)
+{
+	struct buffer_head * bh;
+	int i, retry = 0;
+	unsigned long block_to_free = 0, free_count = 0;
+	int blocks = inode->i_sb->s_blocksize / 512;
+	int direct_block = DIRECT_BLOCK(inode);
+	int dirty = 0;
+	
+	for (i = direct_block ; i < EXT3_NDIR_BLOCKS ; i++) {
+		u32 * p = inode->u.ext3_i.i_data_u.i_data + i;
+		int tmp = *p;
+
+		if (!tmp)
+			continue;
+
+		extend_transaction(handle, inode);
+		
+		bh = find_buffer(inode->i_dev, tmp, inode->i_sb->s_blocksize);
+		if (bh) {
+			bh->b_count++;
+			if(journal_is_buffer_shared(bh) || buffer_locked(bh)) {
+				brelse(bh);
+				retry = 1;
+				continue;
+			}
+		}
+		
+		*p = 0;
+		inode->i_blocks -= blocks;
+		dirty = 1;
+		if (bh)
+			journal_forget(handle, bh);
+
+		/* accumulate blocks to free if they're contiguous */
+		if (free_count == 0)
+			goto free_this;
+		else if (block_to_free == tmp - free_count)
+			free_count++;
+		else {
+			ext3_free_blocks (handle, inode, block_to_free, free_count);
+		free_this:
+			block_to_free = tmp;
+			free_count = 1;
+		}
+	}
+	if (free_count > 0)
+		ext3_free_blocks (handle, inode, block_to_free, free_count);
+
+	if (dirty)
+		ext3_mark_inode_dirty(handle, inode);
+	return retry;
+}
+
+static int trunc_indirect (handle_t *handle,
+			   struct inode * inode, int offset, u32 * p,
+			   struct buffer_head *dind_bh)
+{
+	struct buffer_head * ind_bh;
+	int i, tmp, retry = 0;
+	unsigned long block_to_free = 0, free_count = 0;
+	int indirect_block, addr_per_block, blocks;
+	int dirty = 0;
+	
+	tmp = dind_bh ? le32_to_cpu(*p) : *p;
+	if (!tmp)
+		return 0;
+	ind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
+	if (tmp != (dind_bh ? le32_to_cpu(*p) : *p)) {
+		brelse (ind_bh);
+		return 1;
+	}
+
+	/* A read failure? Report error and clear slot (should be rare). */ 
+	if (!ind_bh) {
+		ext3_error(inode->i_sb, "trunc_indirect",
+			"Read failure, inode=%ld, block=%d",
+			inode->i_ino, tmp);
+		if (dind_bh) {
+			journal_get_write_access(handle, dind_bh);
+			*p = 0;
+			journal_dirty_metadata(handle, dind_bh);
+		} else {
+			*p = 0;
+			ext3_mark_inode_dirty(handle, inode);
+		}
+		return 0;
+	}
+
+	journal_get_write_access(handle, ind_bh);
+
+	blocks = inode->i_sb->s_blocksize / 512;
+	addr_per_block = EXT3_ADDR_PER_BLOCK(inode->i_sb);
+	indirect_block = INDIRECT_BLOCK(inode, offset);
+	if (indirect_block < 0)
+		indirect_block = 0;
+	for (i = indirect_block ; i < addr_per_block ; i++) {
+		u32 * ind = i + (u32 *) ind_bh->b_data;
+		struct buffer_head * bh;
+
+		wait_on_buffer(ind_bh);
+		tmp = le32_to_cpu(*ind);
+		if (!tmp)
+			continue;
+
+		if (extend_transaction(handle, inode))
+			journal_get_write_access(handle, ind_bh);
+		/*
+		 * Use find_buffer so we don't block here.
+		 */
+		bh = find_buffer(inode->i_dev, tmp, inode->i_sb->s_blocksize);
+		if (bh) {
+			bh->b_count++;
+			if (journal_is_buffer_shared(bh) || buffer_locked(bh)) {
+				brelse (bh);
+				retry = 1;
+				continue;
+			}
+		}
+
+		*ind = 0;
+		inode->i_blocks -= blocks;
+		dirty = 1;
+		if (bh)
+			journal_forget(handle, bh);
+
+		/* accumulate blocks to free if they're contiguous */
+		if (free_count == 0)
+			goto free_this;
+		else if (block_to_free == tmp - free_count)
+			free_count++;
+		else {
+			ext3_free_blocks (handle, inode, block_to_free, free_count);
+		free_this:
+			block_to_free = tmp;
+			free_count = 1;
+		}
+	}
+	if (free_count > 0)
+		ext3_free_blocks (handle, inode, block_to_free, free_count);
+
+	if (dirty) {
+		journal_dirty_metadata(handle, ind_bh);
+		ext3_mark_inode_dirty(handle, inode);
+	}
+	
+	/*
+	 * Check the block and dispose of the ind_bh buffer.
+	 */
+	retry |= check_block_empty(handle, inode, ind_bh, p, dind_bh);
+
+	return retry;
+}
+
+static int trunc_dindirect (handle_t *handle,
+			    struct inode * inode, int offset, u32 * p,
+			    struct buffer_head * tind_bh)
+{
+	struct buffer_head * dind_bh;
+	int i, tmp, retry = 0;
+	int dindirect_block, addr_per_block;
+
+	tmp = tind_bh ? le32_to_cpu(*p) : *p;
+	if (!tmp)
+		return 0;
+	dind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
+	if (tmp != (tind_bh ? le32_to_cpu(*p) : *p)) {
+		brelse (dind_bh);
+		return 1;
+	}
+	/* A read failure? Report error and clear slot (should be rare). */ 
+	if (!dind_bh) {
+		ext3_error(inode->i_sb, "trunc_dindirect",
+			"Read failure, inode=%ld, block=%d",
+			inode->i_ino, tmp);
+		if (tind_bh) {
+			journal_get_write_access(handle, tind_bh);
+			*p = 0;
+			journal_dirty_metadata(handle, tind_bh);
+		} else {
+			*p = 0;
+			ext3_mark_inode_dirty(handle, inode);
+		}
+		return 0;
+	}
+
+	addr_per_block = EXT3_ADDR_PER_BLOCK(inode->i_sb);
+	dindirect_block = DINDIRECT_BLOCK(inode, offset);
+	if (dindirect_block < 0)
+		dindirect_block = 0;
+	for (i = dindirect_block ; i < addr_per_block ; i++) {
+		u32 * dind = i + (u32 *) dind_bh->b_data;
+
+		retry |= trunc_indirect(handle, inode,
+					offset + (i * addr_per_block),
+					dind, dind_bh);
+	}
+	/*
+	 * Check the block and dispose of the dind_bh buffer.
+	 */
+	retry |= check_block_empty(handle, inode, dind_bh, p, tind_bh);
+
+	return retry;
+}
+
+static int trunc_tindirect (handle_t *handle, struct inode * inode)
+{
+	u32 * p = inode->u.ext3_i.i_data_u.i_data + EXT3_TIND_BLOCK;
+	struct buffer_head * tind_bh;
+	int i, tmp, retry = 0;
+	int tindirect_block, addr_per_block, offset;
+
+	if (!(tmp = *p))
+		return 0;
+	tind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
+	if (tmp != *p) {
+		brelse (tind_bh);
+		return 1;
+	}
+	/* A read failure? Report error and clear slot (should be rare). */ 
+	if (!tind_bh) {
+		ext3_error(inode->i_sb, "trunc_tindirect",
+			"Read failure, inode=%ld, block=%d",
+			inode->i_ino, tmp);
+		*p = 0;
+		ext3_mark_inode_dirty(handle, inode);
+		return 0;
+	}
+
+	addr_per_block = EXT3_ADDR_PER_BLOCK(inode->i_sb);
+	offset = EXT3_NDIR_BLOCKS + addr_per_block + 
+		(addr_per_block * addr_per_block);
+	tindirect_block = TINDIRECT_BLOCK(inode, offset);
+	if (tindirect_block < 0)
+		tindirect_block = 0;
+	for (i = tindirect_block ; i < addr_per_block ; i++) {
+		u32 * tind = i + (u32 *) tind_bh->b_data;
+
+		retry |= trunc_dindirect(handle, inode,
+				offset + (i * addr_per_block * addr_per_block),
+				tind, tind_bh);
+	}
+	/*
+	 * Check the block and dispose of the tind_bh buffer.
+	 */
+	retry |= check_block_empty(handle, inode, tind_bh, p, NULL);
+
+	return retry;
+}
+
+void ext3_truncate (struct inode * inode)
+{
+	int err, offset, retry;
+	handle_t *handle;
+	
+	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+	    S_ISLNK(inode->i_mode)))
+		return;
+	if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+		return;
+	
+	ext3_discard_prealloc(inode);
+
+	handle = start_transaction(inode);
+	if (IS_ERR(handle))
+		return PTR_ERR(handle);
+	
+	/* Add inode to orphan list, so that if this truncate spans multiple
+	 * transactions, and we crash and don't recover the last transaction
+	 * we will resume the truncate when the filesystem recovers.
+	 */
+	ext3_orphan_add(handle, inode);
+	ext3_mark_inode_dirty(handle, inode);
+
+	/* The orphan list will now protect us from a crash before the
+	 * truncate completes, so it is finally safe to propagate the
+	 * new inode size (held for now in i_size) into the on-disk
+	 * inode. */
+	inode->u.ext3_i.i_disksize = inode->i_size;
+
+	while (1) {
+		retry = trunc_direct(handle, inode);
+		retry |= trunc_indirect (handle, inode, 
+				EXT3_IND_BLOCK,
+				(u32 *) &inode->u.ext3_i.i_data_u.i_data[EXT3_IND_BLOCK],
+				NULL);
+		retry |= trunc_dindirect (handle, inode,
+				EXT3_IND_BLOCK+EXT3_ADDR_PER_BLOCK(inode->i_sb),
+				(u32 *)&inode->u.ext3_i.i_data_u.i_data[EXT3_DIND_BLOCK],
+				NULL);
+		retry |= trunc_tindirect (handle, inode);
+		if (!retry)
+			break;
+		ext3_journal_stop(handle, inode);
+		current->counter = 0;
+		run_task_queue(&tq_disk);
+		current->policy |= SCHED_YIELD;
+		schedule ();
+		handle = start_transaction(inode);
+		if (IS_ERR(handle))
+			return PTR_ERR(handle);
+	}
+	/*
+	 * If the file is not being truncated to a block boundary, the
+	 * contents of the partial block following the end of the file
+	 * must be zeroed in case it ever becomes accessible again due
+	 * to subsequent file growth.
+	 */
+	offset = inode->i_size & (inode->i_sb->s_blocksize - 1);
+	if (offset) {
+		struct buffer_head * bh;
+		bh = ext3_bread (NULL, inode,
+				 inode->i_size >> EXT3_BLOCK_SIZE_BITS(inode->i_sb),
+				 0, &err);
+		if (bh) {
+			journal_get_write_access(handle, bh);
+			memset (bh->b_data + offset, 0,
+				inode->i_sb->s_blocksize - offset);
+			journal_dirty_metadata(handle, bh); /* @@@ DATA! */
+			brelse (bh);
+		}
+	}
+	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	if (inode->i_nlink) {
+		ext3_orphan_del(handle, inode);
+		NEXT_ORPHAN(inode) = 0;
+	}
+	ext3_mark_inode_dirty(handle, inode);
+	ext3_journal_stop(handle, inode);
+}
--- linux-2.2.18pre17.ext3-0.0.5a/fs/filesystems.c.~1~	Wed Oct 25 18:57:53 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/filesystems.c	Wed Oct 25 19:10:56 2000
@@ -11,6 +11,7 @@
 
 #include <linux/minix_fs.h>
 #include <linux/ext2_fs.h>
+#include <linux/ext3_fs.h>
 #include <linux/msdos_fs.h>
 #include <linux/umsdos_fs.h>
 #include <linux/proc_fs.h>
@@ -54,6 +55,10 @@
 
 void __init filesystem_setup(void)
 {
+#ifdef CONFIG_EXT3_FS
+	init_ext3_fs();
+#endif
+
 #ifdef CONFIG_EXT2_FS
 	init_ext2_fs();
 #endif
--- linux-2.2.18pre17.ext3-0.0.5a/fs/jfs/Makefile.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/jfs/Makefile	Wed Oct 25 19:10:56 2000
@@ -0,0 +1,21 @@
+#
+# Makefile for the linux journaling routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := jfs.o
+O_OBJS   := transaction.o commit.o recovery.o checkpoint.o revoke.o
+
+M_OBJS   := $(O_TARGET)
+
+ifeq ($(CONFIG_MODULES),y)
+OX_OBJS  += journal.o
+else
+O_OBJS	 += journal.o
+endif
+
+include $(TOPDIR)/Rules.make
--- linux-2.2.18pre17.ext3-0.0.5a/fs/jfs/checkpoint.c.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/jfs/checkpoint.c	Wed Oct 25 19:10:56 2000
@@ -0,0 +1,436 @@
+/*
+ * linux/fs/checkpoint.c
+ * 
+ * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
+ *
+ * Copyright 1999 Red Hat Software --- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * Checkpoint routines for the generic filesystem journaling code.  
+ * Part of the ext2fs journaling system.  
+ *
+ * Checkpointing is the process of ensuring that a section of the log is
+ * committed fully to disk, so that that portion of the log can be
+ * reused.
+ */
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/jfs.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/locks.h>
+#include <linux/buffer.h>
+
+static int cleanup_journal_tail(journal_t *);
+
+/*
+ * Unlink a buffer from a transaction. 
+ */
+
+static inline void buffer_unlink(struct buffer_head *bh)
+{
+	transaction_t *transaction;
+	
+	transaction = bh->b_cp_transaction;
+	bh->b_cp_transaction = NULL;
+
+	bh->b_cpnext->b_cpprev = bh->b_cpprev;
+	bh->b_cpprev->b_cpnext = bh->b_cpnext;
+	if (transaction->t_checkpoint_list == bh)
+		transaction->t_checkpoint_list = bh->b_cpnext;
+	if (transaction->t_checkpoint_list == bh)
+		transaction->t_checkpoint_list = NULL;
+}
+ 
+
+/*
+ * log_wait_for_space: wait until there is space in the journal.
+ *
+ * Called with the journal already locked, but it will be unlocked if we have
+ * to wait for a checkpoint to free up some space in the log.
+ */
+
+void log_wait_for_space(journal_t *journal, int nblocks)
+{
+	while (log_space_left(journal) < nblocks) {
+		if (journal->j_flags & JFS_ABORT)
+			return;
+		unlock_journal(journal);
+		down(&journal->j_checkpoint_sem);
+		lock_journal(journal);
+		
+		/* Test again, another process may have checkpointed
+		 * while we were waiting for the checkpoint lock */
+		if (log_space_left(journal) < nblocks) {
+			log_do_checkpoint(journal, nblocks);
+		}
+		up(&journal->j_checkpoint_sem);
+	}
+}
+
+
+/*
+ * Clean up a transaction's checkpoint list.  
+ *
+ * We wait for any pending IO to complete and make sure any clean
+ * buffers are removed from the transaction. 
+ *
+ * Return 1 if we performed any actions which might have destroyed the
+ * checkpoint (refile_buffer deletes the transaction when the last
+ * checkpoint buffer is cleansed)
+ */
+
+static int cleanup_transaction(journal_t *journal, transaction_t *transaction)
+{
+	struct buffer_head *bh;
+	
+	bh = transaction->t_checkpoint_list;
+	if (!bh)
+		return 0;
+	
+	do {
+		if (buffer_locked(bh)) {
+			unlock_journal(journal);
+			bh->b_count++;
+			wait_on_buffer(bh);
+			brelse(bh);
+			lock_journal(journal);
+			return 1;
+		}
+		
+		if (bh->b_transaction != NULL) {
+			unlock_journal(journal);
+			log_wait_commit (journal,bh->b_transaction->t_tid);
+			lock_journal(journal);
+			return 1;
+		}
+		
+		if (!buffer_dirty(bh) && bh->b_list != BUF_CLEAN) {
+			unlock_journal(journal);
+			refile_buffer(bh);
+			lock_journal(journal);
+			return 1;
+		}
+		
+		bh = bh->b_cpnext;
+	} while (bh != transaction->t_checkpoint_list);
+	
+	return 0;
+}
+
+
+/*
+ * Try to flush one buffer from the checkpoint list to disk.
+ *
+ * Return 1 if something happened which requires us to abort the current
+ * scan of the checkpoint list.  
+ */
+static inline int flush_buffer(journal_t *journal, transaction_t *transaction,
+			       struct buffer_head *bh)
+{
+	/* 
+	 * Flush out only dirty, writable buffers.  
+	 */
+	
+	if (buffer_dirty(bh) && !buffer_locked(bh) && 
+	    bh->b_jlist == 0) {
+		J_ASSERT(bh->b_transaction == NULL);
+		
+		/*
+		 * Important: we are about to write the buffer, and
+		 * possibly block, while still holding the journal lock.
+		 * We cannot afford to let the transaction logic start
+		 * messing around with this buffer before we write it to
+		 * disk, as that would break recoverability.  
+		 */
+		
+		bh->b_count++;
+
+		J_ASSERT(!test_bit(BH_JWrite, &bh->b_state));
+		set_bit(BH_JWrite, &bh->b_state);
+		ll_rw_block(WRITE, 1, &bh);
+		clear_bit(BH_JWrite, &bh->b_state);
+				
+		/* We may have blocked, so check whether our context is
+		 * still valid and start over the transaction list again
+		 * if not. */
+				
+		if (transaction != journal->j_checkpoint_transactions ||
+		    bh->b_cp_transaction != transaction) {
+			brelse(bh);
+			return 1;
+		}
+		bh->b_count--; /* NOT brelse: don't block! */
+	}
+	return 0;
+}
+
+	
+/*
+ * Perform an actual checkpoint.  We don't write out only enough to
+ * satisfy the current blocked requests: rather we submit a reasonbly
+ * sized chunk of the outstanding data to disk at once for
+ * efficiency.  log_wait_for_space() will retry if we didn't free enough.
+ * 
+ * However, we _do_ take into account the amount requested so that once
+ * the IO has been queued, we can return as soon as enough of it has
+ * completed to disk.  
+ *
+ * The journal should be locked before calling this function.
+ */
+
+int log_do_checkpoint (journal_t *journal, int nblocks)
+{
+	transaction_t * transaction;
+	int result;
+	int target;
+	struct buffer_head *bh;
+
+	jfs_debug(1, "Start checkpoint\n");
+
+	/* How many buffers will we try to write at once? */
+	#define MAXBUFS 128
+	
+	/* 
+	 * First thing: if there are any transactions in the log which
+	 * don't need checkpointing, just eliminate them from the
+	 * journal straight away.  
+	 */
+	
+	result = cleanup_journal_tail(journal);
+	jfs_debug(1, "cleanup_journal_tail returned %d\n", result);
+	if (result <= 0)
+		return result;
+
+	/*
+	 * OK, we need to start writing disk blocks.  Try to free up a
+	 * quarter of the log in a single checkpoint if we can.
+	 */
+
+	target = (journal->j_last - journal->j_first) / 4;
+	
+ repeat:
+	while (target > 0 && 
+	       (transaction = journal->j_checkpoint_transactions) != NULL) {
+
+		/* 
+		 * We'll remove all of the buffers from the transaction
+		 * first, and then flush them to disk once we are no
+		 * longer worried about the effect of refile_buffer
+		 * trying to checkpoint-unlink the buffers.
+		 */
+		
+		bh = transaction->t_checkpoint_list;
+		J_ASSERT (bh != NULL);
+		
+		do {
+			if (flush_buffer(journal, transaction, bh))
+				goto repeat;
+			bh = bh->b_cpnext;
+		} while (bh != transaction->t_checkpoint_list);
+
+		/*
+		 * We have walked the whole transaction list without
+		 * finding anything to write to disk.  We had better be
+		 * able to make some progress or we are in trouble. 
+		 */
+			
+		J_ASSERT(cleanup_transaction(journal, transaction));
+	}
+
+	result = cleanup_journal_tail(journal);
+	if (result < 0)
+		return result;
+	
+	return 0;
+}
+
+
+/*
+ * Check the list of checkpoint transactions for the journal to see if
+ * we have already got rid of any since the last update of the log tail
+ * in the journal superblock.  If so, we can instantly roll the
+ * superblock forward to remove those transactions from the log.
+ * 
+ * Return <0 on error, 0 on success, 1 if there was nothing to clean up.
+ * 
+ * Called with the journal lock held.
+ *
+ * This is the only part of the journaling code which really needs to be
+ * aware of transaction aborts.  Checkpointing involves writing to the
+ * main filesystem area rather than to the journal, so it can proceed
+ * even in abort state, but we must not update the journal superblock if
+ * we have an abort error outstanding.
+ */
+
+
+static int cleanup_journal_tail(journal_t *journal)
+{
+	transaction_t * transaction;
+	tid_t		first_tid;
+	unsigned long	blocknr, freed;
+
+	/* OK, work out the oldest transaction remaining in the log, and
+	 * the log block it starts at. 
+	 * 
+	 * If the log is now empty, we need to work out which is the
+	 * next transaction ID we will write, and where it will
+	 * start. */
+
+	transaction = journal->j_checkpoint_transactions;
+	if (transaction) {
+		first_tid = transaction->t_tid;
+		blocknr = transaction->t_log_start;
+	} else if ((transaction = journal->j_committing_transaction) != NULL) {
+		first_tid = transaction->t_tid;
+		blocknr = transaction->t_log_start;
+	} else if ((transaction = journal->j_running_transaction) != NULL) {
+		first_tid = transaction->t_tid;
+		blocknr = journal->j_head;
+	} else {
+		first_tid = journal->j_transaction_sequence;
+		blocknr = journal->j_head;
+	}
+	J_ASSERT (blocknr != 0);
+
+	/* If the oldest pinned transaction is at the tail of the log
+           already then there's not much we can do right now. */
+	if (journal->j_tail_sequence == first_tid)
+		return 1;
+
+	if (journal->j_flags & JFS_ABORT)
+		return -EROFS;
+	
+	/* OK, update the superblock to recover the freed space.
+	 * Physical blocks come first: have we wrapped beyond the end of
+	 * the log?  */
+	freed = blocknr - journal->j_tail;
+	if (blocknr < journal->j_tail)
+		freed = freed + journal->j_last - journal->j_first;
+
+	jfs_debug(1,
+		  "Cleaning journal tail from %ld to %ld (offset %lu), "
+		  "freeing %lu\n",
+		  journal->j_tail_sequence, first_tid, blocknr, freed);
+
+	journal->j_free += freed;
+	journal->j_tail_sequence = first_tid;
+	journal->j_tail = blocknr;
+	journal_update_superblock(journal, 1);
+	return 0;
+}
+
+
+/* Checkpoint list management */
+
+/* 
+ * journal_remove_checkpoint: called after a buffer has been committed
+ * to disk (either by being write-back flushed to disk, or being
+ * committed to the log).
+ *
+ * We cannot safely clean a transaction out of the log until all of the
+ * buffer updates committed in that transaction have safely been stored
+ * elsewhere on disk.  To achieve this, all of the buffers in a
+ * transaction need to be maintained on the transaction's checkpoint
+ * list until they have been rewritten, at which point this function is
+ * called to remove the buffer from the existing transaction's
+ * checkpoint list.  
+ */
+
+void journal_remove_checkpoint(struct buffer_head *bh)
+{
+	transaction_t *transaction;
+	journal_t *journal;
+
+	
+	if ((transaction = bh->b_cp_transaction) == NULL)
+		return;
+
+	journal = transaction->t_journal;
+
+	buffer_unlink(bh);
+
+	if (transaction->t_checkpoint_list != NULL)
+		return;
+
+	/* There is one special case to worry about: if we have just
+           pulled the buffer off a committing transaction's forget list,
+           then even if the checkpoint list is empty, the transaction
+           obviously cannot be dropped! */
+
+	if (transaction == journal->j_committing_transaction)
+		return;
+
+	/* OK, that was the last buffer for the transaction: we can now
+	   safely remove this transaction from the log */
+
+	journal_drop_transaction(journal, transaction);
+	
+	/* Just in case anybody was waiting for more transactions to be
+           checkpointed... */
+	wake_up(&journal->j_wait_logspace);
+}
+
+/*
+ * journal_insert_checkpoint: put a committed buffer onto a checkpoint
+ * list so that we know when it is safe to clean the transaction out of
+ * the log. 
+ */
+
+void journal_insert_checkpoint(struct buffer_head *bh, 
+			       transaction_t *transaction)
+{
+	J_ASSERT (buffer_dirty(bh));
+	J_ASSERT (bh->b_cp_transaction == NULL);
+
+	bh->b_cp_transaction = transaction;
+
+	if (!transaction->t_checkpoint_list) {
+		bh->b_cpnext = bh->b_cpprev = bh;
+	} else {
+		bh->b_cpnext = transaction->t_checkpoint_list;
+		bh->b_cpprev = transaction->t_checkpoint_list->b_cpprev;
+		bh->b_cpprev->b_cpnext = bh;
+		bh->b_cpnext->b_cpprev = bh;
+	}
+	transaction->t_checkpoint_list = bh;
+}
+
+/*
+ * We've finished with this transaction structure: adios...
+ * 
+ * The transaction must have no links except for the checkpoint by this
+ * point.  */
+
+void journal_drop_transaction(journal_t *journal, transaction_t *transaction)
+{
+	if (transaction->t_cpnext) {
+		transaction->t_cpnext->t_cpprev = transaction->t_cpprev;
+		transaction->t_cpprev->t_cpnext = transaction->t_cpnext;
+		if (journal->j_checkpoint_transactions == transaction)
+			journal->j_checkpoint_transactions = transaction->t_cpnext;
+		if (journal->j_checkpoint_transactions == transaction)
+			journal->j_checkpoint_transactions = NULL;
+	}
+
+	J_ASSERT (transaction->t_ilist == NULL);
+	J_ASSERT (transaction->t_buffers == NULL);
+	J_ASSERT (transaction->t_datalist == NULL);
+	J_ASSERT (transaction->t_forget == NULL);
+	J_ASSERT (transaction->t_iobuf_list == NULL);
+	J_ASSERT (transaction->t_shadow_list == NULL);
+	J_ASSERT (transaction->t_log_list == NULL);
+	J_ASSERT (transaction->t_checkpoint_list == NULL);
+	J_ASSERT (transaction->t_updates == 0);
+	
+	J_ASSERT (transaction->t_journal->j_committing_transaction != transaction);
+	
+	jfs_debug (1, "Dropping transaction %ld, all done\n", 
+		   transaction->t_tid);
+	kfree (transaction);
+}
+
--- linux-2.2.18pre17.ext3-0.0.5a/fs/jfs/commit.c.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/jfs/commit.c	Wed Oct 25 19:10:56 2000
@@ -0,0 +1,511 @@
+/*
+ * linux/fs/commit.c
+ * 
+ * Written by Stephen C. Tweedie <sct@redhat.com>, 1998
+ *
+ * Copyright 1998 Red Hat corp --- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * Journal commit routines for the generic filesystem journaling code;
+ * part of the ext2fs journaling system.
+ */
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/jfs.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/locks.h>
+#include <linux/buffer.h>
+
+void wakeup_bdflush(int);
+
+/*
+ * is_journal_abort
+ *
+ * Simple test wrapper function to test the JFS_ABORT state flag.  This
+ * bit, when set, indicates that we have had a fatal error somewhere,
+ * either inside the journaling layer or indicated to us by the client
+ * (eg. ext3), and that we and should not commit any further
+ * transactions.  
+ */
+
+static inline int is_journal_abort(journal_t *journal)
+{
+	return journal->j_flags & JFS_ABORT;
+}
+
+/*
+ * journal_commit_transaction
+ *
+ * The primary function for committing a transaction to the log.  This
+ * function is called by the journal thread to begin a complete commit.
+ */
+
+void journal_commit_transaction(journal_t *journal)
+{
+	transaction_t *commit_transaction;
+	struct buffer_head *bh, *new_bh, *descriptor;
+	struct buffer_head *wbuf[journal_flush_nr_buffers];
+	int bufs;
+	int flags;
+	int blocknr;
+	char *tagp = NULL;
+	journal_header_t *header;
+	journal_block_tag_t *tag = NULL;
+	int space_left = 0;
+	int first_tag = 0;
+	int tag_flag;
+	
+	int i;
+	
+	/*
+	 * First job: lock down the current transaction and wait for 
+	 * all outstanding updates to complete. 
+	 */
+	
+	lock_journal(journal);
+
+	J_ASSERT (journal->j_running_transaction != NULL);
+	J_ASSERT (journal->j_committing_transaction == NULL);
+	
+	commit_transaction = journal->j_running_transaction;
+	J_ASSERT (commit_transaction->t_state == T_RUNNING);
+
+	jfs_debug (1, "Starting commit of transaction %ld\n",
+		   commit_transaction->t_tid);
+	
+	commit_transaction->t_state = T_LOCKED;
+	while (commit_transaction->t_updates != 0) {
+		unlock_journal(journal);
+		sleep_on(&commit_transaction->t_wait);
+		lock_journal(journal);
+	}
+
+	J_ASSERT (commit_transaction->t_outstanding_credits <= journal->j_max_transaction_buffers);
+	
+	/*
+	 * First thing we are allowed to do is to discard any remaining
+	 * BJ_Reserved buffers.  Note, it is _not_ permissible to assume
+	 * that there are no such buffers: if a large filesystem
+	 * operation like a truncate needs to split itself over multiple
+	 * transactions, then it may try to do a journal_restart() while
+	 * there are still BJ_Reserved buffers outstanding.  These must
+	 * be released cleanly from the current transaction.
+	 *
+	 * In this case, the filesystem must still reserve write access
+	 * again before modifying the buffer in the new transaction, but
+	 * we do not require it to remember exactly which old buffers it
+	 * has reserved.  This is consistent with the existing behaviour
+	 * that multiple journal_get_write_access() calls to the same
+	 * buffer are perfectly permissable.
+	 */
+
+	while (commit_transaction->t_reserved_list) {
+		bh = commit_transaction->t_reserved_list;
+		journal_refile_buffer(bh);
+	}
+	
+
+	/* 
+	 * Start committing the old transaction: time to get a new
+	 * running transaction for incoming filesystem updates */
+
+	jfs_debug (3, "commit phase 1\n");
+
+	commit_transaction->t_state = T_FLUSH;
+	wake_up(&journal->j_wait_transaction_locked);
+	
+	journal->j_committing_transaction = commit_transaction;
+	journal->j_running_transaction = NULL;
+	
+	commit_transaction->t_log_start = journal->j_head;
+
+	/* 
+	 * Now start flushing things to disk, in the order they appear
+	 * on the transaction lists.  Data blocks go first.  
+	 */
+	
+ wait_for_data:
+	bufs = 0;
+	
+	/* Cleanup any flushed data buffers from the data list.  Even in
+	 * abort mode, we want to flush this out as soon as possible. */
+	journal_clean_data_list(commit_transaction);
+
+	bh = commit_transaction->t_datalist;
+	
+#if 0 /* This version flushes buffers from within commit, but interacts
+         badly with bdflush going on at the same time. */
+	if (bh) do {
+		if (buffer_dirty(bh) && !buffer_locked(bh)) {
+			wbuf[bufs++] = bh;
+		}
+		bh = bh->b_tnext;
+	} while (bufs < journal_flush_nr_buffers &&
+		 bh != commit_transaction->t_datalist);
+
+	if (bufs) {
+		unlock_journal(journal);
+		ll_rw_block(WRITE, bufs, wbuf);
+		run_task_queue(&tq_disk);
+		lock_journal(journal);
+		goto wait_for_data;
+	}
+#else /* This version relies on bdflush, so provides better throughput
+         for now at the cost of potentially flushing other filesystems'
+         buffers to disk before we strictly need to. */
+	if (bh) {
+		if (buffer_locked(bh)) {
+			bh->b_count++;
+			unlock_journal(journal);
+			wait_on_buffer(bh);
+			brelse(bh);
+			lock_journal(journal);
+		} else {
+			unlock_journal(journal);
+			wakeup_bdflush(1);
+			lock_journal(journal);
+		}
+		goto wait_for_data;
+	}
+#endif
+
+	/*
+	 * If we got through the write loop without submitting any new
+	 * IO, then wait for all previously submitted IO on the data
+	 * list to complete. 
+	 */
+
+	bh = commit_transaction->t_datalist;
+	
+	if (bh) do {
+		if (buffer_locked(bh)) {
+			unlock_journal(journal);
+			wait_on_buffer(bh);
+			lock_journal(journal);
+			goto wait_for_data;
+		}
+		
+		bh = bh->b_tnext;
+	} while (bh != commit_transaction->t_datalist);
+
+	/*
+	 * If we found any dirty or locked buffers, then we should have
+	 * looped back up to the wait_for_data label.  If there weren't
+	 * any then journal_clean_data_list should have wiped the list
+	 * clean by now, so check that it is in fact empty. 
+	 */
+	
+	J_ASSERT (commit_transaction->t_datalist == NULL);
+
+	jfs_debug (3, "Commit phase 2\n");
+
+	/*
+	 * Way to go: we have now written out all of the data for a
+	 * transaction!  Now comes the tricky part: we need to write out
+	 * metadata now.  Loop over the transaction's entire buffer list:
+	 */
+
+	commit_transaction->t_state = T_COMMIT;
+	descriptor = 0;
+	bufs = 0;
+	
+	while (commit_transaction->t_buffers) {
+
+		/* Find the next buffer to be journaled... */
+
+		bh = commit_transaction->t_buffers;
+
+		/* If we're in abort mode, we just un-journal the buffer and 
+		   release it for background writing. */
+		
+		if (is_journal_abort(journal)) {
+			journal_refile_buffer(bh);
+			continue;
+		}
+		
+		/* Make sure we have a descriptor block in which to
+		   record the metadata buffer. */
+
+		if (!descriptor) {
+			J_ASSERT (bufs == 0);
+			
+			jfs_debug(4, "get descriptor\n");
+
+			descriptor = journal_get_descriptor_buffer(journal);
+			jfs_debug(4, "JFS: got buffer %ld (%p)\n", 
+				  descriptor->b_blocknr, descriptor->b_data);
+			header = (journal_header_t *) &descriptor->b_data[0];
+			header->h_magic     = htonl(JFS_MAGIC_NUMBER);
+			header->h_blocktype = htonl(JFS_DESCRIPTOR_BLOCK);
+			header->h_sequence  = htonl(commit_transaction->t_tid);
+
+			tagp = &descriptor->b_data[sizeof(journal_header_t)];
+			space_left = descriptor->b_size - sizeof(journal_header_t);
+			first_tag = 1;
+			set_bit(BH_JWrite, &descriptor->b_state);
+			wbuf[bufs++] = descriptor;
+			
+			/* Record it so that we can wait for IO
+                           completion later */
+			journal_file_buffer(descriptor, 
+					    commit_transaction,
+					    BJ_LogCtl);
+		}
+
+		/* Where is the buffer to be written? */
+		
+		blocknr = journal_next_log_block(journal);
+
+		/* Bump b_count to prevent truncate from stumbling over
+                   the shadowed buffer!  @@@ This can go if we ever get
+                   rid of the BJ_IO/BJ_Shadow pairing of buffers. */
+		bh->b_count++;
+
+		/* Make a temporary IO buffer with which to write it out
+                   (this will requeue both the metadata buffer and the
+                   temporary IO buffer). */
+
+		set_bit(BH_JWrite, &bh->b_state);
+		flags = journal_write_metadata_buffer(commit_transaction,
+						      bh, 
+						      &new_bh,
+						      blocknr);
+		set_bit(BH_JWrite, &new_bh->b_state);
+		wbuf[bufs++] = new_bh;
+		
+		/* Record the new block's tag in the current descriptor
+                   buffer */
+
+		tag_flag = 0;
+		if (flags & 1)
+			tag_flag |= JFS_FLAG_ESCAPE;
+		if (!first_tag)
+			tag_flag |= JFS_FLAG_SAME_UUID;
+
+		tag = (journal_block_tag_t *) tagp;
+		tag->t_blocknr = htonl(bh->b_blocknr);
+		tag->t_flags = htonl(tag_flag);
+		tagp += sizeof(journal_block_tag_t);
+		space_left -= sizeof(journal_block_tag_t);
+		
+		if (first_tag) {
+			memcpy (tagp, journal->j_uuid, 16);
+			tagp += 16;
+			space_left -= 16;
+			first_tag = 0;
+		}
+		
+		/* If there's no more to do, or if the descriptor is full,
+		   let the IO rip! */
+
+		if (bufs == journal_flush_nr_buffers ||
+		    commit_transaction->t_buffers == NULL ||		    
+		    space_left < sizeof(journal_block_tag_t) + 16) {
+
+			jfs_debug(4, "JFS: Submit %d IOs\n", bufs);
+			
+			/* Write an end-of-descriptor marker before
+                           submitting the IOs.  "tag" still points to
+                           the last tag we set up. */
+			
+			tag->t_flags |= htonl(JFS_FLAG_LAST_TAG);
+
+			unlock_journal(journal);
+			ll_rw_block (WRITE, bufs, wbuf);
+			lock_journal(journal);
+			
+			/* Force a new descriptor to be generated next
+                           time round the loop. */
+			descriptor = NULL;
+			bufs = 0;
+		}
+	}
+
+	/* Lo and behold: we have just managed to send a transaction to
+           the log.  Before we can commit it, wait for the IO so far to
+           complete.  Control buffers being written are on the
+           transaction's t_log_list queue, and metadata buffers are on
+           the t_iobuf_list queue. 
+	
+	   Wait for the transactions in reverse order.  That way we are
+	   less likely to be woken up until all IOs have completed, and
+	   so we incur less scheduling load.
+	*/
+
+	jfs_debug(3, "JFS: commit phase 3\n");
+
+ wait_for_iobuf:
+	while (commit_transaction->t_iobuf_list != NULL) {
+		struct buffer_head *bh = commit_transaction->t_iobuf_list->b_tprev;
+		if (buffer_locked(bh)) {
+			unlock_journal(journal); 
+			wait_on_buffer(bh);
+			lock_journal(journal); 
+			goto wait_for_iobuf;
+		}
+		
+		clear_bit(BH_JWrite, &bh->b_state);
+
+		journal_unfile_buffer(bh);
+		put_unused_buffer_head(bh);
+		
+		/* We also have to unlock and free the corresponding
+                   shadowed buffer */
+
+		bh = commit_transaction->t_shadow_list->b_tprev;
+		clear_bit(BH_JWrite, &bh->b_state);
+		J_ASSERT(buffer_dirty(bh));
+		
+		/* The metadata is now released for reuse, but we need
+                   to remember it against this transaction so that when
+                   we finally commit, we can do any checkpointing
+                   required. */
+
+		journal_file_buffer(bh, commit_transaction, BJ_Forget);
+
+		/* Wake up any transactions which were waiting for this
+		   IO to complete */
+		unlock_journal(journal);
+		wake_up(&bh->b_wait);
+		brelse(bh);
+		lock_journal(journal);
+	}
+
+	J_ASSERT (commit_transaction->t_shadow_list == NULL);
+	
+	jfs_debug(3, "JFS: commit phase 4\n");
+
+ wait_for_ctlbuf:
+	while (commit_transaction->t_log_list != NULL) {
+		struct buffer_head *bh = commit_transaction->t_log_list->b_tprev;
+		
+		if (buffer_locked(bh)) {
+			unlock_journal(journal); 
+			wait_on_buffer(bh);
+			lock_journal(journal); 
+			goto wait_for_ctlbuf;
+		}
+		
+		clear_bit(BH_JWrite, &bh->b_state);	
+		journal_unfile_buffer(bh);
+		bh->b_transaction = NULL;
+		brelse(bh);
+	}
+	
+	jfs_debug(3, "JFS: commit phase 5\n");
+
+	/* Done it all: now write the commit record.  We should have
+	 * cleaned up our previous buffers by now, so if we are in abort
+	 * mode we can now just skip the rest of the journal write
+	 * entirely. */
+
+	if (is_journal_abort(journal))
+		goto skip_commit;
+
+	descriptor = journal_get_descriptor_buffer(journal);
+
+	for (i = 0; i < descriptor->b_size; i += 512) {
+		journal_header_t *tmp = (journal_header_t*)descriptor->b_data;
+		tmp->h_magic = htonl(JFS_MAGIC_NUMBER);
+		tmp->h_blocktype = htonl(JFS_COMMIT_BLOCK);
+		tmp->h_sequence = htonl(commit_transaction->t_tid);
+	}
+
+	unlock_journal(journal); 
+	ll_rw_block(WRITE, 1, &descriptor);
+	wait_on_buffer(descriptor);
+	brelse(descriptor);
+	lock_journal(journal); 
+	
+	/* End of a transaction!  Finally, we can do checkpoint
+           processing: any buffers committed as a result of this
+           transaction can be removed from any checkpoint list it was on
+           before. */
+
+skip_commit:
+
+	jfs_debug(3, "JFS: commit phase 6\n");
+
+	J_ASSERT(commit_transaction->t_datalist == NULL);
+	J_ASSERT(commit_transaction->t_buffers == NULL);
+	J_ASSERT(commit_transaction->t_checkpoint_list == NULL);
+	J_ASSERT(commit_transaction->t_iobuf_list == NULL);
+	J_ASSERT(commit_transaction->t_shadow_list == NULL);
+	J_ASSERT(commit_transaction->t_log_list == NULL);
+
+	while (commit_transaction->t_forget) {
+		transaction_t *cp_transaction;
+		
+		bh = commit_transaction->t_forget;
+		J_ASSERT (bh->b_transaction == commit_transaction || 
+			  bh->b_transaction == journal->j_running_transaction);
+
+
+		/* 
+		 * If there is undo-protected committed data against
+		 * this buffer, then we can remove it now.  If it is a
+		 * buffer needing such protection, the old frozen_data
+		 * field now points to a committed version of the
+		 * buffer, so rotate that field to the new committed
+		 * data.
+		 *
+		 * Otherwise, we can just throw away the frozen data now.  
+		 */
+		if (bh->b_committed_data) {
+			kfree(bh->b_committed_data);
+			bh->b_committed_data = NULL;
+			if (bh->b_frozen_data) {
+				bh->b_committed_data = bh->b_frozen_data;
+				bh->b_frozen_data = NULL;
+			}
+		} else if (bh->b_frozen_data) {
+			kfree(bh->b_frozen_data);
+			bh->b_frozen_data = NULL;
+		}
+
+		cp_transaction = bh->b_cp_transaction;
+		if (cp_transaction) {
+			J_ASSERT (commit_transaction != cp_transaction);
+			journal_remove_checkpoint(bh);
+		}
+		journal_insert_checkpoint(bh, commit_transaction);
+       
+		journal_refile_buffer(bh);
+	}
+	
+	/* Done with this transaction! */
+
+	jfs_debug(3, "JFS: commit phase 7\n");
+
+	J_ASSERT (commit_transaction->t_state == T_COMMIT);
+	commit_transaction->t_state = T_FINISHED;
+
+	J_ASSERT (commit_transaction == journal->j_committing_transaction);
+	journal->j_commit_sequence = commit_transaction->t_tid;
+	journal->j_committing_transaction = NULL;
+
+	if (commit_transaction->t_checkpoint_list == NULL) {
+		journal_drop_transaction(journal, commit_transaction);
+	} else {
+		if (journal->j_checkpoint_transactions == NULL) {
+			journal->j_checkpoint_transactions = commit_transaction;
+			commit_transaction->t_cpnext = commit_transaction;
+			commit_transaction->t_cpprev = commit_transaction;
+		} else {
+			commit_transaction->t_cpnext = journal->j_checkpoint_transactions;
+			commit_transaction->t_cpprev = commit_transaction->t_cpnext->t_cpprev;
+			commit_transaction->t_cpnext->t_cpprev = commit_transaction;
+			commit_transaction->t_cpprev->t_cpnext = commit_transaction;
+		}
+	}
+	
+	jfs_debug(1, "Commit complete\n");
+
+	unlock_journal(journal); 
+	wake_up(&journal->j_wait_done_commit);
+}
--- linux-2.2.18pre17.ext3-0.0.5a/fs/jfs/journal.c.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/jfs/journal.c	Wed Oct 25 22:18:37 2000
@@ -0,0 +1,1116 @@
+/*
+ * linux/fs/journal.c
+ * 
+ * Written by Stephen C. Tweedie <sct@redhat.com>, 1998
+ *
+ * Copyright 1998 Red Hat corp --- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * Generic filesystem journal-writing code; part of the ext2fs
+ * journaling system.  
+ *
+ * This file manages journals: areas of disk reserved for logging
+ * transactional updates.  This includes the kernel journaling thread
+ * which is responsible for scheduling updates to the log.
+ *
+ * We do not actually manage the physical storage of the journal in this
+ * file: that is left to a per-journal policy function, which allows us
+ * to store the journal within a filesystem-specified area for ext2
+ * journaling (ext2 can use a reserved inode for storing the log).
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/jfs.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/locks.h>
+#include <linux/buffer.h>
+#include <linux/smp_lock.h>
+#include <linux/sched.h>
+
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+/*
+ * Tunable parameters
+ */
+
+/* Number of buffers to flush out at once before starting the disk up */
+int journal_flush_nr_buffers = 64;
+
+
+#ifdef JFS_DEBUG
+int journal_enable_debug = 0;
+EXPORT_SYMBOL(journal_enable_debug);
+#endif
+
+EXPORT_SYMBOL(journal_start);
+EXPORT_SYMBOL(journal_restart);
+EXPORT_SYMBOL(journal_extend);
+EXPORT_SYMBOL(journal_stop);
+EXPORT_SYMBOL(journal_get_write_access);
+EXPORT_SYMBOL(journal_get_create_access);
+EXPORT_SYMBOL(journal_get_undo_access);
+EXPORT_SYMBOL(journal_dirty_data);
+EXPORT_SYMBOL(journal_dirty_metadata);
+EXPORT_SYMBOL(journal_release_buffer);
+EXPORT_SYMBOL(journal_forget);
+EXPORT_SYMBOL(journal_sync_buffer);
+EXPORT_SYMBOL(journal_flush);
+EXPORT_SYMBOL(journal_revoke);
+
+EXPORT_SYMBOL(journal_init_dev);
+EXPORT_SYMBOL(journal_init_inode);
+EXPORT_SYMBOL(journal_update_format);
+EXPORT_SYMBOL(journal_check_features);
+EXPORT_SYMBOL(journal_create);
+EXPORT_SYMBOL(journal_load);
+EXPORT_SYMBOL(journal_release);
+EXPORT_SYMBOL(journal_recover);
+EXPORT_SYMBOL(journal_update_superblock);
+EXPORT_SYMBOL(__journal_abort);
+EXPORT_SYMBOL(journal_abort);
+EXPORT_SYMBOL(journal_errno);
+EXPORT_SYMBOL(journal_ack_err);
+EXPORT_SYMBOL(journal_clear_err);
+
+static int journal_convert_superblock_v1(journal_t *, journal_superblock_t *);
+
+/*
+ * Helper function used to manage commit timeouts
+ */
+
+static void commit_timeout(unsigned long __data)
+{
+	struct task_struct * p = (struct task_struct *) __data;
+
+	wake_up_process(p);
+}
+
+/* Static check for data structure consistency.  There's no code
+ * invoked --- we'll just get a linker failure if things aren't right.
+ */
+void __journal_internal_check(void)
+{
+	extern void journal_bad_superblock_size(void);
+	if (sizeof(struct journal_superblock_s) != 1024)
+		journal_bad_superblock_size();
+}
+
+/* 
+ * kjournald: The main thread function used to manage a logging device
+ * journal.
+ *
+ * This kernel thread is responsible for two things:
+ *
+ * 1) COMMIT:  Every so often we need to commit the current state of the
+ *    filesystem to disk.  The journal thread is responsible for writing
+ *    all of the metadata buffers to disk.
+ *
+ * 2) CHECKPOINT: We cannot reuse a used section of the log file until all
+ *    of the data in that part of the log has been rewritten elsewhere on 
+ *    the disk.  Flushing these old buffers to reclaim space in the log is
+ *    known as checkpointing, and this thread is responsible for that job.
+ */
+
+int kjournald(void *arg)
+{
+	journal_t *journal = (journal_t *) arg;
+	transaction_t *transaction;
+	struct timer_list timer;
+	
+	lock_kernel();
+	
+	exit_files(current);
+	exit_mm(current);
+
+	spin_lock_irq(&current->sigmask_lock);
+	sigfillset(&current->blocked);
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+
+	current->session = 1;
+	current->pgrp = 1;
+	sprintf(current->comm, "kjournald");
+
+	/* Set up an interval timer which can be used to trigger a
+           commit wakeup after the commit interval expires */
+	init_timer(&timer);
+	timer.data = (unsigned long) current;
+	timer.function = commit_timeout;
+	journal->j_commit_timer = &timer;
+	
+	/* Record that the journal thread is running */
+	journal->j_task = current;
+	wake_up(&journal->j_wait_done_commit);
+
+	jfs_debug(1, "Journal thread starting.\n");
+	
+	/* And now, wait forever for commit wakeup events. */
+	while (1) {
+		if (journal->j_flags & JFS_UNMOUNT)
+			break;
+
+		if (journal->j_commit_sequence != journal->j_commit_request) {
+			if (journal->j_commit_timer_active) {
+				journal->j_commit_timer_active = 0;
+				del_timer(journal->j_commit_timer);
+			}
+
+			journal_commit_transaction(journal);
+			continue;
+		}
+		
+		wake_up(&journal->j_wait_done_commit);
+		interruptible_sleep_on(&journal->j_wait_commit);
+
+		/* Were we woken up by a commit wakeup event? */
+		if ((transaction = journal->j_running_transaction) != NULL &&
+		    time_after_eq(jiffies, transaction->t_expires))
+			journal->j_commit_request = transaction->t_tid;
+	}
+	
+	if (journal->j_commit_timer_active) {
+		journal->j_commit_timer_active = 0;
+		del_timer(journal->j_commit_timer);
+	}
+
+	journal->j_task = NULL;
+	wake_up(&journal->j_wait_done_commit);
+	jfs_debug(1, "Journal thread exiting.\n");
+	return 0;
+}
+
+static void journal_start_thread(journal_t *journal)
+{
+	kernel_thread(kjournald, (void *) journal, 
+		      CLONE_VM | CLONE_FS | CLONE_FILES);
+	while (!journal->j_task)
+		sleep_on(&journal->j_wait_done_commit);
+}
+	
+static void journal_kill_thread(journal_t *journal)
+{
+	journal->j_flags |= JFS_UNMOUNT;
+	
+	while (journal->j_task) {
+		wake_up(&journal->j_wait_commit);
+		sleep_on(&journal->j_wait_done_commit);
+	}
+}
+	
+
+/*
+ * journal_clean_data_list: cleanup after data IO.
+ *
+ * Once the IO system has finished writing the buffers on the transaction's
+ * data list, we can remove those buffers from the list.  This function 
+ * scans the list for such buffers and removes them cleanly.
+ *
+ * We assume that the journal is already locked.  This function may block.
+ */
+
+void journal_clean_data_list(transaction_t *transaction)
+{
+	struct buffer_head *bh, *next, *last;
+	
+ repeat:
+	next = transaction->t_datalist;
+	if (!next)
+		return;
+
+	last = next->b_tprev;
+	
+	do {
+		bh = next;
+		transaction->t_datalist = next = bh->b_tnext;
+		
+		if (!buffer_locked(bh) && 
+		    !buffer_dirty(bh)) {
+			journal_unfile_buffer(bh);
+			bh->b_transaction = NULL;
+			unlock_journal(transaction->t_journal);
+			refile_buffer(bh);
+			/* refile buffer may block! */
+			lock_journal(transaction->t_journal);
+			goto repeat;
+		}
+	} while (bh != last);
+}
+
+/*
+ * journal_write_metadata_buffer: write a metadata buffer to the journal.
+ *
+ * Writes a metadata buffer to a given disk block.  The actual IO is not
+ * performed but a new buffer_head is constructed which labels the data
+ * to be written with the correct destination disk block.
+ *
+ * Any magic-number escaping which needs to be done will cause a
+ * copy-out here.  If the buffer happens to start with the
+ * JFS_MAGIC_NUMBER, then we can't write it to the log directly: the
+ * magic number is only written to the log for descripter blocks.  In
+ * this case, we copy the data and replace the first word with 0, and we
+ * return a result code which indicates that this buffer needs to be
+ * marked as an escaped buffer in the corresponding log descriptor
+ * block.  The missing word can then be restored when the block is read
+ * during recovery.
+ * 
+ * If the source buffer has already been modified by a new transaction
+ * since we took the last commit snapshot, we use the frozen copy of
+ * that data for IO.  If we end up using the existing buffer_head's data
+ * for the write, then we *have* to lock the buffer to prevent anyone
+ * else from using and possibly modifying it while the IO is in
+ * progress.
+ *
+ * The function returns a pointer to the buffer_heads to be used for IO.
+ *
+ * We assume that the journal has already been locked in this function.
+ *
+ * Return value:
+ *  <0: Error
+ * >=0: Finished OK
+ * 
+ * On success:
+ * Bit 0 set == escape performed on the data
+ * Bit 1 set == buffer copy-out performed (kfree the data after IO)
+ */
+
+int journal_write_metadata_buffer(transaction_t *transaction,
+				  struct buffer_head  *bh_in,
+				  struct buffer_head **bh_out,
+				  int blocknr)
+{
+	int need_copy_out = 0;
+	int done_copy_out = 0;
+	int do_escape = 0;
+	char *new_data;
+	struct buffer_head * new_bh;
+
+	/* 
+	 * The buffer really shouldn't be locked: only the current committing
+	 * transaction is allowed to write it, so nobody else is allowed
+	 * to do any IO.
+	 */
+
+	J_ASSERT (!buffer_locked(bh_in));
+	J_ASSERT (buffer_dirty(bh_in));
+
+	/*
+	 * If a new transaction has already done a buffer copy-out, then 
+	 * we use that version of the data for the commit.
+	 */
+
+	if (bh_in->b_frozen_data) {
+		done_copy_out = 1;
+		new_data = bh_in->b_frozen_data;
+	} else
+		new_data = bh_in->b_data;
+		
+	/*
+	 * Check for escaping 
+	 */
+
+	if (* ((unsigned int *) new_data) == htonl(JFS_MAGIC_NUMBER)) {
+		need_copy_out = 1;
+		do_escape = 1;
+	}
+
+	/*
+	 * Do we need to do a data copy?
+	 */
+
+	if (need_copy_out && !done_copy_out) {
+		char *tmp;
+		do {
+			tmp = kmalloc(bh_in->b_size, GFP_KERNEL);
+			if (!tmp) {
+				printk (KERN_ERR __FUNCTION__
+					": ENOMEM getting frozen data, "
+					"trying again.\n");
+				sys_sched_yield();
+			}
+		} while (!tmp);
+
+		bh_in->b_frozen_data = tmp;
+		memcpy (tmp, new_data, bh_in->b_size);
+		new_data = tmp;
+		done_copy_out = 1;
+	}
+	
+	/*
+	 * Right, time to make up the new buffer_head.
+	 */
+
+	do {
+		new_bh = get_unused_buffer_head(0);
+		if (!new_bh) {
+			printk (KERN_ERR __FUNCTION__
+				": ENOMEM at get_unused_buffer_head, "
+				"trying again.\n");
+			sys_sched_yield();
+			}
+	} while (!new_bh);
+
+	init_buffer(new_bh, transaction->t_journal->j_dev, blocknr, 
+		    end_buffer_io_sync, NULL);
+
+	new_bh->b_size    = bh_in->b_size;
+	new_bh->b_data    = new_data;
+	new_bh->b_state	  = (1 << BH_Temp) | (1 << BH_Dirty);
+	new_bh->b_count	  = 1;
+
+	*bh_out = new_bh;
+
+	/* 
+	 * Did we need to do an escaping?  Now we've done all the
+	 * copying, we can finally do so.
+	 */
+
+	if (do_escape)
+		* ((unsigned int *) new_data) = 0;
+
+	/* 
+	 * The to-be-written buffer needs to get moved to the io queue,
+	 * and the original buffer whose contents we are shadowing or
+	 * copying is moved to the transaction's shadow queue. 
+	 */
+
+	journal_file_buffer(bh_in, transaction, BJ_Shadow);
+	journal_file_buffer(new_bh, transaction, BJ_IO);
+	
+	return do_escape | (done_copy_out << 1);
+}
+
+
+/* The call to lock_buffer() above should be the only place we ever lock
+ * a buffer which is being journaled (ignoring the checkpoint lists).
+ *
+ * @@@ This is heavily dependent on the big kernel lock in 2.2! */
+
+void jfs_prelock_buffer_check(struct buffer_head *bh)
+{
+	transaction_t *transaction = bh->b_transaction;
+	journal_t *journal;
+
+	if (bh->b_jlist == 0 && transaction == NULL)
+		return;
+	J_ASSERT(bh->b_jlist == 0 || bh->b_jlist == BJ_LogCtl || bh->b_jlist == BJ_IO || bh->b_jlist == BJ_Data);
+	J_ASSERT(transaction != NULL);
+	journal = transaction->t_journal;
+	if (bh->b_jlist == BJ_Data) {
+		J_ASSERT(transaction == journal->j_running_transaction || transaction == journal->j_committing_transaction);
+	} else {
+		J_ASSERT(transaction == journal->j_running_transaction || transaction == journal->j_committing_transaction);
+		J_ASSERT(test_bit(BH_JWrite, &bh->b_state));
+	}
+}
+
+/* We are not allowed to forget the dirty status on any buffer which is
+ * being journaled! */
+
+void jfs_preclean_buffer_check(struct buffer_head *bh)
+{
+	jfs_prelock_buffer_check(bh);
+}
+
+
+
+
+/*
+ * Allocation code for the journal file.  Manage the space left in the
+ * journal, so that we can begin checkpointing when appropriate.
+ */
+
+/*
+ * log_space_left: Return the number of free blocks left in the journal.
+ *
+ * Called with the journal already locked.
+ */
+
+int log_space_left (journal_t *journal)
+{
+	int left = journal->j_free;
+
+	/* Be pessimistic here about the number of those free blocks
+	 * which might be required for log descriptor control blocks. */
+
+#define MIN_LOG_RESERVED_BLOCKS 32 /* Allow for rounding errors */
+
+	left -= MIN_LOG_RESERVED_BLOCKS;
+	
+	if (left <= 0)
+		return 0;
+	left -= (left >> 3);
+	return left;
+}
+
+
+void log_start_commit (journal_t *journal, transaction_t *transaction)
+{
+	/*
+	 * Are we already doing a recent enough commit?
+	 */
+	if ((long)(journal->j_commit_request - transaction->t_tid) >= 0)
+		return;
+
+	/* 
+	 * We want a new commit: OK, mark the request and wakup the
+	 * commit thread.  We do _not_ do the commit ourselves.  
+	 */
+	
+	journal->j_commit_request = transaction->t_tid;
+	jfs_debug(1, "JFS: requesting commit %ld/%ld\n", 
+		  journal->j_commit_request,
+		  journal->j_commit_sequence);
+	wake_up(&journal->j_wait_commit);
+}
+
+/* Wait for a specified commit to complete. */
+void log_wait_commit (journal_t *journal, tid_t tid)
+{
+	while (((long) (journal->j_commit_sequence - tid)) < 0) {
+		wake_up(&journal->j_wait_commit);
+		sleep_on(&journal->j_wait_done_commit);
+	}
+}
+
+/* 
+ * Log buffer allocation routines: 
+ */
+
+unsigned long journal_next_log_block(journal_t *journal)
+{
+	unsigned long blocknr;
+
+	J_ASSERT(journal->j_free > 1);
+	
+	blocknr = journal->j_head;
+	journal->j_head++;
+	journal->j_free--;
+	if (journal->j_head == journal->j_last)
+		journal->j_head = journal->j_first;
+	
+	if (journal->j_inode) {
+		blocknr = bmap(journal->j_inode, blocknr);
+		J_ASSERT(blocknr != 0);
+	}
+	return blocknr;
+}
+
+/* 
+ * We play buffer_head aliasing tricks to write data/metadata blocks to
+ * the journal without copying their contents, but for journal
+ * descriptor blocks we do need to generate bona fide buffers.
+ */
+
+struct buffer_head * journal_get_descriptor_buffer(journal_t *journal)
+{
+	struct buffer_head *bh;
+	unsigned long blocknr = journal_next_log_block(journal);
+	bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);
+	bh->b_state |= (1 << BH_Dirty);
+	J_ASSERT(bh != NULL);
+	return bh;
+}
+
+
+
+
+/*
+ * Management for journal control blocks: functions to create and
+ * destroy journal_t structures, and to initialise and read existing
+ * journal blocks from disk.  */
+
+/* First: create and setup a journal_t object in memory.  We initialise
+ * very few fields yet: that has to wait until we have created the
+ * journal structures from from scratch, or loaded them from disk. */
+
+static journal_t * journal_init_common (void)
+{
+	journal_t *journal;
+	
+	journal = kmalloc(sizeof(*journal), GFP_KERNEL);
+	if (!journal)
+		return 0;
+	memset(journal, 0, sizeof(*journal));
+
+	init_waitqueue(&journal->j_wait_lock);
+	init_waitqueue(&journal->j_wait_transaction_locked);
+	init_waitqueue(&journal->j_wait_logspace);
+	init_waitqueue(&journal->j_wait_done_commit);
+	init_waitqueue(&journal->j_wait_checkpoint);
+	init_waitqueue(&journal->j_wait_commit);
+	journal->j_checkpoint_sem = MUTEX;
+
+	/* The journal is marked for error until we succeed with recovery! */
+	journal->j_flags = JFS_ABORT;
+	
+	return journal;
+}
+
+/* journal_init_dev and journal_init_inode:
+ *
+ * Create a journal structure assigned some fixed set of disk blocks to
+ * the journal.  We don't actually touch those disk blocks yet, but we
+ * need to set up all of the mapping information to tell the journaling
+ * system where the journal blocks are.
+ *
+ * journal_init_dev creates a journal which maps a fixed contiguous
+ * range of blocks on an arbitrary block device.
+ * 
+ * journal_init_inode creates a journal which maps an on-disk inode as
+ * the journal.  The inode must exist already, must support bmap() and
+ * must have all data blocks preallocated.  
+ */
+
+journal_t * journal_init_dev (kdev_t dev, int start, int len, int blocksize)
+{
+	journal_t *journal = journal_init_common();
+	struct buffer_head *bh;
+	
+	if (!journal)
+		return 0;
+	
+	journal->j_dev = dev;
+	journal->j_blk_offset = start;
+	journal->j_maxlen = len;
+	journal->j_blocksize = blocksize;
+	
+	bh = getblk(journal->j_dev, start, journal->j_blocksize);
+	J_ASSERT(bh != NULL);
+	journal->j_sb_buffer = bh;
+	journal->j_superblock = (journal_superblock_t *) bh->b_data;
+
+	return journal;
+}
+
+journal_t * journal_init_inode (struct inode *inode)
+{
+	struct buffer_head *bh;
+	journal_t *journal = journal_init_common();
+	int blocknr;
+	
+	if (!journal)
+		return 0;
+	
+	journal->j_dev = inode->i_dev;
+	journal->j_inode = inode;
+	jfs_debug(0, 
+		  "journal %p: inode %s/%ld, size %ld, bits %d, blksize %ld\n",
+		  journal,
+		  kdevname(inode->i_dev), inode->i_ino, inode->i_size, 
+		  inode->i_sb->s_blocksize_bits, inode->i_sb->s_blocksize);
+	
+	journal->j_maxlen = inode->i_size >> inode->i_sb->s_blocksize_bits;
+	journal->j_blocksize = inode->i_sb->s_blocksize;
+
+	blocknr = bmap(journal->j_inode, 0);
+	J_ASSERT(blocknr != 0);
+	bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);
+	J_ASSERT(bh != NULL);
+	journal->j_sb_buffer = bh;
+	journal->j_superblock = (journal_superblock_t *) bh->b_data;
+
+	return journal;
+}
+
+/*
+ * Given a journal_t structure, initialise the various fields for
+ * startup of a new journaling session.  We use this both when creating
+ * a journal, and after recovering an old journal to reset it for
+ * subsequent use.  
+ */
+
+static int journal_reset (journal_t *journal)
+{
+	journal_superblock_t *sb = journal->j_superblock;
+	unsigned int first, last;
+	
+	first = ntohl(sb->s_first);
+	last = ntohl(sb->s_maxlen);
+
+	journal->j_first = first;
+	journal->j_last = last;
+	
+	journal->j_head = first;
+	journal->j_tail = first;
+	journal->j_free = last - first;
+	
+	journal->j_tail_sequence = journal->j_transaction_sequence;
+	journal->j_commit_sequence = journal->j_transaction_sequence - 1;
+	journal->j_commit_request = journal->j_commit_sequence;	
+
+	journal->j_max_transaction_buffers = journal->j_maxlen / 4;
+	journal->j_commit_interval = (HZ * 5);
+
+	/* Add the dynamic fields and write it to disk. */
+	journal_update_superblock(journal, 1);
+
+	lock_journal(journal);
+	journal_start_thread(journal);
+	unlock_journal(journal);
+
+	return 0;
+}
+
+/* 
+ * Given a journal_t structure which tells us which disk blocks we can
+ * use, create a new journal superblock and initialise all of the
+ * journal fields from scratch.  */
+
+int journal_create (journal_t *journal)
+{
+	int blocknr;
+	struct buffer_head *bh;
+	journal_superblock_t *sb;
+	int i;
+	
+	if (journal->j_maxlen < JFS_MIN_JOURNAL_BLOCKS) {
+		printk (KERN_ERR "Journal length (%d blocks) too short.\n",
+			journal->j_maxlen);
+		return -EINVAL;
+	}
+
+	/* Zero out the entire journal on disk.  We cannot afford to
+	   have any blocks on disk beginning with JFS_MAGIC_NUMBER. */
+	jfs_debug(1, "JFS: Zeroing out journal blocks...\n");
+	for (i = 1; i < journal->j_maxlen; i++) {
+		blocknr = i;
+		if (journal->j_inode) {
+			blocknr = bmap(journal->j_inode, blocknr);
+			if (blocknr == 0) {
+				printk (KERN_ERR 
+					"Error writing journal block %d\n", i);
+				return -EIO;
+			}
+		}
+		bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);
+		memset (bh->b_data, 0, journal->j_blocksize);
+		mark_buffer_dirty(bh, 0);
+		brelse(bh);
+	}
+	sync_buffers(journal->j_dev, 1);
+	jfs_debug(1, "JFS: journal cleared.\n");
+	
+	/* OK, fill in the initial static fields in the new superblock */
+	sb = journal->j_superblock;
+
+	sb->s_header.h_magic	 = htonl(JFS_MAGIC_NUMBER);
+	sb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2);
+	
+	sb->s_blocksize	= htonl(journal->j_blocksize);
+	sb->s_maxlen	= htonl(journal->j_maxlen);
+	sb->s_first	= htonl(1);
+
+	journal->j_transaction_sequence = 1;
+	
+	journal->j_flags &= ~JFS_ABORT;
+	journal->j_format_version = 2;
+
+	return journal_reset(journal);
+}
+
+/*
+ * Update a journal's dynamic superblock fields and write it to disk,
+ * optionally waiting for the IO to complete. 
+*/
+
+void journal_update_superblock(journal_t *journal, int wait)
+{
+	journal_superblock_t *sb = journal->j_superblock;
+	struct buffer_head *bh = journal->j_sb_buffer;
+	
+	sb->s_sequence = htonl(journal->j_tail_sequence);
+	sb->s_start    = htonl(journal->j_tail);
+	sb->s_errno    = htonl(journal->j_errno);
+
+	mark_buffer_dirty(bh, 0);
+	ll_rw_block(WRITE, 1, &bh);
+	if (wait)
+		wait_on_buffer(bh);
+}
+	
+
+/* 
+ * Read the superblock for a given journal, performing initial
+ * validation of the format.
+ */
+
+static int journal_get_superblock(journal_t *journal)
+{
+	struct buffer_head *bh;
+	journal_superblock_t *sb;
+	
+	bh = journal->j_sb_buffer;
+	J_ASSERT(bh != NULL);
+	ll_rw_block(READ, 1, &bh);
+	wait_on_buffer(bh);
+	if (!buffer_uptodate(bh)) {
+		printk (KERN_ERR "JFS: IO error reading journal superblock\n");
+		return -EIO;
+	}
+
+	sb = journal->j_superblock;
+
+	if (sb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER) ||
+	    sb->s_blocksize != htonl(journal->j_blocksize)) {
+		printk (KERN_WARNING "JFS: no valid journal superblock found\n");
+		return -EINVAL;
+	}
+
+	switch(ntohl(sb->s_header.h_blocktype)) {
+	case JFS_SUPERBLOCK_V1:
+		journal->j_format_version = 1;
+		break;
+	case JFS_SUPERBLOCK_V2:
+		journal->j_format_version = 2;
+		break;
+	default:
+		printk (KERN_WARNING "JFS: unrecognised superblock format ID\n");
+		return -EINVAL;
+	}
+
+	if (ntohl(sb->s_maxlen) < journal->j_maxlen)
+		journal->j_maxlen = ntohl(sb->s_maxlen);
+	else if (ntohl(sb->s_maxlen) > journal->j_maxlen) {
+		printk (KERN_WARNING "JFS: journal file too short\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * Given a journal_t structure which tells us which disk blocks contain
+ * a journal, read the journal from disk to initialise the in-memory
+ * structures.
+ */
+
+int journal_load (journal_t *journal)
+{
+	journal_superblock_t *sb;
+	unsigned long blocknr;
+	int err;
+	
+	blocknr = 0;
+	if (journal->j_inode) {
+		blocknr = bmap(journal->j_inode, blocknr);
+		J_ASSERT(blocknr != 0);
+	}
+
+	err = journal_get_superblock(journal);
+	if (err)
+		return err;
+	
+	sb = journal->j_superblock;
+	
+	journal->j_tail_sequence = ntohl(sb->s_sequence);
+	journal->j_tail = ntohl(sb->s_start);
+	journal->j_first = ntohl(sb->s_first);
+	journal->j_last = ntohl(sb->s_maxlen);
+	journal->j_errno = ntohl(sb->s_errno);
+
+	/* Let the recovery code check whether it needs to recover any
+	 * data from the journal. */
+	if (journal_recover(journal))
+		goto recovery_error;
+	
+	/* OK, we've finished with the dynamic journal bits:
+	 * reinitialise the dynamic contents of the superblock in memory
+	 * and reset them on disk. */
+	if (journal_reset(journal))
+		goto recovery_error;
+
+	journal->j_flags &= ~JFS_ABORT;
+	return 0;
+
+recovery_error:
+	printk (KERN_WARNING "JFS: recovery failed\n");
+	return -EIO;
+}
+
+/* 
+ * Release a journal_t structure once it is no longer in use by the
+ * journaled object.  
+ */
+
+void journal_release (journal_t *journal)
+{
+	/* Wait for the commit thread to wake up and die. */
+	journal_kill_thread(journal);
+
+	/* Force a final log commit */
+	if (journal->j_running_transaction)
+		journal_commit_transaction(journal);
+
+	/* Force any old transactions to disk */
+	lock_journal(journal);
+	while (journal->j_checkpoint_transactions != NULL)
+		log_do_checkpoint(journal, 1);
+
+	J_ASSERT(journal->j_running_transaction == NULL);	
+	J_ASSERT(journal->j_committing_transaction == NULL);
+	J_ASSERT(journal->j_checkpoint_transactions == NULL);
+
+	/* We can now mark the journal as empty. */
+	journal->j_tail = 0;
+	journal->j_tail_sequence = ++journal->j_transaction_sequence;
+	journal_update_superblock(journal, 1);
+
+	if (journal->j_inode)
+		iput(journal->j_inode);
+
+	unlock_journal(journal);
+	brelse(journal->j_sb_buffer);
+	kfree(journal);
+}
+
+
+/* Published API: Check whether the journal has a given set of features.
+ * Return true (non-zero) if it does. */
+
+int journal_check_features (journal_t *journal, 
+			    unsigned long compat, 
+			    unsigned long ro, 
+			    unsigned long incompat)
+{
+	journal_superblock_t *sb;
+	
+	if (!compat && !ro && !incompat)
+		return 1;
+	
+	sb = journal->j_superblock;
+	
+	if (((be32_to_cpu(sb->s_feature_compat) & compat) == compat) &&
+	    ((be32_to_cpu(sb->s_feature_ro_compat) & ro) == ro) &&
+	    ((be32_to_cpu(sb->s_feature_incompat) & incompat) == incompat))
+		return 1;
+	
+	return 0;
+}
+
+
+/* 
+ * Published API:
+ * Given an initialised but unloaded journal struct, poke about in the
+ * on-disk structure to update it to the most recent supported version.
+ */
+
+int journal_update_format (journal_t *journal)
+{
+	journal_superblock_t *sb;
+	int err;
+	
+	err = journal_get_superblock(journal);
+	if (err)
+		return err;
+
+	sb = journal->j_superblock;
+
+	switch (ntohl(sb->s_header.h_blocktype)) {
+	case JFS_SUPERBLOCK_V2:
+		return 0;
+	case JFS_SUPERBLOCK_V1:
+		return journal_convert_superblock_v1(journal, sb);
+	default:
+	}
+	return -EINVAL;
+}
+
+static int journal_convert_superblock_v1(journal_t *journal,
+					 journal_superblock_t *sb)
+{
+	int offset, blocksize;
+	struct buffer_head *bh;
+
+	printk(KERN_WARNING "JFS: Converting superblock from version 1 to 2.\n");
+	
+	/* Pre-initialise new fields to zero */
+	offset = ((char *) &(sb->s_feature_compat)) - ((char *) sb);
+	blocksize = ntohl(sb->s_blocksize);
+	memset(&sb->s_feature_compat, 0, blocksize-offset);
+
+	sb->s_nr_users = cpu_to_be32(1);
+	sb->s_header.h_blocktype = cpu_to_be32(JFS_SUPERBLOCK_V2);
+	journal->j_format_version = 2;
+
+	/* By default, the only new feature used on a V2 superblock is
+	 * the revoke log record */
+	sb->s_feature_incompat = cpu_to_be32(JFS_FEATURE_INCOMPAT_REVOKE);
+
+	bh = journal->j_sb_buffer;
+	mark_buffer_dirty(bh, 0);
+	ll_rw_block(WRITE, 1, &bh);
+	wait_on_buffer(bh);
+	return 0;
+}
+
+
+/* 
+ * Flush all data for a given journal to disk and empty the journal.
+ * Filesystems can use this when remounting readonly to ensure that
+ * recovery does not need to happen on remount.  
+ */
+
+int journal_flush (journal_t *journal)
+{
+	int err = 0;
+	transaction_t *transaction = NULL;
+	
+	/* Force everything buffered to the log... */
+	if (journal->j_running_transaction) {
+		transaction = journal->j_running_transaction;
+		log_start_commit(journal, transaction);
+	} else if (journal->j_committing_transaction)
+		transaction = journal->j_committing_transaction;
+
+	/* Wait for the log commit to complete... */
+	if (transaction)
+		log_wait_commit(journal, transaction->t_tid);
+	
+	/* ...and flush everything in the log out to disk. */
+	lock_journal(journal);
+	while (!err && journal->j_checkpoint_transactions != NULL)
+		err = log_do_checkpoint(journal, journal->j_maxlen);
+	unlock_journal(journal);
+
+	J_ASSERT(!journal->j_running_transaction);
+	J_ASSERT(!journal->j_committing_transaction);
+	J_ASSERT(!journal->j_checkpoint_transactions);
+	J_ASSERT(journal->j_head == journal->j_tail);
+	J_ASSERT(journal->j_tail_sequence == journal->j_transaction_sequence);
+	
+	return err;
+}
+
+/*
+ * journal_dev_name: format a character string to describe on what
+ * device this journal is present.  
+ */
+
+char * journal_dev_name(journal_t *journal)
+{
+	kdev_t dev;
+	
+	if (journal->j_inode)
+		dev = journal->j_inode->i_dev;
+	else
+		dev = journal->j_dev;
+	
+	return kdevname(dev);
+}
+
+/* 
+ * journal_abort: perform a complete, immediate shutdown of the ENTIRE
+ * journal (not of a single transaction).  This operation cannot be
+ * undone without closing and reopening the journal.
+ *
+ * The journal_abort function is intended to support higher level error
+ * recovery mechanisms such as the ext2/ext3 remount-readonly error
+ * mode.
+ *
+ * Journal abort has very specific semantics.  Any existing dirty,
+ * unjournaled buffers in the main filesystem will still be written to
+ * disk by bdflush, but the journaling mechanism will be suspended
+ * immediately and no further transaction commits will be honoured.  
+ * 
+ * Any dirty, journaled buffers will be written back to disk without
+ * hitting the journal.  Atomicity cannot be guaranteed on an aborted
+ * filesystem, but we _do_ attempt to leave as much data as possible
+ * behind for fsck to use for cleanup.
+ *
+ * Any attempt to get a new transaction handle on a journal which is in
+ * ABORT state will just result in an -EROFS error return.  A
+ * journal_stop on an existing handle will return -EIO if we have
+ * entered abort state during the update.  
+ *
+ * Recursive transactions are not disturbed by journal abort until the
+ * final journal_stop, which will receive the -EIO error.
+ *
+ * Finally, the journal_abort call allows the caller to supply an errno
+ * which will be recored (if possible) in the journal superblock.  This
+ * allows a client to record failure conditions in the middle of a
+ * transaction without having to complete the transaction to record the
+ * failure to disk.  ext3_error, for example, now uses this
+ * functionality.
+ *
+ * Errors which originate from within the journaling layer will NOT
+ * supply an errno; a null errno implies that absolutely no further
+ * writes are done to the journal (unless there are any already in
+ * progress).
+ */
+
+/* Quick version for internal journal use (doesn't lock the journal) */
+void __journal_abort (journal_t *journal)
+{
+	transaction_t *transaction;
+
+	printk (KERN_ERR "Aborting journal on device %s.\n", 
+		journal_dev_name(journal));
+
+	journal->j_flags |= JFS_ABORT;
+	transaction = journal->j_running_transaction;
+	if (transaction)
+		log_start_commit(journal, transaction);
+}
+
+/* Full version for external use */
+void journal_abort (journal_t *journal, int errno)
+{
+	lock_journal(journal);
+	
+	if (journal->j_flags & JFS_ABORT)
+		goto out;
+	
+	if (!journal->j_errno)
+		journal->j_errno = errno;
+
+	__journal_abort(journal);
+
+	if (errno)
+		journal_update_superblock(journal, 1);
+
+ out:
+	unlock_journal(journal);
+}
+
+int journal_errno (journal_t *journal)
+{
+	int err;
+	
+	lock_journal(journal);
+	if (journal->j_flags & JFS_ABORT)
+		err = -EROFS;
+	else 
+		err = journal->j_errno;
+	unlock_journal(journal);
+	return err;
+}
+
+int journal_clear_err (journal_t *journal)
+{
+	int err = 0;
+	
+	lock_journal(journal);
+	if (journal->j_flags & JFS_ABORT)
+		err = -EROFS;
+	else 
+		journal->j_errno = 0;
+	unlock_journal(journal);
+	return err;
+}
+
+void journal_ack_err (journal_t *journal)
+{
+	lock_journal(journal);
+	if (journal->j_errno)
+		journal->j_flags |= JFS_ACK_ERR;
+	unlock_journal(journal);
+}
+
+
--- linux-2.2.18pre17.ext3-0.0.5a/fs/jfs/recovery.c.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/jfs/recovery.c	Wed Oct 25 19:10:56 2000
@@ -0,0 +1,418 @@
+/*
+ * linux/fs/recovery.c
+ * 
+ * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
+ *
+ * Copyright 1999 Red Hat Software --- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * Journal recovery routines for the generic filesystem journaling code;
+ * part of the ext2fs journaling system.  
+ */
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/jfs.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/locks.h>
+#include <linux/buffer.h>
+
+
+/* Release readahead buffers after use */
+static void brelse_array(struct buffer_head *b[], int n)
+{
+	while (--n >= 0)
+		brelse (b[n]);
+}
+
+
+/*
+ * When reading from the journal, we are going through the block device
+ * layer directly and so there is no readahead being done for us.  We
+ * need to implement any readahead ourselves if we want it to happen at
+ * all.  Recovery is basically one long sequential read, so make sure we
+ * do the IO in reasonably large chunks.
+ *
+ * This is not so critical that we need to be enormously clever about
+ * the readahead size, though.  128K is a purely arbitrary, good-enough
+ * fixed value.
+ */
+
+static int do_readahead(journal_t *journal, unsigned int start)
+{
+	int err;
+	unsigned int max, nbufs, next, blocknr;
+	struct buffer_head *bh;
+	
+	#define MAXBUF 8
+	struct buffer_head * bufs[MAXBUF];
+	
+	/* Do up to 128K of readahead */
+	max = start + (128 * 1024 / journal->j_blocksize);
+	if (max > journal->j_maxlen)
+		max = journal->j_maxlen;
+
+	/* Do the readahead itself.  We'll submit MAXBUF buffer_heads at
+	 * a time to the block device IO layer. */
+	
+	nbufs = 0;
+	
+	for (next = start; next < max; next++) {
+		blocknr = next;
+		if (journal->j_inode)
+			blocknr = bmap(journal->j_inode, next);
+		if (!blocknr) {
+			printk (KERN_ERR "JFS: bad block at offset %u\n",
+				next);
+			err = -EIO;
+			goto failed;
+		}
+		
+		bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);
+		if (!bh) {
+			err = -ENOMEM;
+			goto failed;
+		}
+
+		if (!buffer_uptodate(bh) && !buffer_locked(bh)) {
+			bufs[nbufs++] = bh;
+			if (nbufs == MAXBUF) {
+				ll_rw_block(READ, nbufs, bufs);
+				brelse_array(bufs, nbufs);
+				nbufs = 0;
+			}
+		} else
+			brelse(bh);
+	}
+
+	if (nbufs)
+		ll_rw_block(READ, nbufs, bufs);
+	err = 0;
+	
+failed:	
+	if (nbufs) 
+		brelse_array(bufs, nbufs);
+	return err;
+}
+
+/*
+ * Read a block from the journal
+ */
+
+static int jread(struct buffer_head **bhp, journal_t *journal, 
+		 unsigned int offset)
+{
+	unsigned int blocknr;
+	struct buffer_head *bh;
+
+	*bhp = NULL;
+
+	J_ASSERT (offset < journal->j_maxlen);
+			
+	blocknr = offset;
+	if (journal->j_inode)
+		blocknr = bmap(journal->j_inode, offset);
+	
+	if (!blocknr) {
+		printk (KERN_ERR "JFS: bad block at offset %u\n",
+			offset);
+		return -EIO;
+	}
+	
+	bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);
+	if (!bh)
+		return -ENOMEM;
+	
+	if (!buffer_uptodate(bh)) {
+		/* If this is a brand new buffer, start readahead.
+                   Otherwise, we assume we are already reading it.  */
+		if (!buffer_req(bh))
+			do_readahead(journal, offset);
+		wait_on_buffer(bh);
+	}
+	
+	if (!buffer_uptodate(bh)) {
+		printk (KERN_ERR "JFS: Failed to read block at offset %u\n",
+			offset);
+		brelse(bh);
+		return -EIO;
+	}
+		
+	*bhp = bh;
+	return 0;
+}
+
+
+/*
+ * Count the number of in-use tags in a journal descriptor block.
+ */
+
+int count_tags(struct buffer_head *bh, int size)
+{
+	char *			tagp;
+	journal_block_tag_t *	tag;
+	int			nr = 0;
+	
+	tagp = &bh->b_data[sizeof(journal_header_t)];
+	
+	while ((tagp - bh->b_data + sizeof(journal_block_tag_t)) <= size) {
+		tag = (journal_block_tag_t *) tagp;
+		
+		nr++;
+		tagp += sizeof(journal_block_tag_t);
+		if (!(tag->t_flags & htonl(JFS_FLAG_SAME_UUID)))
+			tagp += 16;
+
+		if (tag->t_flags & htonl(JFS_FLAG_LAST_TAG))
+			break;
+	}
+	
+	return nr;
+}
+
+
+/* Make sure we wrap around the log correctly! */
+#define wrap(journal, var)						\
+do {									\
+	if (var >= (journal)->j_last)					\
+		var -= ((journal)->j_last - (journal)->j_first);	\
+} while (0)
+
+/*
+ * journal_recover
+ *
+ * The primary function for recovering the log contents when mounting a
+ * journaled device.  
+ */
+
+int journal_recover(journal_t *journal)
+{
+	unsigned int		first_commit_ID, next_commit_ID;
+	unsigned long		next_log_block;
+	unsigned long		transaction_start;
+	int			err, success = 0;
+	journal_superblock_t *	sb;
+	journal_header_t * 	tmp;
+	struct buffer_head *	bh;
+
+	/* Precompute the maximum metadata descriptors in a descriptor block */
+	int			MAX_BLOCKS_PER_DESC;
+	MAX_BLOCKS_PER_DESC = ((journal->j_blocksize-sizeof(journal_header_t))
+			       / sizeof(journal_block_tag_t));
+
+	/* 
+	 * First thing is to establish what we expect to find in the log
+	 * (in terms of transaction IDs), and where (in terms of log
+	 * block offsets): query the superblock.  
+	 */
+
+	sb = journal->j_superblock;
+	next_commit_ID = ntohl(sb->s_sequence);
+	next_log_block = ntohl(sb->s_start);
+
+	first_commit_ID = next_commit_ID;
+	
+	/* 
+	 * The journal superblock's s_start field (the current log head)
+	 * is always zero if, and only if, the journal was cleanly
+	 * unmounted.  
+	 */
+
+	if (!sb->s_start) {
+		jfs_debug(1, "No recovery required, last transaction %d\n",
+			  ntohl(sb->s_sequence));
+		journal->j_transaction_sequence = ++next_commit_ID;
+		return 0;
+	}
+	
+	jfs_debug(1, "Starting recovery\n");
+	
+	/*
+	 * Now we walk through the log, transaction by transaction,
+	 * making sure that each transaction has a commit block in the
+	 * expected place.  Each complete transaction gets replayed back
+	 * into the main filesystem. 
+	 */
+
+	while (1) { 
+		jfs_debug(2, "Looking for commit ID %u at %lu/%lu\n",
+			  next_commit_ID, next_log_block, journal->j_last);
+ 		transaction_start = next_log_block;
+
+		while (next_log_block < journal->j_last) {
+			/* Skip over each chunk of the transaction
+			 * looking either the next descriptor block or
+			 * the final commit record. */
+
+			jfs_debug(3, "JFS: checking block %ld\n", 
+				  next_log_block);
+			err = jread(&bh, journal, next_log_block);
+			if (err)
+				goto failed;
+			
+			/* What kind of buffer is it? 
+			 * 
+			 * If it is a descriptor block, work out the
+			 * expected location of the next and skip to it.
+			 *
+			 * If it is the right commit block, end the
+			 * search and start recovering the transaction.
+			 *
+			 * Any non-control block, or an unexpected
+			 * control block is interpreted as old data from
+			 * a previous wrap of the log: stop recovery at
+			 * this point.  
+			 */
+		
+			tmp = (journal_header_t *) bh->b_data;
+			
+			if (tmp->h_magic == htonl(JFS_MAGIC_NUMBER)) {
+				int blocktype = ntohl(tmp->h_blocktype);
+				jfs_debug(3, "Found magic %d\n", blocktype);
+				
+				if (blocktype == JFS_DESCRIPTOR_BLOCK) {
+					/* Work out where the next descriptor
+					 * should be. */
+					next_log_block++;
+					next_log_block += count_tags(bh, journal->j_blocksize);
+					wrap(journal, next_log_block);
+					brelse(bh);
+					continue;
+				} else if (blocktype == JFS_COMMIT_BLOCK) {
+					unsigned int sequence = tmp->h_sequence;
+					brelse(bh);
+					if (sequence == htonl(next_commit_ID))
+						goto commit;
+					jfs_debug(2, "found sequence %d, expected %d.\n", ntohl(sequence), next_commit_ID);
+					goto finished;
+				}
+			}
+
+			/* We didn't recognise it?  OK, we've gone off
+			 * the tail of the log in that case. */
+			brelse(bh);
+			break;
+		}
+
+		goto finished;
+		
+	commit:
+		jfs_debug(2, "Found transaction %d\n", next_commit_ID);
+
+		/* OK, we have a transaction to commit.  Rewind to the
+		 * start of it, gather up all of the buffers in each
+		 * transaction segment, and replay the segments one by
+		 * one. */
+
+		next_log_block = transaction_start;
+		
+		while (1) {
+			int			flags;
+			char *			tagp;
+			journal_block_tag_t *	tag;
+			struct buffer_head *	obh;
+			struct buffer_head *	nbh;
+			
+			err = jread(&bh, journal, next_log_block++);
+			wrap(journal, next_log_block);
+			if (err)
+				goto failed;
+			
+			tmp = (journal_header_t *) bh->b_data;
+			J_ASSERT(tmp->h_magic == htonl(JFS_MAGIC_NUMBER));
+			
+			/* If it is the commit block, then we are all done! */
+
+			if (tmp->h_blocktype == htonl(JFS_COMMIT_BLOCK)) {
+				brelse(bh);
+				break;
+			}
+			
+			/* A descriptor block: we can now write all of
+			 * the data blocks.  Yay, useful work is finally
+			 * getting done here! */
+
+			tagp = &bh->b_data[sizeof(journal_header_t)];
+			
+			while ((tagp - bh->b_data +sizeof(journal_block_tag_t))
+			       <= journal->j_blocksize) {
+				tag = (journal_block_tag_t *) tagp;
+				flags = ntohl(tag->t_flags);
+				
+				err = jread(&obh, journal, next_log_block++);
+				wrap(journal, next_log_block);
+				if (err) {
+					/* Recover what we can, but
+					 * report failure at the end. */
+					success = err;
+					printk (KERN_ERR 
+						"JFS: IO error %d recovering "
+						"block %ld in log\n",
+						err, next_log_block-1);
+				} else {
+					J_ASSERT(obh != NULL);
+
+					/* And find a buffer for the new data
+					 * being restored */
+					nbh = getblk(journal->j_dev, 
+						     ntohl(tag->t_blocknr),
+						     journal->j_blocksize);
+					if (nbh == NULL) {
+						printk(KERN_ERR 
+						       "JFS: Out of memory "
+						       "during recovery.\n");
+						err = -ENOMEM;
+						brelse(bh);
+						brelse(obh);
+						goto failed;
+					}
+
+					memcpy(nbh->b_data, obh->b_data, 
+					       journal->j_blocksize);
+					if (flags & JFS_FLAG_ESCAPE) {
+						* ((unsigned int *) bh->b_data) = htonl(JFS_MAGIC_NUMBER);
+					}
+					
+					mark_buffer_dirty(nbh, 1);
+					// ll_rw_block(WRITE, 1, &nbh);
+					brelse(obh);
+					brelse(nbh);
+				}
+				
+				tagp += sizeof(journal_block_tag_t);
+				if (!(flags & JFS_FLAG_SAME_UUID))
+					tagp += 16;
+
+				if (flags & JFS_FLAG_LAST_TAG)
+					break;
+
+			} /* end of tag loop */
+
+			brelse(bh);
+			
+		} /* end of descriptor block loop */
+			
+		/* We have now replayed that entire transaction: start
+		 * looking for the next transaction. */
+		next_commit_ID++;
+	}
+		
+ finished:
+	err = success;
+	fsync_dev(journal->j_dev);
+
+ failed:
+	
+	/* Restart the log at the next transaction ID, thus invalidating
+	 * any existing commit records in the log. */
+	jfs_debug(0, "JFS: recovery, exit status %d, "
+		  "recovered transactions %u to %u\n", 
+		  err, first_commit_ID, next_commit_ID);
+	journal->j_transaction_sequence = ++next_commit_ID;
+
+	return err;
+}
--- linux-2.2.18pre17.ext3-0.0.5a/fs/jfs/revoke.c.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/jfs/revoke.c	Wed Oct 25 19:10:56 2000
@@ -0,0 +1,41 @@
+/*
+ * linux/fs/revoke.c
+ * 
+ * Written by Stephen C. Tweedie <sct@redhat.com>, 2000
+ *
+ * Copyright 2000 Red Hat corp --- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * Journal commit routines for the generic filesystem journaling code;
+ * part of the ext2fs journaling system.
+ */
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/jfs.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/locks.h>
+#include <linux/buffer.h>
+
+
+/* 
+ * journal_revoke: revoke a given buffer_head from the journal.  This
+ * prevents the block from being replayed during recovery if we take a
+ * crash after this current transaction commits.  Any subsequent
+ * metadata writes of the buffer in this transaction cancel the
+ * revoke.  
+ *
+ * Note that this call may block --- it is up to the caller to make
+ * sure that there are no further calls to journal_write_metadata
+ * before the revoke is complete.  In ext3, this implies calling the
+ * revoke before clearing the block bitmap when we are deleting
+ * metadata. 
+ */
+int journal_revoke(handle_t *handle, int blocknr)
+{
+	return 0;
+}
--- linux-2.2.18pre17.ext3-0.0.5a/fs/jfs/transaction.c.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/jfs/transaction.c	Wed Oct 25 19:24:25 2000
@@ -0,0 +1,1105 @@
+/*
+ * linux/fs/transaction.c
+ * 
+ * Written by Stephen C. Tweedie <sct@redhat.com>, 1998
+ *
+ * Copyright 1998 Red Hat corp --- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * Generic filesystem transaction handling code; part of the ext2fs
+ * journaling system.  
+ *
+ * This file manages transactions (compound commits managed by the
+ * journaling code) and handles (individual atomic operations by the
+ * filesystem).
+ */
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/jfs.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/locks.h>
+#include <linux/timer.h>
+
+
+/* 
+ * Forward declarations for internal use by this file only.
+ */
+
+static inline void 
+blist_add_buffer(struct buffer_head **, struct buffer_head *);
+static inline void
+blist_del_buffer(struct buffer_head **, struct buffer_head *);
+
+
+/*
+ * Journal locking.
+ *
+ * We need to lock the journal during transaction state changes so that
+ * nobody ever tries to take a handle on the running transaction while
+ * we are in the middle of moving it to the commit phase.  
+ *
+ * Note that the locking is completely interrupt unsafe.  We never touch
+ * journal structures from interrupts.
+ */
+
+void __wait_on_journal (journal_t * journal)
+{
+	while (journal->j_locked)
+		sleep_on (&journal->j_wait_lock);
+}
+
+
+/*
+ * get_transaction: obtain a new transaction_t object.
+ *
+ * Simply allocate and initialise a new transaction.  Create it in
+ * RUNNING state and add it to the current journal (which should not
+ * have an existing running transaction: we only make a new transaction
+ * once we have started to commit the old one).
+ *
+ * Preconditions:
+ *	The journal MUST be locked.  We don't perform atomic mallocs on the
+ *	new transaction	and we can't block without protecting against other
+ *	processes trying to touch the journal while it is in transition.
+ */
+
+transaction_t * get_transaction (journal_t * journal)
+{
+	transaction_t * transaction;
+
+	J_ASSERT (journal->j_locked);
+	
+	transaction = kmalloc (sizeof (transaction_t), GFP_KERNEL);
+	if (!transaction)
+		return NULL;
+	
+	memset (transaction, 0, sizeof (transaction_t));
+	
+	transaction->t_journal = journal;
+	transaction->t_state = T_RUNNING;
+	transaction->t_tid = journal->j_transaction_sequence++;
+	transaction->t_expires = jiffies + journal->j_commit_interval;
+
+	/* Set up the commit timer for the new transaction. */
+	J_ASSERT (!journal->j_commit_timer_active);
+	journal->j_commit_timer_active = 1;
+	journal->j_commit_timer->expires = transaction->t_expires;
+	add_timer(journal->j_commit_timer);
+	
+	J_ASSERT (journal->j_running_transaction == NULL);
+	journal->j_running_transaction = transaction;
+
+	return transaction;
+}
+
+
+
+/*
+ * Handle management.
+ *
+ * A handle_t is an object which represents a single atomic update to a
+ * filesystem, and which tracks all of the modifications which form part
+ * of that one update.
+ */
+
+/*
+ * start_this_handle: Given a handle, deal with any locking or stalling
+ * needed to make sure that there is enough journal space for the handle
+ * to begin.  Attach the handle to a transaction and set up the
+ * transaction's buffer credits.  
+ */
+
+static int start_this_handle(journal_t *journal, handle_t *handle)
+{
+	transaction_t *transaction;
+	int needed;
+	int nblocks = handle->h_buffer_credits;
+	
+	jfs_debug(4, "New handle %p going live.\n", handle);
+
+repeat:
+
+	lock_journal(journal);
+
+	if ((journal->j_flags & JFS_ABORT) ||
+	    (journal->j_errno != 0 && !(journal->j_flags & JFS_ACK_ERR))) {
+		unlock_journal(journal);
+		return -EROFS; 
+	}
+	
+repeat_locked:
+	if (!journal->j_running_transaction)
+		get_transaction(journal);
+	/* @@@ Error? */
+	J_ASSERT(journal->j_running_transaction);
+	
+	transaction = journal->j_running_transaction;
+
+	/* If the current transaction is locked down for commit, wait
+	 * for the lock to be released. */
+
+	if (transaction->t_state == T_LOCKED) {
+		unlock_journal(journal);
+		jfs_debug(3, "Handle %p stalling...\n", handle);
+		sleep_on(&journal->j_wait_transaction_locked);
+		goto repeat;
+	}
+	
+	/* If there is not enough space left in the log to write all
+	 * potential buffers requested by this operation, we need to
+	 * stall pending a log checkpoint to free some more log
+	 * space. */
+
+	needed = transaction->t_outstanding_credits + nblocks;
+
+	if (needed > journal->j_max_transaction_buffers) {
+		/* If the current transaction is already too large, then
+		 * start to commit it: we can then go back and attach
+		 * this handle to a new transaction. */
+		
+		jfs_debug(2, "Handle %p starting new commit...\n", handle);
+		log_start_commit(journal, transaction);
+		unlock_journal(journal);
+		sleep_on(&journal->j_wait_transaction_locked);
+		lock_journal(journal);
+		goto repeat_locked;
+	}
+
+	/* 
+	 * The commit code assumes that it can get enough log space
+	 * without forcing a checkpoint.  This is *critical* for
+	 * correctness: a checkpoint of a buffer which is also
+	 * associated with a committing transaction creates a deadlock,
+	 * so commit simply cannot force through checkpoints.
+	 *
+	 * We must therefore ensure the necessary space in the journal
+	 * *before* starting to dirty potentially checkpointed buffers
+	 * in the new transaction. 
+	 *
+	 * The worst part is, any transaction currently committing can
+	 * reduce the free space arbitrarily.  Be careful to account for
+	 * those buffers when checkpointing.
+	 */
+
+	needed = journal->j_max_transaction_buffers;
+	if (journal->j_committing_transaction) 
+		needed += journal->j_committing_transaction->t_outstanding_credits;
+	
+	if (log_space_left(journal) < needed) {
+		jfs_debug(2, "Handle %p waiting for checkpoint...\n", handle);
+		log_wait_for_space(journal, needed);
+		goto repeat_locked;
+	}
+
+	/* OK, account for the buffers that this operation expects to
+	 * use and add the handle to the running transaction. */
+
+	handle->h_transaction = transaction;
+	transaction->t_outstanding_credits += nblocks;
+	transaction->t_updates++;
+	jfs_debug(4, "Handle %p given %d credits (total %d, free %d)\n",
+		  handle, nblocks, transaction->t_outstanding_credits,
+		  log_space_left(journal));
+
+	unlock_journal(journal);
+	
+	return 0;
+}
+
+
+/*
+ * Obtain a new handle.  
+ *
+ * We make sure that the transaction can guarantee at least nblocks of
+ * modified buffers in the log.  We block until the log can guarantee
+ * that much space.  
+ *
+ * This function is visible to journal users (like ext2fs), so is not
+ * called with the journal already locked.
+ *
+ * Return a pointer to a newly allocated handle, or NULL on failure
+ */
+
+handle_t *journal_start (journal_t *journal, int nblocks)
+{
+	handle_t *handle;
+	int err;
+	
+	if (!journal)
+		return ERR_PTR(-EROFS);
+	
+	if (current->j_handle) {
+		current->j_handle->h_ref++;
+		return current->j_handle;
+	}
+	
+	handle = kmalloc (sizeof (handle_t), GFP_KERNEL);
+	if (!handle)
+		return ERR_PTR(-ENOMEM);
+	memset (handle, 0, sizeof (handle_t));
+
+	handle->h_buffer_credits = nblocks;
+	handle->h_ref = 1;
+	current->j_handle = handle;
+
+  	err = start_this_handle(journal, handle);
+	if (err < 0) {
+		kfree(handle);
+		current->j_handle = NULL;
+		return ERR_PTR(err);
+	}
+
+	return handle;
+}
+
+
+/*
+ * journal_extend: extend buffer credits.
+ *
+ * Some transactions, such as large extends and truncates, can be done
+ * atomically all at once or in several stages.  The operation requests
+ * a credit for a number of buffer modications in advance, but can
+ * extend its credit if it needs more.  
+ *
+ * journal_extend tries to give the running handle more buffer credits.
+ * It does not guarantee that allocation: this is a best-effort only.
+ * The calling process MUST be able to deal cleanly with a failure to
+ * extend here.
+ *
+ * Return 0 on success, non-zero on failure.
+ */
+
+int journal_extend (handle_t *handle, int nblocks)
+{
+	transaction_t *transaction = handle->h_transaction;
+	journal_t *journal = transaction->t_journal;
+	int result = 1;
+	int wanted;
+	
+	lock_journal (journal);
+
+	/* Don't extend a locked-down transaction! */
+	if (handle->h_transaction->t_state != T_RUNNING) {
+		jfs_debug(3, "denied handle %p %d blocks: "
+			  "transaction not running\n", handle, nblocks);
+		goto error_out;
+	}
+	
+	wanted = transaction->t_outstanding_credits + nblocks;
+	
+	if (wanted > journal->j_max_transaction_buffers) {
+		jfs_debug(3, "denied handle %p %d blocks: "
+			  "transaction too large\n", handle, nblocks);
+		goto error_out;
+	}
+
+	if (wanted > log_space_left(journal)) {
+		jfs_debug(3, "denied handle %p %d blocks: "
+			  "insufficient log space\n", handle, nblocks);
+		goto error_out;
+	}
+	
+	handle->h_buffer_credits += nblocks;
+	transaction->t_outstanding_credits += nblocks;
+	result = 0;
+
+	jfs_debug(3, "extended handle %p by %d\n", handle, nblocks);
+	
+error_out:
+	unlock_journal (journal);
+	return result;
+}
+
+
+/*
+ * journal_restart: restart a handle for a multi-transaction filesystem
+ * operation.
+ *
+ * If the journal_extend() call above fails to grant new buffer credits
+ * to a running handle, a call to journal_restart will commit the
+ * handle's transaction so far and reattach the handle to a new
+ * transaction capabable of guaranteeing the requested number of
+ * credits.
+ */
+
+int journal_restart(handle_t *handle, int nblocks)
+{
+	transaction_t *transaction = handle->h_transaction;
+	journal_t *journal = transaction->t_journal;
+		
+	/* First unlink the handle from its current transaction, and
+	 * start the commit on that. */
+	
+	J_ASSERT (transaction->t_updates > 0);
+	J_ASSERT (current->j_handle == handle);
+
+	transaction->t_outstanding_credits -= handle->h_buffer_credits;
+	transaction->t_updates--;
+
+	if (!transaction->t_updates)
+		wake_up(&transaction->t_wait);
+
+	jfs_debug(2, "restarting handle %p\n", handle);
+	log_start_commit(journal, transaction);
+
+	handle->h_buffer_credits = nblocks;
+	return start_this_handle(journal, handle);
+}
+
+
+static void lock_journal_bh_wait(struct buffer_head *bh, journal_t *journal)
+{
+repeat:
+	wait_on_buffer(bh);
+	lock_journal(journal);
+	if (buffer_locked(bh)) {
+		unlock_journal(journal);
+		goto repeat;
+	}
+}
+
+
+/*
+ * journal_get_write_access: notify intent to modify a buffer for metadata
+ * (not data) update.
+ *
+ * If the buffer is already part of the current transaction, then there
+ * is nothing we need to do.  If it is already part of a prior
+ * transaction which we are still committing to disk, then we need to
+ * make sure that we do not overwrite the old copy: we do copy-out to
+ * preserve the copy going to disk.  We also account the buffer against
+ * the handle's metadata buffer credits (unless the buffer is already
+ * part of the transaction, that is).
+ *
+ * Returns an error code or 0 on success.  */
+
+int do_get_write_access (handle_t *handle, struct buffer_head *bh) 
+{
+	transaction_t *transaction = handle->h_transaction;
+	journal_t *journal = transaction->t_journal;
+	int error = 0;
+
+repeat:
+	/* @@@ Need to check for errors here at some point. */
+
+	J_ASSERT (buffer_uptodate(bh));
+	
+	/* The buffer is already part of this transaction if
+	 * b_transaction or b_next_transaction points to it. */
+
+	if (bh->b_transaction == transaction ||
+	    bh->b_next_transaction == transaction)
+		goto done;
+
+	/* If there is already a copy-out version of this buffer, then
+	 * we don't need to make another one. */
+
+	if (bh->b_frozen_data) {
+		J_ASSERT(bh->b_next_transaction == NULL);
+		bh->b_next_transaction = transaction;
+
+		J_ASSERT(handle->h_buffer_credits > 0);
+		handle->h_buffer_credits--;
+		goto done;
+	}
+	
+	/* Is there data here we need to preserve? */
+	
+	if (bh->b_transaction && bh->b_transaction != transaction) {
+		J_ASSERT (bh->b_next_transaction == NULL);
+		J_ASSERT (bh->b_transaction == journal->j_committing_transaction);
+		J_ASSERT (bh->b_list == BUF_JOURNAL);
+
+		/* There is one case we have to be very careful about.
+		 * If the committing transaction is currently writing
+		 * this buffer out to disk and has NOT made a copy-out,
+		 * then we cannot modify the buffer contents at all
+		 * right now.  The essence of copy-out is that it is the
+		 * extra copy, not the primary copy, which gets
+		 * journaled.  If the primary copy is already going to
+		 * disk then we cannot do copy-out here. */
+
+		if (bh->b_jlist == BJ_Shadow) {
+			unlock_journal(journal);
+			/* commit wakes up all shadow buffers after IO */
+			sleep_on(&bh->b_wait);
+			lock_journal(journal);
+			goto repeat;
+		}
+			
+		bh->b_next_transaction = transaction;
+
+		/* Only do the copy if the currently-owning transaction
+		 * still needs it.  If it is on the Forget list, the
+		 * committing transaction is past that stage.  The
+		 * buffer had better remain locked during the kmalloc,
+		 * but that should be true --- we hold the journal lock
+		 * still and the buffer is already on the BUF_JOURNAL
+		 * list so won't be flushed. */
+
+		if (bh->b_jlist != BJ_Forget) {
+			bh->b_frozen_data = kmalloc(bh->b_size, GFP_KERNEL);
+			if (!bh->b_frozen_data) {
+				error = -ENOMEM;
+				goto done;
+			}
+			memcpy (bh->b_frozen_data, bh->b_data, bh->b_size);
+		}
+	}
+
+	J_ASSERT(handle->h_buffer_credits > 0);
+	handle->h_buffer_credits--;
+	
+	/* Finally, if the buffer is not journaled right now, we need to
+	 * make sure it doesn't get written to disk before the caller
+	 * actually commits the new data. */
+
+	if (!bh->b_transaction) {
+		J_ASSERT (!bh->b_next_transaction);
+		bh->b_transaction = transaction;
+		journal_file_buffer(bh, transaction, BJ_Reserved);
+	}
+	
+ done:
+	if (bh->b_list != BUF_JOURNAL)
+		refile_buffer(bh);
+	return error;
+}
+
+int journal_get_write_access (handle_t *handle, struct buffer_head *bh) 
+{
+	transaction_t *transaction = handle->h_transaction;
+	journal_t *journal = transaction->t_journal;
+	int rc;
+	
+	/* We do not want to get caught playing with fields which the
+	 * log thread also manipulates.  Make sure that the buffer
+	 * completes any outstanding IO before proceeding. */
+	lock_journal_bh_wait(bh, journal);
+	rc = do_get_write_access(handle, bh);
+	unlock_journal(journal);
+
+	return rc;
+}
+
+
+/*
+ * When the user wants to journal a newly created buffer_head
+ * (ie. getblk() returned a new buffer and we are going to populate it
+ * manually rather than reading off disk), then we need to keep the
+ * buffer_head locked until it has been completely filled with new
+ * data.  In this case, we should be able to make the assertion that
+ * the bh is not already part of an existing transaction.  
+ * 
+ * The buffer should already be locked by the caller by this point.
+ * There is no lock ranking violation: it was a newly created,
+ * unlocked buffer beforehand. */
+
+int journal_get_create_access (handle_t *handle, struct buffer_head *bh) 
+{
+	transaction_t *transaction = handle->h_transaction;
+	journal_t *journal = transaction->t_journal;
+
+	wait_on_buffer(bh);
+
+	lock_journal(journal);
+
+	/* The buffer may already belong to this transaction due to
+           pre-zeroing in the filesystem's new_block code */
+	J_ASSERT (bh->b_transaction == transaction || bh->b_transaction == NULL);
+	J_ASSERT (bh->b_next_transaction == NULL);
+	J_ASSERT (buffer_locked(bh));
+
+	J_ASSERT(handle->h_buffer_credits > 0);
+	handle->h_buffer_credits--;
+	
+	bh->b_transaction = transaction;
+	journal_file_buffer(bh, transaction, BJ_Reserved);
+	refile_buffer(bh);
+	unlock_journal(journal);
+	return 0;
+}
+
+
+
+/*
+ * journal_get_undo_access: Notify intent to modify metadata with non-
+ * rewindable consequences
+ *
+ * Sometimes there is a need to distinguish between metadata which has
+ * been committed to disk and that which has not.  The ext3fs code uses
+ * this for freeing and allocating space: we have to make sure that we
+ * do not reuse freed space until the deallocation has been committed,
+ * since if we overwrote that space we would make the delete
+ * un-rewindable in case of a crash.
+ * 
+ * To deal with that, journal_get_undo_access requests write access to a
+ * buffer for parts of non-rewindable operations such as delete
+ * operations on the bitmaps.  The journaling code must keep a copy of
+ * the buffer's contents prior to the undo_access call until such time
+ * as we know that the buffer has definitely been committed to disk.
+ * 
+ * We never need to know which transaction the committed data is part
+ * of: buffers touched here are guaranteed to be dirtied later and so
+ * will be committed to a new transaction in due course, at which point
+ * we can discard the old committed data pointer.
+ *
+ * Returns error number or 0 on success.  
+ */
+
+int journal_get_undo_access (handle_t *handle, struct buffer_head *bh)
+{
+	journal_t *journal = handle->h_transaction->t_journal;
+	int err;
+
+	lock_journal_bh_wait(bh, journal);
+
+	/* Make sure it didn't change while we slept... */
+
+	if (!bh->b_committed_data) {
+
+		/* Copy out the current buffer contents into the
+		 * preserved, committed copy. */
+
+		bh->b_committed_data = kmalloc(bh->b_size, GFP_KERNEL);
+		if (!bh->b_committed_data) {
+			unlock_journal(journal);
+			return -ENOMEM;
+		}
+		
+		memcpy (bh->b_committed_data, bh->b_data, bh->b_size);
+	}
+	
+	err = do_get_write_access (handle, bh);
+	unlock_journal(journal);
+	if (!err)
+		J_ASSERT(bh->b_committed_data);
+	return err;
+}
+
+
+
+/* 
+ * journal_dirty_data: mark a buffer as containing dirty data which
+ * needs to be flushed before we can commit the current transaction.  
+ *
+ * The buffer is placed on the transaction's data list and is marked as
+ * belonging to the transaction.
+ * 
+ * Returns error number or 0 on success.  
+ */
+
+int journal_dirty_data (handle_t *handle, struct buffer_head *bh)
+{
+	journal_t *journal = handle->h_transaction->t_journal;
+	lock_journal(journal);
+
+	mark_buffer_dirty(bh, 0);
+	
+	/*
+	 * What if the buffer is already part of a running transaction?
+	 * 
+	 * There are two cases:
+	 * 1) It is part of the current running transaction.  Refile it,
+	 *    just in case we have allocated it as metadata, deallocated
+	 *    it, then reallocated it as data. 
+	 * 2) It is part of the previous, still-committing transaction.
+	 *    If all we want to do is to guarantee that the buffer will be
+	 *    written to disk before this new transaction commits, then
+	 *    being sure that the *previous* transaction has this same 
+	 *    property is sufficient for us!  Just leave it on its old
+	 *    transaction.
+	 *
+	 * In case (2), the buffer must not already exist as metadata
+	 * --- that would violate write ordering (a transaction is free
+	 * to write its data at any point, even before the previous
+	 * committing transaction has committed).  The caller must
+	 * never, ever allow this to happen: there's nothing we can do
+	 * about it in this layer.
+	 */
+
+	if (bh->b_transaction) {
+		if (bh->b_transaction != handle->h_transaction) {
+			J_ASSERT (bh->b_transaction == journal->j_committing_transaction);
+
+			/* @@@ IS THIS TRUE  ? */
+			J_ASSERT (bh->b_next_transaction == NULL);
+
+			/* Special case --- the buffer might actually
+                           have been allocated and then immediately
+                           deallocated in the previous, committing
+                           transaction, so might still be left on that
+                           transaction's metadata lists. */
+			if (bh->b_jlist != BJ_Data) {
+				J_ASSERT (bh->b_jlist != BJ_Shadow);
+				J_ASSERT (bh->b_forget_transaction == journal->j_committing_transaction);
+				journal_unfile_buffer(bh);
+				bh->b_transaction = NULL;
+				journal_file_buffer(bh, handle->h_transaction, BJ_Data);
+				refile_buffer(bh);
+			}
+		}
+	} else {
+		journal_file_buffer(bh, handle->h_transaction, BJ_Data);
+		refile_buffer(bh);
+	}
+	
+	unlock_journal(journal);
+	return 0;
+}
+
+
+/* 
+ * journal_dirty_metadata: mark a buffer as containing dirty metadata
+ * which needs to be journaled as part of the current transaction.
+ *
+ * The buffer is placed on the transaction's metadata list and is marked
+ * as belonging to the transaction.  
+ *
+ * Special care needs to be taken if the buffer already belongs to the
+ * current committing transaction (in which case we should have frozen
+ * data present for that commit).  In that case, we don't relink the
+ * buffer: that only gets done when the old transaction finally
+ * completes its commit.
+ * 
+ * Returns error number or 0 on success.  
+ */
+
+int journal_dirty_metadata (handle_t *handle, struct buffer_head *bh)
+{
+	transaction_t *transaction = handle->h_transaction;
+	journal_t *journal = transaction->t_journal;
+	lock_journal(journal);
+
+	mark_buffer_dirty(bh, 0);
+
+	J_ASSERT(bh->b_transaction != NULL);
+	
+	/* 
+	 * Metadata already on the current transaction list doesn't
+	 * need to be filed.  Metadata on another transaction's list must
+	 * be committing, and will be refiled once the commit completes:
+	 * leave it alone for now. 
+	 */
+
+	if (bh->b_transaction != transaction) {
+		J_ASSERT (bh->b_transaction == journal->j_committing_transaction);
+		J_ASSERT (bh->b_next_transaction == transaction);
+		/* And this case is illegal: we can't reuse another
+		 * transaction's data buffer, ever. */
+		J_ASSERT (bh->b_jlist != BJ_Data);
+		goto done;
+	}
+	
+	/* That test should have eliminated the following case: */
+	J_ASSERT (bh->b_frozen_data == 0);
+
+	journal_file_buffer (bh, handle->h_transaction, BJ_Metadata);
+
+ done:
+	unlock_journal(journal);
+	return 0;
+}
+
+
+/* 
+ * journal_release_buffer: undo a get_write_access without any buffer
+ * updates, if the update decided in the end that it didn't need access.
+ *
+ * journal_get_write_access() can block, so it is quite possible for a
+ * journaling component to decide after the write access is returned
+ * that global state has changed and the update is no longer required.  */
+
+void journal_release_buffer (handle_t *handle, struct buffer_head *bh)
+{
+	transaction_t *transaction = handle->h_transaction;
+	journal_t *journal = transaction->t_journal;
+	lock_journal(journal);
+
+	/* If the buffer is reserved but not modified by this
+	 * transaction, then it is safe to release it.  In all other
+	 * cases, just leave the buffer as it is. */
+
+	if (bh->b_jlist == BJ_Reserved && bh->b_transaction == transaction &&
+	    !buffer_dirty(bh)) {
+		handle->h_buffer_credits++;
+		journal_refile_buffer(bh);
+	}
+	
+	unlock_journal(journal);
+}
+
+
+/* 
+ * journal_forget: bforget() for potentially-journaled buffers.  We can
+ * only do the bforget if there are no commits pending against the
+ * buffer.  If the buffer is dirty in the current running transaction we
+ * can safely unlink it.  */
+
+void journal_forget (handle_t *handle, struct buffer_head *bh)
+{
+	journal_t * journal = handle->h_transaction->t_journal;
+
+	bh->b_forget_transaction = handle->h_transaction;
+	
+	/* If we can't lock the journal because somebody else already
+	 * has the lock, then just release the buffer: that's better
+	 * than changing the semantics of bforget() to include a possible
+	 * context switch. */
+	if (try_lock_journal(journal))
+		goto nolock;
+
+ 	if (bh->b_transaction == handle->h_transaction) {
+		/* @@@ Tripping this unexpectedly --- try to catch it
+                   early! */
+		J_ASSERT(!bh->b_frozen_data);
+
+		/* If we are forgetting a buffer which is already part
+		 * of this transaction, then we can just drop it from
+		 * the transaction immediately. */
+		journal_unfile_buffer(bh);
+		bh->b_transaction = 0;
+	} else if (bh->b_transaction) {
+		/* However, if the buffer is still owned by a prior
+		 * (committing) transaction, we can't do anything with
+		 * it right now. */
+		unlock_journal(journal);
+		brelse(bh);
+		return;
+	}
+
+	J_ASSERT(!bh->b_frozen_data);
+	J_ASSERT(!bh->b_committed_data);
+
+	unlock_journal(journal);
+
+	/* @@@ DEBUG ONLY.  Eventually we will indeed want to be able to
+	 * discard forgotten buffers a bit more intelligently. */
+
+ nolock:
+	brelse(bh);
+	return;
+	
+	/* Now we know that the buffer isn't being committed anywhere,
+	 * but it might still be on a transaction's checkpoint list.  If
+	 * so, we want to place it on the new transaction's forget list:
+	 * on commit it will undo the old checkpoint.  Remember, we have
+	 * to be able to unwind the forget() if we take a crash before
+	 * the commit! */
+	
+	if (bh->b_cp_transaction) {
+		journal_file_buffer(bh, handle->h_transaction, BJ_Forget);
+		bh->b_transaction = handle->h_transaction;
+		brelse(bh);
+	} else
+		__bforget(bh);	
+}
+
+
+/*
+ * journal_sync_buffer: flush a potentially-journaled buffer to disk.
+ *
+ * Used for O_SYNC filesystem operations.  If the buffer is journaled,
+ * we need to complete the O_SYNC by waiting for the transaction to
+ * complete.  It is an error to call journal_sync_buffer before
+ * journal_stop!
+ */
+
+void journal_sync_buffer(struct buffer_head *bh)
+{
+	transaction_t *transaction;
+	journal_t *journal;
+	long sequence;
+	
+	/* If the buffer isn't journaled, this is easy: just sync it to
+	 * disk.  */
+
+	if (bh->b_transaction == NULL) {
+		/* If the buffer has already been journaled, then this
+		 * is a noop. */
+		if (bh->b_cp_transaction == NULL) 
+			return;
+		ll_rw_block (WRITE, 1, &bh);
+		wait_on_buffer (bh);
+		return;
+	}
+	
+	/* Otherwise, just wait until the transaction is synced to disk. */
+	transaction = bh->b_transaction;
+	journal = transaction->t_journal;
+	sequence = transaction->t_tid;
+	
+	jfs_debug(2, "requesting commit for bh %p\n", bh);
+	log_start_commit (journal, transaction);
+	
+	while (((long) (journal->j_commit_sequence - sequence)) < 0) {
+		wake_up(&journal->j_wait_done_commit);
+		sleep_on(&journal->j_wait_done_commit);
+	}
+}
+
+
+
+/*
+ * All done for a particular handle.
+ *
+ * There is not much action needed here.  We just return any remaining
+ * buffer credits to the transaction and remove the handle.  The only
+ * complication is that we need to start a commit operation if the
+ * filesystem is marked for synchronous update.
+ *
+ * journal_stop itself will not usually return an error, but it may
+ * do so in unusual circumstances.  In particular, expect it to 
+ * return -EIO if a journal_abort has been executed since the
+ * transaction began.
+ */
+
+int journal_stop (handle_t *handle)
+{
+	transaction_t *transaction = handle->h_transaction;
+	journal_t *journal = transaction->t_journal;
+	int force_sync;
+	
+	if (!handle)
+		return 0;
+	
+	J_ASSERT (transaction->t_updates > 0);
+	J_ASSERT (current->j_handle == handle);
+	
+	if (--handle->h_ref > 0)
+		return 0;
+
+	jfs_debug(4, "Handle %p going down\n", handle);
+	
+	current->j_handle = NULL;
+	transaction->t_outstanding_credits -= handle->h_buffer_credits;
+	transaction->t_updates--;
+	if (!transaction->t_updates)
+		wake_up(&transaction->t_wait);
+
+	/* 
+	 * If the journal is marked SYNC, we need to set another commit
+	 * going!  We also want to force a commit if the current
+	 * transaction is occupying too much of the log, or if the
+	 * transaction is too old now.
+	 */
+
+	force_sync = (journal->j_flags & JFS_SYNC) || handle->h_sync;
+	
+	if (force_sync ||
+	    transaction->t_outstanding_credits > journal->j_max_transaction_buffers ||
+	    time_after_eq(jiffies, transaction->t_expires)) {
+		tid_t tid = transaction->t_tid;
+		
+		jfs_debug(2, "transaction too old, requesting commit for handle %p\n", handle);
+		log_start_commit(journal, transaction);
+		
+		/*
+		 * Special case: JFS_SYNC synchronous updates require us
+		 * to wait for the commit to complete.  
+		 */
+		if (force_sync) 
+			log_wait_commit(journal, tid);
+	}
+	
+	kfree(handle);
+	
+	return 0;
+}
+
+
+
+/*
+ *
+ * List management code snippets: various functions for manipulating the
+ * transaction buffer lists.
+ *
+ */
+
+/*
+ * Add a buffer to a transaction list, given the transaction's list head
+ * pointer
+ */
+
+static inline void 
+blist_add_buffer(struct buffer_head **list, struct buffer_head *buf)
+{
+	if (!*list) {
+		buf->b_tnext = buf->b_tprev = buf;
+		*list = buf;
+	} else {
+		/* Insert at the tail of the list to preserve order */
+		struct buffer_head *first = *list, *last = first->b_tprev;
+		buf->b_tprev = last;
+		buf->b_tnext = first;
+		last->b_tnext = first->b_tprev = buf;
+	}
+}
+
+/* 
+ * Remove a buffer from a transaction list, given the transaction's list
+ * head pointer
+ */
+
+static inline void
+blist_del_buffer(struct buffer_head **list, struct buffer_head *buf)
+{
+	if (*list == buf) {
+		*list = buf->b_tnext;
+		if (*list == buf)
+			*list = 0;
+	}
+	buf->b_tprev->b_tnext = buf->b_tnext;
+	buf->b_tnext->b_tprev = buf->b_tprev;
+}
+
+/* 
+ * Remove a buffer from the appropriate transaction list. 
+ */
+
+void journal_unfile_buffer(struct buffer_head *buf)
+{
+	struct buffer_head **list = 0;
+	transaction_t * transaction;
+
+	transaction = buf->b_transaction;
+	
+#ifdef __SMP__
+	J_ASSERT (current->lock_depth >= 0);
+#endif
+	J_ASSERT (transaction->t_journal->j_locked);
+	J_ASSERT (buf->b_jlist < BJ_Types);
+	
+	if (buf->b_jlist != BJ_None)
+		J_ASSERT (transaction != 0);
+	
+	switch (buf->b_jlist) {
+	case BJ_None:
+		return;
+	case BJ_Data:
+		list = &transaction->t_datalist;
+		break;
+	case BJ_Metadata:
+		transaction->t_nr_buffers--;
+		J_ASSERT(transaction->t_nr_buffers >= 0);
+		list = &transaction->t_buffers;
+		break;
+	case BJ_Forget:
+		list = &transaction->t_forget;
+		break;
+	case BJ_IO:
+		list = &transaction->t_iobuf_list;
+		break;
+	case BJ_Shadow:
+		list = &transaction->t_shadow_list;
+		break;
+	case BJ_LogCtl:
+		list = &transaction->t_log_list;
+		break;
+	case BJ_Reserved:
+		list = &transaction->t_reserved_list;
+		break;
+	}
+	
+	blist_del_buffer(list, buf);
+	buf->b_jlist = BJ_None;
+}
+
+
+/* 
+ * File a buffer on the given transaction list. 
+ */
+
+void journal_file_buffer(struct buffer_head *buf, 
+			 transaction_t *transaction, 
+			 int jlist)
+{
+	struct buffer_head **list = 0;
+	
+#ifdef __SMP__
+	J_ASSERT (current->lock_depth >= 0);
+#endif
+	J_ASSERT (transaction->t_journal->j_locked);
+	J_ASSERT (buf->b_jlist < BJ_Types);
+	J_ASSERT (buf->b_transaction == transaction || buf->b_transaction ==0);
+
+	if (buf->b_transaction) {
+		if (buf->b_jlist == jlist)
+			return;
+		journal_unfile_buffer(buf);
+	} else
+		buf->b_transaction = transaction;
+
+	switch (jlist) {
+	case BJ_None:
+		J_ASSERT(!buf->b_committed_data);
+		J_ASSERT(!buf->b_frozen_data);
+		return;
+	case BJ_Data:
+		list = &transaction->t_datalist;
+		break;
+	case BJ_Metadata:
+		transaction->t_nr_buffers++;
+		list = &transaction->t_buffers;
+		break;
+	case BJ_Forget:
+		list = &transaction->t_forget;
+		break;
+	case BJ_IO:
+		list = &transaction->t_iobuf_list;
+		break;
+	case BJ_Shadow:
+		list = &transaction->t_shadow_list;
+		break;
+	case BJ_LogCtl:
+		list = &transaction->t_log_list;
+		break;
+	case BJ_Reserved:
+		list = &transaction->t_reserved_list;
+		break;
+	}
+	
+	blist_add_buffer(list, buf);
+	buf->b_jlist = jlist;
+}
+
+
+/* 
+ * Remove a buffer from its current buffer list in preparation for
+ * dropping it from its current transaction entirely.  If the buffer has
+ * already started to be used by a subsequent transaction, refile the
+ * buffer on that transaction's metadata list.
+ */
+
+void journal_refile_buffer(struct buffer_head *bh)
+{
+#ifdef __SMP__
+	J_ASSERT (current->lock_depth >= 0);
+#endif
+	journal_unfile_buffer(bh);
+
+	/* If the buffer is now unused, just drop it.  If it has been
+	   modified by a later transaction, add it to the new
+	   transaction's metadata list. */
+
+	bh->b_transaction = bh->b_next_transaction;
+	bh->b_next_transaction = NULL;
+	
+	if (bh->b_transaction != NULL) {
+		int tstate;
+		journal_file_buffer(bh, bh->b_transaction, BJ_Metadata);
+		tstate = bh->b_transaction->t_state;
+		J_ASSERT(tstate == T_RUNNING);
+	}
+		
+	/* If necessary, remove it from the global journaled
+	   buffer list and replace it back on the main dirty
+	   buffer list. */
+	refile_buffer(bh);
+}
+
--- linux-2.2.18pre17.ext3-0.0.5a/fs/super.c.~1~	Wed Oct 25 18:57:53 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/fs/super.c	Wed Oct 25 19:10:56 2000
@@ -107,7 +107,7 @@
 
 	sema_init(&lptr->mnt_dquot.dqio_sem, 1);
 	sema_init(&lptr->mnt_dquot.dqoff_sem, 1);
-	lptr->mnt_dquot.flags = 0;
+	lptr->mnt_dquot.flags = sb->s_default_dquot_flags;
 
 	/* N.B. Is it really OK to have a vfsmount without names? */
 	if (dev_name && !IS_ERR(tmp = getname(dev_name))) {
@@ -1138,6 +1138,8 @@
 	goto dput_and_out;
 }
 
+extern char * root_mount_data;
+
 void __init mount_root(void)
 {
 	struct file_system_type * fs_type;
@@ -1225,7 +1227,7 @@
 	else for (fs_type = file_systems ; fs_type ; fs_type = fs_type->next) {
   		if (!(fs_type->fs_flags & FS_REQUIRES_DEV))
   			continue;
-  		sb = read_super(ROOT_DEV,fs_type->name,root_mountflags,NULL,1);
+  		sb = read_super(ROOT_DEV,fs_type->name,root_mountflags,root_mount_data,1);
 		if (sb) {
 			sb->s_flags = root_mountflags;
 			current->fs->root = dget(sb->s_root);
--- linux-2.2.18pre17.ext3-0.0.5a/include/linux/buffer.h.~1~	Wed Oct 25 19:10:56 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/include/linux/buffer.h	Wed Oct 25 19:10:56 2000
@@ -0,0 +1,23 @@
+#ifndef _LINUX_BUFFER_H
+#define _LINUX_BUFFER_H
+
+/*
+ * This file has definitions for buffer management structures shared between
+ * the main buffer cache management and the journaling code.  
+ */
+
+
+#ifdef __KERNEL__
+
+extern struct wait_queue * buffer_wait;
+
+extern int sync_buffers(kdev_t dev, int wait);
+
+extern void end_buffer_io_sync(struct buffer_head *, int);
+
+void put_unused_buffer_head(struct buffer_head * bh);
+struct buffer_head * get_unused_buffer_head(int async);
+
+#endif /* __KERNEL__ */
+
+#endif /*_LINUX_BUFFER_H */
--- linux-2.2.18pre17.ext3-0.0.5a/include/linux/ext3_fs.h.~1~	Wed Oct 25 19:11:05 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/include/linux/ext3_fs.h	Thu Oct 26 00:11:32 2000
@@ -0,0 +1,677 @@
+/*
+ *  linux/include/linux/ext3_fs.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.h
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+#ifndef _LINUX_EXT3_FS_H
+#define _LINUX_EXT3_FS_H
+
+#include <linux/types.h>
+
+/*
+ * The second extended filesystem constants/structures
+ */
+
+/*
+ * Define EXT3FS_DEBUG to produce debug messages
+ */
+#undef EXT3FS_DEBUG
+
+/*
+ * Define EXT3_PREALLOCATE to preallocate data blocks for expanding files
+ */
+#undef  EXT3_PREALLOCATE /* @@@ Fix this! */
+#define EXT3_DEFAULT_PREALLOC_BLOCKS	8
+
+/*
+ * The second extended file system version
+ */
+#define EXT3FS_DATE		"2000/10/24"
+#define EXT3FS_VERSION		"0.0.5a"
+
+/*
+ * Debug code
+ */
+#ifdef EXT3FS_DEBUG
+#	define ext3_debug(f, a...)	{ \
+					printk ("EXT3-fs DEBUG (%s, %d): %s:", \
+						__FILE__, __LINE__, __FUNCTION__); \
+				  	printk (f, ## a); \
+					}
+#else
+#	define ext3_debug(f, a...)	/**/
+#endif
+
+/*
+ * Special inodes numbers
+ */
+#define	EXT3_BAD_INO		 1	/* Bad blocks inode */
+#define EXT3_ROOT_INO		 2	/* Root inode */
+#define EXT3_ACL_IDX_INO	 3	/* ACL inode */
+#define EXT3_ACL_DATA_INO	 4	/* ACL inode */
+#define EXT3_BOOT_LOADER_INO	 5	/* Boot loader inode */
+#define EXT3_UNDEL_DIR_INO	 6	/* Undelete directory inode */
+
+/* First non-reserved inode for old ext3 filesystems */
+#define EXT3_GOOD_OLD_FIRST_INO	11
+
+/*
+ * The second extended file system magic number
+ */
+#define EXT3_SUPER_MAGIC	0xEF53
+
+/*
+ * Maximal count of links to a file
+ */
+#define EXT3_LINK_MAX		32000
+
+/*
+ * Macro-instructions used to manage several block sizes
+ */
+#define EXT3_MIN_BLOCK_SIZE		1024
+#define	EXT3_MAX_BLOCK_SIZE		4096
+#define EXT3_MIN_BLOCK_LOG_SIZE		  10
+#ifdef __KERNEL__
+# define EXT3_BLOCK_SIZE(s)		((s)->s_blocksize)
+#else
+# define EXT3_BLOCK_SIZE(s)		(EXT3_MIN_BLOCK_SIZE << (s)->s_log_block_size)
+#endif
+#define EXT3_ACLE_PER_BLOCK(s)		(EXT3_BLOCK_SIZE(s) / sizeof (struct ext3_acl_entry))
+#define	EXT3_ADDR_PER_BLOCK(s)		(EXT3_BLOCK_SIZE(s) / sizeof (__u32))
+#ifdef __KERNEL__
+# define EXT3_BLOCK_SIZE_BITS(s)	((s)->s_blocksize_bits)
+#else
+# define EXT3_BLOCK_SIZE_BITS(s)	((s)->s_log_block_size + 10)
+#endif
+#ifdef __KERNEL__
+#define	EXT3_ADDR_PER_BLOCK_BITS(s)	((s)->u.ext3_sb.s_addr_per_block_bits)
+#define EXT3_INODE_SIZE(s)		((s)->u.ext3_sb.s_inode_size)
+#define EXT3_FIRST_INO(s)		((s)->u.ext3_sb.s_first_ino)
+#else
+#define EXT3_INODE_SIZE(s)	(((s)->s_rev_level == EXT3_GOOD_OLD_REV) ? \
+				 EXT3_GOOD_OLD_INODE_SIZE : \
+				 (s)->s_inode_size)
+#define EXT3_FIRST_INO(s)	(((s)->s_rev_level == EXT3_GOOD_OLD_REV) ? \
+				 EXT3_GOOD_OLD_FIRST_INO : \
+				 (s)->s_first_ino)
+#endif
+
+/*
+ * Macro-instructions used to manage fragments
+ */
+#define EXT3_MIN_FRAG_SIZE		1024
+#define	EXT3_MAX_FRAG_SIZE		4096
+#define EXT3_MIN_FRAG_LOG_SIZE		  10
+#ifdef __KERNEL__
+# define EXT3_FRAG_SIZE(s)		((s)->u.ext3_sb.s_frag_size)
+# define EXT3_FRAGS_PER_BLOCK(s)	((s)->u.ext3_sb.s_frags_per_block)
+#else
+# define EXT3_FRAG_SIZE(s)		(EXT3_MIN_FRAG_SIZE << (s)->s_log_frag_size)
+# define EXT3_FRAGS_PER_BLOCK(s)	(EXT3_BLOCK_SIZE(s) / EXT3_FRAG_SIZE(s))
+#endif
+
+/*
+ * ACL structures
+ */
+struct ext3_acl_header	/* Header of Access Control Lists */
+{
+	__u32	aclh_size;
+	__u32	aclh_file_count;
+	__u32	aclh_acle_count;
+	__u32	aclh_first_acle;
+};
+
+struct ext3_acl_entry	/* Access Control List Entry */
+{
+	__u32	acle_size;
+	__u16	acle_perms;	/* Access permissions */
+	__u16	acle_type;	/* Type of entry */
+	__u16	acle_tag;	/* User or group identity */
+	__u16	acle_pad1;
+	__u32	acle_next;	/* Pointer on next entry for the */
+					/* same inode or on next free entry */
+};
+
+/*
+ * Structure of a blocks group descriptor
+ */
+struct ext3_group_desc
+{
+	__u32	bg_block_bitmap;		/* Blocks bitmap block */
+	__u32	bg_inode_bitmap;		/* Inodes bitmap block */
+	__u32	bg_inode_table;		/* Inodes table block */
+	__u16	bg_free_blocks_count;	/* Free blocks count */
+	__u16	bg_free_inodes_count;	/* Free inodes count */
+	__u16	bg_used_dirs_count;	/* Directories count */
+	__u16	bg_pad;
+	__u32	bg_reserved[3];
+};
+
+/*
+ * Macro-instructions used to manage group descriptors
+ */
+#ifdef __KERNEL__
+# define EXT3_BLOCKS_PER_GROUP(s)	((s)->u.ext3_sb.s_blocks_per_group)
+# define EXT3_DESC_PER_BLOCK(s)		((s)->u.ext3_sb.s_desc_per_block)
+# define EXT3_INODES_PER_GROUP(s)	((s)->u.ext3_sb.s_inodes_per_group)
+# define EXT3_DESC_PER_BLOCK_BITS(s)	((s)->u.ext3_sb.s_desc_per_block_bits)
+#else
+# define EXT3_BLOCKS_PER_GROUP(s)	((s)->s_blocks_per_group)
+# define EXT3_DESC_PER_BLOCK(s)		(EXT3_BLOCK_SIZE(s) / sizeof (struct ext3_group_desc))
+# define EXT3_INODES_PER_GROUP(s)	((s)->s_inodes_per_group)
+#endif
+
+/*
+ * Constants relative to the data blocks
+ */
+#define	EXT3_NDIR_BLOCKS		12
+#define	EXT3_IND_BLOCK			EXT3_NDIR_BLOCKS
+#define	EXT3_DIND_BLOCK			(EXT3_IND_BLOCK + 1)
+#define	EXT3_TIND_BLOCK			(EXT3_DIND_BLOCK + 1)
+#define	EXT3_N_BLOCKS			(EXT3_TIND_BLOCK + 1)
+
+/*
+ * Inode flags
+ */
+#define	EXT3_SECRM_FL			0x00000001 /* Secure deletion */
+#define	EXT3_UNRM_FL			0x00000002 /* Undelete */
+#define	EXT3_COMPR_FL			0x00000004 /* Compress file */
+#define EXT3_SYNC_FL			0x00000008 /* Synchronous updates */
+#define EXT3_IMMUTABLE_FL		0x00000010 /* Immutable file */
+#define EXT3_APPEND_FL			0x00000020 /* writes to file may only append */
+#define EXT3_NODUMP_FL			0x00000040 /* do not dump file */
+#define EXT3_NOATIME_FL			0x00000080 /* do not update atime */
+/* Reserved for compression usage... */
+#define EXT3_DIRTY_FL			0x00000100
+#define EXT3_COMPRBLK_FL		0x00000200 /* One or more compressed clusters */
+#define EXT3_NOCOMP_FL			0x00000400 /* Don't compress */
+#define EXT3_ECOMPR_FL			0x00000800 /* Compression error */
+/* End compression flags --- maybe not all used */	
+#define EXT3_BTREE_FL			0x00001000 /* btree format dir */
+#define EXT3_RESERVED_FL		0x80000000 /* reserved for ext3 lib */
+
+#define EXT3_FL_USER_VISIBLE		0x00001FFF /* User visible flags */
+#define EXT3_FL_USER_MODIFIABLE		0x000000FF /* User modifiable flags */
+
+/*
+ * ioctl commands
+ */
+#define	EXT3_IOC_GETFLAGS		_IOR('f', 1, long)
+#define	EXT3_IOC_SETFLAGS		_IOW('f', 2, long)
+#define	EXT3_IOC_GETVERSION		_IOR('v', 1, long)
+#define	EXT3_IOC_SETVERSION		_IOW('v', 2, long)
+
+/*
+ * Structure of an inode on the disk
+ */
+struct ext3_inode {
+	__u16	i_mode;		/* File mode */
+	__u16	i_uid;		/* Owner Uid */
+	__u32	i_size;		/* Size in bytes */
+	__u32	i_atime;	/* Access time */
+	__u32	i_ctime;	/* Creation time */
+	__u32	i_mtime;	/* Modification time */
+	__u32	i_dtime;	/* Deletion Time */
+	__u16	i_gid;		/* Group Id */
+	__u16	i_links_count;	/* Links count */
+	__u32	i_blocks;	/* Blocks count */
+	__u32	i_flags;	/* File flags */
+	union {
+		struct {
+			__u32  l_i_reserved1;
+		} linux1;
+		struct {
+			__u32  h_i_translator;
+		} hurd1;
+		struct {
+			__u32  m_i_reserved1;
+		} masix1;
+	} osd1;				/* OS dependent 1 */
+	__u32	i_block[EXT3_N_BLOCKS];/* Pointers to blocks */
+	__u32	i_version;	/* File version (for NFS) */
+	__u32	i_file_acl;	/* File ACL */
+	__u32	i_dir_acl;	/* Directory ACL */
+	__u32	i_faddr;	/* Fragment address */
+	union {
+		struct {
+			__u8	l_i_frag;	/* Fragment number */
+			__u8	l_i_fsize;	/* Fragment size */
+			__u16	i_pad1;
+			__u32	l_i_reserved2[2];
+		} linux2;
+		struct {
+			__u8	h_i_frag;	/* Fragment number */
+			__u8	h_i_fsize;	/* Fragment size */
+			__u16	h_i_mode_high;
+			__u16	h_i_uid_high;
+			__u16	h_i_gid_high;
+			__u32	h_i_author;
+		} hurd2;
+		struct {
+			__u8	m_i_frag;	/* Fragment number */
+			__u8	m_i_fsize;	/* Fragment size */
+			__u16	m_pad1;
+			__u32	m_i_reserved2[2];
+		} masix2;
+	} osd2;				/* OS dependent 2 */
+};
+
+#define i_size_high	i_dir_acl
+
+#if defined(__KERNEL__) || defined(__linux__)
+#define i_reserved1	osd1.linux1.l_i_reserved1
+#define i_frag		osd2.linux2.l_i_frag
+#define i_fsize		osd2.linux2.l_i_fsize
+#define i_reserved2	osd2.linux2.l_i_reserved2
+#endif
+
+#ifdef	__hurd__
+#define i_translator	osd1.hurd1.h_i_translator
+#define i_frag		osd2.hurd2.h_i_frag;
+#define i_fsize		osd2.hurd2.h_i_fsize;
+#define i_uid_high	osd2.hurd2.h_i_uid_high
+#define i_gid_high	osd2.hurd2.h_i_gid_high
+#define i_author	osd2.hurd2.h_i_author
+#endif
+
+#ifdef	__masix__
+#define i_reserved1	osd1.masix1.m_i_reserved1
+#define i_frag		osd2.masix2.m_i_frag
+#define i_fsize		osd2.masix2.m_i_fsize
+#define i_reserved2	osd2.masix2.m_i_reserved2
+#endif
+
+/*
+ * File system states
+ */
+#define	EXT3_VALID_FS			0x0001	/* Unmounted cleanly */
+#define	EXT3_ERROR_FS			0x0002	/* Errors detected */
+
+/*
+ * Mount flags
+ */
+#define EXT3_MOUNT_CHECK_NORMAL		0x0001	/* Do some more checks */
+#define EXT3_MOUNT_CHECK_STRICT		0x0002	/* Do again more checks */
+#define EXT3_MOUNT_CHECK		(EXT3_MOUNT_CHECK_NORMAL | \
+					 EXT3_MOUNT_CHECK_STRICT)
+#define EXT3_MOUNT_GRPID		0x0004	/* Create files with directory's group */
+#define EXT3_MOUNT_DEBUG		0x0008	/* Some debugging messages */
+#define EXT3_MOUNT_ERRORS_CONT		0x0010	/* Continue on errors */
+#define EXT3_MOUNT_ERRORS_RO		0x0020	/* Remount fs ro on errors */
+#define EXT3_MOUNT_ERRORS_PANIC		0x0040	/* Panic on errors */
+#define EXT3_MOUNT_MINIX_DF		0x0080	/* Mimics the Minix statfs */
+#define EXT3_MOUNT_NOLOAD		0x0100	/* Don't use existing journal*/
+#define EXT3_MOUNT_ABORT		0x0200	/* Fatal error detected */
+#define EXT3_MOUNT_DATA_FLAGS		0x0C00	/* Mode for data writes: */
+  #define EXT3_MOUNT_JOURNAL_DATA	0x0400	/* Write data to journal */
+  #define EXT3_MOUNT_ORDERED_DATA	0x0800	/* Flush data before commit */
+  #define EXT3_MOUNT_WRITEBACK_DATA	0x0C00	/* No data ordering */
+#define EXT3_MOUNT_UPDATE_JOURNAL	0x1000	/* Update the journal format */
+
+/* Compatibility, for having both ext2_fs.h and ext3_fs.h included at once */
+#ifndef _LINUX_EXT2_FS_H
+#define clear_opt(o, opt)		o &= ~EXT3_MOUNT_##opt
+#define set_opt(o, opt)			o |= EXT3_MOUNT_##opt
+#define test_opt(sb, opt)		((sb)->u.ext3_sb.s_mount_opt & \
+					 EXT3_MOUNT_##opt)
+#else
+#define EXT2_MOUNT_NOLOAD		EXT3_MOUNT_NOLOAD
+#define EXT2_MOUNT_ABORT		EXT3_MOUNT_ABORT
+#endif
+
+#define ext3_set_bit			ext2_set_bit
+#define ext3_clear_bit			ext2_clear_bit
+#define ext3_test_bit			ext2_test_bit
+#define ext3_find_first_zero_bit	ext2_find_first_zero_bit
+#define ext3_find_next_zero_bit		ext2_find_next_zero_bit
+
+/*
+ * Maximal mount counts between two filesystem checks
+ */
+#define EXT3_DFL_MAX_MNT_COUNT		20	/* Allow 20 mounts */
+#define EXT3_DFL_CHECKINTERVAL		0	/* Don't use interval check */
+
+/*
+ * Behaviour when detecting errors
+ */
+#define EXT3_ERRORS_CONTINUE		1	/* Continue execution */
+#define EXT3_ERRORS_RO			2	/* Remount fs read-only */
+#define EXT3_ERRORS_PANIC		3	/* Panic */
+#define EXT3_ERRORS_DEFAULT		EXT3_ERRORS_CONTINUE
+
+/*
+ * Structure of the super block
+ */
+struct ext3_super_block {
+	__u32	s_inodes_count;		/* Inodes count */
+	__u32	s_blocks_count;		/* Blocks count */
+	__u32	s_r_blocks_count;	/* Reserved blocks count */
+	__u32	s_free_blocks_count;	/* Free blocks count */
+	__u32	s_free_inodes_count;	/* Free inodes count */
+	__u32	s_first_data_block;	/* First Data Block */
+	__u32	s_log_block_size;	/* Block size */
+	__s32	s_log_frag_size;	/* Fragment size */
+	__u32	s_blocks_per_group;	/* # Blocks per group */
+	__u32	s_frags_per_group;	/* # Fragments per group */
+	__u32	s_inodes_per_group;	/* # Inodes per group */
+	__u32	s_mtime;		/* Mount time */
+	__u32	s_wtime;		/* Write time */
+	__u16	s_mnt_count;		/* Mount count */
+	__s16	s_max_mnt_count;	/* Maximal mount count */
+	__u16	s_magic;		/* Magic signature */
+	__u16	s_state;		/* File system state */
+	__u16	s_errors;		/* Behaviour when detecting errors */
+	__u16	s_minor_rev_level; 	/* minor revision level */
+	__u32	s_lastcheck;		/* time of last check */
+	__u32	s_checkinterval;	/* max. time between checks */
+	__u32	s_creator_os;		/* OS */
+	__u32	s_rev_level;		/* Revision level */
+	__u16	s_def_resuid;		/* Default uid for reserved blocks */
+	__u16	s_def_resgid;		/* Default gid for reserved blocks */
+	/*
+	 * These fields are for EXT3_DYNAMIC_REV superblocks only.
+	 *
+	 * Note: the difference between the compatible feature set and
+	 * the incompatible feature set is that if there is a bit set
+	 * in the incompatible feature set that the kernel doesn't
+	 * know about, it should refuse to mount the filesystem.
+	 * 
+	 * e2fsck's requirements are more strict; if it doesn't know
+	 * about a feature in either the compatible or incompatible
+	 * feature set, it must abort and not try to meddle with
+	 * things it doesn't understand...
+	 */
+	__u32	s_first_ino; 		/* First non-reserved inode */
+	__u16   s_inode_size; 		/* size of inode structure */
+	__u16	s_block_group_nr; 	/* block group # of this superblock */
+	__u32	s_feature_compat; 	/* compatible feature set */
+	__u32	s_feature_incompat; 	/* incompatible feature set */
+	__u32	s_feature_ro_compat; 	/* readonly-compatible feature set */
+	__u8	s_uuid[16];		/* 128-bit uuid for volume */
+	char	s_volume_name[16]; 	/* volume name */
+	char	s_last_mounted[64]; 	/* directory where last mounted */
+	__u32	s_algorithm_usage_bitmap; /* For compression */
+	/*
+	 * Performance hints.  Directory preallocation should only
+	 * happen if the EXT3_COMPAT_PREALLOC flag is on.
+	 */
+	__u8	s_prealloc_blocks;	/* Nr of blocks to try to preallocate*/
+	__u8	s_prealloc_dir_blocks;	/* Nr to preallocate for dirs */
+	__u16	s_padding1;
+	/* 
+	 * Journaling support.
+	 */
+	__u8	s_journal_uuid[16];	/* uuid of journal superblock */
+	__u32	s_journal_inum;		/* inode number of journal file */
+	__u32	s_journal_dev;		/* device number of journal file */
+	__u32	s_last_orphan;		/* start of list of inodes to delete */
+	
+	__u32	s_reserved[197];	/* Padding to the end of the block */
+};
+
+#ifdef __KERNEL__
+#define EXT3_SB(sb)	(&((sb)->u.ext3_sb))
+#else
+/* Assume that user mode programs are passing in an ext3fs superblock, not
+ * a kernel struct super_block.  This will allow us to call the feature-test
+ * macros from user land. */
+#define EXT3_SB(sb)	(sb)
+#endif
+
+#define NEXT_ORPHAN(inode) inode->u.ext3_i.i_dtime
+
+/*
+ * Codes for operating systems
+ */
+#define EXT3_OS_LINUX		0
+#define EXT3_OS_HURD		1
+#define EXT3_OS_MASIX		2
+#define EXT3_OS_FREEBSD		3
+#define EXT3_OS_LITES		4
+
+/*
+ * Revision levels
+ */
+#define EXT3_GOOD_OLD_REV	0	/* The good old (original) format */
+#define EXT3_DYNAMIC_REV	1 	/* V2 format w/ dynamic inode sizes */
+
+#define EXT3_CURRENT_REV	EXT3_GOOD_OLD_REV
+#define EXT3_MAX_SUPP_REV	EXT3_DYNAMIC_REV
+
+#define EXT3_GOOD_OLD_INODE_SIZE 128
+
+/*
+ * Feature set definitions
+ */
+
+#define EXT3_HAS_COMPAT_FEATURE(sb,mask)			\
+	( EXT3_SB(sb)->s_feature_compat & (mask) )
+#define EXT3_HAS_RO_COMPAT_FEATURE(sb,mask)			\
+	( EXT3_SB(sb)->s_feature_ro_compat & (mask) )
+#define EXT3_HAS_INCOMPAT_FEATURE(sb,mask)			\
+	( EXT3_SB(sb)->s_feature_incompat & (mask) )
+
+#define EXT3_FEATURE_COMPAT_DIR_PREALLOC	0x0001
+/*      EXT3_FEATURE_COMPAT_<reserved for AFS>	0x0002 */
+#define EXT3_FEATURE_COMPAT_HAS_JOURNAL		0x0004
+
+#define EXT3_FEATURE_RO_COMPAT_SPARSE_SUPER	0x0001
+#define EXT3_FEATURE_RO_COMPAT_LARGE_FILE	0x0002
+#define EXT3_FEATURE_RO_COMPAT_BTREE_DIR	0x0004
+
+#define EXT3_FEATURE_INCOMPAT_COMPRESSION	0x0001
+#define EXT3_FEATURE_INCOMPAT_FILETYPE		0x0002
+#define EXT3_FEATURE_INCOMPAT_RECOVER		0x0004
+
+#define EXT3_FEATURE_COMPAT_SUPP	0
+#define EXT3_FEATURE_INCOMPAT_SUPP	(EXT3_FEATURE_INCOMPAT_FILETYPE| \
+					 EXT3_FEATURE_INCOMPAT_RECOVER)
+#define EXT3_FEATURE_RO_COMPAT_SUPP	(EXT3_FEATURE_RO_COMPAT_SPARSE_SUPER| \
+					 EXT3_FEATURE_RO_COMPAT_LARGE_FILE| \
+					 EXT3_FEATURE_RO_COMPAT_BTREE_DIR)
+
+/*
+ * Default values for user and/or group using reserved blocks
+ */
+#define	EXT3_DEF_RESUID		0
+#define	EXT3_DEF_RESGID		0
+
+/*
+ * Structure of a directory entry
+ */
+#define EXT3_NAME_LEN 255
+
+struct ext3_dir_entry {
+	__u32	inode;			/* Inode number */
+	__u16	rec_len;		/* Directory entry length */
+	__u16	name_len;		/* Name length */
+	char	name[EXT3_NAME_LEN];	/* File name */
+};
+
+/*
+ * The new version of the directory entry.  Since EXT3 structures are
+ * stored in intel byte order, and the name_len field could never be
+ * bigger than 255 chars, it's safe to reclaim the extra byte for the
+ * file_type field.
+ */
+struct ext3_dir_entry_2 {
+	__u32	inode;			/* Inode number */
+	__u16	rec_len;		/* Directory entry length */
+	__u8	name_len;		/* Name length */
+	__u8	file_type;
+	char	name[EXT3_NAME_LEN];	/* File name */
+};
+
+/*
+ * Ext3 directory file types.  Only the low 3 bits are used.  The
+ * other bits are reserved for now.
+ */
+#define EXT3_FT_UNKNOWN		0
+#define EXT3_FT_REG_FILE	1
+#define EXT3_FT_DIR		2
+#define EXT3_FT_CHRDEV		3
+#define EXT3_FT_BLKDEV 		4
+#define EXT3_FT_FIFO		5
+#define EXT3_FT_SOCK		6
+#define EXT3_FT_SYMLINK		7
+
+#define EXT3_FT_MAX		8
+
+/*
+ * EXT3_DIR_PAD defines the directory entries boundaries
+ *
+ * NOTE: It must be a multiple of 4
+ */
+#define EXT3_DIR_PAD		 	4
+#define EXT3_DIR_ROUND 			(EXT3_DIR_PAD - 1)
+#define EXT3_DIR_REC_LEN(name_len)	(((name_len) + 8 + EXT3_DIR_ROUND) & \
+					 ~EXT3_DIR_ROUND)
+
+#ifdef __KERNEL__
+
+/* Filesize hard limits for 64-bit file offsets */
+extern long long ext3_max_sizes[];
+
+/*
+ * Describe an inode's exact location on disk and in memory
+ */
+struct ext3_iloc
+{
+	struct buffer_head *bh;
+	struct ext3_inode *raw_inode;
+	unsigned long block_group;
+};
+
+/*
+ * Function prototypes
+ */
+
+/*
+ * Ok, these declarations are also in <linux/kernel.h> but none of the
+ * ext3 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 ext3_permission (struct inode *, int);
+
+/* balloc.c */
+extern int ext3_group_sparse(int group);
+extern struct buffer_head * ext3_new_block (handle_t *, const struct inode *, 
+					    unsigned long,
+					    __u32 *, __u32 *, int *);
+extern void ext3_free_blocks (handle_t *, const struct inode *, unsigned long,
+			      unsigned long);
+extern unsigned long ext3_count_free_blocks (struct super_block *);
+extern void ext3_check_blocks_bitmap (struct super_block *);
+extern struct ext3_group_desc * ext3_get_group_desc(struct super_block * sb,
+						    unsigned int block_group,
+						    struct buffer_head ** bh);
+
+/* bitmap.c */
+extern unsigned long ext3_count_free (struct buffer_head *, unsigned);
+
+/* dir.c */
+extern int ext3_check_dir_entry (const char *, struct inode *,
+				 struct ext3_dir_entry_2 *, struct buffer_head *,
+				 unsigned long);
+
+/* file.c */
+extern int ext3_read (struct inode *, struct file *, char *, int);
+extern int ext3_write (struct inode *, struct file *, char *, int);
+
+/* fsync.c */
+extern int ext3_sync_file (struct file *, struct dentry *);
+
+/* ialloc.c */
+extern struct inode * ext3_new_inode (handle_t *, const struct inode *, int, int *);
+extern void ext3_free_inode (handle_t *, struct inode *);
+extern struct inode * ext3_orphan_get (struct super_block * sb, ino_t ino);
+extern unsigned long ext3_count_free_inodes (struct super_block *);
+extern void ext3_check_inodes_bitmap (struct super_block *);
+
+/* inode.c */
+
+extern int ext3_bmap (struct inode *, int);
+
+extern struct buffer_head * ext3_getblk (handle_t *, struct inode *, long, int, int *);
+extern struct buffer_head * ext3_bread (handle_t *, struct inode *, int, int, int *);
+
+extern int  ext3_getcluster (struct inode * inode, long block);
+extern int  ext3_get_inode_loc (struct inode *, struct ext3_iloc *);
+extern int  ext3_do_update_inode (handle_t *, struct inode *, struct ext3_iloc *, int);
+extern void ext3_read_inode (struct inode *);
+extern void ext3_write_inode (struct inode *);
+extern void ext3_put_inode (struct inode *);
+extern void ext3_orphan_del (handle_t *handle, struct inode *);
+extern void ext3_delete_inode (struct inode *);
+extern int  ext3_sync_inode (handle_t *, struct inode *);
+extern int  ext3_notify_change(struct dentry *, struct iattr *);
+extern void ext3_discard_prealloc (struct inode *);
+
+/* ioctl.c */
+extern int ext3_ioctl (struct inode *, struct file *, unsigned int,
+		       unsigned long);
+
+/* namei.c */
+extern void ext3_release (struct inode *, struct file *);
+extern struct dentry *ext3_lookup (struct inode *, struct dentry *);
+extern int ext3_create (struct inode *,struct dentry *,int);
+extern int ext3_mkdir (struct inode *,struct dentry *,int);
+extern int ext3_rmdir (struct inode *,struct dentry *);
+extern void ext3_orphan_add(handle_t *, struct inode *);
+extern int ext3_unlink (struct inode *,struct dentry *);
+extern int ext3_symlink (struct inode *,struct dentry *,const char *);
+extern int ext3_link (struct dentry *, struct inode *, struct dentry *);
+extern int ext3_mknod (struct inode *, struct dentry *, int, int);
+extern int ext3_rename (struct inode *, struct dentry *,
+			struct inode *, struct dentry *);
+
+/* super.c */
+extern void ext3_error (struct super_block *, const char *, const char *, ...)
+	__attribute__ ((format (printf, 3, 4)));
+extern void ext3_abort (struct super_block *, const char *, const char *, ...)
+	__attribute__ ((format (printf, 3, 4)));
+extern NORET_TYPE void ext3_panic (struct super_block *, const char *,
+				   const char *, ...)
+	__attribute__ ((NORET_AND format (printf, 3, 4)));
+extern void ext3_warning (struct super_block *, const char *, const char *, ...)
+	__attribute__ ((format (printf, 3, 4)));
+extern void ext3_put_super (struct super_block *);
+extern void ext3_write_super (struct super_block *);
+extern int ext3_remount (struct super_block *, int *, char *);
+extern struct super_block * ext3_read_super (struct super_block *,void *,int);
+extern int init_ext3_fs(void);
+extern int ext3_statfs (struct super_block *, struct statfs *, int);
+
+/* truncate.c */
+extern void ext3_truncate (struct inode *);
+
+/*
+ * Inodes and files operations
+ */
+
+/* dir.c */
+extern struct inode_operations ext3_dir_inode_operations;
+
+/* file.c */
+extern struct inode_operations ext3_file_inode_operations;
+
+/* symlink.c */
+extern struct inode_operations ext3_symlink_inode_operations;
+
+#endif	/* __KERNEL__ */
+
+#endif	/* _LINUX_EXT3_FS_H */
--- linux-2.2.18pre17.ext3-0.0.5a/include/linux/ext3_fs_i.h.~1~	Wed Oct 25 19:11:05 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/include/linux/ext3_fs_i.h	Wed Oct 25 19:11:05 2000
@@ -0,0 +1,51 @@
+/*
+ *  linux/include/linux/ext3_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_EXT3_FS_I
+#define _LINUX_EXT3_FS_I
+
+/*
+ * second extended file system inode data in memory
+ */
+struct ext3_inode_info {
+	union {
+		struct pipe_inode_info	pipe_i;		/* placeholder */
+		__u32			i_data[15];
+	} i_data_u;
+	__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	i_version;
+	__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;
+	struct list_head i_orphan;	/* unlinked but open inodes */
+	int	i_new_inode:1;	/* Is a freshly allocated inode */
+	/* i_disksize keeps track of what the inode size is ON DISK, not
+	 * in memory.  During truncate, i_size is set to 0 by the VFS
+	 * but the filesystem won't set i_disksize to 0 until the
+	 * truncate is actually under way. */
+	off_t	i_disksize;
+};
+
+#endif	/* _LINUX_EXT3_FS_I */
--- linux-2.2.18pre17.ext3-0.0.5a/include/linux/ext3_fs_sb.h.~1~	Wed Oct 25 19:11:05 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/include/linux/ext3_fs_sb.h	Wed Oct 25 19:11:05 2000
@@ -0,0 +1,69 @@
+/*
+ *  linux/include/linux/ext3_fs_sb.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_sb.h
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+#ifndef _LINUX_EXT3_FS_SB
+#define _LINUX_EXT3_FS_SB
+
+/*
+ * The following is not needed anymore since the descriptors buffer
+ * heads are now dynamically allocated
+ */
+/* #define EXT3_MAX_GROUP_DESC	8 */
+
+#define EXT3_MAX_GROUP_LOADED	8
+
+/*
+ * second extended-fs super-block data in memory
+ */
+struct ext3_sb_info {
+	unsigned long s_frag_size;	/* Size of a fragment in bytes */
+	unsigned long s_frags_per_block;/* Number of fragments per block */
+	unsigned long s_inodes_per_block;/* Number of inodes per block */
+	unsigned long s_frags_per_group;/* Number of fragments in a group */
+	unsigned long s_blocks_per_group;/* Number of blocks in a group */
+	unsigned long s_inodes_per_group;/* Number of inodes in a group */
+	unsigned long s_itb_per_group;	/* Number of inode table blocks per group */
+	unsigned long s_db_per_group;	/* Number of descriptor blocks per group */
+	unsigned long s_desc_per_block;	/* Number of group descriptors per block */
+	unsigned long s_groups_count;	/* Number of groups in the fs */
+	struct buffer_head * s_sbh;	/* Buffer containing the super block */
+	struct ext3_super_block * s_es;	/* Pointer to the super block in the buffer */
+	struct buffer_head ** s_group_desc;
+	unsigned short s_loaded_inode_bitmaps;
+	unsigned short s_loaded_block_bitmaps;
+	unsigned long s_inode_bitmap_number[EXT3_MAX_GROUP_LOADED];
+	struct buffer_head * s_inode_bitmap[EXT3_MAX_GROUP_LOADED];
+	unsigned long s_block_bitmap_number[EXT3_MAX_GROUP_LOADED];
+	struct buffer_head * s_block_bitmap[EXT3_MAX_GROUP_LOADED];
+	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;
+
+	/* Journaling */
+	struct inode * s_journal_inode;
+	struct journal_s * s_journal;
+	struct list_head s_orphan;
+};
+
+#endif	/* _LINUX_EXT3_FS_SB */
--- linux-2.2.18pre17.ext3-0.0.5a/include/linux/ext3_jfs.h.~1~	Wed Oct 25 19:11:05 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/include/linux/ext3_jfs.h	Wed Oct 25 19:17:08 2000
@@ -0,0 +1,140 @@
+/*
+ * linux/include/linux/ext3_jfs.h
+ *
+ * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
+ *
+ * Copyright 1998--1999 Red Hat corp --- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * Ext3-specific journaling extensions.
+ */
+
+#ifndef _LINUX_EXT3_JFS_H
+#define _LINUX_EXT3_JFS_H
+
+#include <linux/fs.h>
+#include <linux/jfs.h>
+#include <linux/ext3_fs.h>
+
+#define EXT3_JOURNAL(inode)	(EXT3_SB((inode)->i_sb)->s_journal)
+
+/* Define the number of blocks we need to account to a transaction to
+ * modify one block of data.
+ * 
+ * We may have to touch one inode, one bitmap buffer, up to three
+ * indirection blocks, the group and superblock summaries, and the data
+ * block to complete the transaction.  */
+
+#define EXT3_SINGLEDATA_TRANS_BLOCKS	8
+
+/* Define the minimum size for a transaction which modifies data.  This
+ * needs to take into account the fact that we may end up modifying two
+ * quota files too (one for the group, one for the user quota).  The
+ * superblock only gets updated once, of course, so don't bother
+ * counting that again for the quota updates. */
+
+#define EXT3_DATA_TRANS_BLOCKS		(3 * EXT3_SINGLEDATA_TRANS_BLOCKS - 2)
+
+/* Delete operations potentially hit one directory's namespace plus an
+ * entire inode, plus arbitrary amounts of bitmap/indirection data.  Be
+ * generous.  We can grow the delete transaction later if necessary. */
+
+#define EXT3_DELETE_TRANS_BLOCKS	(2 * EXT3_DATA_TRANS_BLOCKS + 64)
+
+/* Define an arbitrary limit for the amount of data we will anticipate
+ * writing to any given transaction.  For unbounded transactions such as
+ * write(2) and truncate(2) we can write more than this, but we always
+ * start off at the maximum transaction size and grow the transaction
+ * optimistically as we go. */
+
+#define EXT3_MAX_TRANS_DATA		64
+
+/* We break up a large truncate or write transaction once the handle's
+ * buffer credits gets this low, we need either to extend the
+ * transaction or to start a new one.  Reserve enough space here for
+ * inode, bitmap, superblock, group and indirection updates for at least
+ * one block, plus two quota updates.  Quota allocations are not
+ * needed. */
+
+#define EXT3_RESERVE_TRANS_BLOCKS	12
+
+static inline int
+ext3_mark_iloc_dirty(handle_t *handle, 
+		     struct inode *inode,
+		     struct ext3_iloc *iloc)
+{
+	if (handle) {
+		/* the do_update_inode consumes one bh->b_count */
+		iloc->bh->b_count++;
+		ext3_do_update_inode(handle, inode, iloc, 0); /* @@@ ERROR */
+		journal_dirty_metadata(handle, iloc->bh); /* @@@ ERROR */
+		brelse(iloc->bh);
+	} else
+		mark_inode_dirty(inode);		
+	return 0;
+}
+
+/* 
+ * On success, We end up with an outstanding reference count against
+ * iloc->bh.  This _must_ be cleaned up later. 
+ */
+
+static inline int
+ext3_reserve_inode_write(handle_t *handle, struct inode *inode, 
+			 struct ext3_iloc *iloc)
+{
+	int err = 0;
+	if (handle) {
+		err = ext3_get_inode_loc(inode, iloc); /* @@@ ERROR */
+		if (!err) {
+			err = journal_get_write_access(handle, iloc->bh);
+			if (err) {
+				brelse(iloc->bh);
+				iloc->bh = NULL;
+			}
+		}
+	}
+	return err;
+}
+
+static inline int
+ext3_mark_inode_dirty(handle_t *handle, 
+		      struct inode *inode)
+{
+	struct ext3_iloc iloc;
+	int err;
+
+	err = ext3_reserve_inode_write(handle, inode, &iloc);
+	if (!err)
+		err = ext3_mark_iloc_dirty(handle, inode, &iloc);
+	return err;
+}
+
+
+/* 
+ * Wrappers for journal_start/end.
+ *
+ * The only special thing we need to do here is to make sure that all
+ * journal_end calls result in the superblock being marked dirty, so
+ * that sync() will call the filesystem's write_super callback if
+ * appropriate. 
+ */
+
+static inline handle_t *ext3_journal_start (struct inode *inode, int nblocks)
+{
+	if (inode->i_sb->s_flags & MS_RDONLY)
+		return ERR_PTR(-EROFS);
+	return journal_start(EXT3_JOURNAL(inode), nblocks);
+}
+
+static inline int ext3_journal_stop (handle_t *handle, struct inode *inode)
+{
+	int rc = journal_stop(handle);
+	inode->i_sb->s_dirt = 1;
+	return rc;
+}
+
+#endif	/* _LINUX_EXT3_JFS_H */
--- linux-2.2.18pre17.ext3-0.0.5a/include/linux/fs.h.~1~	Wed Oct 25 18:57:53 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/include/linux/fs.h	Wed Oct 25 19:16:56 2000
@@ -177,6 +177,12 @@
 extern void file_table_init(void);
 extern void dcache_init(void);
 
+/* We declare a few opaque types for use by the journaling code. */
+typedef unsigned long		tid_t;		/* Unique transaction ID */
+typedef struct transaction_s	transaction_t;	/* Compound transaction type */
+typedef struct handle_s		handle_t;	/* Atomic operation type */
+typedef struct journal_s	journal_t;	/* Journal control structure */
+
 typedef char buffer_block[BLOCK_SIZE];
 
 /* bh state bits */
@@ -185,6 +191,19 @@
 #define BH_Lock		2	/* 1 if the buffer is locked */
 #define BH_Req		3	/* 0 if the buffer has been invalidated */
 #define BH_Protected	6	/* 1 if the buffer is protected */
+#define BH_Temp		8	/* 1 if the buffer is temporary (unlinked) */
+#define BH_JWrite	9	/* 1 if being written to log (@@@ DEBUGGING) */
+
+/* journaling buffer types */
+#define BJ_None		0	/* Not journaled */
+#define BJ_Data		1	/* Normal data: flush before commit */
+#define BJ_Metadata	2	/* Normal journaled metadata */
+#define BJ_Forget	3	/* Buffer superceded by this transaction */
+#define BJ_IO		4	/* Buffer is for temporary IO use */
+#define BJ_Shadow	5	/* Buffer contents being shadowed to the log */
+#define BJ_LogCtl	6	/* Buffer contains log descriptors */
+#define BJ_Reserved	7	/* Buffer is reserved for access by journal */
+#define BJ_Types	8
 
 /*
  * Try to keep the most commonly used fields in single cache lines (16
@@ -213,7 +232,8 @@
 
 	/* Non-performance-critical data follows. */
 	char * b_data;			/* pointer to data block (1024 bytes) */
-	unsigned int b_list;		/* List that this buffer appears */
+	unsigned char b_list;		/* List that this buffer appears */
+	unsigned char b_jlist;		/* Journaling list for this buffer */
 	unsigned long b_flushtime;      /* Time when this (dirty) buffer
 					 * should be written */
 	struct wait_queue * b_wait;
@@ -226,6 +246,45 @@
 	 */
 	void (*b_end_io)(struct buffer_head *bh, int uptodate);
 	void *b_dev_id;
+
+	/* Journaling-related fields */
+
+	/* Copy of the buffer data frozen for writing to the log. */
+	char * b_frozen_data;
+
+	/* Pointer to a saved copy of the buffer containing no
+           uncommitted deallocation references, so that allocations can
+           avoid overwriting uncommitted deletes. */
+	char * b_committed_data;
+	
+	/* Pointer to the compound transaction which owns this buffer's
+           metadata: either the running transaction or the committing
+           transaction (if there is one).  Only applies to buffers on a
+           transaction's data or metadata journaling list. */
+	transaction_t * b_transaction;
+	
+	/* Pointer to the running compound transaction which is
+           currently modifying the buffer's metadata, if there was
+           already a transaction committing it when the new transaction
+           touched it. */
+	transaction_t * b_next_transaction;
+	
+	/* @@@ Pointer to the transaction which last deleted this
+	   buffer, as a debugging aid only. */
+	transaction_t * b_forget_transaction;
+	
+	/* Doubly-linked list of buffers on a transaction's data,
+           metadata or forget queue. */
+	struct buffer_head *b_tnext, *b_tprev;
+
+	/* Pointer to the compound transaction against which this buffer
+           is checkpointed.  Only dirty buffers can be checkpointed. */
+	transaction_t * b_cp_transaction;
+	
+	/* Doubly-linked list of buffers still remaining to be flushed
+           before an old transaction can be checkpointed. */
+	struct buffer_head *b_cpnext, *b_cpprev;
+
 };
 
 typedef void (bh_end_io_t)(struct buffer_head *bh, int uptodate);
@@ -257,12 +316,18 @@
 	return test_bit(BH_Protected, &bh->b_state);
 }
 
+static inline int buffer_journaled(struct buffer_head * bh)
+{
+	return (bh->b_jlist != BJ_None || bh->b_cp_transaction != NULL);
+}
+
 #define buffer_page(bh)		(mem_map + MAP_NR((bh)->b_data))
 #define touch_buffer(bh)	set_bit(PG_referenced, &buffer_page(bh)->flags)
 
 #include <linux/pipe_fs_i.h>
 #include <linux/minix_fs_i.h>
 #include <linux/ext2_fs_i.h>
+#include <linux/ext3_fs_i.h>
 #include <linux/hpfs_fs_i.h>
 #include <linux/ntfs_fs_i.h>
 #include <linux/msdos_fs_i.h>
@@ -374,10 +439,26 @@
 	int			i_writecount;
 	unsigned int		i_attr_flags;
 	__u32			i_generation;
+
+	/* Journaling support fields */
+
+	/* Transaction which owns the latest modifications in this inode */
+	transaction_t	      * i_transaction;
+	
+	/* Doubly-linked list of inodes which have been modified by that
+           transaction */
+	struct inode	      * i_tnext, ** i_tprev;
+	
+	/* Pointer to the buffer which backs this inode, to save massive
+           repeated getblk()s when modifying files */
+	struct buffer_head    * i_ibuf;
+	
+	/* Filesystem-specific data: */
 	union {
 		struct pipe_inode_info		pipe_i;
 		struct minix_inode_info		minix_i;
 		struct ext2_inode_info		ext2_i;
+		struct ext3_inode_info		ext3_i;
 		struct hpfs_inode_info		hpfs_i;
 		struct ntfs_inode_info          ntfs_i;
 		struct msdos_inode_info		msdos_i;
@@ -505,6 +586,7 @@
 
 #include <linux/minix_fs_sb.h>
 #include <linux/ext2_fs_sb.h>
+#include <linux/ext3_fs_sb.h>
 #include <linux/hpfs_fs_sb.h>
 #include <linux/ntfs_fs_sb.h>
 #include <linux/msdos_fs_sb.h>
@@ -546,9 +628,13 @@
 	short int		s_ibasket_max;
 	struct list_head	s_dirty;	/* dirty inodes */
 
+	unsigned int		s_default_dquot_flags;
+
+	/* Filesystem-specific data: */
 	union {
 		struct minix_sb_info	minix_sb;
 		struct ext2_sb_info	ext2_sb;
+		struct ext3_sb_info	ext3_sb;
 		struct hpfs_sb_info	hpfs_sb;
 		struct ntfs_sb_info     ntfs_sb;
 		struct msdos_sb_info	msdos_sb;
@@ -787,13 +873,16 @@
 
 #define BUF_CLEAN	0
 #define BUF_LOCKED	1	/* Buffers scheduled for write */
-#define BUF_DIRTY	2	/* Dirty buffers, not yet scheduled for write */
-#define NR_LIST		3
+#define BUF_DIRTY	2	/* Dirty buffers, not yet scheduled for write*/
+#define BUF_JOURNAL	3
+#define NR_LIST		4
 
 void mark_buffer_uptodate(struct buffer_head * bh, int on);
 
 extern inline void mark_buffer_clean(struct buffer_head * bh)
 {
+	extern void jfs_preclean_buffer_check(struct buffer_head *);
+	jfs_preclean_buffer_check(bh); /* @@@ Expensive debugging */
 	if (test_and_clear_bit(BH_Dirty, &bh->b_state)) {
 		if (bh->b_list == BUF_DIRTY)
 			refile_buffer(bh);
--- linux-2.2.18pre17.ext3-0.0.5a/include/linux/jfs.h.~1~	Wed Oct 25 19:11:05 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/include/linux/jfs.h	Wed Oct 25 19:16:56 2000
@@ -0,0 +1,572 @@
+/*
+ * linux/include/linux/jfs.h
+ * 
+ * Written by Stephen C. Tweedie <sct@redhat.com>, 1998
+ *
+ * Copyright 1998 Red Hat corp --- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * Definitions for transaction data structures for the buffer cache
+ * filesystem journaling support.
+ */
+
+#ifndef _LINUX_JFS_H
+#define _LINUX_JFS_H
+
+#include <asm/semaphore.h>
+
+/*
+ * Debug code
+ */
+
+#define JFS_DEBUG
+
+extern int journal_enable_debug;
+
+#ifdef JFS_DEBUG
+#define jfs_debug(n, f, a...)						\
+	do {								\
+		if ((n) <= journal_enable_debug) {			\
+			printk (KERN_DEBUG "JFS DEBUG: (%s, %d): %s: ",	\
+				__FILE__, __LINE__, __FUNCTION__);	\
+		  	printk (f, ## a);			\
+		}							\
+	} while (0)
+#else
+#define jfs_debug(f, a...)	/**/
+#endif
+
+#define JFS_MIN_JOURNAL_BLOCKS 1024
+
+/*
+ * Internal structures used by the logging mechanism:
+ */
+
+#define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */
+
+
+/*
+ * On-disk structures
+ */
+
+/* 
+ * Descriptor block types:
+ */
+
+#define JFS_DESCRIPTOR_BLOCK	1
+#define JFS_COMMIT_BLOCK	2
+#define JFS_SUPERBLOCK_V1	3
+#define JFS_SUPERBLOCK_V2	4
+
+/*
+ * Standard header for all descriptor blocks:
+ */
+typedef struct journal_header_s
+{
+	__u32		h_magic;
+	__u32		h_blocktype;
+	__u32		h_sequence;
+} journal_header_t;
+
+
+/* 
+ * The block tag: used to describe a single buffer in the journal 
+ */
+typedef struct journal_block_tag_s
+{
+	__u32		t_blocknr;	/* The on-disk block number */
+	__u32		t_flags;	/* See below */
+} journal_block_tag_t;
+
+/* Definitions for the journal tag flags word: */
+#define JFS_FLAG_ESCAPE		1	/* on-disk block is escaped */
+#define JFS_FLAG_SAME_UUID	2	/* block has same uuid as previous */
+#define JFS_FLAG_DELETED	4	/* block deleted by this transaction */
+#define JFS_FLAG_LAST_TAG	8	/* last tag in this descriptor block */
+
+
+/*
+ * The journal superblock.  All fields are in big-endian byte order.
+ */
+typedef struct journal_superblock_s
+{
+/* 0x0000 */
+	journal_header_t s_header;
+
+/* 0x000C */
+	/* Static information describing the journal */
+	__u32	s_blocksize;		/* journal device blocksize */
+	__u32	s_maxlen;		/* total blocks in journal file */
+	__u32	s_first;		/* first block of log information */
+	
+/* 0x0018 */
+	/* Dynamic information describing the current state of the log */
+	__u32	s_sequence;		/* first commit ID expected in log */
+	__u32	s_start;		/* blocknr of start of log */
+
+/* 0x0020 */
+	/* Error value, as set by journal_abort(). */
+	__s32	s_errno;
+
+/* 0x0024 */
+	/* Remaining fields are only valid in a version-2 superblock */
+	__u32	s_feature_compat; 	/* compatible feature set */
+	__u32	s_feature_incompat; 	/* incompatible feature set */
+	__u32	s_feature_ro_compat; 	/* readonly-compatible feature set */
+/* 0x0030 */
+	__u8	s_uuid[16];		/* 128-bit uuid for journal */
+
+/* 0x0040 */
+	__u32	s_nr_users;		/* Nr of filesystems sharing log */
+	
+	__u32	s_dynsuper;		/* Blocknr of dynamic superblock copy*/
+	
+/* 0x0048 */
+	__u32	s_max_transaction;	/* Limit of journal blocks per trans.*/
+	__u32	s_max_trans_data;	/* Limit of data blocks per trans. */
+
+/* 0x0050 */
+	__u32	s_padding[44];
+
+/* 0x0100 */
+	__u8	s_users[16*48];		/* ids of all fs'es sharing the log */
+/* 0x0400 */
+} journal_superblock_t;
+
+#define JFS_HAS_COMPAT_FEATURE(j,mask)					\
+	((j)->j_format_version >= 2 &&					\
+	 ((j)->j_superblock->s_feature_compat & cpu_to_be32((mask))))
+#define JFS_HAS_RO_COMPAT_FEATURE(j,mask)				\
+	((j)->j_format_version >= 2 &&					\
+	 ((j)->j_superblock->s_feature_ro_compat & cpu_to_be32((mask))))
+#define JFS_HAS_INCOMPAT_FEATURE(j,mask)				\
+	((j)->j_format_version >= 2 &&					\
+	 ((j)->j_superblock->s_feature_incompat & cpu_to_be32((mask))))
+
+#define JFS_FEATURE_INCOMPAT_REVOKE	0x00000001
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+
+
+#define J_ASSERT(assert)						\
+	do { if (!(assert)) {						\
+		printk (KERN_CRIT					\
+			"Assertion failure in %s() at %s line %d: "	\
+			"\"%s\"\n",					\
+			__FUNCTION__, __FILE__, __LINE__, # assert);	\
+		* ((char *) 0) = 0;					\
+	} } while (0)
+
+
+
+/* The handle_t type represents a single atomic update being performed
+ * by some process.  All filesystem modifications made by the process go
+ * through this handle.  Recursive operations (such as quota operations)
+ * are gathered into a single update.
+ *
+ * The buffer credits field is used to account for journaled buffers
+ * being modified by the running process.  To ensure that there is
+ * enough log space for all outstanding operations, we need to limit the
+ * number of outstanding buffers possible at any time.  When the
+ * operation completes, any buffer credits not used are credited back to
+ * the transaction, so that at all times we know how many buffers the
+ * outstanding updates on a transaction might possibly touch. */
+
+struct handle_s 
+{
+	/* Which compound transaction is this update a part of? */
+	transaction_t	      * h_transaction;
+
+	/* Number of remaining buffers we are allowed to dirty: */
+	int			h_buffer_credits;
+
+	/* Reference count on this handle */
+	int			h_ref;
+
+	/* Flags */
+	unsigned int		h_sync	: 1;	/* sync-on-close */
+	unsigned int		h_jdata	: 1;	/* force data journaling */
+};
+
+
+/* The transaction_t type is the guts of the journaling mechanism.  It
+ * tracks a compound transaction through its various states:
+ *
+ * RUNNING:	accepting new updates
+ * LOCKED:	Updates still running but we don't accept new ones
+ * RUNDOWN:	Updates are tidying up but have finished requesting
+ *		new buffers to modify (state not used for now)
+ * FLUSH:       All updates complete, but we are still writing to disk
+ * COMMIT:      All data on disk, writing commit record
+ * FINISHED:	We still have to keep the transaction for checkpointing.
+ *
+ * The transaction keeps track of all of the buffers modified by a
+ * running transaction, and all of the buffers committed but not yet
+ * flushed to home for finished transactions.
+ */
+
+struct transaction_s 
+{
+	/* Pointer to the journal for this transaction. */
+	journal_t *		t_journal;
+	
+	/* Sequence number for this transaction */
+	tid_t			t_tid;
+	
+	/* Transaction's current state */
+	enum {
+		T_RUNNING,
+		T_LOCKED,
+		T_RUNDOWN,
+		T_FLUSH,
+		T_COMMIT,
+		T_FINISHED 
+	}			t_state;
+
+	/* Where in the log does this transaction's commit start? */
+	unsigned long		t_log_start;
+	
+	/* Doubly-linked circular list of all inodes owned by this
+           transaction */
+	struct inode *		t_ilist;
+	
+	/* Number of buffers on the t_buffers list */
+	int			t_nr_buffers;
+	
+	/* Doubly-linked circular list of all buffers reserved but not
+           yet modified by this transaction */
+	struct buffer_head *	t_reserved_list;
+	
+	/* Doubly-linked circular list of all metadata buffers owned by this
+           transaction */
+	struct buffer_head *	t_buffers;
+	
+	/* Doubly-linked circular list of all data buffers still to be
+           flushed before this transaction can be committed */
+	struct buffer_head *	t_datalist;
+	
+	/* Doubly-linked circular list of all forget buffers (superceded
+           buffers which we can un-checkpoint once this transaction
+           commits) */
+	struct buffer_head *	t_forget;
+	
+	/* Doubly-linked circular list of all buffers still to be
+           flushed before this transaction can be checkpointed */
+	struct buffer_head *	t_checkpoint_list;
+	
+	/* Doubly-linked circular list of temporary buffers currently
+           undergoing IO in the log */
+	struct buffer_head *	t_iobuf_list;
+	
+	/* Doubly-linked circular list of metadata buffers being
+           shadowed by log IO.  The IO buffers on the iobuf list and the
+           shadow buffers on this list match each other one for one at
+           all times. */
+	struct buffer_head *	t_shadow_list;
+	
+	/* Doubly-linked circular list of control buffers being written
+           to the log. */
+	struct buffer_head *	t_log_list;
+	
+	/* Number of outstanding updates running on this transaction */
+	int			t_updates;
+
+	/* Number of buffers reserved for use by all handles in this
+	 * transaction handle but not yet modified. */
+	int			t_outstanding_credits;
+	
+	/* Wait queue to wait for updates to complete */
+	struct wait_queue *	t_wait;
+
+	/* Forward and backward links for the circular list of all
+	 * transactions awaiting checkpoint */
+	transaction_t		*t_cpnext, *t_cpprev;
+
+	/* When will the transaction expire (become due for commit), in
+	 * jiffies ? */
+	unsigned long		t_expires;
+};
+
+
+/* The journal_t maintains all of the journaling state information for a
+ * single filesystem.  It is linked to from the fs superblock structure.
+ * 
+ * We use the journal_t to keep track of all outstanding transaction
+ * activity on the filesystem, and to manage the state of the log
+ * writing process. */
+
+struct journal_s
+{
+	/* General journaling state flags */
+	unsigned long		j_flags;
+
+	/* Is there an outstanding uncleared error on the journal (from
+	 * a prior abort)? */
+	int			j_errno;
+	
+	/* The superblock buffer */
+	struct buffer_head *	j_sb_buffer;
+	journal_superblock_t *	j_superblock;
+
+	/* Version of the superblock format */
+	int			j_format_version;
+	
+	/* Transactions: The current running transaction... */
+	transaction_t *		j_running_transaction;
+	
+	/* ... the transaction we are pushing to disk ... */
+	transaction_t *		j_committing_transaction;
+	
+	/* ... and a linked circular list of all transactions waiting
+	 * for checkpointing. */
+	transaction_t *		j_checkpoint_transactions;
+
+	/* Wait queue for locking of the journal structure.  */
+	struct wait_queue *	j_wait_lock;
+
+	/* Wait queue for waiting for a locked transaction to start
+           committing */
+	struct wait_queue *	j_wait_transaction_locked;
+	
+	/* Wait queue for waiting for checkpointing to complete */
+	struct wait_queue *	j_wait_logspace;
+	
+	/* Wait queue for waiting for commit to complete */
+	struct wait_queue *	j_wait_done_commit;
+	
+	/* Wait queue to trigger checkpointing */
+	struct wait_queue *	j_wait_checkpoint;
+	
+	/* Wait queue to trigger commit */
+	struct wait_queue *	j_wait_commit;
+	
+	/* Semaphore for locking against concurrent checkpoints */
+	struct semaphore 	j_checkpoint_sem;
+		
+	/* Journal running state: */
+	/* The lock flag is *NEVER* touched from interrupts. */
+	unsigned int		j_locked : 1;
+
+	/* Journal head: identifies the first unused block in the journal. */
+	unsigned long		j_head;
+	
+	/* Journal tail: identifies the oldest still-used block in the
+	 * journal. */
+	unsigned long		j_tail;
+
+	/* Journal free: how many free blocks are there in the journal? */
+	unsigned long		j_free;
+
+	/* Journal start and end: the block numbers of the first usable
+	 * block and one beyond the last usable block in the journal. */
+	unsigned long		j_first, j_last;
+
+	/* Device, blocksize and starting block offset for the location
+	 * where we store the journal. */
+	kdev_t			j_dev;
+	int			j_blocksize;
+	unsigned int		j_blk_offset;
+
+	/* Total maximum capacity of the journal region on disk. */
+	unsigned int		j_maxlen;
+
+	/* Optional inode where we store the journal.  If present, all
+	 * journal block numbers are mapped into this inode via
+	 * bmap(). */
+	struct inode *		j_inode;
+
+	/* Sequence number of the oldest transaction in the log */
+	tid_t			j_tail_sequence;
+	/* Sequence number of the next transaction to grant */
+	tid_t			j_transaction_sequence;
+	/* Sequence number of the most recently committed transaction */
+	tid_t			j_commit_sequence;
+	/* Sequence number of the most recent transaction wanting commit */
+	tid_t			j_commit_request;
+
+	/* Journal uuid: identifies the object (filesystem, LVM volume
+	 * etc) backed by this journal.  This will eventually be
+	 * replaced by an array of uuids, allowing us to index multiple
+	 * devices within a single journal and to perform atomic updates
+	 * across them.  */
+
+	__u8			j_uuid[16];
+
+	/* Pointer to the current commit thread for this journal */
+	struct task_struct *	j_task;
+
+	/* Maximum number of metadata buffers to allow in a single
+	 * compound commit transaction */
+	int			j_max_transaction_buffers;
+
+	/* What is the maximum transaction lifetime before we begin a
+	 * commit? */
+	unsigned long		j_commit_interval;
+
+	/* The timer used to wakeup the commit thread: */
+	struct timer_list *	j_commit_timer;
+	int			j_commit_timer_active;
+};
+
+/* 
+ * Journal flag definitions 
+ */
+#define JFS_UNMOUNT	1	/* Journal thread is being destroyed */
+#define JFS_SYNC	2	/* Perform synchronous transaction commits */
+#define JFS_ABORT	4	/* Journaling has been aborted for errors. */
+#define JFS_ACK_ERR	8	/* The errno in the sb has been acked */
+
+/* 
+ * Journaling internal variables/parameters 
+ */
+
+extern int journal_flush_nr_buffers;
+
+
+/* 
+ * Function declarations for the journaling transaction and buffer
+ * management
+ */
+
+/* Filing buffers */
+extern void journal_unfile_buffer(struct buffer_head *);
+extern void journal_refile_buffer(struct buffer_head *);
+extern void journal_file_buffer(struct buffer_head *, transaction_t *, int);
+extern void journal_clean_data_list(transaction_t *transaction);
+
+/* Log buffer allocation */
+extern struct buffer_head * journal_get_descriptor_buffer(journal_t *);
+extern unsigned long journal_next_log_block(journal_t *);
+
+/* Commit management */
+extern void journal_commit_transaction(journal_t *);
+
+/* Checkpoint list management */
+extern void journal_remove_checkpoint(struct buffer_head *);
+extern void journal_insert_checkpoint(struct buffer_head *, transaction_t *);
+
+/* Buffer IO */
+extern int 
+journal_write_metadata_buffer(transaction_t	  *transaction,
+			      struct buffer_head  *bh_in,
+			      struct buffer_head **bh_out,
+			      int		   blocknr);
+
+/* Create and destroy transactions */
+extern transaction_t *	get_transaction (journal_t *);
+extern void		put_transaction (transaction_t *);
+
+/* Notify state transitions (called by the log writer thread): */
+extern int		set_transaction_state (transaction_t *, int);
+
+
+/* Transaction locking */
+extern void		__wait_on_journal (journal_t *);
+
+/* Journal locking.  In 2.2, we assume that the kernel lock is already
+ * held. */
+static inline void lock_journal (journal_t * journal)
+{
+#ifdef __SMP__
+	J_ASSERT(current->lock_depth >= 0);
+#endif
+	if (journal->j_locked)
+		__wait_on_journal(journal);
+	journal->j_locked = 1;
+}
+
+static inline int try_lock_journal (journal_t * journal)
+{
+	if (journal->j_locked)
+		return 1;
+	journal->j_locked = 1;
+	return 0;
+}
+
+static inline void unlock_journal (journal_t * journal)
+{
+	J_ASSERT (journal->j_locked);
+	journal->j_locked = 0;
+	wake_up(&journal->j_wait_lock);
+}
+
+/* This function is gross, but unfortunately we need it as long as
+ * existing filesystems want to guard against races by testing
+ * bh->b_count.  @@@ Remove this?  We no longer abuse b_count so badly!
+ */
+
+static inline int journal_is_buffer_shared(struct buffer_head *bh)
+{
+	int count = bh->b_count;
+	J_ASSERT (count >= 1);
+	return (count > 1);
+}
+
+/* The journaling code user interface:
+ *
+ * Create and destroy handles
+ * Register buffer modifications against the current transaction. 
+ */
+
+extern handle_t *journal_start (journal_t *, int nblocks);
+extern int	 journal_restart (handle_t *, int nblocks);
+extern int	 journal_extend (handle_t *, int nblocks);
+extern int	 journal_get_write_access (handle_t *, struct buffer_head *);
+extern int	 journal_get_create_access (handle_t *, struct buffer_head *);
+extern int	 journal_get_undo_access (handle_t *, struct buffer_head *);
+extern int	 journal_dirty_data (handle_t *, struct buffer_head *);
+extern int	 journal_dirty_metadata (handle_t *, struct buffer_head *);
+extern void	 journal_release_buffer (handle_t *, struct buffer_head *);
+extern void	 journal_forget (handle_t *, struct buffer_head *);
+extern void	 journal_sync_buffer (struct buffer_head *);
+extern int	 journal_stop (handle_t *);
+extern int	 journal_flush (journal_t *);
+extern int	 journal_revoke (handle_t *, int);
+
+extern journal_t * journal_init_dev   (kdev_t, int start, int len, int bsize);
+extern journal_t * journal_init_inode (struct inode *);
+extern int	   journal_update_format (journal_t *);
+extern int	   journal_check_features (journal_t *, unsigned long, unsigned long, unsigned long);
+extern int	   journal_create     (journal_t *);
+extern int	   journal_load       (journal_t *);
+extern void	   journal_release    (journal_t *);
+extern int	   journal_recover    (journal_t *);
+extern void	   journal_update_superblock (journal_t *, int);
+extern void	   __journal_abort      (journal_t *);
+extern void	   journal_abort      (journal_t *, int);
+extern int	   journal_errno      (journal_t *);
+extern void	   journal_ack_err    (journal_t *);
+extern int	   journal_clear_err  (journal_t *);
+
+
+/* The log thread user interface:
+ *
+ * Request space in the current transaction, and force transaction commit
+ * transitions on demand.
+ */
+
+extern int	log_space_left (journal_t *); /* Called with journal locked */
+extern void	log_start_commit (journal_t *, transaction_t *);
+extern void	log_wait_commit (journal_t *, tid_t);
+extern int	log_do_checkpoint (journal_t *, int);
+
+extern void	log_wait_for_space(journal_t *, int nblocks);
+extern void	journal_drop_transaction(journal_t *, transaction_t *);
+
+
+/* Debugging code only: */
+
+#define jfs_ENOSYS() \
+do {								      \
+	printk (KERN_ERR "JFS unimplemented function " __FUNCTION__); \
+	current->state = TASK_UNINTERRUPTIBLE;			      \
+	schedule();						      \
+} while (1)
+
+#endif /* __KERNEL__   */
+#endif /* _LINUX_JFS_H */
--- linux-2.2.18pre17.ext3-0.0.5a/include/linux/locks.h.~1~	Tue May 11 18:36:15 1999
+++ linux-2.2.18pre17.ext3-0.0.5a/include/linux/locks.h	Wed Oct 25 19:17:05 2000
@@ -8,6 +8,8 @@
 #include <linux/pagemap.h>
 #endif
 
+extern void jfs_prelock_buffer_check(struct buffer_head *);
+
 /*
  * Buffer cache locking - note that interrupts may only unlock, not
  * lock buffers.
@@ -22,6 +24,8 @@
 
 extern inline void lock_buffer(struct buffer_head * bh)
 {
+	/* @@@ Debugging for the journaling code */
+	jfs_prelock_buffer_check(bh);
 	while (test_and_set_bit(BH_Lock, &bh->b_state))
 		__wait_on_buffer(bh);
 }
--- linux-2.2.18pre17.ext3-0.0.5a/include/linux/mount.h.~1~	Mon Aug  9 20:04:41 1999
+++ linux-2.2.18pre17.ext3-0.0.5a/include/linux/mount.h	Wed Oct 25 19:11:05 2000
@@ -13,6 +13,8 @@
 
 #define DQUOT_USR_ENABLED	0x01		/* User diskquotas enabled */
 #define DQUOT_GRP_ENABLED	0x02		/* Group diskquotas enabled */
+#define DQUOT_WRITETHROUGH	0x04		/* Write dquot updates to the
+						   quota file immediately */
 
 struct quota_mount_options
 {
--- linux-2.2.18pre17.ext3-0.0.5a/include/linux/sched.h.~1~	Wed Oct 25 18:57:53 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/include/linux/sched.h	Wed Oct 25 19:16:56 2000
@@ -118,6 +118,7 @@
 #define	MAX_SCHEDULE_TIMEOUT	LONG_MAX
 extern signed long FASTCALL(schedule_timeout(signed long timeout));
 asmlinkage void schedule(void);
+asmlinkage int sys_sched_yield(void);
 
 /*
  * The default fd array needs to be at least BITS_PER_LONG,
@@ -331,6 +332,9 @@
 
 /* oom handling */
 	int oom_kill_try;
+
+/* local vfs journaling data */
+	struct handle_s *j_handle;
 };
 
 /*
@@ -399,6 +403,7 @@
 /* signals */	SPIN_LOCK_UNLOCKED, &init_signals, {{0}}, {{0}}, NULL, &init_task.sigqueue, 0, 0, \
 /* exec cts */	0,0, \
 /* oom */	0, \
+/* jfs */	NULL, \
 }
 
 union task_union {
--- linux-2.2.18pre17.ext3-0.0.5a/init/main.c.~1~	Wed Oct 25 19:09:54 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/init/main.c	Wed Oct 25 19:11:05 2000
@@ -71,6 +71,8 @@
 #error sorry, your GCC is too old. It builds incorrect kernels.
 #endif
 
+char * root_mount_data = NULL;
+
 extern char _stext, _etext;
 extern char *linux_banner;
 
@@ -702,6 +704,14 @@
 	ROOT_DEV = name_to_kdev_t(line);
 }
 
+static void root_data_setup(char *line, int *num)
+{
+	static char buffer[128];
+
+	strcpy(buffer, line);
+	root_mount_data = buffer;
+}
+
 /*
  * List of kernel command line parameters. The first table lists parameters
  * which are subject to values parsing (leading numbers are converted to
@@ -1071,6 +1081,7 @@
 
 static struct kernel_param raw_params[] __initdata = {
 	{ "root=", root_dev_setup },
+	{ "rootflags=", root_data_setup },
 #ifdef CONFIG_ROOT_NFS
 	{ "nfsroot=", nfs_root_setup },
 	{ "nfsaddrs=", ip_auto_config_setup },
--- linux-2.2.18pre17.ext3-0.0.5a/kernel/ksyms.c.~1~	Wed Oct 25 18:57:53 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/kernel/ksyms.c	Wed Oct 25 19:11:05 2000
@@ -70,6 +70,9 @@
 };
 #endif
 
+extern void jfs_preclean_buffer_check(struct buffer_head *);
+extern void jfs_prelock_buffer_check(struct buffer_head *);
+
 
 #ifdef CONFIG_KMOD
 EXPORT_SYMBOL(request_module);
@@ -252,6 +255,11 @@
 EXPORT_SYMBOL(max_sectors);
 EXPORT_SYMBOL(max_segments);
 EXPORT_SYMBOL(max_readahead);
+EXPORT_SYMBOL(show_buffers);
+
+/* JFS debugging only: */
+EXPORT_SYMBOL(jfs_prelock_buffer_check);
+EXPORT_SYMBOL(jfs_preclean_buffer_check);
 
 /* tty routines */
 EXPORT_SYMBOL(tty_hangup);
--- linux-2.2.18pre17.ext3-0.0.5a/kernel/sysctl.c.~1~	Wed Oct 25 18:57:53 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/kernel/sysctl.c	Wed Oct 25 19:19:41 2000
@@ -22,6 +22,8 @@
 #include <linux/smp_lock.h>
 #include <linux/init.h>
 #include <linux/sysrq.h>
+#include <linux/fs.h>
+#include <linux/jfs.h>
 
 #include <asm/uaccess.h>
 
@@ -287,6 +289,10 @@
 	 0644, NULL, &proc_dointvec},
 	{FS_DENTRY, "dentry-state", &dentry_stat, 6*sizeof(int),
 	 0444, NULL, &proc_dointvec},
+#if defined(CONFIG_EXT3_FS) && defined(JFS_DEBUG)
+	{KERN_SHMMAX, "jfs-debug", &journal_enable_debug, sizeof (int),
+	 0644, NULL, &proc_dointvec},
+#endif
 	{0}
 };
 
--- linux-2.2.18pre17.ext3-0.0.5a/mm/slab.c.~1~	Wed Oct 25 18:57:53 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/mm/slab.c	Wed Oct 25 19:11:05 2000
@@ -125,7 +125,7 @@
  * SLAB_SELFTEST	- 1 to perform a few tests, mainly for development.
  */
 #define		SLAB_MGMT_CHECKS	1
-#define		SLAB_DEBUG_SUPPORT	0
+#define		SLAB_DEBUG_SUPPORT	1
 #define		SLAB_STATS		0
 #define		SLAB_SELFTEST		0
 
@@ -471,7 +471,7 @@
 			 * allow tighter packing of the smaller caches. */
 			if (!(sizes->cs_cachep =
 			      kmem_cache_create(*names++, sizes->cs_size,
-						0, SLAB_HWCACHE_ALIGN, NULL, NULL)))
+						0, SLAB_HWCACHE_ALIGN|SLAB_POISON, NULL, NULL)))
 				goto panic_time;
 			if (!found) {
 				/* Inc off-slab bufctl limit until the ceiling is hit. */
--- linux-2.2.18pre17.ext3-0.0.5a/scripts/genkdbsym.awk.~1~	Wed Oct 25 19:09:54 2000
+++ linux-2.2.18pre17.ext3-0.0.5a/scripts/genkdbsym.awk	Wed Oct 25 19:11:05 2000
@@ -1,7 +1,7 @@
 BEGIN	{
 		printf("#include <linux/kdb.h>\n");
 		printf("#include \"ksym.h\"\n\n");
-		printf("#define   KDBMAXSYMTABSIZE 7500\n");
+		printf("#define   KDBMAXSYMTABSIZE 10000\n");
 		printf("\n__ksymtab_t __attribute__ ((section(\"kdbsymtab\"))) __kdbsymtab[KDBMAXSYMTABSIZE] = {\n");
 		symcount = 0
 		printf("/* Generated file */\n") > "ksym.h";
