Index: linux/Documentation/filesystems/supermount.txt
diff -u linux/Documentation/filesystems/supermount.txt:1.1 linux/Documentation/filesystems/supermount.txt:1.1.6.1
--- linux/Documentation/filesystems/supermount.txt:1.1	Mon Feb 26 17:55:03 1996
+++ linux/Documentation/filesystems/supermount.txt	Sat Nov  9 20:35:00 1996
@@ -0,0 +1,179 @@
+Supermount README
+=================
+
+For supermount v0.4c, on linux kernels 2.0.23 or later.
+
+Supermount is a pseudo-filesystem which manages filesystems on
+removable media like floppy disks and CD-ROMs.  It aims to make
+management of removable media as easy as it is under DOS.
+
+With supermount, you can change the disk in the drive whenever you
+want (with the obvious exception that you shouldn't do it when the
+filesystem is actively in use).  You don't need to "cd" out of the
+directory first, and you don't need to tell the kernel what you're
+doing --- supermount will detect the media change automatically.
+
+Supermount will automatically detect whether the media you are
+mounting is read-write or readonly, and if you mount a write-protected
+disk, then the subfs will be mounted as a readonly filesystem.
+
+Supermount detects when you have finished activity on the subfs, and
+will flush all buffers to the disk before completing the operation.
+So, if you copy a file onto a supermounted floppy disk, the data will
+all be written to disk before the "cp" command finishes.  When the
+command does complete, it will be safe to remove the disk.
+
+It is worth while defining what I mean by "activity" here.  The subfs
+is active if there are any processes running which have a handle on a
+non-directory inode on the subfs, or which have a file open on the
+subfs (even if only for reading).  There is one important case which
+does NOT count as activity: if you "cd" to a directory or a
+subdirectory under the supermount mount point, then that reference
+does not make the subfs active, and you can safely remove the disk.
+
+Yes, that's right.  You can "cd /floppy; ls" and get a listing of a
+dos floppy.  Remove the disk, insert a new one, and "ls" will now list
+the new contents.  Remove the disk altogether, and "ls" will give you
+an I/O error.  Put in a new disk, and "ls" starts working again.  It
+is NOT an error to remove a supermounted disk which is acting as the
+current working directory for one or more processes!
+
+
+The Superfilesystem and Subfilesystem concepts
+----------------------------------------------
+
+Normally, when you mount a filesystem, you create a direct connection
+between a mount point in the directory tree and a filesystem on a
+block device.  Supermount adds an extra layer in between; with
+supermount, you explicitly mount the pseudo-filesystem (the
+"superfilesystem") on the mount point, and supermount then
+automatically mounts the real filesystem (the "subfilesystem") on the
+block device when needed.
+
+Running supermount
+------------------
+
+To run supermount, compile and install a kernel with the supermount
+patches and select "Y" to the question
+
+	Dynamic mounting of removable media?
+
+when you run "make config".  You set up a supermount filesystem with
+the normal mount command, using the syntax:
+
+	mount -o <superfs-options>,--,<subfs-options> <dev> <mount-point>
+
+<dev> can be anything you want.  Using supermount, the block device
+you are using is not physically mounted until there is a filesystem
+access to the mount point, so the device specified on the command line
+is ignored.  It is convenient just to specify the mount point as the
+device as well, so that you give the mount point twice on the command
+line.  Doing this reduces the chance that the mount program will
+confuse the mount point with some other mount point defined in
+/etc/fstab.
+
+The way you specify the location of the block device you want to mount
+is by providing the <superfs-options> field, where the following
+options are currently recognised:
+
+* subfs=<filesystem-type>		[default is "msdos"]
+
+	Specify the subfilesystem type.  "msdos" and "iso9660" have
+been tested, but any block-device filesystem should work with one
+important restriction: the filesystem must NOT try to write to the
+device when you unmount it.  This is because supermount doesn't know
+in advance when it will have to unmount the subfs, so it doesn't try
+to do so until it detects that the media has been changed.  By this
+time it is too late to write to the device!
+
+	If you mount supermount as a readonly filesystem ("mount -r"
+or "mount -o ro"), then you won't have this problem.  Otherwise, you
+will not be able to use the ext2fs or minix filesystems with
+supermount.  This will hopefully be addressed in a future release of
+supermount.
+
+* dev=<block-device>			[default is "/dev/fd0"]
+
+	Specify the block device on which the subfs is to be mounted.
+
+* 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.
+
+* '--'
+
+	All options after the option string '--' will be passed
+directly to the subfilesystem when it gets mounted.
+
+
+Here is an example of supermount options, taken directly out of my
+current /etc/fstab:
+
+/floppy /floppy	supermount	--,gid=51,conv=binary	0 0
+/cd	/cd	supermount	ro,fs=iso9660,dev=/dev/hdd,--,conv=binary 0 0
+
+This tells supermount to manage a msdos filesystem on /dev/fd0 mounted
+at /floppy (msdos and /dev/fd0 are defaults for supermount), with the
+msdos filesystem getting the options "gid=51,conv=binary".  My cdrom
+on /dev/hdd is similarly mounted on /cd.
+
+
+Caveats and Provisos
+--------------------
+
+There are still some limitations to the current version of
+supermount.  I hope to overcome these shortly in future releases, but
+for now, be aware that:
+
+* You can only specify one filesystem type on the mount command line.
+  Supermount cannot yet autodetect the type of filesystem you supply.
+
+* With the 2.0 kernel, CDROM door locking has been made much more
+  aggressive.  You will probably find that once supermount has mounted
+  your disk, you cannot unmount it again.  Not helpful!
+
+* The only filesystem which is supported read/write with supermount is
+  msdos.  The msdos filesystem has the special characteristic that it
+  never has to access the disk when you unmount it, so supermount can
+  safely defer the unmounting until after the disk is removed.  That
+  doesn't work so well for filesystems such as ext2fs which try to
+  write to the filesystem when it is unmounted.
+
+Problems 2) and 3) are being solved by a development patch to support
+unmount-on-timeout, which will do a full unmount of the media after
+half a second or so of inactivity, releasing the volume for removal.
+Problem 1) has been addressed by a contributed patch which I hope to
+integrate shortly.  As usual, watch this space, and anybody who I've
+got on my list of supermount users will be emailed with further
+developments!
+
+
+Enjoy supermount.  I hope you find it useful --- I certainly find it
+extremely convenient.  Send any comments, bug-fixes or bug-reports,
+suggestions and success stories to sct@dcs.ed.ac.uk.  Flames to
+/dev/null, please!
+
+Cheers,
+ Stephen.
+--
+
+================================================================
+Changes for v0.4c:
+Works against linux-2.0.23.
+Updated documentation.
+Fixed problem with default root inode mode.
+
+Changes for v0.4:
+Performance tuning only.  Read-only operations like "find" should now
+be MUCH faster.
+
+Changes for v0.3:
+Fixed supermount_create bug; now returns a properly attached
+superinode.
+
+Changes for v0.2:
+Improved device invalidation code, so CD-ROMs work now.
+
Index: linux/fs/Config.in
diff -u linux/fs/Config.in:1.5 linux/fs/Config.in:1.5.2.1
--- linux/fs/Config.in:1.5	Wed Nov  6 23:16:50 1996
+++ linux/fs/Config.in	Sat Nov  9 17:26:20 1996
@@ -6,6 +6,7 @@
 
 bool	 'Quota support' CONFIG_QUOTA
 bool	 'Mandatory lock support' CONFIG_LOCK_MANDATORY
+bool	 'Supermount removable media support' CONFIG_SUPERMOUNT
 tristate 'Minix fs support' CONFIG_MINIX_FS
 tristate 'Extended fs support' CONFIG_EXT_FS
 tristate 'Second extended fs support' CONFIG_EXT2_FS
Index: linux/fs/Makefile
diff -u linux/fs/Makefile:1.4 linux/fs/Makefile:1.4.4.1
--- linux/fs/Makefile:1.4	Wed May 15 14:12:18 1996
+++ linux/fs/Makefile	Sat Nov  9 17:26:21 1996
@@ -17,7 +17,7 @@
 
 MOD_LIST_NAME := FS_MODULES
 ALL_SUB_DIRS = minix ext ext2 fat msdos vfat proc isofs nfs xiafs umsdos \
-		hpfs sysv smbfs ncpfs ufs affs
+		hpfs sysv smbfs ncpfs ufs affs supermount
 
 ifeq ($(CONFIG_QUOTA),y)
 O_OBJS += dquot.o
@@ -139,6 +139,14 @@
   ifeq ($(CONFIG_HPFS_FS),m)
   MOD_SUB_DIRS += hpfs
   endif
+endif
+
+ifeq ($(CONFIG_SUPERMOUNT),y)
+SUB_DIRS += supermount
+#else
+#  ifeq ($(CONFIG_SUPERMOUNT),m)
+#  MOD_SUB_DIRS += supermount
+#  endif
 endif
 
 ifeq ($(CONFIG_UFS_FS),y)
