diff -ruN linux-2.3.45-vanilla/CREDITS linux-2.3.45-fist/CREDITS
--- linux-2.3.45-vanilla/CREDITS	Sun Feb 13 21:21:45 2000
+++ linux-2.3.45-fist/CREDITS	Mon Feb 14 01:15:47 2000
@@ -2591,6 +2591,14 @@
 S: Bellevue, Washington 98007
 S: USA
 
+N: Erez Zadok
+E: ezk@cs.columbia.edu
+W: http://www.cs.columbia.edu/~ezk
+D: stackable file systems: lofs, wrapfs, cryptfs, etc.
+S: Columbia University, M/C 0401
+S: New York, NY 10027
+S: USA
+
 N: Richard Zidlicky
 E: rdzidlic@geocities.com,rdzidlic@cip.informatik.uni-erlangen.de
 W: http://www.geocities.com/SiliconValley/Bay/2602/
diff -ruN linux-2.3.45-vanilla/Documentation/Configure.help linux-2.3.45-fist/Documentation/Configure.help
--- linux-2.3.45-vanilla/Documentation/Configure.help	Thu Feb 10 15:28:01 2000
+++ linux-2.3.45-fist/Documentation/Configure.help	Mon Feb 14 01:15:47 2000
@@ -8944,6 +8944,28 @@
   whenever you want), say M here and read Documentation/modules.txt.
   The module will be called efs.o. 
 
+Loopback (lofs) filesystem support (EXPERIMENTAL)
+CONFIG_LOFS_FS
+  This is a loopback-mount stackable file system much like
+  SunOS/Solaris's lofs or BSD-44's nullfs file systems.  Saying Y here
+  will allow you to mount any directory on any other directory, as if
+  the two were linked together.  Lofs is especially useful inside
+  chroot()ed environments, to provide file access to portions of the
+  file systems outside the chrooted directory.
+
+  Please read the file Documentation/filesystems/stackable-fs.txt for
+  more information.  Additional information, documentation, and
+  sources to other stackable file systems (Wrapfs, Cryptfs, Rot13fs,
+  Usenetfs, and more) can be found in
+  http://www.cs.columbia.edu/~ezk/research/software/.
+
+  If you want to compile the LOFS filesystem support as a module
+  (i.e., code which can be inserted in and removed from the running
+  kernel whenever you want), say M here and read
+  Documentation/modules.txt.  The module will be called lofs.o.
+
+  If you haven't heard about all of this before, it's safe to say N.
+
 SGI disklabel support
 CONFIG_SGI_DISKLABEL
   Say Y to this only if you plan on mounting disks with SGI
diff -ruN linux-2.3.45-vanilla/Documentation/filesystems/00-INDEX linux-2.3.45-fist/Documentation/filesystems/00-INDEX
--- linux-2.3.45-vanilla/Documentation/filesystems/00-INDEX	Sat Nov 27 18:27:48 1999
+++ linux-2.3.45-fist/Documentation/filesystems/00-INDEX	Mon Feb 14 01:15:48 2000
@@ -24,6 +24,8 @@
 	- Description of the ROMFS filesystem.
 smbfs.txt
 	- info on using filesystems with the SMB protocol (Windows 3.11 and NT)
+stackable-fs.txt
+	- info on using stackable file systems such as lofs.
 sysv-fs.txt
 	- info on the SystemV/Coherent filesystem.
 udf.txt
diff -ruN linux-2.3.45-vanilla/Documentation/filesystems/stackable-fs.txt linux-2.3.45-fist/Documentation/filesystems/stackable-fs.txt
--- linux-2.3.45-vanilla/Documentation/filesystems/stackable-fs.txt	Wed Dec 31 19:00:00 1969
+++ linux-2.3.45-fist/Documentation/filesystems/stackable-fs.txt	Mon Feb 14 01:15:48 2000
@@ -0,0 +1,25 @@
+USING LOFS AND OTHER STACKABLE FILE SYSTEMS
+
+(1) Build a kernel that includes the "LOFS" file system.  If you built lofs
+    as a module, then run "insmod lofs".
+
+(2) Mount the lofs file system.  Let's say you want the mount point to be
+    /mnt and the source files to come from /n/fist:
+
+$ mount -t lofs -o dir=/n/fist /dev/null /mnt
+
+(3) Start using the file system.
+
+$ cd /mnt
+
+Have fun.  You can create new files here, and do everything normally inside
+the new stackable file system.
+
+If you have any questions or face problems, contact me.
+
+Additional information, documentation, and sources to other stackable files
+(Wrapfs, Cryptfs, Rot13fs, Usenetfs, and more) can be found in
+
+	http://www.cs.columbia.edu/~ezk/research/software/.
+
+Erez Zadok <ezk@cs.columbia.edu>
diff -ruN linux-2.3.45-vanilla/fs/Config.in linux-2.3.45-fist/fs/Config.in
--- linux-2.3.45-vanilla/fs/Config.in	Sun Feb 13 15:40:41 2000
+++ linux-2.3.45-fist/fs/Config.in	Mon Feb 14 01:15:48 2000
@@ -34,6 +34,10 @@
    define_bool CONFIG_JOLIET n
 fi
 
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  tristate 'Loopback (lofs) filesystem support (EXPERIMENTAL)' CONFIG_LOFS_FS
+fi
+
 tristate 'Minix fs support' CONFIG_MINIX_FS
 
 tristate 'NTFS filesystem support (read only)' CONFIG_NTFS_FS
diff -ruN linux-2.3.45-vanilla/fs/Makefile linux-2.3.45-fist/fs/Makefile
--- linux-2.3.45-vanilla/fs/Makefile	Tue Jan 25 14:45:02 2000
+++ linux-2.3.45-fist/fs/Makefile	Mon Feb 14 01:15:48 2000
@@ -206,6 +206,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_LOFS_FS),y)
+SUB_DIRS += lofs
+else
+  ifeq ($(CONFIG_LOFS_FS),m)
+  MOD_SUB_DIRS += lofs
+  endif
+endif
+
 ifeq ($(CONFIG_EFS_FS),y)
 SUB_DIRS += efs
 else
diff -ruN linux-2.3.45-vanilla/fs/filesystems.c linux-2.3.45-fist/fs/filesystems.c
--- linux-2.3.45-vanilla/fs/filesystems.c	Tue Jan 25 17:18:52 2000
+++ linux-2.3.45-fist/fs/filesystems.c	Mon Feb 14 01:15:48 2000
@@ -56,6 +56,10 @@
 extern int init_openprom_fs(void);
 #endif
 
