--- /dev/null
+diff -ur kernel-2.4.18/drivers/block/blkpg.c kernel-2.4.18.patched/drivers/block/blkpg.c
+--- kernel-2.4.18/drivers/block/blkpg.c Tue May 7 17:33:10 2002
++++ kernel-2.4.18.patched/drivers/block/blkpg.c Thu May 30 13:38:14 2002
+@@ -295,3 +295,38 @@
+ }
+
+ EXPORT_SYMBOL(blk_ioctl);
++
++#define NUM_DEV_NO_WRITE 16
++static int dev_no_write[NUM_DEV_NO_WRITE];
++
++/*
++ * Debug code for turning block devices "read-only" (will discard writes
++ * silently). This is for filesystem crash/recovery testing.
++ */
++void dev_set_rdonly(kdev_t dev, int no_write)
++{
++ if (dev) {
++ printk(KERN_WARNING "Turning device %s read-only\n",
++ bdevname(dev));
++ dev_no_write[no_write] = 0xdead0000 + dev;
++ }
++}
++
++int dev_check_rdonly(kdev_t dev) {
++ int i;
++
++ for (i = 0; i < NUM_DEV_NO_WRITE; i++) {
++ if ((dev_no_write[i] & 0xffff0000) == 0xdead0000 &&
++ dev == (dev_no_write[i] & 0xffff))
++ return 1;
++ }
++ return 0;
++}
++
++void dev_clear_rdonly(int no_write) {
++ dev_no_write[no_write] = 0;
++}
++
++EXPORT_SYMBOL(dev_set_rdonly);
++EXPORT_SYMBOL(dev_check_rdonly);
++EXPORT_SYMBOL(dev_clear_rdonly);
+diff -ur kernel-2.4.18/drivers/block/loop.c kernel-2.4.18.patched/drivers/block/loop.c
+--- kernel-2.4.18/drivers/block/loop.c Tue May 7 16:48:59 2002
++++ kernel-2.4.18.patched/drivers/block/loop.c Thu May 30 13:38:14 2002
+@@ -503,6 +503,11 @@
+ spin_unlock_irq(&lo->lo_lock);
+
+ if (rw == WRITE) {
++#ifdef CONFIG_DEV_RDONLY
++ if (dev_check_rdonly(rbh->b_rdev))
++ goto err;
++#endif
++
+ if (lo->lo_flags & LO_FLAGS_READ_ONLY)
+ goto err;
+ } else if (rw == READA) {
+diff -ur kernel-2.4.18/drivers/ide/ide-disk.c kernel-2.4.18.patched/drivers/ide/ide-disk.c
+--- kernel-2.4.18/drivers/ide/ide-disk.c Tue May 7 15:43:35 2002
++++ kernel-2.4.18.patched/drivers/ide/ide-disk.c Thu May 30 13:38:14 2002
+@@ -557,6 +557,12 @@
+ */
+ static ide_startstop_t do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block)
+ {
++#ifdef CONFIG_DEV_RDONLY
++ if (rq->cmd == WRITE && dev_check_rdonly(rq->rq_dev)) {
++ ide_end_request(1, HWGROUP(drive));
++ return ide_stopped;
++ }
++#endif
+ if (IDE_CONTROL_REG)
+ OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
+
+diff -ur kernel-2.4.18/fs/ext3/Makefile kernel-2.4.18.patched/fs/ext3/Makefile
+--- kernel-2.4.18/fs/ext3/Makefile Tue May 7 14:53:46 2002
++++ kernel-2.4.18.patched/fs/ext3/Makefile Thu May 30 13:38:14 2002
+@@ -9,6 +9,8 @@
+
+ O_TARGET := ext3.o
+
++export-objs := super.o
++
+ obj-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \
+ ioctl.o namei.o super.o symlink.o
+ obj-m := $(O_TARGET)
+diff -ur kernel-2.4.18/fs/ext3/super.c kernel-2.4.18.patched/fs/ext3/super.c
+--- kernel-2.4.18/fs/ext3/super.c Tue May 7 16:43:17 2002
++++ kernel-2.4.18.patched/fs/ext3/super.c Thu May 30 13:38:14 2002
+@@ -1746,7 +1746,7 @@
+ unregister_filesystem(&ext3_fs_type);
+ }
+
+-EXPORT_NO_SYMBOLS;
++EXPORT_SYMBOL(ext3_bread);
+
+ MODULE_AUTHOR("Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others");
+ MODULE_DESCRIPTION("Second Extended Filesystem with journaling extensions");
+diff -ur kernel-2.4.18/fs/jbd/commit.c kernel-2.4.18.patched/fs/jbd/commit.c
+--- kernel-2.4.18/fs/jbd/commit.c Tue May 7 15:39:35 2002
++++ kernel-2.4.18.patched/fs/jbd/commit.c Thu May 30 13:38:14 2002
+@@ -482,7 +482,7 @@
+ 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
++ Wait for the buffers 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.
+ */
+@@ -575,8 +575,10 @@
+
+ jbd_debug(3, "JBD: commit phase 6\n");
+
+- if (is_journal_aborted(journal))
++ if (is_journal_aborted(journal)) {
++ unlock_journal(journal);
+ goto skip_commit;
++ }
+
+ /* Done it all: now write the commit record. We should have
+ * cleaned up our previous buffers by now, so if we are in abort
+@@ -586,9 +588,10 @@
+ descriptor = journal_get_descriptor_buffer(journal);
+ if (!descriptor) {
+ __journal_abort_hard(journal);
++ unlock_journal(journal);
+ goto skip_commit;
+ }
+-
++
+ /* AKPM: buglet - add `i' to tmp! */
+ for (i = 0; i < jh2bh(descriptor)->b_size; i += 512) {
+ journal_header_t *tmp =
+@@ -609,7 +612,6 @@
+ put_bh(bh); /* One for getblk() */
+ journal_unlock_journal_head(descriptor);
+ }
+- lock_journal(journal);
+
+ /* End of a transaction! Finally, we can do checkpoint
+ processing: any buffers committed as a result of this
+@@ -617,6 +619,25 @@
+ before. */
+
+ skip_commit:
++
++ /* Call any callbacks that had been registered for handles in this
++ * transaction. It is up to the callback to free any allocated
++ * memory.
++ */
++ if (!list_empty(&commit_transaction->t_jcb)) {
++ struct list_head *p, *n;
++ int error = is_journal_aborted(journal);
++
++ list_for_each_safe(p, n, &commit_transaction->t_jcb) {
++ struct journal_callback *jcb;
++
++ jcb = list_entry(p, struct journal_callback, jcb_list);
++ list_del(p);
++ jcb->jcb_func(jcb, error);
++ }
++ }
++
++ lock_journal(journal);
+
+ jbd_debug(3, "JBD: commit phase 7\n");
+
+diff -ur kernel-2.4.18/fs/jbd/journal.c kernel-2.4.18.patched/fs/jbd/journal.c
+--- kernel-2.4.18/fs/jbd/journal.c Tue May 7 16:43:17 2002
++++ kernel-2.4.18.patched/fs/jbd/journal.c Thu May 30 13:38:14 2002
+@@ -58,6 +58,7 @@
+ #endif
+ EXPORT_SYMBOL(journal_flush);
+ EXPORT_SYMBOL(journal_revoke);
++EXPORT_SYMBOL(journal_callback_set);
+
+ EXPORT_SYMBOL(journal_init_dev);
+ EXPORT_SYMBOL(journal_init_inode);
+diff -ur kernel-2.4.18/fs/jbd/transaction.c kernel-2.4.18.patched/fs/jbd/transaction.c
+--- kernel-2.4.18/fs/jbd/transaction.c Tue May 7 16:43:17 2002
++++ kernel-2.4.18.patched/fs/jbd/transaction.c Thu May 30 13:38:14 2002
+@@ -57,6 +57,7 @@
+ transaction->t_state = T_RUNNING;
+ transaction->t_tid = journal->j_transaction_sequence++;
+ transaction->t_expires = jiffies + journal->j_commit_interval;
++ INIT_LIST_HEAD(&transaction->t_jcb);
+
+ /* Set up the commit timer for the new transaction. */
+ J_ASSERT (!journal->j_commit_timer_active);
+@@ -201,6 +202,20 @@
+ return 0;
+ }
+
++/* Allocate a new handle. This should probably be in a slab... */
++static handle_t *get_handle(int nblocks)
++{
++ handle_t *handle = jbd_kmalloc(sizeof (handle_t), GFP_NOFS);
++ if (!handle)
++ return NULL;
++ memset(handle, 0, sizeof (handle_t));
++ handle->h_buffer_credits = nblocks;
++ handle->h_ref = 1;
++ INIT_LIST_HEAD(&handle->h_jcb);
++
++ return handle;
++}
++
+ /*
+ * Obtain a new handle.
+ *
+@@ -227,14 +242,11 @@
+ handle->h_ref++;
+ return handle;
+ }
+-
+- handle = jbd_kmalloc(sizeof (handle_t), GFP_NOFS);
++
++ handle = get_handle(nblocks);
+ if (!handle)
+ return ERR_PTR(-ENOMEM);
+- memset (handle, 0, sizeof (handle_t));
+
+- handle->h_buffer_credits = nblocks;
+- handle->h_ref = 1;
+ current->journal_info = handle;
+
+ err = start_this_handle(journal, handle);
+@@ -333,14 +345,11 @@
+
+ if (is_journal_aborted(journal))
+ return ERR_PTR(-EIO);
+-
+- handle = jbd_kmalloc(sizeof (handle_t), GFP_NOFS);
++
++ handle = get_handle(nblocks);
+ if (!handle)
+ return ERR_PTR(-ENOMEM);
+- memset (handle, 0, sizeof (handle_t));
+
+- handle->h_buffer_credits = nblocks;
+- handle->h_ref = 1;
+ current->journal_info = handle;
+
+ err = try_start_this_handle(journal, handle);
+@@ -1319,6 +1328,29 @@
+ #endif
+
+ /*
++ * Register a callback function for this handle. The function will be
++ * called when the transaction that this handle is part of has been
++ * committed to disk with the original callback data struct and the
++ * error status of the journal as parameters. There is no guarantee of
++ * ordering between handles within a single transaction, nor between
++ * callbacks registered on the same handle.
++ *
++ * The caller is responsible for allocating the journal_callback struct.
++ * This is to allow the caller to add as much extra data to the callback
++ * as needed, but reduce the overhead of multiple allocations. The caller
++ * allocated struct must start with a struct journal_callback at offset 0,
++ * and has the caller-specific data afterwards.
++ */
++void journal_callback_set(handle_t *handle, void (*func)(void *, int),
++ void *cb_data)
++{
++ struct journal_callback *jcb = cb_data;
++
++ list_add(&jcb->jcb_list, &handle->h_jcb);
++ jcb->jcb_func = func;
++}
++
++/*
+ * All done for a particular handle.
+ *
+ * There is not much action needed here. We just return any remaining
+@@ -1383,7 +1415,10 @@
+ wake_up(&journal->j_wait_transaction_locked);
+ }
+
+- /*
++ /* Move callbacks from the handle to the transaction. */
++ list_splice(&handle->h_jcb, &transaction->t_jcb);
++
++ /*
+ * If the handle 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
+diff -ur kernel-2.4.18/include/linux/blkdev.h kernel-2.4.18.patched/include/linux/blkdev.h
+--- kernel-2.4.18/include/linux/blkdev.h Tue May 7 15:46:15 2002
++++ kernel-2.4.18.patched/include/linux/blkdev.h Thu May 30 13:42:25 2002
+@@ -276,4 +276,9 @@
+ }
+ return retval;
+ }
++
++#define CONFIG_DEV_RDONLY
++void dev_set_rdonly(kdev_t, int);
++int dev_Check_rdonly(kdev_t);
++void dev_clear_rdonly(int);
+ #endif
+diff -ur kernel-2.4.18/include/linux/fs.h kernel-2.4.18.patched/include/linux/fs.h
+--- kernel-2.4.18/include/linux/fs.h Tue May 7 16:40:30 2002
++++ kernel-2.4.18.patched/include/linux/fs.h Thu May 30 13:40:51 2002
+@@ -1034,6 +1034,7 @@
+ extern struct vfsmount *kern_mount(struct file_system_type *);
+ extern int may_umount(struct vfsmount *);
+ extern long do_mount(char *, char *, char *, unsigned long, void *);
++struct vfsmount *do_kern_mount(const char *type, int flags, char *name, void *data);
+ extern void umount_tree(struct vfsmount *);
+
+ #define kern_umount mntput
+diff -ur kernel-2.4.18/include/linux/jbd.h kernel-2.4.18.patched/include/linux/jbd.h
+--- kernel-2.4.18/include/linux/jbd.h Tue May 7 16:43:17 2002
++++ kernel-2.4.18.patched/include/linux/jbd.h Thu May 30 13:38:14 2002
+@@ -257,6 +257,13 @@
+ return bh->b_private;
+ }
+
++#define HAVE_JOURNAL_CALLBACK_STATUS
++struct journal_callback {
++ struct list_head jcb_list;
++ void (*jcb_func)(void *cb_data, int error);
++ /* user data goes here */
++};
++
+ struct jbd_revoke_table_s;
+
+ /* The handle_t type represents a single atomic update being performed
+@@ -287,6 +294,12 @@
+ operations */
+ int h_err;
+
++ /* List of application registered callbacks for this handle.
++ * The function(s) will be called after the transaction that
++ * this handle is part of has been committed to disk.
++ */
++ struct list_head h_jcb;
++
+ /* Flags */
+ unsigned int h_sync: 1; /* sync-on-close */
+ unsigned int h_jdata: 1; /* force data journaling */
+@@ -406,6 +419,10 @@
+
+ /* How many handles used this transaction? */
+ int t_handle_count;
++
++ /* List of registered callback functions for this transaction.
++ * Called when the transaction is committed. */
++ struct list_head t_jcb;
+ };
+
+
+@@ -654,6 +671,8 @@
+ extern int journal_try_to_free_buffers(journal_t *, struct page *, int);
+ extern int journal_stop(handle_t *);
+ extern int journal_flush (journal_t *);
++extern void journal_callback_set(handle_t *handle, void (*fn)(void *, int),
++ void *cb_data);
+
+ extern void journal_lock_updates (journal_t *);
+ extern void journal_unlock_updates (journal_t *);
+diff -ur kernel-2.4.18/kernel/ksyms.c kernel-2.4.18.patched/kernel/ksyms.c
+--- kernel-2.4.18/kernel/ksyms.c Mon May 20 00:15:42 2002
++++ kernel-2.4.18.patched/kernel/ksyms.c Thu May 30 13:38:14 2002
+@@ -305,6 +305,11 @@
+ EXPORT_SYMBOL(lock_may_write);
+ EXPORT_SYMBOL(dcache_readdir);
+
++/* lustre */
++EXPORT_SYMBOL(pagecache_lock);
++EXPORT_SYMBOL(do_kern_mount);
++
++
+ /* for stackable file systems (lofs, wrapfs, cryptfs, etc.) */
+ EXPORT_SYMBOL(default_llseek);
+ EXPORT_SYMBOL(dentry_open);