GoboLinux Downloads Documentation Community Recipes Screenshots

GoboLinux Recipe & Package Search Tool

71 versions of Linux.

ProgramAgeSizeByWWWSummary
Linux 4.13.2-r1 791  88794 Luca...
The Linux Kernel.
Linux 4.9.16-r3 962  100651 Luca...
The Linux Kernel.
Linux 4.9.4-r5 1018  99374 Luca...
The Linux Kernel.
Linux 4.8.2-r2 1084  89394 Luca...
The Linux Kernel.
Linux 4.7.4-r1 1154  82767 Luca...
The Linux Kernel.
Linux 4.7.0-r4 1209  82585 Luca...
The Linux Kernel.
Linux 3.13.3-r1 2089  252629 Luca...
The Linux Kernel.
Linux 3.12.6-r1 2149  238949 Luca...
The Linux Kernel.
Linux 3.9.4-r2 2344  70048 Luca...
The Linux Kernel.
Linux 3.7.1-r2 2517  67579 Luca...
The Linux Kernel.
Linux 3.5.0-r1 2668  124391 Luca...
The Linux Kernel.
Linux 3.4.4-r1 2668  124348 Luca...
The Linux Kernel.
Linux 3.3.6-r1 2668  124410 Luca...
The Linux Kernel.
Linux 3.2.12-r2 2796  124345 Luca...
The Linux Kernel.
Linux 3.2.7-r1 2826  123550 Mich...
The Linux Kernel.
Linux 3.1.1-r1 2931  122907 Mich...
The Linux Kernel.
Linux 3.0.4-r4 2992  122754 Luca...
The Linux Kernel.
Linux 2.6.36.3-r1 3215  116087 Diog...
The Linux Kernel.
Linux 2.6.32.3-r1 3593  117990 Luca...
The Linux Kernel.
Linux 2.6.32-r1 3634  117751 Luca...
The Linux Kernel.
Linux 2.6.31.6-r3 3639  126499 Luca...
The Linux Kernel.
Linux 2.6.30.5-r1 3718  166102 Jona...
The Linux Kernel.
Linux 2.6.29.1-r1 3861  117500 Luca...
The Linux Kernel.
Linux 2.6.28.7-r1 3917  115518 Giam...
The Linux Kernel.
Linux 2.6.28.1-r1 3917  115487 Giam...
The Linux Kernel.
Linux 2.6.28-r1 3917  116681 Mich...
The Linux Kernel.
Linux 2.6.27.8-r1 3917  134160 Giam...
The Linux Kernel.
Linux 2.6.27.4-r3 3917  149529 Luca...
The Linux Kernel.
Linux 2.6.25.17-r1 3917  172834 Giam...
The Linux Kernel.
Linux 2.6.25.16-r1 3917  166500 Giam...
The Linux Kernel.
Linux 2.6.25.10-r2 3917  165320 Giam...
The Linux Kernel.
Linux 2.6.25.7-r1 3917  157294 Giam...
The Linux Kernel.
Linux 2.6.25.4-r1 3917  133017 Hopp...
The Linux Kernel.
Linux 2.6.25-r1 3917  133216 Luca...
The Linux Kernel.
Linux 2.6.24.4-r5 3917  150733
The Linux Kernel.
Linux 2.6.24.3-r5 3917  150221 Luca...
The Linux Kernel.
Linux 2.6.24.2-r3 3917  146488 Giam...
The Linux Kernel.
Linux 2.6.24.1-r1 3917  146454 Giam...
The Linux Kernel.
Linux 2.6.24-r1 3917  146428 Luca...
The Linux Kernel.
Linux 2.6.23.8-r4 3917  155842 Luca...
The Linux Kernel.
Linux 2.6.22.7-r4 3917  114727 Luca...
The Linux Kernel.
Linux 2.6.22.1-r1 3917  121391 Luca...
The Linux Kernel.
Linux 2.6.21.1-r3 3917  118854 Luca...
The Linux Kernel.
Linux 2.6.20.7-r1 3917  117945 Luca...
The Linux Kernel.
Linux 2.6.20.4-r3 3917  151150 Luca...
The Linux Kernel.
Linux 2.6.20-r1 3917  106429 Luca...
The Linux Kernel.
Linux 2.6.18.3-r2 3917  111124 Luca...
The Linux Kernel.
Linux 2.6.17.11-r1 3917  164053 Luca...
The Linux Kernel.
Linux 2.6.17.3-r1 3917  165067 Luca...
The Linux Kernel.
Linux 2.6.16.20-r1 3917  133625 Luca...
The Linux Kernel.
Linux 2.6.16.14-r1 3917  168270 Luca...
The Linux Kernel.
Linux 2.6.15.5-r1 3917  224686 Jona...
The Linux Kernel.
Linux 2.6.15.2-r1 3917  177165 Carl...
The Linux Kernel.
Linux 2.6.15.1-r1 3917  149219 Jona...
The Linux Kernel.
Linux 2.6.15-r1 3917  149214 Luca...
The Linux Kernel.
Linux 2.6.14.4-r1 3917  150166 Jona...
The Linux Kernel.
Linux 2.6.14.3-r1 3917  150060 Jona...
The Linux Kernel.
Linux 2.6.14.2-r1 3917  149791 Carl...
The Linux Kernel.
Linux 2.6.13.4-r1 3917  149559 Luca...
The Linux Kernel.
Linux 2.6.13.2-r1 3917  56611 Jona...
The Linux Kernel.
Linux 2.6.13.1-r1 3917  56378 Luca...
The Linux Kernel.
Linux 2.6.12.2-r1 3917  50355 Luca...
The Linux Kernel.
Linux 2.6.11.9-r1 3917  98969 Luca...
The Linux Kernel.
Linux 2.6.11.8-r1 3917  109424 Jona...
The Linux Kernel.
Linux 2.6.11-r1 3917  99032 Luca...
The Linux Kernel.
view entry at GitHub | download recipe.bz2 file
01-Makefile.patch
02-gobohide.patch
03-devfs.patch
04-squashfs2.1-r2.patch
05-squashfs-gobohide.patch
06-nvidiafb.patch
07-supermount.patch
Recipe
Resources/Dependencies
Resources/Description
dot-config
diff -X dontdiff-2.6 -Nur linux-2.6.11/Documentation/filesystems/00-INDEX linux-2.6.11-Gobo/Documentation/filesystems/00-INDEX
--- linux-2.6.11/Documentation/filesystems/00-INDEX	2005-03-02 04:38:33.000000000 \
-0300
+++ linux-2.6.11-Gobo/Documentation/filesystems/00-INDEX	2005-03-07 01:58:47.000000000 \
-0300
@@ -36,6 +36,8 @@
 	- 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
diff -X dontdiff-2.6 -Nur linux-2.6.11/Documentation/filesystems/supermount.txt linux-2.6.11-Gobo/Documentation/filesystems/supermount.txt
--- linux-2.6.11/Documentation/filesystems/supermount.txt	1969-12-31 21:00:00.000000000 \
-0300
+++ linux-2.6.11-Gobo/Documentation/filesystems/supermount.txt	2005-03-07 01:58:47.000000000 \
-0300
@@ -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 $
diff -X dontdiff-2.6 -Nur linux-2.6.11/drivers/cdrom/cdrom.c linux-2.6.11-Gobo/drivers/cdrom/cdrom.c
--- linux-2.6.11/drivers/cdrom/cdrom.c	2005-03-02 04:37:49.000000000 -0300
+++ linux-2.6.11-Gobo/drivers/cdrom/cdrom.c	2005-03-07 01:58:47.000000000 -0300
@@ -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 @@
 #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 @@
 	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 @@
 }
 
 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 @@
 		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 @@
 	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 @@
 		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) \
&&
 	    (cdo->capability & CDC_LOCK) && !keeplocked) {
 		cdinfo(CD_CLOSE, "Unlocking door!\n");
 		cdo->lock_door(cdi, 0);
@@ -1483,6 +1485,38 @@
 		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 @@
 		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_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);
diff -X dontdiff-2.6 -Nur linux-2.6.11/drivers/cdrom/cdu31a.c linux-2.6.11-Gobo/drivers/cdrom/cdu31a.c
--- linux-2.6.11/drivers/cdrom/cdu31a.c	2005-03-02 04:38:13.000000000 -0300
+++ linux-2.6.11-Gobo/drivers/cdrom/cdu31a.c	2005-03-07 01:58:47.000000000 -0300
@@ -3003,6 +3003,25 @@
 
 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 @@
 	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 @@
 	.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;
diff -X dontdiff-2.6 -Nur linux-2.6.11/drivers/cdrom/cm206.c linux-2.6.11-Gobo/drivers/cdrom/cm206.c
--- linux-2.6.11/drivers/cdrom/cm206.c	2005-03-02 04:38:25.000000000 -0300
+++ linux-2.6.11-Gobo/drivers/cdrom/cm206.c	2005-03-07 01:58:47.000000000 -0300
@@ -1357,6 +1357,25 @@
 
 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 @@
 	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 @@
 	.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;
diff -X dontdiff-2.6 -Nur linux-2.6.11/drivers/cdrom/mcd.c linux-2.6.11-Gobo/drivers/cdrom/mcd.c
--- linux-2.6.11/drivers/cdrom/mcd.c	2005-03-02 04:38:12.000000000 -0300
+++ linux-2.6.11-Gobo/drivers/cdrom/mcd.c	2005-03-07 01:58:47.000000000 -0300
@@ -219,6 +219,25 @@
 
 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 @@
 	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 @@
 	.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;
diff -X dontdiff-2.6 -Nur linux-2.6.11/drivers/cdrom/mcdx.c linux-2.6.11-Gobo/drivers/cdrom/mcdx.c
--- linux-2.6.11/drivers/cdrom/mcdx.c	2005-03-02 04:38:08.000000000 -0300
+++ linux-2.6.11-Gobo/drivers/cdrom/mcdx.c	2005-03-07 01:58:47.000000000 -0300
@@ -223,6 +223,25 @@
 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 @@
 	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 @@
 	.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
 };
 
 