Index: linux/fs/devices.c
diff -u linux/fs/devices.c:1.3 linux/fs/devices.c:1.3.4.1
--- linux/fs/devices.c:1.3	Wed May 15 14:12:20 1996
+++ linux/fs/devices.c	Sat Nov  9 17:26:22 1996
@@ -187,15 +187,14 @@
 }
 
 /*
- * This routine checks whether a removable media has been changed,
- * and invalidates all buffer-cache-entries in that case. This
- * is a relatively slow routine, so we have to try to minimize using
- * it. Thus it is called only upon a 'mount' or 'open'. This
- * is the best way of combining speed and utility, I think.
- * People changing diskettes in the middle of an operation deserve
- * to loose :-)
- */
-int check_disk_change(kdev_t dev)
+ * These routines checks whether a removable media has been changed,
+ * and (for check_disk_change only) invalidate all
+ * buffer-cache-entries in that case. This is a relatively slow
+ * routine, so we have to try to minimize using it. Thus it is called
+ * only upon a 'mount' or 'open'. This is the best way of combining
+ * speed and utility, I think.  People changing diskettes in the
+ * middle of an operation deserve to loose :-) */
+int query_disk_change(kdev_t dev)
 {
 	int i;
 	struct file_operations * fops;
@@ -208,17 +207,37 @@
 	if (!fops->check_media_change(dev))
 		return 0;
 
+	return 1;
+}
+
+int check_disk_change(kdev_t dev)
+{
+	if (!query_disk_change(dev))
+		return 0;
 	printk(KERN_DEBUG "VFS: Disk change detected on device %s\n",
 		kdevname(dev));
+	invalidate_media(dev);
+	return 1;
+}
+
+void invalidate_media(kdev_t dev) 
+{
+	int i;
+	struct file_operations * fops;
+	
+	i = MAJOR(dev);
+	if (i >= MAX_BLKDEV)
+		return;
+	fops = blkdevs[i].fops;
 	for (i=0 ; i<NR_SUPER ; i++)
 		if (super_blocks[i].s_dev == dev)
 			put_super(super_blocks[i].s_dev);
 	invalidate_inodes(dev);
 	invalidate_buffers(dev);
 
-	if (fops->revalidate)
+	if (fops && fops->revalidate)
 		fops->revalidate(dev);
-	return 1;
+	return;
 }
 
 /*
Index: linux/fs/filesystems.c
diff -u linux/fs/filesystems.c:1.3 linux/fs/filesystems.c:1.3.4.1
--- linux/fs/filesystems.c:1.3	Sat Apr 27 17:06:29 1996
+++ linux/fs/filesystems.c	Sat Nov  9 17:26:22 1996
@@ -9,6 +9,7 @@
 #include <linux/config.h>
 #include <linux/fs.h>
 
+#include <linux/supermount_fs.h>
 #include <linux/minix_fs.h>
 #include <linux/ext_fs.h>
 #include <linux/ext2_fs.h>
@@ -39,6 +40,11 @@
 	callable = 0;
 
 	device_setup();
+
+#ifdef CONFIG_SUPERMOUNT
+	register_filesystem(&(struct file_system_type)
+		{supermount_read_super, "supermount", 0, NULL});
+#endif
 
 	binfmt_setup();
 
Index: linux/fs/inode.c
diff -u linux/fs/inode.c:1.5 linux/fs/inode.c:1.5.2.1
--- linux/fs/inode.c:1.5	Wed Nov  6 23:16:52 1996
+++ linux/fs/inode.c	Sat Nov  9 17:26:22 1996
@@ -219,6 +219,8 @@
 		if (inode == mount_root && inode->i_count ==
 		    (inode->i_mount != inode ? 1 : 2))
 			continue;
+		if (IS_UNBOUND(inode))
+			continue;
 		return 0;
 	}
 	return 1;
@@ -254,6 +256,9 @@
 	inode->i_lock = 1;	
 	inode->i_sb->s_op->write_inode(inode);
 	unlock_inode(inode);
+	if (inode->i_shadow && inode->i_shadow->i_shadow_op && 
+	    inode->i_shadow->i_shadow_op->write)
+		inode->i_shadow->i_shadow_op->write(inode->i_shadow);
 }
 
 static inline void read_inode(struct inode * inode)
@@ -414,15 +419,24 @@
 	}
 }
 
+static inline void release_shadow(struct inode * inode) {
+	/* Shadow-release should be atomic. */
+	struct inode * tmp;
+	tmp = inode->i_shadow;
+	inode->i_shadow = 0;
+}
+
 void iput(struct inode * inode)
 {
+	struct inode * shadow;
+
 	if (!inode)
 		return;
 	wait_on_inode(inode);
 	if (!inode->i_count) {
 		printk("VFS: iput: trying to free free inode\n");
 		printk("VFS: device %s, inode %lu, mode=0%07o\n",
-			kdevname(inode->i_rdev), inode->i_ino, inode->i_mode);
+			kdevname(inode->i_dev), inode->i_ino, inode->i_mode);
 		return;
 	}
 	if (inode->i_pipe)
@@ -441,9 +455,22 @@
 	}
 
 	if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->put_inode) {
+		shadow = inode->i_shadow;
 		inode->i_sb->s_op->put_inode(inode);
-		if (!inode->i_nlink)
+		if (!inode->i_nlink) {
+			/* The inode should have been cleared, so we
+                           don't reset inode->i_shadow here. */
+			if (shadow) {
+			if (shadow->i_shadow_op && 
+			    shadow->i_shadow_op->release)
+				shadow->i_shadow_op->release(shadow);
+			iput (shadow);
+			}
+			/* put_inode should do a clear_inode() if the
+			   inode is unlinked, so don't bother falling
+			   through... */
 			return;
+		}
 	}
 
 	if (inode->i_dirt) {
@@ -473,6 +500,14 @@
 	}
 
 	nr_free_inodes++;
+	shadow = inode->i_shadow;
+	inode->i_shadow = 0;
+	if (shadow) {
+		if (shadow->i_shadow_op && 
+		    shadow->i_shadow_op->release)
+			shadow->i_shadow_op->release(shadow);
+		iput (shadow);
+	}
 	return;
 }
 
Index: linux/fs/open.c
diff -u linux/fs/open.c:1.4 linux/fs/open.c:1.4.2.1
--- linux/fs/open.c:1.4	Wed Nov  6 23:16:53 1996
+++ linux/fs/open.c	Sat Nov  9 17:26:22 1996
@@ -523,6 +523,10 @@
 			goto cleanup_inode;
 	}
 
+	if (inode->i_shadow && inode->i_shadow->i_shadow_op &&
+	    inode->i_shadow->i_shadow_op->open)
+		inode->i_shadow->i_shadow_op->open(inode->i_shadow, flag);
+
 	f->f_inode = inode;
 	f->f_pos = 0;
 	f->f_reada = 0;
Index: linux/fs/super.c
diff -u linux/fs/super.c:1.9 linux/fs/super.c:1.9.2.1
--- linux/fs/super.c:1.9	Wed Nov  6 23:16:54 1996
+++ linux/fs/super.c	Sat Nov  9 17:26:23 1996
@@ -628,21 +628,12 @@
  * functions, they should be faked here.  -- jrs
  */
 
-asmlinkage int sys_umount(char * name)
+int do_umounti(struct inode * inode)
 {
-	struct inode * inode;
 	kdev_t dev;
 	int retval;
 	struct inode dummy_inode;
 
-	if (!suser())
-		return -EPERM;
-	retval = namei(name, &inode);
-	if (retval) {
-		retval = lnamei(name, &inode);
-		if (retval)
-			return retval;
-	}
 	if (S_ISBLK(inode->i_mode)) {
 		dev = inode->i_rdev;
 		if (IS_NODEV(inode)) {
@@ -681,50 +672,55 @@
 	return 0;
 }
 
-/*
- * do_mount() does the actual mounting after sys_mount has done the ugly
- * parameter parsing. When enough time has gone by, and everything uses the
- * new mount() parameters, sys_mount() can then be cleaned up.
+asmlinkage int sys_umount(char * name)
+{
+	struct inode * inode;
+	int retval;
+
+	if (!suser())
+		return -EPERM;
+	retval = namei(name,&inode);
+	if (retval) {
+		retval = lnamei(name,&inode);
+		if (retval)
+			return retval;
+	}
+	return do_umounti(inode);
+}
+
+/* 
+ * do_mountdev() does the actual mounting after sys_mount has done the
+ * ugly parameter parsing. When enough time has gone by, and
+ * everything uses the new mount() parameters, sys_mount() can then be
+ * cleaned up.
  *
  * We cannot mount a filesystem if it has active, used, or dirty inodes.
  * We also have to flush all inode-data for this device, as the new mount
- * might need new info.
+ * might need new info.  
  */
 
-int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const char * type, int flags, void * data)
+int do_mountdev(kdev_t dev, 
+		struct inode *dir_i,
+		const char * dev_name,
+		const char * dir_name,
+		const char * type, 
+		int flags,
+		void * data)
 {
-	struct inode * dir_i;
 	struct super_block * sb;
 	struct vfsmount *vfsmnt;
-	int error;
 
-	if (!(flags & MS_RDONLY) && dev && is_read_only(dev))
-		return -EACCES;
-		/*flags |= MS_RDONLY;*/
-	error = namei(dir_name, &dir_i);
-	if (error)
-		return error;
-	if (dir_i->i_count != 1 || dir_i->i_mount) {
-		iput(dir_i);
+	if (dir_i->i_count != 1 || dir_i->i_mount)
 		return -EBUSY;
-	}
-	if (!S_ISDIR(dir_i->i_mode)) {
-		iput(dir_i);
+	if (!S_ISDIR(dir_i->i_mode))
 		return -ENOTDIR;
-	}
-	if (!fs_may_mount(dev)) {
-		iput(dir_i);
+	if (!fs_may_mount(dev))
 		return -EBUSY;
-	}
 	sb = read_super(dev,type,flags,data,0);
-	if (!sb) {
-		iput(dir_i);
+	if (!sb)
 		return -EINVAL;
-	}
-	if (sb->s_covered) {
-		iput(dir_i);
+	if (sb->s_covered)
 		return -EBUSY;
-	}
 	vfsmnt = add_vfsmnt(dev, dev_name, dir_name);
 	if (vfsmnt) {
 		vfsmnt->mnt_sb = sb;
@@ -736,6 +732,26 @@
 }
 
 
+int do_mount(kdev_t dev,
+	     const char * dev_name,
+	     const char * dir_name,
+	     const char * type,
+	     int flags,
+	     void * data)
+{
+	int error;
+	struct inode * dir_i;
+
+	error = namei(dir_name, &dir_i);
+	if (error)
+		return error;
+	error = do_mountdev(dev, dir_i, dev_name, dir_name, type, flags, data);
+	if (error)
+		iput(dir_i);
+	return error;
+}
+
+  
 /*
  * Alters the mount flags of a mounted file system. Only the mount point
  * is used as a reference - file system type and the device are ignored.
@@ -794,14 +810,23 @@
 	if (!data)
 		return 0;
 
-	vma = find_vma(current->mm, (unsigned long) data);
-	if (!vma || (unsigned long) data < vma->vm_start)
-		return -EFAULT;
-	if (!(vma->vm_flags & VM_READ))
-		return -EFAULT;
-	i = vma->vm_end - (unsigned long) data;
-	if (PAGE_SIZE <= (unsigned long) i)
+	/* 
+	 * Supermount calls the mount code from kernel space, so don't
+	 * validate the mount data if it is already in kernel address
+	 * space.
+	 */
+	if (get_fs() != get_ds()) {
+		vma = find_vma(current->mm, (unsigned long) data);
+		if (!vma || (unsigned long) data < vma->vm_start)
+			return -EFAULT;
+		if (!(vma->vm_flags & VM_READ))
+			return -EFAULT;
+		i = vma->vm_end - (unsigned long) data;
+		if (PAGE_SIZE <= (unsigned long) i)
+			i = PAGE_SIZE-1;
+	} else {
 		i = PAGE_SIZE-1;
+	}
 	if (!(page = __get_free_page(GFP_KERNEL))) {
 		return -ENOMEM;
 	}
@@ -825,15 +850,10 @@
  * version that didn't understand them.
  */
 asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
-	unsigned long new_flags, void * data)
+			 unsigned long new_flags, void * data)
 {
-	struct file_system_type * fstype;
-	struct inode * inode;
-	struct file_operations * fops;
-	kdev_t dev;
+	struct inode * dir_inode;
 	int retval;
-	const char * t;
-	unsigned long flags = 0;
 	unsigned long page = 0;
 
 	if (!suser())
@@ -849,6 +869,29 @@
 		free_page(page);
 		return retval;
 	}
