ALT Linux Bugzilla
– Attachment 2655 Details for
Bug 15838
supermount-ng
New bug
|
Search
|
[?]
|
Help
Register
|
Log In
[x]
|
Forgot Password
Login:
[x]
|
EN
|
RU
[patch]
linux-2.6.25.4-supermount-ng-2.2.2.patch
linux-2.6.25.4-supermount-ng-2.2.2.patch (text/plain), 163.47 KB, created by
led
on 2008-05-30 00:17:56 MSD
(
hide
)
Description:
linux-2.6.25.4-supermount-ng-2.2.2.patch
Filename:
MIME Type:
Creator:
led
Created:
2008-05-30 00:17:56 MSD
Size:
163.47 KB
patch
obsolete
>diff -urN linux-2.6.25.4.orig/Documentation/filesystems/00-INDEX linux-2.6.25.4/Documentation/filesystems/00-INDEX >--- linux-2.6.25.4.orig/Documentation/filesystems/00-INDEX 2008-04-18 01:19:55 +0300 >+++ linux-2.6.25.4/Documentation/filesystems/00-INDEX 2008-05-20 01:27:53 +0300 >@@ -92,6 +92,8 @@ > - a description of shared subtrees for namespaces. > smbfs.txt > - info on using filesystems with the SMB protocol (Win 3.11 and NT). >+supermount.txt >+ - info on using supermount for removable media. > spufs.txt > - info and mount options for the SPU filesystem used on Cell. > sysfs-pci.txt >diff -urN linux-2.6.25.4.orig/Documentation/filesystems/supermount.txt linux-2.6.25.4/Documentation/filesystems/supermount.txt >--- linux-2.6.25.4.orig/Documentation/filesystems/supermount.txt 1970-01-01 03:00:00 +0300 >+++ linux-2.6.25.4/Documentation/filesystems/supermount.txt 2008-05-20 01:27:53 +0300 >@@ -0,0 +1,258 @@ >+Supermount README >+================= >+ >+Running supermount >+------------------ >+ >+To run supermount, compile and install a kernel with the supermount >+patches and select "Y" to the question >+ >+ Supermount removable media support (CONFIG_SUPERMOUNT) [Y/n/?] >+ >+when you run "make config". You mount a supermount filesystem with >+the normal mount command, using the syntax >+ >+ mount -t supermount -o <superfs-options>,--,<subfs-options> none <mpt> >+ >+or by adding correpsonding line to /etc/fstab >+ >+ none <mpt> supermount <superfs-options>,--,<subfs-options> 0 0 >+ >+where >+ >+ <superfs-options> are the options you want to pass to supermount >+ itself. These are described below. >+ >+ <subfs-options> are the options you want supermount to pass to the >+ dismountable filesystem underneath. >+ >+ <mpt> is the mount point where you want your removable media to be >+ mounted. >+ >+WARNING: If you get errors with GCC 3.4.X or avobe with inline on subfs.c >+replace all instances of "inline" by "" (nothing) and go ahead. >+[updated for kernel 2.6.X by madgus@gmail.com] >+Many thanks to: >+Robert Sombrutzki <sombrutz@informatik.hu-berlin.de> >+Alexandre Teixeira <alexandre@insignesoftware.com> >+Keith Smith <keith@ksmith.com> >+English >+[http://www.compunauta.com/forums/linux/instalarlinux/supermount_en.html] >+EspaƱol >+[http://www.compunauta.com/forums/linux/instalarlinux/supermount_es.html] >+ >+WARNING: in the above description `none' is literal word. While device >+is ignored by supermount itself, using real files in this place (real >+device name or mount point directory name) is known to cause problems. >+Some programs - fuser is one of them - will try to descend into filesystem >+if dev can be statted, thus making supermount to attempt to access media. >+This is annoying at best - in the worst case it can take very long time >+during startup or shutdown. >+ >+Notice that you do not directly specify the block device you are going >+to mount on the mount command line. This is because the supermount >+filesystem is NOT connected to a block device; rather, supermount is >+responsible for connecting a separate filesystem to the block device. >+You specify the sub-filesystem and block device name by providing the >+<superfs-options> field, where the following options are currently >+recognised: >+ >+ >+* fs=<filesystem-type> [default is "auto"] >+ >+ Specify the subfilesystem type. Not every filesystem type has >+been tested. If you use `auto', it will try the following filesystems >+in order: >+ "udf" >+ "iso9660" >+ "ext2" >+ "vfat" >+ "msdos" >+ >+It is also possible to give list of types separated by `:', like >+ >+ fs=ext2:vfat >+ - or - >+ fs=udf:iso9660 >+ >+ >+* dev=<block-device> [no default, mandatory] >+ >+ Specify the block device on which the subfs is to be mounted. >+ >+ >+* tray_lock={always,onwrite,never} [default is "onwrite"] >+ >+ Specify when supermount is to prevent media removal. `always' means >+on every access (it was default in earlier versions), `onwrite' means only >+for write access and `never' means never :) `onwrite' and `never' are the >+same for ro media like CD-ROM. It is not clear when `never' is actually useful, >+it is presented for completeness only. >+ >+* debug[=<bitmap>] [default is no debug] >+ >+ Enable debugging code in the supermount filesystem, if >+the debug option was enabled at compile time. By default, debugging >+code is compiled into the kernel but is disabled until a debug mount >+option is seen. <bitmap> is the combination of debug flags, following >+flags are possible: >+ >+ 0x001 - "generic" debug (used by supermount_debug) - default >+ 0x002 - trace dentry.c >+ 0x004 - trace file.c >+ 0x008 - trace filemap.c >+ 0x010 - trace mediactl.c >+ 0x020 - trace namei.c >+ 0x040 - trace subfs.c >+ 0x080 - trace super.c >+ >+Trace flags turn on tracing of functions in correpsonding files. >+"Generic" debug flag is tested in supermount_debug; for compatibility, >+if no flags are specified, this flag is set. >+ >+ >+* '--' >+ >+ All options after the option string '--' will be passed >+directly to the subfilesystem when it gets mounted. >+ >+Errors >+------ >+ >+In addition to "normal" errors during file operations supermount may >+return following error codes: >+ >+* No medium found >+ >+ You attempt to access supermounted filesystem when there is no >+medium inserted >+ >+* Wrong medium type >+ >+ (Not really generated by supermount) You attempt to mount CD >+without data tracks >+ >+* Stale NFS file handle >+ >+ You attempt to use file handle after medium has been changed. >+ >+* No such device or address >+ >+ (Not really generated by supermount) device specified in >+dev=<device> option does not exist. Also some drivers return this >+error instead of "No medium found", one example being floppy driver. >+ >+* Device or resource busy >+ >+ (Not really generated by supermount) device is already mounted. >+Supermount prevents double mount even if kernel otherwise would make it >+possible. >+ >+* No such file or directory >+ >+ (Not really generated by supermount) file name specified by >+dev=<device> option does not exist >+ >+* Operation not permitted >+ >+ You attempt to access subfs that is currently disabled >+ >+/proc support >+------------- >+ >+If kernel has been compiled with procfs support, supermount will provide >+/proc interface to read subfs status and to control some aspects of subfs. >+The following files are created under /proc/fs/supermount: >+ >+* version (ro) >+ Shows supermount version. >+ >+* subfs (rw) >+ >+ Reading this file returns list of all subfs status. One line for >+every subfs is returned; the format is >+ >+ <devname> disabled >+ - or - >+ <devname> unmounted >+ - or - >+ <devname> mounted readcount writecount >+ >+where <devname> is the string passed in `dev=' parameter during mount. >+`readcount' is number of current subfs "users" needing ro access; `writecount' >+is the number of "users" needing rw mode. It is mostly the number of open >+files, but inode operations also add to these counts. Those operations >+are normally short-lived to be seen. >+ >+ Writing this file changes subfs status; the following commands are >+suported: >+ >+ <devname> [disable|enable] [release [force]] >+ >+`disable' will disable subfs (i.e. any futher attempt to mount is rejected). >+Subfs must be unmounted; use `disable release' or `disable release force' to >+unmount and disable at the same time. >+ >+`enable' will enable disabled subfs, it has no effect if subfs is already >+enabled. >+ >+`release' will unmount subfs unless it is busy (opencount > 0). To unmount >+busy subfs add `force'; the effect is very much as if media change has been >+detected (with the difference that subfs will be cleanly unmounted). >+ >+Some basic sanity checks are performed, i.e. it is impossible to specify >+both `enable' and `disable' or `force' without `release'. >+ >+Internals/Locking >+----------------- >+ >+THIS SECTION IS PROBABLY INCOMPLETE. CORRECTIONS ARE WELCOME. >+ >+Supermount itself is using two locks and relies on two more locking rules >+as implemented by kernel. >+ >+* supermount_proc_sem >+ >+ Global mutex used to protect list of sbi during access to >+/proc/fs/supermount/subfs >+ >+* sbi->sem >+ >+ Per-filesystem mutex that protects subfs state (mounted/unmounted). >+Any changes to subfs state (mounting, unmouting, adding or removing file, >+dentry or inode) happen under this mutex. >+ >+* inode->i_sem (see Documentation/filesystem/Locking) >+ >+ Per-inode mutex used by VFS to serialize inode unlink operation. >+Supermount relies on the fact that link/unlink for an inode are mutually >+locked and thus inode->i_nlink is atomic inside of fs method. >+ >+* BKL >+ >+ Used to protect device usage count. It is changed in open/release >+and referenced in ioctl all of which run under BKL. Supermount adds mediactl >+and internally wraps it in BKL as well. >+ >+Caveats/BUGS >+------------ >+ >+Inode times are believed to be correctly updated; still it is possible that >+I missed some point. >+ >+If subfs is not yet mounted, find /mnt/cdrom fails with "find: /mnt/cdrom >+changed during execution of find". It is unlikely it can be fixed in >+supermount; much more simple is to provide wrapper that will check if subfs >+is mounted and do simple "touch /mnt/cdrom" if not to make sure it is. >+ >+Supermount attempts to check for changed media at every operation and >+invalidate and umount old subfs to avoid new media corruption in rw case. >+Still it is possible that kernel will write out stale information and >+it is outside of supermount control. >+ >+By default cdrom driver does not check media type i.e. supermount will >+try all configured fs types in turn that may be quite time consuming. >+Use sysctl -w dev.cdrom.check_media=1 to enable it. Comments in cdrom.c >+indicate that some (non conforming) software may have problems with this >+settings. YMMV >+ >diff -urN linux-2.6.25.4.orig/drivers/cdrom/cdrom.c linux-2.6.25.4/drivers/cdrom/cdrom.c >--- linux-2.6.25.4.orig/drivers/cdrom/cdrom.c 2008-04-18 01:20:00 +0300 >+++ linux-2.6.25.4/drivers/cdrom/cdrom.c 2008-05-20 01:27:53 +0300 >@@ -280,6 +280,7 @@ > #include <linux/fcntl.h> > #include <linux/blkdev.h> > #include <linux/times.h> >+#include <linux/supermount_media.h> > > #include <asm/uaccess.h> > >@@ -344,7 +345,7 @@ > #define CDROM_DEF_TIMEOUT (7 * HZ) > > /* Not-exported routines. */ >-static int open_for_data(struct cdrom_device_info * cdi); >+static int open_for_data(struct cdrom_device_info * cdi, struct block_device *bdev); > static int check_for_audio_disc(struct cdrom_device_info * cdi, > struct cdrom_device_ops * cdo); > static void sanitize_format(union cdrom_addr *addr, >@@ -1007,7 +1008,7 @@ > if ((fp->f_flags & O_NONBLOCK) && (cdi->options & CDO_USE_FFLAGS)) { > ret = cdi->ops->open(cdi, 1); > } else { >- ret = open_for_data(cdi); >+ ret = open_for_data(cdi, ip->i_bdev); > if (ret) > goto err; > cdrom_mmc3_profile(cdi); >@@ -1043,7 +1044,7 @@ > } > > static >-int open_for_data(struct cdrom_device_info * cdi) >+int open_for_data(struct cdrom_device_info * cdi, struct block_device *bdev) > { > int ret; > struct cdrom_device_ops *cdo = cdi->ops; >@@ -1128,7 +1129,8 @@ > cdinfo(CD_OPEN, "open device failed.\n"); > goto clean_up_and_return; > } >- if (CDROM_CAN(CDC_LOCK) && (cdi->options & CDO_LOCK)) { >+ if (CDROM_CAN(CDC_LOCK) && (cdi->options & CDO_LOCK) && >+ supermount_usage_count(bdev, cdi->use_count) > 0) { > cdo->lock_door(cdi, 1); > cdinfo(CD_OPEN, "door locked.\n"); > } >@@ -1220,7 +1222,8 @@ > cdinfo(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", cdi->name); > cdrom_dvd_rw_close_write(cdi); > >- if ((cdo->capability & CDC_LOCK) && !keeplocked) { >+ if (!supermount_usage_count(fp ? fp->f_dentry->d_inode->i_bdev : 0, cdi->use_count) && >+ (cdo->capability & CDC_LOCK) && !keeplocked) { > cdinfo(CD_CLOSE, "Unlocking door!\n"); > cdo->lock_door(cdi, 0); > } >@@ -1494,6 +1497,38 @@ > tracks->cdi, tracks->xa); > } > >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+/* >+ * MEDIA_LOCK, MEDIA_UNLOCK >+ * optarg == 0 - do not adjust usage count (compatibility) >+ * optarg == 1 - adjust usage count >+ */ >+int cdrom_mediactl(struct cdrom_device_info *cdi, struct block_device *bdev, int op, int optarg) >+{ >+ struct cdrom_device_ops *cdo = cdi->ops; >+ >+ switch (op) { >+ case MEDIA_LOCK: >+ case MEDIA_UNLOCK: >+ if (op == MEDIA_UNLOCK && optarg) { >+ cdi->use_count--; >+ if (cdi->use_count < 0) >+ cdi->use_count = 0; >+ } >+ if (cdo->capability & ~cdi->mask & CDC_LOCK && >+ cdi->options & CDO_LOCK && >+ supermount_usage_count(bdev, cdi->use_count) == 0) >+ cdo->lock_door(cdi, (op == MEDIA_LOCK)); >+ if (op == MEDIA_LOCK && optarg) >+ cdi->use_count++; >+ break; >+ default: >+ return -EINVAL; >+ } >+ return 0; >+} >+#endif >+ > /* Requests to the low-level drivers will /always/ be done in the > following format convention: > >@@ -2237,14 +2272,18 @@ > return 0; > } > >-static int cdrom_ioctl_eject(struct cdrom_device_info *cdi) >+static int cdrom_ioctl_eject(struct cdrom_device_info *cdi, struct inode *ip) > { > cdinfo(CD_DO_IOCTL, "entering CDROMEJECT\n"); > > if (!CDROM_CAN(CDC_OPEN_TRAY)) > return -ENOSYS; >- if (cdi->use_count != 1 || keeplocked) >- return -EBUSY; >+// if (cdi->use_count != 1 || keeplocked) >+// return -EBUSY; >+ if (keeplocked || >+ (supermount_usage_count(ip->i_bdev,cdi->use_count) != 1)) >+ return -EBUSY; >+ > if (CDROM_CAN(CDC_LOCK)) { > int ret = cdi->ops->lock_door(cdi, 0); > if (ret) >@@ -2713,7 +2752,7 @@ > case CDROMMULTISESSION: > return cdrom_ioctl_multisession(cdi, argp); > case CDROMEJECT: >- return cdrom_ioctl_eject(cdi); >+ return cdrom_ioctl_eject(cdi,ip); > case CDROMCLOSETRAY: > return cdrom_ioctl_closetray(cdi); > case CDROMEJECT_SW: >@@ -3270,6 +3309,9 @@ > EXPORT_SYMBOL(cdrom_release); > EXPORT_SYMBOL(cdrom_ioctl); > EXPORT_SYMBOL(cdrom_media_changed); >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+EXPORT_SYMBOL(cdrom_mediactl); >+#endif > EXPORT_SYMBOL(cdrom_number_of_slots); > EXPORT_SYMBOL(cdrom_mode_select); > EXPORT_SYMBOL(cdrom_mode_sense); >diff -urN linux-2.6.25.4.orig/drivers/ide/ide-cd.c linux-2.6.25.4/drivers/ide/ide-cd.c >--- linux-2.6.25.4.orig/drivers/ide/ide-cd.c 2008-04-18 01:20:03 +0300 >+++ linux-2.6.25.4/drivers/ide/ide-cd.c 2008-05-20 01:27:53 +0300 >@@ -40,10 +40,10 @@ > #include <linux/bcd.h> > > #include <scsi/scsi.h> /* For SCSI -> ATAPI command conversion */ >- > #include <asm/irq.h> > #include <asm/io.h> > #include <asm/byteorder.h> >+#include <linux/supermount_media.h> > #include <asm/uaccess.h> > #include <asm/unaligned.h> > >@@ -2106,6 +2106,25 @@ > struct gendisk *disk = inode->i_bdev->bd_disk; > struct cdrom_info *info = ide_cd_g(disk); > >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+ /* >+ * This is the same sort of clockload as use in blkdev_get. >+ * We need information whether device is supermounted inside >+ * of cdrom_release to decide if tray must be unlocked. >+ * This information so far is available only by looking >+ * up superblock but it needs struct *bdev and it is not >+ * available in cdrom_release anymore >+ */ >+ struct dentry t_dentry; >+ struct file t_file; >+ >+ if (!file) { >+ t_file.f_dentry = &t_dentry; >+ t_dentry.d_inode = inode; >+ file = &t_file; >+ } >+#endif >+ > cdrom_release (&info->devinfo, file); > > ide_cd_put(info); >@@ -2190,6 +2209,38 @@ > > return 0; > } >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+/* >+MADGUS: AJJJJ PLEASE READ DTHE LKML ABOUT NEW STUFFS >+static int idecd_mediactl(struct block_device *bdev, int op, int arg) >+{ >+ struct gendisk *disk = bdev->bd_disk; >+ ide_drive_t *drive = disk->private_data; >+ struct cdrom_info *info = drive->driver_data; >+ return cdrom_mediactl(&info->devinfo, bdev, op, arg); >+ >+ struct cdrom_info *info = drive->driver_data; >+ >+ CDROM_STATE_FLAGS (drive)->media_changed = 1; >+ CDROM_STATE_FLAGS (drive)->toc_valid = 0; >+ info->nsectors_buffered = 0; >+ >+}*/ >+static int idecd_mediactl(struct block_device *bdev, int op, int arg) >+{ >+ //struct gendisk *disk = bdev->bd_disk; //this def works >+ return 0; >+ /* >+ FIXME: some of this two defs, goes into Page Faults >+ There are a lot of controls over CD_ROM disabling for a while >+ >+ ide_drive_t *drive = disk->private_data; >+ //info->drive; >+ struct cdrom_info *info = drive->driver_data; >+ return cdrom_mediactl(&info->devinfo, bdev, op, arg); >+ */ >+} >+#endif > > static struct block_device_operations idecd_ops = { > .owner = THIS_MODULE, >@@ -2197,7 +2248,11 @@ > .release = idecd_release, > .ioctl = idecd_ioctl, > .media_changed = idecd_media_changed, >- .revalidate_disk= idecd_revalidate_disk >+ .revalidate_disk= idecd_revalidate_disk, >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+ .mediactl = idecd_mediactl, >+#endif >+ > }; > > /* options */ >diff -urN linux-2.6.25.4.orig/drivers/ide/ide-floppy.c linux-2.6.25.4/drivers/ide/ide-floppy.c >--- linux-2.6.25.4.orig/drivers/ide/ide-floppy.c 2008-04-18 01:20:03 +0300 >+++ linux-2.6.25.4/drivers/ide/ide-floppy.c 2008-05-20 01:29:29 +0300 >@@ -36,6 +36,8 @@ > > #include <scsi/scsi_ioctl.h> > >+#include <linux/supermount_media.h> >+ > #include <asm/byteorder.h> > #include <linux/irq.h> > #include <linux/uaccess.h> >@@ -1439,7 +1441,7 @@ > > floppy->openers++; > >- if (floppy->openers == 1) { >+ if (supermount_usage_count(inode->i_bdev, floppy->openers) == 1) { > floppy->flags &= ~IDEFLOPPY_FLAG_FORMAT_IN_PROGRESS; > /* Just in case */ > >@@ -1493,7 +1495,7 @@ > > debug_log("Reached %s\n", __func__); > >- if (floppy->openers == 1) { >+ if (supermount_usage_count(inode->i_bdev, floppy->openers) == 1) { > /* IOMEGA Clik! drives do not support lock/unlock commands */ > if (!(floppy->flags & IDEFLOPPY_FLAG_CLIK_DRIVE)) { > idefloppy_create_prevent_cmd(&pc, 0); >@@ -1522,9 +1524,10 @@ > } > > static int ide_floppy_lockdoor(idefloppy_floppy_t *floppy, idefloppy_pc_t *pc, >+ struct block_device *bdev, > unsigned long arg, unsigned int cmd) > { >- if (floppy->openers > 1) >+ if (supermount_usage_count(bdev, floppy->openers) > 1) > return -EBUSY; > > /* The IOMEGA Clik! Drive doesn't support this command - >@@ -1610,7 +1613,7 @@ > case CDROMEJECT: > /* fall through */ > case CDROM_LOCKDOOR: >- return ide_floppy_lockdoor(floppy, &pc, arg, cmd); >+ return ide_floppy_lockdoor(floppy, &pc, bdev, arg, cmd); > case IDEFLOPPY_IOCTL_FORMAT_SUPPORTED: > return 0; > case IDEFLOPPY_IOCTL_FORMAT_GET_CAPACITY: >@@ -1662,6 +1665,38 @@ > set_capacity(disk, idefloppy_capacity(floppy->drive)); > return 0; > } >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+static int idefloppy_mediactl (struct block_device *bdev, int op, int optarg) >+{ >+ ide_drive_t *drive = bdev->bd_disk->private_data; >+ idefloppy_floppy_t *floppy = drive->driver_data; >+ idefloppy_pc_t pc; >+ >+ switch (op) { >+ case MEDIA_LOCK: >+ case MEDIA_UNLOCK: >+ if (op == MEDIA_UNLOCK && optarg) { >+ floppy->openers--; >+ if (floppy->openers < 0) >+ floppy->openers = 0; >+ } >+ /* IOMEGA Clik! drives do not support lock/unlock commands */ >+ if ((floppy->flags & IDEFLOPPY_FLAG_CLIK_DRIVE) >+ && supermount_usage_count(bdev, floppy->openers) == 0) { >+ idefloppy_create_prevent_cmd(&pc, (op == MEDIA_LOCK)); >+ (void) idefloppy_queue_pc_tail(drive, &pc); >+ } >+ if (op == MEDIA_LOCK && optarg) >+ floppy->openers++; >+ break; >+ default: >+ return -EINVAL; >+ } >+ return 0; >+} >+#endif >+ >+ > > static struct block_device_operations idefloppy_ops = { > .owner = THIS_MODULE, >@@ -1670,7 +1705,10 @@ > .ioctl = idefloppy_ioctl, > .getgeo = idefloppy_getgeo, > .media_changed = idefloppy_media_changed, >- .revalidate_disk= idefloppy_revalidate_disk >+ .revalidate_disk= idefloppy_revalidate_disk, >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+ .mediactl = idefloppy_mediactl, >+#endif > }; > > static int ide_floppy_probe(ide_drive_t *drive) >diff -urN linux-2.6.25.4.orig/drivers/scsi/sr.c linux-2.6.25.4/drivers/scsi/sr.c >--- linux-2.6.25.4.orig/drivers/scsi/sr.c 2008-04-18 01:20:09 +0300 >+++ linux-2.6.25.4/drivers/scsi/sr.c 2008-05-20 01:27:53 +0300 >@@ -488,6 +488,27 @@ > { > int ret; > struct scsi_cd *cd = scsi_cd(inode->i_bdev->bd_disk); >+ >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+ /* >+ * This is the same sort of clockload as use in blkdev_get. >+ * We need information whether device is supermounted inside >+ * of cdrom_release to decide if tray must be unlocked. >+ * This information so far is available only by looking >+ * up superblock but it needs struct *bdev and it is not >+ * available in cdrom_release anymore >+ */ >+ struct dentry t_dentry; >+ struct file t_file; >+ >+ if (!file) { >+ t_file.f_dentry = &t_dentry; >+ t_dentry.d_inode = inode; >+ file = &t_file; >+ } >+#endif >+ >+ > ret = cdrom_release(&cd->cdi, file); > if(ret) > return ret; >@@ -537,6 +558,15 @@ > return cdrom_media_changed(&cd->cdi); > } > >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+static int sr_block_mediactl(struct block_device *bdev, int op, int arg) >+{ >+ struct gendisk *disk = bdev->bd_disk; >+ struct scsi_cd *cd = scsi_cd(disk); >+ return cdrom_mediactl(&cd->cdi, bdev, op, arg); >+} >+#endif >+ > static struct block_device_operations sr_bdops = > { > .owner = THIS_MODULE, >@@ -544,6 +574,9 @@ > .release = sr_block_release, > .ioctl = sr_block_ioctl, > .media_changed = sr_block_media_changed, >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+ .mediactl = sr_block_mediactl, >+#endif > /* > * No compat_ioctl for now because sr_block_ioctl never > * seems to pass arbitary ioctls down to host drivers. >diff -urN linux-2.6.25.4.orig/fs/block_dev.c linux-2.6.25.4/fs/block_dev.c >--- linux-2.6.25.4.orig/fs/block_dev.c 2008-04-18 01:20:10 +0300 >+++ linux-2.6.25.4/fs/block_dev.c 2008-05-20 01:27:53 +0300 >@@ -881,11 +881,46 @@ > struct gendisk *disk = bdev->bd_disk; > struct block_device_operations * bdops = disk->fops; > >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+ struct super_block *sb = NULL; >+ int supermounted = 0; >+#endif >+ > if (!bdops->media_changed) > return 0; > if (!bdops->media_changed(bdev->bd_disk)) > return 0; > >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+ sb = get_super(bdev); >+ if (sb) { >+ atomic_set(&sb->s_media_changed, 1); >+ supermounted = sb->s_flags & MS_SUPERMOUNTED; >+ drop_super(sb); >+ } >+ >+ /* >+ * Supermount used to did invalidate_device followed by >+ * destroy_buffers. invalidate_device hardly does anything >+ * useful here as it won't really invalidate "busy" inodes >+ * and every inode in subfs is busy (at least one reference >+ * from superfs exists). So now it just flushes >+ * buffers so they do not accidentally overwrite newly >+ * inserted media >+ * >+ * FIXME unfortunately during umount VFS may write on its >+ * own, like write_super. Those writes will go to a wrong >+ * media thus corrupting it :( >+ * >+ * There is no error print because supermount warns user >+ * itself. >+ */ >+ >+ if (supermounted) { >+ invalidate_bdev(bdev); >+ } else >+#endif >+ > if (__invalidate_device(bdev)) > printk("VFS: busy inodes on changed media.\n"); > >diff -urN linux-2.6.25.4.orig/fs/ext2/super.c linux-2.6.25.4/fs/ext2/super.c >--- linux-2.6.25.4.orig/fs/ext2/super.c 2008-04-18 01:20:10 +0300 >+++ linux-2.6.25.4/fs/ext2/super.c 2008-05-20 01:27:53 +0300 >@@ -424,6 +424,12 @@ > {Opt_usrquota, "usrquota"}, > {Opt_reservation, "reservation"}, > {Opt_noreservation, "noreservation"}, >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+ /* Silently ignore NLS options */ >+ {Opt_ignore, "iocharset"}, >+ {Opt_ignore, "codepage"}, >+ {Opt_ignore, "umask"}, >+#endif > {Opt_err, NULL} > }; > >diff -urN linux-2.6.25.4.orig/fs/isofs/inode.c linux-2.6.25.4/fs/isofs/inode.c >--- linux-2.6.25.4.orig/fs/isofs/inode.c 2008-04-18 01:20:10 +0300 >+++ linux-2.6.25.4/fs/isofs/inode.c 2008-05-20 01:27:53 +0300 >@@ -345,6 +345,10 @@ > {Opt_ignore, "conv=auto"}, > {Opt_ignore, "conv=a"}, > {Opt_nocompress, "nocompress"}, >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+ {Opt_ignore, "codepage=%s"}, >+ {Opt_ignore, "umask=%s"}, >+#endif > {Opt_err, NULL} > }; > >diff -urN linux-2.6.25.4.orig/fs/Kconfig linux-2.6.25.4/fs/Kconfig >--- linux-2.6.25.4.orig/fs/Kconfig 2008-04-18 01:20:10 +0300 >+++ linux-2.6.25.4/fs/Kconfig 2008-05-20 01:27:53 +0300 >@@ -867,6 +867,8 @@ > > menu "Pseudo filesystems" > >+source "fs/supermount/Kconfig" >+ > config PROC_FS > bool "/proc file system support" if EMBEDDED > default y >diff -urN linux-2.6.25.4.orig/fs/Makefile linux-2.6.25.4/fs/Makefile >--- linux-2.6.25.4.orig/fs/Makefile 2008-04-18 01:20:10 +0300 >+++ linux-2.6.25.4/fs/Makefile 2008-05-20 01:27:53 +0300 >@@ -116,6 +116,7 @@ > obj-$(CONFIG_BEFS_FS) += befs/ > obj-$(CONFIG_HOSTFS) += hostfs/ > obj-$(CONFIG_HPPFS) += hppfs/ >+obj-$(CONFIG_SUPERMOUNT) += supermount/ > obj-$(CONFIG_DEBUG_FS) += debugfs/ > obj-$(CONFIG_OCFS2_FS) += ocfs2/ > obj-$(CONFIG_GFS2_FS) += gfs2/ >diff -urN linux-2.6.25.4.orig/fs/namespace.c linux-2.6.25.4/fs/namespace.c >--- linux-2.6.25.4.orig/fs/namespace.c 2008-04-18 01:20:10 +0300 >+++ linux-2.6.25.4/fs/namespace.c 2008-05-20 01:27:53 +0300 >@@ -1438,6 +1438,15 @@ > if (retval) > goto dput_out; > >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+ if (!(flags & (MS_REMOUNT | MS_MOVE)) && >+ (nd.path.mnt->mnt_sb->s_type->fs_flags & FS_NO_SUBMNT)) { >+ retval = -EPERM; >+ path_put(&nd.path); >+ return retval; >+ } >+#endif >+ > if (flags & MS_REMOUNT) > retval = do_remount(&nd, flags & ~MS_REMOUNT, mnt_flags, > data_page); >diff -urN linux-2.6.25.4.orig/fs/super.c linux-2.6.25.4/fs/super.c >--- linux-2.6.25.4.orig/fs/super.c 2008-04-18 01:20:10 +0300 >+++ linux-2.6.25.4/fs/super.c 2008-05-20 01:27:53 +0300 >@@ -90,6 +90,9 @@ > s->s_qcop = sb_quotactl_ops; > s->s_op = &default_op; > s->s_time_gran = 1000000000; >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+ atomic_set(&s->s_media_changed, 0); >+#endif > } > out: > return s; >@@ -618,6 +621,10 @@ > return 0; > } > >+#ifdef CONFIG_SUPERMOUNT_MODULE >+EXPORT_SYMBOL(do_remount_sb); >+#endif >+ > static void do_emergency_remount(unsigned long foo) > { > struct super_block *sb; >@@ -750,7 +757,13 @@ > goto error_s; > > if (s->s_root) { >- if ((flags ^ s->s_flags) & MS_RDONLY) { >+ if (((flags ^ s->s_flags) & MS_RDONLY) >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+ /* disallow double mounting for supermounted device */ >+ || ((flags | s->s_flags) & MS_SUPERMOUNTED) >+#endif >+ ) >+ { > up_write(&s->s_umount); > deactivate_super(s); > error = -EBUSY; >@@ -866,6 +879,17 @@ > return ERR_PTR(-ENODEV); > > error = -ENOMEM; >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+ /* sanity checks; supermount relies on these assumptions */ >+ if (flags & MS_SUPERMOUNTED) { >+ error = -EINVAL; >+ if (type->fs_flags & FS_RENAME_DOES_D_MOVE) >+ goto out; >+ if (!(type->fs_flags & FS_REQUIRES_DEV)) >+ goto out; >+ error = -ENOMEM; >+ } >+#endif > mnt = alloc_vfsmnt(name); > if (!mnt) > goto out; >@@ -945,7 +969,11 @@ > put_filesystem(type); > return mnt; > } >+#ifdef CONFIG_SUPERMOUNT_MODULE >+EXPORT_SYMBOL(do_kern_mount); >+#else > EXPORT_SYMBOL_GPL(do_kern_mount); >+#endif > > struct vfsmount *kern_mount_data(struct file_system_type *type, void *data) > { >diff -urN linux-2.6.25.4.orig/fs/supermount/changelog linux-2.6.25.4/fs/supermount/changelog >--- linux-2.6.25.4.orig/fs/supermount/changelog 1970-01-01 03:00:00 +0300 >+++ linux-2.6.25.4/fs/supermount/changelog 2008-05-20 01:27:53 +0300 >@@ -0,0 +1,462 @@ >+* ? ? ? Andrey Borzenkov <arvidjaar@mail.ru> ?.?.? >+ >+* Sun 18 Jan 2004 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.4 >+ >+ - yet another attempt to fix detection of USB drive removale >+ (explicitly check for SDVE_DEL state in sd.c:sd_media_changed) >+ >+ - fixed i18n patch for ext2 and udf (ignored options) >+ >+ - ->nopage prototype changed; it makes it incompatible with >+ kernel < 2.6.1 (I wonder how it compiled - or did I miss >+ warning). Add comments about ->populate and why it won't be >+ implemented >+ >+ - fix "auto" option. Simplification of option processing turned >+ out to be a mess :( Reported by Bart >+ >+* Mon 12 Jan 2004 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.3b >+ >+ - Fix compilation problem in subfs.c with older GCC - declaration >+ after statement in clear_inodes(bug 875009). Pointed by Adrian Punga. >+ >+* Wed 07 Jan 2004 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.3a >+ >+ - fix Oops during options parsing if no subfs option is >+ given after "--" (bug 869863) >+ >+* Mon 29 Dec 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.3 >+ >+ - move test for device offline in sd.c to the top in attempt >+ to fix USB removale problem >+ >+ - simplify options procesing >+ >+ - remove cdrom "open failed" message (it is now under >+ CDROM_OPEN flag like all others in cdrom_open). >+ >+ - rewrote option parsing to use new lib/parse library >+ >+* Sun 26 Oct 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.2a >+ >+ - remove some obsolete files that are not used anymore from CVS >+ (i.e. from patch) >+ >+ - isofs needs extra ignored options now. It rurns out it silently >+ ignored any unknown option before new and shiny option parsing >+ code >+ >+ - consistently use "kernel 2.6" in version print >+ >+ - updates for -test8. Change ext2/udf patches to match new options >+ parsing code. >+ >+* Wed 10 Sep 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.2 >+ >+ - NODEV is gone, use MKDEV(UNNAMED_MAJOR, 0) instead, it is just >+ as good as anything else >+ >+* Sat 23 Aug 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.1d >+ >+ - rediff against 2.6.0-test4 >+ >+* Mon 18 Aug 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.1c >+ >+ - fix compilation without CONFIG_SUPERMOUNT. Reported by >+ Igor Strygin >+ >+* Sun 10 Aug 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.1b >+ >+ - /proc/fs/supermount/version is read-only, do not set permissions to >+ rw >+ >+ - remove generic_unplug_queue from ide_do_drive_cmd, apparently >+ it is redundant now (fixes bug 785691) >+ >+ - rediff for 2.6.0-test3 >+ >+* Sun 27 Jul 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.1a >+ >+ - rediff for 2.6.0-test2 >+ >+* Sun 13 Jul 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.1 >+ >+ - hush most unused variables warnings with CONFIG_DEBUG (idea >+ suggested by Jeff Garzik) >+ >+ - replace ugly SUPERMOUNT_TARCE_{ENTER,LEAVE}[_ARGS] with ENTER/LEAVE >+ Use %p throughout for pointer output. >+ >+ - send DN_CREATE for root on mount >+ >+ - allow open and readdir on mountpoint even if subfs is unmounted. >+ This provides for active monitoring by FAM/dnotify and is overall >+ less surprising - ls /mnt/cdrom just returns empty directory now >+ instead of error. For technical reasons ->flush and ->permissions >+ need be aware about it too. >+ >+ Files opened while subfs is unmounted are NOT usable for anything >+ except readdir even AFTER subfs gets mounted. So application >+ has to reopen directory to rescan it. AFAIK all of them do it :) >+ >+* Sat 12 Jul 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.0a >+ >+ - add struct nameidata * goes into d_revalidate as well >+ >+ - add struct nameidata * to some inode methods (introduced in >+ 2.5.75). Currently it is just passed along - it is not clear if >+ local subfs-specific nameidata has to be constructed. Apparently >+ it existed in follow_link all the time without any evil done. >+ >+ - fix small races in supermount_getattr - do not attempt to >+ get subdent if prevent_umount failed >+ >+* Mon 23 Jun 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.0 >+ >+ - added ->getattr; it never fails on both unmounted mountpoint >+ and stale open files - to ensure fuser -m /mnt/cdrom still finds them >+ >+ - merge 2.5.73; update sd.c to new refcount interface in sd.c >+ >+ - fix SUPERMOUNT_BUG_ON_LOCKED without SUPERMOUNT_DEBUG >+ >+ - add media management to ide-floppy and those CD-ROM drivers that >+ are using standard cdrom_* calls (cdu31a, cm206, mcd, mcdx, sbpcd). >+ Testdrive revealed that none of them builds for other reasons :) >+ >+ - add (un)mark_media_supermounted; mark after mount, unmark >+ before umount. These functions just manipulate bd_disk->scount >+ >+ - add scount to struct gendisk to serve as count of total >+ supermounted partitions; use everywhere to check if device >+ is supermounted >+ >+* Tue 17 Jun 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.0test2 >+ >+ - update kernel patch for 2.5.72 (there was a reject in >+ drivers/ide/ide-io.c) >+ >+ - move init_inode_info into alloc_inode; use iget_locked >+ instead of iget to skip ->read_inode with intent to move >+ inode init under if (inode->i_state & I_NEW) >+ >+ - polish dentry.c a bit while I am on it >+ >+ - ->d_compare is not run under dcache_lock anymore. Pointed by >+ Maneesh Soni. As we can't sleep in d_compare, use rwlock to >+ protect against races with supermount_clean_dentries until >+ I find better solution. >+ >+ - remove new_subfs_dentry - it is not used anymore and comments >+ there were wrong anyway >+ >+* Sun 01 Jun 2003 Andrey Borzenkov <arvidjaar@mail.ru> 2.0.0test1 >+ >+ - add sr and sd support >+ >+ - update sb s_blocksize and s_blocksize_bits on subfs mount >+ >+ - use BKL around chek_disk_change; it is not garanteed by VFS now >+ >+ - llseek no more updates file readahead pointer >+ >+ - used bdev not gendisk in mediactl - it needs it for >+ supermount_usage_count. It probably needs redesign. Also some minor >+ changes on the way. >+ >+ - 2.5 changes. supermount_get_sb, inode alloc/dealloc, use bdev instead >+ of dev, Makefile change, add CONFIG_SUPERMOUNT_DEBUG, do_remount_sb >+ prototype change, mknod prototype change, UPDATE_ATIME dead, >+ dnotify_parent is now true function >+ >+* Tue 27 May 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.2.7 >+ >+ - (re-)set root inode flags after unmounting subfs >+ >+ - fix mounting of ro media on rw filesystem. It was broken by fix >+ in 1.2.4. Remove saved s_mflags and just use real sb->s_flags. >+ Reported by Con Kolivas >+ >+ - do not use MS_MGC_VAL in subfs_real_mount - it is not used anywhere >+ in kernel and all lower bits are already taken in 2.5 >+ >+ - configure help from Marc-Christian Petersen >+ >+ - use subsb->s_bdev instead of looking up using s_dev. This also >+ fixes problem with O(1) patch (bug 737783) >+ >+* Wed 14 May 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.2.6 >+ >+ - check for correct inode in attach_subfs_inode - if super inode >+ has subinode attached it must be the same as is currently >+ resuested. Else it is a bug. >+ >+ - use lookup_one_len to lookup subfs dentry in cache; check if >+ subfs did not change name and lookup "real" dentry in parent >+ cache in this case. This finally puts an end to the attempt to >+ control subfs dcache. The only thing we rely upon now is that >+ nothing can change directory contents while in lookup method. >+ >+ - implement d_hash and d_compare to properly support case- >+ insensitive filesystems (vfat & Co). >+ >+ - reset super inode i_mapping so it does not point to freed >+ subinode mapping when unmounting subfs >+ >+ - send dnotify_parent on dentry cleanup, not on inode >+ >+ - use write_inode_now in ->write_inode. Reading intricated flags >+ manipulations in fs/inode.c was just too intimidating. It is not as >+ much overhead as it looks like - it is called from sync_one only >+ so all dirty pages are scheduled to be flushed anyway. >+ >+* Sun 04 May 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.2.5 >+ >+ - enable dentries caching; properly use d_add when dentry is first >+ looked up and d_instantiate when inode is finally available. >+ >+ - first stab at sending notifications on media umount/mount >+ >+ - more caveats :( >+ >+* Sat 26 Apr 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.2.4 >+ >+ - fix write_inode/prepare_inode deadlock. It looked like >+ set I_LOCK >+ down(&sbi->sem); >+ iget4 -> wait_on_inode >+ down(&sbi->sem) >+ >+ - MS_ACTIVE was reset on subfs remount >+ >+ - print process PID in trace >+ >+ - adjust directory structure to make patch generation easier >+ >+* Sun 20 Apr 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.2.3 >+ >+ - dummy version due to script problems >+ >+* Sun 20 Apr 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.2.2 >+ >+ - remove #ifdef CONFIG_PROC_FS from init.c, it already exists in >+ supermount.h >+ >+ - no_tray_lock -> TRAY_LOCK_NEVER. Just to make sure it remains >+ compatible (even if it is very unlikely anybody is using it). >+ >+ - backout kernel stat patch; remove getattr alltogether. It fixed >+ fuser -m for stale files. getattr remains uniplemented because >+ it does not seem to be used anywhere in kernel at all so I am not >+ actually sure how to implement it >+ >+ It also backouts 10_readlik and 30_lseek as they are not used >+ anymore for a long time. >+ >+ - remove bogus check for sub- and superinodes i_count. i_count is >+ outside of supermount control; the only thing that we must be >+ sure in - inode is free on umount. >+ >+ - reset root inode attributes and operations on umount. This fixes >+ the problem of attempt to mount media on ls -l /mnt after subfs >+ has been mounted once. >+ >+ - remove ->stale. For all practical purposes an entity is stale iff >+ it does not (yet) have associated subfs entity. Creating yet another >+ field that just mirrors this condition does not add a single bit >+ of useful information. >+ >+ - subfs_is_busy iff subfs mnt_count > 1 >+ >+* Sat 05 Apr 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.2.1 >+ >+ - fixed stupid thinko in mediactl methods (the effect was it was >+ impossible to manually eject ro media). It was exposed by recent >+ changes. No changes in supermount itself only in driver code >+ >+ - replace no_tray_lock with tray_lock={never,onwrite,always} with >+ "onwrite" being default. I stil do not quite like resulting code >+ but it will do for now. >+ >+ - simplified subfs_(get|put)_(write|read) interface; merge both >+ read/write in one function; remove subfs_get_atime. To my surprise >+ it resulted on more clean interface in the rest of supermount even >+ if subfs_(get|put)_access does look a bit weird. >+ >+* Sun 30 Mar 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.2.0 >+ >+ - changed proc command format to >+ <devname> [enable|disable] [release [force]] >+ >+ - add /proc/fs/supermount/version; printk version on starup only >+ if procfs is not configured. >+ >+ - unify media lock rules for thosed drivers using implementing >+ mediactl (cdrom, sd, ide-floppy as of this writing). Lock >+ media for both manual and software eject; do not unlock media >+ that is in used by something else. >+ >+ - added show_options method to super.c >+ >+ - implemented /proc/fs/supermount/subfs read as seq_file >+ >+* Sat 29 Mar 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.1.4 >+ >+ - supermount_rename leftover - it does not need new_subfs_dentry. >+ It still worked because new dentry was destroyed immediately, but >+ let's do it the right way. Also mark target rw if exists >+ >+ - propagate S_IMMUTABLE, S_NOATIME and S_APPEND flags from subfs >+ to super inode. Filesystems usually rely on VFS to check these >+ flags which means it was possible to overwrite immutable file. Also >+ needed for atime management (to be added). >+ >+ - added atime management. It is based on grep UPDATE_ATIME so it may >+ be incomplete; still it gives right results in most obvious cases :) >+ It respects no(dir)atime mount flag. >+ >+ - Make sure super inode times and sizes are updated to reflect subfs >+ inode. Done in preparation to remove stat kernel patch. Only those >+ fields needed by stat are updated. >+ >+ - remove assertion subi->i_count == 1 from clear_inode. Subfs icache >+ is managed independently and there is no lock (as opposed to dentry >+ case where we never do cached lookup for subfs). >+ >+ - use inode cache for supermount inodes to avoid linear search. This >+ moves almost the whole inode creation task into read_inode2 - the >+ only place where inode needs to be created is root inode. >+ >+ We use inode numbers from subfs; root inode is never cached until >+ subfs has been mounted. Stale inodes are removed from cache in >+ clean_inodes (just like dentries are in clean_dentries) so aliasing >+ problem is solved. >+ >+ - add "disabled" state - prevent any attempt to mount media >+ >+ - add procfs support. Reading /proc/fs/supermount/subfs will return >+ list of subfs and their status. Writing into it allows you to >+ control status of subfs (mounted/unmounted, enabled/dusabled) >+ >+ - fix race in clear_inode. It was possible that subfs was umounted >+ after inode has been removed from list but before its access >+ counters were cleared. The code that checkes for that exists only >+ for debugging still I'd like it to be useful. >+ >+* Sun 16 Mar 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.1.3 >+ >+ - add CVS verion Id's to files >+ >+ - finally get rid of ugly generation number. It is enough to just >+ mark entity obsolete; we do not care actually when it happened. >+ Now every entity starts out stale; stale flag is reset when subfs >+ object is attached; stale flag is set again for the rest of life >+ in subfs_umount >+ >+ - as a side effect read/write count is associated with inodes >+ exclusively >+ >+ - clear_inode and d_iput need prevent/allow umount to avoid "stealing" >+ subfs while holding active entities. File release is safe as VFS does >+ nou put mnt until _release is finished. >+ >+ - subfs_prevent_umount does not need validator. The worst thing that >+ can happen is that newly mounted subfs is accessed. That does not >+ matter, access to subfs object is checked for validity separately in >+ every case. >+ >+ - finally clarify dcache management. As we cannot garantee coherency >+ between sub- and super- fs caches we ignore subfs cache entirely. >+ Super dentry is instantiated in supermount_lookup and passed around >+ with subfs dentry attached. It does not matter if it is positive or >+ negative. Currently dentries are forced to be deleted in >+ supermount_d_delete but I believe it is not really needed. >+ >+ - print version number on registering filesystem to facilitate debugging >+ >+ - umount subfs as soon as possible in check_disk_change. This eliminates >+ zombie state (mounted but inactive due to media change detected). >+ >+ - kill subfs_is_active, it is the same as subfs_is_mounted now. >+ >+ - kill translations.c it does not do anything special now; move functions >+ in file/dentry/namei >+ >+ - fix stupid bug in get_supermount_inode again - super inode must >+ be inited and attached just once not every time. >+ Praise assertions again :) >+ >+* 10 Mar 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.1.2 >+ >+ - allow fs=type1:type2:... >+ >+ - remove supermount_drop_dentries (no more used) >+ >+ - add some more assertions to subfs_umount >+ >+* 10 Mar 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.1.1 >+ >+ - fix stupid bug in subfs inode refcounting. It was visible with >+ hardlinks only so CD-ROM users would not ever see it. >+ >+ - fix even more stupid bug in unlink - subi was used instead of superi >+ >+* 8 Mar 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.1.0 >+ >+ - files cannot use inode obsolete flag - it does not work for >+ root inodes >+ >+ - make file system structure exact mirror of subfs - now there >+ is 1-to-1 correspondence between super- and subfs files, dentries >+ and inodes. This is needed to properly manage write access without >+ too much locking >+ >+ - finally realized that subfs cannot be obsolete; use either file >+ or inode pointer in all calls into subfs.c to check for freshness >+ >+ - debug is now per-mount; added trace of all functions >+ >+ - move setting of SUPERMOUNT_DEBUG to Makefile instead of supermount_i.h >+ >+* 1 Mar 2003 Andrey Borzenkov <arvidjaar@mail.ru> 1.0.0 >+ >+ - make sure it is always possible umount subfs. It does it by >+ keeping list of opened files (in addition to inodes) and closing >+ them if media change is detected. >+ >+ - make sure subfs can't go away while it is being in use. For >+ files it get_file for subfile to ensure subfs mnt is not freed. For >+ inode operation it increments usage counter of submnt for duration >+ of operation. >+ >+ - make sure supermounted device can't be mounted twice (that is >+ allowed by kernel) because it relies on being the only "owner" of >+ subfs. It does it by checking for MS_SUPERMOUNTED flag on mount. >+ >+ - make sure you can't mount over supermounted directory. Allowing >+ it is a nightmare and I cannot see any reason for it (if you can >+ provide valid one - I may reconsider this part). It does so by >+ adding fstype flag and checking it on mounting. >+ >+ - reject mount -o remount for the time being (until we can deal >+ with it properly) >+ >+ - completely change implementation of individual inode methods. >+ Instead of calling VFS recursively it calls subfs methods directly. >+ It is to ensure we are in full control of subfs (assuming it behaves >+ properly :) >+ >+ - properly implement VM methods. Currently changing media would >+ leave you with vm_area pointing to no more existing inode ... >+ >+ - remove many obsolete pieces like flags that just duplicate >+ existing information. >+ >+ - ensure coherency between super- and subfs dentries. Either we >+ have both active in dcache or do not have them at all. >+ >+ - fixe the problem with media ejection after direct access to CD-ROM >+ >+ - add "just disc change" to all operations to detect media change >+ as soon as possible. It is possible to add option to do it just >+ for writable media or to completely turn it off. >diff -urN linux-2.6.25.4.orig/fs/supermount/dentry.c linux-2.6.25.4/fs/supermount/dentry.c >--- linux-2.6.25.4.orig/fs/supermount/dentry.c 1970-01-01 03:00:00 +0300 >+++ linux-2.6.25.4/fs/supermount/dentry.c 2008-05-20 01:27:53 +0300 >@@ -0,0 +1,282 @@ >+/* >+ * linux/fs/supermount/dentry.c >+ * >+ * Original version: >+ * Copyright (C) 1995 >+ * Stephen Tweedie (sct@dcs.ed.ac.uk) >+ * >+ * Rewriten for kernel 2.2 & 2.4. (C) 1999, 2000 Alexis Mikhailov >+ * (alexis@abc.cap.ru) >+ * Rewritten for kernel 2.4.21 (C) 2003 Andrey Borzenkov >+ * (arvidjaar@mail.ru) >+ * Rewritten for kernel 2.5.70 (C) 2003 Andrey Borzenkov >+ * (arvidjaar@mail.ru) >+ * >+ * $Id: dentry.c,v 1.9.2.3 2003/07/13 14:52:43 bor Exp $ >+ */ >+ >+#define S_DBG_TRACE_CURRENT S_DBG_TRACE_DENTRY >+#include "supermount.h" >+ >+int >+init_dentry_info(struct dentry *dentry) >+{ >+ struct super_block *sb = dentry->d_sb; >+ struct supermount_dentry_info *sdi; >+ int rc; >+ >+ ENTER(sb, "dentry=%s", dentry->d_name.name); >+ >+ rc = 1; >+ sdi = kmalloc(sizeof(*sdi), GFP_KERNEL); >+ if (!sdi) >+ goto out; >+ >+ memset(sdi, 0, sizeof(*sdi)); >+ >+ INIT_LIST_HEAD(&sdi->list); >+ sdi->dentry = 0; >+ sdi->host = dentry; >+ >+ dentry->d_fsdata = sdi; >+ dentry->d_op = &supermount_dops; >+ >+ rc = 0; >+out: >+ LEAVE(sb, "dentry=%s rc=%d", dentry->d_name.name, rc); >+ >+ return rc; >+ >+} >+ >+void >+attach_subfs_dentry(struct dentry *dentry, struct dentry *subdent) >+{ >+ struct super_block *sb = dentry->d_sb; >+ struct supermount_sb_info *sbi = supermount_sbi(sb); >+ struct supermount_dentry_info *sdi; >+ >+ ENTER(sb, "dentry=%s subd=%p", dentry->d_name.name, subdent); >+ >+ sdi = supermount_d(dentry); >+ SUPERMOUNT_BUG_LOCKED_ON(sb, sdi->dentry); >+ sdi->dentry = dget(subdent); >+ list_add(&sdi->list, &sbi->s_dentries); >+ >+ LEAVE(sb, "dentry=%s", dentry->d_name.name); >+} >+ >+ >+struct dentry * >+get_subfs_dentry(struct dentry *dentry) >+{ >+ struct super_block *sb = dentry->d_sb; >+ struct dentry *err; >+ struct supermount_dentry_info *sdi = supermount_d(dentry); >+ >+ ENTER(sb, "dentry=%s", dentry->d_name.name); >+ >+ subfs_lock(sb); >+ >+ err = ERR_PTR(-ENOMEDIUM); >+ if (!subfs_is_mounted(sb)) >+ goto out; >+ >+ err = ERR_PTR(-ESTALE); >+ if (is_dentry_obsolete(dentry)) >+ goto out; >+ >+ err = dget(sdi->dentry); >+ SUPERMOUNT_BUG_LOCKED_ON(sb, !err); >+ SUPERMOUNT_BUG_LOCKED_ON(sb, (dentry->d_inode == 0) ^ (err->d_inode == 0)); >+ >+out: >+ subfs_unlock(sb); >+ >+ LEAVE(sb, "dentry=%s subd=%p", dentry->d_name.name, err); >+ >+ return err; >+} >+ >+static int >+supermount_d_revalidate(struct dentry *dentry, struct nameidata *nd) >+{ >+ struct super_block *sb = dentry->d_sb; >+ struct dentry *subd; >+ int rc; >+ struct vfsmount *mnt; >+ >+ ENTER(sb, "dentry=%s", dentry->d_name.name); >+ >+ rc = 0; >+ mnt = subfs_prevent_umount(sb); >+ if (!mnt) >+ goto out; >+ >+ subd = get_subfs_dentry(dentry); >+ >+ if (IS_ERR(subd)) >+ goto allow_umount; >+ >+ rc = 1; >+ /* FIXME do we need to build proper subfs nd? */ >+ if (subd->d_op && subd->d_op->d_revalidate) >+ rc = subd->d_op->d_revalidate(subd, nd); >+ >+ dput(subd); >+allow_umount: >+ subfs_allow_umount(sb, mnt); >+out: >+ >+ LEAVE(sb, "dentry=%s rc=%d", dentry->d_name.name, rc); >+ >+ return rc; >+} >+ >+static int >+supermount_d_hash(struct dentry *dentry, struct qstr *name) >+{ >+ struct super_block *sb = dentry->d_sb; >+ struct dentry *subd; >+ int rc; >+ struct vfsmount *mnt; >+ >+ ENTER(sb, "dentry=%s name=%s", dentry->d_name.name, name->name); >+ >+ rc = -ENOMEDIUM; >+ mnt = subfs_prevent_umount(sb); >+ if (!mnt) >+ goto out; >+ >+ subd = get_subfs_dentry(dentry); >+ rc = PTR_ERR(subd); >+ if (IS_ERR(subd)) >+ goto allow_umount; >+ >+ rc = 0; >+ if (subd->d_op && subd->d_op && subd->d_op->d_hash) >+ rc = subd->d_op->d_hash(subd, name); >+ >+ dput(subd); >+allow_umount: >+ subfs_allow_umount(sb, mnt); >+out: >+ LEAVE(sb, "dentry=%s rc=%d", dentry->d_name.name, rc); >+ >+ return rc; >+} >+ >+ >+rwlock_t d_compare_lock = RW_LOCK_UNLOCKED; >+ >+static int >+supermount_d_compare(struct dentry *dentry, struct qstr *name1, struct qstr *name2) >+{ >+ struct super_block *sb = dentry->d_sb; >+ struct supermount_dentry_info *sdi; >+ struct dentry *subd; >+ int rc; >+ >+ ENTER(sb, "dentry=%s name1=%s name2=%s", dentry->d_name.name, name1->name, name2->name); >+ >+ rc = 1; /* fail by default */ >+ sdi = dentry->d_fsdata; >+ SUPERMOUNT_BUG_ON(!sdi); >+ >+ /* >+ * HACK - FIXME >+ * this protects against races with supermount_clean_dentries >+ * I cannot use blocking calls (i.e. sbi->sem) here and it is not >+ * protected by dcache_lock anymore >+ */ >+ read_lock(&d_compare_lock); >+ subd = sdi->dentry; >+ if (!subd) { >+ read_unlock(&d_compare_lock); >+ goto out; >+ } >+ >+ if (subd->d_op && subd->d_op->d_compare) >+ rc = subd->d_op->d_compare(subd, name1, name2); >+ else { /* based on fs/dcache.c:d_lookup */ >+ if (name1->len == name2->len) >+ rc = memcmp(name1->name, name2->name, name1->len); >+ } >+ read_unlock(&d_compare_lock); >+ >+out: >+ LEAVE(sb, "dentry=%s rc=%d", dentry->d_name.name, rc); >+ return rc; >+} >+ >+static inline void >+handle_subdent(struct dentry *dentry) >+{ >+ struct super_block *sb = dentry->d_sb; >+ struct vfsmount *mnt; >+ struct supermount_dentry_info *sdi = supermount_d(dentry); >+ struct dentry *subd = 0; >+ >+ mnt = subfs_prevent_umount(sb); >+ if (!mnt) >+ goto out; >+ >+ subfs_lock(sb); >+ if (sdi) { >+ subd = sdi->dentry; >+ list_del_init(&sdi->list); >+ sdi->dentry = 0; >+ } >+ subfs_unlock(sb); >+ >+ if (subd) >+ dput(subd); >+ >+ subfs_allow_umount(sb, mnt); >+out: >+ return; >+} >+ >+/* >+ * in case of active dentry we must be sure subfs dentry is released >+ * before subfs inode to correctly maintain write state >+ */ >+static void >+supermount_d_iput(struct dentry *dentry, struct inode *inode) >+{ >+ struct super_block *sb = dentry->d_sb; >+ >+ ENTER(sb, "dentry=%s inode=%p", dentry->d_name.name, inode); >+ >+ handle_subdent(dentry); >+ iput(inode); >+ >+ LEAVE(sb, "dentry=%s", dentry->d_name.name); >+} >+ >+ >+/* >+ * this duplicated code is due to the lack of common "destroy" method >+ * for both negative and positive dentries >+ */ >+static void >+supermount_d_release(struct dentry *dentry) >+{ >+ struct super_block *sb = dentry->d_sb; >+ struct supermount_dentry_info *sdi = supermount_d(dentry); >+ >+ ENTER(sb, "dentry=%s", dentry->d_name.name); >+ >+ handle_subdent(dentry); >+ kfree(sdi); >+ >+ LEAVE(sb, "dentry=%s", dentry->d_name.name); >+} >+ >+struct dentry_operations supermount_dops = { >+ .d_revalidate = supermount_d_revalidate, >+ .d_hash = supermount_d_hash, >+ .d_compare = supermount_d_compare, >+ .d_iput = supermount_d_iput, >+ .d_release = supermount_d_release, >+}; >diff -urN linux-2.6.25.4.orig/fs/supermount/file.c linux-2.6.25.4/fs/supermount/file.c >--- linux-2.6.25.4.orig/fs/supermount/file.c 1970-01-01 03:00:00 +0300 >+++ linux-2.6.25.4/fs/supermount/file.c 2008-05-20 01:27:53 +0300 >@@ -0,0 +1,690 @@ >+/* >+ * linux/fs/supermount/file.c >+ * >+ * Original version: >+ * Copyright (C) 1995, 1997 >+ * Stephen Tweedie (sct@dcs.ed.ac.uk) >+ * >+ * from >+ * >+ * linux/fs/minix/dir.c >+ * Copyright (C) 1991, 1992 Linus Torvalds >+ * >+ * and >+ * >+ * linux/fs/ext2/dir.c >+ * Copyright (C) 1992, 1993, 1994, 1995 Remy Card >+ * >+ * Rewriten for kernel 2.2 & 2.4. (C) 1999, 2000 Alexis Mikhailov >+ * (alexis@abc.cap.ru) >+ * Rewriten for kernel 2.4. (C) 2001 MandrakeSoft Inc. >+ * Juan Quintela (quintela@mandrakesoft.com) >+ * Rewritten for kernel 2.4.21 (C) 2003 Andrey Borzenkov >+ * (arvidjaar@mail.ru) >+ * Rewritten for kernel 2.5.70 (C) 2003 Andrey Borzenkov >+ * (arvidjaar@mail.ru) >+ * >+ * $Id: file.c,v 1.13.4.4 2003/07/13 14:52:43 bor Exp $ >+ */ >+ >+#define S_DBG_TRACE_CURRENT S_DBG_TRACE_FILE >+#include "supermount.h" >+ >+#define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM) >+ >+static inline int >+init_file_info(struct file *file, unsigned int fake) >+{ >+ struct super_block *sb = file->f_dentry->d_sb; >+ struct supermount_file_info *sfi; >+ int rc; >+ >+ ENTER(sb, "file=%p fake=%d", file, fake); >+ >+ rc = 1; >+ sfi = kmalloc(sizeof(*sfi), GFP_KERNEL); >+ if (!sfi) >+ goto out; >+ >+ memset(sfi, 0, sizeof(*sfi)); >+ >+ INIT_LIST_HEAD(&sfi->list); >+ sfi->host = file; >+ sfi->owner = current->pid; >+ sfi->vm_ops = 0; >+ sfi->file = 0; >+ sfi->fake = fake; >+ >+ file->f_supermount = sfi; >+ >+ rc = 0; >+out: >+ LEAVE(sb, "file=%p fake=%d rc=%d", file, fake, rc); >+ >+ return rc; >+} >+ >+static inline void >+attach_subfs_file(struct file *file, struct file *subfile) >+{ >+ struct super_block *sb = file->f_dentry->d_sb; >+ struct supermount_sb_info *sbi = supermount_sbi(sb); >+ struct supermount_file_info *sfi; >+ >+ ENTER(sb, "file=%p subfile=%p", file, subfile); >+ >+ sfi = supermount_f(file); >+ sfi->file = subfile; >+ list_add(&sfi->list, &sbi->s_files); >+ >+ LEAVE(sb, "file=%p subfile=%p", file, subfile); >+} >+ >+static inline int >+prepare_file(struct file *file, struct file *subfile) >+{ >+ struct super_block *sb = file->f_dentry->d_sb; >+ int rc; >+ >+ ENTER(sb, "file=%p subfile=%p", file, subfile); >+ >+ subfs_lock(sb); >+ >+ rc = -ENOMEDIUM; >+ if (!subfs_is_mounted(sb)) >+ goto out; >+ >+ rc = -ESTALE; >+ if (is_dentry_obsolete(file->f_dentry)) >+ goto out; >+ >+ rc = -ENOMEM; >+ if (init_file_info(file, 0)) >+ goto out; >+ >+ attach_subfs_file(file, subfile); >+ rc = 0; >+ >+out: >+ subfs_unlock(sb); >+ >+ LEAVE(sb, "file=%p subfile=%p", file, subfile); >+ >+ return rc; >+} >+ >+struct file * >+get_subfs_file(struct file *file) >+{ >+ struct super_block *sb = file->f_dentry->d_sb; >+ struct supermount_file_info *sfi = supermount_f(file); >+ struct file *err; >+ >+ ENTER(sb, "file=%p", file); >+ >+ subfs_lock(sb); >+ >+ err = ERR_PTR(-ENOMEDIUM); >+ if (!subfs_is_mounted(sb)) >+ goto out; >+ >+ err = ERR_PTR(-ESTALE); >+ if (is_file_fake(file) || is_file_obsolete(file)) >+ goto out; >+ >+ err = sfi->file; >+ SUPERMOUNT_BUG_LOCKED_ON(sb, !err->f_dentry); >+ SUPERMOUNT_BUG_LOCKED_ON(sb, !err->f_dentry->d_inode); >+ get_file(err); >+ >+out: >+ subfs_unlock(sb); >+ >+ LEAVE(sb, "file=%p subfile=%p", file, err); >+ >+ return err; >+} >+ >+static loff_t >+supermount_llseek(struct file *file, loff_t offset, int origin) >+{ >+ struct super_block *sb = file->f_dentry->d_sb; >+ struct file *subfile; >+ loff_t rc; >+ >+ ENTER(sb, "file=%p offset=%lld origin=%d", file, offset, origin); >+ >+ rc = -ESTALE; >+ if (subfs_check_disk_change(sb)) >+ goto out; >+ >+ subfile = get_subfs_file(file); >+ rc = PTR_ERR(subfile); >+ if (IS_ERR(subfile)) >+ goto out; >+ >+ if (subfile->f_op && subfile->f_op->llseek) >+ rc = subfile->f_op->llseek(subfile, offset, origin); >+ else >+ rc = default_llseek(subfile, offset, origin); >+ file->f_pos = subfile->f_pos; >+ file->f_version = subfile->f_version; >+ >+ fput(subfile); >+out: >+ LEAVE(sb, "file=%p rc=%lld", file, rc); >+ >+ return rc; >+} >+ >+static ssize_t >+supermount_read(struct file *file, char *buf, size_t count, loff_t * ppos) >+{ >+ struct super_block *sb = file->f_dentry->d_sb; >+ struct inode *inode = file->f_dentry->d_inode; >+ struct file *subfile; >+ int write_on = NEED_WRITE_ATIME(inode); >+ int rc; >+ >+ ENTER(sb, "file=%p", file); >+ >+ rc = -ESTALE; >+ if (subfs_check_disk_change(sb)) >+ goto out; >+ >+ subfile = get_subfs_file(file); >+ rc = PTR_ERR(subfile); >+ if (IS_ERR(subfile)) >+ goto out; >+ >+ rc = -EINVAL; >+ if (!subfile->f_op || !subfile->f_op->read) >+ goto put_subfile; >+ >+ rc = subfs_get_access(inode, write_on); >+ if (rc) >+ goto put_subfile; >+ >+ rc = subfile->f_op->read(subfile, buf, count, ppos); >+ subfs_put_access(inode, write_on); >+ if (rc < 0) >+ goto put_subfile; >+ >+ inode->i_atime = subfile->f_dentry->d_inode->i_atime; >+ >+ if (rc > 0) >+ file->f_pos = subfile->f_pos = *ppos; >+ >+put_subfile: >+ fput(subfile); >+out: >+ LEAVE(sb, "file=%p rc=%d", file, rc); >+ >+ return rc; >+} >+ >+static ssize_t >+supermount_write(struct file *file, const char *buf, >+ size_t count, loff_t * ppos) >+{ >+ struct super_block *sb = file->f_dentry->d_sb; >+ struct file *subfile; >+ int rc; >+ >+ ENTER(sb, "file=%p", file); >+ >+ rc = -ESTALE; >+ if (subfs_check_disk_change(sb)) >+ goto out; >+ >+ subfile = get_subfs_file(file); >+ rc = PTR_ERR(subfile); >+ if (IS_ERR(subfile)) >+ goto out; >+ >+ rc = 0; >+ if (subfile->f_op && subfile->f_op->write) >+ rc = subfile->f_op->write(subfile, buf, count, ppos); >+ if (rc > 0) { >+ struct inode *subinode = subfile->f_dentry->d_inode; >+ >+ file->f_pos = subfile->f_pos = *ppos; >+ file->f_mode = subfile->f_mode; >+ file->f_dentry->d_inode->i_size = subinode->i_size; >+ file->f_dentry->d_inode->i_blocks = subinode->i_blocks; >+ file->f_dentry->d_inode->i_mode = subinode->i_mode; >+ file->f_dentry->d_inode->i_ctime = subinode->i_ctime; >+ file->f_dentry->d_inode->i_mtime = subinode->i_mtime; >+ } >+ >+ fput(subfile); >+out: >+ LEAVE(sb, "file=%p rc=%d", file, rc); >+ >+ return rc; >+} >+ >+int >+supermount_readdir(struct file *file, void *buf, filldir_t fill_fn) >+{ >+ struct super_block *sb = file->f_dentry->d_sb; >+ struct inode *inode = file->f_dentry->d_inode; >+ struct file *subfile; >+ int write_on = NEED_WRITE_ATIME(inode); >+ int fake_readdir = 1; >+ int rc; >+ >+ ENTER(sb, "file=%p", file); >+ >+ rc = -ESTALE; >+ if (subfs_check_disk_change(sb)) >+ goto out; >+ >+ subfile = get_subfs_file(file); >+ rc = PTR_ERR(subfile); >+ if (IS_ERR(subfile)) >+ goto out; >+ >+ rc = -ENOTDIR; >+ if (!subfile->f_op || !subfile->f_op->readdir) >+ goto put_subfile; >+ >+ rc = subfs_get_access(inode, write_on); >+ if (rc) >+ goto put_subfile; >+ >+ /* FIXME should it go before get_access? */ >+ fake_readdir = 0; >+ rc = subfile->f_op->readdir(subfile, buf, fill_fn); >+ subfs_put_access(inode, write_on); >+ if (rc) >+ goto put_subfile; >+ >+ inode->i_atime = subfile->f_dentry->d_inode->i_atime; >+ file->f_pos = subfile->f_pos; >+ >+put_subfile: >+ fput(subfile); >+out: >+ if (fake_readdir && is_file_fake(file)) { >+ /* cf. supermount_open */ >+ rc = 0; >+ } >+ LEAVE(sb, "file=%p rc=%d fpos=%lld", file, rc, file->f_pos); >+ >+ return rc; >+} >+ >+static unsigned int >+supermount_poll(struct file *file, struct poll_table_struct *table) >+{ >+ struct super_block *sb = file->f_dentry->d_sb; >+ struct file *subfile; >+ int rc; >+ >+ ENTER(sb, "file=%p", file); >+ >+ rc = -ESTALE; >+ if (subfs_check_disk_change(sb)) >+ goto out; >+ >+ subfile = get_subfs_file(file); >+ rc = PTR_ERR(subfile); >+ if (IS_ERR(subfile)) >+ goto out; >+ >+ rc = DEFAULT_POLLMASK; >+ if (subfile->f_op && subfile->f_op->poll) >+ rc = subfile->f_op->poll(subfile, table); >+ >+ fput(subfile); >+out: >+ LEAVE(sb, "file=%p rc=%d", file, rc); >+ >+ return rc; >+} >+ >+static int >+supermount_ioctl(struct inode *inode, struct file *file, >+ unsigned int cmd, unsigned long arg) >+{ >+ struct super_block *sb = file->f_dentry->d_sb; >+ struct file *subfile; >+ struct inode *subinode; >+ int rc; >+ >+ ENTER(sb, "file=%p cmd=%u arg=%lu", file, cmd, arg); >+ >+ rc = -ESTALE; >+ if (subfs_check_disk_change(sb)) >+ goto out; >+ >+ subfile = get_subfs_file(file); >+ rc = PTR_ERR(subfile); >+ if (IS_ERR(subfile)) >+ goto out; >+ >+ rc = -ENOTTY; >+ subinode = subfile->f_dentry->d_inode; >+ if (subfile->f_op && subfile->f_op->ioctl) >+ rc = subfile->f_op->ioctl(subinode, subfile, cmd, arg); >+ >+ /* flags may have been changed by ioctl */ >+ if (!rc) >+ set_inode_flags(file->f_dentry->d_inode, subinode); >+ >+ fput(subfile); >+out: >+ LEAVE(sb, "file=%p rc=%d", file, rc); >+ >+ return rc; >+} >+ >+int >+supermount_open(struct inode *inode, struct file *file) >+{ >+ struct super_block *sb = inode->i_sb; >+ struct dentry *subdent; >+ struct file *subfile = 0; >+ struct vfsmount *submnt; >+ int write_on = file->f_mode & FMODE_WRITE; >+ int fake_open = 1; >+ int rc; >+ >+ ENTER(sb, "inode=%p file=%p", inode, file); >+ >+ rc = -ESTALE; >+ if (subfs_check_disk_change(sb)) >+ goto out; >+ >+ submnt = subfs_get_mnt(sb); >+ if (!submnt) >+ goto out; >+ >+ subdent = get_subfs_dentry(file->f_dentry); >+ rc = PTR_ERR(subdent); >+ if (IS_ERR(subdent)) >+ goto put_submnt; >+ >+ rc = subfs_get_access(inode, write_on); >+ if (rc) >+ goto put_subdent; >+ >+ /* >+ * the following is used to simplify error processing. dentry_open >+ * automatically does mntput and dput in error case, this may result >+ * in subfs being destroyed >+ * We just make sure we need to do mntput exactly once here; >+ * additionally it guards against accidental remounting of subfs >+ * until we has cleaned up >+ */ >+ submnt = mntget(submnt); >+ subdent = dget(subdent); >+ >+ subfile = dentry_open(subdent, submnt, file->f_flags); >+ rc = PTR_ERR(subfile); >+ if (IS_ERR(subfile)) >+ goto put_access; >+ /* >+ * no need to do extra mntput and dput, it is done automatically in >+ * dentry_open on error >+ */ >+ >+ rc = prepare_file(file, subfile); >+ if (rc) >+ goto put_subfile; >+ >+ subfile->f_mode = file->f_mode; >+ /* >+ * this is needed for mmap to work. In current model vm_area >+ * is associated with superfile; we never explicitly call >+ * any vm method with subfile as pointer. But many drivers >+ * attach private structures to this field and mmap of special >+ * files on supermount fs won't work without it >+ */ >+ file->private_data = subfile->private_data; >+ /* >+ * we have real subfile now, do not fake anything >+ */ >+ fake_open = 0; >+ >+ /* >+ * Now get rid of extra mntget and dget >+ */ >+ goto put_subdent; >+ >+ /* >+ * error cleanup >+ */ >+ >+put_subfile: >+ fput(subfile); >+ subfile = 0; >+put_access: >+ subfs_put_access(inode, write_on); >+put_subdent: >+ dput(subdent); >+put_submnt: >+ mntput(submnt); >+out: >+ if (fake_open && inode == sb->s_root->d_inode) { >+ /* >+ * always appear to succeed for root open. It allows active >+ * monitoring of mountpoint using FAM/dnotify and also is less >+ * surprising for other programs >+ */ >+ rc = init_file_info(file, 1); >+ if (rc) >+ rc = -ENOMEM; >+ } >+ LEAVE(sb, "inode=%p file=%p rc=%d subfile=0x%p", inode, file, rc, subfile); >+ >+ return rc; >+} >+ >+static int >+supermount_flush(struct file *file, fl_owner_t id) >+{ >+ struct super_block *sb = file->f_dentry->d_sb; >+ struct file *subfile; >+ int fake_flush = 1; >+ int rc; >+ >+ ENTER(sb, "file=%p", file); >+ >+ rc = -ESTALE; >+ if (subfs_check_disk_change(sb)) >+ goto out; >+ >+ subfile = get_subfs_file(file); >+ rc = PTR_ERR(subfile); >+ if (IS_ERR(subfile)) >+ goto out; >+ >+ rc = 0; >+ fake_flush = 0; >+ if (subfile->f_op && subfile->f_op->flush) >+ rc = subfile->f_op->flush(subfile,id); >+ >+ fput(subfile); >+out: >+ if (fake_flush && is_file_fake(file)) { >+ /* cf. supermount_open */ >+ rc = 0; >+ } >+ LEAVE(sb, "file=%p rc=%d fake=%d", file, rc, fake_flush); >+ >+ return rc; >+} >+ >+/* >+ * if subfile is NULL it has already been released in supermount_clean_files >+ * together with adjusting open/write counters. Else we do it here. >+ * >+ * The reason is, it may be called long after media has been changed >+ * and we definitely do not want this function to mess up the >+ * new subfs state. >+ */ >+static int >+supermount_release(struct inode *inode, struct file *file) >+{ >+ struct file *subfile = 0; >+ struct super_block *sb = inode->i_sb; >+ struct supermount_file_info *sfi = file->f_supermount; >+ >+ ENTER(sb, "inode=%p file=%p", inode, file); >+ >+ subfs_lock(sb); >+ /* >+ * FIXME >+ * this sucks. But there does not seem to be any way >+ * to distinguish between ENOMEM on _open (legitimate >+ * case) and anything else (plain bug) >+ */ >+ if (sfi) { >+ list_del(&sfi->list); >+ subfile = sfi->file; >+ sfi->file = 0; >+ } else >+ supermount_warning(sb, "no supermount file info attached"); >+ subfs_unlock(sb); >+ >+ if (subfile) { >+ int bug = atomic_read(&subfile->f_count) != 1; >+ fput(subfile); >+ subfs_put_access(inode, file->f_mode & FMODE_WRITE); >+ SUPERMOUNT_BUG_ON(bug); >+ } >+ >+ if (sfi) >+ kfree(sfi); >+ >+ LEAVE(sb, "inode=%p file=%p", inode, file); >+ >+ return 0; >+ >+} >+ >+static int >+supermount_fsync(struct file *file, struct dentry *dentry, int datasync) >+{ >+ struct super_block *sb = file->f_dentry->d_sb; >+ struct file *subfile; >+ int rc; >+ >+ ENTER(sb, "file=%p dentry=%s sync=%d", file, dentry->d_name.name, datasync); >+ >+ rc = -ESTALE; >+ if (subfs_check_disk_change(sb)) >+ goto out; >+ >+ subfile = get_subfs_file(file); >+ rc = PTR_ERR(subfile); >+ if (IS_ERR(subfile)) >+ goto out; >+ >+ rc = -EINVAL; >+ if (subfile->f_op && subfile->f_op->fsync) >+ rc = subfile->f_op->fsync(subfile, subfile->f_dentry, datasync); >+ >+ fput(subfile); >+out: >+ ENTER(sb, "file=%p dentry=%s rc=%d", file, dentry->d_name.name, rc); >+ >+ return rc; >+} >+ >+static int >+supermount_fasync(int fd, struct file *file, int on) >+{ >+ struct super_block *sb = file->f_dentry->d_sb; >+ struct file *subfile; >+ int rc; >+ >+ ENTER(sb, "fd=%d file=%p on=%d", fd, file, on); >+ >+ rc = -ESTALE; >+ if (subfs_check_disk_change(sb)) >+ goto out; >+ >+ subfile = get_subfs_file(file); >+ rc = PTR_ERR(subfile); >+ if (IS_ERR(subfile)) >+ goto out; >+ >+ rc = -EINVAL; >+ if (subfile->f_op && subfile->f_op->fasync) >+ rc = subfile->f_op->fasync(fd, subfile, on); >+ >+ fput(subfile); >+out: >+ LEAVE(sb, "fd=%d file=%p rc=%d", fd, file, rc); >+ >+ return rc; >+} >+ >+static int >+supermount_lock(struct file *file, int cmd, struct file_lock *fl) >+{ >+ struct super_block *sb = file->f_dentry->d_sb; >+ struct file *subfile; >+ struct file_lock *fl_tmp; >+ int rc; >+ >+ ENTER(sb, "file=%p cmd=%d", file, cmd); >+ >+ rc = -ESTALE; >+ if (subfs_check_disk_change(sb)) >+ goto out; >+ >+ subfile = get_subfs_file(file); >+ rc = PTR_ERR(subfile); >+ if (IS_ERR(subfile)) >+ goto out; >+ >+ rc = 0; >+ if (subfile->f_op && subfile->f_op->lock) >+ rc = subfile->f_op->lock(subfile, cmd, fl); >+ else if (cmd == F_GETLK) >+ posix_test_lock(file, fl); >+ >+ fput(subfile); >+out: >+ LEAVE(sb, "file=%p rc=%d", file, rc); >+ >+ return rc; >+} >+ >+/* Fixme: >+ * readv: easy, export churnk from vfs >+ * writev: easy, export churnk from vfs >+ * sendpage: only used for networking, not needed >+ * get_unmmapped_area: only used for devices, not needed >+ */ >+ >+struct file_operations supermount_dir_operations = { >+ .llseek = supermount_llseek, >+ .read = supermount_read, >+ .readdir = supermount_readdir, >+ .ioctl = supermount_ioctl, >+ .open = supermount_open, >+ .flush = supermount_flush, >+ .release = supermount_release, >+ .fsync = supermount_fsync, >+ .fasync = supermount_fasync, >+}; >+ >+struct file_operations supermount_file_operations = { >+ .llseek = supermount_llseek, >+ .read = supermount_read, >+ .write = supermount_write, >+ .poll = supermount_poll, >+ .ioctl = supermount_ioctl, >+ .mmap = supermount_file_mmap, /* from filemap.c */ >+ .open = supermount_open, >+ .flush = supermount_flush, >+ .release = supermount_release, >+ .fsync = supermount_fsync, >+ .fasync = supermount_fasync, >+ .lock = supermount_lock, >+}; >diff -urN linux-2.6.25.4.orig/fs/supermount/filemap.c linux-2.6.25.4/fs/supermount/filemap.c >--- linux-2.6.25.4.orig/fs/supermount/filemap.c 1970-01-01 03:00:00 +0300 >+++ linux-2.6.25.4/fs/supermount/filemap.c 2008-05-20 01:27:53 +0300 >@@ -0,0 +1,182 @@ >+/* >+ * linux/fs/supermount/filemap.c >+ * >+ * Initial version for kernel 2.4.21 (C) 2003 Andrey Borzenkov >+ * (arvidjaar@mail.ru) >+ * >+ * $Id: filemap.c,v 1.4.4.2 2004/01/14 20:28:01 bor Exp $ >+ */ >+ >+#define S_DBG_TRACE_CURRENT S_DBG_TRACE_FILEMAP >+#include "supermount.h" >+ >+/* >+ * Some rationale and justification for this file >+ * >+ * We play dirty tricks with mm management for mmaped files on supermount. >+ * Address space points to subinode but vm area is associated with superfile. >+ * When media change is detected, subinode together with all associated >+ * pages goes away as well (at least, I hope so ...) Now we must prevent >+ * any attempt to access no more existing address space. To do so we save >+ * original vm_ops in private field and replace them with vm_ops in this file. >+ * They check if file is stale, if yes, they try to return sensible error. >+ * There is some doubt about possibility to block here ... OTOH any write >+ * lock on subfs is hold in context where no mm lock is expected. >+ */ >+static void >+supermount_vm_open(struct vm_area_struct *area) >+{ >+ struct file *file = area->vm_file, *subfile; >+ struct super_block *sb = file->f_dentry->d_sb; >+ struct supermount_file_info *sfi; >+ >+ ENTER(sb, "vm=%p", area); >+ >+ if (subfs_check_disk_change(sb)) >+ goto out; >+ >+ subfile = get_subfs_file(file); >+ if (IS_ERR(subfile)) >+ goto out; >+ >+ sfi = supermount_f(file); >+ if (sfi->vm_ops && sfi->vm_ops->open) >+ sfi->vm_ops->open(area); >+ >+ fput(subfile); >+out: >+ LEAVE(sb, "vm=%p", area); >+ >+ return; >+} >+ >+static void >+supermount_vm_close(struct vm_area_struct *area) >+{ >+ struct file *file = area->vm_file, *subfile; >+ struct super_block *sb = file->f_dentry->d_sb; >+ struct supermount_file_info *sfi; >+ >+ ENTER(sb, "vm=%p", area); >+ >+ if (subfs_check_disk_change(sb)) >+ goto out; >+ >+ subfile = get_subfs_file(file); >+ if (IS_ERR(subfile)) >+ goto out; >+ >+ sfi = supermount_f(file); >+ if (sfi->vm_ops && sfi->vm_ops->close) >+ sfi->vm_ops->close(area); >+ >+ fput(subfile); >+out: >+ LEAVE(sb, "vm=%p", area); >+ >+ return; >+} >+ >+static struct page * >+supermount_vm_nopage(struct vm_area_struct *area, unsigned long address, int *type) >+{ >+ struct file *file = area->vm_file, *subfile; >+ struct super_block *sb = file->f_dentry->d_sb; >+ struct supermount_file_info *sfi; >+ struct page *page = 0; >+ >+ ENTER(sb, "vm=%p addr=%lx", area, address); >+ >+ /* >+ * this is called with mm semaphore down read and pagetable >+ * spinlock released. So it _appears_ safe to sleep ... >+ */ >+ if (subfs_check_disk_change(sb)) >+ goto out; >+ >+ subfile = get_subfs_file(file); >+ if (IS_ERR(subfile)) >+ goto out; >+ >+ sfi = supermount_f(file); >+ page = sfi->vm_ops->nopage(area, address, type); >+ >+ fput(subfile); >+out: >+ LEAVE(sb, "vm=%p page=%p", area, page); >+ >+ return page; >+} >+ >+int >+supermount_file_mmap(struct file *file, struct vm_area_struct *vma) >+{ >+ >+ struct super_block *sb = file->f_dentry->d_sb; >+ struct inode *inode = file->f_dentry->d_inode; >+ struct supermount_file_info *sfi; >+ struct file *subfile; >+ int write_on = NEED_WRITE_ATIME(inode); >+ int rc; >+ >+ ENTER(sb, "file=%p vm=%p", file, vma); >+ >+ rc = -ESTALE; >+ if (subfs_check_disk_change(sb)) >+ goto out; >+ >+ subfile = get_subfs_file(file); >+ rc = PTR_ERR(subfile); >+ if (IS_ERR(subfile)) >+ goto out; >+ >+ sfi = supermount_f(file); >+ >+ rc = -ENODEV; >+ if (!subfile->f_op || !subfile->f_op->mmap) >+ goto put_subfile; >+ >+ rc = subfs_get_access(inode, write_on); >+ if (rc) >+ goto put_subfile; >+ >+ rc = subfile->f_op->mmap(subfile, vma); >+ subfs_put_access(inode, write_on); >+ if (rc) >+ goto put_subfile; >+ /* >+ * we cannot deal with anonymous mapping >+ */ >+ if (!vma->vm_ops || !vma->vm_ops->nopage) { >+ rc = -ENOSYS; >+ goto put_subfile; >+ } >+ >+ /* >+ * now do the nasty trick >+ */ >+ sfi->vm_ops = vma->vm_ops; >+ vma->vm_ops = &supermount_vm_ops; >+ >+put_subfile: >+ fput(subfile); >+out: >+ LEAVE(sb, "file=%p vm=%p rc=%d", file, vma, rc); >+ >+ return rc; >+} >+ >+/* >+ * ->populate: to properly implement it it should call do_no_page if >+ * subfs does not provide its own ->populate. Unfortunately >+ * populate gets different parameters. Translating them into >+ * what ->nopage expects just does not worth it - currently >+ * supermount is slow enough to bother about this imaginary >+ * speed up even if it was possible (which probably is not >+ * due to how locking is done; see mm/memory.c). >+ */ >+struct vm_operations_struct supermount_vm_ops = { >+ .open = supermount_vm_open, >+ .close = supermount_vm_close, >+ .nopage = supermount_vm_nopage, >+}; >diff -urN linux-2.6.25.4.orig/fs/supermount/init.c linux-2.6.25.4/fs/supermount/init.c >--- linux-2.6.25.4.orig/fs/supermount/init.c 1970-01-01 03:00:00 +0300 >+++ linux-2.6.25.4/fs/supermount/init.c 2008-05-20 01:27:53 +0300 >@@ -0,0 +1,48 @@ >+/* >+ * linux/fs/supermount/init.c >+ * >+ * (C) Copyright 2001-2002 Juan Quintela <quintela@mandrakesoft.com> >+ * Released unde GPL v2. >+ * Rewritten for kernel 2.4.21 (C) 2003 Andrey Borzenkov >+ * (arvidjaar@mail.ru) >+ * Rewritten for kernel 2.5.70 (C) 2003 Andrey Borzenkov >+ * (arvidjaar@mail.ru) >+ * >+ * $Id: init.c,v 1.6.4.4 2003/10/26 16:21:25 bor Exp $ >+ */ >+ >+#include "supermount.h" >+ >+struct file_system_type supermount_fs_type = { >+ .owner = THIS_MODULE, >+ .name = "supermount", >+ .get_sb = supermount_get_sb, >+ .kill_sb = kill_anon_super, >+ .fs_flags = FS_NO_SUBMNT, >+}; >+ >+static int __init >+init_supermount_fs(void) >+{ >+ int rc = register_filesystem(&supermount_fs_type); >+ >+ if (!rc) { >+ printk(KERN_INFO "Supermount version %s for kernel 2.6\n", SUPERMOUNT_VERSION); >+ supermount_proc_register(); >+ } >+ >+ return rc; >+} >+ >+static void __exit >+exit_supermount_fs(void) >+{ >+ supermount_proc_unregister(); >+ unregister_filesystem(&supermount_fs_type); >+} >+ >+MODULE_AUTHOR("Stephen Tweedie, Alexis Mikhailov, Juan Quintela, Andrey Borzenkov and others"); >+MODULE_DESCRIPTION("Transparent removable media support"); >+MODULE_LICENSE("GPL"); >+module_init(init_supermount_fs); >+module_exit(exit_supermount_fs); >diff -urN linux-2.6.25.4.orig/fs/supermount/Kconfig linux-2.6.25.4/fs/supermount/Kconfig >--- linux-2.6.25.4.orig/fs/supermount/Kconfig 1970-01-01 03:00:00 +0300 >+++ linux-2.6.25.4/fs/supermount/Kconfig 2008-05-20 01:27:53 +0300 >@@ -0,0 +1,26 @@ >+config SUPERMOUNT >+ tristate "Supermount removable media support" >+ help >+ Supermount gives you the ability to access CD-ROMs and Floppies >+ without mounting/unmounting them every time you want to access >+ a different disk/floppy. Just eject the media, insert a new one >+ and you are able to access it. >+ >+ Read Documentation/filesystems/supermount.txt for more information. >+ >+ If you want to compile the Supermount support as a module ( = code >+ which can be inserted in and removed from the running kernel whenever >+ you want), say M here and read Documentation/modules.txt. The module >+ will be called supermount.o. >+ >+ If unsure, say N. >+ >+config SUPERMOUNT_DEBUG >+ bool "Enable supermount debug code" >+ depends on SUPERMOUNT >+ help >+ If you set this to Y, additional debug code will be compiled in. >+ Debug output is controlled with debug=N mount option. Possible >+ values are listed in Documentation/filesystems/supermount.txt. >+ >+ If unsure, say Y. >diff -urN linux-2.6.25.4.orig/fs/supermount/Makefile linux-2.6.25.4/fs/supermount/Makefile >--- linux-2.6.25.4.orig/fs/supermount/Makefile 1970-01-01 03:00:00 +0300 >+++ linux-2.6.25.4/fs/supermount/Makefile 2008-05-20 01:27:53 +0300 >@@ -0,0 +1,15 @@ >+# >+# Makefile for the linux supermounting routines. >+# >+ >+supermount-objs := dentry.o \ >+ file.o \ >+ filemap.o \ >+ init.o \ >+ mediactl.o \ >+ namei.o \ >+ proc.o \ >+ subfs.o \ >+ super.o >+ >+obj-$(CONFIG_SUPERMOUNT) += supermount.o >diff -urN linux-2.6.25.4.orig/fs/supermount/mediactl.c linux-2.6.25.4/fs/supermount/mediactl.c >--- linux-2.6.25.4.orig/fs/supermount/mediactl.c 1970-01-01 03:00:00 +0300 >+++ linux-2.6.25.4/fs/supermount/mediactl.c 2008-05-20 01:27:53 +0300 >@@ -0,0 +1,69 @@ >+/* >+ * linux/fs/supermount/mediactl.c >+ * >+ * Original version: >+ * Copyright (C) 1995, 1997 >+ * Stephen Tweedie (sct@dcs.ed.ac.uk) >+ * >+ * Rewriten for kernel 2.2, 2.4. (C) 1999, 2000 Alexis Mikhailov >+ * (alexis@abc.cap.ru) >+ * Rewritten for kernel 2.4.21 (C) 2003 Andrey Borzenkov >+ * (arvidjaar@mail.ru) >+ * Rewritten for kernel 2.5.70 (C) 2003 Andrey Borzenkov >+ * (arvidjaar@mail.ru) >+ * >+ * $Id: mediactl.c,v 1.5.2.5 2003/07/13 19:15:20 bor Exp $ >+ */ >+ >+#define S_DBG_TRACE_CURRENT S_DBG_TRACE_MEDIACTL >+#include "supermount.h" >+ >+/* >+ * Try to lock the drive door. This is not guaranteed to work on all >+ * hardware, but we try any tricks we know of anyway. >+ */ >+void >+supermount_mediactl(struct super_block *sb, int operation, int opt) >+{ >+ struct block_device *bdev; >+ struct super_block *subsb; >+ struct block_device_operations *fops = 0; >+ >+ if (!subfs_is_mounted(sb)) >+ return; >+ >+ subsb = subfs_sb(sb); >+ bdev = subsb->s_bdev; >+ if (!bdev->bd_disk) >+ return; >+ >+ /* FIXME is it enough to use bd_sem here? */ >+ switch (operation) { >+ case SUPERMOUNT_INC_COUNT: >+ lock_kernel(); >+ SUPERMOUNT_BUG_ON(bdev->bd_disk->scount < 0); >+ bdev->bd_disk->scount++; >+ unlock_kernel(); >+ return; >+ case SUPERMOUNT_DEC_COUNT: >+ lock_kernel(); >+ SUPERMOUNT_BUG_ON(bdev->bd_disk->scount <= 0); >+ bdev->bd_disk->scount--; >+ unlock_kernel(); >+ return; >+ } >+ >+ fops = bdev->bd_disk->fops; >+ if (!fops || !fops->mediactl) >+ return; >+ /* >+ * tray is (un-)locked in open (BKL for bdev), release (BKL for bdev) >+ * and ioctl (BKL). We are in good company. >+ * This must be changed if block devices ever stop using BKL >+ * for open/release. Unfortunately, using just bdev->bd_sem is not >+ * enough due to ioctl. >+ */ >+ lock_kernel(); >+ fops->mediactl(bdev, operation, opt); >+ unlock_kernel(); >+} >diff -urN linux-2.6.25.4.orig/fs/supermount/namei.c linux-2.6.25.4/fs/supermount/namei.c >--- linux-2.6.25.4.orig/fs/supermount/namei.c 1970-01-01 03:00:00 +0300 >+++ linux-2.6.25.4/fs/supermount/namei.c 2008-05-20 01:27:53 +0300 >@@ -0,0 +1,1145 @@ >+/* >+ * linux/fs/supermount/namei.c >+ * >+ * Original version: >+ * Copyright (C) 1995 >+ * Stephen Tweedie (sct@dcs.ed.ac.uk) >+ * >+ * from >+ * >+ * linux/fs/minix/namei.c >+ * Copyright (C) 1991, 1992 Linus Torvalds >+ * >+ * and >+ * >+ * linux/fs/ext2/namei.c >+ * Copyright (C) 1992, 1993, 1994, 1995 Remy Card >+ * >+ * Rewriten for kernel 2.2 & 2.4. (C) 1999, 2000 Alexis Mikhailov >+ * (alexis@abc.cap.ru) >+ * Rewritten for kernel 2.4.21 (C) 2003 Andrey Borzenkov >+ * (arvidjaar@mail.ru) >+ * Rewritten for kernel 2.5.70 (C) 2003 Andrey Borzenkov >+ * (arvidjaar@mail.ru) >+ * >+ * $Id: namei.c,v 1.21.2.11 2004/01/04 15:05:03 bor Exp $ >+ */ >+ >+#define S_DBG_TRACE_CURRENT S_DBG_TRACE_NAMEI >+#include "supermount.h" >+ >+/** >+ * Attach super dentry to sub dentry >+ * >+ * @dentry: super dentry that is being created >+ * @subd: subfs dentry that just has been found >+ * >+ * It checks whether subfs is still valid using @dentry->d_parent; >+ * new supermount_dentry_info is created and set to point to @subd. >+ * >+ * It is possible that @subd actually has different name (ntfs, vfat or >+ * in general any case-insensitive filesystems). Search @dentry->d_parent >+ * for a child matching == @subd->d_name. If found, discard @dentry and child >+ * (after some validity checks) is returned. Check that child actually points >+ * to @subd >+ * >+ * d_lookup relies on the fact that hash is properly initialized in >+ * @subd->d_name and that superfs is using the same compare method as subfs. >+ * >+ * About ref counting. @subd is dput in supermount_lookup. I.e. in case of >+ * error or if we find out it is already connected to superfs the excessive >+ * counter gets decremented. @dentry is finally dput in caller of ->lookup >+ * if ->lookup returns something != 0. >+ */ >+ >+static struct dentry * >+prepare_dentry(struct dentry *dentry, struct dentry *subd) >+{ >+ struct super_block *sb = dentry->d_sb; >+ struct dentry *rc; >+ >+ ENTER(sb, "dentry=%s subd=%s", dentry->d_name.name, subd->d_name.name); >+ >+ subfs_lock(sb); >+ >+ SUPERMOUNT_BUG_LOCKED_ON(sb, !subd); >+ SUPERMOUNT_BUG_LOCKED_ON(sb, dentry->d_fsdata); >+ >+ rc = ERR_PTR(-ENOMEDIUM); >+ if (!subfs_is_mounted(sb)) >+ goto out; >+ >+ rc = ERR_PTR(-ESTALE); >+ if (is_dentry_obsolete(dentry->d_parent)) >+ goto out; >+ >+ rc = d_lookup(dentry->d_parent, &subd->d_name); >+ if (IS_ERR(rc)) >+ goto out; >+ >+ if (rc) { >+ SUPERMOUNT_BUG_LOCKED_ON(sb, !rc->d_fsdata); >+ SUPERMOUNT_BUG_LOCKED_ON(sb, >+ ((struct supermount_dentry_info *)rc->d_fsdata)->dentry != subd); >+ } else { >+ /* >+ * this is theoretically possible. We cannot garantee full >+ * coherency between subfs and superfs cache; i.e. entry >+ * may have been left in one cache but removed from another >+ */ >+ rc = ERR_PTR(-ENOMEM); >+ if (init_dentry_info(dentry)) >+ goto out; >+ attach_subfs_dentry(dentry, subd); >+ d_add(dentry, 0); >+ rc = 0; >+ } >+ >+out: >+ subfs_unlock(sb); >+ >+ LEAVE(sb, "dentry=%p subd=%p rc=%p", dentry, subd, rc); >+ >+ return rc; >+ /* >+ * subdent is implicitly freed on return if we skip dget here >+ */ >+} >+ >+ >+/** >+ * Attach superfs inode to subfs inode >+ * >+ * @dentry: superfs dentry >+ * @subd: subfs dentry >+ * >+ * This is expected to be called only in cointext that requires >+ * negative dentry. >+ * >+ * FIXME >+ * Holding sbi->sem during iget4 creates deadlock with write_inode - >+ * write_inode sets I_LOCK abd calls supermount_write_inode at the >+ * same moment as iget4 sleeps waiting for I_LOCK to be cleared. So >+ * it first acquires inode and then checks if subfs is still valid. >+ */ >+ >+static int >+prepare_inode(struct dentry *dentry, struct dentry *subd) >+{ >+ struct super_block *sb = dentry->d_sb; >+ struct inode *inode = dentry->d_inode; >+ struct inode *subi = subd->d_inode; >+ int rc; >+ >+ ENTER(sb, "dentry=%s", dentry->d_name.name); >+ >+ SUPERMOUNT_BUG_ON(inode); >+ SUPERMOUNT_BUG_ON(!subi); >+ >+ rc = -ENOMEM; >+ inode = iget_locked(sb, subi->i_ino); >+ if (!inode) >+ goto out; >+ else if (inode->i_state & I_NEW) >+ unlock_new_inode(inode); >+ >+ rc = 0; >+ >+ subfs_lock(sb); >+ if (!subfs_is_mounted(sb)) >+ rc = -ENOMEDIUM; >+ else if (is_dentry_obsolete(dentry)) >+ rc = -ESTALE; >+ else { >+ attach_subfs_inode(inode, subi); >+ d_instantiate(dentry, inode); >+ } >+ subfs_unlock(sb); >+ >+ if (rc) >+ iput(inode); >+ >+out: >+ LEAVE(sb, "dentry=%s inode=%p rc=%d", dentry->d_name.name, inode, rc); >+ >+ return rc; >+} >+ >+struct inode * >+get_subfs_inode(struct inode *inode) >+{ >+ struct super_block *sb = inode->i_sb; >+ struct inode *err; >+ struct supermount_inode_info *sii = supermount_i(inode); >+ >+ ENTER(sb, "inode=%p", inode); >+ >+ subfs_lock(sb); >+ >+ err = ERR_PTR(-ENOMEDIUM); >+ if (!subfs_is_mounted(sb)) >+ goto out; >+ >+ err = ERR_PTR(-ESTALE); >+ if (is_inode_obsolete(inode)) >+ goto out; >+ >+ err = igrab(sii->inode); >+ SUPERMOUNT_BUG_LOCKED_ON(sb, !err); >+ >+out: >+ subfs_unlock(sb); >+ >+ LEAVE(sb, "inode=%p subi=%p", inode, err); >+ >+ return err; >+} >+ >+/* inode methods */ >+ >+static int >+supermount_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) >+{ >+ struct super_block *sb = dir->i_sb; >+ struct dentry *subdent; >+ struct inode *subdir; >+ int rc; >+ struct vfsmount *mnt; >+ >+ ENTER(sb, "dir=%p dentry=%s", dir, dentry->d_name.name); >+ >+ mnt = subfs_go_online(sb); >+ rc = PTR_ERR(mnt); >+ if (IS_ERR(mnt)) >+ goto out; >+ >+ subdir = get_subfs_inode(dir); >+ rc = PTR_ERR(subdir); >+ if (IS_ERR(subdir)) >+ goto go_offline; >+ >+ rc = -EACCES; >+ if (!subdir->i_op || !subdir->i_op->create) >+ goto put_subdir; >+ >+ subdent = get_subfs_dentry(dentry); >+ rc = PTR_ERR(subdent); >+ if (IS_ERR(subdent)) >+ goto put_subdir; >+ >+ rc = subfs_get_access(dir, 1); >+ if (rc) >+ goto put_subdent; >+ >+ /* FIXME build proper nd struct */ >+ rc = subdir->i_op->create(subdir, subdent, mode, nd); >+ subfs_put_access(dir, 1); >+ if (rc) >+ goto put_subdent; >+ >+ rc = prepare_inode(dentry, subdent); >+ if (rc) >+ goto put_subdent; >+ >+ dir->i_mtime = subdir->i_mtime; >+ dir->i_ctime = subdir->i_ctime; >+ dir->i_nlink = subdir->i_nlink; >+ dir->i_size = subdir->i_size; >+ dir->i_blocks = subdir->i_blocks; >+ >+put_subdent: >+ dput(subdent); >+put_subdir: >+ iput(subdir); >+go_offline: >+ subfs_go_offline(sb, mnt); >+out: >+ LEAVE(sb, "dir=%p dentry=%s rc=%d", dir, dentry->d_name.name, rc); >+ >+ return rc; >+} >+ >+/** >+ * Search directory for a matching name >+ * >+ * @dir: directory that is being searched >+ * @dentry: name to search for (in dentry->d_name) >+ * >+ * This is (currently :) the only method where we do not call subfs >+ * directly. The reason is coherency between subfs and superfs dentry >+ * caches. It is impossible to ensure it without modifying the very >+ * guts of fs/dcache.c; so we check cache before doing actual lookup. >+ * lookup_one_len just avoids duplicating of code. >+ * >+ * Supermount is in exclusive control of subfs so it is garanteed that >+ * dentry cannot magically appear in the middle of lookup. >+ * >+ * There are filesystems that support multiple forms of file name, like >+ * case-insensitive or short-long names on NTFS. In this case cache lookup >+ * fails but filesystem may return dentry for different name. In this case >+ * we check if dentry with matching name exists in parent and reuse it. >+ */ >+static struct dentry * >+supermount_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) >+{ >+ struct super_block *sb = dir->i_sb; >+ struct dentry *rc, *subdent, *subparent; >+ struct inode *subdir; >+ struct vfsmount *mnt; >+ int ret; >+ >+ ENTER(sb, "dir=%p dentry=%s", dir, dentry->d_name.name); >+ >+ mnt = subfs_go_online(sb); >+ rc = (struct dentry *)mnt; >+ if (IS_ERR(mnt)) >+ goto out; >+ >+ subdir = get_subfs_inode(dir); >+ rc = (struct dentry *)subdir; >+ if (IS_ERR(subdir)) >+ goto go_offline; >+ >+ subparent = get_subfs_dentry(dentry->d_parent); >+ rc = subparent; >+ if (IS_ERR(subparent)) >+ goto put_subdir; >+ >+ ret = subfs_get_access(dir, 0); >+ rc = ERR_PTR(ret); >+ if (ret) >+ goto put_subparent; >+ >+ SUPERMOUNT_BUG_ON(subparent->d_inode != subdir); >+ >+ /* FIXME usually lookup_one_len is called under i_sem */ >+ /* FIXME what to do with nd? */ >+ subdent = lookup_one_len(dentry->d_name.name, subparent, >+ dentry->d_name.len); >+ >+ subfs_put_access(dir, 0); >+ rc = subdent; >+ if (IS_ERR(rc)) >+ goto put_subparent; >+ >+ rc = prepare_dentry(dentry, subdent); >+ if (IS_ERR(rc)) >+ goto put_subdent; >+ >+ if (!rc && subdent->d_inode) { >+ ret = prepare_inode(dentry, subdent); >+ if (ret < 0) >+ rc = ERR_PTR(ret); >+ } >+ >+put_subdent: >+ dput(subdent); >+put_subparent: >+ dput(subparent); >+put_subdir: >+ iput(subdir); >+go_offline: >+ subfs_go_offline(sb, mnt); >+out: >+ LEAVE(sb, "dir=%p dentry=%s rc=%p", dir, dentry->d_name.name, rc); >+ >+ return rc; >+} >+ >+static int >+supermount_link(struct dentry *old_dentry, struct inode *dir, >+ struct dentry *new_dentry) >+{ >+ struct super_block *sb = dir->i_sb; >+ struct dentry *old_subdent, *new_subdent; >+ struct inode *subdir; >+ int rc; >+ struct vfsmount *mnt; >+ >+ ENTER(sb, "from=%s dir=%p to=%s", old_dentry->d_name.name, dir, new_dentry->d_name.name); >+ >+ SUPERMOUNT_BUG_ON(new_dentry->d_inode); >+ >+ mnt = subfs_go_online(sb); >+ rc = PTR_ERR(mnt); >+ if (IS_ERR(mnt)) >+ goto out; >+ >+ subdir = get_subfs_inode(dir); >+ rc = PTR_ERR(subdir); >+ if (IS_ERR(subdir)) >+ goto go_offline; >+ >+ rc = -EPERM; >+ if (!subdir->i_op || !subdir->i_op->link) >+ goto put_subdir; >+ >+ old_subdent = get_subfs_dentry(old_dentry); >+ rc = PTR_ERR(old_subdent); >+ if (IS_ERR(old_subdent)) >+ goto put_subdir; >+ >+ new_subdent = get_subfs_dentry(new_dentry); >+ rc = PTR_ERR(new_subdent); >+ if (IS_ERR(new_subdent)) >+ goto put_old_subdent; >+ >+ rc = subfs_get_access(dir, 1); >+ if (rc) >+ goto put_new_subdent; >+ >+ rc = subdir->i_op->link(old_subdent, subdir, new_subdent); >+ subfs_put_access(dir, 1); >+ if (rc) >+ goto put_new_subdent; >+ >+ rc = prepare_inode(new_dentry, new_subdent); >+ if (rc) >+ goto put_new_subdent; >+ >+ dir->i_mtime = subdir->i_mtime; >+ dir->i_ctime = subdir->i_ctime; >+ dir->i_nlink = subdir->i_nlink; >+ dir->i_size = subdir->i_size; >+ dir->i_blocks = subdir->i_blocks; >+ >+put_new_subdent: >+ dput(new_subdent); >+put_old_subdent: >+ dput(old_subdent); >+put_subdir: >+ iput(subdir); >+go_offline: >+ subfs_go_offline(sb, mnt); >+out: >+ LEAVE(sb, "from=%s dir=%p to=%s rc=%d", old_dentry->d_name.name, dir, new_dentry->d_name.name, rc); >+ >+ return rc; >+} >+ >+static int >+supermount_unlink(struct inode *dir, struct dentry *dentry) >+{ >+ struct super_block *sb = dir->i_sb; >+ struct dentry *subdent; >+ struct inode *subdir; >+ int rc; >+ struct vfsmount *mnt; >+ >+ ENTER(sb, "dir=%p dentry=%s", dir, dentry->d_name.name); >+ >+ mnt = subfs_go_online(sb); >+ rc = PTR_ERR(mnt); >+ if (IS_ERR(mnt)) >+ goto out; >+ >+ subdir = get_subfs_inode(dir); >+ rc = PTR_ERR(subdir); >+ if (IS_ERR(subdir)) >+ goto go_offline; >+ >+ rc = -EPERM; >+ if (!subdir->i_op || !subdir->i_op->unlink) >+ goto put_subdir; >+ >+ subdent = get_subfs_dentry(dentry); >+ rc = PTR_ERR(subdent); >+ if (IS_ERR(subdent)) >+ goto put_subdir; >+ >+ /* >+ * below is not a typo. We have to mark _deleted_ inode >+ * for possible later delete in clear_inode >+ */ >+ rc = subfs_get_access(dentry->d_inode, 1); >+ if (rc) >+ goto put_subdent; >+ >+ rc = subdir->i_op->unlink(subdir, subdent); >+ if (rc) >+ goto put_write_access; >+ >+ dir->i_mtime = subdir->i_mtime; >+ dir->i_ctime = subdir->i_ctime; >+ dir->i_nlink = subdir->i_nlink; >+ dir->i_size = subdir->i_size; >+ dir->i_blocks = subdir->i_blocks; >+ >+ dentry->d_inode->i_nlink = subdent->d_inode->i_nlink; >+ dentry->d_inode->i_ctime = subdent->d_inode->i_ctime; >+ >+put_write_access: >+ /* >+ * we can't put write access if there are pending deletes >+ * so we leave it on and let it be put in clear_inode for >+ * i_nlink == 0 >+ * >+ * While i_nlink is believed to be atomic here i_count not, >+ * so we cannot check && i_count == 0. It is expected that >+ * deleted files are kept open only rarely. >+ */ >+ if (dentry->d_inode->i_nlink) >+ subfs_put_access(dentry->d_inode, 1); >+put_subdent: >+ dput(subdent); >+put_subdir: >+ iput(subdir); >+go_offline: >+ subfs_go_offline(sb, mnt); >+out: >+ LEAVE(sb, "dir=%p dentry=%s rc=%d", dir, dentry->d_name.name, rc); >+ >+ return rc; >+} >+ >+static int >+supermount_symlink(struct inode *dir, struct dentry *dentry, >+ const char *symname) >+{ >+ struct super_block *sb = dir->i_sb; >+ struct dentry *subdent; >+ struct inode *subdir; >+ int rc; >+ struct vfsmount *mnt; >+ >+ ENTER(sb, "dir=%p dentry=%s", dir, dentry->d_name.name); >+ >+ mnt = subfs_go_online(sb); >+ rc = PTR_ERR(mnt); >+ if (IS_ERR(mnt)) >+ goto out; >+ >+ subdir = get_subfs_inode(dir); >+ rc = PTR_ERR(subdir); >+ if (IS_ERR(subdir)) >+ goto go_offline; >+ >+ rc = -EPERM; >+ if (!subdir->i_op || !subdir->i_op->symlink) >+ goto put_subdir; >+ >+ subdent = get_subfs_dentry(dentry); >+ rc = PTR_ERR(subdent); >+ if (IS_ERR(subdent)) >+ goto put_subdir; >+ >+ rc = subfs_get_access(dir, 1); >+ if (rc) >+ goto put_subdent; >+ >+ rc = subdir->i_op->symlink(subdir, subdent, symname); >+ subfs_put_access(dir, 1); >+ if (rc) >+ goto put_subdent; >+ >+ rc = prepare_inode(dentry, subdent); >+ if (rc) >+ goto put_subdent; >+ >+ dir->i_mtime = subdir->i_mtime; >+ dir->i_ctime = subdir->i_ctime; >+ dir->i_nlink = subdir->i_nlink; >+ dir->i_size = subdir->i_size; >+ dir->i_blocks = subdir->i_blocks; >+ >+put_subdent: >+ dput(subdent); >+put_subdir: >+ iput(subdir); >+go_offline: >+ subfs_go_offline(sb, mnt); >+out: >+ LEAVE(sb, "dir=%p dentry=%s rc=%d", dir, dentry->d_name.name, rc); >+ >+ return rc; >+} >+ >+static int >+supermount_mkdir(struct inode *dir, struct dentry *dentry, int mode) >+{ >+ struct super_block *sb = dir->i_sb; >+ struct dentry *subdent; >+ struct inode *subdir; >+ int rc; >+ struct vfsmount *mnt; >+ >+ ENTER(sb, "dir=%p dentry=%s", dir, dentry->d_name.name); >+ >+ mnt = subfs_go_online(sb); >+ rc = PTR_ERR(mnt); >+ if (IS_ERR(mnt)) >+ goto out; >+ >+ subdir = get_subfs_inode(dir); >+ rc = PTR_ERR(subdir); >+ if (IS_ERR(subdir)) >+ goto go_offline; >+ >+ rc = -EPERM; >+ if (!subdir->i_op || !subdir->i_op->mkdir) >+ goto put_subdir; >+ >+ subdent = get_subfs_dentry(dentry); >+ rc = PTR_ERR(subdent); >+ if (IS_ERR(subdent)) >+ goto put_subdir; >+ >+ rc = subfs_get_access(dir, 1); >+ if (rc) >+ goto put_subdent; >+ >+ rc = subdir->i_op->mkdir(subdir, subdent, mode); >+ subfs_put_access(dir, 1); >+ if (rc) >+ goto put_subdent; >+ >+ rc = prepare_inode(dentry, subdent); >+ if (rc) >+ goto put_subdent; >+ >+ dir->i_mtime = subdir->i_mtime; >+ dir->i_ctime = subdir->i_ctime; >+ dir->i_nlink = subdir->i_nlink; >+ dir->i_size = subdir->i_size; >+ dir->i_blocks = subdir->i_blocks; >+ >+put_subdent: >+ dput(subdent); >+put_subdir: >+ iput(subdir); >+go_offline: >+ subfs_go_offline(sb, mnt); >+out: >+ LEAVE(sb, "dir=%p dentry=%s rc=%d", dir, dentry->d_name.name, rc); >+ >+ return rc; >+} >+ >+static int >+supermount_rmdir(struct inode *dir, struct dentry *dentry) >+{ >+ struct super_block *sb = dir->i_sb; >+ struct dentry *subdent; >+ struct inode *subdir; >+ int rc; >+ struct vfsmount *mnt; >+ >+ ENTER(sb, "dir=%p dentry=%s", dir, dentry->d_name.name); >+ >+ mnt = subfs_go_online(sb); >+ rc = PTR_ERR(mnt); >+ if (IS_ERR(mnt)) >+ goto out; >+ >+ subdir = get_subfs_inode(dir); >+ rc = PTR_ERR(subdir); >+ if (IS_ERR(subdir)) >+ goto go_offline; >+ >+ rc = -EPERM; >+ if (!subdir->i_op || !subdir->i_op->rmdir) >+ goto put_subdir; >+ >+ subdent = get_subfs_dentry(dentry); >+ rc = PTR_ERR(subdent); >+ if (IS_ERR(subdent)) >+ goto put_subdir; >+ >+ /* cf. supermount_unlink */ >+ rc = subfs_get_access(dentry->d_inode, 1); >+ if (rc) >+ goto put_subdent; >+ >+ rc = subdir->i_op->rmdir(subdir, subdent); >+ if (rc) >+ goto put_write_access; >+ >+ dir->i_mtime = subdir->i_mtime; >+ dir->i_ctime = subdir->i_ctime; >+ dir->i_nlink = subdir->i_nlink; >+ dir->i_size = subdir->i_size; >+ dir->i_blocks = subdir->i_blocks; >+ >+ /* hmm ... hard links to directories are not allowed, are they? */ >+ dentry->d_inode->i_nlink = subdent->d_inode->i_nlink; >+ dentry->d_inode->i_ctime = subdent->d_inode->i_ctime; >+ >+put_write_access: >+ /* cf. supermount_unlink */ >+ if (dentry->d_inode->i_nlink) >+ subfs_put_access(dentry->d_inode, 1); >+put_subdent: >+ dput(subdent); >+put_subdir: >+ iput(subdir); >+go_offline: >+ subfs_go_offline(sb, mnt); >+out: >+ LEAVE(sb, "dir=%p dentry=%s rc=%d", dir, dentry->d_name.name, rc); >+ >+ return rc; >+} >+ >+static int >+supermount_mknod(struct inode *dir, struct dentry *dentry, int >+ mode, dev_t dev) >+{ >+ struct super_block *sb = dir->i_sb; >+ struct dentry *subdent; >+ struct inode *subdir; >+ int rc; >+ struct vfsmount *mnt; >+ >+ ENTER(sb, "dir=%p dentry=%s", dir, dentry->d_name.name); >+ >+ mnt = subfs_go_online(sb); >+ rc = PTR_ERR(mnt); >+ if (IS_ERR(mnt)) >+ goto out; >+ >+ subdir = get_subfs_inode(dir); >+ rc = PTR_ERR(subdir); >+ if (IS_ERR(subdir)) >+ goto go_offline; >+ >+ rc = -EPERM; >+ if (!subdir->i_op || !subdir->i_op->mknod) >+ goto put_subdir; >+ >+ subdent = get_subfs_dentry(dentry); >+ rc = PTR_ERR(subdent); >+ if (IS_ERR(subdent)) >+ goto put_subdir; >+ >+ rc = subfs_get_access(dir, 1); >+ if (rc) >+ goto put_subdent; >+ >+ rc = subdir->i_op->mknod(subdir, subdent, mode, dev); >+ subfs_put_access(dir, 1); >+ if (rc) >+ goto put_subdent; >+ >+ rc = prepare_inode(dentry, subdent); >+ if (rc) >+ goto put_subdent; >+ >+ dir->i_mtime = subdir->i_mtime; >+ dir->i_ctime = subdir->i_ctime; >+ dir->i_nlink = subdir->i_nlink; >+ dir->i_size = subdir->i_size; >+ dir->i_blocks = subdir->i_blocks; >+ >+put_subdent: >+ dput(subdent); >+put_subdir: >+ iput(subdir); >+go_offline: >+ subfs_go_offline(sb, mnt); >+out: >+ LEAVE(sb, "dir=%p dentry=%s rc=%d", dir, dentry->d_name.name, rc); >+ >+ return rc; >+} >+ >+static int >+supermount_rename(struct inode *olddir, struct dentry *olddentry, >+ struct inode *newdir, struct dentry *newdentry) >+{ >+ struct super_block *sb = olddir->i_sb; >+ struct dentry *oldsubdent, *newsubdent; >+ struct inode *oldsubdir, *newsubdir; >+ int rc; >+ struct vfsmount *mnt; >+ >+ ENTER(sb, "olddir=%p olddentry=%s newdir=%p newdentry=%s", olddir, olddentry->d_name.name, newdir, newdentry->d_name.name); >+ >+ mnt = subfs_go_online(sb); >+ rc = PTR_ERR(mnt); >+ if (IS_ERR(mnt)) >+ goto out; >+ >+ oldsubdir = get_subfs_inode(olddir); >+ rc = PTR_ERR(oldsubdir); >+ if (IS_ERR(oldsubdir)) >+ goto go_offline; >+ >+ rc = -EPERM; >+ if (!oldsubdir->i_op || !oldsubdir->i_op->rename) >+ goto put_old_subdir; >+ >+ oldsubdent = get_subfs_dentry(olddentry); >+ rc = PTR_ERR(oldsubdent); >+ if (IS_ERR(oldsubdent)) >+ goto put_old_subdir; >+ >+ newsubdir = get_subfs_inode(newdir); >+ rc = PTR_ERR(newsubdir); >+ if (IS_ERR(newsubdir)) >+ goto put_old_subdent; >+ >+ newsubdent = get_subfs_dentry(newdentry); >+ rc = PTR_ERR(newsubdent); >+ if (IS_ERR(newsubdent)) >+ goto put_new_subdir; >+ >+ /* >+ * If new file exists it will be implcitly unlinked so >+ * behave like in unlink case. >+ * If it does not exist we have two write accesses - for >+ * both old and new directory, I guess it does not matter >+ * which one is used in this case >+ */ >+ if (newdentry->d_inode) >+ rc = subfs_get_access(newdentry->d_inode, 1); >+ else >+ rc = subfs_get_access(olddir, 1); >+ if (rc) >+ goto put_new_subdent; >+ >+ rc = oldsubdir->i_op->rename(oldsubdir, oldsubdent, >+ newsubdir, newsubdent); >+ if (rc) >+ goto put_write_access; >+ >+ /* >+ * this is strictly speaking conditional on FS_RENAME_DOES_D_MOVE >+ * flag, but as of this writing this flag is set only >+ * for NFS or intermezzo and it hopefully goes away sometimes ... >+ * >+ * Supermount patch adds code to do_kern_mount that checks >+ * for this flag and refuses mounting >+ */ >+ d_move(oldsubdent, newsubdent); >+ >+ olddir->i_mtime = oldsubdir->i_mtime; >+ olddir->i_ctime = oldsubdir->i_ctime; >+ olddir->i_nlink = oldsubdir->i_nlink; >+ olddir->i_size = oldsubdir->i_size; >+ olddir->i_blocks = oldsubdir->i_blocks; >+ >+ newdir->i_mtime = newsubdir->i_mtime; >+ newdir->i_ctime = newsubdir->i_ctime; >+ newdir->i_nlink = newsubdir->i_nlink; >+ newdir->i_size = newsubdir->i_size; >+ newdir->i_blocks = newsubdir->i_blocks; >+ >+ olddentry->d_inode->i_nlink = oldsubdent->d_inode->i_nlink; >+ olddentry->d_inode->i_ctime = oldsubdent->d_inode->i_ctime; >+ >+ if (newdentry->d_inode) { >+ newdentry->d_inode->i_nlink = newsubdent->d_inode->i_nlink; >+ newdentry->d_inode->i_ctime = newsubdent->d_inode->i_ctime; >+ } >+ >+put_write_access: >+ if (newdentry->d_inode) { >+ /* cf. supermount_unlink */ >+ if (newdentry->d_inode->i_nlink) >+ subfs_put_access(newdentry->d_inode, 1); >+ } else >+ subfs_put_access(olddir, 1); >+put_new_subdent: >+ dput(newsubdent); >+put_new_subdir: >+ iput(newsubdir); >+put_old_subdent: >+ dput(oldsubdent); >+put_old_subdir: >+ iput(oldsubdir); >+go_offline: >+ subfs_go_offline(sb, mnt); >+out: >+ LEAVE(sb, "olddentry=%s newdentry=%s rc=%d", olddentry->d_name.name, newdentry->d_name.name, rc); >+ >+ return rc; >+} >+ >+static int >+supermount_readlink(struct dentry *dentry, char *buffer , int buflen) >+{ >+ struct super_block *sb = dentry->d_sb; >+ struct inode *inode = dentry->d_inode; >+ struct dentry *subdent; >+ int write_on = NEED_WRITE_ATIME(inode); >+ int rc; >+ struct vfsmount *mnt; >+ >+ ENTER(sb, "dentry=%s", dentry->d_name.name); >+ >+ mnt = subfs_go_online(sb); >+ rc = PTR_ERR(mnt); >+ if (IS_ERR(mnt)) >+ goto out; >+ >+ subdent = get_subfs_dentry(dentry); >+ rc = PTR_ERR(subdent); >+ if (IS_ERR(subdent)) >+ goto go_offline; >+ >+ rc = -EINVAL; >+ if (!subdent->d_inode->i_op || !subdent->d_inode->i_op->readlink) >+ goto put_subdent; >+ >+ rc = subfs_get_access(inode, write_on); >+ if (rc) >+ goto put_subdent; >+ >+ //update_atime(subdent->d_inode); >+ touch_atime(mnt,subdent); >+ rc = subdent->d_inode->i_op->readlink(subdent, buffer, buflen); >+ subfs_put_access(inode, write_on); >+ inode->i_atime = subdent->d_inode->i_atime; >+ >+put_subdent: >+ dput(subdent); >+go_offline: >+ subfs_go_offline(sb, mnt); >+out: >+ LEAVE(sb, "dentry=%s rc=%d", dentry->d_name.name, rc); >+ >+ return rc; >+} >+ >+static int >+supermount_follow_link(struct dentry *dentry, struct nameidata *nd) >+{ >+ struct super_block *sb = dentry->d_sb; >+ struct inode *inode = dentry->d_inode; >+ struct dentry *subdent; >+ int write_on = NEED_WRITE_ATIME(inode); >+ int rc; >+ struct vfsmount *mnt; >+ >+ ENTER(sb, "dentry=%s", dentry->d_name.name); >+ >+ mnt = subfs_go_online(sb); >+ rc = PTR_ERR(mnt); >+ if (IS_ERR(mnt)) >+ goto out; >+ >+ subdent = get_subfs_dentry(dentry); >+ rc = PTR_ERR(subdent); >+ if (IS_ERR(subdent)) >+ goto go_offline; >+ >+ rc = subfs_get_access(inode, write_on); >+ if (rc) >+ goto put_subdent; >+ >+ //update_atime(subdent->d_inode); >+ touch_atime(mnt,subdent); >+ /* FIXME do we need proper subfs nd here? */ >+ rc = subdent->d_inode->i_op->follow_link(subdent, nd); >+ subfs_put_access(inode, write_on); >+ inode->i_atime = subdent->d_inode->i_atime; >+ >+put_subdent: >+ dput(subdent); >+go_offline: >+ subfs_go_offline(sb, mnt); >+out: >+ LEAVE(sb, "dentry=%s rc=%d", dentry->d_name.name, rc); >+ >+ return rc; >+} >+ >+static int >+supermount_permission(struct inode *inode, int mask, struct nameidata *nd) >+{ >+ struct super_block *sb = inode->i_sb; >+ struct inode *subi; >+ int rc; >+ int write_on = !IS_RDONLY(inode) && (mask & MAY_WRITE); >+ struct vfsmount *mnt; >+ int fake_permissions = 1; >+ >+ ENTER(sb, "inode=%p", inode); >+ >+ mnt = subfs_go_online(sb); >+ rc = PTR_ERR(mnt); >+ if (IS_ERR(mnt)) >+ goto out; >+ >+ subi = get_subfs_inode(inode); >+ rc = PTR_ERR(subi); >+ if (IS_ERR(subi)) >+ goto go_offline; >+ >+ rc = subfs_get_access(inode, write_on); >+ if (rc) >+ goto put_subi; >+ >+ fake_permissions = 0; >+ /* FIXME do we need proper subfs nd here */ >+ if (subi->i_op && subi->i_op->permission) >+ rc = subi->i_op->permission(subi, mask, nd); >+ else >+ rc = generic_permission(subi, mask, NULL); >+ >+ subfs_put_access(inode, write_on); >+ >+put_subi: >+ iput(subi); >+go_offline: >+ subfs_go_offline(sb, mnt); >+out: >+ if (fake_permissions && inode == sb->s_root->d_inode) { >+ /* cf. file.c:supermount_open() */ >+ rc = generic_permission(inode, mask, NULL); >+ } >+ >+ LEAVE(sb, "inode=%p rc=%d fake=%d", inode, rc, fake_permissions); >+ >+ return rc; >+} >+ >+static int >+supermount_setattr(struct dentry *dentry, struct iattr *attr) >+{ >+ struct super_block *sb = dentry->d_sb; >+ struct inode *subi; >+ struct dentry *subdent; >+ int rc; >+ struct vfsmount *mnt; >+ >+ ENTER(sb, "dentry=%s", dentry->d_name.name); >+ >+ mnt = subfs_go_online(sb); >+ rc = PTR_ERR(mnt); >+ if (IS_ERR(mnt)) >+ goto out; >+ >+ subdent = get_subfs_dentry(dentry); >+ rc = PTR_ERR(subdent); >+ if (IS_ERR(subdent)) >+ goto go_offline; >+ >+ rc = subfs_get_access(dentry->d_inode, 1); >+ if (rc) >+ goto put_subdent; >+ >+ subi = subdent->d_inode; >+ if (subi->i_op && subi->i_op->setattr) >+ rc = subi->i_op->setattr(subdent, attr); >+ else { >+ rc = inode_change_ok(subi, attr); >+ /* >+ * FIXME >+ * What to do with quota? >+ */ >+ if (!rc) >+ rc = inode_setattr(subi, attr); >+ } >+ subfs_put_access(dentry->d_inode, 1); >+ if (rc) >+ goto put_subdent; >+ >+ /* >+ * If it worked, then we need to mark the modification >+ * to the subfs, and we also need to propogate the >+ * change up to the shadowing inode. >+ */ >+ attr->ia_mode = subi->i_mode; >+ attr->ia_uid = subi->i_uid; >+ attr->ia_gid = subi->i_gid; >+ attr->ia_size = subi->i_size; >+ attr->ia_atime = subi->i_atime; >+ attr->ia_mtime = subi->i_mtime; >+ attr->ia_ctime = subi->i_ctime; >+ attr->ia_valid = >+ ATTR_UID | ATTR_GID | ATTR_MODE | ATTR_SIZE | >+ ATTR_ATIME | ATTR_MTIME | ATTR_CTIME; >+ inode_setattr(dentry->d_inode, attr); >+ >+put_subdent: >+ dput(subdent); >+go_offline: >+ subfs_go_offline(sb, mnt); >+out: >+ LEAVE(sb, "dentry=%s rc=%d", dentry->d_name.name, rc); >+ >+ return rc; >+} >+ >+static int >+supermount_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) >+{ >+ struct super_block *sb = dentry->d_sb; >+ struct vfsmount *submnt; >+ struct dentry *subdent = 0; >+ int rc; >+ >+ ENTER(sb, "dentry=%s", dentry->d_name.name); >+ >+ /* >+ * do not use subfs_go_online - it will result in ls /mnt >+ * attempting to mount all supermounted directories >+ */ >+ submnt = subfs_prevent_umount(sb); >+ if (submnt) >+ subdent = get_subfs_dentry(dentry); >+ >+ /* >+ * do not fail stat for stale files - it is needed to >+ * make sure fuser -m /mnt/cdrom lists all processes still >+ * having any (obsolete) file open >+ */ >+ if (submnt && subdent && !IS_ERR(subdent)) { >+ rc = vfs_getattr(submnt, subdent, stat); >+ stat->dev = sb->s_dev; >+ } else { >+ subfs_lock(sb); >+ generic_fillattr(dentry->d_inode, stat); >+ subfs_unlock(sb); >+ rc = 0; >+ } >+ >+ if (subdent && !IS_ERR(subdent)) >+ dput(subdent); >+ if (submnt) >+ subfs_allow_umount(sb, submnt); >+ >+ LEAVE(sb, "dentry=%s rc=%d", dentry->d_name.name, rc); >+ >+ return rc; >+} >+ >+/* >+ * directories can handle most operations... supermount/namei.c just >+ * passes them through to the underlying subfs, except for lookup(). >+ */ >+ >+/* truncate: is not necesary, handled with setattr >+ * revalidate: only needed by nfs >+ * ->getattr: FIXME is not appeared to be used anywhere in kernel; so I am >+ * not sure how to implement it or what to return >+ * FIXME: implement accl functions >+ */ >+ >+struct inode_operations supermount_dir_iops = { >+ .create = supermount_create, >+ .lookup = supermount_lookup, >+ .link = supermount_link, >+ .unlink = supermount_unlink, >+ .symlink = supermount_symlink, >+ .mkdir = supermount_mkdir, >+ .rmdir = supermount_rmdir, >+ .mknod = supermount_mknod, >+ .rename = supermount_rename, >+ .permission = supermount_permission, >+ .setattr = supermount_setattr, >+ .getattr = supermount_getattr, >+}; >+ >+struct inode_operations supermount_symlink_iops = { >+ .readlink = supermount_readlink, >+ .follow_link = supermount_follow_link, >+ .setattr = supermount_setattr, >+ .getattr = supermount_getattr, >+}; >+ >+struct inode_operations supermount_file_iops = { >+ .setattr = supermount_setattr, >+ .getattr = supermount_getattr, >+}; >diff -urN linux-2.6.25.4.orig/fs/supermount/proc.c linux-2.6.25.4/fs/supermount/proc.c >--- linux-2.6.25.4.orig/fs/supermount/proc.c 1970-01-01 03:00:00 +0300 >+++ linux-2.6.25.4/fs/supermount/proc.c 2008-05-20 01:27:53 +0300 >@@ -0,0 +1,289 @@ >+/* >+ * linux/fs/supermount/proc.c >+ * >+ * Initial version for kernel 2.4.21 (C) 2003 Andrey Borzenkov >+ * (arvidjaar@mail.ru) >+ * Rewritten for kernel 2.5.70 (C) 2003 Andrey Borzenkov >+ * (arvidjaar@mail.ru) >+ * >+ * $Id: proc.c,v 1.7.4.4 2003/10/26 16:21:25 bor Exp $ >+ */ >+ >+#include "supermount.h" >+ >+#ifdef CONFIG_PROC_FS >+ >+/* >+ * we use semaphore because we have to lock subfs inside and it can >+ * sleep. Unlocking procfs list would mean adding generic usage count >+ * management to sbi and this is far too fetching >+ */ >+ >+static DECLARE_MUTEX(supermount_proc_sem); >+static struct proc_dir_entry *supermount_proc_root; >+static struct proc_dir_entry *supermount_proc_subfs; >+static struct proc_dir_entry *supermount_proc_version; >+static struct supermount_sb_info *supermount_list_head; >+ >+#define SKIP_BLANKS(s) while(*s == ' ' || *s == '\t' || *s == '\n') s++ >+#define CHECK_COUNT do { \ >+ size += n; \ >+ count -= n; \ >+ if (count <= 0) { \ >+ subfs_unlock(sbi->host); \ >+ break; \ >+ } \ >+} while (0) >+ >+/* iterator; shamelessly copied over from pci.c */ >+static void *supermount_seq_start(struct seq_file *sf, loff_t *pos) >+{ >+ struct supermount_sb_info *sbi; >+ loff_t n = *pos; >+ >+ down(&supermount_proc_sem); >+ >+ sbi = supermount_list_head; >+ while (n-- && sbi) >+ sbi = sbi->next; >+ >+ return sbi; >+} >+ >+static void *supermount_seq_next(struct seq_file *sf, void *v, loff_t *pos) >+{ >+ struct supermount_sb_info *sbi = v; >+ (*pos)++; >+ return sbi->next; >+} >+ >+static void supermount_seq_stop(struct seq_file *sf, void *v) >+{ >+ up(&supermount_proc_sem); >+} >+ >+ >+static int >+supermount_show_sbi(struct seq_file *sf, void *v) >+{ >+ struct supermount_sb_info *sbi = v; >+ >+ >+ subfs_lock(sbi->host); >+ seq_puts(sf, sbi->devname); >+ if (sbi->disabled) >+ seq_puts(sf, " disabled\n"); >+ else if (subfs_is_mounted(sbi->host)) >+ seq_printf(sf, " mounted %d %d\n", >+ sbi->readcount, sbi->writecount); >+ else >+ seq_puts(sf, " unmounted\n"); >+ subfs_unlock(sbi->host); >+ >+ return 0; >+} >+ >+static struct seq_operations supermount_proc_subfs_op = { >+ start: supermount_seq_start, >+ next: supermount_seq_next, >+ stop: supermount_seq_stop, >+ show: supermount_show_sbi >+}; >+ >+static int supermount_proc_subfs_open(struct inode *inode, struct file *file) >+{ >+ return seq_open(file, &supermount_proc_subfs_op); >+} >+ >+/* >+ * mostly copied over from drivers/scsi/scsi.c:proc_scsi_gen_write() >+ */ >+static int >+supermount_proc_subfs_write(struct file *file, const char *buf, size_t length, loff_t *offset) >+{ >+ char *buffer, *s, *dev = 0; >+ int disable = 0, enable = 0, release = 0, force = 0; >+ struct supermount_sb_info *sbi; >+ size_t rc; >+ >+ rc = -EINVAL; >+ if (!buf || length > PAGE_SIZE) >+ goto out; >+ >+ rc = -ENOMEM; >+ if (!(s = buffer = (char *)__get_free_page(GFP_KERNEL))) >+ goto out; >+ >+ rc =-EFAULT; >+ if(copy_from_user(buffer, buf, length)) >+ goto free_buffer; >+ >+ rc = -EINVAL; >+ if (length < PAGE_SIZE) >+ buffer[length] = '\0'; >+ else if (buffer[PAGE_SIZE-1]) >+ goto free_buffer; >+ >+ /* >+ * echo "/dev/cdrom [enable|disable] [release [force]]" > \ >+ * /proc/fs/supermount/subfs >+ */ >+ >+ do { >+ char *p; >+ >+ SKIP_BLANKS(s); >+ p = strpbrk(s, " \t\n"); >+ if (p) >+ *p++ = '\0'; >+ if (!dev) >+ dev = s; >+ else if (!strcmp(s, "disable")) >+ disable = 1; >+ else if (!strcmp(s, "enable")) >+ enable = 1; >+ else if (!strcmp(s, "release")) >+ release = 1; >+ else if (!strcmp(s, "force")) >+ force = 1; >+ else >+ goto free_buffer; >+ >+ s = p; >+ } while (s && *s); >+ >+ if ((enable && disable) || (force && !release)) >+ goto free_buffer; >+ >+ down(&supermount_proc_sem); >+ for(sbi = supermount_list_head; sbi; sbi = sbi->next) { >+ if (strcmp(sbi->devname, dev)) >+ continue; >+ >+ subfs_lock(sbi->host); >+ >+ rc = length; >+ if (release && subfs_is_mounted(sbi->host)) { >+ if (!subfs_is_busy(sbi->host) || force) >+ subfs_umount(sbi->host, SUBFS_UMNT_USER); >+ else >+ rc = -EBUSY; >+ } >+ >+ if (disable && subfs_is_mounted(sbi->host)) >+ rc = -EBUSY; >+ >+ if (rc >= 0) { >+ if (disable) >+ sbi->disabled = 1; >+ else if (enable) >+ sbi->disabled = 0; >+ } >+ >+ subfs_unlock(sbi->host); >+ break; >+ } >+ up(&supermount_proc_sem); >+ >+free_buffer: >+ free_page((unsigned long)buffer); >+out: >+ return rc; >+ >+} >+ >+static struct file_operations supermount_proc_subfs_operations = { >+ open: supermount_proc_subfs_open, >+ read: seq_read, >+ llseek: seq_lseek, >+ release: seq_release, >+ write: supermount_proc_subfs_write, >+}; >+ >+static int >+supermount_proc_version_read(char *page, char **start, off_t pos, int count, int *eof, void *data) >+{ >+ int rc; >+ >+ rc = snprintf(page, count, "Supermount version %s for kernel 2.6\n", >+ SUPERMOUNT_VERSION); >+ *eof = 1; >+ return rc; >+} >+ >+void >+supermount_proc_register(void) >+{ >+ supermount_proc_root = proc_mkdir("fs/supermount", 0); >+ if (!supermount_proc_root) { >+ printk(KERN_ERR "SUPERMOUNT failed to create /proc/fs/supermount"); >+ return; >+ } >+ supermount_proc_root->owner = THIS_MODULE; >+ >+ supermount_proc_version = create_proc_read_entry("version", >+ S_IFREG | S_IRUGO, >+ supermount_proc_root, >+ supermount_proc_version_read, >+ 0); >+ >+ if (supermount_proc_version) >+ supermount_proc_version->owner = THIS_MODULE; >+ else >+ printk(KERN_ERR >+ "SUPERMOUNT failed to create /proc/fs/supermount/version"); >+ >+ supermount_proc_subfs = create_proc_entry("subfs", >+ S_IFREG | S_IRUGO | S_IWUSR, >+ supermount_proc_root); >+ >+ if (supermount_proc_subfs) { >+ supermount_proc_subfs->proc_fops = >+ &supermount_proc_subfs_operations; >+ supermount_proc_subfs->owner = THIS_MODULE; >+ } else >+ printk(KERN_ERR >+ "SUPERMOUNT failed to create /proc/fs/supermount/subfs"); >+ >+} >+ >+void >+supermount_proc_unregister(void) >+{ >+ remove_proc_entry("fs/supermount/subfs", 0); >+ remove_proc_entry("fs/supermount/version", 0); >+ remove_proc_entry("fs/supermount", 0); >+} >+ >+void >+supermount_proc_insert(struct supermount_sb_info *sbi) >+{ >+ >+ down(&supermount_proc_sem); >+ >+ sbi->next = supermount_list_head; >+ supermount_list_head = sbi; >+ >+ up(&supermount_proc_sem); >+} >+ >+void >+supermount_proc_remove(struct supermount_sb_info *sbi) >+{ >+ struct supermount_sb_info **p, *q; >+ >+ down(&supermount_proc_sem); >+ >+ for(p = &supermount_list_head, q = supermount_list_head; >+ q; p = &q->next, q = q->next) >+ if (q == sbi) >+ break; >+ >+ if (q) >+ *p = q->next; >+ >+ up(&supermount_proc_sem); >+ >+ SUPERMOUNT_BUG_ON(!q); >+} >+#endif /* CONFIG_PROC_FS */ >diff -urN linux-2.6.25.4.orig/fs/supermount/subfs.c linux-2.6.25.4/fs/supermount/subfs.c >--- linux-2.6.25.4.orig/fs/supermount/subfs.c 1970-01-01 03:00:00 +0300 >+++ linux-2.6.25.4/fs/supermount/subfs.c 2008-05-20 01:27:53 +0300 >@@ -0,0 +1,742 @@ >+/* >+ * linux/fs/supermount/subfs.c >+ * >+ * Original version: >+ * Copyright (C) 1995, 1997 >+ * Stephen Tweedie (sct@dcs.ed.ac.uk) >+ * >+ * from >+ * >+ * linux/fs/minix/inode.c >+ * Copyright (C) 1991, 1992 Linus Torvalds >+ * >+ * and >+ * >+ * linux/fs/ext2/super.c >+ * Copyright (C) 1992, 1993, 1994, 1995 Remy Card >+ * >+ * Rewriten for kernel 2.2, 2.4. (C) 1999, 2000 Alexis Mikhailov >+ * (alexis@abc.cap.ru) >+ * Rewritten for kernel 2.4.21 (C) 2003 Andrey Borzenkov >+ * (arvidjaar@mail.ru) >+ * Rewritten for kernel 2.5.70 (C) 2003 Andrey Borzenkov >+ * (arvidjaar@mail.ru) >+ * >+ * $Id: subfs.c,v 1.29.2.11 2004/01/14 19:24:10 bor Exp $ >+ */ >+ >+#define S_DBG_TRACE_CURRENT S_DBG_TRACE_SUBFS >+#include "supermount.h" >+ >+/* >+ * close all open files on subfs >+ */ >+static void >+supermount_clean_files(struct super_block *sb) >+{ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); >+ struct list_head *ptr, *n; >+ >+ ENTER(sb); >+ >+ list_for_each_safe(ptr, n, &sbi->s_files) { >+ struct supermount_file_info *sfi; >+ struct file *subfile; >+ >+ sfi = list_entry(ptr, struct supermount_file_info, list); >+ >+ subfile = sfi->file; >+ sfi->file = 0; >+ list_del_init(&sfi->list); >+ >+ SUPERMOUNT_BUG_LOCKED_ON(sb, !subfile); >+ >+ fput(subfile); >+ } >+ >+ LEAVE(sb); >+ >+} >+ >+/* >+ * close all open dentries on subfs >+ */ >+static void >+supermount_clean_dentries(struct super_block *sb) >+{ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); >+ struct list_head *ptr, *n; >+ >+ ENTER(sb); >+ >+ list_for_each_safe(ptr, n, &sbi->s_dentries) { >+ struct supermount_dentry_info *sdi; >+ struct dentry *subdent; >+ >+ sdi = list_entry(ptr, struct supermount_dentry_info, list); >+ /* >+ * HACK - FIXME >+ * see dentry.c:supermount_d_compare >+ */ >+ write_lock(&d_compare_lock); >+ subdent = sdi->dentry; >+ sdi->dentry = 0; >+ write_unlock(&d_compare_lock); >+ >+ list_del_init(&sdi->list); >+ d_drop(sdi->host); >+ dnotify_parent(sdi->host, DN_DELETE); >+ >+ SUPERMOUNT_BUG_LOCKED_ON(sb, !subdent); >+ >+ dput(subdent); >+ } >+ >+ LEAVE(sb); >+ >+} >+ >+/* >+ * close all open inodes on subfs >+ */ >+static void >+supermount_clean_inodes(struct super_block *sb) >+{ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); >+ struct list_head *ptr, *n; >+ >+ ENTER(sb); >+ >+ list_for_each_safe(ptr, n, &sbi->s_inodes) { >+ struct supermount_inode_info *sii; >+ struct inode *host; >+ struct inode *subi; >+ >+ sii = list_entry(ptr, struct supermount_inode_info, list); >+ host = &sii->vfs_inode; >+ >+ subi = sii->inode; >+ sii->inode = NULL; >+ list_del_init(&sii->list); >+ remove_inode_hash(host); >+ host->i_mapping = &host->i_data; >+ >+ /* >+ * it is possible to have no subi here. clear_inode does >+ * release lock after removing subi but before unlinking >+ * it >+ */ >+ if (subi) >+ iput(subi); >+ >+ SUPERMOUNT_BUG_LOCKED_ON(sb, sii->writecount < 0); >+ SUPERMOUNT_BUG_LOCKED_ON(sb, sii->readcount < 0); >+ while (sii->writecount) { >+ sii->writecount--; >+ sbi->writecount--; >+ } >+ while (sii->readcount) { >+ sii->readcount--; >+ sbi->readcount--; >+ } >+ } >+ >+ LEAVE(sb); >+} >+ >+/* >+ * reason can be >+ * SUBFS_UMNT_NORMAL - normal umount, do not try to release subfs >+ * SUBFS_UMNT_MEDIA - media change detected, release subfs, >+ * do not remount ro (as media is already gone) >+ * SUBFS_UMNT_USER - user request, release subfs, remount ro before >+ * releasing tray lock >+ * >+ * unlock_door is always needed to keep device usage count correct >+ */ >+static int subfs_remount_ro(struct super_block *sb); >+void >+subfs_umount(struct super_block *sb, int reason) >+{ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); >+ int writecount = sbi->writecount; >+ >+ ENTER(sb); >+ >+ if (reason != SUBFS_UMNT_NORMAL) { >+ /* >+ * we used to did shrink_dcache here. It compicates locking >+ * (clear_inode is called under sbi->sem thus requiring either >+ * recursive lock or separate lock just for inode list). >+ * This is not needed any more to ensure subfs can be umounted >+ * so we let dentries die and rely on dentry_revalidate to >+ * reject stale dentries >+ */ >+ >+ if (subfs_is_busy(sb)) >+ supermount_warning(sb, "opened files during media change"); >+ >+ supermount_clean_files(sb); >+ supermount_clean_dentries(sb); >+ supermount_clean_inodes(sb); >+ >+ if (reason == SUBFS_UMNT_USER && writecount > 0) >+ subfs_remount_ro(sb); >+ if (sbi->lockcount > 0) >+ supermount_unlock_door(sb); >+ /* >+ * this is quite ugly but so far I have no idea how to >+ * do it cleanly >+ */ >+ sbi->lockcount = 0; >+ } >+ >+ /* >+ * all files are expected to be closed >+ */ >+ SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->writecount); >+ SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->readcount); >+ SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->lockcount); >+ SUPERMOUNT_BUG_LOCKED_ON(sb, !list_empty(&sbi->s_files)); >+ SUPERMOUNT_BUG_LOCKED_ON(sb, !list_empty(&sbi->s_dentries)); >+ SUPERMOUNT_BUG_LOCKED_ON(sb, !list_empty(&sbi->s_inodes)); >+ >+ unmark_media_supermounted(sb); >+ mntput(sbi->s_undermount); >+ sbi->s_undermount = NULL; >+ if (sbi->rw) >+ sb->s_flags &= ~MS_RDONLY; >+ sb->s_blocksize = 1024; >+ sb->s_blocksize_bits = 10; >+ >+ /* >+ * FIXME >+ * again the problem of unmounting subfs from inside of put_super >+ */ >+ if (sb->s_root) >+ supermount_init_root_inode(sb->s_root->d_inode); >+ >+ LEAVE(sb); >+} >+ >+static int >+subfs_remount_ro(struct super_block *sb) >+{ >+ struct super_block *subsb = subfs_sb(sb); >+ int rc; >+ >+ ENTER(sb); >+ >+ rc = do_remount_sb(subsb, subsb->s_flags | MS_RDONLY, NULL, 0); >+ >+ LEAVE(sb); >+ >+ return rc; >+} >+ >+static int >+subfs_remount_rw(struct super_block *sb) >+{ >+ struct super_block *subsb = subfs_sb(sb); >+ int rc; >+ >+ ENTER(sb); >+ >+ rc = do_remount_sb(subsb, subsb->s_flags & ~MS_RDONLY, NULL, 0); >+ >+ LEAVE(sb); >+ >+ return rc; >+} >+ >+static struct vfsmount * >+subfs_real_mount(struct super_block *sb, char *type) >+{ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); >+ struct vfsmount *mnt; >+ char *opts = NULL; >+ >+ ENTER(sb, "type=%s", type); >+ >+ if (sbi->s_data) { >+ opts = strdup(sbi->s_data); >+ if (!opts) { >+ mnt = ERR_PTR(-ENOMEM); >+ goto fail; >+ } >+ } >+ >+ mnt = do_kern_mount(type, >+ (sb->s_flags & MS_RMT_MASK) | MS_SUPERMOUNTED, >+ sbi->devname, opts); >+ if (opts) >+ kfree(opts); >+ >+fail: >+ LEAVE(sb, "submnt=%p", mnt); >+ >+ return mnt; >+} >+ >+static int >+subfs_real_mount2(struct super_block *sb, char *type) >+{ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); >+ struct vfsmount *mnt; >+ struct super_block *subsb; >+ int rc = 0; >+ >+ ENTER(sb, "type=%s", type); >+ >+ mnt = subfs_real_mount(sb, type); >+ >+ if (IS_ERR(mnt) && PTR_ERR(mnt) == -EROFS) { >+ sb->s_flags |= MS_RDONLY; >+ mnt = subfs_real_mount(sb, type); >+ } >+ rc = PTR_ERR(mnt); >+ if (IS_ERR(mnt)) >+ goto out; >+ >+ /* paranoid check for double-mounting */ >+ SUPERMOUNT_BUG_LOCKED_ON(sb, atomic_read(&mnt->mnt_sb->s_active) != 1); >+ >+ sbi->s_undermount = mnt; >+ >+ if (!(sb->s_flags & MS_RDONLY)) { >+ rc = subfs_remount_ro(sb); >+ if (rc) >+ goto mntput; >+ } >+ >+ subsb = mnt->mnt_sb; >+ sb->s_blocksize = subsb->s_blocksize; >+ sb->s_blocksize_bits = subsb->s_blocksize_bits; >+ attach_subfs_dentry(sb->s_root, mnt->mnt_root); >+ attach_subfs_inode(sb->s_root->d_inode, mnt->mnt_root->d_inode); >+ insert_inode_hash(sb->s_root->d_inode); >+ dnotify_parent(sb->s_root, DN_CREATE); >+ rc = 0; >+ goto out; >+ >+ /* >+ * error clean up >+ */ >+mntput: >+ sbi->s_undermount = 0; >+ mntput(mnt); >+out: >+ LEAVE(sb, "type=%s rc=%d", type, rc); >+ >+ return rc; >+} >+ >+/* >+ * Error values from mount >+ * ENOENT - no device file (quite possible with devfs) >+ * ENXIO - device does not exist >+ * ENOMEDIUM - no medium inserted (surprise, surprise :) >+ * EBUSY - attempt to mount on already mounted device >+ * we specifically disallow it even when both >+ * file system and mode (ro/rw) are the same >+ */ >+static int >+subfs_mount(struct super_block *sb) >+{ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); >+ int retval = -ENODEV; >+ char *types = strdup(sbi->s_type); >+ >+ ENTER(sb); >+ >+ if (sbi->disabled) >+ retval = -EPERM; >+ else if (!types) >+ retval = -ENOMEM; >+ else { >+ char *p = types, *fs; >+ >+ while ((fs = strsep(&p, ":")) != NULL && retval >+ && retval != -ENXIO >+ && retval != -ENOMEDIUM >+ && retval != -ENOENT >+ && retval != -EBUSY) >+ retval = subfs_real_mount2(sb, fs); >+ } >+ >+ if (types) >+ kfree(types); >+ >+ if (!retval) >+ mark_media_supermounted(sb); >+ >+ LEAVE(sb, "rc=%d", retval); >+ >+ return retval; >+} >+ >+static int >+__subfs_check_disk_change(struct super_block *sb) >+{ >+ struct super_block *subsb = subfs_sb(sb); >+ int rc; >+ struct block_device *dev = subsb->s_bdev; >+ >+ ENTER(sb); >+ >+ rc = atomic_read(&subsb->s_media_changed); >+ if (!rc) { >+ lock_kernel(); >+ rc = check_disk_change(dev); >+ unlock_kernel(); >+ } >+ >+ if (rc) >+ subfs_umount(sb, SUBFS_UMNT_MEDIA); >+ >+ LEAVE(sb, "rc=%d", rc); >+ >+ return rc; >+} >+ >+/* >+ * this must really be called subfs_active ... >+ */ >+int >+subfs_check_disk_change(struct super_block *sb) >+{ >+ int rc; >+ >+ ENTER(sb); >+ >+ subfs_lock(sb); >+ if (subfs_is_mounted(sb)) >+ rc = __subfs_check_disk_change(sb); >+ else >+ rc = 1; >+ subfs_unlock(sb); >+ >+ >+ LEAVE(sb, "rc=%d", rc); >+ >+ return rc; >+} >+ >+static int >+check_and_remount_subfs(struct super_block *sb) >+{ >+ int rc; >+ >+ ENTER(sb); >+ >+ if (subfs_is_mounted(sb) && !__subfs_check_disk_change(sb)) { >+ rc = 0; >+ goto out; >+ } >+ >+ rc = subfs_mount(sb); >+ >+out: >+ LEAVE(sb, "rc=%d", rc); >+ >+ return rc; >+} >+ >+static void >+subfs_tray_lock(struct super_block *sb) >+{ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); >+ >+ ENTER(sb); >+ >+ SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->lockcount < 0); >+ >+ if (!sbi->lockcount++) >+ supermount_lock_door(sb); >+ >+ LEAVE(sb); >+} >+ >+static void >+subfs_tray_unlock(struct super_block *sb) >+{ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); >+ >+ ENTER(sb); >+ >+ SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->lockcount <= 0); >+ >+ if (!--sbi->lockcount) >+ supermount_unlock_door(sb); >+ >+ LEAVE(sb); >+} >+ >+static void >+subfs_get_read_access(struct super_block *sb) >+{ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); >+ >+ ENTER(sb); >+ >+ SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->readcount < 0); >+ >+ if (!sbi->readcount++ && sbi->tray_lock == TRAY_LOCK_ALWAYS) >+ subfs_tray_lock(sb); >+ >+ LEAVE(sb); >+} >+ >+static int >+subfs_get_write_access(struct super_block *sb) >+{ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); >+ int rc; >+ >+ ENTER(sb); >+ >+ SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->writecount < 0); >+ >+ rc = 0; >+ if (!sbi->writecount) { >+ if (sb->s_flags & MS_RDONLY) >+ rc = -EROFS; >+ else >+ rc = subfs_remount_rw(sb); >+ } >+ >+ if (!rc && !sbi->writecount++ && sbi->tray_lock != TRAY_LOCK_NEVER) >+ subfs_tray_lock(sb); >+ >+ LEAVE(sb, "rc=%d", rc); >+ >+ return rc; >+} >+ >+static void >+subfs_put_read_access(struct super_block *sb) >+{ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); >+ >+ ENTER(sb); >+ >+ SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->readcount <= 0); >+ >+ if (!--sbi->readcount && sbi->tray_lock == TRAY_LOCK_ALWAYS) >+ subfs_tray_unlock(sb); >+ >+ LEAVE(sb); >+} >+ >+static void >+subfs_put_write_access(struct super_block *sb) >+{ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); >+ >+ ENTER(sb); >+ >+ SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->writecount <= 0); >+ >+ if (!--sbi->writecount) { >+ /* >+ * no need to fsync, it is done automatically on remount >+ */ >+ int rc = subfs_remount_ro(sb); >+ if (rc) >+ supermount_error(sb, "failed to remount ro, error = %d", rc); >+ if (sbi->tray_lock != TRAY_LOCK_NEVER) >+ subfs_tray_unlock(sb); >+ } >+ >+ LEAVE(sb); >+} >+ >+int >+subfs_get_access(struct inode *inode, int rw) >+{ >+ struct super_block *sb = inode->i_sb; >+ int rc = 0; >+ >+ ENTER(sb); >+ >+ subfs_lock(sb); >+ if (is_inode_obsolete(inode)) >+ rc = -ESTALE; >+ else { >+ struct supermount_inode_info *sii = supermount_i(inode); >+ >+ if (rw) { >+ SUPERMOUNT_BUG_LOCKED_ON(sb, sii->writecount < 0); >+ rc = subfs_get_write_access(sb); >+ if (!rc) >+ sii->writecount++; >+ } else { >+ SUPERMOUNT_BUG_LOCKED_ON(sb, sii->readcount < 0); >+ subfs_get_read_access(sb); >+ sii->readcount++; >+ } >+ } >+ subfs_unlock(sb); >+ >+ LEAVE(sb, "rc=%d", rc); >+ >+ return rc; >+} >+ >+void >+subfs_put_access(struct inode *inode, int rw) >+{ >+ struct super_block *sb = inode->i_sb; >+ >+ ENTER(sb); >+ >+ subfs_lock(sb); >+ if (!is_inode_obsolete(inode)) { >+ struct supermount_inode_info *sii = supermount_i(inode); >+ >+ if (rw) { >+ SUPERMOUNT_BUG_LOCKED_ON(sb, sii->writecount <= 0); >+ sii->writecount--; >+ subfs_put_write_access(sb); >+ } else { >+ SUPERMOUNT_BUG_LOCKED_ON(sb, sii->readcount <= 0); >+ sii->readcount--; >+ subfs_put_read_access(sb); >+ } >+ >+ } >+ subfs_unlock(sb); >+ >+ LEAVE(sb); >+} >+ >+struct vfsmount * >+subfs_go_online(struct super_block *sb) >+{ >+ int rc; >+ struct vfsmount *mnt; >+ >+ ENTER(sb); >+ >+ subfs_lock(sb); >+ >+ rc = check_and_remount_subfs(sb); >+ mnt = ERR_PTR(rc); >+ if (!rc) >+ mnt = mntget(subfs_mnt(sb)); >+ subfs_unlock(sb); >+ >+ LEAVE(sb, "mnt=%p", mnt); >+ >+ return mnt; >+} >+ >+void >+subfs_go_offline(struct super_block *sb, struct vfsmount *mnt) >+{ >+ ENTER(sb); >+ >+ SUPERMOUNT_BUG_ON(!mnt); >+ mntput(mnt); >+ >+ LEAVE(sb); >+ >+} >+ >+struct vfsmount * >+subfs_prevent_umount(struct super_block *sb) >+{ >+ struct vfsmount *mnt; >+ >+ ENTER(sb); >+ >+ subfs_lock(sb); >+ mnt = subfs_mnt(sb); >+ if (mnt) >+ mnt = mntget(mnt); >+ subfs_unlock(sb); >+ >+ LEAVE(sb, "mnt=%p", mnt); >+ >+ return mnt; >+} >+ >+void >+subfs_allow_umount(struct super_block *sb, struct vfsmount *mnt) >+{ >+ ENTER(sb); >+ >+ SUPERMOUNT_BUG_ON(!mnt); >+ mntput(mnt); >+ >+ LEAVE(sb); >+ >+} >+ >+struct vfsmount * >+subfs_get_mnt(struct super_block *sb) >+{ >+ struct vfsmount *mnt = NULL; >+ >+ ENTER(sb); >+ >+ subfs_lock(sb); >+ if (subfs_is_mounted(sb)) >+ mnt = mntget(subfs_mnt(sb)); >+ subfs_unlock(sb); >+ >+ LEAVE(sb, "mnt=%p", mnt); >+ >+ return mnt; >+} >+ >+struct super_block * >+subfs_get_sb(struct super_block *sb) >+{ >+ struct super_block *subsb = NULL; >+ >+ ENTER(sb); >+ >+ subfs_lock(sb); >+ if (subfs_is_mounted(sb)) >+ subsb = subfs_sb(sb); >+ subfs_unlock(sb); >+ >+ LEAVE(sb, "subsb=%p", subsb); >+ >+ return subsb; >+} >+ >+/* >+ * contrary to its name this function deals with _supermount_ inode >+ */ >+void >+subfs_clear_inode(struct inode *inode) >+{ >+ struct super_block *sb = inode->i_sb; >+ struct supermount_inode_info *sii = supermount_i(inode); >+ >+ ENTER(sb, "inode=%p", inode); >+ >+ subfs_lock(sb); >+ >+ /* >+ * this is safe. If subfs has been unmounted, counters has been >+ * set to 0 already >+ */ >+ while(sii->writecount > 0) { >+ sii->writecount--; >+ subfs_put_write_access(sb); >+ } >+ >+ while(sii->readcount > 0) { >+ sii->readcount--; >+ subfs_put_read_access(sb); >+ } >+ >+ list_del(&sii->list); >+ >+ subfs_unlock(sb); >+ >+ LEAVE(sb, "inode=%p", inode); >+} >diff -urN linux-2.6.25.4.orig/fs/supermount/super.c linux-2.6.25.4/fs/supermount/super.c >--- linux-2.6.25.4.orig/fs/supermount/super.c 1970-01-01 03:00:00 +0300 >+++ linux-2.6.25.4/fs/supermount/super.c 2008-05-20 01:27:53 +0300 >@@ -0,0 +1,648 @@ >+/* >+ * linux/fs/supermount/super.c >+ * >+ * Original version: >+ * Copyright (C) 1995, 1997 >+ * Stephen Tweedie (sct@@dcs.ed.ac.uk) >+ * >+ * from >+ * >+ * linux/fs/minix/inode.c >+ * Copyright (C) 1991, 1992 Linus Torvalds >+ * >+ * and >+ * >+ * linux/fs/ext2/super.c >+ * Copyright (C) 1992, 1993, 1994, 1995 Remy Card >+ * >+ * Rewriten for kernel 2.2, 2.4. (C) 1999, 2000 Alexis Mikhailov >+ * (alexis@@abc.cap.ru) >+ * Rewritten for kernel 2.4.21 (C) 2003 Andrey Borzenkov >+ * (arvidjaar@@mail.ru) >+ * Rewritten for kernel 2.5.70 (C) 2003 Andrey Borzenkov >+ * (arvidjaar@@mail.ru) >+ * >+ * $Id: super.c,v 1.25.2.11 2004/01/14 19:24:10 bor Exp $ >+ */ >+ >+#define S_DBG_TRACE_CURRENT S_DBG_TRACE_SUPER >+ >+#include <linux/statfs.h> >+#include <linux/parser.h> >+#include "supermount.h" >+ >+/* >+ * vfat and msdos do not have any valididty checks for superblock and must >+ * be the last entries! >+ */ >+ >+static char *default_fs_types = "udf:iso9660:ext2:vfat:msdos"; >+ >+static struct super_operations supermount_sops; >+ >+/* ========================= helpers ================================= */ >+ >+static inline void >+supermount_update_inode(struct inode *superi, struct inode *subi) >+{ >+ struct super_block *sb = superi->i_sb; >+ >+ ENTER(sb, "superi=%p subi=%p", superi, subi); >+ >+ superi->i_ino = subi->i_ino; >+ superi->i_mode = subi->i_mode; >+ superi->i_uid = subi->i_uid; >+ superi->i_gid = subi->i_gid; >+ superi->i_nlink = subi->i_nlink; >+ superi->i_size = subi->i_size; >+ superi->i_atime = subi->i_atime; >+ superi->i_ctime = subi->i_ctime; >+ superi->i_mtime = subi->i_mtime; >+ //superi->i_blksize = subi->i_blksize; >+ superi->i_blocks = subi->i_blocks; >+ superi->i_rdev = subi->i_rdev; >+ superi->i_version++; >+ set_inode_flags(superi, subi); >+ >+ if (S_ISDIR(superi->i_mode)) { >+ superi->i_op = &supermount_dir_iops; >+ superi->i_fop = &supermount_dir_operations; >+ } else if (S_ISLNK(superi->i_mode)) { >+ superi->i_op = &supermount_symlink_iops; >+ superi->i_mapping = subi->i_mapping; >+ } else { >+ superi->i_op = &supermount_file_iops; >+ superi->i_fop = &supermount_file_operations; >+ superi->i_mapping = subi->i_mapping; >+ } >+ >+ LEAVE(sb, "superi=%p subi=%p", superi, subi); >+} >+ >+/* this is also called from subfs_mount to reinstantiate root inode */ >+void >+attach_subfs_inode(struct inode *inode, struct inode *subi) >+{ >+ struct super_block *sb = inode->i_sb; >+ struct supermount_sb_info *sbi = supermount_sbi(sb); >+ struct supermount_inode_info *sii; >+ >+ ENTER(sb, "inode=%p subi=%p", inode, subi); >+ >+ sii = supermount_i(inode); >+ if (!sii->inode) { >+ /* >+ * this can be run concurrently. It is executed under >+ * sbi->sem so only one task would actually instantate >+ * inode. See namei.c:prepare_inode >+ * Another user is subfs.c:subfs_real_mount2 >+ */ >+ sii->inode = igrab(subi); >+ supermount_update_inode(inode, subi); >+ list_add(&sii->list, &sbi->s_inodes); >+ } else >+ SUPERMOUNT_BUG_LOCKED_ON(sb, sii->inode != subi); >+ >+ LEAVE(sb, "inode=%p subi=%p", inode, subi); >+} >+ >+char * >+strdup(const char *val) >+{ >+ char *tmp; >+ tmp = kmalloc(1 + strlen(val), GFP_KERNEL); >+ if (tmp) >+ strcpy(tmp, val); >+ return tmp; >+} >+ >+static struct supermount_sb_info * >+create_sbi(struct super_block *sb) >+{ >+ struct supermount_sb_info *sbi = kmalloc(sizeof (*sbi), GFP_KERNEL); >+ >+ if (!sbi) >+ return NULL; >+ >+ memset(sbi, 0, sizeof (*sbi)); >+ >+ sbi->s_undermount = NULL; >+ sbi->s_type = sbi->devname = sbi->s_data = NULL; >+ INIT_LIST_HEAD(&sbi->s_inodes); >+ INIT_LIST_HEAD(&sbi->s_files); >+ INIT_LIST_HEAD(&sbi->s_dentries); >+ init_MUTEX(&sbi->sem); >+ sbi->host = sb; >+ sbi->tray_lock = TRAY_LOCK_ONWRITE; >+ sbi->rw = !(sb->s_flags & MS_RDONLY); >+ >+ return sbi; >+} >+ >+static void >+free_sbi(struct supermount_sb_info *sbi) >+{ >+ if (sbi->s_type && sbi->s_type != default_fs_types) >+ kfree(sbi->s_type); >+ if (sbi->devname) >+ kfree(sbi->devname); >+ if (sbi->s_data) >+ kfree(sbi->s_data); >+ kfree(sbi); >+} >+ >+enum { >+ Opt_fs, Opt_fs_auto, Opt_dev, Opt_debug, Opt_debug_old, Opt_subfsopts, >+ Opt_tray_lock_always, Opt_tray_lock_never, Opt_tray_lock_onwrite, >+ Opt_ignore, Opt_err >+}; >+ >+static match_table_t tokens = { >+ {Opt_fs_auto, "fs=auto"}, >+ {Opt_fs, "fs=%s"}, >+ {Opt_dev, "dev=%s"}, >+ {Opt_debug, "debug=%u"}, >+ {Opt_debug_old, "debug"}, >+ {Opt_subfsopts, "--"}, >+ {Opt_tray_lock_always, "tray_lock=always"}, >+ {Opt_tray_lock_never, "tray_lock=never"}, >+ {Opt_tray_lock_onwrite, "tray_lock=onwrite"}, >+ {Opt_ignore, "no_tray_lock"}, >+ {Opt_ignore, "fail_statfs_until_mount"}, >+ {Opt_err, NULL} >+}; >+ >+static int >+parse_options(char *options, struct super_block *sb) >+{ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); >+ int rc; >+ char *p; >+ >+ if (!options) >+ return 0; >+ >+ while ((p = strsep(&options, ",")) != NULL) { >+ int token; >+ substring_t args[MAX_OPT_ARGS]; >+ int n; >+ >+ /* empty option means start of subfs options */ >+ if (!*p) >+ goto copy_subfs_options; >+ >+ token = match_token(p, tokens, args); >+ switch(token) { >+ case Opt_fs: >+ sbi->s_type = match_strdup(&args[0]); >+ if (!sbi->s_type) >+ return -ENOMEM; >+ break; >+ case Opt_fs_auto: >+ sbi->s_type = default_fs_types; >+ break; >+ case Opt_dev: >+ sbi->devname = match_strdup(&args[0]); >+ if (!sbi->devname) >+ return -ENOMEM; >+ break; >+ case Opt_debug: >+ if ((rc = match_int(&args[0], &n))) >+ return rc; >+ sbi->s_debug = n; >+ break; >+ case Opt_debug_old: >+ sbi->s_debug = S_DBG_DEBUG; >+ break; >+ case Opt_tray_lock_always: >+ sbi->tray_lock = TRAY_LOCK_ALWAYS; >+ break; >+ case Opt_tray_lock_onwrite: >+ sbi->tray_lock = TRAY_LOCK_ONWRITE; >+ break; >+ case Opt_tray_lock_never: >+ sbi->tray_lock = TRAY_LOCK_NEVER; >+ break; >+ case Opt_subfsopts: >+copy_subfs_options: >+ /* mount may have removed options after -- */ >+ if (options && *options) { >+ sbi->s_data = strdup(options); >+ if (!sbi->s_data) >+ return -ENOMEM; >+ } >+ return 0; >+ break; >+ case Opt_ignore: >+ break; >+ default: >+ supermount_error(sb, "Unrecognized mount option \"%s\"", >+ p); >+ return -EINVAL; >+ break; >+ } >+ } >+ >+ return 0; >+} >+ >+void >+supermount_init_root_inode(struct inode *inode) >+{ >+ inode->i_mode = 0777 | S_IFDIR; >+ inode->i_uid = current->fsuid; >+ inode->i_gid = current->fsgid; >+ //inode->i_blksize = inode->i_sb->s_blocksize; >+ inode->i_rdev = MKDEV(UNNAMED_MAJOR, 0); >+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; >+ inode->i_size = 0; >+ inode->i_blocks = 0; >+ inode->i_ino = 0; >+ inode->i_nlink = 0; >+ >+ set_inode_flags(inode, 0); >+ >+ inode->i_op = &supermount_dir_iops; >+ inode->i_fop = &supermount_dir_operations; >+} >+ >+static struct inode * >+supermount_root_inode(struct super_block *sb) >+{ >+ struct inode *inode = new_inode(sb); >+ >+ ENTER(sb); >+ >+ if (inode) { >+ inode->i_version = 0; >+ supermount_init_root_inode(inode); >+ } >+ >+ LEAVE(sb, "inode=%p", inode); >+ >+ return inode; >+} >+ >+/* ========================== used in fstype declaration ================*/ >+ >+/* read_super: the main mount() entry point into the VFS layer. */ >+static int >+supermount_read_super(struct super_block *sb, void *data, int silent) >+{ >+ struct inode *root_inode; >+ struct dentry *root; >+ struct supermount_sb_info *sbi = create_sbi(sb); >+ int rc = -ENOMEM; >+ >+ if (!sbi) >+ goto fail_no_memory; >+ >+ sb->s_fs_info = sbi; >+ >+ rc = parse_options((char *) data, sb); >+ if (rc) >+ goto fail_parsing; >+ >+ rc = -EINVAL; >+ if (!sbi->devname) { >+ supermount_error(sb, "no dev=<device> option"); >+ goto fail_parsing; >+ } >+ >+ if (!sbi->s_type) { >+ sbi->s_type = default_fs_types; >+ supermount_warning(sb, "no fs=<filesystem> option, assuming fs=auto"); >+ } >+ >+ rc = -ENOMEM; >+ >+ sb->s_blocksize = 1024; >+ sb->s_blocksize_bits = 10; >+ sb->s_magic = SUPERMOUNT_SUPER_MAGIC; >+ sb->s_op = &supermount_sops; >+ >+ root_inode = supermount_root_inode(sb); >+ if (!root_inode) >+ goto fail_allocating_root_inode; >+ >+ root = d_alloc_root(root_inode); >+ if (!root) >+ goto fail_allocating_root_dentry; >+ if (init_dentry_info(root)) >+ goto fail_init_root_info; >+ >+ sb->s_root = root; >+ >+ supermount_proc_insert(sbi); >+ >+ return 0; >+ >+fail_init_root_info: >+ dput(root); >+fail_allocating_root_dentry: >+ iput(root_inode); >+fail_parsing: >+fail_allocating_root_inode: >+ free_sbi(sbi); >+ >+fail_no_memory: >+ return rc; >+} >+ >+struct super_block *supermount_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, struct vfsmount *mnt) >+{ >+ return get_sb_nodev(fs_type, flags, data, supermount_read_super,mnt); >+} >+ >+/* ======================= super_operations methods ==================== */ >+ >+static struct inode * >+supermount_alloc_inode(struct super_block *sb) >+{ >+ struct supermount_inode_info *sii = kmalloc(sizeof(*sii), GFP_KERNEL); >+ struct inode *inode = 0; >+ >+ ENTER(sb); >+ >+ if (sii) { >+ INIT_LIST_HEAD(&sii->list); >+ sii->inode = 0; >+ sii->readcount = 0; >+ sii->writecount = 0; >+ inode = &sii->vfs_inode; >+ inode_init_once(inode); >+ } >+ >+ LEAVE(sb, "inode=%p", inode); >+ >+ return inode; >+} >+ >+static void >+supermount_destroy_inode(struct inode *inode) >+{ >+ struct super_block *sb = inode->i_sb; >+ struct supermount_inode_info *sii = supermount_i(inode); >+ >+ ENTER(sb, "inode=%p", inode); >+ >+ kfree(sii); >+ >+ ENTER(sb, "inode=%p", inode); >+} >+ >+/* >+ * FIXME >+ * I am still unsure if (or why) this functions is needed; it is likely >+ * to go away. So far the only _real_ user seems to be knfsd. >+ */ >+static void >+supermount_write_inode(struct inode *inode, int sync) >+{ >+ struct super_block *sb = inode->i_sb; >+ struct super_block *subsb; >+ struct inode *subi; >+ struct vfsmount *submnt; >+ >+ ENTER(sb); >+ >+ if (subfs_check_disk_change(sb)) >+ goto out; >+ >+ submnt = subfs_prevent_umount(sb); >+ if (!submnt) >+ goto out; >+ >+ subi = get_subfs_inode(inode); >+ if (IS_ERR(subi)) >+ goto allow_umount; >+ >+ subsb = subfs_get_sb(sb); >+ if (!sb) >+ goto put_subi; >+ >+ if (subsb->s_op && subsb->s_op->write_inode) { >+ if (subfs_get_access(inode, 1)) >+ goto put_subi; >+ >+ write_inode_now(subi, sync); >+ >+ subfs_put_access(inode, 1); >+ } >+put_subi: >+ iput(subi); >+allow_umount: >+ subfs_allow_umount(sb, submnt); >+out: >+ LEAVE(sb); >+ >+ return; >+} >+ >+/* >+ * FIXME >+ * subfs_umount has no business here >+ * it should be moved into umount_begin; in this case force umount will >+ * free subfs (and give false No medium as error ... oh, well) >+ */ >+static void >+supermount_put_super(struct super_block *sb) >+{ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); >+ >+ ENTER(sb); >+ >+ subfs_lock(sb); >+ if (subfs_is_mounted(sb)) >+ subfs_umount(sb, SUBFS_UMNT_NORMAL); >+ subfs_unlock(sb); >+ >+ supermount_proc_remove(sbi); >+ >+ LEAVE(sb); >+ >+ sb->s_fs_info = 0; >+ free_sbi(sbi); >+} >+ >+/* >+ * The following has to be done in two steps (unfortunately) >+ * First make sure iput(subi) is run just once - and then put >+ * read/write access and finally remove inode from list >+ * >+ * We can't do it in one shot because removing inode from list >+ * will remove also possibility to mark it stale. It has been >+ * introduced by me :( removing generation numbers. OTOH so far >+ * it is the only place that needs extra treatment because of it. >+ */ >+static void >+supermount_clear_inode(struct inode *inode) >+{ >+ struct supermount_inode_info *sii = supermount_i(inode); >+ struct super_block *sb = inode->i_sb; >+ struct inode *subi; >+ struct vfsmount *mnt; >+ >+ ENTER(sb, "inode=%p", inode); >+ >+ mnt = subfs_prevent_umount(sb); >+ if (!mnt) >+ goto out; >+ >+ subfs_lock(sb); >+ subi = sii->inode; >+ sii->inode = 0; >+ subfs_unlock(sb); >+ >+ >+ if (subi) { >+ /* >+ * we used to check for subi->i_count != 1 here. >+ * This does not actually work - it is quite possible >+ * that inode has been looked up while we were in this >+ * function. So just ignore it (the worst thing that >+ * may happen is that subfs cannot be remounted) >+ */ >+ >+ iput(subi); >+ >+ } >+ subfs_allow_umount(sb, mnt); >+ >+ /* >+ * This does not has much to do with subfs but I did not want >+ * to export __ functions >+ */ >+ subfs_clear_inode(inode); >+out: >+ LEAVE(sb, "inode=%p", inode); >+} >+/* >+ * FIXME why it is needed? >+ */ >+ >+static void >+supermount_write_super(struct super_block *sb) >+{ >+ struct vfsmount *mnt; >+ >+ ENTER(sb); >+ >+ if (subfs_check_disk_change(sb)) >+ goto out; >+ >+ if (subfs_is_rw(sb)) { >+ struct super_block *subsb; >+ >+ mnt = subfs_prevent_umount(sb); >+ if (!mnt) >+ goto out; >+ >+ subsb = subfs_get_sb(sb); >+ if (subsb && subsb->s_op && subsb->s_op->write_super) >+ subsb->s_op->write_super(subsb); >+ >+ subfs_allow_umount(sb, mnt); >+ } >+ >+out: >+ LEAVE(sb); >+ >+ return; >+} >+ >+static int >+//supermount_statfs(struct super_block *sb, struct kstatfs *buf) >+supermount_statfs(struct dentry *dentry, struct kstatfs *buf) >+{ >+ struct super_block *sb=dentry->d_sb; >+ int rc = 0; >+ struct super_block *subsb; >+ struct vfsmount *mnt; >+ >+ ENTER(sb); >+ >+ (void)subfs_check_disk_change(sb); >+ >+ mnt = subfs_prevent_umount(sb); >+ if (!mnt) >+ goto out; >+ >+ subsb = subfs_get_sb(sb); >+ if (subsb && subsb->s_op && subsb->s_op->statfs) >+ rc = subsb->s_op->statfs(dentry, buf); >+ if (!rc) >+ buf->f_type = SUPERMOUNT_SUPER_MAGIC; >+ >+ subfs_allow_umount(sb, mnt); >+out: >+ LEAVE(sb, "rc=%d", rc); >+ >+ return rc; >+} >+ >+static int >+supermount_remount_fs(struct super_block *sb, int *flags, char *data) >+{ >+ return -ENOSYS; >+} >+ >+/* >+ * based on fs/ntfs/inode.c:ntfs_show_options >+ */ >+static int >+supermount_show_options(struct seq_file *sf, struct vfsmount *mnt) >+{ >+ struct supermount_sb_info *sbi = supermount_sbi(mnt->mnt_sb); >+ >+ seq_printf(sf, ",dev=%s", sbi->devname); >+ seq_puts(sf, ",fs="); >+ if (sbi->s_type == default_fs_types) >+ seq_puts(sf, "auto"); >+ else >+ seq_puts(sf, sbi->s_type); >+ seq_puts(sf, ",tray_lock="); >+ if (sbi->tray_lock == TRAY_LOCK_ALWAYS) >+ seq_puts(sf, "always"); >+ else if (sbi->tray_lock == TRAY_LOCK_NEVER) >+ seq_puts(sf, "never"); >+ else if (sbi->tray_lock == TRAY_LOCK_ONWRITE) >+ seq_puts(sf, "onwrite"); >+ else >+ SUPERMOUNT_BUG_ON(1); >+ >+ if (sbi->s_debug) >+ seq_printf(sf, ",debug=0x%lx", sbi->s_debug); >+ if (sbi->s_data) >+ seq_printf(sf, ",--,%s", sbi->s_data); >+ return 0; >+} >+ >+/* >+ * ->read_inode: not needed >+ * ->dirty_inode: probably not needed. It appears VFS layer never >+ * calls mark_inode_dirty itself; exception is >+ * UPDATE_ATIME and it is already handled specially >+ * FIXME >+ * It may be needed for knfsd and similar >+ * ->put_inode: not needed >+ * ->delete_inode: not needed >+ * ->write_super: FIXME not needed, we do not have any backing store >+ * ->sync_fs: not needed >+ * ->write_super_lockfs:not needed >+ * ->unlockfs: not needed >+ * ->umount_begin: TODO maybe to move subfs_umount aways from put_super >+ * ->fh_to_dentry: TODO may be >+ * ->dentry_to_fh: TODO may be >+ */ >+static struct super_operations supermount_sops = { >+ .alloc_inode = supermount_alloc_inode, >+ .destroy_inode = supermount_destroy_inode, >+ .write_inode = supermount_write_inode, >+ .clear_inode = supermount_clear_inode, >+ .put_super = supermount_put_super, >+ .write_super = supermount_write_super, >+ .statfs = supermount_statfs, >+ .remount_fs = supermount_remount_fs, >+ .show_options = supermount_show_options, >+}; >diff -urN linux-2.6.25.4.orig/fs/supermount/supermount.h linux-2.6.25.4/fs/supermount/supermount.h >--- linux-2.6.25.4.orig/fs/supermount/supermount.h 1970-01-01 03:00:00 +0300 >+++ linux-2.6.25.4/fs/supermount/supermount.h 2008-05-20 01:27:53 +0300 >@@ -0,0 +1,508 @@ >+#ifndef _SUPERMOUNT_I_H >+#define _SUPERMOUNT_I_H >+ >+/* >+ * $Id: supermount.h,v 1.42.2.29 2004/01/18 18:44:25 bor Exp $ >+ */ >+ >+#include <linux/module.h> >+#include <linux/version.h> >+ >+#include <linux/slab.h> >+#include <linux/sched.h> >+#include <linux/errno.h> >+#include <linux/fs.h> >+#include <linux/list.h> >+#include <linux/file.h> >+#include <linux/mm.h> >+#include <linux/poll.h> >+#include <linux/smp_lock.h> >+#include <linux/proc_fs.h> >+#include <linux/seq_file.h> >+#include <linux/dnotify.h> >+#include <linux/mount.h> >+#include <linux/moduleparam.h> >+#include <linux/genhd.h> >+#include <linux/namei.h> >+#include <linux/supermount_media.h> >+ >+#ifdef CONFIG_SUPERMOUNT_DEBUG >+#define SUPERMOUNT_DEBUG >+#endif >+/* >+ * The supermount superblock magic number >+ */ >+ >+#define SUPERMOUNT_SUPER_MAGIC 0x9fa1 >+#define SUPERMOUNT_VERSION "2.0.4" >+ >+#define S_DBG_DEBUG 0x001 >+#define S_DBG_TRACE_DENTRY 0x002 >+#define S_DBG_TRACE_FILE 0x004 >+#define S_DBG_TRACE_FILEMAP 0x008 >+#define S_DBG_TRACE_MEDIACTL 0x010 >+#define S_DBG_TRACE_NAMEI 0x020 >+#define S_DBG_TRACE_SUBFS 0x040 >+#define S_DBG_TRACE_SUPER 0x080 >+ >+/* >+ * The subfs umount reason >+ */ >+#define SUBFS_UMNT_NORMAL 0 /* normal umount */ >+#define SUBFS_UMNT_MEDIA 1 /* media change detected */ >+#define SUBFS_UMNT_USER 2 /* user request */ >+ >+/* >+ * When to lock media >+ */ >+#define TRAY_LOCK_NEVER 0 >+#define TRAY_LOCK_ONWRITE 1 >+#define TRAY_LOCK_ALWAYS 2 >+ >+extern struct file_system_type supermount_fs_type; >+ >+#ifdef SUPERMOUNT_DEBUG >+ >+#define SUPERMOUNT_BUG_ON(x) BUG_ON(x) >+#define SUPERMOUNT_BUG_LOCKED_ON(sb, x) \ >+do { \ >+ if (x) { \ >+ subfs_unlock(sb); \ >+ BUG(); \ >+ } \ >+} while (0) >+ >+#define supermount_debug(sb, args...) \ >+do { \ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); \ >+ char *dev = sbi->devname ? sbi->devname : "unknown"; \ >+\ >+ if (supermount_dbg(sb, S_DBG_DEBUG)) { \ >+ printk("%sSUPERMOUNT DEBUG [dev=%s] <%s:%d> ", \ >+ KERN_DEBUG, dev, __FUNCTION__, __LINE__); \ >+ printk(args); \ >+ printk("\n"); \ >+ } \ >+} while(0) >+ >+#define ENTER(sb, args...) \ >+do { \ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); \ >+ char *dev = sbi->devname ? sbi->devname : "unknown"; \ >+\ >+ if (supermount_dbg(sb, S_DBG_TRACE_CURRENT)) { \ >+ printk("%sSUPERMOUNT TRACE [dev=%s] PID=%lu ENTER %s", \ >+ KERN_DEBUG, dev, (unsigned long)current->pid, \ >+ __FUNCTION__); \ >+ printk(" " args); \ >+ printk("\n"); \ >+ } \ >+} while (0) >+ >+#define LEAVE(sb, args...) \ >+do { \ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); \ >+ char *dev = sbi->devname ? sbi->devname : "unknown"; \ >+\ >+ if (supermount_dbg(sb, S_DBG_TRACE_CURRENT)) { \ >+ printk("%sSUPERMOUNT TRACE [dev=%s] PID=%lu LEAVE %s", \ >+ KERN_DEBUG, dev, (unsigned long)current->pid, \ >+ __FUNCTION__); \ >+ printk(" " args); \ >+ printk("\n"); \ >+ } \ >+} while (0) >+ >+#else /* SUPERMOUNT_DEBUG */ >+#define supermount_debug(f, a...) /**/ >+#define SUPERMOUNT_BUG_ON(x) do { } while (0) >+#define SUPERMOUNT_BUG_LOCKED_ON(sb, x) do { } while (0) >+ >+#define ENTER(sb, args...) do { (void)sb; } while (0) >+#define LEAVE(sb, args...) do { (void)sb; } while (0) >+#endif /* SUPERMOUNT_DEBUG */ >+ >+#define supermount_warning(sb, ...) \ >+do { \ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); \ >+ char *dev = sbi->devname ? sbi->devname : "unknown"; \ >+\ >+ printk("%sSUPERMOUNT WARNING [dev=%s] ", KERN_WARNING, dev); \ >+ printk(__VA_ARGS__); \ >+ printk("\n"); \ >+} while(0) >+ >+#define supermount_error(sb, ...) \ >+do { \ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); \ >+ char *dev = sbi->devname ? sbi->devname : "unknown"; \ >+\ >+ printk("%sSUPERMOUNT ERROR [dev=%s] ", KERN_ERR, dev); \ >+ printk(__VA_ARGS__); \ >+ printk("\n"); \ >+} while(0) >+ >+/* >+ * The following is drived from fs/inode.c:update_atime() >+ */ >+/*#define NEED_WRITE_ATIME(inode) \ >+ (!(inode->i_flags & S_NOATIME) || \ >+ (inode->i_sb->s_flags & MS_NOATIME) || \ >+ ((inode->i_sb->s_flags & MS_NODIRATIME) && S_ISDIR(inode->i_mode))) >+*/ >+#define NEED_WRITE_ATIME(inode) \ >+ (!((inode->i_flags & S_NOATIME) || \ >+ ((inode->i_sb->s_flags & MS_NODIRATIME) && S_ISDIR(inode->i_mode)) || \ >+ IS_RDONLY(inode))) >+ >+/* >+#define NEED_WRITE_ATIME(inode) \ >+ (!(IS_NOATIME(inode) || \ >+ (IS_NODIRATIME(inode) && S_ISDIR(inode->i_mode)) || \ >+ IS_RDONLY(inode))) >+*/ >+ >+/* >+ * supermount super-block data in memory >+ */ >+struct supermount_sb_info; >+struct supermount_sb_info { >+ /* == options == */ >+ unsigned long s_debug; /* debug flags S_DBG_* */ >+ char *s_type; /* Type of fs to be sub-mounted */ >+ char *devname; /* Where to mount the subfs from */ >+ char *s_data; /* Data to pass when mounting subfs */ >+ /* == end of options == */ >+ struct vfsmount *s_undermount; >+ /* Mount point for subfs */ >+ int readcount; /* Refcount of read access >+ on the filesystem */ >+ int writecount; /* Refcount of write access >+ on the filesystem */ >+ int lockcount; /* Refcount of requests to lock tray */ >+ struct list_head s_inodes; >+ /* list of active inodes */ >+ struct list_head s_files; >+ /* list of active files */ >+ struct list_head s_dentries; >+ /* list of active dentries */ >+ struct semaphore sem; >+ struct super_block *host; >+ /* needed for procfs support */ >+ struct supermount_sb_info *next; >+ /* list of all supermount fs */ >+ unsigned int tray_lock:2; >+ unsigned int disabled:1; >+ unsigned int rw:1; >+}; >+ >+static inline struct supermount_sb_info * >+supermount_sbi(struct super_block *sb) >+{ >+ struct supermount_sb_info *sbi; >+ BUG_ON(!sb); >+ BUG_ON(sb->s_type != &supermount_fs_type); >+ >+ sbi = (struct supermount_sb_info *) (sb->s_fs_info); >+ BUG_ON(!sbi); >+ >+ return sbi; >+} >+ >+static inline struct vfsmount * >+subfs_mnt(struct super_block *sb) >+{ >+ return supermount_sbi(sb)->s_undermount; >+} >+ >+static inline struct super_block * >+subfs_sb(struct super_block *sb) >+{ >+ struct vfsmount *mnt = subfs_mnt(sb); >+ >+ if (mnt) return mnt->mnt_sb; >+ >+ return 0; >+} >+ >+/* this is expected to run under sb->sem */ >+static inline int >+subfs_is_mounted(struct super_block *sb) >+{ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); >+ >+ return sbi->s_undermount != 0; >+} >+ >+/* this is expected to run under sb->sem */ >+static inline int >+subfs_is_busy(struct super_block *sb) >+{ >+ struct vfsmount *mnt = subfs_mnt(sb); >+ >+ /* >+ * In "normal" case mnt_count is 2. But we currently do >+ * not insert subfs into task namespace so count is 1 >+ * FIXME >+ * This also means we do not do_umount i.e. do not run >+ * either umount_begin or DQUOT_OFF for subfs. >+ */ >+ if (mnt) return atomic_read(&mnt->mnt_count) > 1; >+ >+ return 0; >+} >+ >+/* this is expected to run under sb->sem */ >+static inline int >+subfs_is_rw(struct super_block *sb) >+{ >+ struct super_block *subsb; >+ >+ if (!subfs_is_mounted(sb)) >+ return 0; >+ >+ subsb = subfs_sb(sb); >+ return !(subsb->s_flags & MS_RDONLY); >+} >+ >+static inline void >+subfs_lock(struct super_block *sb) >+{ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); >+ >+ down(&sbi->sem); >+} >+ >+static inline void >+subfs_unlock(struct super_block *sb) >+{ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); >+ >+ up(&sbi->sem); >+} >+ >+/* >+ * query debug flags set >+ */ >+static inline int supermount_dbg(struct super_block *sb, unsigned long flags) >+{ >+ struct supermount_sb_info *sbi = supermount_sbi(sb); >+ >+ return sbi->s_debug & flags; >+} >+ >+/* >+ * supermount inode info >+ */ >+ >+struct supermount_inode_info { >+ struct list_head list; >+ struct inode *inode; /* subfs inode */ >+ int readcount; >+ int writecount; >+ struct inode vfs_inode; /* superfs inode */ >+}; >+ >+static inline int >+is_inode_supermounted(struct inode *inode) >+{ >+ return inode && inode->i_sb && inode->i_sb->s_type == &supermount_fs_type; >+} >+ >+static inline struct supermount_inode_info * >+supermount_i(struct inode *inode) >+{ >+ SUPERMOUNT_BUG_ON(!is_inode_supermounted(inode)); >+ >+ return list_entry(inode, struct supermount_inode_info, vfs_inode); >+} >+ >+static inline int >+is_inode_obsolete(struct inode *inode) >+{ >+ struct supermount_inode_info *sii = supermount_i(inode); >+ >+ return sii->inode == 0; >+} >+ >+static inline void >+supermount_list_add_inode(struct inode *inode) >+{ >+ struct supermount_inode_info *sii = supermount_i(inode); >+ >+ list_add(&(sii->list), &(supermount_sbi(inode->i_sb)->s_inodes)); >+} >+ >+/* >+ * FIXME >+ * Should we propagate all flags? *_QUOTA looks very possible candidate >+ * We can't just assign them because other flags may be set by VFS >+ */ >+#define SMNT_INODE_FLAGS (S_IMMUTABLE|S_NOATIME|S_APPEND|S_SYNC) >+static inline void >+set_inode_flags(struct inode *inode, struct inode *subi) >+{ >+ inode->i_flags &= ~SMNT_INODE_FLAGS; >+ if (subi) >+ inode->i_flags |= (subi->i_flags & SMNT_INODE_FLAGS); >+} >+ >+/* >+ * supermount dentry info >+ */ >+ >+struct supermount_dentry_info { >+ struct list_head list; >+ struct dentry *dentry; >+ struct dentry *host; >+}; >+ >+static inline int >+is_dentry_supermounted(struct dentry *dentry) >+{ >+ return (dentry && dentry->d_sb && dentry->d_sb->s_type == &supermount_fs_type); >+} >+ >+static inline struct supermount_dentry_info * >+supermount_d(struct dentry *dentry) >+{ >+ SUPERMOUNT_BUG_ON(!is_dentry_supermounted(dentry)); >+ SUPERMOUNT_BUG_ON(!dentry->d_fsdata); >+ >+ return (struct supermount_dentry_info *)dentry->d_fsdata; >+} >+ >+static inline int >+is_dentry_obsolete(struct dentry *dentry) >+{ >+ struct supermount_dentry_info *sdi = supermount_d(dentry); >+ >+ return sdi->dentry == 0; >+} >+ >+/* >+ * Supermount file info >+ */ >+ >+struct supermount_file_info { >+ struct list_head list; >+ struct file * host; >+ struct file *file; >+ pid_t owner; >+ struct vm_operations_struct *vm_ops; >+ unsigned int fake:1; >+}; >+ >+static inline int >+is_file_supermounted(struct file *file) >+{ >+ return file && file->f_dentry && is_dentry_supermounted(file->f_dentry); >+} >+ >+static inline struct supermount_file_info * >+supermount_f(struct file *file) >+{ >+ SUPERMOUNT_BUG_ON(!is_file_supermounted(file)); >+ SUPERMOUNT_BUG_ON(!file->f_supermount); >+ >+ return file->f_supermount; >+} >+ >+static inline int >+is_file_obsolete(struct file *file) >+{ >+ struct supermount_file_info *sfi = supermount_f(file); >+ >+ return sfi->file == NULL; >+} >+ >+static inline int >+is_file_fake(struct file *file) >+{ >+ struct supermount_file_info *sfi = supermount_f(file); >+ >+ return sfi->fake; >+} >+ >+/* dentry.c */ >+extern rwlock_t d_compare_lock; >+extern struct dentry_operations supermount_dops; >+extern int init_dentry_info(struct dentry *); >+extern void attach_subfs_dentry(struct dentry *, struct dentry *); >+extern struct dentry *get_subfs_dentry(struct dentry *dentry); >+ >+/* file.c */ >+extern struct file_operations supermount_dir_operations; >+extern struct file_operations supermount_file_operations; >+ >+/* filemap.c */ >+extern struct vm_operations_struct supermount_vm_ops; >+extern int supermount_file_mmap(struct file *, struct vm_area_struct *); >+extern struct file *get_subfs_file(struct file*); >+ >+/* mediactl.c */ >+extern void supermount_mediactl(struct super_block *, int, int); >+static inline void >+supermount_lock_door(struct super_block *sb) >+{ >+ supermount_mediactl(sb, MEDIA_LOCK, 1); >+} >+static inline void >+supermount_unlock_door(struct super_block *sb) >+{ >+ supermount_mediactl(sb, MEDIA_UNLOCK, 1); >+} >+ >+static inline void >+mark_media_supermounted(struct super_block *sb) >+{ >+ supermount_mediactl(sb, SUPERMOUNT_INC_COUNT, 0); >+ supermount_mediactl(sb, MEDIA_UNLOCK, 0); >+} >+ >+static inline void >+unmark_media_supermounted(struct super_block *sb) >+{ >+ supermount_mediactl(sb, SUPERMOUNT_DEC_COUNT, 0); >+} >+ >+/* namei.c */ >+extern struct inode_operations supermount_dir_iops; >+extern struct inode_operations supermount_file_iops; >+extern struct inode_operations supermount_symlink_iops; >+extern struct inode *create_supermount_inode(struct super_block *); >+extern struct inode *get_subfs_inode(struct inode *inode); >+ >+/* proc.c */ >+#ifdef CONFIG_PROC_FS >+extern void supermount_proc_register(void); >+extern void supermount_proc_unregister(void); >+extern void supermount_proc_insert(struct supermount_sb_info *); >+extern void supermount_proc_remove(struct supermount_sb_info *); >+#else >+#define supermount_proc_register() do { } while(0) >+#define supermount_proc_unregister() do { } while(0) >+#define supermount_proc_insert(sbi) do { } while(0) >+#define supermount_proc_remove(sbi) do { } while(0) >+#endif >+ >+/* subfs.c */ >+extern void subfs_umount(struct super_block *sb, int); >+extern struct vfsmount *subfs_go_online(struct super_block *); >+extern void subfs_go_offline(struct super_block *, struct vfsmount *); >+extern int subfs_get_access(struct inode *, int); >+extern void subfs_put_access(struct inode *, int); >+extern int subfs_check_disk_change(struct super_block *); >+extern struct vfsmount *subfs_prevent_umount(struct super_block *); >+extern void subfs_allow_umount(struct super_block *, struct vfsmount *); >+extern struct vfsmount *subfs_get_mnt(struct super_block *sb); >+extern struct super_block *subfs_get_sb(struct super_block *sb); >+extern void subfs_clear_inode(struct inode *); >+ >+/* super.c */ >+extern struct super_block *supermount_get_sb(struct file_system_type *, int, const char *, void *,struct vfsmount *mnt); >+extern void attach_subfs_inode(struct inode *, struct inode *); >+extern void supermount_init_root_inode(struct inode *); >+extern char *strdup(const char *); >+ >+#endif /* _SUPERMOUNT_I_H */ >diff -urN linux-2.6.25.4.orig/fs/udf/super.c linux-2.6.25.4/fs/udf/super.c >--- linux-2.6.25.4.orig/fs/udf/super.c 2008-04-18 01:20:10 +0300 >+++ linux-2.6.25.4/fs/udf/super.c 2008-05-20 01:27:53 +0300 >@@ -368,7 +368,7 @@ > Opt_gid, Opt_uid, Opt_umask, Opt_session, Opt_lastblock, > Opt_anchor, Opt_volume, Opt_partition, Opt_fileset, > Opt_rootdir, Opt_utf8, Opt_iocharset, >- Opt_err, Opt_uforget, Opt_uignore, Opt_gforget, Opt_gignore >+ Opt_ignore, Opt_err, Opt_uforget, Opt_uignore, Opt_gforget, Opt_gignore > }; > > static match_table_t tokens = { >@@ -397,6 +397,10 @@ > {Opt_rootdir, "rootdir=%u"}, > {Opt_utf8, "utf8"}, > {Opt_iocharset, "iocharset=%s"}, >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+ /* Silently ignore NLS option */ >+ {Opt_ignore, "codepage"}, >+#endif > {Opt_err, NULL} > }; > >@@ -687,6 +691,8 @@ > udf_debug("ISO9660 Volume Descriptor Set " > "Terminator found\n"); > break; >+ case Opt_ignore: >+ break; > default: > udf_debug("ISO9660 VRS (%u) found\n", > vsd->structType); >diff -urN linux-2.6.25.4.orig/include/linux/cdrom.h linux-2.6.25.4/include/linux/cdrom.h >--- linux-2.6.25.4.orig/include/linux/cdrom.h 2008-04-18 01:20:17 +0300 >+++ linux-2.6.25.4/include/linux/cdrom.h 2008-05-20 01:27:53 +0300 >@@ -992,6 +992,9 @@ > extern int cdrom_ioctl(struct file *file, struct cdrom_device_info *cdi, > struct inode *ip, unsigned int cmd, unsigned long arg); > extern int cdrom_media_changed(struct cdrom_device_info *); >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+extern int cdrom_mediactl(struct cdrom_device_info *, struct block_device *, int, int); >+#endif > > extern int register_cdrom(struct cdrom_device_info *cdi); > extern int unregister_cdrom(struct cdrom_device_info *cdi); >diff -urN linux-2.6.25.4.orig/include/linux/genhd.h linux-2.6.25.4/include/linux/genhd.h >--- linux-2.6.25.4.orig/include/linux/genhd.h 2008-04-18 01:20:17 +0300 >+++ linux-2.6.25.4/include/linux/genhd.h 2008-05-20 01:27:53 +0300 >@@ -141,6 +141,10 @@ > struct disk_stats dkstats; > #endif > struct work_struct async_notify; >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+ int scount; /* number of supermounted partitions */ >+#endif >+ > }; > > /* >diff -urN linux-2.6.25.4.orig/include/linux/supermount_media.h linux-2.6.25.4/include/linux/supermount_media.h >--- linux-2.6.25.4.orig/include/linux/supermount_media.h 1970-01-01 03:00:00 +0300 >+++ linux-2.6.25.4/include/linux/supermount_media.h 2008-05-20 01:27:53 +0300 >@@ -0,0 +1,47 @@ >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+ >+/* >+ * These are the "op" operation codes for mediactl() media control >+ * calls for device special files >+ */ >+enum { >+ MEDIA_LOCK = 1234, /* Lock the drive door */ >+ MEDIA_UNLOCK, /* Unlock the drive door */ >+ SUPERMOUNT_INC_COUNT, >+ SUPERMOUNT_DEC_COUNT, >+}; >+ >+static inline int >+supermount_usage_count(struct block_device *bdev, int count) >+{ >+ if (bdev) { >+ count -= bdev->bd_disk->scount; >+ if (count < 0) >+ count = 0; >+ } >+ return count; >+} >+ >+static inline int >+dev_is_supermounted(struct block_device *bdev) >+{ >+ return bdev ? bdev->bd_disk->scount > 0 : 0; >+} >+ >+#else /* CONFIG_SUPERMOUNT */ >+ >+static inline int >+supermount_usage_count(struct block_device *bdev, int count) >+{ >+ (void)bdev; // make compiler happy >+ return count; >+} >+ >+static inline int >+dev_is_supermounted(struct block_device *bdev) >+{ >+ (void)bdev; // make compiler happy >+ return 0; >+} >+ >+#endif /* CONFIG_SUPERMOUNT */ >diff -urN linux-2.6.25.4.orig/drivers/scsi/sd.c linux-2.6.25.4/drivers/scsi/sd.c >--- linux-2.6.25.4.orig/drivers/scsi/sd.c 2008-04-18 01:20:09 +0300 >+++ linux-2.6.25.4/drivers/scsi/sd.c 2008-05-20 01:27:53 +0300 >@@ -55,6 +55,7 @@ > #include <scsi/scsi_device.h> > #include <scsi/scsi_driver.h> > #include <scsi/scsi_eh.h> >+#include <linux/supermount_media.h> > #include <scsi/scsi_host.h> > #include <scsi/scsi_ioctl.h> > #include <scsi/scsicam.h> >@@ -610,6 +611,12 @@ > scsi_set_medium_removal(sdev, SCSI_REMOVAL_PREVENT); > } > >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+ if (supermount_usage_count(inode->i_bdev, sdkp->openers++) == 0 && sdev->removable) { >+ if (scsi_block_when_processing_errors(sdev)) >+ scsi_set_medium_removal(sdev, SCSI_REMOVAL_PREVENT); >+ } >+#endif > return 0; > > error_out: >@@ -640,7 +647,12 @@ > if (scsi_block_when_processing_errors(sdev)) > scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW); > } >- >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+ if (supermount_usage_count(inode->i_bdev, --sdkp->openers) == 0 && sdev->removable) { >+ if (scsi_block_when_processing_errors(sdev)) >+ scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW); >+ } >+#endif > /* > * XXX and what if there are packets in flight and this close() > * XXX is followed by a "rmmod sd_mod"? >@@ -782,6 +794,17 @@ > sshdr); > } > >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+ /* >+ * FIXME HACK >+ * busy device that is unplugged is SDEV_DEL but online and ioctl >+ * does not return any error. Oh well, it is likely layering >+ * violation but for now it enables media checks for supermount >+ */ >+ >+ if (sdp->sdev_state == SDEV_DEL) >+ retval = 1; >+#endif > /* > * Unable to test, unit probably not ready. This usually > * means there is no disc in the drive. Mark as changed, >@@ -867,6 +890,51 @@ > } > } > >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+/* >+ * This function performs media control operations. Currently the >+ * only functions used are MEDIA_LOCK and MEDIA_UNLOCK, to lock and >+ * unlock the drive door. >+ */ >+ >+static int sd_mediactl(struct block_device *bdev, int op, int optarg) >+{ >+ struct scsi_disk *sdkp = scsi_disk(bdev->bd_disk); >+ struct scsi_device *sdp = sdkp->device; >+ int rc = 0; >+ >+ SCSI_LOG_HLQUEUE(3, printk("sd_mediactl: disk=%s\n", >+ bdev->bd_disk->disk_name)); >+ >+ if (!sdp->removable) >+ return 0; >+ >+ if (!scsi_block_when_processing_errors(sdp)) >+ return -ENODEV; >+ >+ switch (op) { >+ case MEDIA_LOCK: >+ if (supermount_usage_count(bdev, sdkp->openers) == 0) >+ rc = scsi_set_medium_removal(sdp, SCSI_REMOVAL_PREVENT); >+ /* FIXME is it the right way? */ >+ if (optarg) >+ sdkp->openers++; >+ break; >+ case MEDIA_UNLOCK: >+ if (optarg && sdkp->openers > 0) >+ sdkp->openers--; >+ if (supermount_usage_count(bdev, sdkp->openers) == 0) >+ rc = scsi_set_medium_removal(sdp, SCSI_REMOVAL_ALLOW); >+ break; >+ default: >+ rc = -EINVAL; >+ break; >+ } >+ >+ return rc; >+} >+#endif >+ > > #ifdef CONFIG_COMPAT > /* >@@ -914,6 +982,10 @@ > #endif > .media_changed = sd_media_changed, > .revalidate_disk = sd_revalidate_disk, >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+ .mediactl = sd_mediactl, >+#endif >+ > }; > > /** >diff -urN linux-2.6.25.4.orig/include/linux/fs.h linux-2.6.25.4/include/linux/fs.h >--- linux-2.6.25.4.orig/include/linux/fs.h 2008-04-18 01:20:17 +0300 >+++ linux-2.6.25.4/include/linux/fs.h 2008-05-20 01:27:53 +0300 >@@ -92,6 +92,9 @@ > /* public flags for file_system_type */ > #define FS_REQUIRES_DEV 1 > #define FS_BINARY_MOUNTDATA 2 >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+#define FS_NO_SUBMNT 64 /* Prevent mounting over this filesystem */ >+#endif > #define FS_HAS_SUBTYPE 4 > #define FS_REVAL_DOT 16384 /* Check the paths ".", ".." for staleness */ > #define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() >@@ -118,6 +121,9 @@ > MS_VERBOSE is deprecated. */ > #define MS_SILENT 32768 > #define MS_POSIXACL (1<<16) /* VFS does not apply the umask */ >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+#define MS_SUPERMOUNTED (1<<29) /* 21 is being used now by update atime*/ >+#endif > #define MS_UNBINDABLE (1<<17) /* change to unbindable */ > #define MS_PRIVATE (1<<18) /* change to private */ > #define MS_SLAVE (1<<19) /* change to slave */ >@@ -809,6 +815,10 @@ > struct list_head f_ep_links; > spinlock_t f_ep_lock; > #endif /* #ifdef CONFIG_EPOLL */ >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+ /* Used by supermount. Use(fullness) unconfirmed */ >+ void *f_supermount; >+#endif > struct address_space *f_mapping; > }; > extern spinlock_t files_lock; >@@ -1029,6 +1039,9 @@ > */ > struct mutex s_vfs_rename_mutex; /* Kludge */ > >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+ atomic_t s_media_changed; >+#endif > /* Granularity of c/m/atime in ns. > Cannot be worse than a second */ > u32 s_time_gran; >@@ -1132,6 +1145,9 @@ > int (*direct_access) (struct block_device *, sector_t, unsigned long *); > int (*media_changed) (struct gendisk *); > int (*revalidate_disk) (struct gendisk *); >+#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) >+ int (*mediactl) (struct block_device *, int, int); >+#endif > int (*getgeo)(struct block_device *, struct hd_geometry *); > struct module *owner; > };
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 15838
:
2654
| 2655