diff -X dontdiff-2.6 -Nur linux-2.6.11/drivers/cdrom/sbpcd.c linux-2.6.11-Gobo/drivers/cdrom/sbpcd.c
--- linux-2.6.11/drivers/cdrom/sbpcd.c	2005-03-02 04:37:49.000000000 -0300
+++ linux-2.6.11-Gobo/drivers/cdrom/sbpcd.c	2005-03-07 01:58:47.000000000 -0300
@@ -5366,6 +5366,25 @@
 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 @@
 	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 @@
 	.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
 };
 /*==========================================================================*/
 /*
diff -X dontdiff-2.6 -Nur linux-2.6.11/drivers/ide/ide-cd.c linux-2.6.11-Gobo/drivers/ide/ide-cd.c
--- linux-2.6.11/drivers/ide/ide-cd.c	2005-03-02 04:38:08.000000000 -0300
+++ linux-2.6.11-Gobo/drivers/ide/ide-cd.c	2005-03-07 01:58:47.000000000 -0300
@@ -3326,6 +3326,24 @@
 {
 	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 @@
 	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 */
diff -X dontdiff-2.6 -Nur linux-2.6.11/drivers/ide/ide-floppy.c linux-2.6.11-Gobo/drivers/ide/ide-floppy.c
--- linux-2.6.11/drivers/ide/ide-floppy.c	2005-03-02 04:38:09.000000000 -0300
+++ linux-2.6.11-Gobo/drivers/ide/ide-floppy.c	2005-03-07 01:58:47.000000000 -0300
@@ -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 @@
 	
 	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 @@
 	
 	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 @@
 		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 @@
 	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)
diff -X dontdiff-2.6 -Nur linux-2.6.11/drivers/scsi/sd.c linux-2.6.11-Gobo/drivers/scsi/sd.c
--- linux-2.6.11/drivers/scsi/sd.c	2005-03-02 04:38:17.000000000 -0300
+++ linux-2.6.11-Gobo/drivers/scsi/sd.c	2005-03-07 01:58:48.000000000 -0300
@@ -47,12 +47,13 @@
 #include <linux/init.h>
 #include <linux/blkdev.h>
 #include <linux/blkpg.h>
