Index: linux-2.6.11-rc3-ck1/Documentation/filesystems/00-INDEX
===================================================================
--- linux-2.6.11-rc3-ck1.orig/Documentation/filesystems/00-INDEX	2005-02-03 21:41:13.000000000 +1100
+++ linux-2.6.11-rc3-ck1/Documentation/filesystems/00-INDEX	2005-02-03 21:47:35.689519634 +1100
@@ -36,6 +36,8 @@ romfs.txt
 	- Description of the ROMFS filesystem.
 smbfs.txt
 	- info on using filesystems with the SMB protocol (Windows 3.11 and NT)
+supermount.txt
+	- info on using supermount for removable media.
 sysv-fs.txt
 	- info on the SystemV/V7/Xenix/Coherent filesystem.
 udf.txt
Index: linux-2.6.11-rc3-ck1/Documentation/filesystems/supermount.txt
===================================================================
--- linux-2.6.11-rc3-ck1.orig/Documentation/filesystems/supermount.txt	2005-02-02 00:05:35.421535248 +1100
+++ linux-2.6.11-rc3-ck1/Documentation/filesystems/supermount.txt	2005-02-03 21:47:35.690519471 +1100
@@ -0,0 +1,250 @@
+Supermount README
+=================
+
+Running supermount
+------------------
+
+To run supermount, compile and install a kernel with the supermount
+patches and select "Y" to the question
+
+	Supermount removable media support (CONFIG_SUPERMOUNT) [Y/n/?] 
+
+when you run "make config".  You mount a supermount filesystem with
+the normal mount command, using the syntax
+
+	mount -t supermount -o <superfs-options>,--,<subfs-options> none <mpt>
+
+or by adding correpsonding line to /etc/fstab
+
+	none  <mpt> supermount <superfs-options>,--,<subfs-options> 0 0
+
+where
+
+	<superfs-options> are the options you want to pass to supermount
+	itself.  These are described below.
+
+	<subfs-options> are the options you want supermount to pass to the
+	dismountable filesystem underneath.
+
+	<mpt> is the mount point where you want your removable media to be
+	mounted. 
+
+WARNING: in the above description `none' is literal word. While device
+is ignored by supermount itself, using real files in this place (real
+device name or mount point directory name) is known to cause problems.
+Some programs - fuser is one of them - will try to descend into filesystem
+if dev can be statted, thus making supermount to attempt to access media.
+This is annoying at best - in the worst case it can take very long time
+during startup or shutdown.
+
+Notice that you do not directly specify the block device you are going
+to mount on the mount command line.  This is because the supermount
+filesystem is NOT connected to a block device; rather, supermount is
+responsible for connecting a separate filesystem to the block device.
+You specify the sub-filesystem and block device name by providing the
+<superfs-options> field, where the following options are currently
+recognised:
+
+
+* fs=<filesystem-type>			[default is "auto"]
+
+	Specify the subfilesystem type. Not every filesystem type has
+been tested.  If you use `auto', it will try the following filesystems
+in order:
+		    	"udf"
+			"iso9660"
+			"ext2"
+			"vfat"
+			"msdos"
+
+It is also possible to give list of types separated by `:', like
+
+		fs=ext2:vfat
+		    - or -
+		fs=udf:iso9660
+ 
+
+* dev=<block-device>			[no default, mandatory]
+
+	Specify the block device on which the subfs is to be mounted.
+
+
+* tray_lock={always,onwrite,never}	[default is "onwrite"]
+
+	Specify when supermount is to prevent media removal. `always' means
+on every access (it was default in earlier versions), `onwrite' means only
+for write access and `never' means never :) `onwrite' and `never' are the
+same for ro media like CD-ROM. It is not clear when `never' is actually useful,
+it is presented for completeness only.
+
+* debug[=<bitmap>]			[default is no debug]
+
+	Enable debugging code in the supermount filesystem, if
+the debug option was enabled at compile time.  By default, debugging
+code is compiled into the kernel but is disabled until a debug mount
+option is seen. <bitmap> is the combination of debug flags, following
+flags are possible:
+
+	0x001 - "generic" debug (used by supermount_debug) - default
+	0x002 - trace dentry.c
+	0x004 - trace file.c
+	0x008 - trace filemap.c
+	0x010 - trace mediactl.c
+	0x020 - trace namei.c
+	0x040 - trace subfs.c
+	0x080 - trace super.c
+
+Trace flags turn on tracing of functions in correpsonding files.
+"Generic" debug flag is tested in supermount_debug; for compatibility,
+if no flags are specified, this flag is set.
+
+
+* '--'
+
+	All options after the option string '--' will be passed
+directly to the subfilesystem when it gets mounted.
+
+Errors
+------
+
+In addition to "normal" errors during file operations supermount may
+return following error codes:
+
+* No medium found
+
+	You attempt to access supermounted filesystem when there is no
+medium inserted
+
+* Wrong medium type
+
+	(Not really generated by supermount) You attempt to mount CD
+without data tracks
+
+* Stale NFS file handle
+
+	You attempt to use file handle after medium has been changed.
+
+* No such device or address
+
+	(Not really generated by supermount) device specified in
+dev=<device> option does not exist. Also some drivers return this
+error instead of "No medium found", one example being floppy driver.
+
+* Device or resource busy
+
+	(Not really generated by supermount) device is already mounted.
+Supermount prevents double mount even if kernel otherwise would make it
+possible.
+
+* No such file or directory
+
+	(Not really generated by supermount) file name specified by
+dev=<device> option does not exist
+
+* Operation not permitted
+
+	You attempt to access subfs that is currently disabled
+
+/proc support
+-------------
+
+If kernel has been compiled with procfs support, supermount will provide
+/proc interface to read subfs status and to control some aspects of subfs.
+The following files are created under /proc/fs/supermount:
+
+* version (ro)
+	Shows supermount version.
+
+* subfs (rw)
+
+	Reading this file returns list of all subfs status. One line for
+every subfs is returned; the format is
+	
+	<devname> disabled
+		- or -
+	<devname> unmounted
+		- or -
+	<devname> mounted readcount writecount
+
+where <devname> is the string passed in `dev=' parameter during mount.
+`readcount' is number of current subfs "users" needing ro access; `writecount'
+is the number of "users" needing rw mode. It is mostly the number of open
+files, but inode operations also add to these counts. Those operations
+are normally short-lived to be seen.
+
+	Writing this file changes subfs status; the following commands are
+suported:
+
+	<devname> [disable|enable] [release [force]]
+
+`disable' will disable subfs (i.e. any futher attempt to mount is rejected).
+Subfs must be unmounted; use `disable release' or `disable release force' to
+unmount and disable at the same time.
+
+`enable' will enable disabled subfs, it has no effect if subfs is already
+enabled.
+
+`release' will unmount subfs unless it is busy (opencount > 0). To unmount
+busy subfs add `force'; the effect is very much as if media change has been
+detected (with the difference that subfs will be cleanly unmounted).
+
+Some basic sanity checks are performed, i.e. it is impossible to specify
+both `enable' and `disable' or `force' without `release'.
+
+Internals/Locking
+-----------------
+
+THIS SECTION IS PROBABLY INCOMPLETE. CORRECTIONS ARE WELCOME.
+
+Supermount itself is using two locks and relies on two more locking rules
+as implemented by kernel.
+
+* supermount_proc_sem
+
+	Global mutex used to protect list of sbi during access to
+/proc/fs/supermount/subfs
+
+* sbi->sem
+
+	Per-filesystem mutex that protects subfs state (mounted/unmounted).
+Any changes to subfs state (mounting, unmouting, adding or removing file,
+dentry or inode) happen under this mutex.
+
+* inode->i_sem (see Documentation/filesystem/Locking)
+
+	Per-inode mutex used by VFS to serialize inode unlink operation.
+Supermount relies on the fact that link/unlink for an inode are mutually
+locked and thus inode->i_nlink is atomic inside of fs method.
+
+* BKL
+
+	Used to protect device usage count. It is changed in open/release
+and referenced in ioctl all of which run under BKL. Supermount adds mediactl
+and internally wraps it in BKL as well.
+
+Caveats/BUGS
+------------
+
+Inode times are believed to be correctly updated; still it is possible that
+I missed some point.
+
+If subfs is not yet mounted, find /mnt/cdrom fails with "find: /mnt/cdrom
+changed during execution of find". It is unlikely it can be fixed in
+supermount; much more simple is to provide wrapper that will check if subfs
+is mounted and do simple "touch  /mnt/cdrom" if not to make sure it is.
+
+Supermount attempts to check for changed media at every operation and
+invalidate and umount old subfs to avoid new media corruption in rw case.
+Still it is possible that kernel will write out stale information and
+it is outside of supermount control.
+
+By default cdrom driver does not check media type i.e. supermount will
+try all configured fs types in turn that may be quite time consuming.
+Use sysctl -w dev.cdrom.check_media=1 to enable it. Comments in cdrom.c
+indicate that some (non conforming) software may have problems with this
+settings. YMMV
+
+atime management does not work for special files. There is no hooks from
+VFS into fs to indicate when it gets updated
+
+$Id: supermount.txt,v 1.17.2.1 2003/06/12 07:41:27 bor Exp $
Index: linux-2.6.11-rc3-ck1/drivers/cdrom/cdrom.c
===================================================================
--- linux-2.6.11-rc3-ck1.orig/drivers/cdrom/cdrom.c	2005-02-03 21:41:18.029161363 +1100
+++ linux-2.6.11-rc3-ck1/drivers/cdrom/cdrom.c	2005-02-03 21:47:35.691519307 +1100
@@ -281,6 +281,7 @@
 #include <linux/fcntl.h>
 #include <linux/blkdev.h>
 #include <linux/times.h>
+#include <linux/supermount_media.h>
 
 #include <asm/uaccess.h>
 
@@ -339,7 +340,7 @@ static const char *mrw_address_space[] =
 #define CHECKAUDIO if ((ret=check_for_audio_disc(cdi, cdo))) return ret
 
 /* Not-exported routines. */