+#ifdef CONFIG_LOFS_FS
+extern int init_lofs_fs(void);
+#endif
+
 void __init filesystem_setup(void)
 {
 #ifdef CONFIG_MINIX_FS
@@ -132,6 +136,10 @@
 
 #ifdef CONFIG_UFS_FS
 	init_ufs_fs();
+#endif
+
+#ifdef CONFIG_LOFS_FS
+	init_lofs_fs();
 #endif
 
 #ifdef CONFIG_EFS_FS
diff -ruN linux-2.3.45-vanilla/fs/lofs/Makefile linux-2.3.45-fist/fs/lofs/Makefile
--- linux-2.3.45-vanilla/fs/lofs/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.3.45-fist/fs/lofs/Makefile	Mon Feb 14 01:15:48 2000
@@ -0,0 +1,39 @@
+#
+# Makefile for the linux lofs-filesystem routines.
+#
+# This is a Makefile to build lofs as part of the linux kernel tree.
+# To build lofs and any other stackable file systems outside the linux
+# kernel tree, download the proper version from
+#	http://www.cs.columbia.edu/~ezk/research/software/
+#
+
+O_TARGET := lofs.o
+O_OBJS   := dentry.o file.o inode.o main.o subr.o super.o vm_area.o
+
+M_OBJS   := $(O_TARGET)
+
+ifdef TOPDIR
+#
+# Part of the kernel code
+#
+include $(TOPDIR)/Rules.make
+EXTRA_CFLAGS += -I.
+else
+#
+# Standalone (handy for development)
+#
+include ../Makefile.rules
+
+CFLAGS += -D__KERNEL__ -DMODULE $(KFLAGS) -I../include -I$(KINCLUDE) $(MODFLAGS)
+
+all: $(O_TARGET)
+
+$(O_TARGET): $(O_OBJS)
+	$(LD) -r -o $(O_TARGET) $(O_OBJS)
+
+install: $(O_TARGET)
+	install -c $(O_TARGET) /lib/modules/`uname -r`/fs
+
+clean:
+	rm -f *.o *.s
+endif
diff -ruN linux-2.3.45-vanilla/fs/lofs/dentry.c linux-2.3.45-fist/fs/lofs/dentry.c
--- linux-2.3.45-vanilla/fs/lofs/dentry.c	Wed Dec 31 19:00:00 1969
+++ linux-2.3.45-fist/fs/lofs/dentry.c	Mon Feb 14 01:15:48 2000
@@ -0,0 +1,149 @@
+/*
+ *  $Id: dentry.c,v 1.2 1999/07/13 07:21:31 ezk Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <fist.h>
+#include <lofs.h>
+
+
+/*
+ * XXX: unsure of this implementation. there may be bugs in cached_lookup
+ * in that we don't understand why it will run d_invalidate after a
+ * succesful file system's d_revalidate.
+ */
+STATIC int
+lofs_d_revalidate(dentry_t *dentry, int flags)
+{
+    int err = 0;
+    dentry_t *hidden_dentry = lofs_hidden_dentry(dentry);
+
+    print_entry_location();
+
+    if (!hidden_dentry->d_op || !hidden_dentry->d_op->d_revalidate)
+	goto out;
+
+    err = hidden_dentry->d_op->d_revalidate(hidden_dentry, flags);
+
+ out:
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+lofs_d_hash(dentry_t *dentry, qstr_t *name)
+{
+    int err = 0;
+    dentry_t *hidden_dentry = lofs_hidden_dentry(dentry);
+
+    print_entry_location();
+
+    if (!hidden_dentry->d_op || !hidden_dentry->d_op->d_hash)
+	goto out;
+
+    err = hidden_dentry->d_op->d_hash(hidden_dentry, name);
+
+ out:
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+lofs_d_compare(dentry_t *dentry, qstr_t *a, qstr_t *b)
+{
+    int err;
+    dentry_t *hidden_dentry = lofs_hidden_dentry(dentry);
+
+    print_entry_location();
+
+    if (hidden_dentry->d_op && hidden_dentry->d_op->d_compare) {
+	err = hidden_dentry->d_op->d_compare(hidden_dentry, a, b);
+    } else {
+	err = ((a->len != b->len) || memcmp(a->name, b->name, b->len));
+    }
+
+    print_exit_status(err);
+    return err;
+}
+
+
+#ifdef NOT_NEEDED
+/*
+ * Not needed because the real place where dput() needs to free up
+ * private dentry resources in right before the actual destruction of
+ * the dentry, which is done in d_free, called from dput().
+ */
+STATIC void
+lofs_d_delete(dentry_t *dentry)
+{
+    fist_print_dentry("lofs_d_delete dentry", dentry);
+    if (dentry)
+	fist_print_inode("lofs_d_delete inode", dentry->d_inode);
+}
+#endif /* NOT_NEEDED */
+
+/*
+ * XXX: we may have a problem with ever increasing dentry refcounts
+ * if so, we may have to add a new dentry op d_dput which will have
+ * to be called from dput().
+ */
+STATIC
+void
+lofs_d_release(dentry_t *dentry)
+{
+    dentry_t *hidden_dentry = lofs_hidden_dentry(dentry);
+
+    print_entry_location();
+    fist_print_dentry("lofs_d_release IN hidden_dentry", hidden_dentry);
+    if (hidden_dentry->d_inode)
+	fist_dprint(6, "lofs_d_release: hidden_inode->i_count %d, i_num %lu.\n",
+		    hidden_dentry->d_inode->i_count,
+		    hidden_dentry->d_inode->i_ino
+		    );
+    dput(hidden_dentry);
+
+    /* XXX: free private data (lofs_dentry_info) here */
+    print_exit_location();
+}
+
+
+/*
+ * we don't need lofs_iput, because dentry_iput will call iput() if
+ * d_iput is not defined, and iput() will eventually call clear_inode(),
+ * will calls our clear_inode(), which does a dput().  dput() will decrement
+ * the refcount of the hidden dentry, and if zero, will call dentry_iput()
+ * on the hidden dentry, calling the hidden file system's d_iput if defined.
+ * We left this implemented for ease of tracing/debugging.
+ */
+STATIC void
+lofs_d_iput(dentry_t *dentry, inode_t *inode)
+{
+    print_entry_location();
+    iput(inode);
+    print_exit_location();
+}
+
+
+struct dentry_operations lofs_dops = {
+    lofs_d_revalidate,		/* revalidate */
+    lofs_d_hash,		/* hash */
+    lofs_d_compare,		/* compare */
+#if NOT_NEEDED
+    lofs_d_delete,		/* delete */
+#else
+    NULL,			/* delete */
+#endif
+    lofs_d_release,		/* release */
+    lofs_d_iput			/* iput */
+};
+
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * End:
+ */
diff -ruN linux-2.3.45-vanilla/fs/lofs/file.c linux-2.3.45-fist/fs/lofs/file.c
--- linux-2.3.45-vanilla/fs/lofs/file.c	Wed Dec 31 19:00:00 1969
+++ linux-2.3.45-fist/fs/lofs/file.c	Mon Feb 14 01:15:48 2000
@@ -0,0 +1,737 @@
+/*
+ *  $Id: file.c,v 1.6 1999/10/26 04:43:25 ezk Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <fist.h>
+#include <lofs.h>
+
+
+/*******************
+ * File Operations *
+ *******************/
+
+
+STATIC loff_t
+lofs_llseek(file_t *file, loff_t offset, int origin)
+{
+    loff_t err;
+    file_t *hidden_file = ftohf(file);
+
+    print_entry_location();
+
+    fist_dprint(6, "lofs_llseek: file=%x, offset=0x%x, origin=%d\n",
+		(int) file, (int) offset, origin);
+
+    /* always set hidden position to this one */
+    hidden_file->f_pos = file->f_pos;
+    if (file->f_reada) { /* update readahead information if needed */
+	hidden_file->f_reada = file->f_reada;
+	hidden_file->f_ramax = file->f_ramax;
+	hidden_file->f_raend = file->f_raend;
+	hidden_file->f_ralen = file->f_ralen;
+	hidden_file->f_rawin = file->f_rawin;
+    }
+    ASSERT(hidden_file->f_reada == file->f_reada);
+    ASSERT(hidden_file->f_ramax == file->f_ramax);
+    ASSERT(hidden_file->f_raend == file->f_raend);
+    ASSERT(hidden_file->f_ralen == file->f_ralen);
+    ASSERT(hidden_file->f_rawin == file->f_rawin);
+
+    if (hidden_file->f_op && hidden_file->f_op->llseek) {
+	err = hidden_file->f_op->llseek(hidden_file, offset, origin);
+    } else {
+	err = default_llseek(hidden_file, offset, origin);
+    }
+    if (err < 0) {
+	goto out;
+    }
+
+    if (err != file->f_pos) {
+	file->f_pos = err;
+	// ION maybe this?
+	// 	file->f_pos = hidden_file->f_pos;
+	file->f_reada = 0;
+	file->f_version = ++event;
+    }
+
+out:
+    print_exit_status(err);
+    return err;
+}
+
+
+#if 0
+STATIC ssize_t
+lofs_read(file_t *file, char *buf, size_t count, loff_t *ppos)
+{
+    int err = -EINVAL;
+    file_t *hidden_file = ftohf(file);
+    loff_t pos = *ppos;
+
+    print_entry_location();
+
+    if (!hidden_file->f_op || !hidden_file->f_op->read)
+	goto out;
+
+    err = hidden_file->f_op->read(hidden_file, buf, count, &pos);
+
+    if (err > 0) {
+	fist_copy_attr_atime(file->f_dentry->d_inode,
+			     hidden_file->f_dentry->d_inode);
+    }
+
+    // MAJOR HACK
+    /*
+     * because pread() does not have any way to tell us that it is
+     * our caller, then we don't know for sure if we have to update
+     * the file positions.  This hack relies on read() having passed us
+     * the "real" pointer of its struct file's f_pos field.
+     */
+    if (ppos == &file->f_pos)
+	hidden_file->f_pos = *ppos = pos;
+
+out:
+    print_exit_status(err);
+    return err;
+}
+#endif
+
+
+#if 1
+/*
+ * Adapted from 2.3 generic_file_write
+ */
+STATIC ssize_t
+lofs_write(file_t *file, const char *buf, size_t count, loff_t *ppos)
+{
+    file_t *hidden_file;
+    page_t *page, **hash;
+    unsigned long page_cache = 0;
+    unsigned long pgpos, orig_pgpos, offset, pgoff;
+    unsigned long bytes, written;
+    size_t bytes_to_write;
+    loff_t pos, orig_pos, tmp_pos;
+    ssize_t status;
+    long didread;
+    mm_segment_t old_fs;
+    char *hidden_buffer;
+    inode_t *inode = file->f_dentry->d_inode;
+
+    print_entry_location();
+
+    /* check that offsets are correct */
+    ASSERT(file != NULL);
+    ASSERT(ppos != NULL);
+
+    pos = *ppos;
+    written = 0;
+    status = 0;
+
+    old_fs = get_fs();
+    hidden_file = ftohf(file);
+    ASSERT(hidden_file != NULL);
+
+    hidden_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
+    if (!hidden_buffer) {
+	printk("Out of memory.\n");
+	status = -ENOMEM;
+	goto out;
+    }
+
+    if (file->f_flags & O_APPEND)
+	pos = inode->i_size;
+
+    fist_dprint(6, "EZK: WRITE: initial values: pos %ld, count %d\n", (long) pos, count);
+    fist_print_file("EZK: WRITE file IN", file);
+
+    /* we need to handle sparse files differently */
+    orig_pos = pos;
+    orig_pgpos = orig_pos & PAGE_MASK;
+    if (pos > inode->i_size)
+	pos = inode->i_size;
+
+    while (count) {
+	fist_dprint(7, "WRITE: pos %ld, written %ld, count %d, buf 0x%lx\n",
+		    (long) pos, written, count, (long) buf);
+	/*
+	 * Try to find the page in the cache. If it isn't there,
+	 * allocate a free page.
+	 */
+	offset = (pos & ~PAGE_MASK);
+	pgoff = pos >> PAGE_CACHE_SHIFT;
+	pgpos = pos & PAGE_MASK;
+
+	if ((bytes = PAGE_SIZE - offset) > count && pgpos >= orig_pgpos)
+	    bytes = count;
+
+	hash = page_hash(INODE_DATA(inode), pgoff);
+repeat_find:
+	page = __find_lock_page(INODE_DATA(inode), pgoff, hash);
+	if (!page) {
+	    if (!page_cache) {
+		page_cache = __get_free_page(GFP_KERNEL);
+		if (page_cache)
+		    goto repeat_find;
+		status = -ENOMEM;
+		break;
+	    }
+	    page = page_cache_entry(page_cache);
+	    if (add_to_page_cache_unique(page, INODE_DATA(inode), pgoff, hash))
+		goto repeat_find;
+	    page_cache = 0;
+	}
+	/*
+	 * Note: setting of the PG_locked bit is handled
+	 * below the i_op->xxx interface.
+	 */
+	didread = 0;
+	/* We should have exclusive IO access to the page... */
+	if (!PageLocked(page)) {
+	    PAGE_BUG(page);
+	}
+
+page_wait:
+	if (Page_Uptodate(page))
+	    goto do_update_page;
+
+	/*
+	 * The page is not up-to-date ... if we're writing less
+	 * than a full page of data, we may have to read it first.
+	 * But if the page is past the current end of file, we must
+	 * clear it before updating.
+	 */
+	if (bytes < PAGE_SIZE) {
+	    if (pgpos < inode->i_size) {
+		status = -EIO;
+		if (didread >= 2)
+		    goto done_with_page;
+		/* call our own readpage function */
+		status = inode->i_op->readpage(file, page);
+		if (status < 0)
+		    goto done_with_page;
+		didread++;
+		goto page_wait;
+	    } else {
+		/* Must clear for partial writes */
+		fist_dprint(7, "WRITE1: clearing page at offset 0x%x\n", (int) pgpos);
+		memset((void *) page_address(page), 0, PAGE_SIZE);
+	    }
+	} else if (pos < orig_pos) {
+	    /* Must clear for sparse files */
+	    fist_dprint(7, "WRITE2: clearing page at offset 0x%x\n", (int) pgpos);
+	    memset((void *) page_address(page), 0, PAGE_SIZE);
+	}
+	/*
+	 * N.B. We should defer setting PG_uptodate at least until
+	 * the data is copied. A failure in i_op->updatepage() could
+	 * leave the page with garbage data.
+	 */
+	set_bit(PG_uptodate, &page->flags);
+
+do_update_page:
+	/* Alright, the page is there.  Now update it. */
+	fist_dprint(7, "WRITE: count %d, pos %ld, offset %ld, pgpos 0x%x, bytes %d\n",
+		    count, (int) pos, (int) offset, (int) pgpos, (int) bytes);
+
+	if (pgpos < orig_pgpos)
+	    /* nothing to do, not even copy_from_user */
+	    goto encode_page;
+	if (pgpos == orig_pgpos) {
+	    /*
+	     * this is the only interesting case,
+	     * we have to shift from orig_pos to pos somehow
+	     */
+	    pos = orig_pos;
+	    offset = pos & ~PAGE_MASK;
+	    if ((bytes = PAGE_SIZE - offset) > count)
+		bytes = count;
+	}
+#if 0
+	status = inode->i_op->updatepage(file, page, buf,
+					 offset, bytes, sync);
+#else
+	fist_dprint(7, "WRITE: copying %ld bytes at offset %ld\n", bytes, offset);
+	copy_from_user((char *) (page_address(page) + offset), buf, bytes);
+encode_page:
+	memcpy(hidden_buffer, (char *) page_address(page), PAGE_SIZE);
+
+	hidden_file->f_pos = pgpos; /* is this needed? */
+	if (inode->i_size < pgpos + PAGE_SIZE)
+	    if (inode->i_size > pos + bytes)
+		bytes_to_write = inode->i_size - pgpos;
+	    else
+		bytes_to_write = offset + bytes;
+	else
+	    bytes_to_write = PAGE_SIZE;
+
+	fist_dprint(7, "WRITE: writing at pgpos %ld, size %ld\n", pgpos, bytes_to_write);
+	/* switch to kernel space */
+	set_fs(KERNEL_DS);
+	tmp_pos = pgpos;
+	status = hidden_file->f_op->write(hidden_file,
+					  hidden_buffer,
+					  bytes_to_write,
+					  &tmp_pos);
+	fist_dprint(7, "WROTE: tmp_pos %ld, status %d\n", (int) tmp_pos, (int) status);
+
+	/* switch back to user space */
+	set_fs(old_fs);
+#endif
+
+	/* the rest of the code must not see that we are writing extra bytes */
+	/* do not adjust status if only filling up holes */
+	if (status > 0 && pgpos >= orig_pgpos) {
+	    if (status < offset) {
+		status = 0;
+	    } else {
+		status -= offset;
+		if (status > bytes)
+		    status = bytes;
+	    }
+	}
+done_with_page:
+	/* page may have gotten unlocked in our readpage routine */
+	if (PageLocked(page))
+	    UnlockPage(page);
+	__free_page(page);	/* => page_cache_release(page) */
+	if (status < 0)
+	    break;
+
+	/* do not adjust these variables if only filling up holes */
+	if (pgpos >= orig_pgpos) {
+	    written += status;
+	    count -= status;
+	    buf += status;
+	}
+	pos += status;
+    }
+    // MAJOR HACK
+    /*
+     * because pread() does not have any way to tell us that it is
+     * our caller, then we don't know for sure if we have to update
+     * the file positions.  This hack relies on read() having passed us
+     * the "real" pointer of its struct file's f_pos field.
+     */
+    if (ppos == &file->f_pos)
+	hidden_file->f_pos = *ppos = pos;
+    //    file->f_pos = pos;
+
+    if (pos > inode->i_size)
+	inode->i_size = pos;
+
+    kfree_s(hidden_buffer, PAGE_SIZE);
+
+    if (page_cache)
+	free_page(page_cache);	/* => page_cache_free(page_cache) */
+
+    status = written ? written : status;
+
+out:
+    print_exit_status(status);
+    return status;
+}
+#endif
+
+
+STATIC int
+lofs_readdir(file_t *file, void *dirent, filldir_t filldir)
+{
+    int err = -ENOTDIR;
+    file_t *hidden_file = ftohf(file);
+    inode_t *inode = file->f_dentry->d_inode;
+    inode_t *hidden_inode = itohi(inode);
+
+    print_entry_location();
+
+    fist_checkinode(inode, "lofs_readdir");
+    if (!hidden_file || !hidden_file->f_op || !hidden_file->f_op->readdir)
+	goto out;
+
+    down(&hidden_inode->i_sem);
+    err = hidden_file->f_op->readdir(hidden_file, dirent, filldir);
+    up(&hidden_inode->i_sem);
+
+    file->f_pos = hidden_file->f_pos;
+    if (err > 0)
+	fist_copy_attr_atime(file->f_dentry->d_inode,
+			     hidden_file->f_dentry->d_inode);
+    fist_checkinode(inode, "post lofs_readdir");
+
+out:
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC unsigned int
+lofs_poll(file_t *file, poll_table *wait)
+{
+    int err = 0;
+    file_t *hidden_file = ftohf(file);
+
+    print_entry_location();
+
+    if (!hidden_file->f_op || !hidden_file->f_op->poll)
+	goto out;
+
+    err = hidden_file->f_op->poll(hidden_file, wait);
+
+out:
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+lofs_ioctl(inode_t *inode, file_t *file, unsigned int cmd, unsigned long arg)
+{
+    int err = 0;
+    file_t *hidden_file;
+#ifdef FIST_DEBUG
+    int val;
+#endif /* FIST_DEBUG */
+
+    print_entry_location();
+
+    /* check if asked for local commands */
+    switch (cmd) {
+#ifdef FIST_DEBUG
+    case FIST_IOCTL_GET_DEBUG_VALUE:
+	val = fist_get_debug_value();
+	printk("IOCTL GET: got arg %d\n", val);
+	err = put_user(val, (int *) arg);
+	break;
+
+    case FIST_IOCTL_SET_DEBUG_VALUE:
+	err = get_user(val, (int *) arg);
+	if (err)
+	    break;
+	fist_dprint(6, "IOCTL SET: got arg %d\n", val);
+	if (val < 0 || val > 20) {
+	    err = EINVAL;
+	    break;
+	}
+	fist_set_debug_value(val);
+	break;
+#endif /* FIST_DEBUG */
+
+	/* add non-debugging fist ioctl's here */
+
+    default:
+	hidden_file = ftohf(file);
+	/* pass operation to hidden filesystem, and return status */
+	if (hidden_file->f_op && hidden_file->f_op->ioctl)
+	    err = hidden_file->f_op->ioctl(itohi(inode), hidden_file, cmd, arg);
+    } /* end of switch statement */
+
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+lofs_mmap(file_t *file, vm_area_t *vma)
+{
+    int err = 0;
+    file_t *hidden_file = ftohf(file);
+    inode_t *inode = file->f_dentry->d_inode;
+    inode_t *hidden_inode = itohi(inode);
+    vm_area_t *hidden_vma = (vm_area_t *) 0xdeadc0de;
+
+    print_entry_location();
+
+    fist_dprint(6, "MMAP1: inode 0x%x, hidden_inode 0x%x, inode->i_count %d, hidden_inode->i_count %d\n",
+		(int) inode, (int) hidden_inode, (int) inode->i_count, (int) hidden_inode->i_count);
+
+    if (!hidden_file->f_op || !hidden_file->f_op->mmap) {
+	err = -ENODEV;
+	goto out;
+    }
+
+    /*
+     * Most of this code comes straight from generic_file_mmap
+     * in mm/filemap.c.
+     */
+    if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) {
+	vma->vm_ops = &lofs_shared_vmops;
+    } else {
+	vma->vm_ops = &lofs_private_vmops;
+    }
+    if (!inode->i_sb || !S_ISREG(inode->i_mode)) {
+	err = -EACCES;
+	goto out;
+    }
+    if (!hidden_inode->i_op || !hidden_inode->i_op->readpage) {
+	err = -ENOEXEC;
+	goto out;
+    }
+    UPDATE_ATIME(inode);
+
+    /*
+     * Now we do the hidden stuff, but only for shared maps.
+     */
+    if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) {
+	hidden_vma = kmalloc(sizeof(vm_area_t), GFP_KERNEL);
+	if (!hidden_vma) {
+	    printk("MMAP: Out of memory\n");
+	    err = -ENOMEM;
+	    goto out;
+	}
+	// ION, is this right?
+	memcpy(hidden_vma, vma, sizeof(vm_area_t));
+	vmatohvma(vma) = hidden_vma;
+	err = hidden_file->f_op->mmap(hidden_file, hidden_vma);
+	hidden_vma->vm_file = hidden_file;
+	atomic_inc(&hidden_file->f_count);
+    }
+
+    /*
+     * XXX: do we need to insert_vm_struct and merge_segments as is
+     * done in do_mmap()?
+     */
+ out:
+    fist_dprint(6, "MMAP2: inode 0x%x, hidden_inode 0x%x, inode->i_count %d, hidden_inode->i_count %d\n",
+		(int) inode, (int) hidden_inode, (int) inode->i_count, (int) hidden_inode->i_count);
+#if 0
+    if (!err) {
+	inode->i_mmap = vma;
+	hidden_inode->i_mmap = hidden_vma;
+    }
+#endif
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+lofs_open(inode_t *inode, file_t *file)
+{
+    int err = -ENFILE;
+    file_t *hidden_file;
+    inode_t *hidden_inode = itohi(inode);
+    dentry_t *hidden_dentry = lofs_hidden_dentry(file->f_dentry);
+
+    print_entry_location();
+
+    fist_print_dentry(__FUNCTION__ " IN hidden_dentry", hidden_dentry);
+
+    hidden_file = get_empty_filp();
+    if (!hidden_file)
+	goto out;
+
+    fist_dprint(8, "lofs_open: got empty hidden_file\n");
+    file->private_data = hidden_file; /* link two file's */
+
+    hidden_file->f_dentry = hidden_dentry;
+    dget(hidden_dentry);
+
+    fist_dprint(6, "lofs_open: got f_inode\n");
+    hidden_file->f_mode = file->f_mode | FMODE_READ;
+    hidden_file->f_flags = file->f_flags;
+    if (file->f_flags & O_APPEND) {
+	fist_dprint(5, "file is opened in append-only mode!!!\n");
+	hidden_file->f_flags &= ~O_APPEND;	/* turn off O_APPEND flag */
+    }
+    hidden_file->f_pos = file->f_pos;
+    hidden_file->f_reada = file->f_reada;
+
+    hidden_file->f_op = NULL;
+    if (hidden_inode->i_op)
+	hidden_file->f_op = hidden_inode->i_op->default_file_ops;
+
+    fist_dprint(6, "lofs_open: pre open\n");
+    if (hidden_file->f_op && hidden_file->f_op->open) {
+	err = hidden_file->f_op->open(hidden_inode, hidden_file);
+	if (err) {
+	    printk(KERN_WARNING "lofs_open: error(%d)\n", err);
+	    hidden_file->f_dentry = NULL;
+	    put_filp(hidden_file);
+	    goto out;
+	}
+    } else {
+	err = 0;
+    }
+
+    hidden_file->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
+    fist_checkinode(inode, "post lofs_open");
+    fist_dprint(6, "lofs_open: done (%d)\n", err);
+
+out:
+    fist_print_dentry(__FUNCTION__ " OUT hidden_dentry", hidden_dentry);
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+lofs_flush(file_t *file)
+{
+    int err = 0;		/* assume ok (see open.c:close_fp) */
+    file_t *hidden_file = ftohf(file);
+
+    print_entry_location();
+
+    if (!hidden_file->f_op || !hidden_file->f_op->flush)
+	goto out;
+
+    err = hidden_file->f_op->flush(hidden_file);
+
+out:
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+lofs_release(inode_t *inode, file_t *file)
+{
+    int err = 0;
+    file_t *hidden_file = ftohf(file);
+    dentry_t *hidden_dentry;
+
+    print_entry_location();
+    ASSERT(hidden_file != NULL);
+
+    fist_print_dentry("lofs_release IN hidden_dentry", hidden_file->f_dentry);
+    fist_checkinode(inode, "lofs_release");
+    fist_dprint(6, "lofs_release IN, file->f_count=%d\n", file->f_count);
+    /*
+     * will decrement file refcount, and if 0, destroy the file,
+     * which will call the lower file system's file release function.
+     */
+    hidden_dentry = hidden_file->f_dentry;
+    fput(hidden_file);
+
+    fist_dprint(6, "lofs_release done\n");
+    fist_checkinode(inode, "post lofs_release");
+
+    fist_print_dentry("lofs_release OUT hidden_dentry", hidden_dentry);
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+lofs_fsync(file_t *file, dentry_t *dentry)
+{
+    int err = -EINVAL;
+    file_t *hidden_file = ftohf(file);
+    dentry_t *hidden_dentry = lofs_hidden_dentry(dentry);
+
+    print_entry_location();
+
+    if (hidden_file->f_op && hidden_file->f_op->fsync) {
+	down(&hidden_dentry->d_inode->i_sem);
+	err = hidden_file->f_op->fsync(hidden_file, hidden_dentry);
+	up(&hidden_dentry->d_inode->i_sem);
+    }
+
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+lofs_fasync(int fd, file_t *file, int flag)
+{
+    int err = 0;
+    file_t *hidden_file = ftohf(file);
+
+    print_entry_location();
+
+    if (hidden_file->f_op && hidden_file->f_op->fasync)
+	err = hidden_file->f_op->fasync(fd, hidden_file, flag);
+
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+lofs_check_media_change(kdev_t dev)
+{
+    int err = 0;
+    super_block_t *sb;
+
+    print_entry_location();
+
+    sb = get_super(dev);
+    err = check_disk_change(stohs(sb)->s_dev);
+
+    print_exit_status(err);
+    return err;
+}
+
+
+#ifdef NOT_NEEDED
+/*
+ * This is not needed, because our check_media_change will call
+ * the generic check_disk_change() which will run the revalidate
+ * function of the hidden file system, if implemented.
+ */
+STATIC int
+lofs_file_revalidate(kdev_t dev)
+{
+    return -ENOSYS;
+}
+#endif /* NOT_NEEDED */
+
+
+STATIC int
+lofs_lock(file_t *file, int cmd, struct file_lock *fl)
+{
+    int err = 0;
+    file_t *hidden_file = ftohf(file);
+
+    print_entry_location();
+
+    if (hidden_file->f_op->lock) {
+	err = hidden_file->f_op->lock(hidden_file, F_GETLK, fl);
+    } else {
+	posix_test_lock(hidden_file, fl);
+    }
+
+    print_exit_status(err);
+    return err;
+}
+
+
+struct file_operations lofs_fops =
+{
+    lofs_llseek,		/* llseek */
+#if 1
+    generic_file_read,		/* read */
+#else
+    lofs_read,			/* read */
+#endif
+#if 0
+    generic_file_write,		/* write */
+#else
+    lofs_write,			/* write */
+#endif
+    lofs_readdir,		/* readdir */
+    lofs_poll,			/* poll */
+    lofs_ioctl,			/* ioctl */
+    lofs_mmap,			/* mmap */
+    lofs_open,			/* open */
+    lofs_flush,			/* flush */
+    lofs_release,		/* release */
+    lofs_fsync,			/* fsync */
+    lofs_fasync,		/* fasync */
+    lofs_check_media_change,	/* check_media_change */
+#ifdef NOT_NEEDED
+    lofs_file_revalidate,	/* revalidate */
+#else /* not NOT_NEEDED */
+    NULL,			/* revalidate */
+#endif /* not NOT_NEEDED */
+    lofs_lock			/* lock */
+};
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * End:
+ */
diff -ruN linux-2.3.45-vanilla/fs/lofs/fist.h linux-2.3.45-fist/fs/lofs/fist.h
--- linux-2.3.45-vanilla/fs/lofs/fist.h	Wed Dec 31 19:00:00 1969
+++ linux-2.3.45-fist/fs/lofs/fist.h	Mon Feb 14 01:15:48 2000
@@ -0,0 +1,204 @@
+/*
+ *  $Id: fist.h,v 1.3 1999/10/18 16:15:46 ezk Exp $
+ */
+
+#ifndef __FIST_H_
+#define __FIST_H_
+
+/*
+ * KERNEL ONLY CODE:
+ */
+#ifdef __KERNEL__
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/file.h>
+#include <linux/malloc.h>
+#include <linux/vmalloc.h>
+#include <linux/poll.h>
+#include <linux/version.h>
+
+#include <linux/dcache_func.h>
+#include <linux/swap.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/mman.h>
+
+
+/*
+ * MACROS:
+ */
+
+/* those mapped to ATTR_* were copied from linux/fs.h */
+#define FA_MODE		ATTR_MODE
+#define FA_UID		ATTR_UID
+#define FA_GID		ATTR_GID
+#define FA_SIZE		ATTR_SIZE
+#define FA_ATIME	ATTR_ATIME
+#define FA_MTIME	ATTR_MTIME
+#define FA_CTIME	ATTR_CTIME
+#define FA_ATIME_SET	ATTR_ATIME_SET
+#define FA_MTIME_SET	ATTR_MTIME_SET
+#define FA_FORCE	ATTR_FORCE
+#define FA_ATTR_FLAGS	ATTR_ATTR_FLAG
+
+/* must be greater than all other ATTR_* flags! */
+#define FA_NLINK	2048
+#define FA_BLKSIZE	4096
+#define FA_BLOCKS	8192
+#define FA_TIMES	(FA_ATIME|FA_MTIME|FA_CTIME)
+#define FA_ALL		0
+
+/* macros to manage changes between kernels */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,24)
+# define INODE_DATA(i)	(i)
+#else /* kernel 2.3.24 or later */
+# define INODE_DATA(i)	(&(i)->i_data)
+#endif /* kernel 2.3.24 or later */
+
+
+/*
+ * TYPEDEFS:
+ */
+typedef struct dentry dentry_t;
+typedef struct file file_t;
+typedef struct inode inode_t;
+typedef struct page page_t;
+typedef struct qstr qstr_t;
+typedef struct super_block super_block_t;
+typedef struct vm_area_struct vm_area_t;
+
+
+/*
+ * EXTERNALS:
+ */
+extern void fist_mod_dec_use_count(void);
+
+
+#ifdef FIST_DEBUG
+
+extern int fist_get_debug_value(void);
+extern int fist_set_debug_value(int val);
+extern void fist_dprint_internal(int level, char *str,...);
+extern void fist_print_dentry(char *str, const dentry_t *dentry);
+extern void fist_print_inode(char *str, const inode_t *inode);
+extern void fist_print_file(char *str, const file_t *file);
+extern void fist_print_buffer_flags(char *str, struct buffer_head *buffer);
+extern void fist_print_page_flags(char *str, page_t *page);
+extern void fist_print_page_bytes(char *str, page_t *page);
+extern void fist_print_pte_flags(char *str, const page_t *page);
+extern void fist_checkinode(inode_t *inode, char *msg);
+
+#define STATIC
+#define ASSERT(EX)	\
+do {	\
+    if (!(EX)) {	\
+	printk(KERN_CRIT "ASSERTION FAILED: %s at %s:%d (%s)\n", #EX,	\
+	       __FILE__, __LINE__, __FUNCTION__);	\
+	(*((char *)0))=0;	\
+    }	\
+} while (0)
+
+#define dprintk(format, args...) printk(KERN_DEBUG format, ##args)
+#define fist_dprint(level, str, args...) fist_dprint_internal(level, KERN_DEBUG str, ## args)
+#define print_entry_location() fist_dprint(4, "IN:  %s %s:%d\n", __FUNCTION__, __FILE__, __LINE__)
+#define print_exit_location() fist_dprint(4, "OUT: %s %s:%d\n", __FUNCTION__, __FILE__, __LINE__)
+#define print_exit_status(status) fist_dprint(4, "OUT: %s %s:%d, STATUS: %d\n", __FUNCTION__, __FILE__, __LINE__, status)
+
+#else /* not FIST_DEBUG */
+
+#define fist_get_debug_value()	-1
+#define fist_set_debug_value(a)	-1
+#define fist_dprint_internal(level, str, args...)
+#define fist_print_dentry(a, b)
+#define fist_print_inode(a, b)
+#define fist_print_file(a, b)
+#define fist_print_buffer_flags(a, b)
+#define fist_print_page_flags(a, b)
+#define fist_print_page_bytes(a, b)
+#define fist_print_pte_flags(a, b)
+#define fist_checkinode(a, b)
+
+#define STATIC	static
+#define ASSERT(EX)
+#define dprintk(format, args...)
+#define fist_dprint(level, format, args...)
+#define print_entry_location()
+#define print_exit_location()
+#define print_exit_status(status)
+
+#endif /* not FIST_DEBUG */
+
+
+static inline void
+fist_copy_attr_atime(inode_t *dest, const inode_t *src)
+{
+    ASSERT(dest != NULL);
+    ASSERT(src != NULL);
+    dest->i_atime = src->i_atime;
+}
+static inline void
+fist_copy_attr_times(inode_t *dest, const inode_t *src)
+{
+    ASSERT(dest != NULL);
+    ASSERT(src != NULL);
+    dest->i_atime = src->i_atime;
+    dest->i_mtime = src->i_mtime;
+    dest->i_ctime = src->i_ctime;
+}
+static inline void
+fist_copy_attr_timesizes(inode_t *dest, const inode_t *src)
+{
+    ASSERT(dest != NULL);
+    ASSERT(src != NULL);
+    dest->i_atime = src->i_atime;
+    dest->i_mtime = src->i_mtime;
+    dest->i_ctime = src->i_ctime;
+    dest->i_size = src->i_size;
+    dest->i_blocks = src->i_blocks;
+}
+static inline void
+fist_copy_attr_all(inode_t *dest, const inode_t *src)
+{
+    print_entry_location();
+    ASSERT(dest != NULL);
+    ASSERT(src != NULL);
+    dest->i_mode = src->i_mode;
+    dest->i_nlink = src->i_nlink;
+    dest->i_uid = src->i_uid;
+    dest->i_gid = src->i_gid;
+    //    dest->i_rdev = src->i_rdev;
+    dest->i_size = src->i_size;
+    dest->i_atime = src->i_atime;
+    dest->i_mtime = src->i_mtime;
+    dest->i_ctime = src->i_ctime;
+    dest->i_blksize = src->i_blksize;
+    dest->i_blocks = src->i_blocks;
+
+    dest->i_attr_flags = src->i_attr_flags;
+    print_exit_location();
+}
+
+#endif /* __KERNEL__ */
+
+
+/*
+ * DEFINITIONS FOR USER AND KERNEL CODE:
+ */
+#define FIST_IOCTL_GET_DEBUG_VALUE      _IOR(0x15, 1, int)
+#define FIST_IOCTL_SET_DEBUG_VALUE      _IOW(0x15, 2, int)
+
+#endif /* not __FIST_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * End:
+ */
diff -ruN linux-2.3.45-vanilla/fs/lofs/inode.c linux-2.3.45-fist/fs/lofs/inode.c
--- linux-2.3.45-vanilla/fs/lofs/inode.c	Wed Dec 31 19:00:00 1969
+++ linux-2.3.45-fist/fs/lofs/inode.c	Mon Feb 14 01:15:48 2000
@@ -0,0 +1,914 @@
+/*
+ *  $Id: inode.c,v 1.5 1999/10/26 04:43:25 ezk Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <fist.h>
+#include <lofs.h>
+
+
+STATIC int
+lofs_create(inode_t *dir, dentry_t *dentry, int mode)
+{
+    int err = -EACCES;
+    dentry_t *hidden_dentry = lofs_hidden_dentry(dentry);
+    dentry_t *hidden_dir_dentry;
+
+    print_entry_location();
+    ASSERT(hidden_dentry != NULL);
+
+    fist_checkinode(dir, "lofs_create");
+
+    hidden_dir_dentry = lock_parent(hidden_dentry);
+    err = PTR_ERR(hidden_dir_dentry);
+    if (IS_ERR(hidden_dir_dentry))
+	goto out;
+    if (!hidden_dir_dentry->d_inode->i_op || !hidden_dir_dentry->d_inode->i_op->create) {
+	unlock_dir(hidden_dir_dentry);
+	goto out;
+    }
+    err = hidden_dir_dentry->d_inode->i_op->create(hidden_dir_dentry->d_inode,
+						   hidden_dentry, mode);
+    unlock_dir(hidden_dir_dentry);
+
+    if (err)
+	goto out;
+
+    err = lofs_interpose(hidden_dentry, dentry, dir->i_sb, 0);
+    if (!err) {
+	fist_copy_attr_timesizes(dir, hidden_dir_dentry->d_inode);
+    }
+    fist_checkinode(dir, "post lofs_create");
+
+ out:
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC dentry_t *
+lofs_lookup(inode_t *dir, dentry_t *dentry)
+{
+    int err = 0;
+    dentry_t *hidden_dir_dentry = lofs_hidden_dentry(dentry->d_parent);
+    dentry_t *hidden_dentry;
+    const char *name = dentry->d_name.name;
+
+    print_entry_location();
+
+    fist_checkinode(dir, "lofs_lookup");
+
+    fist_print_dentry("LOOKUP: dentry IN", dentry);
+    fist_print_dentry("LOOKUP: dentry->d_parent IN", dentry->d_parent);
+    fist_print_dentry("LOOKUP: hidden_dir_dentry IN", hidden_dir_dentry);
+    fist_print_inode("LOOKUP: dir IN", dir);
+
+    if (hidden_dir_dentry->d_inode)
+	fist_print_inode("LOOKUP: hidden_dir_dentry->d_inode",
+			 hidden_dir_dentry->d_inode);
+
+    /* must initialize dentry operations */
+    dentry->d_op = &lofs_dops;
+
+    /* increase refcount of base dentry (lookup_dentry will decrement) */
+    // THIS IS RIGHT! (don't "fix" it)
+    dget(hidden_dir_dentry);
+
+    /* will allocate a new hidden dentry if needed */
+    hidden_dentry = lookup_dentry(name, hidden_dir_dentry, 0);
+    if (IS_ERR(hidden_dentry)) {
+	printk("ERR from hidden_dentry!!!\n");
+	err = PTR_ERR(hidden_dentry);
+	goto out;
+    }
+
+    /* update parent directory's atime */
+    fist_copy_attr_atime(dir, hidden_dir_dentry->d_inode);
+
+    /* lookup is special: it needs to handle negative dentries */
+    if (!hidden_dentry->d_inode) {
+	dtohd(dentry) = hidden_dentry;
+	d_add(dentry, NULL);
+	fist_print_dentry("lookup hidden", hidden_dentry);
+	fist_print_dentry("lookup dentry", dentry);
+	// ION: not needed b/c d_inode is already null here
+	//	dentry->d_inode = NULL;
+	// dput(hidden_dentry);
+	goto out;
+    }
+
+    fist_dprint(6, "lookup \"%s\" -> inode %d\n", name, hidden_dentry->d_inode->i_ino);
+    err = lofs_interpose(hidden_dentry, dentry, dir->i_sb, 1);
+#if 0
+    /*
+     * interpose always increments the hidden inode's refcount by one
+     * but in this function it was already done by the lookup_dentry
+     * above, which eventually calls iget() on hidden_inode, thus
+     * increasing the refcount once too many.
+     */
+    if (!err)
+	iput(hidden_dentry->d_inode);
+#endif
+
+    fist_checkinode(dentry->d_inode, "lofs_lookup OUT: dentry->d_inode:");
+    fist_checkinode(dir, "lofs_lookup OUT: dir:");
+
+out:
+    fist_print_dentry("LOOKUP: dentry->d_parent OUT", dentry->d_parent);
+    fist_print_dentry(__FUNCTION__ " OUT hidden_dentry", hidden_dentry);
+    fist_print_dentry(__FUNCTION__ " OUT hidden_dir_dentry", hidden_dir_dentry);
+    print_exit_status(err);
+    return ERR_PTR(err);
+}
+
+
+STATIC int
+lofs_link(dentry_t *old_dentry, inode_t *dir, dentry_t *new_dentry)
+{
+    int err = -EPERM;
+    dentry_t *hidden_old_dentry = lofs_hidden_dentry(old_dentry);
+    dentry_t *hidden_new_dentry = lofs_hidden_dentry(new_dentry);
+    dentry_t *hidden_dir_dentry;
+
+    print_entry_location();
+
+    fist_checkinode(dir, "lofs_link-dir");
+    fist_checkinode(old_dentry->d_inode, "lofs_link-oldinode");
+
+    hidden_dir_dentry = lock_parent(hidden_new_dentry);
+    err = PTR_ERR(hidden_dir_dentry);
+    if (IS_ERR(hidden_dir_dentry))
+	goto out;
+
+    err = -EXDEV;
+    if (hidden_dir_dentry->d_inode->i_dev != hidden_old_dentry->d_inode->i_dev)
+	goto out_lock;
+
+    err = -EPERM;
+    if (!hidden_dir_dentry->d_inode->i_op || !hidden_dir_dentry->d_inode->i_op->link)
+	goto out_lock;
+
+    // XXX: DQUOT_INIT(dir->d_inode); // do we need that
+    err = hidden_dir_dentry->d_inode->i_op->link(hidden_old_dentry,
+						 hidden_dir_dentry->d_inode,
+						 hidden_new_dentry);
+
+    if (err)
+	goto out_lock;
+    err = lofs_interpose(hidden_new_dentry, new_dentry, dir->i_sb, 0);
+    /*
+     * XXX: we have to do a lofs_copy_inode (or subset thereof) in
+     * *ALL* functions, all file systems, and all OSs!!!
+     */
+    if (!err)
+	fist_copy_attr_timesizes(dir, hidden_new_dentry->d_inode);
+
+ out_lock:
+    unlock_dir(hidden_dir_dentry);
+ out:
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+lofs_unlink(inode_t *dir, dentry_t *dentry)
+{
+    int err = -EPERM;
+    inode_t *hidden_dir = itohi(dir);
+    dentry_t *hidden_dentry = lofs_hidden_dentry(dentry);
+
+    print_entry_location();
+    ASSERT(hidden_dentry != NULL);
+
+    fist_checkinode(dir, "lofs_unlink-dir");
+    if (!hidden_dir->i_op || !hidden_dir->i_op->unlink)
+	goto out;
+
+    err = hidden_dir->i_op->unlink(hidden_dir, hidden_dentry);
+    if (!err)
+        fist_copy_attr_times(dir, hidden_dir);
+    fist_checkinode(dir, "post lofs_unlink-dir");
+
+ out:
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+lofs_symlink(inode_t *dir, dentry_t *dentry, const char *symname)
+{
+    int err = -EPERM;
+    dentry_t *hidden_dentry = lofs_hidden_dentry(dentry);
+    const char *hidden_symname = symname; /* XXX: can modify this */
+    dentry_t *hidden_dir_dentry;
+
+    print_entry_location();
+
+    hidden_dir_dentry = lock_parent(hidden_dentry);
+    err = PTR_ERR(hidden_dir_dentry);
+    if (IS_ERR(hidden_dir_dentry))
+	goto out;
+
+    fist_checkinode(dir, "lofs_symlink-dir");
+    if (!hidden_dir_dentry->d_inode->i_op ||
+	!hidden_dir_dentry->d_inode->i_op->symlink) {
+	err = -EPERM;
+	goto out_lock;
+    }
+
+    err = hidden_dir_dentry->d_inode->i_op->symlink(hidden_dir_dentry->d_inode,
+						    hidden_dentry,
+						    hidden_symname);
+    if (err)
+	goto out_lock;
+    err = lofs_interpose(hidden_dentry, dentry, dir->i_sb, 0);
+    if (!err)
+        fist_copy_attr_timesizes(dir, hidden_dir_dentry->d_inode);
+
+    fist_checkinode(dir, "post lofs_symlink-dir");
+
+ out_lock:
+    unlock_dir(hidden_dir_dentry);
+ out:
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+lofs_mkdir(inode_t *dir, dentry_t *dentry, int mode)
+{
+    int err = -EPERM;
+    dentry_t *hidden_dentry = lofs_hidden_dentry(dentry);
+    dentry_t *hidden_dir_dentry;
+
+    print_entry_location();
+
+    hidden_dir_dentry = lock_parent(hidden_dentry);
+    err = PTR_ERR(hidden_dir_dentry);
+    if (IS_ERR(hidden_dir_dentry))
+	goto out;
+
+    fist_checkinode(dir, "lofs_mkdir-dir");
+    if (!hidden_dir_dentry->d_inode->i_op ||
+	!hidden_dir_dentry->d_inode->i_op->mkdir) {
+	err = -EPERM;
+	goto out_lock;
+    }
+
+    err = hidden_dir_dentry->d_inode->i_op->mkdir(hidden_dir_dentry->d_inode,
+						  hidden_dentry,
+						  mode);
+    if (err)
+	goto out_lock;
+    err = lofs_interpose(hidden_dentry, dentry, dir->i_sb, 0);
+    if (!err)
+        fist_copy_attr_timesizes(dir, hidden_dir_dentry->d_inode);
+
+    fist_checkinode(dir, "post lofs_mkdir-dir");
+
+ out_lock:
+    unlock_dir(hidden_dir_dentry);
+ out:
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+lofs_rmdir(inode_t *dir, dentry_t *dentry)
+{
+    int err = -EPERM;
+    dentry_t *hidden_dentry = lofs_hidden_dentry(dentry);
+    dentry_t *hidden_dir_dentry;
+
+    print_entry_location();
+
+    hidden_dir_dentry = dget(hidden_dentry->d_parent);
+    err = PTR_ERR(hidden_dir_dentry);
+    if (IS_ERR(hidden_dir_dentry))
+	goto out;
+
+    if (!hidden_dentry->d_inode) {
+	err = -ENOENT;
+	goto out_dput;
+    }
+
+    fist_checkinode(dir, "lofs_rmdir-dir");
+    if (!hidden_dir_dentry->d_inode->i_op ||
+	!hidden_dir_dentry->d_inode->i_op->rmdir) {
+	err = -EPERM;
+	goto out_dput;
+    }
+
+    /*
+     * The dentry->d_count stuff confuses d_delete() enough to
+     * not kill the inode from under us while it is locked. This
+     * wouldn't be needed, except the dentry semaphore is really
+     * in the inode, not in the dentry..
+     */
+    hidden_dentry->d_count++;
+    double_lock(hidden_dir_dentry, hidden_dentry);
+
+    err = vfs_rmdir(hidden_dir_dentry->d_inode, hidden_dentry);
+    if (!err)
+        fist_copy_attr_times(dir, hidden_dir_dentry->d_inode);
+
+    double_unlock(hidden_dentry, hidden_dir_dentry);
+
+ out_dput:
+    /* don't dput here, it will be done later in d_delete */
+    //    dput(hidden_dentry);
+ out:
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+lofs_mknod(inode_t *dir, dentry_t *dentry, int mode, int dev)
+{
+    int err = -EPERM;
+    dentry_t *hidden_dentry = lofs_hidden_dentry(dentry);
+    dentry_t *hidden_dir_dentry;
+
+    print_entry_location();
+
+    hidden_dir_dentry = lock_parent(hidden_dentry);
+    err = PTR_ERR(hidden_dir_dentry);
+    if (IS_ERR(hidden_dir_dentry))
+	goto out;
+
+    fist_checkinode(dir, "lofs_mknod-dir");
+    if (!hidden_dir_dentry->d_inode->i_op || !hidden_dir_dentry->d_inode->i_op->mknod) {
+	err = -EPERM;
+	goto out_lock;
+    }
+
+    err = hidden_dir_dentry->d_inode->i_op->mknod(hidden_dir_dentry->d_inode,
+						  hidden_dentry,
+						  mode,
+						  dev);
+    if (err)
+	goto out_lock;
+    err = lofs_interpose(hidden_dentry, dentry, dir->i_sb, 0);
+    fist_checkinode(dir, "post lofs_mknod-dir");
+    if (!err)
+        fist_copy_attr_timesizes(dir, hidden_dir_dentry->d_inode);
+
+ out_lock:
+    unlock_dir(hidden_dir_dentry);
+ out:
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+lofs_rename(inode_t *old_dir, dentry_t *old_dentry,
+	      inode_t *new_dir, dentry_t *new_dentry)
+{
+    int err = -EPERM;
+    dentry_t *hidden_old_dentry = lofs_hidden_dentry(old_dentry);
+    dentry_t *hidden_new_dentry = lofs_hidden_dentry(new_dentry);
+    dentry_t *hidden_old_dir_dentry;
+    dentry_t *hidden_new_dir_dentry;
+
+    print_entry_location();
+
+    hidden_old_dir_dentry = get_parent(hidden_old_dentry);
+    hidden_new_dir_dentry = get_parent(hidden_new_dentry);
+    err = PTR_ERR(hidden_old_dir_dentry);
+    if (IS_ERR(hidden_old_dir_dentry))
+	goto out;
+    err = PTR_ERR(hidden_new_dir_dentry);
+    if (IS_ERR(hidden_new_dir_dentry))
+	goto out;
+    double_lock(hidden_old_dir_dentry, hidden_new_dir_dentry);
+
+    fist_checkinode(old_dir, "lofs_rename-old_dir");
+    fist_checkinode(new_dir, "lofs_rename-new_dir");
+
+    err = -ENOENT;
+    if (check_parent(hidden_old_dir_dentry, hidden_old_dentry) &&
+	check_parent(hidden_new_dir_dentry, hidden_new_dentry))
+	err = vfs_rename(hidden_old_dir_dentry->d_inode, hidden_old_dentry,
+			 hidden_new_dir_dentry->d_inode, hidden_new_dentry);
+    if (err)
+	goto out_lock;
+
+    if (!err) {
+        fist_copy_attr_all(new_dir, hidden_new_dir_dentry->d_inode);
+	if (new_dir != old_dir)
+	    fist_copy_attr_times(old_dir, hidden_old_dir_dentry->d_inode);
+    }
+
+    fist_checkinode(new_dir, "post lofs_mknod-new_dir");
+
+ out_lock:
+    double_unlock(hidden_old_dir_dentry, hidden_new_dir_dentry);
+    // new do_rename has dput() after double_unlock.  Is it needed?
+ out:
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+lofs_readlink(dentry_t *dentry, char *buf, int bufsiz)
+{
+    int err;
+    dentry_t *hidden_dentry = lofs_hidden_dentry(dentry);
+
+    print_entry_location();
+    fist_print_dentry("lofs_readlink IN", dentry);
+
+    if (!hidden_dentry->d_inode->i_op || !hidden_dentry->d_inode->i_op->readlink) {
+	err = -EPERM;
+	goto out;
+    }
+
+    err = hidden_dentry->d_inode->i_op->readlink(hidden_dentry,
+						 buf,
+						 bufsiz);
+    if (err > 0)
+	fist_copy_attr_atime(dentry->d_inode, hidden_dentry->d_inode);
+
+ out:
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC dentry_t *
+lofs_follow_link(dentry_t *dentry, dentry_t *base, unsigned int follow)
+{
+    char *buf;
+    int len = PAGE_SIZE, err;
+    dentry_t *ret_dentry = dentry;
+    dentry_t *hidden_dentry = lofs_hidden_dentry(dentry);
+    mm_segment_t old_fs;
+    //    dentry_t *hidden_base = lofs_hidden_dentry(base);
+
+    print_entry_location();
+    fist_print_dentry("lofs_follow_link dentry IN", dentry);
+    fist_print_dentry("lofs_follow_link base IN", base);
+
+    if (!hidden_dentry->d_inode->i_op ||
+	!hidden_dentry->d_inode->i_op->readlink ||
+	!hidden_dentry->d_inode->i_op->follow_link) {
+	/*
+	 * do_follow_link will do a dput() on the dentry passed to us
+	 * and if that is the same as ret_dentry, and we want it kept,
+	 * then we must increment the dentry's refcount to avoid having
+	 * it deallocate/destroyed.
+	 */
+	dget(ret_dentry);
+	/*
+	 * Looking at do_follow_link, if we implement follow_link, then
+	 * we have to dput(base).  Normally that's done by lookup_dentry
+	 * but here we don't run that code.
+	 */
+	dput(base);
+	goto out;
+    }
+
+    buf = kmalloc(len, GFP_KERNEL);
+    if (!buf) {
+	ret_dentry = ERR_PTR(-ENOMEM);
+	dput(base); // for the same reason as above
+	goto out;
+    }
+    /* read the hidden symlink, and then we will follow it */
+    old_fs = get_fs();
+    set_fs(KERNEL_DS);
+    err = hidden_dentry->d_inode->i_op->readlink(hidden_dentry, buf, len);
+    set_fs(old_fs);
+    if (err < 0) {
+	ret_dentry = ERR_PTR(err);
+	dput(base); // for the same reason as above
+	goto out_free;
+    }
+    fist_copy_attr_atime(dentry->d_inode, hidden_dentry->d_inode);
+    buf[err] = 0;	// terminate the buffer for lookup_dentry's sake
+
+#if 0
+    /* run dget b/c lookup_dentry expects the caller to do so */
+    dget(base);
+#endif
+    /*
+     * We do not dget(base) b/c the way we were called here,
+     * the base (passed to us) was already dget'ed.
+     */
+    ret_dentry = lookup_dentry(buf, base, follow);
+    fist_print_dentry("lofs_follow_link base OUT", base);
+
+ out_free:
+    kfree_s(buf, len);
+ out:
+    print_exit_location();
+    return ret_dentry;
+}
+
+
+STATIC int
+lofs_get_block(inode_t *inode, long iblock, struct buffer_head *bh_result, int create)
+{
+    int err = -EINVAL; // XXX/ION/EZK: NOT IMPLEMENTED YET
+    inode_t *hidden_inode = itohi(inode);
+
+    panic("YOU SHOULD IMPLEMENT GET_BLOCK FIRST\n");
+
+    if (hidden_inode->i_op && hidden_inode->i_op->get_block)
+	err = hidden_inode->i_op->get_block(hidden_inode,
+					    iblock, bh_result, create);
+
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+lofs_readpage(file_t *file, page_t *page)
+{
+    int err = 0;
+    inode_t *inode = file->f_dentry->d_inode;
+    inode_t *hidden_inode = itohi(inode);
+    file_t *hidden_file = ftohf(file);
+    page_t *hidden_page, **hash;
+    unsigned long page_cache = 0, page_data, hidden_page_data;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,16)
+    int free;
+#endif /* kernel before 2.3.16 */
+
+    print_entry_location();
+
+    ASSERT(hidden_inode->i_op != NULL);
+    fist_dprint(6, "READPAGE: f_pos:0x%x inode:0x%x i_size:0x%x hidden_inode:0x%x page->index:%lu\n",
+		(int) file->f_pos, (int) inode, inode->i_size, (int) hidden_inode,
+		page->index);
+
+    /* create new hidden_page (look for it in cache?) */
+    hash = page_hash(INODE_DATA(hidden_inode), page->index);
+
+restart_page:
+    // must lock hidden pages in 2.3
+    hidden_page = find_lock_page(INODE_DATA(hidden_inode), page->index);
+    if (!hidden_page) {
+	if (!page_cache) {		/* allocate new page */
+	    page_cache = __get_free_page(GFP_KERNEL);
+	    if (!page_cache) {
+		err = -ENOMEM;
+		goto out;		/* no need to free anything */
+	    }
+	    goto restart_page;
+	}
+	hidden_page = mem_map + MAP_NR(page_cache);
+	page_cache = 0;
+	add_to_page_cache(hidden_page, INODE_DATA(hidden_inode), page->index);
+    }
+
+    /* now we have a valid hidden page */
+
+    fist_dprint(6, "READPAGE: hidden_page %x\n", (int) hidden_page);
+
+    /*
+     * XXX: some times the hidden_page shows up as up-to-date (?!)
+     * If we call readpage on lower f/s, we get a BUG() in fs/buffer.c,
+     * line 1669 in block_read_full_page().  So we turn off the bit.
+     * We should do something better and more optimized in here, such as
+     * skip calling the lower f/s if page is up-to-date.
+     * -Erez
+     */
+    ClearPageUptodate(hidden_page);
+    ASSERT(!Page_Uptodate(hidden_page));
+
+    if (hidden_inode->i_op->readpage) {
+	/*
+	 * create new hidden_page (look for it in cache?)
+	 * call lower level readpage on hidden_inode and hidden_page
+	 */
+	// ION, I add this. needed?
+	file->f_pos = hidden_file->f_pos = (page->index << PAGE_CACHE_SHIFT);
+
+#if 0
+	hidden_file->f_reada = file->f_reada;
+	hidden_file->f_ramax = file->f_ramax;
+	hidden_file->f_raend = file->f_raend;
+	hidden_file->f_ralen = file->f_ralen;
+	hidden_file->f_rawin = file->f_rawin;
+#endif
+
+	err = hidden_inode->i_op->readpage(hidden_file, hidden_page);
+	if (err) {
+	    printk("READPAGE: error reading hidden page: %d\n", err);
+	    goto out_free;
+	}
+
+	/* XXX: may need to wait_on_page(hidden_page) */
+	wait_on_page(hidden_page);
+	if (!Page_Uptodate(hidden_page) || PageError(hidden_page)) {
+	    printk("hidden page not up-to-date or error!\n");
+	    err = -EIO;
+	    goto out_free;
+	}
+    }
+    /* XXX: what if the lower f/s doesn't implement readpage? */
+
+    /*
+     * decode hidden_page data onto page
+     */
+    page_data = page_address(page);
+    hidden_page_data = page_address(hidden_page);
+
+    //    lofs_decode_block((char *) hidden_page_data, (char *) page_data, PAGE_SIZE);
+    memcpy((char *) page_data, (char *) hidden_page_data, PAGE_SIZE);
+
+    /*
+     * adjust flags and wake up processes waiting on the page
+     * code shamelessly stolen from generic_readpage
+     */
+    // ION, why? we never set the locked bit?
+    clear_bit(PG_locked, &page->flags);
+    set_bit(PG_uptodate, &page->flags);
+    wake_up(&page->wait);
+
+    // ION/EZK: this code taken from 2.3.9-pre7's fs/buffer.c's
+    // end_buffer_io_async() function
+    /*
+     * Note - we need to test the flags before we unlock the page, but
+     * we must not actually free the page until after the unlock!
+     */
+    if (test_and_clear_bit(PG_decr_after, &page->flags))
+	atomic_dec(&nr_async_pages);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,16)
+    if (test_and_clear_bit(PG_free_swap_after, &page->flags))
+	swap_free(page->offset);
+    free = test_and_clear_bit(PG_free_after, &page->flags);
+#endif /* kernel before 2.3.16 */
+
+#if 0
+# error THIS CODE IS NEEDED!!!?
+    // is this needed?
+    if (page->owner != -1)
+	PAGE_BUG(page);
+    page->owner = (int)current;
+    UnlockPage(page);
+
+    if (free)
+	__free_page(page);
+#else
+# if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,16)
+    ASSERT(free == 0);		/* XXX: tell me something for now */
+# endif /* kernel before 2.3.16 */
+#endif
+
+out_free:
+    if (page_cache) {
+	printk("READPAGE: page cache is not null: 0x%x\n", (int) page_cache);
+	free_page(page_cache);
+    }
+#if 0
+    if (hidden_page->owner != -1)
+	PAGE_BUG(hidden_page);
+    hidden_page->owner = (int)current;
+    UnlockPage(hidden_page);	/* EZK/ION:XXX: is this right? */
+#endif
+    // ION, do we need to __free_page? isn't is sub-optimal
+    __free_page(hidden_page);	/* release_page() in mm/filemap.c */
+out:
+    fist_print_page_flags("READPAGE flags (exit)", page);
+    print_exit_status(err);
+    return err;
+}
+
+
+/* XXX: the VFS never calls writepage */
+STATIC int
+lofs_writepage(file_t *file, page_t *page)
+{
+    int err = -EIO;
+    file_t *hidden_file = ftohf(file);
+    inode_t *hidden_inode = hidden_file->f_dentry->d_inode;
+
+    panic("ARE YOU SURE YOU WANT TO DEFINE WRITEPAGE???\n");
+    print_entry_location();
+
+    if (hidden_inode->i_op && hidden_inode->i_op->writepage) {
+	fist_dprint(2, "non-generic writepage\n");
+	err = hidden_inode->i_op->writepage(hidden_file, page);
+    }
+
+    print_exit_status(err);
+    return err;
+}
+
+
+// new in 2.3
+// ION/EZK: is this code correct?
+STATIC int
+lofs_flushpage(inode_t *inode, page_t *page, unsigned long offset)
+{
+    int err = -EINVAL;
+    inode_t *hidden_inode = itohi(inode);
+    page_t *hidden_page = NULL;
+    unsigned long pgoff;
+
+    print_entry_location();
+
+    ASSERT(page);
+#if 0
+    if ((page->index << PAGE_CACHE_SHIFT) != offset) {
+	printk(KERN_WARNING "FLUSHPAGE: mismatching index/offsets 0x%x 0x%x\n",
+	       (int)page->index, (int)offset);
+	err = -EIO;
+	goto out;
+    }
+#endif
+
+    if (!hidden_inode->i_op || !hidden_inode->i_op->flushpage)
+	goto out;
+
+    hidden_page = find_lock_page(INODE_DATA(hidden_inode), page->index);
+    if (!hidden_page) {
+	printk(KERN_ERR "flushpage: cannot find hidden_page at index 0x%x\n",
+	       (int) page->index);
+	err = -EIO;
+	goto out;
+    }
+
+    if (!PageLocked(hidden_page))
+	PAGE_BUG(hidden_page);
+
+    pgoff = page->index << PAGE_CACHE_SHIFT;
+    err = hidden_inode->i_op->flushpage(hidden_inode, hidden_page, pgoff);
+    if (!PageLocked(hidden_page))
+	PAGE_BUG(hidden_page);
+
+#if 0
+    SetPageUptodate(hidden_page);
+#else
+    ClearPageUptodate(hidden_page);
+    lru_cache_del(hidden_page);
+    remove_inode_page(hidden_page);
+    lru_cache_del(page);
+#endif
+    // no need to UnlockPage(page) b/c VFS un/locks it
+    UnlockPage(hidden_page);
+    page_cache_release(hidden_page);
+    page_cache_release(hidden_page);
+
+out:
+    print_exit_status(err);
+    return err;
+}
+
+
+/* kludgey - replace when up/down problem fixed */
+STATIC void
+lofs_truncate(inode_t *inode)
+{
+    inode_t *hidden_inode = itohi(inode);
+
+    print_entry_location();
+
+    fist_checkinode(inode, "lofs_truncate");
+    if (!hidden_inode->i_op || !hidden_inode->i_op->truncate)
+	goto out;
+
+    down(&hidden_inode->i_sem);
+    hidden_inode->i_op->truncate(hidden_inode);
+    up(&hidden_inode->i_sem);
+
+    fist_copy_attr_timesizes(inode, hidden_inode);
+
+ out:
+    print_exit_location();
+}
+
+
+STATIC int
+lofs_permission(inode_t *inode, int mask)
+{
+    inode_t *hidden_inode = itohi(inode);
+    int mode = hidden_inode->i_mode;
+    int err;
+
+    print_entry_location();
+    //    err = -ENOSPC;goto out;
+    if (hidden_inode->i_op && hidden_inode->i_op->permission) {
+	err = hidden_inode->i_op->permission(hidden_inode, mask);
+	goto out;
+    }
+
+    if ((mask & S_IWOTH) && IS_RDONLY(hidden_inode) &&
+	(S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) {
+	err = -EROFS; /* Nobody gets write access to a read-only fs */
+	goto out;
+    }
+    if ((mask & S_IWOTH) && IS_IMMUTABLE(hidden_inode)) {
+	err = -EACCES; /* Nobody gets write access to an immutable file */
+	goto out;
+    }
+    if (current->fsuid == hidden_inode->i_uid)
+	mode >>= 6;
+    else if (in_group_p(hidden_inode->i_gid))
+	mode >>= 3;
+
+    if (((mode & mask & S_IRWXO) == mask) || capable(CAP_DAC_OVERRIDE)) {
+	err = 0;
+	goto out;
+    }
+
+    /* read and search access */
+    if ((mask == S_IROTH) ||
+	(S_ISDIR(mode)  && !(mask & ~(S_IROTH | S_IXOTH))))
+	if (capable(CAP_DAC_READ_SEARCH)) {
+	    err = 0;
+	    goto out;
+	}
+    err = -EACCES;
+
+ out:
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+lofs_smap(inode_t *inode, int block)
+{
+    int err = -EINVAL;
+    inode_t *hidden_inode = itohi(inode);
+
+    print_entry_location();
+
+    if (hidden_inode->i_op && hidden_inode->i_op->smap)
+	err = hidden_inode->i_op->smap(hidden_inode, block);
+    else
+	err = bmap(hidden_inode, block);
+
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+lofs_inode_revalidate(dentry_t *dentry)
+{
+    int err = 0;
+    dentry_t *hidden_dentry = lofs_hidden_dentry(dentry);
+    inode_t *hidden_inode = hidden_dentry->d_inode;
+
+    print_entry_location();
+    fist_print_dentry("inode_revalidate IN", dentry);
+
+    if (hidden_inode->i_op && hidden_inode->i_op->revalidate) {
+	err = hidden_inode->i_op->revalidate(hidden_dentry);
+	if (!err)
+	    fist_copy_attr_all(dentry->d_inode, hidden_inode);
+    }
+
+    fist_print_dentry("inode_revalidate OUT", dentry);
+    print_exit_status(err);
+    return err;
+}
+
+
+struct inode_operations lofs_iops =
+{
+    &lofs_fops,			/* default file operations */
+    lofs_create,		/* create */
+    lofs_lookup,		/* lookup */
+    lofs_link,			/* link */
+    lofs_unlink,		/* unlink */
+    lofs_symlink,		/* symlink */
+    lofs_mkdir,			/* mkdir */
+    lofs_rmdir,			/* rmdir */
+    lofs_mknod,			/* mknod */
+    lofs_rename,		/* rename */
+    lofs_readlink,		/* readlink */
+#if 1
+    lofs_follow_link,		/* follow_link */
+#else
+    NULL,			/* follow link */
+#endif
+    lofs_get_block,		/* get_block */
+    lofs_readpage,		/* readpage */
+    lofs_writepage,		/* writepage */
+    lofs_flushpage,		/* flushpage */
+    lofs_truncate,		/* truncate */
+    lofs_permission,		/* permission */
+    lofs_smap,			/* smap */
+    lofs_inode_revalidate	/* revalidate */
+};
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * End:
+ */
diff -ruN linux-2.3.45-vanilla/fs/lofs/lofs.h linux-2.3.45-fist/fs/lofs/lofs.h
--- linux-2.3.45-vanilla/fs/lofs/lofs.h	Wed Dec 31 19:00:00 1969
+++ linux-2.3.45-fist/fs/lofs/lofs.h	Mon Feb 14 01:15:48 2000
@@ -0,0 +1,73 @@
+/*
+ *  $Id: lofs.h,v 1.2 1999/10/18 16:15:46 ezk Exp $
+ */
+
+#ifndef __FIST_LOFS_H_
+#define __FIST_LOFS_H_
+
+/*
+ * STRUCTURES:
+ */
+
+/* lofs inode data in memory */
+struct lofs_inode_info {
+    inode_t *inode;
+};
+
+/* lofs super-block data in memory */
+struct lofs_sb_info {
+    super_block_t *s_sb;
+#if 0
+    /* not needed b/c linux has an s_root field in struct super_block */
+    dentry_t *s_root;
+#endif
+    // inode_t *s_rootinode;
+};
+
+/*
+ * MACROS:
+ */
+// File TO Hidden File
+#define ftohf(file) ((file_t *)((file)->private_data))
+// Inode TO Private Data
+#define itopd(ino) ((struct lofs_inode_info *)(ino)->u.generic_ip)
+// Inode TO Hidden Inode
+#define itohi(ino) (itopd(ino)->inode)
+// Superblock TO Private Data
+#define stopd(super) ((struct lofs_sb_info *)(super)->u.generic_sbp)
+// Superblock TO Hidden Superblock
+#define stohs(super) (stopd(super)->s_sb)
+// Dentry TO Hidden Dentry (and private data --- for the future)
+#define dtopd(dentry) ((dentry)->d_fsdata)
+#define dtohd(dentry) ((dentry_t *)dtopd(dentry))
+/* VMArea TO Hidden VMArea */
+#define vmatohvma(vma) ((vm_area_t *)((vma)->vm_private_data))
+
+#define sbt(sb) ((sb)->s_type->name)
+
+/*
+ * EXTERNALS:
+ */
+extern struct file_operations lofs_fops;
+extern struct inode_operations lofs_iops;
+extern struct super_operations lofs_sops;
+extern struct dentry_operations lofs_dops;
+extern struct vm_operations_struct lofs_shared_vmops;
+extern struct vm_operations_struct lofs_private_vmops;
+
+extern int lofs_interpose(dentry_t *hidden_dentry, dentry_t *this_dentry, super_block_t *sb, int flag);
+
+#ifdef FIST_DEBUG
+# define lofs_hidden_dentry(d) __lofs_hidden_dentry(__FILE__,__FUNCTION__,__LINE__,(d))
+extern dentry_t *__lofs_hidden_dentry(char *file, char *func, int line, dentry_t *this_dentry);
+#else /* not FIST_DEBUG */
+# define lofs_hidden_dentry(dentry) dtohd(dentry)
+#endif /* not FIST_DEBUG */
+
+#endif	/* __FIST_LOFS_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * End:
+ */
diff -ruN linux-2.3.45-vanilla/fs/lofs/main.c linux-2.3.45-fist/fs/lofs/main.c
--- linux-2.3.45-vanilla/fs/lofs/main.c	Wed Dec 31 19:00:00 1969
+++ linux-2.3.45-fist/fs/lofs/main.c	Mon Feb 14 01:15:48 2000
@@ -0,0 +1,244 @@
+/*
+ *  $Id: main.c,v 1.3 1999/09/15 05:30:53 ezk Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <linux/module.h>	/* include only once in a logical module */
+#include <linux/init.h>
+#include <fist.h>
+#include <lofs.h>
+
+
+
+// lofs_interpose(inode_t *hidden_inode, inode_t **result, super_block_t *super)
+int
+lofs_interpose(dentry_t *hidden_dentry, dentry_t *this_dentry, super_block_t *sb, int flag)
+{
+    inode_t *hidden_inode = hidden_dentry->d_inode;
+    int err = 0;
+    inode_t *new_inode;
+
+    print_entry_location();
+
+    ASSERT(hidden_inode != NULL);
+    ASSERT(this_dentry->d_inode == NULL);
+
+    /* check that the lower file system didn't cross a mount point */
+    if (hidden_inode->i_dev != stohs(sb)->s_dev) {
+	dput(hidden_dentry);
+	err = -EXDEV;
+	goto out;
+    }
+
+    /*
+     * iget will eventually call our read_inode, and that will create
+     * the link between our inode and hidden_inode.
+     *
+     * XXX: fix assumption that hidden and this inode numbers are same
+     */
+    new_inode = iget(sb, hidden_inode->i_ino);
+    if (!new_inode) {
+	dput(hidden_dentry);
+	err = -EACCES;		/* should be impossible??? */
+	goto out;
+    }
+
+    /* only (our) lookup wants to do a d_add */
+    if (flag)
+	d_add(this_dentry, new_inode);
+    else
+	d_instantiate(this_dentry, new_inode);
+
+    if (dtopd(this_dentry) == NULL) {
+	dtohd(this_dentry) = hidden_dentry;
+    } else {
+	ASSERT(dtohd(this_dentry) == hidden_dentry);
+    }
+
+    /*
+     * We allocated our new inode above, as a side effect of calling iget
+     * which is done in a VFS generic code.   Linking our inode to the
+     * hidden one is done when iget calls the generic read_inode,
+     * which calls our read_inode.
+     */
+
+    // all well, update inode modes */
+    fist_copy_attr_all(new_inode, hidden_inode);
+
+out:
+    print_exit_status(err);
+    return err;
+}
+
+
+#ifdef FIST_DEBUG
+/* find hidden dentry given this lofs dentry */
+dentry_t *
+__lofs_hidden_dentry(char *file, char *func, int line, dentry_t *this_dentry)
+{
+    dentry_t *hidden_dentry;
+
+    ASSERT(this_dentry != NULL);
+    ASSERT(this_dentry->d_op != NULL);
+    ASSERT(this_dentry->d_op == &lofs_dops);
+    ASSERT(this_dentry->d_sb->s_op == &lofs_sops);
+    if (this_dentry->d_inode)
+	ASSERT(this_dentry->d_inode->i_op == &lofs_iops);
+
+    hidden_dentry = dtohd(this_dentry);
+    ASSERT(hidden_dentry != NULL);
+    return hidden_dentry;
+}
+#endif /* FIST_DEBUG */
+
+
+STATIC dentry_t *
+lofs_parse_options(super_block_t *sb, char *options)
+{
+    dentry_t *hidden_root = ERR_PTR(-EINVAL);
+    print_entry_location();
+
+    while (*options) {
+	if (!strncmp("dir=", options, 4)) {
+	    fist_dprint(4, "lofs: using directory: %s\n", options + 4);
+	    hidden_root = lookup_dentry(options+4, NULL, LOOKUP_FOLLOW);
+	    fist_dprint(6, "parse_options: new s_root, inode: 0x%x, 0x%x\n",
+			(int) hidden_root, (int) hidden_root->d_inode);
+	} else {
+	    printk(KERN_WARNING "lofs: unrecognized options '%s'\n", options);
+	}
+	while (*options && *options != ',')
+	    options++;
+    }
+    print_exit_location();
+    return hidden_root;
+}
+
+
+super_block_t *
+lofs_read_super(super_block_t *sb, void *raw_data, int silent)
+{
+    super_block_t *ret_sb = NULL;
+    dentry_t *hidden_root;
+
+    print_entry_location();
+
+    MOD_INC_USE_COUNT;
+    lock_super(sb);
+    if (!raw_data) {
+	printk(KERN_WARNING "lofs_read_super: missing data argument\n");
+	goto out_module;
+    }
+    /*
+     * Allocate superblock private data
+     */
+    stopd(sb) = kmalloc(sizeof(struct lofs_sb_info), GFP_KERNEL);
+    stohs(sb) = NULL;
+
+    hidden_root = lofs_parse_options(sb, raw_data);
+    if (IS_ERR(hidden_root)) {
+	printk(KERN_WARNING "lofs_read_super: lookup_dentry failed (err = %ld)\n", PTR_ERR(hidden_root));
+	goto out_free;
+    }
+    if (!hidden_root->d_inode) {
+	printk(KERN_WARNING "lofs_read_super: no directory to interpose on\n");
+	goto out_free;
+    }
+    stohs(sb) = hidden_root->d_sb;
+
+    sb->s_op = &lofs_sops;
+    /*
+     * we can't use d_alloc_root if we want to use
+     * our own interpose function unchanged,
+     * so we simply replicate *most* of the code in d_alloc_root here
+     */
+    sb->s_root = d_alloc(NULL, &(const struct qstr) { "/", 1, 0 });
+    if (IS_ERR(sb->s_root)) {
+	printk(KERN_WARNING "lofs_read_super: iget failed\n");
+	goto out_dput;
+    }
+
+    sb->s_root->d_op = &lofs_dops;
+    sb->s_root->d_sb = sb;
+    sb->s_root->d_parent = sb->s_root;
+    if (lofs_interpose(hidden_root, sb->s_root, sb, 0))
+	goto out_dput2;
+#if 0
+    /*
+     * interpose always increments the hidden inode's refcount by one
+     * but in this function it was already done by the lookup_dentry
+     * above (called from parse_options), which eventually calls iget() on
+     * hidden_inode, thus increasing the refcount once too many.
+     */
+    iput(hidden_root->d_inode);
+#endif
+
+    ret_sb = sb;
+    fist_print_dentry(__FUNCTION__ "OUT hidden_dentry", hidden_root);
+    goto out;
+
+ out_dput2:
+    dput(sb->s_root);
+ out_dput:
+    dput(hidden_root);
+ out_free:
+    kfree_s(stopd(sb), sizeof(struct lofs_sb_info));
+    stopd(sb) = NULL;
+ out_module:
+    MOD_DEC_USE_COUNT;
+ out:
+    unlock_super(sb);
+    print_exit_location();
+    return ret_sb;
+}
+
+
+/*----*/
+// this structure *must* be static!!! (or the memory for 'name' gets
+// corrupted in 2.3...)
+static struct file_system_type lofs_fs_type =
+{
+    "lofs",			/* name */
+    0,				/* flags */
+    lofs_read_super,		/* read_super function */
+    NULL			/* next file_system_type */
+};
+
+
+int __init
+init_lofs_fs(void)
+{
+    return register_filesystem(&lofs_fs_type);
+}
+
+
+/* Every kernel module contains stuff like this. */
+#ifdef MODULE
+int
+init_module(void)
+{
+    printk("Inserting module lofs version $Id: main.c,v 1.3 1999/09/15 05:30:53 ezk Exp $\n");
+    return init_lofs_fs();
+}
+
+void
+cleanup_module(void)
+{
+    printk("Removing module lofs version $Id: main.c,v 1.3 1999/09/15 05:30:53 ezk Exp $\n");
+    unregister_filesystem(&lofs_fs_type);
+}
+
+void
+fist_mod_dec_use_count(void)
+{
+    MOD_DEC_USE_COUNT;
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * End:
+ */
diff -ruN linux-2.3.45-vanilla/fs/lofs/subr.c linux-2.3.45-fist/fs/lofs/subr.c
--- linux-2.3.45-vanilla/fs/lofs/subr.c	Wed Dec 31 19:00:00 1969
+++ linux-2.3.45-fist/fs/lofs/subr.c	Mon Feb 14 01:15:48 2000
@@ -0,0 +1,17 @@
+/*
+ *  $Id: subr.c,v 1.2 1999/10/18 16:15:48 ezk Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <fist.h>
+#include <lofs.h>
+
+/* no subroutines needed for lofs */
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * End:
+ */
diff -ruN linux-2.3.45-vanilla/fs/lofs/super.c linux-2.3.45-fist/fs/lofs/super.c
--- linux-2.3.45-vanilla/fs/lofs/super.c	Wed Dec 31 19:00:00 1969
+++ linux-2.3.45-fist/fs/lofs/super.c	Mon Feb 14 01:15:48 2000
@@ -0,0 +1,283 @@
+/*
+ *  $Id: super.c,v 1.3 1999/09/15 05:30:53 ezk Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <fist.h>
+#include <lofs.h>
+
+
+STATIC void
+lofs_read_inode(inode_t *inode)
+{
+    inode_t *hidden_inode;
+
+    print_entry_location();
+    fist_checkinode(inode, "lofs_read_inode IN");
+
+    /*
+     * XXX: calling iget from code which iget called us may result
+     * in a deadlock on an SMP kernel!!!
+     */
+    hidden_inode = iget(stohs(inode->i_sb), inode->i_ino);
+    if (!hidden_inode)
+	printk(KERN_ERR "lofs: lofs_read_inode couldn't find hidden_inode\n");
+
+    fist_copy_attr_all(inode, hidden_inode);
+    inode->i_version = ++event;	/* increment inode version */
+    inode->i_op = &lofs_iops;
+    itopd(inode) = kmalloc(sizeof(struct lofs_inode_info), GFP_KERNEL);
+    itohi(inode) = hidden_inode;
+
+    fist_checkinode(inode, "lofs_read_inode OUT");
+    print_exit_location();
+}
+
+
+#ifdef NOT_NEEDED
+/*
+ * Not needed.  This function will be called by sync_one(), which is
+ * called by sync_list(), which is called by sync_inodes(kdev_t dev),
+ * in order to sync dirty inodes of a vfs to disk.  But the higher
+ * vfs code will call sync_inodes() on the lower mounted device anyway
+ * so we should not have call write_inode() on the hidden_inode.
+ */
+STATIC void
+lofs_write_inode(inode_t *inode)
+{
+    return;
+}
+#endif /*  NOT_NEEDED */
+
+
+#ifdef NOT_NEEDED
+/*
+ * We don't think we need it b/c iput() will call this first, resulting
+ * in decrementing our refcount, but our code will free the lower inode
+ * b/c it calls iput() on it.
+ * That's b/c the lower and upper f/s don't have the same refcount
+ * on their inodes.
+ */
+STATIC void
+lofs_put_inode(inode_t *inode)
+{
+    inode_t *hidden_inode = itohi(inode);
+
+    print_entry_location();
+    iput(hidden_inode);
+    print_exit_location();
+    return;
+}
+#endif /* NOT_NEEDED */
+
+
+#ifdef NOT_NEEDED
+/*
+ * no need to do anything (maybe sync lower inode)
+ * because out put_inode calls the lower put_inode, which will
+ * delete the lower inode if needed.  Plus, we don't have all
+ * the information needed to determine if the lower inode should
+ * be deleted.  This is a decision best left to the lower f/s via
+ * its iput.
+ */
+STATIC void
+lofs_delete_inode(inode_t *inode)
+{
+    return;
+}
+#endif /* NOT_NEEDED */
+
+
+STATIC int
+lofs_notify_change(dentry_t *dentry, struct iattr *ia)
+{
+    int err = 0;
+    dentry_t *hidden_dentry = lofs_hidden_dentry(dentry);
+    inode_t *inode = dentry->d_inode;
+    inode_t *hidden_inode;
+
+    print_entry_location();
+    ASSERT(hidden_dentry != NULL);
+    ASSERT(inode != NULL);
+
+    hidden_inode = itohi(inode);
+    ASSERT(hidden_inode != NULL);
+
+    fist_checkinode(inode, "lofs_notify_change");
+    if (!hidden_inode->i_sb ||
+	!hidden_inode->i_sb->s_op ||
+	!hidden_inode->i_sb->s_op->notify_change) {
+	err = inode_change_ok(hidden_inode, ia);
+	if (!err)
+	    inode_setattr(hidden_inode, ia);
+	goto out_update;
+    }
+
+    err = hidden_inode->i_sb->s_op->notify_change(hidden_dentry, ia);
+    if (err)
+	goto out;
+    fist_checkinode(inode, "post lofs_notify_change");
+
+ out_update:
+    err = inode_change_ok(inode, ia);
+    if (!err)
+	inode_setattr(inode, ia);
+#if 0
+    /*
+     * The lower file system might have changed the attributes further,
+     * so we copy the hidden_inode's attributes (and a few more) to
+     * our inode.
+     */
+    fist_copy_attr_all(inode, hidden_inode);
+#endif
+
+out:
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC void
+lofs_put_super(super_block_t *sb)
+{
+    print_entry_location();
+
+    //    dput(stopd(sb)->s_root);
+
+    if (stopd(sb)) {
+	kfree_s(stopd(sb), sizeof(struct lofs_sb_info));
+	stopd(sb) = NULL;
+    }
+    fist_dprint(6, "lofs: released super\n");
+#ifdef MODULE
+    fist_mod_dec_use_count();
+#endif /* MODULE */
+
+    print_exit_location();
+}
+
+
+#ifdef NOT_NEEDED
+/*
+ * This is called in do_umount before put_super.
+ * The superblock lock is not held yet.
+ * We probably do not need to define this or call write_super
+ * on the hidden_sb, because sync_supers() will get to hidden_sb
+ * sooner or later.  But it is also called from file_fsync()...
+ */
+STATIC void
+lofs_write_super(super_block_t *sb)
+{
+    return;
+}
+#endif /* NOT_NEEDED */
+
+
+STATIC int
+lofs_statfs(super_block_t *sb, struct statfs *buf, int bufsiz)
+{
+    int err = 0;
+    super_block_t *hidden_sb = stohs(sb);
+
+    print_entry_location();
+
+    if (hidden_sb->s_op && hidden_sb->s_op->statfs)
+	err = hidden_sb->s_op->statfs(hidden_sb, buf, bufsiz);
+
+    print_exit_status(err);
+    return err;
+}
+
+
+/*
+ * XXX: not implemented.  This is not allowed yet.
+ * Should we call this on the hidden_sb?  Probably not.
+ */
+STATIC int
+lofs_remount_fs(super_block_t *sb, int *flags, char *data)
+{
+    return -ENOSYS;
+}
+
+
+/*
+ * Called by iput() when the inode reference count reached zero
+ * and the inode is not hashed anywhere.  Used to clear anything
+ * that needs to be, before the inode is completely destroyed and put
+ * on the inode free list.
+ */
+STATIC void
+lofs_clear_inode(inode_t *inode)
+{
+    print_entry_location();
+
+    fist_checkinode(inode, "lofs_clear_inode IN");
+    /*
+     * Decrement a reference to a hidden_inode, which was incremented
+     * by our read_inode when it was created initially.
+     */
+    iput(itohi(inode));
+    kfree_s(itopd(inode), sizeof(struct lofs_inode_info));
+    itopd(inode) = NULL;
+    fist_checkinode(inode, "lofs_clear_inode OUT");
+
+    print_exit_location();
+}
+
+
+/*
+ * Called in do_umount() if the MNT_FORCE flag was used and this
+ * function is defined.  See comment in linux/fs/super.c:do_umount().
+ * Used only in nfs, to kill any pending RPC tasks, so that subsequent
+ * code can actually succeed and won't leave tasks that need handling.
+ *
+ * PS. I wonder if this is somehow useful to undo damage that was
+ * left in the kernel after a user level file server (such as amd)
+ * dies.
+ */
+STATIC void
+lofs_umount_begin(super_block_t *sb)
+{
+    super_block_t *hidden_sb = stohs(sb);
+
+    print_entry_location();
+
+    if (hidden_sb->s_op->umount_begin)
+	hidden_sb->s_op->umount_begin(hidden_sb);
+
+    print_exit_location();
+}
+
+
+struct super_operations lofs_sops =
+{
+    lofs_read_inode,		/* read inode */
+#ifdef NOT_NEEDED
+    lofs_write_inode,		/* write inode */
+    lofs_put_inode,		/* put inode */
+    lofs_delete_inode,		/* delete inode */
+#else /* not NOT_NEEDED */
+    NULL,			/* write inode */
+    NULL,			/* put inode */
+    NULL,			/* delete inode */
+#endif /* not NOT_NEEDED */
+    lofs_notify_change,		/* notify change */
+    lofs_put_super,		/* put superblock */
+#ifdef NOT_NEEDED
+    lofs_write_super,		/* write superblock */
+#else /* not NOT_NEEDED */
+    NULL,			/* write superblock */
+#endif /* not NOT_NEEDED */
+    lofs_statfs,		/* stat filesystem */
+    lofs_remount_fs,		/* remount filesystem */
+    lofs_clear_inode,		/* clear inode */
+    lofs_umount_begin		/* umount begin */
+};
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * End:
+ */
diff -ruN linux-2.3.45-vanilla/fs/lofs/vm_area.c linux-2.3.45-fist/fs/lofs/vm_area.c
--- linux-2.3.45-vanilla/fs/lofs/vm_area.c	Wed Dec 31 19:00:00 1969
+++ linux-2.3.45-fist/fs/lofs/vm_area.c	Mon Feb 14 01:15:48 2000
@@ -0,0 +1,147 @@
+/*
+ *  $Id: vm_area.c,v 1.3 1999/08/30 07:31:58 ezk Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <fist.h>
+#include <lofs.h>
+
+
+STATIC void
+lofs_vm_open(vm_area_t *vma)
+{
+    vm_area_t *hidden_vma = vmatohvma(vma), *hidden_vma2;
+    file_t *file = vma->vm_file;
+    file_t *hidden_file = ftohf(file);
+
+    print_entry_location();
+    fist_dprint(6, "VM_OPEN: file 0x%x, hidden_file 0x%x, file->f_count %d, hidden_file->f_count %d\n",
+		(int) file, (int) hidden_file,
+		(int) atomic_read(&file->f_count),
+		(int) atomic_read(&hidden_file->f_count));
+
+    if (hidden_vma->vm_ops->open)
+	hidden_vma->vm_ops->open(hidden_vma);
+    atomic_inc(&hidden_file->f_count);
+
+    /* we need to duplicate the private data */
+    hidden_vma2 = kmalloc(sizeof(vm_area_t), GFP_KERNEL);
+    if (!hidden_vma2) {
+	printk("VM_OPEN: Out of memory\n");
+	goto out;
+    }
+    memcpy(hidden_vma2, hidden_vma, sizeof(vm_area_t));
+    vmatohvma(vma) = hidden_vma2;
+
+ out:
+    fist_dprint(6, "VM_OPEN2: file 0x%x, hidden_file 0x%x, file->f_count %d, hidden_file->f_count %d\n",
+		(int) file, (int) hidden_file,
+		(int) atomic_read(&file->f_count),
+		(int) atomic_read(&hidden_file->f_count));
+    print_exit_location();
+}
+
+
+STATIC void
+lofs_vm_close(vm_area_t *vma)
+{
+    vm_area_t *hidden_vma = vmatohvma(vma);
+    file_t *file = vma->vm_file;
+    file_t *hidden_file = ftohf(file);
+
+    print_entry_location();
+    fist_dprint(6, "VM_CLOSE1: file 0x%x, hidden_file 0x%x, file->f_count %d, hidden_file->f_count %d\n",
+		(int) file, (int) hidden_file,
+		(int) atomic_read(&file->f_count),
+		(int) atomic_read(&hidden_file->f_count));
+
+    ASSERT(hidden_vma != NULL);
+
+    if (hidden_vma->vm_ops->close)
+	hidden_vma->vm_ops->close(hidden_vma);
+    fput(hidden_file);
+    kfree_s(hidden_vma, sizeof(vm_area_t));
+    vmatohvma(vma) = NULL;
+
+    fist_dprint(6, "VM_CLOSE2: file 0x%x, hidden_file 0x%x, file->f_count %d, hidden_file->f_count %d\n",
+		(int) file, (int) hidden_file,
+		(int) atomic_read(&file->f_count),
+		(int) atomic_read(&hidden_file->f_count));
+    print_exit_location();
+}
+
+
+STATIC void
+lofs_vm_shared_unmap(vm_area_t *vma, unsigned long start, size_t len)
+{
+    vm_area_t *hidden_vma = vmatohvma(vma);
+#ifdef FIST_DEBUG
+    file_t *file = vma->vm_file;
+    file_t *hidden_file = ftohf(file);
+#endif /* FIST_DEBUG */
+
+    print_entry_location();
+    fist_dprint(6, "VM_S_UNMAP1: file 0x%x, hidden_file 0x%x, file->f_count %d, hidden_file->f_count %d\n",
+		(int) file, (int) hidden_file,
+		(int) atomic_read(&file->f_count),
+		(int) atomic_read(&hidden_file->f_count));
+
+    /*
+     * call sync (filemap_sync) because the default filemap_unmap
+     * calls it too.
+     */
+    filemap_sync(vma, start, len, MS_ASYNC);
+
+    if (hidden_vma->vm_ops->unmap)
+	hidden_vma->vm_ops->unmap(hidden_vma, start, len);
+
+    fist_dprint(6, "VM_S_UNMAP2: file 0x%x, hidden_file 0x%x, file->f_count %d, hidden_file->f_count %d\n",
+		(int) file, (int) hidden_file,
+		(int) atomic_read(&file->f_count),
+		(int) atomic_read(&hidden_file->f_count));
+    print_exit_location();
+}
+
+
+/*
+ * Shared mappings need to be able to do the right thing at
+ * close/unmap/sync. They will also use the private file as
+ * backing-store for swapping..
+ */
+struct vm_operations_struct lofs_shared_vmops = {
+    lofs_vm_open,		/* open */
+    lofs_vm_close,		/* close */
+    lofs_vm_shared_unmap,	/* unmap */
+    NULL,			/* protect */
+    filemap_sync,		/* sync */
+    NULL,			/* advise */
+    filemap_nopage,		/* nopage */
+    NULL,			/* wppage */
+    filemap_swapout		/* swapout */
+};
+
+/*
+ * Private mappings just need to be able to load in the map.
+ *
+ * (This is actually used for shared mappings as well, if we
+ * know they can't ever get write permissions..)
+ */
+struct vm_operations_struct lofs_private_vmops = {
+    NULL,			/* open */
+    NULL,			/* close */
+    NULL,			/* unmap */
+    NULL,			/* protect */
+    NULL,			/* sync */
+    NULL,			/* advise */
+    filemap_nopage,		/* nopage */
+    NULL,			/* wppage */
+    NULL			/* swapout */
+};
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * End:
+ */
diff -ruN linux-2.3.45-vanilla/include/asm-i386/resource.h linux-2.3.45-fist/include/asm-i386/resource.h
--- linux-2.3.45-vanilla/include/asm-i386/resource.h	Thu Dec  9 16:29:05 1999
+++ linux-2.3.45-fist/include/asm-i386/resource.h	Mon Feb 14 21:32:33 2000
@@ -18,13 +18,13 @@
 
 #define RLIM_NLIMITS	10
 
+#ifdef __KERNEL__
+
 /*
  * SuS says limits have to be unsigned.
  * Which makes a ton more sense anyway.
  */
 #define RLIM_INFINITY	(~0UL)
-
-#ifdef __KERNEL__
 
 #define INIT_RLIMITS					\
 {							\
diff -ruN linux-2.3.45-vanilla/include/linux/fs.h linux-2.3.45-fist/include/linux/fs.h
--- linux-2.3.45-vanilla/include/linux/fs.h	Sun Feb 13 22:04:14 2000
+++ linux-2.3.45-fist/include/linux/fs.h	Mon Feb 14 12:42:04 2000
@@ -921,6 +921,8 @@
 
 typedef int (*read_actor_t)(read_descriptor_t *, struct page *, unsigned long, unsigned long);
 
+/* needed for stackable file system support */
+extern loff_t default_llseek(struct file *file, loff_t offset, int origin);
 
 extern struct dentry * lookup_dentry(const char *, struct dentry *, unsigned int);
 extern struct dentry * __namei(const char *, unsigned int);
diff -ruN linux-2.3.45-vanilla/kernel/ksyms.c linux-2.3.45-fist/kernel/ksyms.c
--- linux-2.3.45-vanilla/kernel/ksyms.c	Sun Feb 13 13:15:25 2000
+++ linux-2.3.45-fist/kernel/ksyms.c	Mon Feb 14 15:31:13 2000
@@ -229,12 +229,19 @@
 EXPORT_SYMBOL(page_symlink_inode_operations);
 EXPORT_SYMBOL(block_symlink);
 
-/* for stackable file systems (lofs, wrapfs, etc.) */
-EXPORT_SYMBOL(add_to_page_cache);
+/* for stackable file systems (lofs, wrapfs, cryptfs, etc.) */
+EXPORT_SYMBOL(default_llseek);
 EXPORT_SYMBOL(filemap_nopage);
 EXPORT_SYMBOL(filemap_swapout);
 EXPORT_SYMBOL(filemap_sync);
+EXPORT_SYMBOL(lock_page);
+#if 0
+EXPORT_SYMBOL(add_to_page_cache);
+EXPORT_SYMBOL(nr_lru_pages);
 EXPORT_SYMBOL(remove_inode_page);
+EXPORT_SYMBOL(swap_free);
+EXPORT_SYMBOL(vfs_create);
+#endif
 
 #if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE)
 EXPORT_SYMBOL(do_nfsservctl);