+#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>
-#include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_dbg.h>
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_driver.h>
@@ -471,7 +472,7 @@
 	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 @@
 
 	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 @@
 		 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 @@
 	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 @@
 	.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
 };
 
 /**
diff -X dontdiff-2.6 -Nur linux-2.6.11/drivers/scsi/sr.c linux-2.6.11-Gobo/drivers/scsi/sr.c
--- linux-2.6.11/drivers/scsi/sr.c	2005-03-02 04:38:37.000000000 -0300
+++ linux-2.6.11-Gobo/drivers/scsi/sr.c	2005-03-07 01:58:48.000000000 -0300
@@ -478,6 +478,25 @@
 {
 	int ret;
 	struct scsi_cd *cd = scsi_cd(inode->i_bdev->bd_disk);
+#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
+
 	ret = cdrom_release(&cd->cdi, file);
 	if(ret)
 		return ret;
@@ -511,6 +530,15 @@
 	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 @@
 	.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)
diff -X dontdiff-2.6 -Nur linux-2.6.11/fs/block_dev.c linux-2.6.11-Gobo/fs/block_dev.c
--- linux-2.6.11/fs/block_dev.c	2005-03-02 04:38:33.000000000 -0300
+++ linux-2.6.11-Gobo/fs/block_dev.c	2005-03-07 01:58:48.000000000 -0300
@@ -524,12 +524,45 @@
 {
 	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");
 
diff -X dontdiff-2.6 -Nur linux-2.6.11/fs/ext2/super.c linux-2.6.11-Gobo/fs/ext2/super.c
--- linux-2.6.11/fs/ext2/super.c	2005-03-02 04:38:13.000000000 -0300
+++ linux-2.6.11-Gobo/fs/ext2/super.c	2005-03-07 01:58:48.000000000 -0300
@@ -292,6 +292,12 @@
 	{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=%s"},
+#endif
 	{Opt_err, NULL}
 };
 
diff -X dontdiff-2.6 -Nur linux-2.6.11/fs/isofs/inode.c linux-2.6.11-Gobo/fs/isofs/inode.c
--- linux-2.6.11/fs/isofs/inode.c	2005-03-02 04:38:26.000000000 -0300
+++ linux-2.6.11-Gobo/fs/isofs/inode.c	2005-03-07 01:58:48.000000000 -0300
@@ -364,6 +364,10 @@
 	{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}
 };
 
diff -X dontdiff-2.6 -Nur linux-2.6.11/fs/Kconfig linux-2.6.11-Gobo/fs/Kconfig
--- linux-2.6.11/fs/Kconfig	2005-03-06 16:08:42.000000000 -0300
+++ linux-2.6.11-Gobo/fs/Kconfig	2005-03-07 01:58:48.000000000 -0300
@@ -867,6 +867,8 @@
 	  To compile this as a module, choose M here: the module will be called
 	  ramfs.
 
+source "fs/supermount/Kconfig"
+
 endmenu
 
 menu "Miscellaneous filesystems"
diff -X dontdiff-2.6 -Nur linux-2.6.11/fs/Makefile linux-2.6.11-Gobo/fs/Makefile
--- linux-2.6.11/fs/Makefile	2005-03-06 16:08:42.000000000 -0300
+++ linux-2.6.11-Gobo/fs/Makefile	2005-03-07 01:58:48.000000000 -0300
@@ -93,6 +93,7 @@
 obj-$(CONFIG_XFS_FS)		+= xfs/
 obj-$(CONFIG_AFS_FS)		+= afs/
 obj-$(CONFIG_BEFS_FS)		+= befs/
+obj-$(CONFIG_SUPERMOUNT)	+= supermount/
 obj-$(CONFIG_HOSTFS)		+= hostfs/
 obj-$(CONFIG_HPPFS)		+= hppfs/
 obj-$(CONFIG_DEBUG_FS)		+= debugfs/
diff -X dontdiff-2.6 -Nur linux-2.6.11/fs/namespace.c linux-2.6.11-Gobo/fs/namespace.c
--- linux-2.6.11/fs/namespace.c	2005-03-02 04:38:13.000000000 -0300
+++ linux-2.6.11-Gobo/fs/namespace.c	2005-03-07 01:58:48.000000000 -0300
@@ -1044,6 +1044,15 @@
 	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);
diff -X dontdiff-2.6 -Nur linux-2.6.11/fs/super.c linux-2.6.11-Gobo/fs/super.c
--- linux-2.6.11/fs/super.c	2005-03-02 04:38:08.000000000 -0300
+++ linux-2.6.11-Gobo/fs/super.c	2005-03-07 01:59:35.000000000 -0300
@@ -86,6 +86,9 @@
 		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 @@
 	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 @@
 		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 @@
 	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;
diff -X dontdiff-2.6 -Nur linux-2.6.11/fs/supermount/changelog linux-2.6.11-Gobo/fs/supermount/changelog
--- linux-2.6.11/fs/supermount/changelog	1969-12-31 21:00:00.000000000 -0300
+++ linux-2.6.11-Gobo/fs/supermount/changelog	2005-03-07 01:58:48.000000000 -0300
@@ -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.
diff -X dontdiff-2.6 -Nur linux-2.6.11/fs/supermount/dentry.c linux-2.6.11-Gobo/fs/supermount/dentry.c
--- linux-2.6.11/fs/supermount/dentry.c	1969-12-31 21:00:00.000000000 -0300
+++ linux-2.6.11-Gobo/fs/supermount/dentry.c	2005-03-07 01:58:48.000000000 -0300
@@ -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,
+};
diff -X dontdiff-2.6 -Nur linux-2.6.11/fs/supermount/file.c linux-2.6.11-Gobo/fs/supermount/file.c
--- linux-2.6.11/fs/supermount/file.c	1969-12-31 21:00:00.000000000 -0300
+++ linux-2.6.11-Gobo/fs/supermount/file.c	2005-03-07 01:58:48.000000000 -0300
@@ -0,0 +1,723 @@
+/*
+ *  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)
+ *  sendfile() function added by RĂ¼diger Kuhlmann (kernel@ruediger-kuhlmann.de)
+ *
+ *  $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;
+}
+
+int supermount_sendfile (struct file *file, loff_t *ppos,
+			      size_t count, read_actor_t actor, void __user *target)
+{
+	struct super_block *sb = file->f_dentry->d_sb;
+	struct file *subfile;
+	struct inode *subinode;
+	int rc;
+
+	ENTER(sb, "file=%p loff=%lu count=%lu actor=%p target=%p", file, ppos ? *ppos : \
0, count, actor, target);
+
+	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;
+	subinode = subfile->f_dentry->d_inode;
+	if (subfile->f_op && subfile->f_op->sendfile)
+		rc = subfile->f_op->sendfile (subfile, ppos, count, actor, target);
+
+	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,
+	.sendfile	= supermount_sendfile,
+};
diff -X dontdiff-2.6 -Nur linux-2.6.11/fs/supermount/filemap.c linux-2.6.11-Gobo/fs/supermount/filemap.c
--- linux-2.6.11/fs/supermount/filemap.c	1969-12-31 21:00:00.000000000 -0300
+++ linux-2.6.11-Gobo/fs/supermount/filemap.c	2005-03-07 01:58:48.000000000 -0300
@@ -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,
+};
diff -X dontdiff-2.6 -Nur linux-2.6.11/fs/supermount/init.c linux-2.6.11-Gobo/fs/supermount/init.c
--- linux-2.6.11/fs/supermount/init.c	1969-12-31 21:00:00.000000000 -0300
+++ linux-2.6.11-Gobo/fs/supermount/init.c	2005-03-07 01:58:48.000000000 -0300
@@ -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);
diff -X dontdiff-2.6 -Nur linux-2.6.11/fs/supermount/Kconfig linux-2.6.11-Gobo/fs/supermount/Kconfig
--- linux-2.6.11/fs/supermount/Kconfig	1969-12-31 21:00:00.000000000 -0300
+++ linux-2.6.11-Gobo/fs/supermount/Kconfig	2005-03-07 01:58:48.000000000 -0300
@@ -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.
diff -X dontdiff-2.6 -Nur linux-2.6.11/fs/supermount/Makefile linux-2.6.11-Gobo/fs/supermount/Makefile
--- linux-2.6.11/fs/supermount/Makefile	1969-12-31 21:00:00.000000000 -0300
+++ linux-2.6.11-Gobo/fs/supermount/Makefile	2005-03-07 01:58:48.000000000 -0300
@@ -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
diff -X dontdiff-2.6 -Nur linux-2.6.11/fs/supermount/mediactl.c linux-2.6.11-Gobo/fs/supermount/mediactl.c
--- linux-2.6.11/fs/supermount/mediactl.c	1969-12-31 21:00:00.000000000 -0300
+++ linux-2.6.11-Gobo/fs/supermount/mediactl.c	2005-03-07 01:58:48.000000000 -0300
@@ -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();
+}
diff -X dontdiff-2.6 -Nur linux-2.6.11/fs/supermount/namei.c linux-2.6.11-Gobo/fs/supermount/namei.c
--- linux-2.6.11/fs/supermount/namei.c	1969-12-31 21:00:00.000000000 -0300
+++ linux-2.6.11-Gobo/fs/supermount/namei.c	2005-03-07 01:58:48.000000000 -0300
@@ -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,
+};
diff -X dontdiff-2.6 -Nur linux-2.6.11/fs/supermount/proc.c linux-2.6.11-Gobo/fs/supermount/proc.c
--- linux-2.6.11/fs/supermount/proc.c	1969-12-31 21:00:00.000000000 -0300
+++ linux-2.6.11-Gobo/fs/supermount/proc.c	2005-03-07 01:58:48.000000000 -0300
@@ -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 */
diff -X dontdiff-2.6 -Nur linux-2.6.11/fs/supermount/subfs.c linux-2.6.11-Gobo/fs/supermount/subfs.c
--- linux-2.6.11/fs/supermount/subfs.c	1969-12-31 21:00:00.000000000 -0300
+++ linux-2.6.11-Gobo/fs/supermount/subfs.c	2005-03-07 01:58:48.000000000 -0300
@@ -0,0 +1,741 @@
+/*
+ *  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_LOCKED_ON(sb, sii->writecount < 0);
+		SUPERMOUNT_BUG_LOCKED_ON(sb, sii->readcount < 0);
+		while (sii->writecount) {
+			sii->writecount--;
+			sbi->writecount--;
+		}
+		while (sii->readcount) {
+			sii->readcount--;
+			sbi->readcount--;
+		}
+	}
+
+	LEAVE(sb);
+}
+
+static inline int
+subfs_remount_ro(struct super_block *sb)
+{
+	struct super_block *subsb = subfs_sb(sb);
+	int rc;
+
+	ENTER(sb);
+
+	rc = do_remount_sb(subsb, subsb->s_flags | MS_RDONLY, NULL, 0);
+
+	LEAVE(sb);
+
+	return rc;
+}
+
+/*
+ * reason can be
+ *   SUBFS_UMNT_NORMAL - normal umount, do not try to release subfs
+ *   SUBFS_UMNT_MEDIA  - media change detected, release subfs,
+ *   			 do not remount ro (as media is already gone)
+ *   SUBFS_UMNT_USER   - user request, release subfs, remount ro before
+ *   			 releasing tray lock
+ *
+ *   unlock_door is always needed to keep device usage count correct
+ */
+void
+subfs_umount(struct super_block *sb, int reason)
+{
+	struct supermount_sb_info *sbi = supermount_sbi(sb);
+	int writecount = sbi->writecount;
+
+	ENTER(sb);
+
+	if (reason != SUBFS_UMNT_NORMAL) {
+		/*
+		 * we used to did shrink_dcache here. It compicates locking
+		 * (clear_inode is called under sbi->sem thus requiring either
+		 * recursive lock or separate lock just for inode list).
+		 * This is not needed any more to ensure subfs can be umounted
+		 * so we let dentries die and rely on dentry_revalidate to
+		 * reject stale dentries
+		 */
+
+		if (subfs_is_busy(sb))
+			supermount_warning(sb, "opened files during media change");
+
+		supermount_clean_files(sb);
+		supermount_clean_dentries(sb);
+		supermount_clean_inodes(sb);
+
+		if (reason == SUBFS_UMNT_USER && writecount > 0)
+			subfs_remount_ro(sb);
+		if (sbi->lockcount > 0)
+			supermount_unlock_door(sb);
+		/*
+		 * this is quite ugly but so far I have no idea how to
+		 * do it cleanly
+		 */
+		sbi->lockcount = 0;
+	}
+
+	/*
+	 * all files are expected to be closed
+	 */
+	SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->writecount);
+	SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->readcount);
+	SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->lockcount);
+	SUPERMOUNT_BUG_LOCKED_ON(sb, !list_empty(&sbi->s_files));
+	SUPERMOUNT_BUG_LOCKED_ON(sb, !list_empty(&sbi->s_dentries));
+	SUPERMOUNT_BUG_LOCKED_ON(sb, !list_empty(&sbi->s_inodes));
+
+	unmark_media_supermounted(sb);
+	mntput(sbi->s_undermount);
+	sbi->s_undermount = NULL;
+	if (sbi->rw)
+		sb->s_flags &= ~MS_RDONLY;
+	sb->s_blocksize = 1024;
+	sb->s_blocksize_bits = 10;
+
+	/*
+	 * FIXME
+	 * again the problem of unmounting subfs from inside of put_super
+	 */
+	if (sb->s_root)
+		supermount_init_root_inode(sb->s_root->d_inode);
+
+	LEAVE(sb);
+}
+
+static inline int
+subfs_remount_rw(struct super_block *sb)
+{
+	struct super_block *subsb = subfs_sb(sb);
+	int rc;
+
+	ENTER(sb);
+
+	rc = do_remount_sb(subsb, subsb->s_flags & ~MS_RDONLY, NULL, 0);
+
+	LEAVE(sb);
+
+	return rc;
+}
+
+static struct vfsmount *
+subfs_real_mount(struct super_block *sb, char *type)
+{
+	struct supermount_sb_info *sbi = supermount_sbi(sb);
+	struct vfsmount *mnt;
+	char *opts = NULL;
+
+	ENTER(sb, "type=%s", type);
+
+	if (sbi->s_data) {
+		opts = strdup(sbi->s_data);
+		if (!opts) {
+			mnt = ERR_PTR(-ENOMEM);
+			goto fail;
+		}
+	}
+
+	mnt = do_kern_mount(type,
+			    (sb->s_flags & MS_RMT_MASK) | MS_SUPERMOUNTED,
+			    sbi->devname, opts);
+	if (opts)
+		kfree(opts);
+
+fail:
+	LEAVE(sb, "submnt=%p", mnt);
+
+	return mnt;
+}
+
+static int
+subfs_real_mount2(struct super_block *sb, char *type)
+{
+	struct supermount_sb_info *sbi = supermount_sbi(sb);
+	struct vfsmount *mnt;
+	struct super_block *subsb;
+	int rc = 0;
+
+	ENTER(sb, "type=%s", type);
+
+	mnt = subfs_real_mount(sb, type);
+
+	if (IS_ERR(mnt) && PTR_ERR(mnt) == -EROFS) {
+		sb->s_flags |= MS_RDONLY;
+		mnt = subfs_real_mount(sb, type);
+	}
+	rc = PTR_ERR(mnt);
+	if (IS_ERR(mnt))
+		goto out;
+
+	/* paranoid check for double-mounting */
+	SUPERMOUNT_BUG_LOCKED_ON(sb, atomic_read(&mnt->mnt_sb->s_active) != 1);
+
+	sbi->s_undermount = mnt;
+
+	if (!(sb->s_flags & MS_RDONLY)) {
+		rc = subfs_remount_ro(sb);
+		if (rc)
+			goto mntput;
+	}
+
+	subsb = mnt->mnt_sb;
+	sb->s_blocksize = subsb->s_blocksize;
+	sb->s_blocksize_bits = subsb->s_blocksize_bits;
+	attach_subfs_dentry(sb->s_root, mnt->mnt_root);
+	attach_subfs_inode(sb->s_root->d_inode, mnt->mnt_root->d_inode);
+	insert_inode_hash(sb->s_root->d_inode);
+	dnotify_parent(sb->s_root, DN_CREATE);
+	rc = 0;
+	goto out;
+
+	/*
+	 * error clean up
+	 */
+mntput:
+	sbi->s_undermount = 0;
+	mntput(mnt);
+out:
+	LEAVE(sb, "type=%s rc=%d", type, rc);
+
+	return rc;
+}
+
+/*
+ * Error values from mount
+ *   ENOENT     - no device file (quite possible with devfs)
+ *   ENXIO      - device does not exist
+ *   ENOMEDIUM  - no medium inserted (surprise, surprise :)
+ *   EBUSY      - attempt to mount on already mounted device
+ *                we specifically disallow it even when both
+ *                file system and mode (ro/rw) are the same
+ */
+static int
+subfs_mount(struct super_block *sb)
+{
+	struct supermount_sb_info *sbi = supermount_sbi(sb);
+	int retval = -ENODEV;
+	char *types = strdup(sbi->s_type);
+
+	ENTER(sb);
+
+	if (sbi->disabled)
+		retval = -EPERM;
+	else if (!types)
+	       retval = -ENOMEM;
+	else {
+		char *p = types, *fs;
+
+		while ((fs = strsep(&p, ":")) != NULL && retval
+		       && retval != -ENXIO
+		       && retval != -ENOMEDIUM
+		       && retval != -ENOENT
+		       && retval != -EBUSY)
+			retval = subfs_real_mount2(sb, fs);
+	}
+
+	if (types)
+		kfree(types);
+
+	if (!retval)
+		mark_media_supermounted(sb);
+
+	LEAVE(sb, "rc=%d", retval);
+
+	return retval;
+}
+
+static int
+__subfs_check_disk_change(struct super_block *sb)
+{
+	struct super_block *subsb = subfs_sb(sb);
+	int rc;
+	struct block_device *dev = subsb->s_bdev;
+
+	ENTER(sb);
+
+	rc = atomic_read(&subsb->s_media_changed);
+	if (!rc) {
+		lock_kernel();
+		rc = check_disk_change(dev);
+		unlock_kernel();
+	}
+
+	if (rc)
+		subfs_umount(sb, SUBFS_UMNT_MEDIA);
+
+	LEAVE(sb, "rc=%d", rc);
+
+	return rc;
+}
+
+/*
+ * this must really be called subfs_active ...
+ */
+int
+subfs_check_disk_change(struct super_block *sb)
+{
+	int rc;
+
+	ENTER(sb);
+
+	subfs_lock(sb);
+	if (subfs_is_mounted(sb))
+		rc = __subfs_check_disk_change(sb);
+	else
+		rc = 1;
+	subfs_unlock(sb);
+
+
+	LEAVE(sb, "rc=%d", rc);
+
+	return rc;
+}
+
+static int
+check_and_remount_subfs(struct super_block *sb)
+{
+	int rc;
+
+	ENTER(sb);
+
+	if (subfs_is_mounted(sb) && !__subfs_check_disk_change(sb)) {
+		rc = 0;
+		goto out;
+	}
+
+	rc = subfs_mount(sb);
+
+out:
+	LEAVE(sb, "rc=%d", rc);
+
+	return rc;
+}
+
+static inline void
+subfs_tray_lock(struct super_block *sb)
+{
+	struct supermount_sb_info *sbi = supermount_sbi(sb);
+
+	ENTER(sb);
+
+	SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->lockcount < 0);
+
+	if (!sbi->lockcount++)
+		supermount_lock_door(sb);
+
+	LEAVE(sb);
+}
+
+static inline void
+subfs_tray_unlock(struct super_block *sb)
+{
+	struct supermount_sb_info *sbi = supermount_sbi(sb);
+
+	ENTER(sb);
+
+	SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->lockcount <= 0);
+
+	if (!--sbi->lockcount)
+		supermount_unlock_door(sb);
+
+	LEAVE(sb);
+}
+
+static void
+subfs_get_read_access(struct super_block *sb)
+{
+	struct supermount_sb_info *sbi = supermount_sbi(sb);
+
+	ENTER(sb);
+
+	SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->readcount < 0);
+
+	if (!sbi->readcount++ && sbi->tray_lock == TRAY_LOCK_ALWAYS)
+		subfs_tray_lock(sb);
+
+	LEAVE(sb);
+}
+
+static int
+subfs_get_write_access(struct super_block *sb)
+{
+	struct supermount_sb_info *sbi = supermount_sbi(sb);
+	int rc;
+
+	ENTER(sb);
+
+	SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->writecount < 0);
+
+	rc = 0;
+	if (!sbi->writecount) {
+		if (sb->s_flags & MS_RDONLY)
+			rc = -EROFS;
+		else
+			rc = subfs_remount_rw(sb);
+	}
+
+	if (!rc && !sbi->writecount++ && sbi->tray_lock != TRAY_LOCK_NEVER)
+		subfs_tray_lock(sb);
+
+	LEAVE(sb, "rc=%d", rc);
+
+	return rc;
+}
+
+static void
+subfs_put_read_access(struct super_block *sb)
+{
+	struct supermount_sb_info *sbi = supermount_sbi(sb);
+
+	ENTER(sb);
+
+	SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->readcount <= 0);
+
+	if (!--sbi->readcount && sbi->tray_lock == TRAY_LOCK_ALWAYS)
+		subfs_tray_unlock(sb);
+
+	LEAVE(sb);
+}
+
+static void
+subfs_put_write_access(struct super_block *sb)
+{
+	struct supermount_sb_info *sbi = supermount_sbi(sb);
+
+	ENTER(sb);
+
+	SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->writecount <= 0);
+
+	if (!--sbi->writecount) {
+		/*
+		 * no need to fsync, it is done automatically on remount
+		 */
+		int rc = subfs_remount_ro(sb);
+		if (rc)
+			supermount_error(sb, "failed to remount ro, error = %d", rc);
+		if (sbi->tray_lock != TRAY_LOCK_NEVER)
+			subfs_tray_unlock(sb);
+	}
+
+	LEAVE(sb);
+}
+
+int
+subfs_get_access(struct inode *inode, int rw)
+{
+	struct super_block *sb = inode->i_sb;
+	int rc = 0;
+	
+	ENTER(sb);
+
+	subfs_lock(sb);
+	if (is_inode_obsolete(inode))
+		rc = -ESTALE;
+	else {
+		struct supermount_inode_info *sii = supermount_i(inode);
+
+		if (rw) {
+			SUPERMOUNT_BUG_LOCKED_ON(sb, sii->writecount < 0);
+			rc = subfs_get_write_access(sb);
+			if (!rc)
+				sii->writecount++;
+		} else {
+			SUPERMOUNT_BUG_LOCKED_ON(sb, sii->readcount < 0);
+			subfs_get_read_access(sb);
+			sii->readcount++;
+		}
+	}
+	subfs_unlock(sb);
+
+	LEAVE(sb, "rc=%d", rc);
+
+	return rc;
+}
+
+void
+subfs_put_access(struct inode *inode, int rw)
+{
+	struct super_block *sb = inode->i_sb;
+
+	ENTER(sb);
+
+	subfs_lock(sb);
+	if (!is_inode_obsolete(inode)) {
+		struct supermount_inode_info *sii = supermount_i(inode);
+
+		if (rw) {
+			SUPERMOUNT_BUG_LOCKED_ON(sb, sii->writecount <= 0);
+			sii->writecount--;
+			subfs_put_write_access(sb);
+		} else {
+			SUPERMOUNT_BUG_LOCKED_ON(sb, sii->readcount <= 0);
+			sii->readcount--;
+			subfs_put_read_access(sb);
+		}
+
+	}
+	subfs_unlock(sb);
+
+	LEAVE(sb);
+}
+
+struct vfsmount *
+subfs_go_online(struct super_block *sb)
+{
+	int rc;
+	struct vfsmount *mnt;
+
+	ENTER(sb);
+
+	subfs_lock(sb);
+
+	rc = check_and_remount_subfs(sb);
+	mnt = ERR_PTR(rc);
+	if (!rc)
+		mnt = mntget(subfs_mnt(sb));
+	subfs_unlock(sb);
+
+	LEAVE(sb, "mnt=%p", mnt);
+
+	return mnt;
+}
+
+void
+subfs_go_offline(struct super_block *sb, struct vfsmount *mnt)
+{
+	ENTER(sb);
+
+	SUPERMOUNT_BUG_ON(!mnt);
+	mntput(mnt);
+
+	LEAVE(sb);
+
+}
+
+struct vfsmount *
+subfs_prevent_umount(struct super_block *sb)
+{
+	struct vfsmount *mnt;
+
+	ENTER(sb);
+
+	subfs_lock(sb);
+	mnt = subfs_mnt(sb);
+	if (mnt)
+		mnt = mntget(mnt);
+	subfs_unlock(sb);
+
+	LEAVE(sb, "mnt=%p", mnt);
+
+	return mnt;
+}
+
+void
+subfs_allow_umount(struct super_block *sb, struct vfsmount *mnt)
+{
+	ENTER(sb);
+
+	SUPERMOUNT_BUG_ON(!mnt);
+	mntput(mnt);
+
+	LEAVE(sb);
+
+}
+
+struct vfsmount *
+subfs_get_mnt(struct super_block *sb)
+{
+	struct vfsmount *mnt = NULL;
+
+	ENTER(sb);
+
+	subfs_lock(sb);
+	if (subfs_is_mounted(sb))
+		mnt = mntget(subfs_mnt(sb));
+	subfs_unlock(sb);
+
+	LEAVE(sb, "mnt=%p", mnt);
+
+	return mnt;
+}
+
+struct super_block *
+subfs_get_sb(struct super_block *sb)
+{
+	struct super_block *subsb = NULL;
+
+	ENTER(sb);
+
+	subfs_lock(sb);
+	if (subfs_is_mounted(sb))
+		subsb = subfs_sb(sb);
+	subfs_unlock(sb);
+
+	LEAVE(sb, "subsb=%p", subsb);
+
+	return subsb;
+}
+
+/*
+ * contrary to its name this function deals with _supermount_ inode
+ */
+void
+subfs_clear_inode(struct inode *inode)
+{
+	struct super_block *sb = inode->i_sb;
+	struct supermount_inode_info *sii = supermount_i(inode);
+
+	ENTER(sb, "inode=%p", inode);
+
+	subfs_lock(sb);
+
+	/*
+	 * this is safe. If subfs has been unmounted, counters has been
+	 * set to 0 already
+	 */
+	while(sii->writecount > 0) {
+		sii->writecount--;
+		subfs_put_write_access(sb);
+	}
+
+	while(sii->readcount > 0) {
+		sii->readcount--;
+		subfs_put_read_access(sb);
+	}
+
+	list_del(&sii->list);
+
+	subfs_unlock(sb);
+
+	LEAVE(sb, "inode=%p", inode);
+}
diff -X dontdiff-2.6 -Nur linux-2.6.11/fs/supermount/super.c linux-2.6.11-Gobo/fs/supermount/super.c
--- linux-2.6.11/fs/supermount/super.c	1969-12-31 21:00:00.000000000 -0300
+++ linux-2.6.11-Gobo/fs/supermount/super.c	2005-03-07 01:58:48.000000000 -0300
@@ -0,0 +1,646 @@
+/*
+ *  linux/fs/supermount/super.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: super.c,v 1.25.2.11 2004/01/14 19:24:10 bor Exp $
+ */
+
+#define S_DBG_TRACE_CURRENT S_DBG_TRACE_SUPER
+
+#include <linux/statfs.h>
+#include <linux/parser.h>
+#include "supermount.h"
+
+/*
+ * vfat and msdos do not have any valididty checks for superblock and must
+ * be the last entries!
+ */
+
+static char *default_fs_types = "udf:iso9660:ext2:vfat:msdos";
+
+static struct super_operations supermount_sops;
+
+/* ========================= helpers ================================= */
+
+static inline void
+supermount_update_inode(struct inode *superi, struct inode *subi)
+{
+	struct super_block *sb = superi->i_sb;
+
+	ENTER(sb, "superi=%p subi=%p", superi, subi);
+
+	superi->i_ino = subi->i_ino;
+	superi->i_mode = subi->i_mode;
+	superi->i_uid = subi->i_uid;
+	superi->i_gid = subi->i_gid;
+	superi->i_nlink = subi->i_nlink;
+	superi->i_size = subi->i_size;
+	superi->i_atime = subi->i_atime;
+	superi->i_ctime = subi->i_ctime;
+	superi->i_mtime = subi->i_mtime;
+	superi->i_blksize = subi->i_blksize;
+	superi->i_blocks = subi->i_blocks;
+	superi->i_rdev = subi->i_rdev;
+	superi->i_version++;
+	set_inode_flags(superi, subi);
+
+	if (S_ISDIR(superi->i_mode)) {
+		superi->i_op = &supermount_dir_iops;
+		superi->i_fop = &supermount_dir_operations;
+	} else if (S_ISLNK(superi->i_mode)) {
+		superi->i_op = &supermount_symlink_iops;
+		superi->i_mapping = subi->i_mapping;
+	} else {
+		superi->i_op = &supermount_file_iops;
+		superi->i_fop = &supermount_file_operations;
+		superi->i_mapping = subi->i_mapping;
+	}
+
+	LEAVE(sb, "superi=%p subi=%p", superi, subi);
+}
+
+/* this is also called from subfs_mount to reinstantiate root inode */
+void
+attach_subfs_inode(struct inode *inode, struct inode *subi)
+{
+	struct super_block *sb = inode->i_sb;
+	struct supermount_sb_info *sbi = supermount_sbi(sb);
+	struct supermount_inode_info *sii;
+
+	ENTER(sb, "inode=%p subi=%p", inode, subi);
+
+	sii = supermount_i(inode);
+	if (!sii->inode) {
+		/*
+		 * this can be run concurrently. It is executed under
+		 * sbi->sem so only one task would actually instantate
+		 * inode. See namei.c:prepare_inode
+		 * Another user is subfs.c:subfs_real_mount2
+		 */
+		sii->inode = igrab(subi);
+		supermount_update_inode(inode, subi);
+		list_add(&sii->list, &sbi->s_inodes);
+	} else
+		SUPERMOUNT_BUG_LOCKED_ON(sb, sii->inode != subi);
+
+	LEAVE(sb, "inode=%p subi=%p", inode, subi);
+}
+
+char *
+strdup(const char *val)
+{
+	char *tmp;
+	tmp = kmalloc(1 + strlen(val), GFP_KERNEL);
+	if (tmp)
+		strcpy(tmp, val);
+	return tmp;
+}
+
+static struct supermount_sb_info *
+create_sbi(struct super_block *sb)
+{
+	struct supermount_sb_info *sbi = kmalloc(sizeof (*sbi), GFP_KERNEL);
+
+	if (!sbi)
+		return NULL;
+
+	memset(sbi, 0, sizeof (*sbi));
+
+	sbi->s_undermount = NULL;
+	sbi->s_type = sbi->devname = sbi->s_data = NULL;
+	INIT_LIST_HEAD(&sbi->s_inodes);
+	INIT_LIST_HEAD(&sbi->s_files);
+	INIT_LIST_HEAD(&sbi->s_dentries);
+	init_MUTEX(&sbi->sem);
+	sbi->host = sb;
+	sbi->tray_lock = TRAY_LOCK_ONWRITE;
+	sbi->rw = !(sb->s_flags & MS_RDONLY);
+
+	return sbi;
+}
+
+static void
+free_sbi(struct supermount_sb_info *sbi)
+{
+	if (sbi->s_type && sbi->s_type != default_fs_types)
+		kfree(sbi->s_type);
+	if (sbi->devname)
+		kfree(sbi->devname);
+	if (sbi->s_data)
+		kfree(sbi->s_data);
+	kfree(sbi);
+}
+
+enum {
+	Opt_fs, Opt_fs_auto, Opt_dev, Opt_debug, Opt_debug_old, Opt_subfsopts,
+	Opt_tray_lock_always, Opt_tray_lock_never, Opt_tray_lock_onwrite,
+	Opt_ignore, Opt_err
+};
+
+static match_table_t tokens = {
+	{Opt_fs_auto, "fs=auto"},
+	{Opt_fs, "fs=%s"},
+	{Opt_dev, "dev=%s"},
+	{Opt_debug, "debug=%u"},
+	{Opt_debug_old, "debug"},
+	{Opt_subfsopts, "--"},
+	{Opt_tray_lock_always, "tray_lock=always"},
+	{Opt_tray_lock_never, "tray_lock=never"},
+	{Opt_tray_lock_onwrite, "tray_lock=onwrite"},
+	{Opt_ignore, "no_tray_lock"},
+	{Opt_ignore, "fail_statfs_until_mount"},
+	{Opt_err, NULL}
+};
+
+static int
+parse_options(char *options, struct super_block *sb)
+{
+	struct supermount_sb_info *sbi = supermount_sbi(sb);
+	int rc;
+	char *p;
+
+	if (!options)
+		return 0;
+
+	while ((p = strsep(&options, ",")) != NULL) {
+		int token;
+		substring_t args[MAX_OPT_ARGS];
+		int n;
+
+		/* empty option means start of subfs options */
+		if (!*p)
+			goto copy_subfs_options;
+
+		token = match_token(p, tokens, args);
+		switch(token) {
+		case Opt_fs:
+			sbi->s_type = match_strdup(&args[0]);
+			if (!sbi->s_type)
+				return -ENOMEM;
+			break;
+		case Opt_fs_auto:
+			sbi->s_type = default_fs_types;
+			break;
+		case Opt_dev:
+			sbi->devname = match_strdup(&args[0]);
+			if (!sbi->devname)
+				return -ENOMEM;
+			break;
+		case Opt_debug:
+			if ((rc = match_int(&args[0], &n)))
+				return rc;
+			sbi->s_debug = n;
+			break;
+		case Opt_debug_old:
+			sbi->s_debug = S_DBG_DEBUG;
+			break;
+		case Opt_tray_lock_always:
+			sbi->tray_lock = TRAY_LOCK_ALWAYS;
+			break;
+		case Opt_tray_lock_onwrite:
+			sbi->tray_lock = TRAY_LOCK_ONWRITE;
+			break;
+		case Opt_tray_lock_never:
+			sbi->tray_lock = TRAY_LOCK_NEVER;
+			break;
+		case Opt_subfsopts:
+copy_subfs_options:
+			/* mount may have removed options after -- */
+			if (options && *options) {
+				sbi->s_data = strdup(options);
+				if (!sbi->s_data)
+					return -ENOMEM;
+			}
+			return 0;
+			break;
+		case Opt_ignore:
+			break;
+		default:
+			supermount_error(sb, "Unrecognized mount option \"%s\"",
+					 p);
+			return -EINVAL;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+void
+supermount_init_root_inode(struct inode *inode)
+{
+	inode->i_mode = 0777 | S_IFDIR;
+	inode->i_uid = current->fsuid;
+	inode->i_gid = current->fsgid;
+	inode->i_blksize = inode->i_sb->s_blocksize;
+	inode->i_rdev = MKDEV(UNNAMED_MAJOR, 0);
+	inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	inode->i_size = 0;
+	inode->i_blocks = 0;
+	inode->i_ino = 0;
+	inode->i_nlink = 0;
+
+	set_inode_flags(inode, 0);
+
+	inode->i_op = &supermount_dir_iops;
+	inode->i_fop = &supermount_dir_operations;
+}
+
+static struct inode *
+supermount_root_inode(struct super_block *sb)
+{
+	struct inode *inode = new_inode(sb);
+
+	ENTER(sb);
+
+	if (inode) {
+		inode->i_version = 0;
+		supermount_init_root_inode(inode);
+	}
+
+	LEAVE(sb, "inode=%p", inode);
+
+	return inode;
+}
+
+/* ========================== used in fstype declaration ================*/
+
+/* read_super: the main mount() entry point into the VFS layer. */
+static int
+supermount_read_super(struct super_block *sb, void *data, int silent)
+{
+	struct inode *root_inode;
+	struct dentry *root;
+	struct supermount_sb_info *sbi = create_sbi(sb);
+	int rc = -ENOMEM;
+
+	if (!sbi)
+		goto fail_no_memory;
+
+	sb->s_fs_info = sbi;
+
+	rc = parse_options((char *) data, sb);
+	if (rc)
+		goto fail_parsing;
+
+	rc = -EINVAL;
+	if (!sbi->devname) {
+		supermount_error(sb, "no dev=<device> option");
+		goto fail_parsing;
+	}
+
+	if (!sbi->s_type) {
+		sbi->s_type = default_fs_types;
+		supermount_warning(sb, "no fs=<filesystem> option, assuming fs=auto");
+	}
+
+	rc = -ENOMEM;
+
+	sb->s_blocksize = 1024;
+	sb->s_blocksize_bits = 10;
+	sb->s_magic = SUPERMOUNT_SUPER_MAGIC;
+	sb->s_op = &supermount_sops;
+
+	root_inode = supermount_root_inode(sb);
+	if (!root_inode)
+		goto fail_allocating_root_inode;
+
+	root = d_alloc_root(root_inode);
+	if (!root)
+		goto fail_allocating_root_dentry;
+	if (init_dentry_info(root))
+		goto fail_init_root_info;
+
+	sb->s_root = root;
+
+	supermount_proc_insert(sbi);
+
+	return 0;
+
+fail_init_root_info:
+	dput(root);
+fail_allocating_root_dentry:
+	iput(root_inode);
+fail_parsing:
+fail_allocating_root_inode:
+	free_sbi(sbi);
+
+fail_no_memory:
+	return rc;
+}
+
+struct super_block *supermount_get_sb(struct file_system_type *fs_type, int flags, \
const char *dev_name, void *data)
+{
+        return get_sb_nodev(fs_type, flags, data, supermount_read_super);
+}
+
+/* ======================= super_operations methods ==================== */
+
+static struct inode *
+supermount_alloc_inode(struct super_block *sb)
+{
+	struct supermount_inode_info *sii = kmalloc(sizeof(*sii), GFP_KERNEL);
+	struct inode *inode = 0;
+
+	ENTER(sb);
+
+	if (sii) {
+		INIT_LIST_HEAD(&sii->list);
+		sii->inode = 0;
+		sii->readcount = 0;
+		sii->writecount = 0;
+		inode = &sii->vfs_inode;
+		inode_init_once(inode);
+	}
+
+	LEAVE(sb, "inode=%p", inode);
+
+	return inode;
+}
+
+static void
+supermount_destroy_inode(struct inode *inode)
+{
+	struct super_block *sb = inode->i_sb;
+	struct supermount_inode_info *sii = supermount_i(inode);
+
+	ENTER(sb, "inode=%p", inode);
+
+	kfree(sii);
+
+	ENTER(sb, "inode=%p", inode);
+}
+
+/*
+ * FIXME
+ * I am still unsure if (or why) this functions is needed; it is likely
+ * to go away. So far the only _real_ user seems to be knfsd.
+ */
+static int
+supermount_write_inode(struct inode *inode, int sync)
+{
+	struct super_block *sb = inode->i_sb;
+	struct super_block *subsb;
+	struct inode *subi;
+	struct vfsmount *submnt;
+
+	ENTER(sb);
+
+	if (subfs_check_disk_change(sb))
+		goto out;
+
+	submnt = subfs_prevent_umount(sb);
+	if (!submnt)
+		goto out;
+
+	subi = get_subfs_inode(inode);
+	if (IS_ERR(subi))
+		goto allow_umount;
+
+	subsb = subfs_get_sb(sb);
+	if (!sb)
+		goto put_subi;
+
+	if (subsb->s_op && subsb->s_op->write_inode) {
+		if (subfs_get_access(inode, 1))
+			goto put_subi;
+
+		write_inode_now(subi, sync);
+
+		subfs_put_access(inode, 1);
+	}
+put_subi:
+	iput(subi);
+allow_umount:
+	subfs_allow_umount(sb, submnt);
+out:
+	LEAVE(sb);
+
+	return 0;
+}
+
+/*
+ * FIXME
+ * subfs_umount has no business here
+ * it should be moved into umount_begin; in this case force umount will
+ * free subfs (and give false No medium as error ... oh, well)
+ */
+static void
+supermount_put_super(struct super_block *sb)
+{
+	struct supermount_sb_info *sbi = supermount_sbi(sb);
+
+	ENTER(sb);
+
+	subfs_lock(sb);
+	if (subfs_is_mounted(sb))
+		subfs_umount(sb, SUBFS_UMNT_NORMAL);
+	subfs_unlock(sb);
+
+	supermount_proc_remove(sbi);
+
+	LEAVE(sb);
+
+	sb->s_fs_info = 0;
+	free_sbi(sbi);
+}
+
+/*
+ * The following has to be done in two steps (unfortunately)
+ * First make sure iput(subi) is run just once - and then put
+ * read/write access and finally remove inode from list
+ *
+ * We can't do it in one shot because removing inode from list
+ * will remove also possibility to mark it stale. It has been 
+ * introduced by me :( removing generation numbers. OTOH so far
+ * it is the only place that needs extra treatment because of it.
+ */
+static void
+supermount_clear_inode(struct inode *inode)
+{
+	struct supermount_inode_info *sii = supermount_i(inode);
+	struct super_block *sb = inode->i_sb;
+	struct inode *subi;
+	struct vfsmount *mnt;
+
+	ENTER(sb, "inode=%p", inode);
+
+	mnt = subfs_prevent_umount(sb);
+	if (!mnt)
+		goto out;
+
+	subfs_lock(sb);
+	subi = sii->inode;
+	sii->inode = 0;
+	subfs_unlock(sb);
+
+
+	if (subi) {
+		/*
+		 * we used to check for subi->i_count != 1 here.
+		 * This does not actually work - it is quite possible
+		 * that inode has been looked up while we were in this
+		 * function. So just ignore it (the worst thing that
+		 * may happen is that subfs cannot be remounted)
+		 */
+
+		iput(subi);
+
+	}
+	subfs_allow_umount(sb, mnt);
+
+	/*
+	 * This does not has much to do with subfs but I did not want
+	 * to export __ functions
+	 */
+	subfs_clear_inode(inode);
+out:
+	LEAVE(sb, "inode=%p", inode);
+}
+/*
+ * FIXME why it is needed?
+ */
+
+static void
+supermount_write_super(struct super_block *sb)
+{
+	struct vfsmount *mnt;
+
+	ENTER(sb);
+
+	if (subfs_check_disk_change(sb))
+		goto out;
+
+	if (subfs_is_rw(sb)) {
+		struct super_block *subsb;
+
+		mnt = subfs_prevent_umount(sb);
+		if (!mnt)
+			goto out;
+
+		subsb = subfs_get_sb(sb);
+		if (subsb && subsb->s_op && subsb->s_op->write_super)
+			subsb->s_op->write_super(subsb);
+
+		subfs_allow_umount(sb, mnt);
+	}
+
+out:
+	LEAVE(sb);
+
+	return;
+}
+
+static int
+supermount_statfs(struct super_block *sb, struct kstatfs *buf)
+{
+	int rc = 0;
+	struct super_block *subsb;
+	struct vfsmount *mnt;
+
+	ENTER(sb);
+
+	(void)subfs_check_disk_change(sb);
+
+	mnt = subfs_prevent_umount(sb);
+	if (!mnt)
+		goto out;
+
+	subsb = subfs_get_sb(sb);
+	if (subsb && subsb->s_op && subsb->s_op->statfs)
+		rc = subsb->s_op->statfs(subsb, buf);
+	if (!rc)
+		buf->f_type = SUPERMOUNT_SUPER_MAGIC;
+
+	subfs_allow_umount(sb, mnt);
+out:
+	LEAVE(sb, "rc=%d", rc);
+
+	return rc;
+}
+
+static int
+supermount_remount_fs(struct super_block *sb, int *flags, char *data)
+{
+	return -ENOSYS;
+}
+
+/*
+ * based on fs/ntfs/inode.c:ntfs_show_options
+ */
+static int
+supermount_show_options(struct seq_file *sf, struct vfsmount *mnt)
+{
+	struct supermount_sb_info *sbi = supermount_sbi(mnt->mnt_sb);
+
+	seq_printf(sf, ",dev=%s", sbi->devname);
+	seq_puts(sf, ",fs=");
+	if (sbi->s_type == default_fs_types)
+		seq_puts(sf, "auto");
+	else 
+		seq_puts(sf, sbi->s_type);
+	seq_puts(sf, ",tray_lock=");
+	if (sbi->tray_lock == TRAY_LOCK_ALWAYS)
+		seq_puts(sf, "always");
+	else if (sbi->tray_lock == TRAY_LOCK_NEVER)
+		seq_puts(sf, "never");
+	else if (sbi->tray_lock == TRAY_LOCK_ONWRITE)
+		seq_puts(sf, "onwrite");
+	else
+		SUPERMOUNT_BUG_ON(1);
+
+	if (sbi->s_debug)
+		seq_printf(sf, ",debug=0x%lx", sbi->s_debug);
+	if (sbi->s_data)
+		seq_printf(sf, ",--,%s", sbi->s_data);
+	return 0;
+}
+
+/*
+ * ->read_inode:	not needed
+ * ->dirty_inode:	probably not needed. It appears VFS layer never
+ *  			calls mark_inode_dirty itself; exception is
+ *  			UPDATE_ATIME and it is already handled specially
+ *  			FIXME
+ *  			It may be needed for knfsd and similar
+ * ->put_inode:		not needed
+ * ->delete_inode:	not needed
+ * ->write_super:	FIXME not needed, we do not have any backing store
+ * ->sync_fs:		not needed
+ * ->write_super_lockfs:not needed
+ * ->unlockfs:		not needed
+ * ->umount_begin:	TODO maybe to move subfs_umount aways from put_super
+ * ->fh_to_dentry:	TODO may be
+ * ->dentry_to_fh:	TODO may be
+ */
+static struct super_operations supermount_sops = {
+	.alloc_inode	= supermount_alloc_inode,
+	.destroy_inode	= supermount_destroy_inode,
+	.write_inode	= supermount_write_inode,
+	.clear_inode	= supermount_clear_inode,
+	.put_super	= supermount_put_super,
+	.write_super	= supermount_write_super,
+	.statfs		= supermount_statfs,
+	.remount_fs	= supermount_remount_fs,
+	.show_options	= supermount_show_options,
+};
diff -X dontdiff-2.6 -Nur linux-2.6.11/fs/supermount/supermount.h linux-2.6.11-Gobo/fs/supermount/supermount.h
--- linux-2.6.11/fs/supermount/supermount.h	1969-12-31 21:00:00.000000000 -0300
+++ linux-2.6.11-Gobo/fs/supermount/supermount.h	2005-03-07 01:58:48.000000000 -0300
@@ -0,0 +1,497 @@
+#ifndef _SUPERMOUNT_I_H
+#define _SUPERMOUNT_I_H
+
+/*
+ *  $Id: supermount.h,v 1.42.2.29 2004/01/18 18:44:25 bor Exp $
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/file.h>
+#include <linux/mm.h>
+#include <linux/poll.h>
+#include <linux/smp_lock.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/dnotify.h>
+#include <linux/mount.h>
+#include <linux/moduleparam.h>
+#include <linux/genhd.h>
+#include <linux/namei.h>
+#include <linux/supermount_media.h>
+
+#ifdef CONFIG_SUPERMOUNT_DEBUG
+#define SUPERMOUNT_DEBUG
+#endif
+/*
+ * The supermount superblock magic number
+ */
+
+#define SUPERMOUNT_SUPER_MAGIC	0x9fa1
+#define SUPERMOUNT_VERSION		"2.0.8"
+
+#define S_DBG_DEBUG			0x001
+#define S_DBG_TRACE_DENTRY		0x002
+#define S_DBG_TRACE_FILE		0x004
+#define S_DBG_TRACE_FILEMAP		0x008
+#define S_DBG_TRACE_MEDIACTL		0x010
+#define S_DBG_TRACE_NAMEI		0x020
+#define S_DBG_TRACE_SUBFS		0x040
+#define S_DBG_TRACE_SUPER		0x080
+
+/*
+ * The subfs umount reason
+ */
+#define SUBFS_UMNT_NORMAL		0 /* normal umount */
+#define SUBFS_UMNT_MEDIA		1 /* media change detected */
+#define SUBFS_UMNT_USER			2 /* user request */
+
+/*
+ * When to lock media
+ */
+#define TRAY_LOCK_NEVER			0
+#define TRAY_LOCK_ONWRITE		1
+#define TRAY_LOCK_ALWAYS		2
+
+extern struct file_system_type supermount_fs_type;
+
+#ifdef SUPERMOUNT_DEBUG
+
+#define SUPERMOUNT_BUG_ON(x) BUG_ON(x)
+#define SUPERMOUNT_BUG_LOCKED_ON(sb, x) \
+do { \
+	if (x) { \
+		subfs_unlock(sb); \
+		BUG(); \
+	} \
+} while (0)
+
+#define supermount_debug(sb, args...) \
+do { \
+	struct supermount_sb_info *sbi = supermount_sbi(sb); \
+	char *dev = sbi->devname ? sbi->devname : "unknown"; \
+\
+	if (supermount_dbg(sb, S_DBG_DEBUG)) { \
+		printk("%sSUPERMOUNT DEBUG [dev=%s] <%s:%d> ", \
+			KERN_DEBUG, dev, __FUNCTION__, __LINE__); \
+		printk(args); \
+		printk("\n"); \
+	} \
+} while(0)
+
+#define ENTER(sb, args...) \
+do { \
+	struct supermount_sb_info *sbi = supermount_sbi(sb); \
+	char *dev = sbi->devname ? sbi->devname : "unknown"; \
+\
+	if (supermount_dbg(sb, S_DBG_TRACE_CURRENT)) { \
+		printk("%sSUPERMOUNT TRACE [dev=%s] PID=%lu ENTER %s", \
+			KERN_DEBUG, dev, (unsigned long)current->pid, \
+			__FUNCTION__); \
+		printk(" " args); \
+		printk("\n"); \
+	} \
+} while (0)
+
+#define LEAVE(sb, args...) \
+do { \
+	struct supermount_sb_info *sbi = supermount_sbi(sb); \
+	char *dev = sbi->devname ? sbi->devname : "unknown"; \
+\
+	if (supermount_dbg(sb, S_DBG_TRACE_CURRENT)) { \
+		printk("%sSUPERMOUNT TRACE [dev=%s] PID=%lu LEAVE %s", \
+			KERN_DEBUG, dev, (unsigned long)current->pid, \
+			__FUNCTION__); \
+		printk(" " args); \
+		printk("\n"); \
+	} \
+} while (0)
+
+#else /* SUPERMOUNT_DEBUG */
+#define supermount_debug(f, a...) /**/
+#define SUPERMOUNT_BUG_ON(x) do { } while (0)
+#define SUPERMOUNT_BUG_LOCKED_ON(sb, x) do { } while (0)
+
+#define ENTER(sb, args...) do { (void)sb; } while (0)
+#define LEAVE(sb, args...) do { (void)sb; } while (0)
+#endif				/* SUPERMOUNT_DEBUG */
+
+#define supermount_warning(sb, ...) \
+do { \
+	struct supermount_sb_info *sbi = supermount_sbi(sb); \
+	char *dev = sbi->devname ? sbi->devname : "unknown"; \
+\
+	printk("%sSUPERMOUNT WARNING [dev=%s] ", KERN_WARNING, dev); \
+	printk(__VA_ARGS__); \
+	printk("\n"); \
+} while(0)
+
+#define supermount_error(sb, ...) \
+do { \
+	struct supermount_sb_info *sbi = supermount_sbi(sb); \
+	char *dev = sbi->devname ? sbi->devname : "unknown"; \
+\
+	printk("%sSUPERMOUNT ERROR [dev=%s] ", KERN_ERR, dev); \
+	printk(__VA_ARGS__); \
+	printk("\n"); \
+} while(0)
+
+/*
+ * The following is drived from fs/inode.c:update_atime()
+ */
+#define NEED_WRITE_ATIME(inode) \
+	(!(IS_NOATIME(inode) || \
+	 (IS_NODIRATIME(inode) && S_ISDIR(inode->i_mode)) || \
+	 IS_RDONLY(inode)))
+
+
+/*
+ * supermount super-block data in memory
+ */
+struct supermount_sb_info;
+struct supermount_sb_info {
+	/* == options == */
+	unsigned long s_debug;	/* debug flags S_DBG_* */
+	char *s_type;		/* Type of fs to be sub-mounted */
+	char *devname;		/* Where to mount the subfs from */
+	char *s_data;		/* Data to pass when mounting subfs */
+	/* == end of options == */
+	struct vfsmount *s_undermount;
+				/* Mount point for subfs */
+	int readcount;		/* Refcount of read access
+				   on the filesystem */
+	int writecount;		/* Refcount of write access
+				   on the filesystem */
+	int lockcount;		/* Refcount of requests to lock tray */
+	struct list_head s_inodes;
+				/* list of active inodes */
+	struct list_head s_files;
+				/* list of active files */
+	struct list_head s_dentries;
+				/* list of active dentries */
+	struct semaphore sem;
+	struct super_block *host;
+				/* needed for procfs support */
+	struct supermount_sb_info *next;
+				/* list of all supermount fs */
+	unsigned int tray_lock:2;
+	unsigned int disabled:1;
+	unsigned int rw:1;
+};
+
+static inline struct supermount_sb_info *
+supermount_sbi(struct super_block *sb)
+{
+	struct supermount_sb_info *sbi;
+	BUG_ON(!sb);
+	BUG_ON(sb->s_type != &supermount_fs_type);
+
+	sbi = (struct supermount_sb_info *) (sb->s_fs_info);
+	BUG_ON(!sbi);
+
+	return sbi;
+}
+
+static inline struct vfsmount *
+subfs_mnt(struct super_block *sb)
+{
+	return supermount_sbi(sb)->s_undermount;
+}
+
+static inline struct super_block *
+subfs_sb(struct super_block *sb)
+{
+	struct vfsmount *mnt = subfs_mnt(sb);
+
+	if (mnt) return mnt->mnt_sb;
+
+	return 0;
+}
+
+/* this is expected to run under sb->sem */
+static inline int
+subfs_is_mounted(struct super_block *sb)
+{
+	struct supermount_sb_info *sbi = supermount_sbi(sb);
+
+	return sbi->s_undermount != 0;
+}
+
+/* this is expected to run under sb->sem */
+static inline int
+subfs_is_busy(struct super_block *sb)
+{
+	struct vfsmount *mnt = subfs_mnt(sb);
+
+	/*
+	 * In "normal" case mnt_count is 2. But we currently do
+	 * not insert subfs into task namespace so count is 1
+	 * FIXME
+	 * This also means we do not do_umount i.e. do not run
+	 * either umount_begin or DQUOT_OFF for subfs.
+	 */
+	if (mnt) return atomic_read(&mnt->mnt_count) > 1;
+
+	return 0;
+}
+
+/* this is expected to run under sb->sem */
+static inline int
+subfs_is_rw(struct super_block *sb)
+{
+	struct super_block *subsb;
+
+	if (!subfs_is_mounted(sb))
+		return 0;
+	
+	subsb = subfs_sb(sb);
+	return !(subsb->s_flags & MS_RDONLY);
+}
+
+static inline void
+subfs_lock(struct super_block *sb)
+{
+	struct supermount_sb_info *sbi = supermount_sbi(sb);
+
+	down(&sbi->sem);
+}
+
+static inline void
+subfs_unlock(struct super_block *sb)
+{
+	struct supermount_sb_info *sbi = supermount_sbi(sb);
+
+	up(&sbi->sem);
+}
+
+/*
+ * query debug flags set
+ */
+static inline int supermount_dbg(struct super_block *sb, unsigned long flags)
+{
+	struct supermount_sb_info *sbi = supermount_sbi(sb);
+
+	return sbi->s_debug & flags;
+}
+
+/*
+ * supermount inode info
+ */
+
+struct supermount_inode_info {
+	struct list_head list;
+  	struct inode *inode;	/* subfs inode */
+	int readcount;
+	int writecount;
+	struct inode vfs_inode;	/* superfs inode */
+};
+
+static inline int
+is_inode_supermounted(struct inode *inode)
+{
+	return inode && inode->i_sb && inode->i_sb->s_type == &supermount_fs_type;
+}
+
+static inline struct supermount_inode_info *
+supermount_i(struct inode *inode)
+{
+	SUPERMOUNT_BUG_ON(!is_inode_supermounted(inode));
+
+	return list_entry(inode, struct supermount_inode_info, vfs_inode);
+}
+
+static inline int
+is_inode_obsolete(struct inode *inode)
+{
+	struct supermount_inode_info *sii = supermount_i(inode);
+
+	return sii->inode == 0;
+}
+
+static inline void
+supermount_list_add_inode(struct inode *inode)
+{
+	struct supermount_inode_info *sii = supermount_i(inode);
+
+	list_add(&(sii->list), &(supermount_sbi(inode->i_sb)->s_inodes));
+}
+
+/*
+ * FIXME
+ * Should we propagate all flags? *_QUOTA looks very possible candidate
+ * We can't just assign them because other flags may be set by VFS
+ */
+#define SMNT_INODE_FLAGS (S_IMMUTABLE|S_NOATIME|S_APPEND|S_SYNC)
+static inline void
+set_inode_flags(struct inode *inode, struct inode *subi)
+{
+	inode->i_flags &= ~SMNT_INODE_FLAGS;
+	if (subi)
+		inode->i_flags |= (subi->i_flags & SMNT_INODE_FLAGS);
+}
+
+/*
+ * supermount dentry info
+ */
+
+struct supermount_dentry_info {
+	struct list_head list;
+	struct dentry *dentry;
+	struct dentry *host;
+};
+
+static inline int
+is_dentry_supermounted(struct dentry *dentry)
+{
+	return (dentry && dentry->d_sb && dentry->d_sb->s_type == &supermount_fs_type);
+}
+
+static inline struct supermount_dentry_info *
+supermount_d(struct dentry *dentry)
+{
+	SUPERMOUNT_BUG_ON(!is_dentry_supermounted(dentry));
+	SUPERMOUNT_BUG_ON(!dentry->d_fsdata);
+
+	return  (struct supermount_dentry_info *)dentry->d_fsdata;
+}
+
+static inline int
+is_dentry_obsolete(struct dentry *dentry)
+{
+	struct supermount_dentry_info *sdi = supermount_d(dentry);
+
+	return sdi->dentry == 0;
+}
+
+/*
+ * Supermount file info 
+ */
+
+struct supermount_file_info {
+	struct list_head	list;
+	struct file *		host;
+	struct file		*file;
+	pid_t			owner;
+	struct vm_operations_struct *vm_ops;
+	unsigned int		fake:1;
+};
+
+static inline int
+is_file_supermounted(struct file *file)
+{
+	return file && file->f_dentry && is_dentry_supermounted(file->f_dentry);
+}
+
+static inline struct supermount_file_info *
+supermount_f(struct file *file)
+{
+	SUPERMOUNT_BUG_ON(!is_file_supermounted(file));
+	SUPERMOUNT_BUG_ON(!file->f_supermount);
+
+	return  file->f_supermount;
+}
+
+static inline int
+is_file_obsolete(struct file *file)
+{
+	struct supermount_file_info *sfi = supermount_f(file);
+
+	return sfi->file == NULL;
+}
+
+static inline int
+is_file_fake(struct file *file)
+{
+	struct supermount_file_info *sfi = supermount_f(file);
+
+	return sfi->fake;
+}
+
+/* dentry.c */
+extern rwlock_t d_compare_lock;
+extern struct dentry_operations supermount_dops;
+extern int init_dentry_info(struct dentry *);
+extern void attach_subfs_dentry(struct dentry *, struct dentry *);
+extern struct dentry *get_subfs_dentry(struct dentry *dentry);
+ 
+/* file.c */
+extern struct file_operations supermount_dir_operations;
+extern struct file_operations supermount_file_operations;
+
+/* filemap.c */
+extern struct vm_operations_struct supermount_vm_ops;
+extern int supermount_file_mmap(struct file *, struct vm_area_struct *);
+extern struct file *get_subfs_file(struct file*);
+
+/* mediactl.c */
+extern void supermount_mediactl(struct super_block *, int, int);
+static inline void
+supermount_lock_door(struct super_block *sb)
+{
+	supermount_mediactl(sb, MEDIA_LOCK, 1);
+}
+static inline void
+supermount_unlock_door(struct super_block *sb)
+{
+	supermount_mediactl(sb, MEDIA_UNLOCK, 1);
+}
+
+static inline void
+mark_media_supermounted(struct super_block *sb)
+{
+	supermount_mediactl(sb, SUPERMOUNT_INC_COUNT, 0);
+	supermount_mediactl(sb, MEDIA_UNLOCK, 0);
+}
+
+static inline void
+unmark_media_supermounted(struct super_block *sb)
+{
+	supermount_mediactl(sb, SUPERMOUNT_DEC_COUNT, 0);
+}
+
+/* namei.c */
+extern struct inode_operations supermount_dir_iops;
+extern struct inode_operations supermount_file_iops;
+extern struct inode_operations supermount_symlink_iops;
+extern struct inode *create_supermount_inode(struct super_block *);
+extern struct inode *get_subfs_inode(struct inode *inode);
+
+/* proc.c */
+#ifdef CONFIG_PROC_FS
+extern void supermount_proc_register(void);
+extern void supermount_proc_unregister(void);
+extern void supermount_proc_insert(struct supermount_sb_info *);
+extern void supermount_proc_remove(struct supermount_sb_info *);
+#else
+#define supermount_proc_register() do { } while(0)
+#define supermount_proc_unregister() do { } while(0)
+#define supermount_proc_insert(sbi) do { } while(0)
+#define supermount_proc_remove(sbi) do { } while(0)
+#endif
+
+/* subfs.c */
+extern void subfs_umount(struct super_block *sb, int);
+extern struct vfsmount *subfs_go_online(struct super_block *);
+extern void subfs_go_offline(struct super_block *, struct vfsmount *);
+extern int subfs_get_access(struct inode *, int);
+extern void subfs_put_access(struct inode *, int);
+extern int subfs_check_disk_change(struct super_block *);
+extern struct vfsmount *subfs_prevent_umount(struct super_block *);
+extern void subfs_allow_umount(struct super_block *, struct vfsmount *);
+extern struct vfsmount *subfs_get_mnt(struct super_block *sb);
+extern struct super_block *subfs_get_sb(struct super_block *sb);
+extern void subfs_clear_inode(struct inode *);
+
+/* super.c */
+extern struct super_block *supermount_get_sb(struct file_system_type *, int, const \
char *, void *);
+extern void attach_subfs_inode(struct inode *, struct inode *);
+extern void supermount_init_root_inode(struct inode *);
+extern char *strdup(const char *);
+
+#endif				/* _SUPERMOUNT_I_H */
diff -X dontdiff-2.6 -Nur linux-2.6.11/fs/udf/super.c linux-2.6.11-Gobo/fs/udf/super.c
--- linux-2.6.11/fs/udf/super.c	2005-03-02 04:38:12.000000000 -0300
+++ linux-2.6.11-Gobo/fs/udf/super.c	2005-03-07 01:58:48.000000000 -0300
@@ -274,7 +274,7 @@
 	Opt_gid, Opt_uid, Opt_umask, Opt_session, Opt_lastblock,
 	Opt_anchor, Opt_volume, Opt_partition, Opt_fileset,
 	Opt_rootdir, Opt_utf8, Opt_iocharset,
-	Opt_err
+	Opt_ignore, Opt_err
 };
 
 static match_table_t tokens = {
@@ -299,6 +299,10 @@
 	{Opt_rootdir, "rootdir=%u"},
 	{Opt_utf8, "utf8"},
 	{Opt_iocharset, "iocharset=%s"},
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+	/* Silently ignore NLS option */
+	{Opt_ignore, "codepage=%s"},
+#endif
 	{Opt_err, NULL}
 };
 
@@ -419,6 +423,8 @@
 				uopt->flags |= (1 << UDF_FLAG_NLS_MAP);
 				break;
 #endif
+			case Opt_ignore:
+				break;
 			default:
 				printk(KERN_ERR "udf: bad mount option \"%s\" "
 						"or missing value\n", p);
diff -X dontdiff-2.6 -Nur linux-2.6.11/include/linux/cdrom.h linux-2.6.11-Gobo/include/linux/cdrom.h
--- linux-2.6.11/include/linux/cdrom.h	2005-03-02 04:37:31.000000000 -0300
+++ linux-2.6.11-Gobo/include/linux/cdrom.h	2005-03-07 01:58:48.000000000 -0300
@@ -992,6 +992,9 @@
 extern int cdrom_ioctl(struct file *file, struct cdrom_device_info *cdi,
 		struct inode *ip, unsigned int cmd, unsigned long arg);
 extern int cdrom_media_changed(struct cdrom_device_info *);
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+extern int cdrom_mediactl(struct cdrom_device_info *, struct block_device *, int, \
int);
+#endif
 
 extern int register_cdrom(struct cdrom_device_info *cdi);
 extern int unregister_cdrom(struct cdrom_device_info *cdi);
diff -X dontdiff-2.6 -Nur linux-2.6.11/include/linux/fs.h linux-2.6.11-Gobo/include/linux/fs.h
--- linux-2.6.11/include/linux/fs.h	2005-03-06 16:00:58.000000000 -0300
+++ linux-2.6.11-Gobo/include/linux/fs.h	2005-03-07 02:01:12.000000000 -0300
@@ -81,6 +81,7 @@
 /* public flags for file_system_type */
 #define FS_REQUIRES_DEV 1 
 #define FS_BINARY_MOUNTDATA 2
+#define FS_NO_SUBMNT	64      /* Prevent mounting over this filesystem */
 #define FS_REVAL_DOT	16384	/* Check the paths ".", ".." for staleness */
 #define FS_ODD_RENAME	32768	/* Temporary stuff; will go away as soon
 				  * as nfs_rename() will be cleaned up
@@ -103,6 +104,7 @@
 #define MS_REC		16384
 #define MS_VERBOSE	32768
 #define MS_POSIXACL	(1<<16)	/* VFS does not apply the umask */
+#define MS_SUPERMOUNTED	(1<<18)
 #define MS_ACTIVE	(1<<30)
 #define MS_NOUSER	(1<<31)
 
@@ -600,6 +602,11 @@
 	spinlock_t		f_ep_lock;
 #endif /* #ifdef CONFIG_EPOLL */
 	struct address_space	*f_mapping;
+
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+	/* Used by supermount. Use(fullness) unconfirmed */
+	void                    *f_supermount;
+#endif
 };
 extern spinlock_t files_lock;
 #define file_list_lock() spin_lock(&files_lock);
@@ -802,6 +809,10 @@
 	/* Granuality of c/m/atime in ns.
 	   Cannot be worse than a second */
 	u32		   s_time_gran;
+
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+        atomic_t s_media_changed;
+#endif
 };
 
 extern struct timespec current_fs_time(struct super_block *sb);