-static int open_for_data(struct cdrom_device_info * cdi);
+static int open_for_data(struct cdrom_device_info * cdi, struct block_device *bdev);
 static int check_for_audio_disc(struct cdrom_device_info * cdi,
 			 struct cdrom_device_ops * cdo);
 static void sanitize_format(union cdrom_addr *addr, 
@@ -1003,7 +1004,7 @@ int cdrom_open(struct cdrom_device_info 
 	if ((fp->f_flags & O_NONBLOCK) && (cdi->options & CDO_USE_FFLAGS)) {
 		ret = cdi->ops->open(cdi, 1);
 	} else {
-		ret = open_for_data(cdi);
+		ret = open_for_data(cdi, ip->i_bdev);
 		if (ret)
 			goto err;
 		cdrom_mmc3_profile(cdi);
@@ -1033,7 +1034,7 @@ err:
 }
 
 static
-int open_for_data(struct cdrom_device_info * cdi)
+int open_for_data(struct cdrom_device_info * cdi, struct block_device *bdev)
 {
 	int ret;
 	struct cdrom_device_ops *cdo = cdi->ops;
@@ -1118,7 +1119,8 @@ int open_for_data(struct cdrom_device_in
 		cdinfo(CD_OPEN, "open device failed.\n"); 
 		goto clean_up_and_return;
 	}
-	if (CDROM_CAN(CDC_LOCK) && (cdi->options & CDO_LOCK)) {
+	if (CDROM_CAN(CDC_LOCK) && cdi->options & CDO_LOCK &&
+	    supermount_usage_count(bdev, cdi->use_count) > 0) {
 			cdo->lock_door(cdi, 1);
 			cdinfo(CD_OPEN, "door locked.\n");
 	}
@@ -1131,7 +1133,7 @@ int open_for_data(struct cdrom_device_in
 	This ensures that the drive gets unlocked after a mount fails.  This 
 	is a goto to avoid bloating the driver with redundant code. */ 
 clean_up_and_return:
-	cdinfo(CD_WARNING, "open failed.\n"); 
+	cdinfo(CD_OPEN, "open failed.\n"); 
 	if (CDROM_CAN(CDC_LOCK) && cdi->options & CDO_LOCK) {
 			cdo->lock_door(cdi, 0);
 			cdinfo(CD_OPEN, "door unlocked.\n");
@@ -1210,7 +1212,7 @@ int cdrom_release(struct cdrom_device_in
 		cdinfo(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", cdi->name);
 	if (cdi->use_count == 0)
 		cdrom_dvd_rw_close_write(cdi);
-	if (cdi->use_count == 0 &&
+	if (supermount_usage_count(fp ? fp->f_dentry->d_inode->i_bdev : 0, cdi->use_count) == 0 &&
 	    (cdo->capability & CDC_LOCK) && !keeplocked) {
 		cdinfo(CD_CLOSE, "Unlocking door!\n");
 		cdo->lock_door(cdi, 0);
@@ -1483,6 +1485,38 @@ static void cdrom_count_tracks(struct cd
 		tracks->cdi, tracks->xa);
 }	
 
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+/*
+ * MEDIA_LOCK, MEDIA_UNLOCK
+ *   optarg == 0 - do not adjust usage count (compatibility)
+ *   optarg == 1 - adjust usage count
+ */
+int cdrom_mediactl(struct cdrom_device_info *cdi, struct block_device *bdev, int op, int optarg)
+{
+	struct cdrom_device_ops *cdo = cdi->ops;
+
+	switch (op) {
+	case MEDIA_LOCK:
+	case MEDIA_UNLOCK:
+		if (op == MEDIA_UNLOCK && optarg) {
+			cdi->use_count--;
+		if (cdi->use_count < 0)
+			cdi->use_count = 0;
+		}
+		if (cdo->capability & ~cdi->mask & CDC_LOCK &&
+		    cdi->options & CDO_LOCK &&
+			supermount_usage_count(bdev, cdi->use_count) == 0)
+			cdo->lock_door(cdi, (op == MEDIA_LOCK));
+		if (op == MEDIA_LOCK && optarg)
+			cdi->use_count++;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+#endif
+
 /* Requests to the low-level drivers will /always/ be done in the
    following format convention:
 
@@ -2234,8 +2268,9 @@ int cdrom_ioctl(struct file * file, stru
 		cdinfo(CD_DO_IOCTL, "entering CDROMEJECT\n"); 
 		if (!CDROM_CAN(CDC_OPEN_TRAY))
 			return -ENOSYS;
-		if (cdi->use_count != 1 || keeplocked)
-			return -EBUSY;
+		if (keeplocked ||
+		    (supermount_usage_count(ip->i_bdev,cdi->use_count) != 1))
+                        return -EBUSY;
 		if (CDROM_CAN(CDC_LOCK))
 			if ((ret=cdo->lock_door(cdi, 0)))
 				return ret;
@@ -3071,6 +3106,9 @@ EXPORT_SYMBOL(cdrom_open);
 EXPORT_SYMBOL(cdrom_release);
 EXPORT_SYMBOL(cdrom_ioctl);
 EXPORT_SYMBOL(cdrom_media_changed);
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+EXPORT_SYMBOL(cdrom_mediactl);
+#endif
 EXPORT_SYMBOL(cdrom_number_of_slots);
 EXPORT_SYMBOL(cdrom_mode_select);
 EXPORT_SYMBOL(cdrom_mode_sense);
Index: linux-2.6.11-rc3-ck1/drivers/cdrom/cdu31a.c
===================================================================
--- linux-2.6.11-rc3-ck1.orig/drivers/cdrom/cdu31a.c	2005-02-03 21:41:18.031161037 +1100
+++ linux-2.6.11-rc3-ck1/drivers/cdrom/cdu31a.c	2005-02-03 21:47:35.693518981 +1100
@@ -3003,6 +3003,25 @@ static int scd_block_open(struct inode *
 
 static int scd_block_release(struct inode *inode, struct file *file)
 {
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+        /*
+         * This is the same sort of clockload as use in blkdev_get.
+         * We need information whether device is supermounted inside
+         * of cdrom_release to decide if tray must be unlocked.
+         * This information so far is available only by looking
+         * up superblock but it needs struct *bdev and it is not
+         * available in cdrom_release anymore
+         */
+        struct dentry t_dentry;
+        struct file t_file;
+
+        if (!file) {
+                t_file.f_dentry = &t_dentry;
+                t_dentry.d_inode = inode;
+                file = &t_file;
+        }
+#endif
+
 	return cdrom_release(&scd_info, file);
 }
 
@@ -3029,6 +3048,13 @@ static int scd_block_media_changed(struc
 	return cdrom_media_changed(&scd_info);
 }
 
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+static int scd_block_mediactl(struct block_device *bdev, int op, int arg)
+{
+	return cdrom_mediactl(&scd_info, bdev, op, arg);
+}
+#endif
+
 struct block_device_operations scd_bdops =
 {
 	.owner		= THIS_MODULE,
@@ -3036,6 +3062,9 @@ struct block_device_operations scd_bdops
 	.release	= scd_block_release,
 	.ioctl		= scd_block_ioctl,
 	.media_changed	= scd_block_media_changed,
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+	.mediactl	= scd_block_mediactl,
+#endif
 };
 
 static struct gendisk *scd_gendisk;
Index: linux-2.6.11-rc3-ck1/drivers/cdrom/cm206.c
===================================================================
--- linux-2.6.11-rc3-ck1.orig/drivers/cdrom/cm206.c	2005-02-03 21:41:18.032160874 +1100
+++ linux-2.6.11-rc3-ck1/drivers/cdrom/cm206.c	2005-02-03 21:47:35.694518818 +1100
@@ -1357,6 +1357,25 @@ static int cm206_block_open(struct inode
 
 static int cm206_block_release(struct inode *inode, struct file *file)
 {
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+        /*
+         * This is the same sort of clockload as use in blkdev_get.
+         * We need information whether device is supermounted inside
+         * of cdrom_release to decide if tray must be unlocked.
+         * This information so far is available only by looking
+         * up superblock but it needs struct *bdev and it is not
+         * available in cdrom_release anymore
+         */
+        struct dentry t_dentry;
+        struct file t_file;
+
+        if (!file) {
+                t_file.f_dentry = &t_dentry;
+                t_dentry.d_inode = inode;
+                file = &t_file;
+        }
+#endif
+
 	return cdrom_release(&cm206_info, file);
 }
 
@@ -1371,6 +1390,13 @@ static int cm206_block_media_changed(str
 	return cdrom_media_changed(&cm206_info);
 }
 
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+static int cm206_block_mediactl(struct block_device *bdev, int op, int arg)
+{
+	return cdrom_mediactl(&cm206_info, bdev, op, arg);
+}
+#endif
+
 static struct block_device_operations cm206_bdops =
 {
 	.owner		= THIS_MODULE,
@@ -1378,6 +1404,9 @@ static struct block_device_operations cm
 	.release	= cm206_block_release,
 	.ioctl		= cm206_block_ioctl,
 	.media_changed	= cm206_block_media_changed,
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+	.mediactl	= cm206_block_mediactl,
+#endif
 };
 
 static struct gendisk *cm206_gendisk;
Index: linux-2.6.11-rc3-ck1/drivers/cdrom/mcd.c
===================================================================
--- linux-2.6.11-rc3-ck1.orig/drivers/cdrom/mcd.c	2005-02-03 21:41:18.035160385 +1100
+++ linux-2.6.11-rc3-ck1/drivers/cdrom/mcd.c	2005-02-03 21:47:35.695518654 +1100
@@ -219,6 +219,25 @@ static int mcd_block_open(struct inode *
 
 static int mcd_block_release(struct inode *inode, struct file *file)
 {
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+        /*
+         * This is the same sort of clockload as use in blkdev_get.
+         * We need information whether device is supermounted inside
+         * of cdrom_release to decide if tray must be unlocked.
+         * This information so far is available only by looking
+         * up superblock but it needs struct *bdev and it is not
+         * available in cdrom_release anymore
+         */
+        struct dentry t_dentry;
+        struct file t_file;
+
+        if (!file) {
+                t_file.f_dentry = &t_dentry;
+                t_dentry.d_inode = inode;
+                file = &t_file;
+        }
+#endif
+
 	return cdrom_release(&mcd_info, file);
 }
 
@@ -233,6 +252,13 @@ static int mcd_block_media_changed(struc
 	return cdrom_media_changed(&mcd_info);
 }
 
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+static int mcd_block_mediactl(struct block_device *bdev, int op, int arg)
+{
+	return cdrom_mediactl(&mcd_info, bdev, op, arg);
+}
+#endif
+
 static struct block_device_operations mcd_bdops =
 {
 	.owner		= THIS_MODULE,
@@ -240,6 +266,9 @@ static struct block_device_operations mc
 	.release	= mcd_block_release,
 	.ioctl		= mcd_block_ioctl,
 	.media_changed	= mcd_block_media_changed,
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+	.mediactl	= mcd_block_mediactl,
+#endif
 };
 
 static struct gendisk *mcd_gendisk;
Index: linux-2.6.11-rc3-ck1/drivers/cdrom/mcdx.c
===================================================================
--- linux-2.6.11-rc3-ck1.orig/drivers/cdrom/mcdx.c	2005-02-03 21:41:18.036160222 +1100
+++ linux-2.6.11-rc3-ck1/drivers/cdrom/mcdx.c	2005-02-03 21:47:35.697518328 +1100
@@ -223,6 +223,25 @@ static int mcdx_block_open(struct inode 
 static int mcdx_block_release(struct inode *inode, struct file *file)
 {
 	struct s_drive_stuff *p = inode->i_bdev->bd_disk->private_data;
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+        /*
+         * This is the same sort of clockload as use in blkdev_get.
+         * We need information whether device is supermounted inside
+         * of cdrom_release to decide if tray must be unlocked.
+         * This information so far is available only by looking
+         * up superblock but it needs struct *bdev and it is not
+         * available in cdrom_release anymore
+         */
+        struct dentry t_dentry;
+        struct file t_file;
+
+        if (!file) {
+                t_file.f_dentry = &t_dentry;
+                t_dentry.d_inode = inode;
+                file = &t_file;
+        }
+#endif
+
 	return cdrom_release(&p->info, file);
 }
 
@@ -239,6 +258,14 @@ static int mcdx_block_media_changed(stru
 	return cdrom_media_changed(&p->info);
 }
 
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+static int mcdx_block_mediactl(struct block_device *bdev, int op, int arg)
+{
+	struct s_drive_stuff *p = bdev->bd_disk->private_data;
+	return cdrom_mediactl(&p->info, bdev, op, arg);
+}
+#endif
+
 static struct block_device_operations mcdx_bdops =
 {
 	.owner		= THIS_MODULE,
@@ -246,6 +273,9 @@ static struct block_device_operations mc
 	.release	= mcdx_block_release,
 	.ioctl		= mcdx_block_ioctl,
 	.media_changed	= mcdx_block_media_changed,
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+	.mediactl	= mcdx_block_mediactl,
+#endif
 };
 
 
Index: linux-2.6.11-rc3-ck1/drivers/cdrom/sbpcd.c
===================================================================
--- linux-2.6.11-rc3-ck1.orig/drivers/cdrom/sbpcd.c	2005-02-03 21:41:18.041159407 +1100
+++ linux-2.6.11-rc3-ck1/drivers/cdrom/sbpcd.c	2005-02-03 21:47:35.700517838 +1100
@@ -5366,6 +5366,25 @@ static int sbpcd_block_open(struct inode
 static int sbpcd_block_release(struct inode *inode, struct file *file)
 {
 	struct sbpcd_drive *p = inode->i_bdev->bd_disk->private_data;
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+        /*
+         * This is the same sort of clockload as use in blkdev_get.
+         * We need information whether device is supermounted inside
+         * of cdrom_release to decide if tray must be unlocked.
+         * This information so far is available only by looking
+         * up superblock but it needs struct *bdev and it is not
+         * available in cdrom_release anymore
+         */
+        struct dentry t_dentry;
+        struct file t_file;
+
+        if (!file) {
+                t_file.f_dentry = &t_dentry;
+                t_dentry.d_inode = inode;
+                file = &t_file;
+        }
+#endif
+
 	return cdrom_release(p->sbpcd_infop, file);
 }
 
@@ -5382,6 +5401,14 @@ static int sbpcd_block_media_changed(str
 	return cdrom_media_changed(p->sbpcd_infop);
 }
 
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+static int sbpcd_block_mediactl(struct block_device *bdev, int op, int arg)
+{
+	struct sbpcd_drive *p = bdev->bd_disk->private_data;
+	return cdrom_mediactl(p->sbpcd_infop, bdev, op, arg);
+}
+#endif
+
 static struct block_device_operations sbpcd_bdops =
 {
 	.owner		= THIS_MODULE,
@@ -5389,6 +5416,9 @@ static struct block_device_operations sb
 	.release	= sbpcd_block_release,
 	.ioctl		= sbpcd_block_ioctl,
 	.media_changed	= sbpcd_block_media_changed,
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+	.mediactl	= sbpcd_block_mediactl,
+#endif
 };
 /*==========================================================================*/
 /*
Index: linux-2.6.11-rc3-ck1/drivers/ide/ide-cd.c
===================================================================
--- linux-2.6.11-rc3-ck1.orig/drivers/ide/ide-cd.c	2005-02-03 21:41:18.482087531 +1100
+++ linux-2.6.11-rc3-ck1/drivers/ide/ide-cd.c	2005-02-03 21:47:35.703517348 +1100
@@ -3326,6 +3326,24 @@ static int idecd_release(struct inode * 
 {
 	ide_drive_t *drive = inode->i_bdev->bd_disk->private_data;
 	struct cdrom_info *info = drive->driver_data;
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+	/*
+	 * This is the same sort of clockload as use in blkdev_get.
+	 * We need information whether device is supermounted inside
+	 * of cdrom_release to decide if tray must be unlocked.
+	 * This information so far is available only by looking
+	 * up superblock but it needs struct *bdev and it is not
+	 * available in cdrom_release anymore
+	 */
+	struct dentry t_dentry;
+	struct file t_file;
+
+	if (!file) {
+		t_file.f_dentry = &t_dentry;
+		t_dentry.d_inode = inode;
+		file = &t_file;
+	}
+#endif
 
 	cdrom_release (&info->devinfo, file);
 	drive->usage--;
@@ -3360,13 +3378,26 @@ static int idecd_revalidate_disk(struct 
 	return  0;
 }
 
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+static int idecd_mediactl(struct block_device *bdev, int op, int arg)
+{
+	struct gendisk *disk = bdev->bd_disk;
+	ide_drive_t *drive = disk->private_data;
+	struct cdrom_info *info = drive->driver_data;
+	return cdrom_mediactl(&info->devinfo, bdev, op, arg);
+}
+#endif
+
 static struct block_device_operations idecd_ops = {
 	.owner		= THIS_MODULE,
 	.open		= idecd_open,
 	.release	= idecd_release,
 	.ioctl		= idecd_ioctl,
 	.media_changed	= idecd_media_changed,
-	.revalidate_disk= idecd_revalidate_disk
+	.revalidate_disk= idecd_revalidate_disk,
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+	.mediactl	= idecd_mediactl,
+#endif
 };
 
 /* options */
Index: linux-2.6.11-rc3-ck1/drivers/ide/ide-floppy.c
===================================================================
--- linux-2.6.11-rc3-ck1.orig/drivers/ide/ide-floppy.c	2005-02-03 21:41:18.485087042 +1100
+++ linux-2.6.11-rc3-ck1/drivers/ide/ide-floppy.c	2005-02-03 21:47:35.704517185 +1100
@@ -99,6 +99,8 @@
 #include <linux/ide.h>
 #include <linux/bitops.h>
 
+#include <linux/supermount_media.h>
+
 #include <asm/byteorder.h>
 #include <asm/irq.h>
 #include <asm/uaccess.h>
@@ -1890,7 +1892,7 @@ static int idefloppy_open(struct inode *
 	
 	debug_log(KERN_INFO "Reached idefloppy_open\n");
 
-	if (drive->usage == 1) {
+	if (supermount_usage_count(inode->i_bdev, drive->usage) == 1) {
 		clear_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, &floppy->flags);
 		/* Just in case */
 
@@ -1937,7 +1939,7 @@ static int idefloppy_release(struct inod
 	
 	debug_log(KERN_INFO "Reached idefloppy_release\n");
 
-	if (drive->usage == 1) {
+	if (supermount_usage_count(inode->i_bdev, drive->usage) == 1) {
 		idefloppy_floppy_t *floppy = drive->driver_data;
 
 		/* IOMEGA Clik! drives do not support lock/unlock commands */
@@ -1970,7 +1972,7 @@ static int idefloppy_ioctl(struct inode 
 		prevent = 0;
 		/* fall through */
 	case CDROM_LOCKDOOR:
-		if (drive->usage > 1)
+		if (supermount_usage_count(bdev, drive->usage) > 1)
 			return -EBUSY;
 
 		/* The IOMEGA Clik! Drive doesn't support this command - no room for an eject mechanism */
@@ -2038,13 +2040,47 @@ static int idefloppy_revalidate_disk(str
 	return 0;
 }
 
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+static int idefloppy_mediactl (struct block_device *bdev, int op, int optarg)
+{
+	ide_drive_t *drive = bdev->bd_disk->private_data;
+	idefloppy_floppy_t *floppy = drive->driver_data;
+	idefloppy_pc_t pc;
+
+        switch (op) {
+                case MEDIA_LOCK:
+                case MEDIA_UNLOCK:
+                        if (op == MEDIA_UNLOCK && optarg) {
+                                drive->usage--;
+                                if (drive->usage < 0)
+                                        drive->usage = 0;
+                        }
+                        /* IOMEGA Clik! drives do not support lock/unlock commands */
+                        if (!test_bit(IDEFLOPPY_CLIK_DRIVE, &floppy->flags)
+                            && supermount_usage_count(bdev, drive->usage) == 0) {
+                                idefloppy_create_prevent_cmd(&pc, (op == MEDIA_LOCK));
+                                (void) idefloppy_queue_pc_tail(drive, &pc);
+                        }
+                        if (op == MEDIA_LOCK && optarg)
+                                drive->usage++;
+                        break;
+                default:
+                        return -EINVAL;
+        }
+        return 0;
+}
+#endif
+
 static struct block_device_operations idefloppy_ops = {
 	.owner		= THIS_MODULE,
 	.open		= idefloppy_open,
 	.release	= idefloppy_release,
 	.ioctl		= idefloppy_ioctl,
 	.media_changed	= idefloppy_media_changed,
-	.revalidate_disk= idefloppy_revalidate_disk
+	.revalidate_disk= idefloppy_revalidate_disk,
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+	.mediactl	= idefloppy_mediactl,
+#endif
 };
 
 static int idefloppy_attach (ide_drive_t *drive)
Index: linux-2.6.11-rc3-ck1/drivers/scsi/sd.c
===================================================================
--- linux-2.6.11-rc3-ck1.orig/drivers/scsi/sd.c	2005-02-03 21:41:20.892694576 +1100
+++ linux-2.6.11-rc3-ck1/drivers/scsi/sd.c	2005-02-03 21:47:35.706516859 +1100
@@ -49,6 +49,7 @@
 #include <linux/blkpg.h>
 #include <linux/kref.h>
 #include <linux/delay.h>
+#include <linux/supermount_media.h>
 #include <asm/uaccess.h>
 
 #include <scsi/scsi.h>
@@ -471,7 +472,7 @@ static int sd_open(struct inode *inode, 
 	if (!scsi_device_online(sdev))
 		goto error_out;
 
-	if (!sdkp->openers++ && sdev->removable) {
+	if (supermount_usage_count(inode->i_bdev, sdkp->openers++) == 0 && sdev->removable) {
 		if (scsi_block_when_processing_errors(sdev))
 			scsi_set_medium_removal(sdev, SCSI_REMOVAL_PREVENT);
 	}
@@ -502,7 +503,7 @@ static int sd_release(struct inode *inod
 
 	SCSI_LOG_HLQUEUE(3, printk("sd_release: disk=%s\n", disk->disk_name));
 
-	if (!--sdkp->openers && sdev->removable) {
+	if (supermount_usage_count(inode->i_bdev, --sdkp->openers) == 0 && sdev->removable) {
 		if (scsi_block_when_processing_errors(sdev))
 			scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW);
 	}
@@ -663,6 +664,16 @@ static int sd_media_changed(struct gendi
 		 goto not_present;
 
 	/*
+	 * FIXME HACK
+	 * busy device that is unplugged is SDEV_DEL but online and ioctl
+	 * does not return any error. Oh well, it is likely layering
+	 * violation but for now it enables media checks for supermount
+	 */
+
+	if (sdp->sdev_state == SDEV_DEL) 
+		goto not_present;
+
+	/*
 	 * For removable scsi disk we have to recognise the presence
 	 * of a disk in the drive. This is kept in the struct scsi_disk
 	 * struct and tested at open !  Daniel Roche (dan@lectra.fr)
@@ -741,6 +752,51 @@ static void sd_rescan(struct device *dev
 	sd_revalidate_disk(sdkp->disk);
 }
 
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+/*
+ * This function performs media control operations.  Currently the
+ * only functions used are MEDIA_LOCK and MEDIA_UNLOCK, to lock and
+ * unlock the drive door.
+ */
+
+static int sd_mediactl(struct block_device *bdev, int op, int optarg)
+{
+	struct scsi_disk *sdkp = scsi_disk(bdev->bd_disk);
+	struct scsi_device *sdp = sdkp->device;
+	int rc = 0;
+
+	SCSI_LOG_HLQUEUE(3, printk("sd_mediactl: disk=%s\n",
+						bdev->bd_disk->disk_name));
+
+	if (!sdp->removable)
+		return 0;
+
+	if (!scsi_block_when_processing_errors(sdp))
+		return -ENODEV;
+
+	switch (op) {
+	case MEDIA_LOCK:
+		if (supermount_usage_count(bdev, sdkp->openers) == 0)
+			rc = scsi_set_medium_removal(sdp, SCSI_REMOVAL_PREVENT);
+		/* FIXME is it the right way? */
+		if (optarg)
+			sdkp->openers++;
+		break;
+	case MEDIA_UNLOCK:
+		if (optarg && sdkp->openers > 0)
+			sdkp->openers--;
+		if (supermount_usage_count(bdev, sdkp->openers) == 0)
+			rc = scsi_set_medium_removal(sdp, SCSI_REMOVAL_ALLOW);
+		break;
+	default:
+		rc = -EINVAL;
+		break;
+	}
+
+	return rc;
+}
+#endif
+
 static struct block_device_operations sd_fops = {
 	.owner			= THIS_MODULE,
 	.open			= sd_open,
@@ -748,6 +804,9 @@ static struct block_device_operations sd
 	.ioctl			= sd_ioctl,
 	.media_changed		= sd_media_changed,
 	.revalidate_disk	= sd_revalidate_disk,
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+	.mediactl		= sd_mediactl,
+#endif
 };
 
 /**
Index: linux-2.6.11-rc3-ck1/drivers/scsi/sr.c
===================================================================
--- linux-2.6.11-rc3-ck1.orig/drivers/scsi/sr.c	2005-02-03 21:41:20.895694087 +1100
+++ linux-2.6.11-rc3-ck1/drivers/scsi/sr.c	2005-02-03 21:47:35.707516695 +1100
@@ -465,6 +465,25 @@ static int sr_block_open(struct inode *i
 	struct scsi_cd *cd = scsi_cd(inode->i_bdev->bd_disk);
 	int ret = 0;
 
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+	/*
+	 * This is the same sort of clockload as use in blkdev_get.
+	 * We need information whether device is supermounted inside
+	 * of cdrom_release to decide if tray must be unlocked.
+	 * This information so far is available only by looking
+	 * up superblock but it needs struct *bdev and it is not
+	 * available in cdrom_release anymore
+	 */
+	struct dentry t_dentry;
+	struct file t_file;
+
+	if (!file) {
+		t_file.f_dentry = &t_dentry;
+		t_dentry.d_inode = inode;
+		file = &t_file;
+	}
+#endif
+
 	if(!(cd = scsi_cd_get(disk)))
 		return -ENXIO;
 
@@ -511,6 +530,15 @@ static int sr_block_media_changed(struct
 	return cdrom_media_changed(&cd->cdi);
 }
 
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+static int sr_block_mediactl(struct block_device *bdev, int op, int arg)
+{
+	struct gendisk *disk = bdev->bd_disk;
+	struct scsi_cd *cd = scsi_cd(disk);
+	return cdrom_mediactl(&cd->cdi, bdev, op, arg);
+}
+#endif
+
 struct block_device_operations sr_bdops =
 {
 	.owner		= THIS_MODULE,
@@ -518,6 +546,9 @@ struct block_device_operations sr_bdops 
 	.release	= sr_block_release,
 	.ioctl		= sr_block_ioctl,
 	.media_changed	= sr_block_media_changed,
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+	.mediactl	= sr_block_mediactl,
+#endif
 };
 
 static int sr_open(struct cdrom_device_info *cdi, int purpose)
Index: linux-2.6.11-rc3-ck1/fs/block_dev.c
===================================================================
--- linux-2.6.11-rc3-ck1.orig/fs/block_dev.c	2005-02-03 21:41:25.000000000 +1100
+++ linux-2.6.11-rc3-ck1/fs/block_dev.c	2005-02-03 21:47:35.708516532 +1100
@@ -524,12 +524,45 @@ int check_disk_change(struct block_devic
 {
 	struct gendisk *disk = bdev->bd_disk;
 	struct block_device_operations * bdops = disk->fops;
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+	struct super_block *sb = NULL;
+	int supermounted = 0;
+#endif
 
 	if (!bdops->media_changed)
 		return 0;
 	if (!bdops->media_changed(bdev->bd_disk))
 		return 0;
 
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+	sb = get_super(bdev);
+	if (sb) {
+		atomic_set(&sb->s_media_changed, 1);
+		supermounted = sb->s_flags & MS_SUPERMOUNTED;
+		drop_super(sb);
+	}
+
+	/*
+	 * Supermount used to did invalidate_device followed by
+	 * destroy_buffers. invalidate_device hardly does anything
+	 * useful here as it won't really invalidate "busy" inodes
+	 * and every inode in subfs is busy (at least one reference
+	 * from superfs exists). So now it just flushes
+	 * buffers so they do not accidentally overwrite newly
+	 * inserted media
+	 *
+	 * FIXME unfortunately during umount VFS may write on its
+	 * own, like write_super. Those writes will go to a wrong
+	 * media thus corrupting it :(
+	 *
+	 * There is no error print because supermount warns user
+	 * itself.
+	 */
+
+	if (supermounted) {
+		invalidate_bdev(bdev, 1);
+	} else
+#endif
 	if (__invalidate_device(bdev, 0))
 		printk("VFS: busy inodes on changed media.\n");
 
Index: linux-2.6.11-rc3-ck1/fs/ext2/super.c
===================================================================
--- linux-2.6.11-rc3-ck1.orig/fs/ext2/super.c	2005-02-03 21:41:25.000000000 +1100
+++ linux-2.6.11-rc3-ck1/fs/ext2/super.c	2005-02-03 21:47:35.727513430 +1100
@@ -292,6 +292,12 @@ static match_table_t tokens = {
 	{Opt_ignore, "noquota"},
 	{Opt_ignore, "quota"},
 	{Opt_ignore, "usrquota"},
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+	/* Silently ignore NLS options */
+	{Opt_ignore, "iocharset=%s"},
+	{Opt_ignore, "codepage=%s"},
+	{Opt_ignore, "umask=%o"},
+#endif
 	{Opt_err, NULL}
 };
 
Index: linux-2.6.11-rc3-ck1/fs/isofs/inode.c
===================================================================
--- linux-2.6.11-rc3-ck1.orig/fs/isofs/inode.c	2004-10-19 08:57:11.000000000 +1000
+++ linux-2.6.11-rc3-ck1/fs/isofs/inode.c	2005-02-03 21:47:35.747510165 +1100
@@ -364,6 +364,10 @@ static match_table_t tokens = {
 	{Opt_ignore, "conv=auto"},
 	{Opt_ignore, "conv=a"},
 	{Opt_nocompress, "nocompress"},
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+	{Opt_ignore, "codepage=%s"},
+	{Opt_ignore, "umask=%s"},
+#endif
 	{Opt_err, NULL}
 };
 
Index: linux-2.6.11-rc3-ck1/fs/Kconfig
===================================================================
--- linux-2.6.11-rc3-ck1.orig/fs/Kconfig	2005-02-03 21:41:25.000000000 +1100
+++ linux-2.6.11-rc3-ck1/fs/Kconfig	2005-02-03 21:47:35.764507390 +1100
@@ -867,6 +867,8 @@ config RAMFS
 	  To compile this as a module, choose M here: the module will be called
 	  ramfs.
 
+source "fs/supermount/Kconfig"
+
 endmenu
 
 menu "Miscellaneous filesystems"
Index: linux-2.6.11-rc3-ck1/fs/Makefile
===================================================================
--- linux-2.6.11-rc3-ck1.orig/fs/Makefile	2005-02-03 21:47:09.808745518 +1100
+++ linux-2.6.11-rc3-ck1/fs/Makefile	2005-02-03 21:47:35.764507390 +1100
@@ -96,3 +96,4 @@ obj-$(CONFIG_BEFS_FS)		+= befs/
 obj-$(CONFIG_HOSTFS)		+= hostfs/
 obj-$(CONFIG_HPPFS)		+= hppfs/
 obj-$(CONFIG_DEBUG_FS)		+= debugfs/
+obj-$(CONFIG_SUPERMOUNT)	+= supermount/
Index: linux-2.6.11-rc3-ck1/fs/namespace.c
===================================================================
--- linux-2.6.11-rc3-ck1.orig/fs/namespace.c	2005-02-03 21:41:26.000000000 +1100
+++ linux-2.6.11-rc3-ck1/fs/namespace.c	2005-02-03 21:47:35.769506574 +1100
@@ -1044,6 +1044,15 @@ long do_mount(char * dev_name, char * di
 	if (retval)
 		goto dput_out;
 
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+	if (!(flags & (MS_REMOUNT | MS_MOVE)) &&
+	    (nd.mnt->mnt_sb->s_type->fs_flags & FS_NO_SUBMNT)) {
+		retval = -EPERM;
+		path_release(&nd);
+		return retval;
+	}
+#endif
+
 	if (flags & MS_REMOUNT)
 		retval = do_remount(&nd, flags & ~MS_REMOUNT, mnt_flags,
 				    data_page);
Index: linux-2.6.11-rc3-ck1/fs/super.c
===================================================================
--- linux-2.6.11-rc3-ck1.orig/fs/super.c	2005-02-03 21:41:28.000000000 +1100
+++ linux-2.6.11-rc3-ck1/fs/super.c	2005-02-03 21:47:35.778505104 +1100
@@ -86,6 +86,9 @@ static struct super_block *alloc_super(v
 		s->s_qcop = sb_quotactl_ops;
 		s->s_op = &default_op;
 		s->s_time_gran = 1000000000;
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+		atomic_set(&s->s_media_changed, 0);
+#endif
 	}
 out:
 	return s;
@@ -553,6 +556,10 @@ int do_remount_sb(struct super_block *sb
 	return 0;
 }
 
+#ifdef CONFIG_SUPERMOUNT_MODULE
+EXPORT_SYMBOL(do_remount_sb);
+#endif
+
 static void do_emergency_remount(unsigned long foo)
 {
 	struct super_block *sb;
@@ -693,11 +700,18 @@ struct super_block *get_sb_bdev(struct f
 		goto out;
 
 	if (s->s_root) {
-		if ((flags ^ s->s_flags) & MS_RDONLY) {
+		if (((flags ^ s->s_flags) & MS_RDONLY)
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+		/* disallow double mounting for supermounted device */
+		    || ((flags | s->s_flags) & MS_SUPERMOUNTED)
+#endif
+		   )
+		{
 			up_write(&s->s_umount);
 			deactivate_super(s);
 			s = ERR_PTR(-EBUSY);
 		}
+
 		goto out;
 	} else {
 		char b[BDEVNAME_SIZE];
@@ -805,6 +819,17 @@ do_kern_mount(const char *fstype, int fl
 	if (!type)
 		return ERR_PTR(-ENODEV);
 
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+	/* sanity checks; supermount relies on these assumptions */
+	if (flags & MS_SUPERMOUNTED) {
+		sb = ERR_PTR(-EINVAL);
+		if (type->fs_flags & FS_ODD_RENAME)
+			goto out;
+		if (!(type->fs_flags & FS_REQUIRES_DEV))
+			goto out;
+		sb = ERR_PTR(-ENOMEM);
+	}
+#endif
 	mnt = alloc_vfsmnt(name);
 	if (!mnt)
 		goto out;
Index: linux-2.6.11-rc3-ck1/fs/supermount/changelog
===================================================================
--- linux-2.6.11-rc3-ck1.orig/fs/supermount/changelog	2005-02-02 00:05:35.421535248 +1100
+++ linux-2.6.11-rc3-ck1/fs/supermount/changelog	2005-02-03 21:47:35.779504941 +1100
@@ -0,0 +1,462 @@
+* ? ? ? Andrey Borzenkov <arvidjaar@mail.ru> ?.?.?
+
+* Sun 18 Jan 2004 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.4
+
+  - yet another attempt to fix detection of USB drive removale
+    (explicitly check for SDVE_DEL state in sd.c:sd_media_changed)
+
+  - fixed i18n patch for ext2 and udf (ignored options)
+
+  - ->nopage prototype changed; it makes it incompatible with
+    kernel < 2.6.1 (I wonder how it compiled - or did I miss
+    warning). Add comments about ->populate and why it won't be
+    implemented
+
+  - fix "auto" option. Simplification of option processing turned
+    out to be a mess :( Reported by Bart
+
+* Mon 12 Jan 2004 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.3b
+
+  - Fix compilation problem in subfs.c with older GCC - declaration
+    after statement in clear_inodes(bug 875009). Pointed by Adrian Punga.
+
+* Wed 07 Jan 2004 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.3a
+
+  - fix Oops during options parsing if no subfs option is
+    given after "--" (bug 869863)
+
+* Mon 29 Dec 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.3
+
+  - move test for device offline in sd.c to the top in attempt
+    to fix USB removale problem
+
+  - simplify options procesing
+
+  - remove cdrom "open failed" message (it is now under
+    CDROM_OPEN flag like all others in cdrom_open).
+
+  - rewrote option parsing to use new lib/parse library
+
+* Sun 26 Oct 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.2a
+
+  - remove some obsolete files that are not used anymore from CVS
+    (i.e. from patch)
+
+  - isofs needs extra ignored options now. It rurns out it silently
+    ignored any unknown option before new and shiny option parsing
+    code
+
+  - consistently use "kernel 2.6" in version print
+
+  - updates for -test8. Change ext2/udf patches to match new options
+    parsing code.
+
+* Wed 10 Sep 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.2
+
+  - NODEV is gone, use MKDEV(UNNAMED_MAJOR, 0) instead, it is just
+    as good as anything else
+
+* Sat 23 Aug 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.1d
+
+  - rediff against 2.6.0-test4
+
+* Mon 18 Aug 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.1c
+
+  - fix compilation without CONFIG_SUPERMOUNT. Reported by
+    Igor Strygin
+
+* Sun 10 Aug 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.1b
+
+  - /proc/fs/supermount/version is read-only, do not set permissions to
+    rw
+
+  - remove generic_unplug_queue from ide_do_drive_cmd, apparently
+    it is redundant now (fixes bug 785691)
+
+  - rediff for 2.6.0-test3
+
+* Sun 27 Jul 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.1a
+
+  - rediff for 2.6.0-test2
+
+* Sun 13 Jul 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.1
+
+  - hush most unused variables warnings with CONFIG_DEBUG (idea
+    suggested by Jeff Garzik)
+
+  - replace ugly SUPERMOUNT_TARCE_{ENTER,LEAVE}[_ARGS] with ENTER/LEAVE
+    Use %p throughout for pointer output.
+
+  - send DN_CREATE for root on mount
+
+  - allow open and readdir on mountpoint even if subfs is unmounted.
+    This provides for active monitoring by FAM/dnotify and is overall
+    less surprising - ls /mnt/cdrom just returns empty directory now
+    instead of error. For technical reasons ->flush and ->permissions
+    need be aware about it too.
+
+    Files opened while subfs is unmounted are NOT usable for anything
+    except readdir even AFTER subfs gets mounted. So application
+    has to reopen directory to rescan it. AFAIK all of them do it :)
+
+* Sat 12 Jul 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.0a
+
+  - add struct nameidata * goes into d_revalidate as well
+
+  - add struct nameidata * to some inode methods (introduced in
+    2.5.75). Currently it is just passed along - it is not clear if
+    local subfs-specific nameidata has to be constructed. Apparently
+    it existed in follow_link all the time without any evil done.
+
+  - fix small races in supermount_getattr - do not attempt to
+    get subdent if prevent_umount failed
+
+* Mon 23 Jun 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.0
+
+  - added ->getattr; it never fails on both unmounted mountpoint
+    and stale open files - to ensure fuser -m /mnt/cdrom still finds them
+
+  - merge 2.5.73; update sd.c to new refcount interface in sd.c
+
+  - fix SUPERMOUNT_BUG_ON_LOCKED without SUPERMOUNT_DEBUG
+
+  - add media management to ide-floppy and those CD-ROM drivers that
+    are using standard cdrom_* calls (cdu31a, cm206, mcd, mcdx, sbpcd).
+    Testdrive revealed that none of them builds for other reasons :)
+
+  - add (un)mark_media_supermounted; mark after mount, unmark
+    before umount. These functions just manipulate bd_disk->scount
+
+  - add scount to struct gendisk to serve as count of total
+    supermounted partitions; use everywhere to check if device
+    is supermounted
+
+* Tue 17 Jun 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.0test2
+
+  - update kernel patch for 2.5.72 (there was a reject in
+    drivers/ide/ide-io.c)
+
+  - move init_inode_info into alloc_inode; use iget_locked
+    instead of iget to skip ->read_inode with intent to move
+    inode init under if (inode->i_state & I_NEW)
+
+  - polish dentry.c a bit while I am on it
+
+  - ->d_compare is not run under dcache_lock anymore. Pointed by
+    Maneesh Soni. As we can't sleep in d_compare, use rwlock to
+    protect against races with supermount_clean_dentries until
+    I find better solution.
+
+  - remove new_subfs_dentry - it is not used anymore and comments
+    there were wrong anyway
+
+* Sun 01 Jun 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.0test1
+
+  - add sr and sd support
+
+  - update sb s_blocksize and s_blocksize_bits on subfs mount
+
+  - use BKL around chek_disk_change; it is not garanteed by VFS now
+
+  - llseek no more updates file readahead pointer
+
+  - used bdev not gendisk in mediactl - it needs it for
+    supermount_usage_count. It probably needs redesign. Also some minor
+    changes on the way.
+
+  - 2.5 changes. supermount_get_sb, inode alloc/dealloc, use bdev instead
+    of dev, Makefile change, add CONFIG_SUPERMOUNT_DEBUG, do_remount_sb
+    prototype change, mknod prototype change, UPDATE_ATIME dead,
+    dnotify_parent is now true function
+
+* Tue 27 May 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.2.7
+
+  - (re-)set root inode flags after unmounting subfs
+
+  - fix mounting of ro media on rw filesystem. It was broken  by fix
+    in 1.2.4. Remove saved s_mflags and just use real sb->s_flags.
+    Reported by Con Kolivas
+    
+  - do not use MS_MGC_VAL in subfs_real_mount - it is not used anywhere
+    in kernel and all lower bits are already taken in 2.5
+
+  - configure help from Marc-Christian Petersen
+
+  - use subsb->s_bdev instead of looking up using s_dev. This also
+    fixes problem with O(1) patch (bug 737783)
+
+* Wed 14 May 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.2.6
+
+  - check for correct inode in attach_subfs_inode - if super inode
+    has subinode attached it must be the same as is currently
+    resuested. Else it is a bug.
+
+  - use lookup_one_len to lookup subfs dentry in cache; check if
+    subfs did not change name and lookup "real" dentry in parent
+    cache in this case. This finally puts an end to the attempt to
+    control subfs dcache. The only thing we rely upon now is that
+    nothing can change directory contents while in lookup method.
+
+  - implement d_hash and d_compare to properly support case-
+    insensitive filesystems (vfat & Co).
+  
+  - reset super inode i_mapping so it does not point to freed
+    subinode mapping when unmounting subfs
+
+  - send dnotify_parent on dentry cleanup, not on inode
+
+  - use write_inode_now in ->write_inode. Reading intricated flags
+    manipulations in fs/inode.c was just too intimidating. It is not as
+    much overhead as it looks like - it is called from sync_one only
+    so all dirty pages are scheduled to be flushed anyway.
+
+* Sun 04 May 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.2.5
+
+  - enable dentries caching; properly use d_add when dentry is first
+    looked up and d_instantiate when inode is finally available.
+
+  - first stab at sending notifications on media umount/mount
+
+  - more caveats :(
+
+* Sat 26 Apr 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.2.4
+
+  - fix write_inode/prepare_inode deadlock. It looked like
+  	set I_LOCK
+			down(&sbi->sem);
+			iget4 -> wait_on_inode
+	down(&sbi->sem)
+
+  - MS_ACTIVE was reset on subfs remount
+
+  - print process PID in trace
+
+  - adjust directory structure to make patch generation easier
+
+* Sun 20 Apr 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.2.3
+
+  - dummy version due to script problems
+
+* Sun 20 Apr 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.2.2
+  
+  - remove #ifdef CONFIG_PROC_FS from init.c, it already exists in
+    supermount.h
+
+  - no_tray_lock -> TRAY_LOCK_NEVER. Just to make sure it remains
+    compatible (even if it is very unlikely anybody is using it).
+
+  - backout kernel stat patch; remove getattr alltogether. It fixed
+    fuser -m for stale files. getattr remains uniplemented because
+    it does not seem to be used anywhere in kernel at all so I am not
+    actually sure how to implement it
+
+    It also backouts 10_readlik and 30_lseek as they are not used
+    anymore for a long time.
+
+  - remove bogus check for sub- and superinodes i_count. i_count is
+    outside of supermount control; the only thing that we must be
+    sure in - inode is free on umount.
+
+  - reset root inode attributes and operations on umount. This fixes
+    the problem of attempt to mount media on ls -l /mnt after subfs
+    has been mounted once.
+
+  - remove ->stale. For all practical purposes an entity is stale iff
+    it does not (yet) have associated subfs entity. Creating yet another
+    field that just mirrors this condition does not add a single bit
+    of useful information.
+
+  - subfs_is_busy iff subfs mnt_count > 1
+
+* Sat 05 Apr 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.2.1
+
+  - fixed stupid thinko in mediactl methods (the effect was it was
+    impossible to manually eject ro media). It was exposed by recent
+    changes. No changes in supermount itself only in driver code
+
+  - replace no_tray_lock with tray_lock={never,onwrite,always} with
+    "onwrite" being default. I stil do not quite like resulting code
+    but it will do for now.
+
+  - simplified subfs_(get|put)_(write|read) interface; merge both
+    read/write in one function; remove subfs_get_atime. To my surprise
+    it resulted on more clean interface in the rest of supermount even
+    if subfs_(get|put)_access does look a bit weird.
+
+* Sun 30 Mar 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.2.0
+
+  - changed proc command format to
+    <devname> [enable|disable] [release [force]]
+
+  - add /proc/fs/supermount/version; printk version on starup only
+    if procfs is not configured.
+
+  - unify media lock rules for thosed drivers using implementing
+    mediactl (cdrom, sd, ide-floppy as of this writing). Lock
+    media for both manual and software eject; do not unlock media
+    that is in used by something else.
+
+  - added show_options method to super.c
+
+  - implemented /proc/fs/supermount/subfs read as seq_file
+
+* Sat 29 Mar 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.1.4
+
+  - supermount_rename leftover - it does not need new_subfs_dentry.
+    It still worked because new dentry was destroyed immediately, but
+    let's do it the right way. Also mark target rw if exists
+
+  - propagate S_IMMUTABLE, S_NOATIME and S_APPEND flags from subfs
+    to super inode. Filesystems usually rely on VFS to check these
+    flags which means it was possible to overwrite immutable file. Also
+    needed for atime management (to be added).
+
+  - added atime management. It is based on grep UPDATE_ATIME so it may
+    be incomplete; still it gives right results in most obvious cases :)
+    It respects no(dir)atime mount flag.
+
+  - Make sure super inode times and sizes are updated to reflect subfs
+    inode. Done in preparation to remove stat kernel patch. Only those
+    fields needed by stat are updated.
+
+  - remove assertion subi->i_count == 1 from clear_inode. Subfs icache
+    is managed independently and there is no lock (as opposed to dentry
+    case where we never do cached lookup for subfs).
+
+  - use inode cache for supermount inodes to avoid linear search. This
+    moves almost the whole inode creation task into read_inode2 - the
+    only place where inode needs to be created is root inode.
+
+    We use inode numbers from subfs; root inode is never cached until
+    subfs has been mounted. Stale inodes are removed from cache in
+    clean_inodes (just like dentries are in clean_dentries) so aliasing
+    problem is solved.
+
+  - add "disabled" state - prevent any attempt to mount media
+
+  - add procfs support. Reading /proc/fs/supermount/subfs will return
+    list of subfs and their status. Writing into it allows you to
+    control status of subfs (mounted/unmounted, enabled/dusabled)
+
+  - fix race in clear_inode. It was possible that subfs was umounted
+    after inode has been removed from list but before its access
+    counters were cleared. The code that checkes for that exists only
+    for debugging still I'd like it to be useful.
+
+* Sun 16 Mar 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.1.3
+
+  - add CVS verion Id's to files
+
+  - finally get rid of ugly generation number. It is enough to just
+    mark entity obsolete; we do not care actually when it happened.
+    Now every entity starts out stale; stale flag is reset when subfs
+    object is attached; stale flag is set again for the rest of life
+    in subfs_umount
+
+  - as a side effect read/write count is associated with inodes
+    exclusively
+
+  - clear_inode and d_iput need prevent/allow umount to avoid "stealing"
+    subfs while holding active entities. File release is safe as VFS does
+    nou put mnt until _release is finished.
+
+  - subfs_prevent_umount does not need validator. The worst thing that
+    can happen is that newly mounted subfs is accessed. That does not
+    matter, access to subfs object is checked for validity separately in
+    every case.
+
+  - finally clarify dcache management. As we cannot garantee coherency
+    between sub- and super- fs caches we ignore subfs cache entirely.
+    Super dentry is instantiated in supermount_lookup and passed around
+    with subfs dentry attached. It does not matter if it is positive or
+    negative. Currently dentries are forced to be deleted in
+    supermount_d_delete but I believe it is not really needed.
+
+  - print version number on registering filesystem to facilitate debugging
+
+  - umount subfs as soon as possible in check_disk_change. This eliminates
+    zombie state (mounted but inactive due to media change detected).
+
+  - kill subfs_is_active, it is the same as subfs_is_mounted now.
+
+  - kill translations.c it does not do anything special now; move functions
+    in file/dentry/namei
+
+  - fix stupid bug in get_supermount_inode again - super inode must
+    be inited and attached just once not every time.
+    Praise assertions again :)
+
+* 10 Mar 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.1.2
+
+  - allow fs=type1:type2:...
+
+  - remove supermount_drop_dentries (no more used)
+
+  - add some more assertions to subfs_umount
+
+* 10 Mar 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.1.1
+
+  - fix stupid bug in subfs inode refcounting. It was visible with
+    hardlinks only so CD-ROM users would not ever see it.
+
+  - fix even more stupid bug in unlink - subi was used instead of superi
+
+* 8 Mar 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.1.0
+
+  - files cannot use inode obsolete flag - it does not work for
+    root inodes
+
+  - make file system structure exact mirror of subfs - now there
+    is 1-to-1 correspondence between super- and subfs files, dentries
+    and inodes. This is needed to properly manage write access without
+    too much locking
+
+  - finally realized that subfs cannot be obsolete; use either file
+    or inode pointer in all calls into subfs.c to check for freshness
+
+  - debug is now per-mount; added trace of all functions
+  
+  - move setting of SUPERMOUNT_DEBUG to Makefile instead of supermount_i.h
+
+* 1 Mar 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.0.0
+
+  - make sure it is always possible umount subfs. It does it by
+    keeping list of opened files (in addition to inodes) and closing
+    them if media change is detected.
+
+  - make sure subfs can't go away while it is being in use. For
+    files it get_file for subfile to ensure subfs mnt is not freed. For
+    inode operation it increments usage counter of submnt for duration
+    of operation.
+
+  - make sure supermounted device can't be mounted twice (that is
+    allowed by kernel) because it relies on being the only "owner" of
+    subfs. It does it by checking for MS_SUPERMOUNTED flag on mount.
+
+  - make sure you can't mount over supermounted directory. Allowing
+    it is a nightmare and I cannot see any reason for it (if you can
+    provide valid one - I may reconsider this part). It does so by
+    adding fstype flag and checking it on mounting.
+
+  - reject mount -o remount for the time being (until we can deal
+    with it properly)
+
+  - completely change implementation of individual inode methods.
+    Instead of calling VFS recursively it calls subfs methods directly.
+    It is to ensure we are in full control of subfs (assuming it behaves
+    properly :)
+
+  - properly implement VM methods. Currently changing media would
+    leave you with vm_area pointing to no more existing inode ...
+
+  - remove many obsolete pieces like flags that just duplicate
+    existing information.
+
+  - ensure coherency between super- and subfs dentries. Either we
+    have both active in dcache or do not have them at all.
+
+  - fixe the problem with media ejection after direct access to CD-ROM
+
+  - add "just disc change" to all operations to detect media change
+    as soon as possible. It is possible to add option to do it just
+    for writable media or to completely turn it off.
Index: linux-2.6.11-rc3-ck1/fs/supermount/dentry.c
===================================================================
--- linux-2.6.11-rc3-ck1.orig/fs/supermount/dentry.c	2005-02-02 00:05:35.421535248 +1100
+++ linux-2.6.11-rc3-ck1/fs/supermount/dentry.c	2005-02-03 21:47:35.780504778 +1100
@@ -0,0 +1,282 @@
+/*
+ *  linux/fs/supermount/dentry.c
+ *
+ *  Original version:
+ *      Copyright (C) 1995
+ *      Stephen Tweedie (sct@dcs.ed.ac.uk)
+ *
+ *  Rewriten for kernel 2.2 & 2.4. (C) 1999, 2000 Alexis Mikhailov
+ *                                    (alexis@abc.cap.ru)
+ *  Rewritten for kernel 2.4.21 (C) 2003 Andrey Borzenkov
+ *                                       (arvidjaar@mail.ru)
+ *  Rewritten for kernel 2.5.70 (C) 2003 Andrey Borzenkov
+ *                                       (arvidjaar@mail.ru)
+ *
+ *  $Id: dentry.c,v 1.9.2.3 2003/07/13 14:52:43 bor Exp $
+ */
+
+#define S_DBG_TRACE_CURRENT S_DBG_TRACE_DENTRY
+#include "supermount.h"
+
+int
+init_dentry_info(struct dentry *dentry)
+{
+	struct super_block *sb = dentry->d_sb;
+	struct supermount_dentry_info *sdi;
+	int rc;
+
+	ENTER(sb, "dentry=%s", dentry->d_name.name);
+
+	rc = 1;
+	sdi = kmalloc(sizeof(*sdi), GFP_KERNEL);
+	if (!sdi)
+		goto out;
+
+	memset(sdi, 0, sizeof(*sdi));
+
+	INIT_LIST_HEAD(&sdi->list);
+	sdi->dentry = 0;
+	sdi->host = dentry;
+	
+	dentry->d_fsdata = sdi;
+	dentry->d_op = &supermount_dops;
+
+	rc = 0;
+out:
+	LEAVE(sb, "dentry=%s rc=%d", dentry->d_name.name, rc);
+
+	return rc;
+
+}
+
+void
+attach_subfs_dentry(struct dentry *dentry, struct dentry *subdent)
+{
+	struct super_block *sb = dentry->d_sb;
+	struct supermount_sb_info *sbi = supermount_sbi(sb);
+	struct supermount_dentry_info *sdi;
+
+	ENTER(sb, "dentry=%s subd=%p", dentry->d_name.name, subdent);
+
+	sdi = supermount_d(dentry);
+	SUPERMOUNT_BUG_LOCKED_ON(sb, sdi->dentry);
+	sdi->dentry = dget(subdent);
+	list_add(&sdi->list, &sbi->s_dentries);
+
+	LEAVE(sb, "dentry=%s", dentry->d_name.name);
+}
+
+
+struct dentry *
+get_subfs_dentry(struct dentry *dentry)
+{
+	struct super_block *sb = dentry->d_sb;
+	struct dentry *err;
+	struct supermount_dentry_info *sdi = supermount_d(dentry);
+
+	ENTER(sb, "dentry=%s", dentry->d_name.name);
+
+	subfs_lock(sb);
+
+	err = ERR_PTR(-ENOMEDIUM);
+	if (!subfs_is_mounted(sb))
+		goto out;
+
+	err = ERR_PTR(-ESTALE);
+	if (is_dentry_obsolete(dentry))
+		goto out;
+
+	err = dget(sdi->dentry);
+	SUPERMOUNT_BUG_LOCKED_ON(sb, !err);
+	SUPERMOUNT_BUG_LOCKED_ON(sb, (dentry->d_inode == 0) ^ (err->d_inode == 0));
+
+out:
+	subfs_unlock(sb);
+
+	LEAVE(sb, "dentry=%s subd=%p", dentry->d_name.name, err);
+
+	return err;
+}
+
+static int
+supermount_d_revalidate(struct dentry *dentry, struct nameidata *nd)
+{
+	struct super_block *sb = dentry->d_sb;
+	struct dentry *subd;
+	int rc;
+	struct vfsmount *mnt;
+
+	ENTER(sb, "dentry=%s", dentry->d_name.name);
+
+	rc = 0;
+	mnt = subfs_prevent_umount(sb);
+	if (!mnt)
+		goto out;
+	
+	subd = get_subfs_dentry(dentry);
+
+	if (IS_ERR(subd))
+		goto allow_umount;
+
+	rc = 1;
+	/* FIXME do we need to build proper subfs nd? */
+	if (subd->d_op && subd->d_op->d_revalidate)
+		rc = subd->d_op->d_revalidate(subd, nd);
+
+	dput(subd);
+allow_umount:
+	subfs_allow_umount(sb, mnt);
+out:
+
+	LEAVE(sb, "dentry=%s rc=%d", dentry->d_name.name, rc);
+
+	return rc;
+}
+
+static int
+supermount_d_hash(struct dentry *dentry, struct qstr *name)
+{
+	struct super_block *sb = dentry->d_sb;
+	struct dentry *subd;
+	int rc;
+	struct vfsmount *mnt;
+
+	ENTER(sb, "dentry=%s name=%s", dentry->d_name.name, name->name);
+
+	rc = -ENOMEDIUM;
+	mnt = subfs_prevent_umount(sb);
+	if (!mnt)
+		goto out;
+	
+	subd = get_subfs_dentry(dentry);
+	rc = PTR_ERR(subd);
+	if (IS_ERR(subd))
+		goto allow_umount;
+
+	rc = 0;
+	if (subd->d_op && subd->d_op && subd->d_op->d_hash)
+		rc = subd->d_op->d_hash(subd, name);
+
+	dput(subd);
+allow_umount:
+	subfs_allow_umount(sb, mnt);
+out:
+	LEAVE(sb, "dentry=%s rc=%d", dentry->d_name.name, rc);
+
+	return rc;
+}
+
+
+rwlock_t d_compare_lock = RW_LOCK_UNLOCKED;
+
+static int
+supermount_d_compare(struct dentry *dentry, struct qstr *name1, struct qstr *name2)
+{
+	struct super_block *sb = dentry->d_sb;
+	struct supermount_dentry_info *sdi;
+	struct dentry *subd;
+	int rc;
+
+	ENTER(sb, "dentry=%s name1=%s name2=%s", dentry->d_name.name, name1->name, name2->name);
+
+	rc = 1; /* fail by default */
+	sdi = dentry->d_fsdata;
+	SUPERMOUNT_BUG_ON(!sdi);
+
+	/*
+	 * HACK - FIXME
+	 * this protects against races with supermount_clean_dentries
+	 * I cannot use blocking calls (i.e. sbi->sem) here and it is not
+	 * protected by dcache_lock anymore
+	 */
+	read_lock(&d_compare_lock);
+	subd = sdi->dentry;
+	if (!subd) {
+		read_unlock(&d_compare_lock);
+		goto out;
+	}
+
+	if (subd->d_op && subd->d_op->d_compare)
+		rc = subd->d_op->d_compare(subd, name1, name2);
+	else { /* based on fs/dcache.c:d_lookup */
+		if (name1->len == name2->len)
+		     rc = memcmp(name1->name, name2->name, name1->len);
+	}
+	read_unlock(&d_compare_lock);
+
+out:
+	LEAVE(sb, "dentry=%s rc=%d", dentry->d_name.name, rc);
+	return rc;
+}
+
+static inline void
+handle_subdent(struct dentry *dentry)
+{
+	struct super_block *sb = dentry->d_sb;
+	struct vfsmount *mnt;
+	struct supermount_dentry_info *sdi = supermount_d(dentry);
+	struct dentry *subd = 0;
+
+	mnt = subfs_prevent_umount(sb);
+	if (!mnt)
+		goto out;
+
+	subfs_lock(sb);
+	if (sdi) {
+		subd = sdi->dentry;
+		list_del_init(&sdi->list);
+		sdi->dentry = 0;
+	}
+	subfs_unlock(sb);
+
+	if (subd)
+		dput(subd);
+
+	subfs_allow_umount(sb, mnt);
+out:
+	return;
+}
+
+/*
+ * in case of active dentry we must be sure subfs dentry is released
+ * before subfs inode to correctly maintain write state
+ */
+static void
+supermount_d_iput(struct dentry *dentry, struct inode *inode)
+{
+	struct super_block *sb = dentry->d_sb;
+
+	ENTER(sb, "dentry=%s inode=%p", dentry->d_name.name, inode);
+
+	handle_subdent(dentry);
+	iput(inode);
+
+	LEAVE(sb, "dentry=%s", dentry->d_name.name);
+}
+	
+
+/*
+ * this duplicated code is due to the lack of common "destroy" method
+ * for both negative and positive dentries
+ */
+static void
+supermount_d_release(struct dentry *dentry)
+{
+	struct super_block *sb = dentry->d_sb;
+	struct supermount_dentry_info *sdi = supermount_d(dentry);
+
+	ENTER(sb, "dentry=%s", dentry->d_name.name);
+
+	handle_subdent(dentry);
+	kfree(sdi);
+
+	LEAVE(sb, "dentry=%s", dentry->d_name.name);
+}
+
+struct dentry_operations supermount_dops = {
+	.d_revalidate	= supermount_d_revalidate,
+	.d_hash		= supermount_d_hash,
+	.d_compare	= supermount_d_compare,
+	.d_iput		= supermount_d_iput,
+	.d_release	= supermount_d_release,
+};
Index: linux-2.6.11-rc3-ck1/fs/supermount/file.c
===================================================================
--- linux-2.6.11-rc3-ck1.orig/fs/supermount/file.c	2005-02-02 00:05:35.421535248 +1100
+++ linux-2.6.11-rc3-ck1/fs/supermount/file.c	2005-02-03 21:47:35.781504615 +1100
@@ -0,0 +1,691 @@
+/*
+ *  linux/fs/supermount/file.c
+ *
+ *  Original version:
+ *      Copyright (C) 1995, 1997
+ *      Stephen Tweedie (sct@dcs.ed.ac.uk)
+ *
+ *      from
+ *
+ *      linux/fs/minix/dir.c
+ *      Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *      and
+ *
+ *      linux/fs/ext2/dir.c
+ *      Copyright (C) 1992, 1993, 1994, 1995  Remy Card
+ *
+ *  Rewriten for kernel 2.2 & 2.4. (C) 1999, 2000 Alexis Mikhailov
+ *                                    (alexis@abc.cap.ru)
+ *  Rewriten for kernel 2.4. (C) 2001 MandrakeSoft Inc.
+ *                                    Juan Quintela (quintela@mandrakesoft.com)
+ *  Rewritten for kernel 2.4.21 (C) 2003 Andrey Borzenkov
+ *                                       (arvidjaar@mail.ru)
+ *  Rewritten for kernel 2.5.70 (C) 2003 Andrey Borzenkov
+ *                                       (arvidjaar@mail.ru)
+ *
+ *  $Id: file.c,v 1.13.4.4 2003/07/13 14:52:43 bor Exp $
+ */
+
+#define S_DBG_TRACE_CURRENT S_DBG_TRACE_FILE
+#include "supermount.h"
+
+#define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)
+
+static inline int
+init_file_info(struct file *file, unsigned int fake)
+{
+	struct super_block *sb = file->f_dentry->d_sb;
+	struct supermount_file_info *sfi;
+	int rc;
+
+	ENTER(sb, "file=%p fake=%d", file, fake);
+
+	rc = 1;
+	sfi = kmalloc(sizeof(*sfi), GFP_KERNEL);
+	if (!sfi)
+		goto out;
+
+	memset(sfi, 0, sizeof(*sfi));
+
+	INIT_LIST_HEAD(&sfi->list);
+	sfi->host = file;
+	sfi->owner = current->pid;
+	sfi->vm_ops = 0;
+	sfi->file = 0;
+	sfi->fake = fake;
+	
+	file->f_supermount = sfi;
+
+	rc = 0;
+out:
+	LEAVE(sb, "file=%p fake=%d rc=%d", file, fake, rc);
+
+	return rc;
+}
+
+static inline void
+attach_subfs_file(struct file *file, struct file *subfile)
+{
+	struct super_block *sb = file->f_dentry->d_sb;
+	struct supermount_sb_info *sbi = supermount_sbi(sb);
+	struct supermount_file_info *sfi;
+
+	ENTER(sb, "file=%p subfile=%p", file, subfile);
+
+	sfi = supermount_f(file);
+	sfi->file = subfile;
+	list_add(&sfi->list, &sbi->s_files);
+
+	LEAVE(sb, "file=%p subfile=%p", file, subfile);
+}
+
+static inline int
+prepare_file(struct file *file, struct file *subfile)
+{
+	struct super_block *sb = file->f_dentry->d_sb;
+	int rc;
+
+	ENTER(sb, "file=%p subfile=%p", file, subfile);
+
+	subfs_lock(sb);
+
+	rc = -ENOMEDIUM;
+	if (!subfs_is_mounted(sb))
+		goto out;
+
+	rc = -ESTALE;
+	if (is_dentry_obsolete(file->f_dentry))
+		goto out;
+
+	rc = -ENOMEM;
+	if (init_file_info(file, 0))
+		goto out;
+
+	attach_subfs_file(file, subfile);
+	rc = 0;
+
+out:
+	subfs_unlock(sb);
+
+	LEAVE(sb, "file=%p subfile=%p", file, subfile);
+
+	return rc;
+}
+
+struct file *
+get_subfs_file(struct file *file)
+{
+	struct super_block *sb = file->f_dentry->d_sb;
+	struct supermount_file_info *sfi = supermount_f(file);
+	struct file *err;
+
+	ENTER(sb, "file=%p", file);
+
+	subfs_lock(sb);
+	
+	err = ERR_PTR(-ENOMEDIUM);
+	if (!subfs_is_mounted(sb))
+		goto out;
+
+	err = ERR_PTR(-ESTALE);
+	if (is_file_fake(file) || is_file_obsolete(file))
+		goto out;
+
+	err = sfi->file;
+	SUPERMOUNT_BUG_LOCKED_ON(sb, !err->f_dentry);
+	SUPERMOUNT_BUG_LOCKED_ON(sb, !err->f_dentry->d_inode);
+	get_file(err);
+
+out:
+	subfs_unlock(sb);
+
+	LEAVE(sb, "file=%p subfile=%p", file, err);
+
+	return err;
+}
+
+static loff_t
+supermount_llseek(struct file *file, loff_t offset, int origin)
+{
+	struct super_block *sb = file->f_dentry->d_sb;
+	struct file *subfile;
+	loff_t rc;
+
+	ENTER(sb, "file=%p offset=%lld origin=%d", file, offset, origin);
+
+	rc = -ESTALE;
+	if (subfs_check_disk_change(sb))
+		goto out;
+
+	subfile = get_subfs_file(file);
+	rc = PTR_ERR(subfile);
+	if (IS_ERR(subfile))
+		goto out;
+
+	if (subfile->f_op && subfile->f_op->llseek)
+		rc = subfile->f_op->llseek(subfile, offset, origin);
+	else
+		rc = default_llseek(subfile, offset, origin);
+	file->f_pos = subfile->f_pos;
+	file->f_version = subfile->f_version;
+
+	fput(subfile);
+out:
+	LEAVE(sb, "file=%p rc=%lld", file, rc);
+
+	return rc;
+}
+
+static ssize_t
+supermount_read(struct file *file, char *buf, size_t count, loff_t * ppos)
+{
+	struct super_block *sb = file->f_dentry->d_sb;
+	struct inode *inode = file->f_dentry->d_inode;
+	struct file *subfile;
+	int write_on = NEED_WRITE_ATIME(inode);
+	int rc;
+
+	ENTER(sb, "file=%p", file);
+
+	rc = -ESTALE;
+	if (subfs_check_disk_change(sb))
+		goto out;
+
+	subfile = get_subfs_file(file);
+	rc = PTR_ERR(subfile);
+	if (IS_ERR(subfile))
+		goto out;
+
+	rc = -EINVAL;
+	if (!subfile->f_op || !subfile->f_op->read)
+		goto put_subfile;
+
+	rc = subfs_get_access(inode, write_on);
+	if (rc)
+		goto put_subfile;
+
+	rc = subfile->f_op->read(subfile, buf, count, ppos);
+	subfs_put_access(inode, write_on);
+	if (rc < 0)
+		goto put_subfile;
+
+	inode->i_atime = subfile->f_dentry->d_inode->i_atime;
+
+	if (rc > 0)
+		file->f_pos = subfile->f_pos = *ppos;
+
+put_subfile:
+	fput(subfile);
+out:
+	LEAVE(sb, "file=%p rc=%d", file, rc);
+
+	return rc;
+}
+
+static ssize_t
+supermount_write(struct file *file, const char *buf,
+		 size_t count, loff_t * ppos)
+{
+	struct super_block *sb = file->f_dentry->d_sb;
+	struct file *subfile;
+	int rc;
+
+	ENTER(sb, "file=%p", file);
+
+	rc = -ESTALE;
+	if (subfs_check_disk_change(sb))
+		goto out;
+
+	subfile = get_subfs_file(file);
+	rc = PTR_ERR(subfile);
+	if (IS_ERR(subfile))
+		goto out;
+
+	rc = 0;
+	if (subfile->f_op && subfile->f_op->write)
+		rc = subfile->f_op->write(subfile, buf, count, ppos);
+	if (rc > 0) {
+		struct inode *subinode = subfile->f_dentry->d_inode;
+
+		file->f_pos = subfile->f_pos = *ppos;
+		file->f_mode = subfile->f_mode;
+		file->f_dentry->d_inode->i_size = subinode->i_size;
+		file->f_dentry->d_inode->i_blocks = subinode->i_blocks;
+		file->f_dentry->d_inode->i_mode = subinode->i_mode;
+		file->f_dentry->d_inode->i_ctime = subinode->i_ctime;
+		file->f_dentry->d_inode->i_mtime = subinode->i_mtime;
+	}
+
+	fput(subfile);
+out:
+	LEAVE(sb, "file=%p rc=%d", file, rc);
+
+	return rc;
+}
+
+int
+supermount_readdir(struct file *file, void *buf, filldir_t fill_fn)
+{
+	struct super_block *sb = file->f_dentry->d_sb;
+	struct inode *inode = file->f_dentry->d_inode;
+	struct file *subfile;
+	int write_on = NEED_WRITE_ATIME(inode);
+	int fake_readdir = 1;
+	int rc;
+
+	ENTER(sb, "file=%p", file);
+
+	rc = -ESTALE;
+	if (subfs_check_disk_change(sb))
+		goto out;
+
+	subfile = get_subfs_file(file);
+	rc = PTR_ERR(subfile);
+	if (IS_ERR(subfile))
+		goto out;
+
+	rc = -ENOTDIR;
+	if (!subfile->f_op || !subfile->f_op->readdir)
+		goto put_subfile;
+
+	rc = subfs_get_access(inode, write_on);
+	if (rc)
+		goto put_subfile;
+
+	/* FIXME should it go before get_access? */
+	fake_readdir = 0;
+	rc = subfile->f_op->readdir(subfile, buf, fill_fn);
+	subfs_put_access(inode, write_on);
+	if (rc)
+		goto put_subfile;
+
+	inode->i_atime = subfile->f_dentry->d_inode->i_atime;
+	file->f_pos = subfile->f_pos;
+
+put_subfile:
+	fput(subfile);
+out:
+	if (fake_readdir && is_file_fake(file)) {
+		/* cf. supermount_open */
+		rc = 0;
+	}
+	LEAVE(sb, "file=%p rc=%d fpos=%lld", file, rc, file->f_pos);
+
+	return rc;
+}
+
+static unsigned int
+supermount_poll(struct file *file, struct poll_table_struct *table)
+{
+	struct super_block *sb = file->f_dentry->d_sb;
+	struct file *subfile;
+	int rc;
+
+	ENTER(sb, "file=%p", file);
+
+	rc = -ESTALE;
+	if (subfs_check_disk_change(sb))
+		goto out;
+
+	subfile = get_subfs_file(file);
+	rc = PTR_ERR(subfile);
+	if (IS_ERR(subfile))
+		goto out;
+
+	rc = DEFAULT_POLLMASK;
+	if (subfile->f_op && subfile->f_op->poll)
+		rc = subfile->f_op->poll(subfile, table);
+
+	fput(subfile);
+out:
+	LEAVE(sb, "file=%p rc=%d", file, rc);
+
+	return rc;
+}
+
+static int
+supermount_ioctl(struct inode *inode, struct file *file,
+		 unsigned int cmd, unsigned long arg)
+{
+	struct super_block *sb = file->f_dentry->d_sb;
+	struct file *subfile;
+	struct inode *subinode;
+	int rc;
+
+	ENTER(sb, "file=%p cmd=%u arg=%lu", file, cmd, arg);
+
+	rc = -ESTALE;
+	if (subfs_check_disk_change(sb))
+		goto out;
+
+	subfile = get_subfs_file(file);
+	rc = PTR_ERR(subfile);
+	if (IS_ERR(subfile))
+		goto out;
+
+	rc = -ENOTTY;
+	subinode = subfile->f_dentry->d_inode;
+	if (subfile->f_op && subfile->f_op->ioctl)
+		rc = subfile->f_op->ioctl(subinode, subfile, cmd, arg);
+
+	/* flags may have been changed by ioctl */
+	if (!rc)
+		set_inode_flags(file->f_dentry->d_inode, subinode);
+
+	fput(subfile);
+out:
+	LEAVE(sb, "file=%p rc=%d", file, rc);
+
+	return rc;
+}
+
+int
+supermount_open(struct inode *inode, struct file *file)
+{
+	struct super_block *sb = inode->i_sb;
+	struct dentry *subdent;
+	struct file *subfile = 0;
+	struct vfsmount *submnt;
+	int write_on = file->f_mode & FMODE_WRITE;
+	int fake_open = 1;
+	int rc;
+
+	ENTER(sb, "inode=%p file=%p", inode, file);
+
+	rc = -ESTALE;
+	if (subfs_check_disk_change(sb))
+		goto out;
+
+	submnt = subfs_get_mnt(sb);
+	if (!submnt)
+		goto out;
+
+	subdent = get_subfs_dentry(file->f_dentry);
+	rc = PTR_ERR(subdent);
+	if (IS_ERR(subdent))
+		goto put_submnt;
+	
+	rc = subfs_get_access(inode, write_on);
+	if (rc)
+		goto put_subdent;
+
+	/*
+	 * the following is used to simplify error processing. dentry_open
+	 * automatically does mntput and dput in error case, this may result
+	 * in subfs being destroyed
+	 * We just make sure we need to do mntput exactly once here;
+	 * additionally it guards against accidental remounting of subfs
+	 * until we has cleaned up
+	 */
+	submnt = mntget(submnt);
+	subdent = dget(subdent);
+
+	subfile = dentry_open(subdent, submnt, file->f_flags);
+	rc = PTR_ERR(subfile);
+	if (IS_ERR(subfile))
+		goto put_access;
+	/*
+	 * no need to do extra mntput and dput, it is done automatically in
+	 * dentry_open on error
+	 */
+
+	rc = prepare_file(file, subfile);
+	if (rc)
+		goto put_subfile;
+
+	subfile->f_mode = file->f_mode;
+	/*
+	 * this is needed for mmap to work. In current model vm_area
+	 * is associated with superfile; we never explicitly call
+	 * any vm method with subfile as pointer. But many drivers
+	 * attach private structures to this field and mmap of special
+	 * files on supermount fs won't work without it
+	 */
+	file->private_data = subfile->private_data;
+	/*
+	 * we have real subfile now, do not fake anything
+	 */
+	fake_open = 0;
+
+	/*
+	 * Now get rid of extra mntget and dget
+	 */
+	goto put_subdent;
+
+	/*
+	 * error cleanup
+	 */
+
+put_subfile:
+	fput(subfile);
+	subfile = 0;
+put_access:
+	subfs_put_access(inode, write_on);
+put_subdent:
+	dput(subdent);
+put_submnt:
+	mntput(submnt);
+out:
+	if (fake_open && inode == sb->s_root->d_inode) {
+		/*
+		 * always appear to succeed for root open. It allows active
+		 * monitoring of mountpoint using FAM/dnotify and also is less
+		 * surprising for other programs
+		 */
+		rc = init_file_info(file, 1);
+		if (rc)
+			rc = -ENOMEM;
+	}
+	LEAVE(sb, "inode=%p file=%p rc=%d subfile=0x%p", inode, file, rc, subfile);
+
+	return rc;
+}
+
+static int
+supermount_flush(struct file *file)
+{
+	struct super_block *sb = file->f_dentry->d_sb;
+	struct file *subfile;
+	int fake_flush = 1;
+	int rc;
+
+	ENTER(sb, "file=%p", file);
+
+	rc = -ESTALE;
+	if (subfs_check_disk_change(sb))
+		goto out;
+
+	subfile = get_subfs_file(file);
+	rc = PTR_ERR(subfile);
+	if (IS_ERR(subfile))
+		goto out;
+
+	rc = 0;
+	fake_flush = 0;
+	if (subfile->f_op && subfile->f_op->flush)
+		rc = subfile->f_op->flush(subfile);
+
+	fput(subfile);
+out:
+	if (fake_flush && is_file_fake(file)) {
+		/* cf. supermount_open */
+		rc = 0;
+	}
+	LEAVE(sb, "file=%p rc=%d fake=%d", file, rc, fake_flush);
+
+	return rc;
+}
+
+/*
+ * if subfile is NULL it has already been released in supermount_clean_files
+ * together with adjusting open/write counters. Else we do it here.
+ *
+ * The reason is, it may be called long after media has been changed
+ * and we definitely do not want this function to mess up the
+ * new subfs state.
+ */
+static int
+supermount_release(struct inode *inode, struct file *file)
+{
+	struct file *subfile = 0;
+	struct super_block *sb = inode->i_sb;
+	struct supermount_file_info *sfi = file->f_supermount;
+
+	ENTER(sb, "inode=%p file=%p", inode, file);
+
+	subfs_lock(sb);
+	/*
+	 * FIXME
+	 * this sucks. But there does not seem to be any way
+	 * to distinguish between ENOMEM on _open (legitimate
+	 * case) and anything else (plain bug)
+	 */
+	if (sfi) {
+		list_del(&sfi->list);
+		subfile = sfi->file;
+		sfi->file = 0;
+	} else
+		supermount_warning(sb, "no supermount file info attached");
+	subfs_unlock(sb);
+
+	if (subfile) {
+#ifdef CONFIG_SUPERMOUNT_DEBUG
+		int bug = atomic_read(&subfile->f_count) != 1;
+#endif
+		fput(subfile);
+		subfs_put_access(inode, file->f_mode & FMODE_WRITE);
+		SUPERMOUNT_BUG_ON(bug);
+	}
+
+	if (sfi)
+		kfree(sfi);
+
+	LEAVE(sb, "inode=%p file=%p", inode, file);
+
+	return 0;
+
+}
+
+static int
+supermount_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+	struct super_block *sb = file->f_dentry->d_sb;
+	struct file *subfile;
+	int rc;
+
+	ENTER(sb, "file=%p dentry=%s sync=%d", file, dentry->d_name.name, datasync);
+
+	rc = -ESTALE;
+	if (subfs_check_disk_change(sb))
+		goto out;
+
+	subfile = get_subfs_file(file);
+	rc = PTR_ERR(subfile);
+	if (IS_ERR(subfile))
+		goto out;
+
+	rc = -EINVAL;
+	if (subfile->f_op && subfile->f_op->fsync)
+		rc = subfile->f_op->fsync(subfile, subfile->f_dentry, datasync);
+
+	fput(subfile);
+out:
+	ENTER(sb, "file=%p dentry=%s rc=%d", file, dentry->d_name.name, rc);
+
+	return rc;
+}
+
+static int
+supermount_fasync(int fd, struct file *file, int on)
+{
+	struct super_block *sb = file->f_dentry->d_sb;
+	struct file *subfile;
+	int rc;
+
+	ENTER(sb, "fd=%d file=%p on=%d", fd, file, on);
+
+	rc = -ESTALE;
+	if (subfs_check_disk_change(sb))
+		goto out;
+
+	subfile = get_subfs_file(file);
+	rc = PTR_ERR(subfile);
+	if (IS_ERR(subfile))
+		goto out;
+
+	rc = -EINVAL;
+	if (subfile->f_op && subfile->f_op->fasync)
+		rc = subfile->f_op->fasync(fd, subfile, on);
+
+	fput(subfile);
+out:
+	LEAVE(sb, "fd=%d file=%p rc=%d", fd, file, rc);
+
+	return rc;
+}
+
+static int
+supermount_lock(struct file *file, int cmd, struct file_lock *fl)
+{
+	struct super_block *sb = file->f_dentry->d_sb;
+	struct file *subfile;
+	int rc;
+
+	ENTER(sb, "file=%p cmd=%d", file, cmd);
+
+	rc = -ESTALE;
+	if (subfs_check_disk_change(sb))
+		goto out;
+
+	subfile = get_subfs_file(file);
+	rc = PTR_ERR(subfile);
+	if (IS_ERR(subfile))
+		goto out;
+
+	rc = 0;
+	if (subfile->f_op && subfile->f_op->lock)
+		rc = subfile->f_op->lock(subfile, cmd, fl);
+	else if (cmd == F_GETLK)
+		posix_test_lock(file, fl);
+
+	fput(subfile);
+out:
+	LEAVE(sb, "file=%p rc=%d", file, rc);
+
+	return rc;
+}
+
+/* Fixme:
+ * readv: easy, export churnk from vfs
+ * writev: easy, export churnk from vfs
+ * sendpage: only used for networking, not needed
+ * get_unmmapped_area: only used for devices, not needed
+ */
+
+struct file_operations supermount_dir_operations = {
+	.llseek		= supermount_llseek,
+	.read		= supermount_read,
+	.readdir	= supermount_readdir,
+	.ioctl		= supermount_ioctl,
+	.open		= supermount_open,
+	.flush		= supermount_flush,
+	.release	= supermount_release,
+	.fsync		= supermount_fsync,
+	.fasync		= supermount_fasync,
+};
+
+struct file_operations supermount_file_operations = {
+	.llseek		= supermount_llseek,
+	.read		= supermount_read,
+	.write		= supermount_write,
+	.poll		= supermount_poll,
+	.ioctl		= supermount_ioctl,
+	.mmap		= supermount_file_mmap, /* from filemap.c */
+	.open		= supermount_open,
+	.flush		= supermount_flush,
+	.release	= supermount_release,
+	.fsync		= supermount_fsync,
+	.fasync		= supermount_fasync,
+	.lock		= supermount_lock,
+};
Index: linux-2.6.11-rc3-ck1/fs/supermount/filemap.c
===================================================================
--- linux-2.6.11-rc3-ck1.orig/fs/supermount/filemap.c	2005-02-02 00:05:35.421535248 +1100
+++ linux-2.6.11-rc3-ck1/fs/supermount/filemap.c	2005-02-03 21:47:35.781504615 +1100
@@ -0,0 +1,182 @@
+/*
+ *  linux/fs/supermount/filemap.c
+ *
+ *  Initial version for kernel 2.4.21 (C) 2003 Andrey Borzenkov
+ *                                             (arvidjaar@mail.ru)
+ *
+ *  $Id: filemap.c,v 1.4.4.2 2004/01/14 20:28:01 bor Exp $
+ */
+
+#define S_DBG_TRACE_CURRENT S_DBG_TRACE_FILEMAP
+#include "supermount.h"
+
+/*
+ * Some rationale and justification for this file
+ *
+ * We play dirty tricks with mm management for mmaped files on supermount.
+ * Address space points to subinode but vm area is associated with superfile.
+ * When media change is detected, subinode together with all associated
+ * pages goes away as well (at least, I hope so ...) Now we must prevent
+ * any attempt to access no more existing address space. To do so we save
+ * original vm_ops in private field and replace them with vm_ops in this file.
+ * They check if file is stale, if yes, they try to return sensible error.
+ * There is some doubt about possibility to block here ... OTOH any write
+ * lock on subfs is hold in context where no mm lock is expected.
+ */
+static void
+supermount_vm_open(struct vm_area_struct *area)
+{
+	struct file *file = area->vm_file, *subfile;
+	struct super_block *sb = file->f_dentry->d_sb;
+	struct supermount_file_info *sfi;
+
+	ENTER(sb, "vm=%p", area);
+
+	if (subfs_check_disk_change(sb))
+		goto out;
+
+	subfile = get_subfs_file(file);
+	if (IS_ERR(subfile))
+		goto out;
+
+	sfi = supermount_f(file);
+	if (sfi->vm_ops && sfi->vm_ops->open)
+		sfi->vm_ops->open(area);
+
+	fput(subfile);
+out:
+	LEAVE(sb, "vm=%p", area);
+
+	return;
+}
+
+static void
+supermount_vm_close(struct vm_area_struct *area)
+{
+	struct file *file = area->vm_file, *subfile;
+	struct super_block *sb = file->f_dentry->d_sb;
+	struct supermount_file_info *sfi;
+
+	ENTER(sb, "vm=%p", area);
+	
+	if (subfs_check_disk_change(sb))
+		goto out;
+
+	subfile = get_subfs_file(file);
+	if (IS_ERR(subfile))
+		goto out;
+
+	sfi = supermount_f(file);
+	if (sfi->vm_ops && sfi->vm_ops->close)
+		sfi->vm_ops->close(area);
+
+	fput(subfile);
+out:
+	LEAVE(sb, "vm=%p", area);
+
+	return;
+}
+
+static struct page *
+supermount_vm_nopage(struct vm_area_struct *area, unsigned long address, int *type)
+{
+	struct file *file = area->vm_file, *subfile;
+	struct super_block *sb = file->f_dentry->d_sb;
+	struct supermount_file_info *sfi;
+	struct page *page = 0;
+
+	ENTER(sb, "vm=%p addr=%lx", area, address);
+	
+	/*
+	 * this is called with mm semaphore down read and pagetable
+	 * spinlock released. So it _appears_ safe to sleep ...
+	 */
+	if (subfs_check_disk_change(sb))
+		goto out;
+
+	subfile = get_subfs_file(file);
+	if (IS_ERR(subfile))
+		goto out;
+
+	sfi = supermount_f(file);
+	page = sfi->vm_ops->nopage(area, address, type);
+
+	fput(subfile);
+out:
+	LEAVE(sb, "vm=%p page=%p", area, page);
+
+	return page;
+}
+
+int
+supermount_file_mmap(struct file *file, struct vm_area_struct *vma)
+{
+
+	struct super_block *sb = file->f_dentry->d_sb;
+	struct inode *inode = file->f_dentry->d_inode;
+	struct supermount_file_info *sfi;
+	struct file *subfile;
+	int write_on = NEED_WRITE_ATIME(inode);
+	int rc;
+
+	ENTER(sb, "file=%p vm=%p", file, vma);
+	
+	rc = -ESTALE;
+	if (subfs_check_disk_change(sb))
+		goto out;
+
+	subfile = get_subfs_file(file);
+	rc = PTR_ERR(subfile);
+	if (IS_ERR(subfile))
+		goto out;
+
+	sfi = supermount_f(file);
+
+	rc = -ENODEV;
+	if (!subfile->f_op || !subfile->f_op->mmap)
+		goto put_subfile;
+
+	rc = subfs_get_access(inode, write_on);
+	if (rc)
+		goto put_subfile;
+
+	rc = subfile->f_op->mmap(subfile, vma);
+	subfs_put_access(inode, write_on);
+	if (rc)
+		goto put_subfile;
+	/*
+	 * we cannot deal with anonymous mapping
+	 */
+	if (!vma->vm_ops || !vma->vm_ops->nopage) {
+		rc = -ENOSYS;
+		goto put_subfile;
+	}
+
+	/*
+	 * now do the nasty trick
+	 */
+	sfi->vm_ops = vma->vm_ops;
+	vma->vm_ops = &supermount_vm_ops;
+
+put_subfile:
+	fput(subfile);
+out:
+	LEAVE(sb, "file=%p vm=%p rc=%d", file, vma, rc);
+
+	return rc;
+}
+
+/*
+ * ->populate:	to properly implement it it should call do_no_page if
+ *  		subfs does not provide its own ->populate. Unfortunately
+ *  		populate gets different parameters. Translating them into
+ *  		what ->nopage expects just does not worth it - currently
+ *  		supermount is slow enough to bother about this imaginary
+ *  		speed up even if it was possible (which probably is not
+ *  		due to how locking is done; see mm/memory.c).
+ */
+struct  vm_operations_struct supermount_vm_ops = {
+	.open		= supermount_vm_open,
+	.close		= supermount_vm_close,
+	.nopage		= supermount_vm_nopage,
+};
Index: linux-2.6.11-rc3-ck1/fs/supermount/init.c
===================================================================
--- linux-2.6.11-rc3-ck1.orig/fs/supermount/init.c	2005-02-02 00:05:35.421535248 +1100
+++ linux-2.6.11-rc3-ck1/fs/supermount/init.c	2005-02-03 21:47:35.782504451 +1100
@@ -0,0 +1,48 @@
+/* 
+ * linux/fs/supermount/init.c
+ *
+ *  (C) Copyright 2001-2002 Juan Quintela <quintela@mandrakesoft.com>
+ *      Released unde GPL v2.
+ *  Rewritten for kernel 2.4.21 (C) 2003 Andrey Borzenkov
+ *                                       (arvidjaar@mail.ru)
+ *  Rewritten for kernel 2.5.70 (C) 2003 Andrey Borzenkov
+ *                                       (arvidjaar@mail.ru)
+ *
+ *  $Id: init.c,v 1.6.4.4 2003/10/26 16:21:25 bor Exp $
+ */
+
+#include "supermount.h"
+
+struct file_system_type supermount_fs_type = {
+        .owner          = THIS_MODULE,
+        .name           = "supermount",
+        .get_sb         = supermount_get_sb,
+        .kill_sb        = kill_anon_super,
+        .fs_flags       = FS_NO_SUBMNT,
+};
+
+static int __init
+init_supermount_fs(void)
+{
+	int rc = register_filesystem(&supermount_fs_type);
+	
+	if (!rc) {
+		printk(KERN_INFO "Supermount version %s for kernel 2.6\n", SUPERMOUNT_VERSION);
+		supermount_proc_register();
+	}
+
+	return rc;
+}
+
+static void __exit
+exit_supermount_fs(void)
+{
+	supermount_proc_unregister();
+	unregister_filesystem(&supermount_fs_type);
+}
+
+MODULE_AUTHOR("Stephen Tweedie, Alexis Mikhailov, Juan Quintela, Andrey Borzenkov and others");
+MODULE_DESCRIPTION("Transparent removable media support");
+MODULE_LICENSE("GPL");
+module_init(init_supermount_fs);
+module_exit(exit_supermount_fs);
Index: linux-2.6.11-rc3-ck1/fs/supermount/Kconfig
===================================================================
--- linux-2.6.11-rc3-ck1.orig/fs/supermount/Kconfig	2005-02-02 00:05:35.421535248 +1100
+++ linux-2.6.11-rc3-ck1/fs/supermount/Kconfig	2005-02-03 21:47:35.782504451 +1100
@@ -0,0 +1,26 @@
+config SUPERMOUNT
+	tristate "Supermount removable media support"
+	help
+	  Supermount gives you the ability to access CD-ROMs and Floppies
+	  without mounting/unmounting them every time you want to access
+	  a different disk/floppy. Just eject the media, insert a new one
+	  and you are able to access it.
+
+	  Read Documentation/filesystems/supermount.txt for more information.
+
+	  If you want to compile the Supermount support as a module ( = 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 supermount.o.
+
+	  If unsure, say N.
+
+config SUPERMOUNT_DEBUG
+	bool "Enable supermount debug code"
+	depends on SUPERMOUNT
+	help
+	  If you set this to Y, additional debug code will be compiled in.
+	  Debug output is controlled with debug=N mount option. Possible
+	  values are listed in Documentation/filesystems/supermount.txt.
+
+	  If unsure, say Y.
Index: linux-2.6.11-rc3-ck1/fs/supermount/Makefile
===================================================================
--- linux-2.6.11-rc3-ck1.orig/fs/supermount/Makefile	2005-02-02 00:05:35.421535248 +1100
+++ linux-2.6.11-rc3-ck1/fs/supermount/Makefile	2005-02-03 21:47:35.782504451 +1100
@@ -0,0 +1,15 @@
+#
+# Makefile for the linux supermounting routines.
+#
+
+supermount-objs :=	dentry.o \
+			file.o \
+			filemap.o \
+			init.o \
+			mediactl.o \
+			namei.o \
+			proc.o \
+			subfs.o \
+			super.o 
+
+obj-$(CONFIG_SUPERMOUNT) += supermount.o
Index: linux-2.6.11-rc3-ck1/fs/supermount/mediactl.c
===================================================================
--- linux-2.6.11-rc3-ck1.orig/fs/supermount/mediactl.c	2005-02-02 00:05:35.421535248 +1100
+++ linux-2.6.11-rc3-ck1/fs/supermount/mediactl.c	2005-02-03 21:47:35.782504451 +1100
@@ -0,0 +1,69 @@
+/*
+ *  linux/fs/supermount/mediactl.c
+ *
+ *  Original version:
+ *      Copyright (C) 1995, 1997
+ *      Stephen Tweedie (sct@dcs.ed.ac.uk)
+ *
+ *  Rewriten for kernel 2.2, 2.4. (C) 1999, 2000 Alexis Mikhailov
+ *                                    (alexis@abc.cap.ru)
+ *  Rewritten for kernel 2.4.21 (C) 2003 Andrey Borzenkov
+ *                                       (arvidjaar@mail.ru)
+ *  Rewritten for kernel 2.5.70 (C) 2003 Andrey Borzenkov
+ *                                       (arvidjaar@mail.ru)
+ *
+ *  $Id: mediactl.c,v 1.5.2.5 2003/07/13 19:15:20 bor Exp $
+ */
+
+#define S_DBG_TRACE_CURRENT S_DBG_TRACE_MEDIACTL
+#include "supermount.h"
+
+/* 
+ * Try to lock the drive door.  This is not guaranteed to work on all
+ * hardware, but we try any tricks we know of anyway.  
+ */
+void
+supermount_mediactl(struct super_block *sb, int operation, int opt)
+{
+	struct block_device *bdev;
+	struct super_block *subsb;
+	struct block_device_operations *fops = 0;
+
+	if (!subfs_is_mounted(sb))
+		return;
+
+	subsb = subfs_sb(sb);
+	bdev = subsb->s_bdev;
+	if (!bdev->bd_disk)
+		return;
+
+	/* FIXME is it enough to use bd_sem here? */
+	switch (operation) {
+		case SUPERMOUNT_INC_COUNT:
+			lock_kernel();
+			SUPERMOUNT_BUG_ON(bdev->bd_disk->scount < 0);
+			bdev->bd_disk->scount++;
+			unlock_kernel();
+			return;
+		case SUPERMOUNT_DEC_COUNT:
+			lock_kernel();
+			SUPERMOUNT_BUG_ON(bdev->bd_disk->scount <= 0);
+			bdev->bd_disk->scount--;
+			unlock_kernel();
+			return;
+	}
+
+	fops = bdev->bd_disk->fops;
+	if (!fops || !fops->mediactl)
+		return;
+	/*
+	 * tray is (un-)locked in open (BKL for bdev), release (BKL for bdev)
+	 * and ioctl (BKL). We are in good company.
+	 * This must be changed if block devices ever stop using BKL
+	 * for open/release. Unfortunately, using just bdev->bd_sem is not
+	 * enough due to ioctl.
+	 */
+	lock_kernel();
+	fops->mediactl(bdev, operation, opt);
+	unlock_kernel();
+}
Index: linux-2.6.11-rc3-ck1/fs/supermount/namei.c
===================================================================
--- linux-2.6.11-rc3-ck1.orig/fs/supermount/namei.c	2005-02-02 00:05:35.421535248 +1100
+++ linux-2.6.11-rc3-ck1/fs/supermount/namei.c	2005-02-03 21:47:35.783504288 +1100
@@ -0,0 +1,1143 @@
+/*
+ *  linux/fs/supermount/namei.c
+ *
+ *  Original version:
+ *      Copyright (C) 1995
+ *      Stephen Tweedie (sct@dcs.ed.ac.uk)
+ *
+ *      from
+ *
+ *      linux/fs/minix/namei.c
+ *      Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *      and
+ *
+ *      linux/fs/ext2/namei.c
+ *      Copyright (C) 1992, 1993, 1994, 1995  Remy Card
+ *
+ *  Rewriten for kernel 2.2 & 2.4. (C) 1999, 2000 Alexis Mikhailov
+ *                                    (alexis@abc.cap.ru)
+ *  Rewritten for kernel 2.4.21 (C) 2003 Andrey Borzenkov
+ *                                       (arvidjaar@mail.ru)
+ *  Rewritten for kernel 2.5.70 (C) 2003 Andrey Borzenkov
+ *                                       (arvidjaar@mail.ru)
+ *
+ *  $Id: namei.c,v 1.21.2.11 2004/01/04 15:05:03 bor Exp $
+ */
+
+#define S_DBG_TRACE_CURRENT S_DBG_TRACE_NAMEI
+#include "supermount.h"
+
+/**
+ * Attach super dentry to sub dentry 
+ *
+ * @dentry:	super dentry that is being created
+ * @subd:	subfs dentry that just has been found
+ *
+ * It checks whether subfs is still valid using @dentry->d_parent;
+ * new supermount_dentry_info is created and set to point to @subd.
+ *
+ * It is possible that @subd actually has different name (ntfs, vfat or
+ * in general any case-insensitive filesystems). Search @dentry->d_parent
+ * for a child matching == @subd->d_name. If found, discard @dentry and child
+ * (after some validity checks) is returned. Check that child actually points
+ * to @subd
+ *
+ * d_lookup relies on the fact that hash is properly initialized in
+ * @subd->d_name and that superfs is using the same compare method as subfs.
+ *
+ * About ref counting. @subd is dput in supermount_lookup. I.e. in case of
+ * error or if we find out it is already connected to superfs the excessive
+ * counter gets decremented. @dentry is finally dput in caller of ->lookup
+ * if ->lookup returns something != 0.
+ */
+
+static struct dentry *
+prepare_dentry(struct dentry *dentry, struct dentry *subd)
+{
+	struct super_block *sb = dentry->d_sb;
+	struct dentry *rc;
+
+	ENTER(sb, "dentry=%s subd=%s", dentry->d_name.name, subd->d_name.name);
+
+	subfs_lock(sb);
+
+	SUPERMOUNT_BUG_LOCKED_ON(sb, !subd);
+	SUPERMOUNT_BUG_LOCKED_ON(sb, dentry->d_fsdata);
+
+	rc = ERR_PTR(-ENOMEDIUM);
+	if (!subfs_is_mounted(sb))
+		goto out;
+
+	rc = ERR_PTR(-ESTALE);
+	if (is_dentry_obsolete(dentry->d_parent))
+		goto out;
+
+	rc = d_lookup(dentry->d_parent, &subd->d_name);
+	if (IS_ERR(rc))
+		goto out;
+
+	if (rc) {
+		SUPERMOUNT_BUG_LOCKED_ON(sb, !rc->d_fsdata);
+		SUPERMOUNT_BUG_LOCKED_ON(sb,
+		((struct supermount_dentry_info *)rc->d_fsdata)->dentry != subd);
+	} else {
+		/*
+		 * this is theoretically possible. We cannot garantee full
+		 * coherency between subfs and superfs cache; i.e. entry
+		 * may have been left in one cache but removed from another
+		 */
+		rc = ERR_PTR(-ENOMEM);
+		if (init_dentry_info(dentry))
+			goto out;
+		attach_subfs_dentry(dentry, subd);
+		d_add(dentry, 0);
+		rc = 0;
+	}
+
+out:
+	subfs_unlock(sb);
+
+	LEAVE(sb, "dentry=%p subd=%p rc=%p", dentry, subd, rc);
+
+	return rc;
+	/*
+	 * subdent is implicitly freed on return if we skip dget here
+	 */
+}
+
+
+/**
+ * Attach superfs inode to subfs inode 
+ *
+ * @dentry:	superfs dentry
+ * @subd:	subfs dentry
+ *
+ * This is expected to be called only in cointext that requires
+ * negative dentry.
+ *
+ * FIXME
+ * Holding sbi->sem during iget4 creates deadlock with write_inode -
+ * write_inode sets I_LOCK abd calls supermount_write_inode at the
+ * same moment as iget4 sleeps waiting for I_LOCK to be cleared. So
+ * it first acquires inode and then checks if subfs is still valid.
+ */
+
+static int
+prepare_inode(struct dentry *dentry, struct dentry *subd)
+{
+	struct super_block *sb = dentry->d_sb;
+	struct inode *inode = dentry->d_inode;
+	struct inode *subi = subd->d_inode;
+	int rc;
+
+	ENTER(sb, "dentry=%s", dentry->d_name.name);
+
+	SUPERMOUNT_BUG_ON(inode);
+	SUPERMOUNT_BUG_ON(!subi);
+
+	rc = -ENOMEM;
+	inode = iget_locked(sb, subi->i_ino);
+	if (!inode)
+		goto out;
+	else if (inode->i_state & I_NEW)
+		unlock_new_inode(inode);
+
+	rc = 0;
+
+	subfs_lock(sb);
+	if (!subfs_is_mounted(sb))
+		rc = -ENOMEDIUM;
+	else if (is_dentry_obsolete(dentry))
+		rc = -ESTALE;
+	else {
+		attach_subfs_inode(inode, subi);
+		d_instantiate(dentry, inode);
+	}
+	subfs_unlock(sb);
+
+	if (rc)
+		iput(inode);
+
+out:
+	LEAVE(sb, "dentry=%s inode=%p rc=%d", dentry->d_name.name, inode, rc);
+
+	return rc;
+}
+
+struct inode *
+get_subfs_inode(struct inode *inode)
+{
+	struct super_block *sb = inode->i_sb;
+	struct inode *err;
+	struct supermount_inode_info *sii = supermount_i(inode);
+
+	ENTER(sb, "inode=%p", inode);
+
+	subfs_lock(sb);
+
+	err = ERR_PTR(-ENOMEDIUM);
+	if (!subfs_is_mounted(sb))
+		goto out;
+
+	err = ERR_PTR(-ESTALE);
+	if (is_inode_obsolete(inode))
+		goto out;
+
+	err = igrab(sii->inode);
+	SUPERMOUNT_BUG_LOCKED_ON(sb, !err);
+
+out:
+	subfs_unlock(sb);
+
+	LEAVE(sb, "inode=%p subi=%p", inode, err);
+
+	return err;
+}
+
+/* inode methods */
+
+static int
+supermount_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd)
+{
+	struct super_block *sb = dir->i_sb;
+	struct dentry *subdent;
+	struct inode *subdir;
+	int rc;
+	struct vfsmount *mnt;
+
+	ENTER(sb, "dir=%p dentry=%s", dir, dentry->d_name.name);
+
+	mnt = subfs_go_online(sb);
+	rc = PTR_ERR(mnt);
+	if (IS_ERR(mnt))
+		goto out;
+
+	subdir = get_subfs_inode(dir);
+	rc = PTR_ERR(subdir);
+	if (IS_ERR(subdir))
+		goto go_offline;
+
+	rc = -EACCES;
+	if (!subdir->i_op || !subdir->i_op->create)
+		goto put_subdir;
+	
+	subdent = get_subfs_dentry(dentry);
+	rc = PTR_ERR(subdent);
+	if (IS_ERR(subdent))
+		goto put_subdir;
+
+	rc = subfs_get_access(dir, 1);
+	if (rc)
+		goto put_subdent;
+
+	/* FIXME build proper nd struct */
+	rc = subdir->i_op->create(subdir, subdent, mode, nd);
+	subfs_put_access(dir, 1);
+	if (rc)
+		goto put_subdent;
+
+	rc = prepare_inode(dentry, subdent);
+	if (rc)
+		goto put_subdent;
+
+	dir->i_mtime = subdir->i_mtime;
+	dir->i_ctime = subdir->i_ctime;
+	dir->i_nlink = subdir->i_nlink;
+	dir->i_size = subdir->i_size;
+	dir->i_blocks = subdir->i_blocks;
+
+put_subdent:
+	dput(subdent);
+put_subdir:
+	iput(subdir);
+go_offline:
+	subfs_go_offline(sb, mnt);
+out:
+	LEAVE(sb, "dir=%p dentry=%s rc=%d", dir, dentry->d_name.name, rc);
+
+	return rc;
+}
+
+/**
+ * Search directory for a matching name
+ *
+ * @dir:	directory that is being searched
+ * @dentry:	name to search for (in dentry->d_name)
+ *
+ * This is (currently :) the only method where we do not call subfs
+ * directly. The reason is coherency between subfs and superfs dentry
+ * caches. It is impossible to ensure it without modifying the very
+ * guts of fs/dcache.c; so we check cache before doing actual lookup.
+ * lookup_one_len just avoids duplicating of code.
+ *
+ * Supermount is in exclusive control of subfs so it is garanteed that
+ * dentry cannot magically appear in the middle of lookup.
+ *
+ * There are filesystems that support multiple forms of file name, like
+ * case-insensitive or short-long names on NTFS. In this case cache lookup
+ * fails but filesystem may return dentry for different name. In this case
+ * we check if dentry with matching name exists in parent and reuse it.
+ */
+static struct dentry *
+supermount_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
+{
+	struct super_block *sb = dir->i_sb;
+	struct dentry *rc, *subdent, *subparent;
+	struct inode *subdir;
+	struct vfsmount *mnt;
+	int ret;
+
+	ENTER(sb, "dir=%p dentry=%s", dir, dentry->d_name.name);
+
+	mnt = subfs_go_online(sb);
+	rc = (struct dentry *)mnt;
+	if (IS_ERR(mnt))
+		goto out;
+
+	subdir = get_subfs_inode(dir);
+	rc = (struct dentry *)subdir;
+	if (IS_ERR(subdir))
+		goto go_offline;
+
+	subparent = get_subfs_dentry(dentry->d_parent);
+	rc = subparent;
+	if (IS_ERR(subparent))
+		goto put_subdir;
+
+	ret = subfs_get_access(dir, 0);
+	rc = ERR_PTR(ret);
+	if (ret)
+		goto put_subparent;
+
+	SUPERMOUNT_BUG_ON(subparent->d_inode != subdir);
+
+	/* FIXME usually lookup_one_len is called under i_sem */
+	/* FIXME what to do with nd? */
+	subdent = lookup_one_len(dentry->d_name.name, subparent,
+				 dentry->d_name.len);
+
+	subfs_put_access(dir, 0);
+	rc = subdent;
+	if (IS_ERR(rc))
+		goto put_subparent;
+
+	rc = prepare_dentry(dentry, subdent);
+	if (IS_ERR(rc))
+		goto put_subdent;
+
+	if (!rc && subdent->d_inode) {
+		ret = prepare_inode(dentry, subdent);
+		if (ret < 0)
+			rc = ERR_PTR(ret);
+	}
+
+put_subdent:
+	dput(subdent);
+put_subparent:
+	dput(subparent);
+put_subdir:
+	iput(subdir);
+go_offline:
+	subfs_go_offline(sb, mnt);
+out:
+	LEAVE(sb, "dir=%p dentry=%s rc=%p", dir, dentry->d_name.name, rc);
+
+	return rc;
+}
+
+static int
+supermount_link(struct dentry *old_dentry, struct inode *dir,
+		struct dentry *new_dentry)
+{
+	struct super_block *sb = dir->i_sb;
+	struct dentry *old_subdent, *new_subdent;
+	struct inode *subdir;
+	int rc;
+	struct vfsmount *mnt;
+
+	ENTER(sb, "from=%s dir=%p to=%s", old_dentry->d_name.name, dir, new_dentry->d_name.name);
+
+	SUPERMOUNT_BUG_ON(new_dentry->d_inode);
+
+	mnt = subfs_go_online(sb);
+	rc = PTR_ERR(mnt);
+	if (IS_ERR(mnt))
+		goto out;
+
+	subdir = get_subfs_inode(dir);
+	rc = PTR_ERR(subdir);
+	if (IS_ERR(subdir))
+		goto go_offline;
+
+	rc = -EPERM;
+	if (!subdir->i_op || !subdir->i_op->link)
+		goto put_subdir;
+
+	old_subdent = get_subfs_dentry(old_dentry);
+	rc = PTR_ERR(old_subdent);
+	if (IS_ERR(old_subdent))
+		goto put_subdir;
+
+	new_subdent = get_subfs_dentry(new_dentry);
+	rc = PTR_ERR(new_subdent);
+	if (IS_ERR(new_subdent))
+		goto put_old_subdent;
+
+	rc = subfs_get_access(dir, 1);
+	if (rc)
+		goto put_new_subdent;
+
+	rc = subdir->i_op->link(old_subdent, subdir, new_subdent);
+	subfs_put_access(dir, 1);
+	if (rc)
+		goto put_new_subdent;
+
+	rc = prepare_inode(new_dentry, new_subdent);
+	if (rc)
+		goto put_new_subdent;
+
+	dir->i_mtime = subdir->i_mtime;
+	dir->i_ctime = subdir->i_ctime;
+	dir->i_nlink = subdir->i_nlink;
+	dir->i_size = subdir->i_size;
+	dir->i_blocks = subdir->i_blocks;
+
+put_new_subdent:
+	dput(new_subdent);
+put_old_subdent:
+	dput(old_subdent);
+put_subdir:
+	iput(subdir);
+go_offline:
+	subfs_go_offline(sb, mnt);
+out:
+	LEAVE(sb, "from=%s dir=%p to=%s rc=%d", old_dentry->d_name.name, dir, new_dentry->d_name.name, rc);
+
+	return rc;
+}
+
+static int
+supermount_unlink(struct inode *dir, struct dentry *dentry)
+{
+	struct super_block *sb = dir->i_sb;
+	struct dentry *subdent;
+	struct inode *subdir;
+	int rc;
+	struct vfsmount *mnt;
+
+	ENTER(sb, "dir=%p dentry=%s", dir, dentry->d_name.name);
+
+	mnt = subfs_go_online(sb);
+	rc = PTR_ERR(mnt);
+	if (IS_ERR(mnt))
+		goto out;
+
+	subdir = get_subfs_inode(dir);
+	rc = PTR_ERR(subdir);
+	if (IS_ERR(subdir))
+		goto go_offline;
+
+	rc = -EPERM;
+	if (!subdir->i_op || !subdir->i_op->unlink)
+		goto put_subdir;
+
+	subdent = get_subfs_dentry(dentry);
+	rc = PTR_ERR(subdent);
+	if (IS_ERR(subdent))
+		goto put_subdir;
+
+	/*
+	 * below is not a typo. We have to mark _deleted_ inode
+	 * for possible later delete in clear_inode
+	 */
+	rc = subfs_get_access(dentry->d_inode, 1);
+	if (rc)
+		goto put_subdent;
+
+	rc = subdir->i_op->unlink(subdir, subdent);
+	if (rc)
+		goto put_write_access;
+
+	dir->i_mtime = subdir->i_mtime;
+	dir->i_ctime = subdir->i_ctime;
+	dir->i_nlink = subdir->i_nlink;
+	dir->i_size = subdir->i_size;
+	dir->i_blocks = subdir->i_blocks;
+
+	dentry->d_inode->i_nlink = subdent->d_inode->i_nlink;
+	dentry->d_inode->i_ctime = subdent->d_inode->i_ctime;
+
+put_write_access:
+	/*
+	 * we can't put write access if there are pending deletes
+	 * so we leave it on and let it be put in clear_inode for
+	 * i_nlink == 0
+	 *
+	 * While i_nlink is believed to be atomic here i_count not,
+	 * so we cannot check && i_count == 0. It is expected that
+	 * deleted files are kept open only rarely.
+	 */
+	if (dentry->d_inode->i_nlink)
+		subfs_put_access(dentry->d_inode, 1);
+put_subdent:
+	dput(subdent);
+put_subdir:
+	iput(subdir);
+go_offline:
+	subfs_go_offline(sb, mnt);
+out:
+	LEAVE(sb, "dir=%p dentry=%s rc=%d", dir, dentry->d_name.name, rc);
+
+	return rc;
+}
+
+static int
+supermount_symlink(struct inode *dir, struct dentry *dentry,
+		   const char *symname)
+{
+	struct super_block *sb = dir->i_sb;
+	struct dentry *subdent;
+	struct inode *subdir;
+	int rc;
+	struct vfsmount *mnt;
+
+	ENTER(sb, "dir=%p dentry=%s", dir, dentry->d_name.name);
+
+	mnt = subfs_go_online(sb);
+	rc = PTR_ERR(mnt);
+	if (IS_ERR(mnt))
+		goto out;
+
+	subdir = get_subfs_inode(dir);
+	rc = PTR_ERR(subdir);
+	if (IS_ERR(subdir))
+		goto go_offline;
+
+	rc = -EPERM;
+	if (!subdir->i_op || !subdir->i_op->symlink)
+		goto put_subdir;
+
+	subdent = get_subfs_dentry(dentry);
+	rc = PTR_ERR(subdent);
+	if (IS_ERR(subdent))
+		goto put_subdir;
+
+	rc = subfs_get_access(dir, 1);
+	if (rc)
+		goto put_subdent;
+
+	rc = subdir->i_op->symlink(subdir, subdent, symname);
+	subfs_put_access(dir, 1);
+	if (rc)
+		goto put_subdent;
+
+	rc = prepare_inode(dentry, subdent);
+	if (rc)
+		goto put_subdent;
+
+	dir->i_mtime = subdir->i_mtime;
+	dir->i_ctime = subdir->i_ctime;
+	dir->i_nlink = subdir->i_nlink;
+	dir->i_size = subdir->i_size;
+	dir->i_blocks = subdir->i_blocks;
+
+put_subdent:
+	dput(subdent);
+put_subdir:
+	iput(subdir);
+go_offline:
+	subfs_go_offline(sb, mnt);
+out:
+	LEAVE(sb, "dir=%p dentry=%s rc=%d", dir, dentry->d_name.name, rc);
+
+	return rc;
+}
+
+static int
+supermount_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+	struct super_block *sb = dir->i_sb;
+	struct dentry *subdent;
+	struct inode *subdir;
+	int rc;
+	struct vfsmount *mnt;
+
+	ENTER(sb, "dir=%p dentry=%s", dir, dentry->d_name.name);
+
+	mnt = subfs_go_online(sb);
+	rc = PTR_ERR(mnt);
+	if (IS_ERR(mnt))
+		goto out;
+
+	subdir = get_subfs_inode(dir);
+	rc = PTR_ERR(subdir);
+	if (IS_ERR(subdir))
+		goto go_offline;
+
+	rc = -EPERM;
+	if (!subdir->i_op || !subdir->i_op->mkdir)
+		goto put_subdir;
+
+	subdent = get_subfs_dentry(dentry);
+	rc = PTR_ERR(subdent);
+	if (IS_ERR(subdent))
+		goto put_subdir;
+
+	rc = subfs_get_access(dir, 1);
+	if (rc)
+		goto put_subdent;
+
+	rc = subdir->i_op->mkdir(subdir, subdent, mode);
+	subfs_put_access(dir, 1);
+	if (rc)
+		goto put_subdent;
+
+	rc = prepare_inode(dentry, subdent);
+	if (rc)
+		goto put_subdent;
+
+	dir->i_mtime = subdir->i_mtime;
+	dir->i_ctime = subdir->i_ctime;
+	dir->i_nlink = subdir->i_nlink;
+	dir->i_size = subdir->i_size;
+	dir->i_blocks = subdir->i_blocks;
+
+put_subdent:
+	dput(subdent);
+put_subdir:
+	iput(subdir);
+go_offline:
+	subfs_go_offline(sb, mnt);
+out:
+	LEAVE(sb, "dir=%p dentry=%s rc=%d", dir, dentry->d_name.name, rc);
+
+	return rc;
+}
+
+static int
+supermount_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	struct super_block *sb = dir->i_sb;
+	struct dentry *subdent;
+	struct inode *subdir;
+	int rc;
+	struct vfsmount *mnt;
+
+	ENTER(sb, "dir=%p dentry=%s", dir, dentry->d_name.name);
+
+	mnt = subfs_go_online(sb);
+	rc = PTR_ERR(mnt);
+	if (IS_ERR(mnt))
+		goto out;
+
+	subdir = get_subfs_inode(dir);
+	rc = PTR_ERR(subdir);
+	if (IS_ERR(subdir))
+		goto go_offline;
+
+	rc = -EPERM;
+	if (!subdir->i_op || !subdir->i_op->rmdir)
+		goto put_subdir;
+
+	subdent = get_subfs_dentry(dentry);
+	rc = PTR_ERR(subdent);
+	if (IS_ERR(subdent))
+		goto put_subdir;
+
+	/* cf. supermount_unlink */
+	rc = subfs_get_access(dentry->d_inode, 1);
+	if (rc)
+		goto put_subdent;
+
+	rc = subdir->i_op->rmdir(subdir, subdent);
+	if (rc)
+		goto put_write_access;
+
+	dir->i_mtime = subdir->i_mtime;
+	dir->i_ctime = subdir->i_ctime;
+	dir->i_nlink = subdir->i_nlink;
+	dir->i_size = subdir->i_size;
+	dir->i_blocks = subdir->i_blocks;
+
+	/* hmm ... hard links to directories are not allowed, are they? */
+	dentry->d_inode->i_nlink = subdent->d_inode->i_nlink;
+	dentry->d_inode->i_ctime = subdent->d_inode->i_ctime;
+
+put_write_access:
+	/* cf. supermount_unlink */
+	if (dentry->d_inode->i_nlink)
+		subfs_put_access(dentry->d_inode, 1);
+put_subdent:
+	dput(subdent);
+put_subdir:
+	iput(subdir);
+go_offline:
+	subfs_go_offline(sb, mnt);
+out:
+	LEAVE(sb, "dir=%p dentry=%s rc=%d", dir, dentry->d_name.name, rc);
+
+	return rc;
+}
+
+static int
+supermount_mknod(struct inode *dir, struct dentry *dentry, int
+		 mode, dev_t dev)
+{
+	struct super_block *sb = dir->i_sb;
+	struct dentry *subdent;
+	struct inode *subdir;
+	int rc;
+	struct vfsmount *mnt;
+
+	ENTER(sb, "dir=%p dentry=%s", dir, dentry->d_name.name);
+
+	mnt = subfs_go_online(sb);
+	rc = PTR_ERR(mnt);
+	if (IS_ERR(mnt))
+		goto out;
+
+	subdir = get_subfs_inode(dir);
+	rc = PTR_ERR(subdir);
+	if (IS_ERR(subdir))
+		goto go_offline;
+
+	rc = -EPERM;
+	if (!subdir->i_op || !subdir->i_op->mknod)
+		goto put_subdir;
+
+	subdent = get_subfs_dentry(dentry);
+	rc = PTR_ERR(subdent);
+	if (IS_ERR(subdent))
+		goto put_subdir;
+
+	rc = subfs_get_access(dir, 1);
+	if (rc)
+		goto put_subdent;
+
+	rc = subdir->i_op->mknod(subdir, subdent, mode, dev);
+	subfs_put_access(dir, 1);
+	if (rc)
+		goto put_subdent;
+
+	rc = prepare_inode(dentry, subdent);
+	if (rc)
+		goto put_subdent;
+
+	dir->i_mtime = subdir->i_mtime;
+	dir->i_ctime = subdir->i_ctime;
+	dir->i_nlink = subdir->i_nlink;
+	dir->i_size = subdir->i_size;
+	dir->i_blocks = subdir->i_blocks;
+
+put_subdent:
+	dput(subdent);
+put_subdir:
+	iput(subdir);
+go_offline:
+	subfs_go_offline(sb, mnt);
+out:
+	LEAVE(sb, "dir=%p dentry=%s rc=%d", dir, dentry->d_name.name, rc);
+
+	return rc;
+}
+
+static int
+supermount_rename(struct inode *olddir, struct dentry *olddentry,
+		  struct inode *newdir, struct dentry *newdentry)
+{
+	struct super_block *sb = olddir->i_sb;
+	struct dentry *oldsubdent, *newsubdent;
+	struct inode *oldsubdir, *newsubdir;
+	int rc;
+	struct vfsmount *mnt;
+
+	ENTER(sb, "olddir=%p olddentry=%s newdir=%p newdentry=%s", olddir, olddentry->d_name.name, newdir, newdentry->d_name.name);
+
+	mnt = subfs_go_online(sb);
+	rc = PTR_ERR(mnt);
+	if (IS_ERR(mnt))
+		goto out;
+
+	oldsubdir = get_subfs_inode(olddir);
+	rc = PTR_ERR(oldsubdir);
+	if (IS_ERR(oldsubdir))
+		goto go_offline;
+
+	rc = -EPERM;
+	if (!oldsubdir->i_op || !oldsubdir->i_op->rename)
+		goto put_old_subdir;
+
+	oldsubdent = get_subfs_dentry(olddentry);
+	rc = PTR_ERR(oldsubdent);
+	if (IS_ERR(oldsubdent))
+		goto put_old_subdir;
+
+	newsubdir = get_subfs_inode(newdir);
+	rc = PTR_ERR(newsubdir);
+	if (IS_ERR(newsubdir))
+		goto put_old_subdent;
+
+	newsubdent = get_subfs_dentry(newdentry);
+	rc = PTR_ERR(newsubdent);
+	if (IS_ERR(newsubdent))
+		goto put_new_subdir;
+
+	/*
+	 * If new file exists it will be implcitly unlinked so
+	 * behave like in unlink case.
+	 * If it does not exist we have two write accesses - for
+	 * both old and new directory, I guess it does not matter
+	 * which one is used in this case
+	 */
+	if (newdentry->d_inode)
+		rc = subfs_get_access(newdentry->d_inode, 1);
+	else
+		rc = subfs_get_access(olddir, 1);
+	if (rc)
+		goto put_new_subdent;
+
+	rc = oldsubdir->i_op->rename(oldsubdir, oldsubdent,
+				     newsubdir, newsubdent);
+	if (rc)
+		goto put_write_access;
+
+	/*
+	 * this is strictly speaking conditional on FS_ODD_RENAME
+	 * flag, but as of this writing this flag is set only
+	 * for NFS or intermezzo and it hopefully goes away sometimes ...
+	 *
+	 * Supermount patch adds code to do_kern_mount that checks
+	 * for this flag and refuses mounting
+	 */
+	d_move(oldsubdent, newsubdent);
+
+	olddir->i_mtime = oldsubdir->i_mtime;
+	olddir->i_ctime = oldsubdir->i_ctime;
+	olddir->i_nlink = oldsubdir->i_nlink;
+	olddir->i_size = oldsubdir->i_size;
+	olddir->i_blocks = oldsubdir->i_blocks;
+
+	newdir->i_mtime = newsubdir->i_mtime;
+	newdir->i_ctime = newsubdir->i_ctime;
+	newdir->i_nlink = newsubdir->i_nlink;
+	newdir->i_size = newsubdir->i_size;
+	newdir->i_blocks = newsubdir->i_blocks;
+
+	olddentry->d_inode->i_nlink = oldsubdent->d_inode->i_nlink;
+	olddentry->d_inode->i_ctime = oldsubdent->d_inode->i_ctime;
+
+	if (newdentry->d_inode) {
+		newdentry->d_inode->i_nlink = newsubdent->d_inode->i_nlink;
+		newdentry->d_inode->i_ctime = newsubdent->d_inode->i_ctime;
+	}
+
+put_write_access:
+	if (newdentry->d_inode) {
+		/* cf. supermount_unlink */
+		if (newdentry->d_inode->i_nlink)
+			subfs_put_access(newdentry->d_inode, 1);
+	} else
+		subfs_put_access(olddir, 1);
+put_new_subdent:
+	dput(newsubdent);
+put_new_subdir:
+	iput(newsubdir);
+put_old_subdent:
+	dput(oldsubdent);
+put_old_subdir:
+	iput(oldsubdir);
+go_offline:
+	subfs_go_offline(sb, mnt);
+out:
+	LEAVE(sb, "olddentry=%s newdentry=%s rc=%d", olddentry->d_name.name, newdentry->d_name.name, rc);
+
+	return rc;
+}
+
+static int 
+supermount_readlink(struct dentry *dentry, char *buffer , int buflen)
+{
+	struct super_block *sb = dentry->d_sb;
+	struct inode *inode = dentry->d_inode;
+	struct dentry *subdent;
+	int write_on = NEED_WRITE_ATIME(inode);
+	int rc;
+	struct vfsmount *mnt;
+
+	ENTER(sb, "dentry=%s", dentry->d_name.name);
+
+	mnt = subfs_go_online(sb);
+	rc = PTR_ERR(mnt);
+	if (IS_ERR(mnt))
+		goto out;
+
+	subdent = get_subfs_dentry(dentry);
+	rc = PTR_ERR(subdent);
+	if (IS_ERR(subdent))
+		goto go_offline;
+
+	rc = -EINVAL;
+	if (!subdent->d_inode->i_op || !subdent->d_inode->i_op->readlink)
+		goto put_subdent;
+
+	rc = subfs_get_access(inode, write_on);
+	if (rc)
+		goto put_subdent;
+
+	update_atime(subdent->d_inode);
+	rc = subdent->d_inode->i_op->readlink(subdent, buffer, buflen);
+	subfs_put_access(inode, write_on);
+	inode->i_atime = subdent->d_inode->i_atime;
+
+put_subdent:
+	dput(subdent);
+go_offline:
+	subfs_go_offline(sb, mnt);
+out:
+	LEAVE(sb, "dentry=%s rc=%d", dentry->d_name.name, rc);
+
+	return rc;
+}
+
+static int
+supermount_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+	struct super_block *sb = dentry->d_sb;
+	struct inode *inode = dentry->d_inode;
+	struct dentry *subdent;
+	int write_on = NEED_WRITE_ATIME(inode);
+	int rc;
+	struct vfsmount *mnt;
+	
+	ENTER(sb, "dentry=%s", dentry->d_name.name);
+
+	mnt = subfs_go_online(sb);
+	rc = PTR_ERR(mnt);
+	if (IS_ERR(mnt))
+		goto out;
+
+	subdent = get_subfs_dentry(dentry);
+	rc = PTR_ERR(subdent);
+	if (IS_ERR(subdent))
+		goto go_offline;
+
+	rc = subfs_get_access(inode, write_on);
+	if (rc)
+		goto put_subdent;
+
+	update_atime(subdent->d_inode);
+	/* FIXME do we need proper subfs nd here? */
+	rc = subdent->d_inode->i_op->follow_link(subdent, nd);
+	subfs_put_access(inode, write_on);
+	inode->i_atime = subdent->d_inode->i_atime;
+
+put_subdent:
+	dput(subdent);	
+go_offline:
+	subfs_go_offline(sb, mnt);
+out:
+	LEAVE(sb, "dentry=%s rc=%d", dentry->d_name.name, rc);
+
+	return rc;
+}
+
+static int
+supermount_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+	struct super_block *sb = inode->i_sb;
+	struct inode *subi;
+	int rc;
+	int write_on = !IS_RDONLY(inode) && (mask & MAY_WRITE);
+	struct vfsmount *mnt;
+	int fake_permissions = 1;
+
+	ENTER(sb, "inode=%p", inode);
+
+	mnt = subfs_go_online(sb);
+	rc = PTR_ERR(mnt);
+	if (IS_ERR(mnt))
+		goto out;
+
+	subi = get_subfs_inode(inode);
+	rc = PTR_ERR(subi);
+	if (IS_ERR(subi))
+		goto go_offline;
+
+	rc = subfs_get_access(inode, write_on);
+	if (rc)
+		goto put_subi;
+
+	fake_permissions = 0;
+	/* FIXME do we need proper subfs nd here */
+	if (subi->i_op && subi->i_op->permission)
+		rc = subi->i_op->permission(subi, mask, nd);
+	else
+		rc = generic_permission(subi, mask, NULL);
+
+	subfs_put_access(inode, write_on);
+
+put_subi:
+	iput(subi);
+go_offline:
+	subfs_go_offline(sb, mnt);
+out:
+	if (fake_permissions && inode == sb->s_root->d_inode) {
+		/* cf. file.c:supermount_open() */
+		rc = generic_permission(inode, mask, NULL);
+	}
+
+	LEAVE(sb, "inode=%p rc=%d fake=%d", inode, rc, fake_permissions);
+
+	return rc;
+}
+
+static int
+supermount_setattr(struct dentry *dentry, struct iattr *attr)
+{
+	struct super_block *sb = dentry->d_sb;
+	struct inode *subi;
+	struct dentry *subdent;
+	int rc;
+	struct vfsmount *mnt;
+
+	ENTER(sb, "dentry=%s", dentry->d_name.name);
+
+	mnt = subfs_go_online(sb);
+	rc = PTR_ERR(mnt);
+	if (IS_ERR(mnt))
+		goto out;
+
+	subdent = get_subfs_dentry(dentry);
+	rc = PTR_ERR(subdent);
+	if (IS_ERR(subdent))
+		goto go_offline;
+
+	rc = subfs_get_access(dentry->d_inode, 1);
+	if (rc)
+		goto put_subdent;
+
+	subi = subdent->d_inode;
+	if (subi->i_op && subi->i_op->setattr)
+		rc = subi->i_op->setattr(subdent, attr);
+	else {
+		rc = inode_change_ok(subi, attr);
+		/*
+		 * FIXME
+		 * What to do with quota?
+		 */
+		if (!rc)
+			rc = inode_setattr(subi, attr);
+	}
+	subfs_put_access(dentry->d_inode, 1);
+	if (rc)
+		goto put_subdent;
+
+	/* 
+	 * If it worked, then we need to mark the modification
+	 * to the subfs, and we also need to propogate the
+	 * change up to the shadowing inode.  
+	 */
+	attr->ia_mode = subi->i_mode;
+	attr->ia_uid = subi->i_uid;
+	attr->ia_gid = subi->i_gid;
+	attr->ia_size = subi->i_size;
+	attr->ia_atime = subi->i_atime;
+	attr->ia_mtime = subi->i_mtime;
+	attr->ia_ctime = subi->i_ctime;
+	attr->ia_valid =
+	    ATTR_UID | ATTR_GID | ATTR_MODE | ATTR_SIZE |
+	    ATTR_ATIME | ATTR_MTIME | ATTR_CTIME;
+	rc = inode_setattr(dentry->d_inode, attr);
+
+put_subdent:
+	dput(subdent);
+go_offline:
+	subfs_go_offline(sb, mnt);
+out:
+	LEAVE(sb, "dentry=%s rc=%d", dentry->d_name.name, rc);
+
+	return rc;
+}
+
+static int
+supermount_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
+{
+	struct super_block *sb = dentry->d_sb;
+	struct vfsmount *submnt;
+        struct dentry *subdent = 0;
+        int rc;
+
+	ENTER(sb, "dentry=%s", dentry->d_name.name);
+
+	/*
+	 * do not use subfs_go_online - it will result in ls /mnt
+	 * attempting to mount all supermounted directories
+	 */
+	submnt = subfs_prevent_umount(sb);
+	if (submnt)
+		subdent = get_subfs_dentry(dentry);
+
+	/*
+	 * do not fail stat for stale files - it is needed to
+	 * make sure fuser -m /mnt/cdrom lists all processes still
+	 * having any (obsolete) file open
+	 */
+        if (submnt && subdent && !IS_ERR(subdent)) {
+                rc = vfs_getattr(submnt, subdent, stat);
+		stat->dev = sb->s_dev;
+	} else {
+		subfs_lock(sb);
+		generic_fillattr(dentry->d_inode, stat);
+		subfs_unlock(sb);
+		rc = 0;
+	}
+
+	if (subdent && !IS_ERR(subdent))
+		dput(subdent);
+	if (submnt)
+		subfs_allow_umount(sb, submnt);
+
+	LEAVE(sb, "dentry=%s rc=%d", dentry->d_name.name, rc);
+
+	return rc;
+}
+
+/*
+ * directories can handle most operations...  supermount/namei.c just
+ * passes them through to the underlying subfs, except for lookup().
+ */
+
+/* truncate: is not necesary, handled with setattr
+ * revalidate: only needed by nfs
+ * ->getattr:	FIXME is not appeared to be used anywhere in kernel; so I am
+ *  		not sure how to implement it or what to return
+ * FIXME: implement accl functions
+ */
+
+struct inode_operations supermount_dir_iops = {
+	.create		= supermount_create,
+	.lookup		= supermount_lookup,
+	.link		= supermount_link,
+	.unlink		= supermount_unlink,
+	.symlink	= supermount_symlink,
+	.mkdir		= supermount_mkdir,
+	.rmdir		= supermount_rmdir,
+	.mknod		= supermount_mknod,
+	.rename		= supermount_rename,
+	.permission	= supermount_permission,
+	.setattr	= supermount_setattr,
+	.getattr	= supermount_getattr,
+};
+
+struct inode_operations supermount_symlink_iops = {
+	.readlink	= supermount_readlink,
+	.follow_link	= supermount_follow_link,
+	.setattr	= supermount_setattr,
+	.getattr	= supermount_getattr,
+};
+
+struct inode_operations supermount_file_iops = {
+	.setattr	= supermount_setattr,
+	.getattr	= supermount_getattr,
+};
Index: linux-2.6.11-rc3-ck1/fs/supermount/proc.c
===================================================================
--- linux-2.6.11-rc3-ck1.orig/fs/supermount/proc.c	2005-02-02 00:05:35.421535248 +1100
+++ linux-2.6.11-rc3-ck1/fs/supermount/proc.c	2005-02-03 21:47:35.784504125 +1100
@@ -0,0 +1,289 @@
+/*
+ *  linux/fs/supermount/proc.c
+ *
+ *  Initial version for kernel 2.4.21 (C) 2003 Andrey Borzenkov
+ *                                             (arvidjaar@mail.ru)
+ *  Rewritten for kernel 2.5.70 (C) 2003 Andrey Borzenkov
+ *                                       (arvidjaar@mail.ru)
+ *
+ *  $Id: proc.c,v 1.7.4.4 2003/10/26 16:21:25 bor Exp $
+ */
+
+#include "supermount.h"
+
+#ifdef CONFIG_PROC_FS
+
+/*
+ * we use semaphore because we have to lock subfs inside and it can
+ * sleep. Unlocking procfs list would mean adding generic usage count
+ * management to sbi and this is far too fetching
+ */
+
+static DECLARE_MUTEX(supermount_proc_sem);
+static struct proc_dir_entry *supermount_proc_root;
+static struct proc_dir_entry *supermount_proc_subfs;
+static struct proc_dir_entry *supermount_proc_version;
+static struct supermount_sb_info *supermount_list_head;
+
+#define SKIP_BLANKS(s)	while(*s == ' ' || *s == '\t' || *s == '\n') s++
+#define CHECK_COUNT do { \
+	size += n; \
+	count -= n; \
+	if (count <= 0) { \
+		subfs_unlock(sbi->host); \
+		break; \
+	} \
+} while (0)
+
+/* iterator; shamelessly copied over from pci.c */
+static void *supermount_seq_start(struct seq_file *sf, loff_t *pos)
+{
+        struct supermount_sb_info *sbi;
+        loff_t n = *pos;
+
+	down(&supermount_proc_sem);
+
+        sbi = supermount_list_head;
+        while (n-- && sbi)
+                sbi = sbi->next;
+
+        return sbi;
+}
+
+static void *supermount_seq_next(struct seq_file *sf, void *v, loff_t *pos)
+{
+        struct supermount_sb_info *sbi = v;
+        (*pos)++;
+        return sbi->next;
+}
+
+static void supermount_seq_stop(struct seq_file *sf, void *v)
+{
+	up(&supermount_proc_sem);
+}
+
+
+static int
+supermount_show_sbi(struct seq_file *sf, void *v)
+{
+	struct supermount_sb_info *sbi = v;
+
+
+	subfs_lock(sbi->host);
+	seq_puts(sf, sbi->devname);
+	if (sbi->disabled)
+		seq_puts(sf, " disabled\n");
+	else if (subfs_is_mounted(sbi->host))
+		seq_printf(sf, " mounted %d %d\n",
+				sbi->readcount, sbi->writecount);
+	else
+		seq_puts(sf, " unmounted\n");
+	subfs_unlock(sbi->host);
+
+	return 0;
+}
+
+static struct seq_operations supermount_proc_subfs_op = {
+        start:  supermount_seq_start,
+        next:   supermount_seq_next,
+        stop:   supermount_seq_stop,
+        show:   supermount_show_sbi
+};
+
+static int supermount_proc_subfs_open(struct inode *inode, struct file *file)
+{
+        return seq_open(file, &supermount_proc_subfs_op);
+}
+
+/*
+ * mostly copied over from drivers/scsi/scsi.c:proc_scsi_gen_write()
+ */
+static int
+supermount_proc_subfs_write(struct file *file, const char *buf, size_t length, loff_t *offset)
+{
+	char *buffer, *s, *dev = 0;
+	int disable = 0, enable = 0, release = 0, force = 0;
+	struct supermount_sb_info *sbi;
+	size_t rc;
+
+	rc = -EINVAL;
+	if (!buf || length > PAGE_SIZE)
+		goto out;
+
+	rc = -ENOMEM;
+	if (!(s = buffer = (char *)__get_free_page(GFP_KERNEL)))
+		goto out;
+
+	rc =-EFAULT;
+	if(copy_from_user(buffer, buf, length))
+		goto free_buffer;
+
+	rc = -EINVAL;
+	if (length < PAGE_SIZE)
+		buffer[length] = '\0';
+	else if (buffer[PAGE_SIZE-1])
+		goto free_buffer;
+
+	/*
+	 * echo "/dev/cdrom [enable|disable] [release [force]]" > \
+	 * 				/proc/fs/supermount/subfs
+	 */
+
+	do {
+		char *p;
+
+		SKIP_BLANKS(s);
+		p = strpbrk(s, " \t\n");
+		if (p)
+			*p++ = '\0';
+		if (!dev)
+			dev = s;
+		else if (!strcmp(s, "disable"))
+			disable = 1;
+		else if (!strcmp(s, "enable"))
+			enable = 1;
+		else if (!strcmp(s, "release"))
+			release = 1;
+		else if (!strcmp(s, "force"))
+			force = 1;
+		else
+			goto free_buffer;
+
+		s = p;
+	} while (s && *s);
+
+	if ((enable && disable) || (force && !release))
+		goto free_buffer;
+
+	down(&supermount_proc_sem);
+	for(sbi = supermount_list_head; sbi; sbi = sbi->next) {
+		if (strcmp(sbi->devname, dev))
+			continue;
+
+		subfs_lock(sbi->host);
+
+		rc = length;
+		if (release && subfs_is_mounted(sbi->host)) {
+			if (!subfs_is_busy(sbi->host) || force)
+				subfs_umount(sbi->host, SUBFS_UMNT_USER);
+			else
+				rc = -EBUSY;
+		}
+		
+		if (disable && subfs_is_mounted(sbi->host))
+			rc = -EBUSY;
+
+		if (rc >= 0) {
+			if (disable)
+				sbi->disabled = 1;
+			else if (enable)
+				sbi->disabled = 0;
+		}
+
+		subfs_unlock(sbi->host);
+		break;
+	}
+	up(&supermount_proc_sem);
+
+free_buffer:
+	free_page((unsigned long)buffer);
+out:
+	return rc;
+
+}
+
+static struct file_operations supermount_proc_subfs_operations = {
+        open:           supermount_proc_subfs_open,
+        read:           seq_read,
+        llseek:         seq_lseek,
+        release:        seq_release,
+	write:		supermount_proc_subfs_write,
+};
+
+static int
+supermount_proc_version_read(char *page, char **start, off_t pos, int count, int *eof, void *data)
+{
+	int rc;
+
+	rc = snprintf(page, count, "Supermount version %s for kernel 2.6\n",
+				    SUPERMOUNT_VERSION);
+	*eof = 1;
+	return rc;
+}
+
+void
+supermount_proc_register(void)
+{
+	supermount_proc_root = proc_mkdir("fs/supermount", 0);
+	if (!supermount_proc_root) {
+		printk(KERN_ERR "SUPERMOUNT failed to create /proc/fs/supermount");
+		return;
+	}
+	supermount_proc_root->owner = THIS_MODULE;
+
+	supermount_proc_version = create_proc_read_entry("version",
+                                        S_IFREG | S_IRUGO,
+                                        supermount_proc_root,
+					supermount_proc_version_read,
+					0);
+
+	if (supermount_proc_version)
+		supermount_proc_version->owner = THIS_MODULE;
+	else
+		printk(KERN_ERR
+		"SUPERMOUNT failed to create /proc/fs/supermount/version");
+
+	supermount_proc_subfs = create_proc_entry("subfs",
+                                        S_IFREG | S_IRUGO | S_IWUSR,
+                                        supermount_proc_root);
+
+	if (supermount_proc_subfs) {
+		supermount_proc_subfs->proc_fops =
+			&supermount_proc_subfs_operations;
+		supermount_proc_subfs->owner = THIS_MODULE;
+	} else
+		printk(KERN_ERR
+		"SUPERMOUNT failed to create /proc/fs/supermount/subfs");
+
+}
+
+void
+supermount_proc_unregister(void)
+{
+	remove_proc_entry("fs/supermount/subfs", 0);
+	remove_proc_entry("fs/supermount/version", 0);
+	remove_proc_entry("fs/supermount", 0);
+}
+
+void
+supermount_proc_insert(struct supermount_sb_info *sbi)
+{
+
+	down(&supermount_proc_sem);
+
+	sbi->next = supermount_list_head;
+	supermount_list_head = sbi;
+
+	up(&supermount_proc_sem);
+}
+
+void
+supermount_proc_remove(struct supermount_sb_info *sbi)
+{
+	struct supermount_sb_info **p, *q;
+
+	down(&supermount_proc_sem);
+
+	for(p = &supermount_list_head, q = supermount_list_head;
+			q; p = &q->next, q = q->next)
+		if (q == sbi)
+			break;
+
+	if (q)
+		*p = q->next;
+
+	up(&supermount_proc_sem);
+
+	SUPERMOUNT_BUG_ON(!q);
+}
+#endif /* CONFIG_PROC_FS */
Index: linux-2.6.11-rc3-ck1/fs/supermount/subfs.c
===================================================================
--- linux-2.6.11-rc3-ck1.orig/fs/supermount/subfs.c	2005-02-02 00:05:35.421535248 +1100
+++ linux-2.6.11-rc3-ck1/fs/supermount/subfs.c	2005-02-03 21:47:35.785503962 +1100
@@ -0,0 +1,742 @@
+/*
+ *  linux/fs/supermount/subfs.c
+ *
+ *  Original version:
+ *      Copyright (C) 1995, 1997
+ *      Stephen Tweedie (sct@dcs.ed.ac.uk)
+ *
+ *      from
+ *
+ *      linux/fs/minix/inode.c
+ *      Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *      and
+ *
+ *      linux/fs/ext2/super.c
+ *      Copyright (C) 1992, 1993, 1994, 1995  Remy Card
+ *
+ *  Rewriten for kernel 2.2, 2.4. (C) 1999, 2000 Alexis Mikhailov
+ *                                    (alexis@abc.cap.ru)
+ *  Rewritten for kernel 2.4.21 (C) 2003 Andrey Borzenkov
+ *                                       (arvidjaar@mail.ru)
+ *  Rewritten for kernel 2.5.70 (C) 2003 Andrey Borzenkov
+ *                                       (arvidjaar@mail.ru)
+ *
+ *  $Id: subfs.c,v 1.29.2.11 2004/01/14 19:24:10 bor Exp $
+ */
+
+#define S_DBG_TRACE_CURRENT S_DBG_TRACE_SUBFS
+#include "supermount.h"
+
+/*
+ * close all open files on subfs
+ */
+static void
+supermount_clean_files(struct super_block *sb)
+{
+	struct supermount_sb_info *sbi = supermount_sbi(sb);
+	struct list_head *ptr, *n;
+
+	ENTER(sb);
+
+	list_for_each_safe(ptr, n, &sbi->s_files) {
+		struct supermount_file_info *sfi;
+		struct file *subfile;
+
+		sfi = list_entry(ptr, struct supermount_file_info, list);
+
+		subfile = sfi->file;
+		sfi->file = 0;
+		list_del_init(&sfi->list);
+
+		SUPERMOUNT_BUG_LOCKED_ON(sb, !subfile);
+
+		fput(subfile);
+	}
+
+	LEAVE(sb);
+
+}
+
+/*
+ * close all open dentries on subfs
+ */
+static void
+supermount_clean_dentries(struct super_block *sb)
+{
+	struct supermount_sb_info *sbi = supermount_sbi(sb);
+	struct list_head *ptr, *n;
+
+	ENTER(sb);
+
+	list_for_each_safe(ptr, n, &sbi->s_dentries) {
+		struct supermount_dentry_info *sdi;
+		struct dentry *subdent;
+
+		sdi = list_entry(ptr, struct supermount_dentry_info, list);
+		/*
+		 * HACK - FIXME
+		 * see dentry.c:supermount_d_compare
+		 */
+		write_lock(&d_compare_lock);
+		subdent = sdi->dentry;
+		sdi->dentry = 0;
+		write_unlock(&d_compare_lock);
+
+		list_del_init(&sdi->list);
+		d_drop(sdi->host);
+		dnotify_parent(sdi->host, DN_DELETE);
+
+		SUPERMOUNT_BUG_LOCKED_ON(sb, !subdent);
+
+		dput(subdent);
+	}
+
+	LEAVE(sb);
+
+}
+
+/*
+ * close all open inodes on subfs
+ */
+static void
+supermount_clean_inodes(struct super_block *sb)
+{
+	struct supermount_sb_info *sbi = supermount_sbi(sb);
+	struct list_head *ptr, *n;
+
+	ENTER(sb);
+
+	list_for_each_safe(ptr, n, &sbi->s_inodes) {
+		struct supermount_inode_info *sii;
+		struct inode *host;
+		struct inode *subi;
+
+		sii = list_entry(ptr, struct supermount_inode_info, list);
+		host = &sii->vfs_inode;
+
+		subi = sii->inode;
+		sii->inode = NULL;
+		list_del_init(&sii->list);
+		remove_inode_hash(host);
+		host->i_mapping = &host->i_data;
+
+		/*
+		 * it is possible to have no subi here. clear_inode does
+		 * release lock after removing subi but before unlinking
+		 * it
+		 */
+		if (subi)
+			iput(subi);
+
+		SUPERMOUNT_BUG_LOC