*** linux/Documentation/filesystems/supermount.txt.~1~	Thu May 30 15:49:07 1996
--- linux/Documentation/filesystems/supermount.txt	Thu May 30 15:22:30 1996
***************
*** 0 ****
--- 1,141 ----
+ Supermount README
+ =================
+ 
+ For supermount v0.4
+ 
+ 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> / <mount-point>
+ 
+ Notice that I specified "/" instead of giving a block device to
+ supermount.  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
+ this 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	supermount	--,gid=51,conv=binary	0 0
+ / /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.
+ 
+ 
+ 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.
+ --
+ Stephen Tweedie <sct@dcs.ed.ac.uk>
+ Department of Computer Science, Edinburgh University, Scotland.
+ 
+ 
+ ================================================================
+ 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.
+ 
*** linux/fs/Config.in.~1~	Thu May 30 15:49:07 1996
--- linux/fs/Config.in	Thu May 30 15:22:31 1996
***************
*** 6,11 ****
--- 6,12 ----
  
  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
*** linux/fs/Makefile.~1~	Thu May 30 15:49:07 1996
--- linux/fs/Makefile	Thu May 30 15:23:05 1996
***************
*** 17,23 ****
  
  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
  
  ifeq ($(CONFIG_QUOTA),y)
  O_OBJS += dquot.o
--- 17,23 ----
  
  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 supermount
  
  ifeq ($(CONFIG_QUOTA),y)
  O_OBJS += dquot.o
***************
*** 139,144 ****
--- 139,152 ----
    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)
*** linux/fs/devices.c.~1~	Thu May 30 15:49:07 1996
--- linux/fs/devices.c	Thu May 30 15:22:30 1996
***************
*** 187,201 ****
  }
  
  /*
!  * 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)
  {
  	int i;
  	struct file_operations * fops;
--- 187,200 ----
  }
  
  /*
!  * 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,224 ****
  	if (!fops->check_media_change(dev))
  		return 0;
  
  	printk(KERN_DEBUG "VFS: Disk change detected on device %s\n",
  		kdevname(dev));
  	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)
  		fops->revalidate(dev);
! 	return 1;
  }
  
  /*
--- 207,243 ----
  	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 && fops->revalidate)
  		fops->revalidate(dev);
! 	return;
  }
  
  /*
*** linux/fs/filesystems.c.~1~	Thu May 30 15:49:07 1996
--- linux/fs/filesystems.c	Thu May 30 15:22:30 1996
***************
*** 9,14 ****
--- 9,15 ----
  #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,44 ****
--- 40,50 ----
  	callable = 0;
  
  	device_setup();
+ 
+ #ifdef CONFIG_SUPERMOUNT
+ 	register_filesystem(&(struct file_system_type)
+ 		{supermount_read_super, "supermount", 0, NULL});
+ #endif
  
  	binfmt_setup();
  
*** linux/fs/inode.c.~1~	Thu May 30 15:49:07 1996
--- linux/fs/inode.c	Thu May 30 15:22:30 1996
***************
*** 219,224 ****
--- 219,226 ----
  		if (inode == mount_root && inode->i_count ==
  		    (inode->i_mount != inode ? 1 : 2))
  			continue;
+ 		if (IS_UNBOUND(inode))
+ 			continue;
  		return 0;
  	}
  	return 1;
***************
*** 254,259 ****
--- 256,264 ----
  	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,428 ****
  	}
  }
  
  void iput(struct inode * inode)
  {
  	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);
  		return;
  	}
  	if (inode->i_pipe)
--- 419,442 ----
  	}
  }
  
+ 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_dev), inode->i_ino, inode->i_mode);
  		return;
  	}
  	if (inode->i_pipe)
***************
*** 441,449 ****
  	}
  
  	if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->put_inode) {
  		inode->i_sb->s_op->put_inode(inode);
! 		if (!inode->i_nlink)
  			return;
  	}
  
  	if (inode->i_dirt) {
--- 455,476 ----
  	}
  
  	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) {
! 			/* 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,478 ****
--- 500,513 ----
  	}
  
  	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;
  }
  
*** linux/fs/open.c.~1~	Thu May 30 15:49:07 1996
--- linux/fs/open.c	Thu May 30 15:22:30 1996
***************
*** 520,525 ****
--- 520,529 ----
  			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;
*** linux/fs/super.c.~1~	Thu May 30 15:49:07 1996
--- linux/fs/super.c	Thu May 30 15:40:16 1996
***************
*** 628,648 ****
   * functions, they should be faked here.  -- jrs
   */
  