@@ -884,6 +895,9 @@
 	long (*compat_ioctl) (struct file *, unsigned, unsigned long);
 	int (*media_changed) (struct gendisk *);
 	int (*revalidate_disk) (struct gendisk *);
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+	int (*mediactl) (struct block_device *, int, int);
+#endif
 	struct module *owner;
 };
 
diff -X dontdiff-2.6 -Nur linux-2.6.11/include/linux/genhd.h linux-2.6.11-Gobo/include/linux/genhd.h
--- linux-2.6.11/include/linux/genhd.h	2005-03-02 04:37:49.000000000 -0300
+++ linux-2.6.11-Gobo/include/linux/genhd.h	2005-03-07 01:58:48.000000000 -0300
@@ -126,6 +126,9 @@
 #else
 	struct disk_stats dkstats;
 #endif
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+	int scount;			/* number of supermounted partitions */
+#endif
 };
 
 /* Structure for sysfs attributes on block devices */
diff -X dontdiff-2.6 -Nur linux-2.6.11/include/linux/supermount_media.h linux-2.6.11-Gobo/include/linux/supermount_media.h
--- linux-2.6.11/include/linux/supermount_media.h	1969-12-31 21:00:00.000000000 -0300
+++ linux-2.6.11-Gobo/include/linux/supermount_media.h	2005-03-07 01:58:48.000000000 \
-0300
@@ -0,0 +1,47 @@
+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE)
+
+/*
+ * These are the "op" operation codes for mediactl() media control
+ * calls for device special files
+ */
+enum {
+	MEDIA_LOCK = 1234,	/* Lock the drive door */
+	MEDIA_UNLOCK,		/* Unlock the drive door */
+	SUPERMOUNT_INC_COUNT,
+	SUPERMOUNT_DEC_COUNT,
+};
+
+static inline int
+supermount_usage_count(struct block_device *bdev, int count)
+{
+	if (bdev) {
+		count -= bdev->bd_disk->scount;
+		if (count < 0)
+			count = 0;
+	}
+	return count;
+}
+
+static inline int
+dev_is_supermounted(struct block_device *bdev)
+{
+	return bdev ? bdev->bd_disk->scount > 0 : 0;
+}
+
+#else /* CONFIG_SUPERMOUNT */
+
+static inline int
+supermount_usage_count(struct block_device *bdev, int count)
+{
+	(void)bdev; // make compiler happy
+	return count;
+}
+
+static inline int
+dev_is_supermounted(struct block_device *bdev)
+{
+	(void)bdev; // make compiler happy
+	return 0;
+}
+
+#endif /* CONFIG_SUPERMOUNT */
Linux 2.6.10-r1 3917  65969 Luca...
The Linux Kernel.
Linux 2.6.9-r1 3917  278461 Luca...
The Linux Kernel.
Linux 2.6.8.1-r1 3917  35576 Luca...
The Linux Kernel.
Linux 2.6.7-r1 3917  22610 Luca...
The Linux Kernel.
Linux 2.6.6-r1 3917  21958 Luca...
The Linux Kernel.
Linux 2.4.26-r1 3917  22359 Luca...
The Linux Kernel.