+	retval = namei(dir_name, &dir_inode);
+	if (retval)
+		return retval;
+	retval = do_mounti(dir_inode,dir_name,dev_name,type,new_flags,data);
+	if (retval)
+		iput(dir_inode);
+	return retval;
+}
+
+/* Mount on a given inode.  Don't iput() the mount point! */
+int do_mounti(struct inode * dir_inode, const char * dir_name, 
+	      const char * dev_name, const char * type, 
+	      unsigned long new_flags, const void * data)
+{
+	struct file_system_type * fstype;
+	struct inode * inode;
+	struct file_operations * fops;
+	dev_t dev;
+	int retval;
+	const char * t;
+	unsigned long flags = 0;
+	unsigned long page = 0;
+
 	retval = copy_mount_options (type, &page);
 	if (retval < 0)
 		return retval;
@@ -906,7 +949,8 @@
 			return retval;
 		}
 	}
-	retval = do_mount(dev,dev_name,dir_name,t,flags,(void *) page);
+	retval = do_mountdev(dev,dir_inode,dev_name,dir_name,
+			     t,flags,(void *) page);
 	free_page(page);
 	if (retval && fops && fops->release)
 		fops->release(inode, NULL);
Index: linux/fs/supermount/Makefile
diff -u linux/fs/supermount/Makefile:1.1 linux/fs/supermount/Makefile:1.1.6.1
--- linux/fs/supermount/Makefile:1.1	Mon Feb 26 17:55:15 1996
+++ linux/fs/supermount/Makefile	Sat Nov  9 17:27:24 1996
@@ -0,0 +1,14 @@
+#
+# Makefile for the linux supermounting routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := supermount.o
+O_OBJS	 := dir.o inode.o namei.o super.o
+M_OBJS	 := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
Index: linux/fs/supermount/TODO
diff -u linux/fs/supermount/TODO:1.1 linux/fs/supermount/TODO:1.1.6.1
--- linux/fs/supermount/TODO:1.1	Mon Feb 26 17:55:15 1996
+++ linux/fs/supermount/TODO	Sat Nov  9 17:27:24 1996
@@ -0,0 +1,40 @@
+Notes:
+
+TODO:
+
+  Supermount directory inodes do auto-remount as root of the subfs.  Make
+  a mount option to restrict this behaviour to the root?
+
+  Make supermount_attach a special case of read_hidden_inode, not the other
+  way around.
+
+  Replace shadow inodes with shadow superblock?  Do release that way?
+
+  Unmount on suspend?  OK iff inode numbers on open directories will
+  be the same next time around!  (Normally true.)  Bad for performance, but
+  maybe necessary for ext2fs, for example.
+
+Done:::
+
+  Sigh.  We can't rely on the one-to-one mapping of superinode to
+  subinode i_ino numbers any more.  Why not?  Well, what happens if we
+  remove and remount a medium?  We can end up with one process holding
+  a handle to an old, obsolete superinode, and a new process opening a
+  subinode with the same i_ino.  Well, we can do a quadratic hash on
+  the superinode numbers to avoid collisions.  Think about this!
+
+  * What happens when we do get an inode collision?  We rehash to make
+    another superinode the current one; the old inode remains as a
+    placeholder.  But then, what if the old inode becomes released?  A
+    subsequent attach may place the subinode under that old inode
+    instead of the new superinode.
+
+    OK: There is only ever one hidden/shadow connection between super
+    and sub inodes.  If that is broken, we can reestablish it from any
+    superinode we want to.  Previously valid superinodes will have to
+    rely on i_subino to get to their subinode (the subinode i_ino will
+    NOT change behind our backs, hopefully).  Such mappings should
+    only ever be one way, from superinode to subinode, done by
+    subiget().
+
+  Special handling of root directory to do auto remount.
Index: linux/fs/supermount/dir.c
diff -u linux/fs/supermount/dir.c:1.1 linux/fs/supermount/dir.c:1.1.6.1
--- linux/fs/supermount/dir.c:1.1	Mon Feb 26 17:55:15 1996
+++ linux/fs/supermount/dir.c	Sat Nov  9 17:27:24 1996
@@ -0,0 +1,97 @@
+/*
+ *  linux/fs/supermount/dir.c
+ *
+ *  Copyright (C) 1995
+ *  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
+ */
+
+#include <asm/segment.h>
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/supermount_fs.h>
+#include <linux/stat.h>
+
+static int supermount_dir_open (struct inode *, struct file *);
+
+static struct file_operations supermount_dir_operations = {
+	NULL,			/* lseek - default */
+	NULL,			/* read */
+	NULL,			/* write - bad */
+	NULL,			/* readdir */
+	NULL,			/* select - default */
+	NULL,			/* ioctl */
+	NULL,			/* mmap */
+	supermount_dir_open,	/* Redirect to the subfs readdir() code */
+	NULL,			/* no special release code */
+	NULL,			/* fsync */
+	NULL,			/* fasync */
+	NULL,			/* check_media_change */
+	NULL			/* revalidate */
+};
+
+/*
+ * directories can handle most operations...  supermount/namei.c just
+ * passes them through to the underlying subfs, except for lookup().
+ */
+struct inode_operations supermount_dir_iops = {
+	&supermount_dir_operations,	/* default directory file-ops */
+	supermount_create,		/* create */
+	supermount_lookup,		/* lookup */
+	supermount_link,		/* link */
+	supermount_unlink,		/* unlink */
+	supermount_symlink,		/* symlink */
+	supermount_mkdir,		/* mkdir */
+	supermount_rmdir,		/* rmdir */
+	supermount_mknod,		/* mknod */
+	supermount_rename,		/* rename */
+	NULL,				/* readlink */
+	NULL,				/* follow_link */
+	NULL,				/* bmap */
+	NULL,				/* truncate */
+	supermount_permission,		/* permission */
+	NULL				/* smap */
+};
+
+
+/* When we open() a directory (for readdir), just rewrite the file
+   struct to point to the subfs inode, and let it handle all the
+   work. */
+static int supermount_dir_open (struct inode * inode, struct file * file)
+{
+	struct inode * subi;
+	int rc;
+	
+	if (file->f_mode & 2)
+		return -EPERM;
+
+	supermount_debug ("dir_open inode %ld\n", inode->i_ino);
+	
+	subi = subiget(inode);
+	if (!subi)
+		return -ENOENT;
+
+	file->f_inode = subi;
+	file->f_op = subi->i_op ? subi->i_op->default_file_ops : NULL;
+	if (file->f_op && file->f_op->open) {
+		rc = file->f_op->open(subi, file);
+		if (rc) {
+			iput(subi);
+			return rc;
+		}
+	}
+	iput(inode);
+	return 0;
+}
+
Index: linux/fs/supermount/file.c
diff -u linux/fs/supermount/file.c:1.1 linux/fs/supermount/file.c:1.1.6.1
--- linux/fs/supermount/file.c:1.1	Mon Feb 26 17:55:15 1996
+++ linux/fs/supermount/file.c	Sat Nov  9 17:27:25 1996
@@ -0,0 +1,21 @@
+/*
+ *  linux/fs/supermount/file.c
+ *
+ *  Copyright (C) 1995
+ *  Stephen Tweedie (sct@dcs.ed.ac.uk)
+ *
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/supermount_fs.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+
+static void supermount_inode_release (struct inode *);
+
Index: linux/fs/supermount/inode.c
diff -u linux/fs/supermount/inode.c:1.1 linux/fs/supermount/inode.c:1.1.6.1
--- linux/fs/supermount/inode.c:1.1	Mon Feb 26 17:55:16 1996
+++ linux/fs/supermount/inode.c	Sat Nov  9 17:27:25 1996
@@ -0,0 +1,380 @@
+/*
+ *  linux/fs/supermount/inode.c
+ *
+ *  Copyright (C) 1995
+ *  Stephen Tweedie (sct@dcs.ed.ac.uk)
+ *
+ *  from
+ *
+ *  linux/fs/minix/inode.c
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  and
+ *
+ *  linux/fs/ext2/inode.c
+ *  Copyright (C) 1992, 1993, 1994, 1995  Remy Card
+ *  and           1993  Stephen Tweedie
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/supermount_fs.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/mm.h>
+
+static void read_hidden_inode (struct inode *);
+static void supermount_inode_open (struct inode *, int);
+static void supermount_inode_write (struct inode *);
+static void supermount_inode_release (struct inode *);
+
+/* Deal with the shadow interface.  Whenever a subfs inode is
+   released, break the bi-directional link between them, and close and
+   iput the supermount inode.  */
+
+struct inode_shadow_operations supermount_shadow_iops = {
+	supermount_inode_open,
+	supermount_inode_write,
+	supermount_inode_release,
+};
+
+void supermount_inode_open (struct inode *inode, int flag)
+{
+	if (flag & 2)
+		mark_subfs_dirty(inode->i_sb);
+}
+
+void supermount_inode_write (struct inode *inode)
+{
+	mark_subfs_dirty(inode->i_sb);
+}
+
+void supermount_inode_release (struct inode *inode)
+{
+	supermount_debug ("inode = %ld\n", inode->i_ino);
+	if (inode->u.supermount_i.i_hidden)
+		supermount_debug("subinode = %ld\n", 
+				 inode->u.supermount_i.i_hidden->i_ino);
+	
+	inode->u.supermount_i.i_hidden = NULL;
+	supermount_iclose(inode);
+}
+
+
+/* Do an iget on the appropriate subfs inode */
+struct inode * subiget(struct inode * inode) 
+{
+	struct inode * tmp;
+	supermount_debug("subiget inode %ld\n", inode->i_ino);
+
+	if (!S_ISDIR(inode->i_mode))
+		return 0;
+	if (supermount_media_check(inode->i_sb)) {
+		supermount_debug("media is invalid\n");
+		return 0;
+	}
+	
+	if (inode_is_obsolete(inode)) {
+		/* For obsolete directory with new mounted media ---
+                   return the new root subinode. */
+		if (inode->u.supermount_i.i_hidden) 
+			supermount_panic (inode->i_sb, "subiget", 
+					  "Hidden inode on obsolete inode %ld",
+					  inode->i_ino);
+		tmp = inode->i_sb->u.supermount_sb.s_subfs->s_mounted;
+		inode->u.supermount_i.i_subino = tmp->i_ino;
+		tmp->i_count++;
+		supermount_debug("mapping obsolete inode %ld to "
+				 "subroot inode %ld\n",
+				 inode->i_ino, tmp->i_ino);
+		return tmp;
+	}
+	
+	if (inode->u.supermount_i.i_hidden) {
+		inode->u.supermount_i.i_hidden->i_count++;
+		supermount_debug("inode %ld/%ld, found hidden, "
+				 "count now %d/%d\n",
+				 inode->i_ino,
+				 inode->u.supermount_i.i_hidden->i_ino,
+				 inode->i_count,
+				 inode->u.supermount_i.i_hidden->i_count);
+		return inode->u.supermount_i.i_hidden;
+	}
+	read_hidden_inode(inode);
+	if (!inode->u.supermount_i.i_hidden) {
+		supermount_debug("inode %ld, no subinode, count=%d\n",
+				 inode->i_ino, inode->i_count);
+		return 0;
+	}
+	
+	supermount_debug("inode %ld/%ld, found new, "
+			 "count now %d/%d\n",
+			 inode->i_ino,
+			 inode->u.supermount_i.i_hidden->i_ino,
+			 inode->i_count,
+			 inode->u.supermount_i.i_hidden->i_count);
+	return inode->u.supermount_i.i_hidden;
+}
+
+/* We sometimes do a lookup on a subinode and want to get back a
+   superinode in return, with a single iget() outstanding on both (not
+   counting the i_shadow reference).  supermount_attach generates the
+   superinode for a given subinode. */
+struct inode * supermount_attach(struct super_block *sb, struct inode *inode) 
+{
+	struct inode *tmp;
+	unsigned long new_ino, incr=1237;
+	
+	supermount_debug ("inode = %ld\n", inode->i_ino);
+	
+	if (inode->i_shadow) {
+		supermount_debug ("return existing shadow %ld, count %d/%d\n",
+				  inode->i_shadow->i_ino,
+				  inode->i_shadow->i_count, inode->i_count);
+		inode->i_shadow->i_count++;
+		return inode->i_shadow;
+	}
+	
+	/* The rules are a little different at the root. */
+	if (inode == sb->u.supermount_sb.s_subfs->s_mounted) {
+		tmp = sb->s_mounted;
+		supermount_debug ("return existing superfs root %ld, "
+				  "count %d/%d\n",
+				  tmp->i_ino, tmp->i_count, inode->i_count);
+		tmp->i_count++;
+		return tmp;
+	}
+
+	/* Find a new superinode, avoiding collisions with old
+           obsolete superinodes by a quadratic hash. */
+	new_ino = inode->i_ino;
+	for ( ; (tmp = iget(sb, new_ino)) && tmp->i_count > 1 &&
+			inode_is_obsolete(tmp); ) {
+		iput(tmp);
+		new_ino += incr;
+		new_ino &= 0xffffff;
+		incr <<= 1; incr++;
+	}
+	
+	tmp->u.supermount_i.i_subino = inode->i_ino;
+	/* If we found a previously obsolete inode which is now no
+           longer used, we can safely remove the obsolesence flag. */
+	if (inode_is_obsolete(tmp)) {
+		tmp->u.supermount_i.i_sb_version = 
+			tmp->i_sb->u.supermount_sb.s_version;
+	}
+	
+	/* We have now done iget() on superi and subi.  Doing the
+           attachment may incur another iget() on the subinode. */
+	if (!tmp->u.supermount_i.i_hidden) {
+		read_hidden_inode(tmp);
+		iput(inode);
+	}
+	return tmp;
+}
+
+/* Supermount open/close inode.  These functions maintain the
+   superblock reference counts of active inodes; the subfs is
+   suspended when that count reaches zero.
+
+   Files are opened on any significant reference, and are not closed
+   until they become fully dereferenced (during the last iput).
+   Directories, on the other hand, are always closed unless they are
+   active and opened; a directory referenced as CWD is not open.
+
+   The difference is in the way the application gets given inodes.
+   For dirs and symlinks, it will be given the superinode to deal
+   with; for other file types, we return the subinode. */
+
+
+void supermount_iopen(struct inode * inode)
+{
+	supermount_debug ("inode %ld, count %d\n", 
+			  inode->i_ino, inode->i_count);
+	if (inode->i_sb->u.supermount_sb.s_state == SUPERMOUNT_UNMOUNTED)
+		supermount_panic (inode->i_sb, "supermount_iopen", 
+				  "opening inode on unmounted subfs");
+	if (!inode->u.supermount_i.i_counted) {
+		supermount_debug("Opened inode %ld\n", inode->i_ino);
+		inode->u.supermount_i.i_counted = 1;
+		if (!subfs_is_active(inode->i_sb)) {
+			supermount_debug ("going online.\n");
+			inode->i_sb->u.supermount_sb.s_state = 
+				SUPERMOUNT_ONLINE;
+		}
+		inode->i_sb->u.supermount_sb.s_opencount++;
+	}
+}
+
+void supermount_iclose(struct inode * inode)
+{
+	supermount_debug ("inode %ld, count %d\n", 
+			  inode->i_ino, inode->i_count);
+	if (inode->u.supermount_i.i_counted) {
+		supermount_debug("Closed inode %ld\n", inode->i_ino);
+		inode->u.supermount_i.i_counted = 0;
+		inode->i_sb->u.supermount_sb.s_opencount--;
+		if (!subfs_is_active(inode->i_sb))
+			supermount_go_inactive(inode->i_sb);
+	}
+}
+
+void supermount_go_inactive(struct super_block *sb)
+{
+	supermount_debug("Checking state\n");
+	if (!subfs_is_active(sb)) {
+		supermount_debug("going offline.\n");
+		sb->u.supermount_sb.s_state = 
+			SUPERMOUNT_SUSPENDED;
+		if (subfs_is_dirty(sb)) {
+			mark_subfs_clean(sb);
+			fsync_dev(sb->u.supermount_sb.s_subfs->s_dev);
+		}
+	}
+}
+
+static void read_hidden_inode (struct inode * inode)
+{
+	struct super_block * sb;
+	struct inode * tmp;
+
+	supermount_debug("inode = %ld\n", inode->i_ino);
+	supermount_media_check(inode->i_sb);
+
+	if (inode->i_sb->u.supermount_sb.s_state ==
+	    SUPERMOUNT_UNMOUNTED) {
+		supermount_panic (inode->i_sb, "read_hidden_inode",
+				  "Trying to read inode on unmounted media");
+	}
+	sb=inode->i_sb->u.supermount_sb.s_subfs;
+	/* The root inode is a bit special.  For that one, the
+           subinode is the root of the subfs, and we can't guarantee
+           its inode number in advance. */
+	if (inode->i_ino == SUPERMOUNT_ROOT_INO) {
+		tmp = sb->s_mounted;
+		tmp->i_count++;
+	} else {
+		if (!inode->u.supermount_i.i_subino)
+			inode->u.supermount_i.i_subino = inode->i_ino;
+		tmp = iget(sb, inode->u.supermount_i.i_subino);
+	}
+	if (!tmp)
+		return;
+
+	/* If the inode is already linked to its superinode, don't do
+           any further processing.  Also, don't bother trying to
+           attach to the superinode if we are reading from an obsolete
+           superinode. */
+	if (tmp->i_shadow || inode_is_obsolete(inode))
+		return;
+	tmp->i_shadow = inode;
+	inode->u.supermount_i.i_hidden = tmp;
+	inode->i_count++;
+	supermount_iopen(inode);
+
+	inode->i_mode = tmp->i_mode;
+	inode->i_uid = tmp->i_uid;
+	inode->i_gid = tmp->i_gid;
+	inode->i_nlink = tmp->i_nlink;
+	inode->i_size = tmp->i_size;
+	inode->i_atime = tmp->i_atime;
+	inode->i_ctime = tmp->i_ctime;
+	inode->i_mtime = tmp->i_mtime;
+	inode->i_blksize = tmp->i_blksize;
+	inode->i_blocks = tmp->i_blocks;
+	inode->i_rdev = tmp->i_rdev;
+	inode->i_version = ++event;
+
+	if (S_ISDIR(inode->i_mode))
+		inode->i_op = &supermount_dir_iops;
+	else
+		inode->i_op = NULL;
+}
+
+void supermount_read_inode (struct inode * inode)
+{
+	supermount_debug ("inode = %ld\n", inode->i_ino);
+	
+	inode->i_shadow_op = &supermount_shadow_iops;
+	inode->u.supermount_i.i_sb_version = 
+		inode->i_sb->u.supermount_sb.s_version;
+	
+	switch (inode->i_ino) {
+	case SUPERMOUNT_ROOT_INO:
+		if (inode->i_sb->u.supermount_sb.s_state !=
+		    SUPERMOUNT_UNMOUNTED) {
+			supermount_panic (inode->i_sb, "supermount_read_inode",
+					  "Help - trying to read root while "
+					  "already mounted!");
+		}
+		/* Fall through */
+	case SUPERMOUNT_HIDDEN_INO:
+		inode->i_mode = inode->i_sb->u.supermount_sb.s_default_mode;
+		inode->i_uid = 0;
+		inode->i_gid = 0;
+		inode->i_nlink = 1;
+		inode->i_size = 0;
+		inode->i_atime = CURRENT_TIME;
+		inode->i_ctime = CURRENT_TIME;
+		inode->i_mtime = CURRENT_TIME;
+		inode->i_blksize = inode->i_sb->s_blocksize;
+		inode->i_blocks = 0;
+		inode->i_version = ++event;
+		inode->i_op = (inode->i_ino == SUPERMOUNT_ROOT_INO)
+			? &supermount_dir_iops : NULL;
+		return;
+	default:
+		;
+	}
+}
+
+void supermount_write_inode (struct inode * inode)
+{
+	struct super_block *tmp;
+	tmp = inode->i_sb->u.supermount_sb.s_subfs;
+	
+	if (inode->u.supermount_i.i_hidden &&
+	    tmp && tmp->s_op && tmp->s_op->write_inode)
+		tmp->s_op->write_inode(inode->u.supermount_i.i_hidden);
+
+	mark_subfs_dirty(inode->i_sb);
+}
+
+int supermount_permission (struct inode * inode, int mask)
+{
+	unsigned short mode;
+	struct inode *subi;
+
+	supermount_debug("inode %ld, mask 0%o\n", inode->i_ino, mask);
+
+	subi = subiget(inode);
+	if (!subi)
+		return -EIO;
+	if (subi->i_op && subi->i_op->permission) {
+		int rc = subi->i_op->permission(subi, mask);
+		iput(subi);
+		return rc;
+	}
+	
+	mode = inode->i_mode;
+	/*
+	 * Special case, access is always granted for root
+	 */
+	if (fsuser()) {
+		iput(subi);
+		return 0;
+	} else if (current->fsuid == inode->i_uid)
+		mode >>= 6;
+	else if (in_group_p (inode->i_gid))
+		mode >>= 3;
+	iput(subi);
+	if (((mode & mask & S_IRWXO) == mask))
+		return 0;
+	else
+		return -EACCES;
+}
Index: linux/fs/supermount/namei.c
diff -u linux/fs/supermount/namei.c:1.1 linux/fs/supermount/namei.c:1.1.6.1
--- linux/fs/supermount/namei.c:1.1	Mon Feb 26 17:55:16 1996
+++ linux/fs/supermount/namei.c	Sat Nov  9 17:27:25 1996
@@ -0,0 +1,348 @@
+/*
+ *  linux/fs/supermount/namei.c
+ *
+ *  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
+ */
+
+/* This file handles almost all of the normal filesystem running of
+   supermount.  We don't have to deal to much with the subfs
+   interface, since we just pass subinodes out to the application on
+   demand. */
+
+#include <asm/segment.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/supermount_fs.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+
+/* Attach a superinode to a subinode, and prepare a result for the
+   upper VFS layers.  We return the superinode for directories, and
+   the subinode for other file types.
+   */
+static void attach_and_prepare(struct super_block *sb,
+			       struct inode *subi, struct inode **result)
+{
+	struct inode *superi;
+	superi = supermount_attach(sb, subi);
+	/* We have now done iget() on superi and subi. */
+	
+	/* If the subfs inode is a directory or a symlink, then we
+	   need to deal with it ourselves, and only give back the
+	   superinode to the application.  Otherwise, we can just
+	   return the subfs inode. */
+	if (S_ISDIR(subi->i_mode)) {
+		superi->i_op = &supermount_dir_iops;
+		iput(subi);
+		*result = superi;
+		return;
+	} else {
+		/* Not a directory-related inode, so we return the
+		   subfs inode to the upper layers.  We still need the
+		   supermount inode in that case, though, to maintain
+		   reference counts in the supermount superblock. */
+
+		iput(superi);
+		*result = subi;
+		return;
+	}
+}
+
+int supermount_lookup (struct inode * dir, const char * name, int len,
+		       struct inode ** result)
+{
+	struct inode *subi, *subdir;
+	int rc;
+	
+	supermount_debug ("inode = %ld, name = %*s\n",
+			  dir->i_ino, len, name);
+
+	*result = NULL;
+	if (!dir)
+		return -ENOENT;
+	if (!S_ISDIR(dir->i_mode)) {
+		iput(dir);
+		return -ENOENT;
+	}
+	if (!(subdir = subiget(dir)))  {
+		/* This now becomes the *easy* case. :-) */
+		iput (dir);
+		return -EIO;
+	}
+
+	rc = subdir->i_op->lookup(subdir, name, len, &subi);
+	supermount_go_inactive(dir->i_sb);
+	iput (dir);
+	if (rc)
+		return rc;
+	/* It worked, so now create a shadow supermount inode for the
+           result... */
+	attach_and_prepare(dir->i_sb, subi, result);
+	return 0;
+}
+
+int supermount_create (struct inode * dir,const char * name, int len, int mode,
+		       struct inode ** result)
+{
+	struct inode * inode, * subi;
+	int rc;
+	
+	supermount_debug ("inode = %ld, name = %*s, mode = %o\n",
+			  dir->i_ino, len, name, mode);
+
+	*result = NULL;
+	if (!dir)
+		return -ENOENT;
+	inode = subiget(dir);
+	if (!inode) {
+		iput(dir);
+		return -ENOENT;
+	}
+	if (!inode->i_op || !inode->i_op->create) {
+		iput(dir);
+		iput(inode);
+		return -EPERM;
+	}
+	
+	rc = inode->i_op->create(inode, name, len, mode, &subi);
+	mark_subfs_dirty(dir->i_sb);
+	supermount_go_inactive(dir->i_sb);
+	iput(dir);
+	if (rc)
+		return rc;
+	attach_and_prepare(dir->i_sb, subi, result);
+	return 0;
+}
+
+int supermount_mknod (struct inode * dir, const char * name, int len, int mode,
+		      int rdev)
+{
+	struct inode * inode;
+	int rc;
+	
+	supermount_debug ("inode = %ld, name = %*s, mode = %o\n",
+			  dir->i_ino, len, name, mode);
+
+	if (!dir)
+		return -ENOENT;
+	inode = subiget(dir);
+	if (!inode) {
+		iput(dir);
+		return -ENOENT;
+	}
+	if (!inode->i_op || !inode->i_op->mknod) {
+		iput(dir);
+		iput(inode);
+		return -EPERM;
+	}
+	
+	rc = inode->i_op->mknod(inode, name, len, mode, rdev);
+	mark_subfs_dirty(dir->i_sb);
+	supermount_go_inactive(dir->i_sb);
+	iput(dir);
+	return rc;
+}
+
+int supermount_mkdir (struct inode * dir, const char * name, int len, int mode)
+{
+	struct inode * inode;
+	int rc;
+	
+	supermount_debug ("inode = %ld, name = %*s, mode = %o\n",
+			  dir->i_ino, len, name, mode);
+
+	if (!dir)
+		return -ENOENT;
+	inode = subiget(dir);
+	if (!inode) {
+		iput(dir);
+		return -ENOENT;
+	}
+	if (!inode->i_op || !inode->i_op->mkdir) {
+		iput(dir);
+		iput(inode);
+		return -EPERM;
+	}
+	
+	rc = inode->i_op->mkdir(inode, name, len, mode);
+	mark_subfs_dirty(dir->i_sb);
+	supermount_go_inactive(dir->i_sb);
+	iput(dir);
+	return rc;
+}
+
+int supermount_rmdir (struct inode * dir, const char * name, int len)
+{
+	struct inode * inode;
+	int rc;
+	
+	supermount_debug ("inode = %ld, name = %*s\n",
+			  dir->i_ino, len, name);
+
+	if (!dir)
+		return -ENOENT;
+	inode = subiget(dir);
+	if (!inode) {
+		iput(dir);
+		return -ENOENT;
+	}
+	if (!inode->i_op || !inode->i_op->rmdir) {
+		iput(dir);
+		iput(inode);
+		return -EPERM;
+	}
+	
+	rc = inode->i_op->rmdir(inode, name, len);
+	mark_subfs_dirty(dir->i_sb);
+	supermount_go_inactive(dir->i_sb);
+	iput(dir);
+	return rc;
+}
+
+int supermount_unlink (struct inode * dir, const char * name, int len)
+{
+	struct inode * inode;
+	int rc;
+	
+	supermount_debug ("inode = %ld, name = %*s\n",
+			  dir->i_ino, len, name);
+
+	if (!dir)
+		return -ENOENT;
+	inode = subiget(dir);
+	if (!inode) {
+		iput(dir);
+		return -ENOENT;
+	}
+	if (!inode->i_op || !inode->i_op->unlink) {
+		iput(dir);
+		iput(inode);
+		return -EPERM;
+	}
+	
+	rc = inode->i_op->unlink(inode, name, len);
+	mark_subfs_dirty(dir->i_sb);
+	supermount_go_inactive(dir->i_sb);
+	iput(dir);
+	return rc;
+}
+
+int supermount_symlink (struct inode * dir, const char * name, int len,
+			const char * symname)
+{
+	struct inode * inode;
+	int rc;
+	
+	supermount_debug ("inode = %ld, name = %*s, link = %s\n",
+			  dir->i_ino, len, name, symname);
+
+	if (!dir)
+		return -ENOENT;
+	inode = subiget(dir);
+	if (!inode) {
+		iput(dir);
+		return -ENOENT;
+	}
+	if (!inode->i_op || !inode->i_op->symlink) {
+		iput(dir);
+		iput(inode);
+		return -EPERM;
+	}
+	
+	rc = inode->i_op->symlink(inode, name, len, symname);
+	mark_subfs_dirty(dir->i_sb);
+	supermount_go_inactive(dir->i_sb);
+	iput(dir);
+	return rc;
+}
+
+/* This probably won't work because higher levels will complain about
+   cross-device links. */
+int supermount_link (struct inode * oldinode, struct inode * dir,
+		     const char * name, int len)
+{
+	struct inode * inode;
+	int rc;
+	
+	supermount_debug ("inode = %ld, name = %*s, link to inode %ld\n",
+			  dir->i_ino, len, name, oldinode->i_ino);
+
+	if (!dir)
+		return -ENOENT;
+	inode = subiget(dir);
+	if (!inode) {
+		iput(dir);
+		return -ENOENT;
+	}
+	if (!inode->i_op || !inode->i_op->link) {
+		iput(dir);
+		iput(inode);
+		return -EPERM;
+	}
+	
+	rc = inode->i_op->link(oldinode, inode, name, len);
+	mark_subfs_dirty(dir->i_sb);
+	supermount_go_inactive(dir->i_sb);
+	iput(dir);
+	return rc;
+}
+
+int supermount_rename (struct inode * old_dir, const char * old_name, 
+		       int old_len,
+		       struct inode * new_dir, const char * new_name, 
+		       int new_len, int must_be_dir)
+{
+	struct inode * old_subi, * new_subi;
+	int rc;
+	
+	supermount_debug ("from inode %ld, %*s to inode %ld, %*s\n",
+			  old_dir->i_ino, old_len, old_name,
+			  new_dir->i_ino, new_len, new_name);
+
+	if (!old_dir || !new_dir)
+		return -ENOENT;
+	old_subi = subiget(old_dir);
+	if (!old_subi) {
+		iput(old_dir);
+		iput(new_dir);
+		return -ENOENT;
+	}
+	new_subi = subiget(new_dir);
+	if (!new_subi) {
+		iput(old_dir);
+		iput(new_dir);
+		iput(old_subi);
+		return -ENOENT;
+	}
+
+	if (!old_subi->i_op || !old_subi->i_op->rename) {
+		iput(old_dir);
+		iput(new_dir);
+		iput(old_subi);
+		iput(new_subi);
+		return -EPERM;
+	}
+	
+	rc = old_subi->i_op->rename(old_subi, old_name, old_len,
+				    new_subi, new_name, new_len, 
+				    must_be_dir);
+	mark_subfs_dirty(old_dir->i_sb);
+	supermount_go_inactive(old_dir->i_sb);
+	iput(old_dir);
+	iput(new_dir);
+	return rc;
+}
Index: linux/fs/supermount/super.c
diff -u linux/fs/supermount/super.c:1.1 linux/fs/supermount/super.c:1.1.6.2
--- linux/fs/supermount/super.c:1.1	Mon Feb 26 17:55:16 1996
+++ linux/fs/supermount/super.c	Sat Nov  9 19:57:42 1996
@@ -0,0 +1,431 @@
+/*
+ *  linux/fs/supermount/super.c
+ *
+ *  Copyright (C) 1995
+ *  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
+ */
+
+#include <stdarg.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/supermount_fs.h>
+#include <linux/malloc.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/major.h>
+
+/* From fs/super.c */
+extern int do_umount(kdev_t);
+
+#ifdef SUPERMOUNT_DEBUG
+char supermount_debug_enable = 0;
+#endif
+
+static struct super_operations supermount_sops = { 
+	supermount_read_inode,
+	NULL,
+	supermount_write_inode,
+	NULL,				/* put_inode */
+	supermount_put_super,
+	supermount_write_super,
+	supermount_statfs,
+	NULL,				 /* supermount_remount */
+};
+
+static char error_buf[1024];
+
+/* Mount and mount the hidden fs */
+static void umount_subfs(struct super_block *);
+
+void supermount_error (struct super_block * sb, const char * function,
+		       const char * fmt, ...)
+{
+	va_list args;
+
+	va_start (args, fmt);
+	vsprintf (error_buf, fmt, args);
+	va_end (args);
+	printk (KERN_CRIT "SUPERMOUNT error (device %d/%d): %s: %s\n",
+		MAJOR(sb->s_dev), MINOR(sb->s_dev), function, error_buf);
+	/* @@ Do we want to do any special error handling here? */
+}
+
+NORET_TYPE void supermount_panic (struct super_block * sb,
+				  const char * function,
+				  const char * fmt, ...)
+{
+	va_list args;
+
+	va_start (args, fmt);
+	vsprintf (error_buf, fmt, args);
+	va_end (args);
+	panic ("SUPERMOUNT panic (device %d/%d): %s: %s\n",
+	       MAJOR(sb->s_dev), MINOR(sb->s_dev), function, error_buf);
+}
+
+void supermount_warning (struct super_block * sb, const char * function,
+		   const char * fmt, ...)
+{
+	va_list args;
+
+	va_start (args, fmt);
+	vsprintf (error_buf, fmt, args);
+	va_end (args);
+	printk (KERN_WARNING "SUPERMOUNT-fs warning (device %d/%d): %s: %s\n",
+		MAJOR(sb->s_dev), MINOR(sb->s_dev), function, error_buf);
+}
+
+/* Release the superblock and any resources it has reserved */
+void supermount_put_super (struct super_block * sb)
+{
+	lock_super (sb);
+	if (sb->u.supermount_sb.s_state != SUPERMOUNT_UNMOUNTED)
+		umount_subfs (sb);
+	sb->s_dev = 0;
+	unlock_super (sb);
+}
+
+
+static int copy_option(const char **option, const char *val)
+{
+	char *tmp;
+	supermount_debug ("assigning value \"%s\"\n", val);
+	if (!val || !*val)
+		return -EINVAL;
+	tmp = (char *) kmalloc(1 + strlen(val), GFP_KERNEL);
+	if (!tmp)
+		return -ENOMEM;
+	strcpy(tmp, val);
+	*option = tmp;
+	return 0;
+}
+
+/*
+ * This function has been shamelessly adapted from the msdos fs
+ */
+static int parse_options (char * options, struct super_block *sb)
+{
+	char * this_char;
+	char * value;
+	int rc;
+	
+	if (!options)
+		return 0;
+	while ((this_char = options)) {
+		if (!strncmp(this_char, "--,", 3))
+			this_char += 2;
+		if (*this_char == ',') {
+			/* An empty option, or the option "--",
+			   introduces options to be passed through to
+			   the subfs */
+			supermount_debug ("assigning remainder\n");
+			return copy_option(&sb->u.supermount_sb.s_data,
+					   ++this_char);
+		}
+		if ((options = strchr (this_char, ',')))
+			*options++ = 0;
+		
+		if ((value = strchr (this_char, '=')) != NULL)
+			*value++ = 0;
+
+		supermount_debug ("parsing option \"%s\"\n", this_char);
+		if (!strcmp (this_char, "fs")) {
+			rc = copy_option(&sb->u.supermount_sb.s_type, value);
+			if (rc) return rc;
+		} else if (!strcmp (this_char, "dev")) {
+			rc = copy_option(&sb->u.supermount_sb.s_devname, 
+					 value);
+			if (rc) return rc;
+		} else if (!strcmp (this_char, "debug")) {
+			supermount_debug_enable = 1;
+		} else {
+			printk ("supermount: "
+				"Unrecognized mount option %s\n", this_char);
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+/* read_super: the main mount() entry point into the VFS layer. */
+struct super_block * supermount_read_super (struct super_block * sb, 
+					    void * data,
+					    int silent)
+{
+	sb->s_blocksize = 1024;
+	sb->s_blocksize_bits = 10;
+	sb->s_magic = SUPERMOUNT_SUPER_MAGIC;
+	sb->s_op = &supermount_sops;
+	unlock_super(sb);
+	sb->u.supermount_sb.s_state = SUPERMOUNT_UNMOUNTED;
+	sb->u.supermount_sb.s_default_mode = 0777 | S_IFDIR;
+	sb->u.supermount_sb.s_mflags = sb->s_flags & (MS_REMOUNT - 1);
+	sb->u.supermount_sb.s_data = NULL;
+	sb->u.supermount_sb.s_undermount = NULL;
+	sb->u.supermount_sb.s_subfs = NULL;
+	sb->u.supermount_sb.s_opencount = 0;
+	sb->u.supermount_sb.s_dirty = 0;
+	sb->u.supermount_sb.s_type = NULL;
+	sb->u.supermount_sb.s_devname = NULL;
+
+	if (parse_options ((char *) data, sb)) {
+		sb->s_dev = 0;
+		return NULL;
+	}
+	if (!sb->u.supermount_sb.s_type)
+		copy_option(&sb->u.supermount_sb.s_type, "msdos");
+	if (!sb->u.supermount_sb.s_devname)
+		copy_option(&sb->u.supermount_sb.s_devname, "/dev/fd0");
+
+	sb->s_flags = sb->u.supermount_sb.s_mflags;
+	if (!(sb->s_mounted = iget(sb, SUPERMOUNT_ROOT_INO))) {
+		sb->s_dev = 0;
+		supermount_error (sb, "supermount_read_super",
+				  "get root inode failed\n");
+		return NULL;
+	}
+	return sb;
+}
+
+void supermount_write_super (struct super_block * sb)
+{
+	supermount_media_check(sb);
+	if (sb->u.supermount_sb.s_state != SUPERMOUNT_UNMOUNTED) {
+		struct super_block *tmp = sb->u.supermount_sb.s_subfs;
+		if (tmp && tmp->s_op && tmp->s_op->write_super)
+			tmp->s_op->write_super(tmp);
+	}
+	sb->s_dirt = 0;
+}
+
+#if 0
+int supermount_remount (struct super_block * sb, int * flags, char * data)
+{
+}
+#endif /* 0 */
+
+void supermount_statfs (struct super_block * sb, struct statfs * buf, 
+			int bufsize)
+{
+	unsigned short fs;
+	struct statfs tmp;
+	supermount_media_check(sb);
+
+	if (sb->u.supermount_sb.s_state != SUPERMOUNT_UNMOUNTED) {
+		struct super_block * tmpsb = 
+			sb->u.supermount_sb.s_undermount->i_mount->i_sb;
+		fs = get_fs();
+		set_fs(KERNEL_DS);
+		if (tmpsb->s_op && tmpsb->s_op->statfs)
+			tmpsb->s_op->statfs(tmpsb, &tmp, sizeof(tmp));
+		set_fs(fs);
+	} else {
+		tmp.f_bsize = sb->s_blocksize;
+		tmp.f_blocks = 0;
+		tmp.f_bfree = 0;
+		tmp.f_bavail = 0;
+		tmp.f_files = 0;
+		tmp.f_ffree = 0;
+		tmp.f_namelen = 0;
+	}
+	tmp.f_type = SUPERMOUNT_SUPER_MAGIC;
+	memcpy_tofs(buf, &tmp, bufsize);
+}
+
+/* Check for media change, but without invalidating buffers and inodes */
+static int just_check_disk_change(dev_t dev)
+{
+	if (!query_disk_change(dev))
+		return 0;
+	supermount_debug("Disk change detected on device %d/%d\n",
+			 MAJOR(dev), MINOR(dev));
+	return 1;
+}
+
+static void umount_subfs(struct super_block *sb)
+{
+	int retval;
+	struct inode *inode, *subi;
+	
+	supermount_debug("Trying to unmount device %s.\n",
+			 sb->u.supermount_sb.s_devname);
+	
+	/* Detach the subfs from the supermount root inode */
+	inode = sb->s_mounted;
+	/* Inode may not be set if we are currently unmounting the superfs */
+	if (inode) {
+		subi = inode->u.supermount_i.i_hidden;
+		if (subi) {
+			subi->i_shadow = 0;
+			inode->u.supermount_i.i_hidden = 0;
+			iput(inode);
+		}
+	}
+		
+	if (sb->u.supermount_sb.s_subfs->s_mounted->i_count != 1)
+		supermount_panic (sb, "umount_subfs",
+				  "subfs root i_count = %d, should be 1\n",
+				  sb->u.supermount_sb.s_subfs->s_mounted->i_count);
+	if (sb->u.supermount_sb.s_undermount->i_count != 1)
+		supermount_panic (sb, "umount_subfs",
+				  "undermount i_count = %d, should be 1\n",
+				  sb->u.supermount_sb.s_subfs->s_mounted->i_count);
+
+	sb->u.supermount_sb.s_subfs->s_mounted->i_count++;
+	sb->u.supermount_sb.s_state = SUPERMOUNT_UNMOUNTED;
+	retval = do_umounti(sb->u.supermount_sb.s_subfs->s_mounted);
+	if (retval)
+		supermount_panic (sb, "umount_subfs",
+				  "Help - can't umount subfs!!!");
+	supermount_debug("subfs is unmounted.\n");
+	sb->u.supermount_sb.s_undermount = 0;
+	sb->u.supermount_sb.s_version = ++event;
+	sb->u.supermount_sb.s_subfs = 0;
+	sb->s_flags = sb->u.supermount_sb.s_mflags;
+	if (inode)
+		inode->i_mode = sb->u.supermount_sb.s_default_mode;
+}
+
+/* Return 0 (OK) if medium is valid. */
+int supermount_media_check(struct super_block * sb) 
+{
+	int retval;
+	unsigned short fs;
+
+	/* If there are still files open, we never bother checking the
+           medium. */
+	if (sb->u.supermount_sb.s_subfs && 
+	    subfs_is_active(sb)) {
+		supermount_debug ("subfs is active.\n");
+		return 0;
+	}
+
+	supermount_debug ("starting (%sactive now)\n",
+			  (sb->u.supermount_sb.s_subfs &&
+			   subfs_is_active(sb)) ?
+			  "" : "not ");
+	
+	lock_super(sb);
+
+	/* Are we checking for mount or unmount/remount? */
+	if (sb->u.supermount_sb.s_state != SUPERMOUNT_UNMOUNTED) {
+		/* Already mounted --- check for disk change or
+		   disk not present */
+		int dev = sb->u.supermount_sb.s_subfs->s_dev;
+		if (!just_check_disk_change(dev)) {
+			unlock_super(sb);
+			return 0;
+		}
+		
+		/* We have a disk change!  Unmount the subfs */
+		supermount_debug ("trying to unmount\n");
+		umount_subfs(sb);
+		/* The call to just_check_disk_change may clear the media-
+		   changed flag on the device, so we need to force a media
+		   invalidation.  We don't want to do this before unmounting
+		   the subfs, naturally! */
+		invalidate_media(dev);
+		/* Now we are unmounted; fall through to the next check. */
+	}
+	
+	if (sb->u.supermount_sb.s_state != SUPERMOUNT_UNMOUNTED) {
+		unlock_super(sb);
+		return 0;
+	}
+	
+	/* OK, we're unmounted now --- can we remount?  Please? */
+	if (!sb->u.supermount_sb.s_undermount) {
+		sb->u.supermount_sb.s_undermount =
+			iget(sb, SUPERMOUNT_HIDDEN_INO);
+		if (!sb->u.supermount_sb.s_undermount)
+			supermount_panic (sb, "supermount_media_check",
+					  "Can't get root hidden inode!");
+		sb->u.supermount_sb.s_undermount->i_flags |= S_UNBOUND;
+	}
+
+	sb->u.supermount_sb.s_version = ++event;
+	sb->s_mounted->u.supermount_i.i_sb_version = event;
+	supermount_debug ("trying to mount\n");
+	supermount_debug ("fs=%s, dev=%s, data=%s, flags=%08lx\n",
+			  sb->u.supermount_sb.s_type,
+			  sb->u.supermount_sb.s_devname,
+			  sb->u.supermount_sb.s_data,
+			  sb->s_flags);
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	sb->s_flags = sb->u.supermount_sb.s_mflags,
+		retval = do_mounti(sb->u.supermount_sb.s_undermount,
+				   "<supermount>",
+				   sb->u.supermount_sb.s_devname,
+				   sb->u.supermount_sb.s_type,
+				   sb->s_flags | MS_MGC_VAL,
+				   sb->u.supermount_sb.s_data);
+	if (retval == -EROFS && !(sb->u.supermount_sb.s_mflags & MS_RDONLY)) {
+		/* OK, that failed, so try it readonly */
+		sb->s_flags |= MS_RDONLY;
+		retval = do_mounti(sb->u.supermount_sb.s_undermount,
+				   "<supermount>",
+				   sb->u.supermount_sb.s_devname,
+				   sb->u.supermount_sb.s_type,
+				   sb->s_flags | MS_MGC_VAL,
+				   sb->u.supermount_sb.s_data);
+	}
+	set_fs(fs);
+	
+	unlock_super(sb);
+	if (retval) {
+		sb->s_flags = sb->u.supermount_sb.s_mflags;
+		iput(sb->u.supermount_sb.s_undermount);
+		sb->u.supermount_sb.s_undermount = 0;
+		supermount_debug ("Mount failed, errno %d\n", -retval);
+		return 1;
+	}
+	/* Hey --- success!!! */
+	sb->u.supermount_sb.s_subfs = sb->u.supermount_sb.s_undermount
+		->i_mount->i_sb;
+	if (!sb->u.supermount_sb.s_subfs->s_mounted)
+		supermount_panic (sb, "supermount_media_check", 
+				  "Mounted subfs has no root inode!");
+	sb->u.supermount_sb.s_state = SUPERMOUNT_SUSPENDED;
+	supermount_debug ("Mount succeeded!\n");
+	/* For counting open inodes on the subfs, we rely on the fact
+           that the root inode is always exactly one count.  So make
+           sure we guarantee it is always open! */
+	supermount_iopen(sb->s_mounted);
+	sb->s_mounted->i_flags |= S_UNBOUND;
+	return 0;
+}
+
+/* Check whether an inode still belongs to valid medium. */
+int supermount_check_inode(struct inode * inode)
+{
+	supermount_debug ("inode = %ld\n", inode->i_ino);
+	
+	if (supermount_media_check(inode->i_sb))
+		return 1;
+	if (inode_is_obsolete(inode)) {
+		/* What happens if we remount an old disk, and get
+                   back the same superinode number?  subiget() will
+                   handle it through the i_subino field, and
+                   supermount_attach will avoid reusing the old
+                   inode. */
+		return 1;
+	}
+	return 0;
+}
Index: linux/include/linux/fs.h
diff -u linux/include/linux/fs.h:1.12 linux/include/linux/fs.h:1.12.2.1
--- linux/include/linux/fs.h:1.12	Wed Nov  6 23:17:37 1996
+++ linux/include/linux/fs.h	Sat Nov  9 17:26:30 1996
@@ -74,6 +74,10 @@
 #define S_WRITE		128	/* Write on file/directory/symlink */
 #define S_APPEND	256	/* Append-only file */
 #define S_IMMUTABLE	512	/* Immutable file */
+#define S_UNBOUND	1024	/* inode is not bound to user space,
+				   and so the filesystem may be
+				   unmounted even if this inode is in
+				   use. */
 
 /*
  * Flags that can be altered by MS_REMOUNT
@@ -103,6 +107,7 @@
 #define IS_WRITABLE(inode) ((inode)->i_flags & S_WRITE)
 #define IS_APPEND(inode) ((inode)->i_flags & S_APPEND)
 #define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE)
+#define IS_UNBOUND(inode) ((inode)->i_flags & S_UNBOUND)
 
 /* the read-only stuff doesn't really belong here, but any other place is
    probably as bad and I don't want to create yet another include file. */
@@ -216,6 +221,7 @@
 }
 
 #include <linux/pipe_fs_i.h>
+#include <linux/supermount_fs_i.h>
 #include <linux/minix_fs_i.h>
 #include <linux/ext_fs_i.h>
 #include <linux/ext2_fs_i.h>
@@ -284,6 +290,8 @@
 	unsigned long	i_nrpages;
 	struct semaphore i_sem;
 	struct inode_operations *i_op;
+	struct inode * i_shadow;
+	struct inode_shadow_operations * i_shadow_op;
 	struct super_block *i_sb;
 	struct wait_queue *i_wait;
 	struct file_lock *i_flock;
@@ -305,6 +313,7 @@
 	unsigned short i_writecount;
 	union {
 		struct pipe_inode_info pipe_i;
+		struct supermount_inode_info supermount_i;
 		struct minix_inode_info minix_i;
 		struct ext_inode_info ext_i;
 		struct ext2_inode_info ext2_i;
@@ -402,6 +411,7 @@
 
 extern int fasync_helper(struct inode *, struct file *, int, struct fasync_struct **);
 
+#include <linux/supermount_fs_sb.h>
 #include <linux/minix_fs_sb.h>
 #include <linux/ext_fs_sb.h>
 #include <linux/ext2_fs_sb.h>
@@ -431,6 +441,7 @@
 	struct inode * s_mounted;
 	struct wait_queue * s_wait;
 	union {
+		struct supermount_sb_info supermount_sb;
 		struct minix_sb_info minix_sb;
 		struct ext_sb_info ext_sb;
 		struct ext2_sb_info ext2_sb;
@@ -491,6 +502,12 @@
 	int (*smap) (struct inode *,int);
 };
 
+struct inode_shadow_operations {
+	void (*open) (struct inode *, int);
+	void (*write) (struct inode *);
+	void (*release) (struct inode *);
+};
+
 struct super_operations {
 	void (*read_inode) (struct inode *);
 	int (*notify_change) (struct inode *, struct iattr *);
@@ -600,9 +617,11 @@
 }
 
 extern int check_disk_change(kdev_t dev);
+extern int query_disk_change(kdev_t dev);
 extern void invalidate_inodes(kdev_t dev);
 extern void invalidate_inode_pages(struct inode *);
 extern void invalidate_buffers(kdev_t dev);
+extern void invalidate_media(kdev_t dev);
 extern int floppy_is_wp(int minor);
 extern void sync_inodes(kdev_t dev);
 extern void sync_dev(kdev_t dev);
@@ -663,6 +682,9 @@
 
 extern void show_buffers(void);
 extern void mount_root(void);
+extern int do_mounti(struct inode *_inode, const char *, const char *, 
+		     const char *, unsigned long, const void *);
+extern int do_umounti(struct inode *);
 
 #ifdef CONFIG_BLK_DEV_INITRD
 extern kdev_t real_root_dev;
Index: linux/include/linux/supermount_fs.h
diff -u linux/include/linux/supermount_fs.h:1.1 linux/include/linux/supermount_fs.h:1.1.4.1
--- linux/include/linux/supermount_fs.h:1.1	Mon Feb 26 17:55:21 1996
+++ linux/include/linux/supermount_fs.h	Sat Nov  9 17:27:49 1996
@@ -0,0 +1,165 @@
+/*
+ *  linux/include/linux/supermount_fs.h
+ *
+ *  Defines and strutures for the dynamic remounting of removable media
+ *
+ *  Copyright (C) 1995
+ *  Stephen Tweedie (sct@dcs.ed.ac.uk)
+ *
+ *  from
+ *
+ *  linux/include/linux/minix_fs.h
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  and
+ *
+ *  linux/include/linux/ext2_fs.h
+ *  Copyright (C) 1992, 1993, 1994, 1995  Remy Card
+ */
+
+#ifndef _LINUX_SUPERMOUNT_FS_H
+#define _LINUX_SUPERMOUNT_FS_H
+
+#include <linux/types.h>
+
+#define SUPERMOUNT_DEBUG
+
+#define SUPERMOUNT_VERSION		"0.4"
+
+/*
+ * Debug code
+ */
+#ifdef SUPERMOUNT_DEBUG
+ extern char supermount_debug_enable;
+
+ #define supermount_debug(f, a...) \
+	{ \
+		if (supermount_debug_enable) { \
+			printk ("SUPERMOUNT DEBUG (%s, %d): %s: ", \
+				__FILE__, __LINE__, __FUNCTION__); \
+			printk (f, ## a); \
+		} \
+	}
+#else
+ #define supermount_debug(f, a...)	/**/
+#endif
+
+/*
+ * Special inodes numbers
+ */
+#define SUPERMOUNT_HIDDEN_INO		 0	/* Hidden inode for
+						   sub-mounting */
+#define SUPERMOUNT_ROOT_INO		 1	/* Root inode */
+
+/*
+ * The supermount superblock magic number
+ */
+#define SUPERMOUNT_SUPER_MAGIC	0x9fa1
+
+#ifdef __KERNEL__
+/*
+ * Function prototypes
+ */
+
+/*
+ * Ok, these declarations are also in <linux/kernel.h> but none of the
+ * supermount source programs needs to include it so they are duplicated here.
+ */
+#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5)
+# define NORET_TYPE    __volatile__
+# define ATTRIB_NORET  /**/
+# define NORET_AND     /**/
+#else
+# define NORET_TYPE    /**/
+# define ATTRIB_NORET  __attribute__((noreturn))
+# define NORET_AND     noreturn,
+#endif
+
+/* How to test if a supermount filesystem is active or not: */
+static inline int subfs_is_active(struct super_block *sb)
+{
+	/* The subfs is deemed inactive iff only the root is open, and
+           the its i_count is one.  */
+	return (sb->u.supermount_sb.s_state != SUPERMOUNT_UNMOUNTED &&
+		(sb->u.supermount_sb.s_opencount > 1 ||
+		 sb->u.supermount_sb.s_undermount->i_mount->i_count > 1));
+}
+
+/* How to test if an inode is obsolete: */
+static inline int inode_is_obsolete(struct inode *inode)
+{
+	return (inode->u.supermount_i.i_sb_version !=
+		inode->i_sb->u.supermount_sb.s_version);
+}
+
+/* Manage the subfs dirty flag */
+static inline void mark_subfs_dirty(struct super_block *sb)
+{
+	sb->u.supermount_sb.s_dirty = 1;
+}
+static inline void mark_subfs_clean(struct super_block *sb)
+{
+	sb->u.supermount_sb.s_dirty = 0;
+}
+static inline int subfs_is_dirty(struct super_block *sb)
+{
+	return (sb->u.supermount_sb.s_dirty);
+}
+
+/* inode.c */
+extern struct inode * subiget(struct inode *);
+extern void supermount_iopen(struct inode *);
+extern void supermount_iclose(struct inode *);
+extern void supermount_go_inactive(struct super_block *);
+extern struct inode * supermount_attach(struct super_block *, struct inode *);
+extern void supermount_read_inode (struct inode *);
+extern void supermount_write_inode (struct inode *);
+extern void supermount_put_inode (struct inode *);
+extern int supermount_permission (struct inode *, int mask);
+
+/* namei.c */
+extern void supermount_release (struct inode *, struct file *);
+extern int supermount_lookup (struct inode *,const char *, int, struct inode **);
+extern int supermount_create (struct inode *,const char *, int, int,
+			      struct inode **);
+extern int supermount_mkdir (struct inode *, const char *, int, int);
+extern int supermount_rmdir (struct inode *, const char *, int);
+extern int supermount_unlink (struct inode *, const char *, int);
+extern int supermount_symlink (struct inode *, const char *, int, const char *);
+extern int supermount_link (struct inode *, struct inode *, const char *, int);
+extern int supermount_mknod (struct inode *, const char *, int, int, int);
+extern int supermount_rename (struct inode *, const char *, int,
+			      struct inode *, const char *, int, int);
+
+/* super.c */
+extern void supermount_error (struct super_block *, const char *, const char *, ...)
+	__attribute__ ((format (printf, 3, 4)));
+extern NORET_TYPE void supermount_panic (struct super_block *, const char *,
+				   const char *, ...)
+	__attribute__ ((NORET_AND format (printf, 3, 4)));
+extern void supermount_warning (struct super_block *, const char *, const char *, ...)
+	__attribute__ ((format (printf, 3, 4)));
+extern void supermount_put_super (struct super_block *);
+extern void supermount_write_super (struct super_block *);
+extern int supermount_remount (struct super_block *, int *, char *);
+extern struct super_block * supermount_read_super (struct super_block *,void *,int);
+extern void supermount_statfs (struct super_block *, struct statfs *, int);
+extern int supermount_media_check (struct super_block *);
+extern int supermount_check_inode (struct inode *);
+
+/* dir.c */
+extern struct inode_operations supermount_dir_iops;
+extern struct inode_operations supermount_root_iops;
+
+/* inode.c */
+extern struct inode_shadow_operations supermount_shadow_iops;
+
+/* file.c */
+/* extern struct inode_operations supermount_shadow_file_iops; */
+
+/* symlink.c */
+extern struct inode_operations supermount_symlink_iops;
+
+#endif	/* __KERNEL__ */
+
+#endif	/* _LINUX_SUPERMOUNT_FS_H */
Index: linux/include/linux/supermount_fs_i.h
diff -u linux/include/linux/supermount_fs_i.h:1.1 linux/include/linux/supermount_fs_i.h:1.1.4.1
--- linux/include/linux/supermount_fs_i.h:1.1	Mon Feb 26 17:55:22 1996
+++ linux/include/linux/supermount_fs_i.h	Sat Nov  9 17:27:49 1996
@@ -0,0 +1,23 @@
+/*
+ *  linux/include/linux/supermount_fs.h
+ *
+ *  In-core inode structure for the dynamic remounting of removable media
+ *
+ *  Copyright (C) 1995
+ *  Stephen Tweedie (sct@dcs.ed.ac.uk)
+ *
+ */
+
+#ifndef _LINUX_SUPERMOUNT_FS_I_H
+#define _LINUX_SUPERMOUNT_FS_I_H
+
+struct supermount_inode_info {
+	struct inode *	i_hidden;
+	int		i_sb_version;
+	int		i_subino;
+	int		i_counted;/* Has this inode been counted in
+				     the superblock reference counts
+				     yet? */
+};
+
+#endif	/* _LINUX_SUPERMOUNT_FS_I_H */
Index: linux/include/linux/supermount_fs_sb.h
diff -u linux/include/linux/supermount_fs_sb.h:1.1 linux/include/linux/supermount_fs_sb.h:1.1.4.1
--- linux/include/linux/supermount_fs_sb.h:1.1	Mon Feb 26 17:55:22 1996
+++ linux/include/linux/supermount_fs_sb.h	Sat Nov  9 17:27:49 1996
@@ -0,0 +1,44 @@
+/*
+ *  linux/include/linux/supermount_sb_fs.h
+ *
+ *  In-core superblock struct for the dynamic remounting of removable media
+ *
+ *  Copyright (C) 1995
+ *  Stephen Tweedie (sct@dcs.ed.ac.uk)
+ *
+ */
+
+#ifndef _LINUX_SUPERMOUNT_FS_SB_H
+#define _LINUX_SUPERMOUNT_FS_SB_H
+
+typedef enum {
+	SUPERMOUNT_UNMOUNTED,	/* No media mounted */
+	SUPERMOUNT_SUSPENDED,	/* Mounted but suspended because 
+				   no files open */
+	SUPERMOUNT_ONLINE,	/* Mounted and active */
+} sm_state_t;
+
+/*
+ * supermount super-block data in memory
+ */
+struct supermount_sb_info {
+	sm_state_t	s_state;
+	int		s_version;	/* Used to indicate obsolete inodes */
+	mode_t		s_default_mode;	/* Default mode for supermount root */
+
+	const char *	s_type;		/* Type of fs to be sub-mounted */
+	const char *	s_devname;	/* Where to mount the subfs */
+	int		s_mflags;	/* Flags to pass when mounting subfs */
+	const char *	s_data;		/* Data to pass when mounting subfs */
+
+	struct inode *	s_undermount;	/* Mount point for subfs */
+	struct super_block * s_subfs;	/* The submounted fs sb */
+	int		s_opencount;	/* Refcount of opened inodes
+					   (an inode in use as cwd
+					   does not count as open) */
+	
+	/* Flags */
+	int		s_dirty : 1;	/* Do we need to fsync() the subfs? */
+};
+
+#endif	/* _LINUX_SUPERMOUNT_FS_SB_H */