! asmlinkage int sys_umount(char * name)
  {
- 	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)) {
--- 628,639 ----
   * functions, they should be faked here.  -- jrs
   */
  
! int do_umounti(struct inode * inode)
  {
  	kdev_t dev;
  	int retval;
  	struct inode dummy_inode;
  
  	if (S_ISBLK(inode->i_mode)) {
  		dev = inode->i_rdev;
  		if (IS_NODEV(inode)) {
***************
*** 681,730 ****
  	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.
   *
   * 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.
!  */
  
! int do_mount(kdev_t dev, 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);
  		return -EBUSY;
! 	}
! 	if (!S_ISDIR(dir_i->i_mode)) {
! 		iput(dir_i);
  		return -ENOTDIR;
! 	}
! 	if (!fs_may_mount(dev)) {
! 		iput(dir_i);
  		return -EBUSY;
- 	}
  	sb = read_super(dev,type,flags,data,0);
! 	if (!sb) {
! 		iput(dir_i);
  		return -EINVAL;
! 	}
! 	if (sb->s_covered) {
! 		iput(dir_i);
  		return -EBUSY;
- 	}
  	vfsmnt = add_vfsmnt(dev, dev_name, dir_name);
  	if (vfsmnt) {
  		vfsmnt->mnt_sb = sb;
--- 672,722 ----
  	return 0;
  }
  
+ 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.  */
  
! 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 super_block * sb;
  	struct vfsmount *vfsmnt;
  
  	if (!(flags & MS_RDONLY) && dev && is_read_only(dev))
  		return -EACCES;
  		/*flags |= MS_RDONLY;*/
! 	if (dir_i->i_count != 1 || dir_i->i_mount)
  		return -EBUSY;
! 	if (!S_ISDIR(dir_i->i_mode))
  		return -ENOTDIR;
! 	if (!fs_may_mount(dev))
  		return -EBUSY;
  	sb = read_super(dev,type,flags,data,0);
! 	if (!sb)
  		return -EINVAL;
! 	if (sb->s_covered)
  		return -EBUSY;
  	vfsmnt = add_vfsmnt(dev, dev_name, dir_name);
  	if (vfsmnt) {
  		vfsmnt->mnt_sb = sb;
***************
*** 735,740 ****
--- 727,745 ----
  	return 0;		/* we don't iput(dir_i) - see umount */
  }
  
+ 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
***************
*** 793,810 ****
  	*where = 0;
  	if (!data)
  		return 0;
! 
! 	vma = find_vma(current, (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;
  	if (!(page = __get_free_page(GFP_KERNEL))) {
  		return -ENOMEM;
  	}
  	memcpy_fromfs((void *) page,data,i);
  	*where = page;
  	return 0;
--- 798,818 ----
  	*where = 0;
  	if (!data)
  		return 0;
! 	if (get_fs() != get_ds()) {
! 		vma = find_vma(current, (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;
  	}
+ 
  	memcpy_fromfs((void *) page,data,i);
  	*where = page;
  	return 0;
***************
*** 825,839 ****
   * version that didn't understand them.
   */
  asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
! 	unsigned long new_flags, void * data)
  {
! 	struct file_system_type * fstype;
! 	struct inode * inode;
! 	struct file_operations * fops;
! 	kdev_t dev;
  	int retval;
- 	const char * t;
- 	unsigned long flags = 0;
  	unsigned long page = 0;
  
  	if (!suser())
--- 833,842 ----
   * version that didn't understand them.
   */
  asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
! 			 unsigned long new_flags, void * data)
  {
! 	struct inode * dir_inode;
  	int retval;
  	unsigned long page = 0;
  
  	if (!suser())
***************
*** 849,854 ****
--- 852,880 ----
  		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,912 ****
  			return retval;
  		}
  	}
! 	retval = do_mount(dev,dev_name,dir_name,t,flags,(void *) page);
  	free_page(page);
  	if (retval && fops && fops->release)
  		fops->release(inode, NULL);
--- 932,939 ----
  			return retval;
  		}
  	}
! 	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);
*** linux/fs/supermount/Makefile.~1~	Thu May 30 15:49:07 1996
--- linux/fs/supermount/Makefile	Thu May 30 15:22:31 1996
***************
*** 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
*** linux/fs/supermount/TODO.~1~	Thu May 30 15:49:07 1996
--- linux/fs/supermount/TODO	Thu May 30 15:22:31 1996
***************
*** 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.
*** linux/fs/supermount/dir.c.~1~	Thu May 30 15:49:07 1996
--- linux/fs/supermount/dir.c	Thu May 30 15:22:31 1996
***************
*** 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;
+ }
+ 
*** linux/fs/supermount/file.c.~1~	Thu May 30 15:49:07 1996
--- linux/fs/supermount/file.c	Thu May 30 15:22:31 1996
***************
*** 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 *);
+ 
*** linux/fs/supermount/inode.c.~1~	Thu May 30 15:49:07 1996
--- linux/fs/supermount/inode.c	Thu May 30 15:22:31 1996
***************
*** 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;
+ }
*** linux/fs/supermount/namei.c.~1~	Thu May 30 15:49:07 1996
--- linux/fs/supermount/namei.c	Thu May 30 15:22:31 1996
***************
*** 0 ****
--- 1,347 ----
+ /*
+  *  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)
+ {
+ 	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);
+ 	mark_subfs_dirty(old_dir->i_sb);
+ 	supermount_go_inactive(old_dir->i_sb);
+ 	iput(old_dir);
+ 	iput(new_dir);
+ 	return rc;
+ }
*** linux/fs/supermount/super.c.~1~	Thu May 30 15:49:07 1996
--- linux/fs/supermount/super.c	Thu May 30 15:22:31 1996
***************
*** 0 ****
--- 1,430 ----
+ /*
+  *  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 = 0111 | 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;
+ }
*** linux/include/linux/fs.h.~1~	Thu May 30 15:49:07 1996
--- linux/include/linux/fs.h	Thu May 30 15:22:31 1996
***************
*** 72,77 ****
--- 72,81 ----
  #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
***************
*** 101,106 ****
--- 105,111 ----
  #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. */
***************
*** 214,219 ****
--- 219,225 ----
  }
  
  #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>
***************
*** 282,287 ****
--- 288,295 ----
  	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;
***************
*** 303,308 ****
--- 311,317 ----
  	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;
***************
*** 400,405 ****
--- 409,415 ----
  
  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>
***************
*** 429,434 ****
--- 439,445 ----
  	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;
***************
*** 489,494 ****
--- 500,511 ----
  	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 *);
***************
*** 598,606 ****
--- 615,625 ----
  }
  
  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);
***************
*** 659,664 ****
--- 678,686 ----
  
  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;
*** linux/include/linux/supermount_fs.h.~1~	Thu May 30 15:49:07 1996
--- linux/include/linux/supermount_fs.h	Thu May 30 15:22:31 1996
***************
*** 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);
+ 
+ /* 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 */
*** linux/include/linux/supermount_fs_i.h.~1~	Thu May 30 15:49:07 1996
--- linux/include/linux/supermount_fs_i.h	Thu May 30 15:22:31 1996
***************
*** 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 */
*** linux/include/linux/supermount_fs_sb.h.~1~	Thu May 30 15:49:07 1996
--- linux/include/linux/supermount_fs_sb.h	Thu May 30 15:22:31 1996
***************
*** 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 */
