diff -Nur linux-2.4.33-imedia/Documentation/Configure.help linux-2.4.33-imedia-patching/Documentation/Configure.help --- linux-2.4.33-imedia/Documentation/Configure.help 2006-01-26 15:21:21.000000000 +0200 +++ linux-2.4.33-imedia-patching/Documentation/Configure.help 2006-01-26 15:23:06.000000000 +0200 @@ -16745,6 +16745,22 @@ performance cost of it. If you are fine-tuning reiserfs, say Y, otherwise say N. +Supermount removable media support +CONFIG_SUPERMOUNT + 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. + Second extended fs support CONFIG_EXT2_FS This is the de facto standard Linux file system (method to organize diff -Nur linux-2.4.33-imedia/Documentation/filesystems/00-INDEX linux-2.4.33-imedia-patching/Documentation/filesystems/00-INDEX --- linux-2.4.33-imedia/Documentation/filesystems/00-INDEX 2004-02-18 15:36:30.000000000 +0200 +++ linux-2.4.33-imedia-patching/Documentation/filesystems/00-INDEX 2006-01-26 15:23:06.000000000 +0200 @@ -36,6 +36,8 @@ - Description of the ROMFS filesystem. smbfs.txt - info on using filesystems with the SMB protocol (Windows 3.11 and NT) +supermount.txt + - info on using supermount for removable media. sysv-fs.txt - info on the SystemV/V7/Xenix/Coherent filesystem. udf.txt diff -Nur linux-2.4.33-imedia/Documentation/filesystems/supermount.txt linux-2.4.33-imedia-patching/Documentation/filesystems/supermount.txt --- linux-2.4.33-imedia/Documentation/filesystems/supermount.txt 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.33-imedia-patching/Documentation/filesystems/supermount.txt 2006-01-26 15:23:06.000000000 +0200 @@ -0,0 +1,256 @@ +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 ,--, none + +or by adding correpsonding line to /etc/fstab + + none supermount ,--, 0 0 + +where + + are the options you want to pass to supermount + itself. These are described below. + + are the options you want supermount to pass to the + dismountable filesystem underneath. + + is the mount point where you want your removable media to be + mounted. + +WARNING: in the above description `none' is literal word. While device +is ignored by supermount itself, using real files in this place (real +device name or mount point directory name) is known to cause problems. +Some programs - fuser is one of them - will try to descend into filesystem +if dev can be statted, thus making supermount to attempt to access media. +This is annoying at best - in the worst case it can take very long time +during startup or shutdown. + +Notice that you do not directly specify the block device you are going +to mount on the mount command line. This is because the supermount +filesystem is NOT connected to a block device; rather, supermount is +responsible for connecting a separate filesystem to the block device. +You specify the sub-filesystem and block device name by providing the + field, where the following options are currently +recognised: + + +* fs= [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= [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[=] [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. 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= 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= 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 + + disabled + - or - + unmounted + - or - + mounted readcount writecount + +where 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: + + [disable|enable] [release [force]] + +`disable' will disable subfs (i.e. any futher attempt to mount is rejected). +Subfs must be unmounted; use `disable release' or `disable release force' to +unmount and disable at the same time. + +`enable' will enable disabled subfs, it has no effect if subfs is already +enabled. + +`release' will unmount subfs unless it is busy (opencount > 0). To unmount +busy subfs add `force'; the effect is very much as if media change has been +detected (with the difference that subfs will be cleanly unmounted). + +Some basic sanity checks are performed, i.e. it is impossible to specify +both `enable' and `disable' or `force' without `release'. + +Internals/Locking +----------------- + +THIS SECTION IS PROBABLY INCOMPLETE. CORRECTIONS ARE WELCOME. + +Supermount itself is using two locks and relies on two more locking rules +as implemented by kernel. + +* supermount_proc_sem + + Global mutex used to protect list of sbi during access to +/proc/fs/supermount/subfs + +* sbi->sem + + Per-filesystem mutex that protects subfs state (mounted/unmounted). +Any changes to subfs state (mounting, unmouting, adding or removing file, +dentry or inode) happen under this mutex. + +* inode->i_sem (see Documentation/filesystem/Locking) + + Per-inode mutex used by VFS to serialize inode unlink operation. +Supermount relies on the fact that link/unlink for an inode are mutually +locked and thus inode->i_nlink is atomic inside of fs method. + +* BKL + + Used to protect device usage count. It is changed in open/release +and referenced in ioctl all of which run under BKL. Supermount adds mediactl +and internally wraps it in BKL as well. + +Caveats/BUGS +------------ + +Inode times are believed to be correctly updated; still it is possible that +I missed some point. + +If subfs is not yet mounted, find /mnt/cdrom fails with "find: /mnt/cdrom +changed during execution of find". It is unlikely it can be fixed in +supermount; much more simple is to provide wrapper that will check if subfs +is mounted and do simple "touch /mnt/cdrom" if not to make sure it is. + +Supermount attempts to check for changed media at every operation and +invalidate and umount old subfs to avoid new media corruption in rw case. +Still it is possible that kernel will write out stale information and +it is outside of supermount control. + +By default cdrom driver does not check media type i.e. supermount will +try all configured fs types in turn that may be quite time consuming. +Use sysctl -w dev.cdrom.check_media=1 to enable it. Comments in cdrom.c +indicate that some (non conforming) software may have problems with this +settings. YMMV + +atime management does not work for special files. There is no hooks from +VFS into fs to indicate when it gets updated + +Media management for multi-partition devices (like SCSI disks or, possibly, +ide-floppy) is broken - it does not into account that several partitions +may be opened at the same time. It is hard to fix it in 2.4 kernel; +hopefully such cases are rare, most people just use a single partition on +Zip/Jaz drive. + +$Id: supermount.txt,v 1.19 2003/06/12 07:45:38 bor Exp $ diff -Nur linux-2.4.33-imedia/drivers/block/paride/pcd.c linux-2.4.33-imedia-patching/drivers/block/paride/pcd.c --- linux-2.4.33-imedia/drivers/block/paride/pcd.c 2002-11-29 01:53:12.000000000 +0200 +++ linux-2.4.33-imedia-patching/drivers/block/paride/pcd.c 2006-01-26 15:23:06.000000000 +0200 @@ -271,6 +271,7 @@ release: cdrom_release, ioctl: cdrom_ioctl, check_media_change: cdrom_media_changed, + mediactl: cdrom_mediactl, }; static struct cdrom_device_ops pcd_dops = { diff -Nur linux-2.4.33-imedia/drivers/cdrom/cdrom.c linux-2.4.33-imedia-patching/drivers/cdrom/cdrom.c --- linux-2.4.33-imedia/drivers/cdrom/cdrom.c 2005-01-19 16:09:43.000000000 +0200 +++ linux-2.4.33-imedia-patching/drivers/cdrom/cdrom.c 2006-01-26 15:23:06.000000000 +0200 @@ -264,6 +264,7 @@ #include #include #include +#include #include #include @@ -568,7 +569,11 @@ cdinfo(CD_OPEN, "open device failed.\n"); goto clean_up_and_return; } - if (CDROM_CAN(CDC_LOCK) && cdi->options & CDO_LOCK) { + /* Adjust locked state only on transition from "free" to + * "busy" state so as to not interfere with supermount. + */ + if (CDROM_CAN(CDC_LOCK) && cdi->options & CDO_LOCK && + supermount_usage_count(cdi->dev, cdi->use_count) == 0) { cdo->lock_door(cdi, 1); cdinfo(CD_OPEN, "door locked.\n"); } @@ -581,7 +586,7 @@ This ensures that the drive gets unlocked after a mount fails. This is a goto to avoid bloating the driver with redundant code. */ clean_up_and_return: - cdinfo(CD_WARNING, "open failed.\n"); + cdinfo(CD_OPEN, "open failed.\n"); if (CDROM_CAN(CDC_LOCK) && cdi->options & CDO_LOCK) { cdo->lock_door(cdi, 0); cdinfo(CD_OPEN, "door unlocked.\n"); @@ -661,7 +666,10 @@ cdi->use_count--; if (cdi->use_count == 0) cdinfo(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", cdi->name); - if (cdi->use_count == 0 && + /* + * we run under BKL so use_count cannot change + */ + if (supermount_usage_count(dev, cdi->use_count) == 0 && cdo->capability & CDC_LOCK && !keeplocked) { cdinfo(CD_CLOSE, "Unlocking door!\n"); cdo->lock_door(cdi, 0); @@ -908,6 +916,37 @@ tracks->cdi, tracks->xa); } +/* + * MEDIA_LOCK, MEDIA_UNLOCK + * optarg == 0 - do not adjust usage count (compatibility) + * optarg == 1 - adjust usage count + */ +int cdrom_mediactl (kdev_t dev, int op, int optarg) +{ + struct cdrom_device_info *cdi = cdrom_find_device(dev); + 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(dev, cdi->use_count) == 0) + cdo->lock_door(cdi, (op == MEDIA_LOCK)); + if (op == MEDIA_LOCK && optarg) + cdi->use_count++; + break; + default: + return -ENOSYS; + } + return 0; +} + /* Requests to the low-level drivers will /always/ be done in the following format convention: @@ -1495,7 +1534,9 @@ cdinfo(CD_DO_IOCTL, "entering CDROMEJECT\n"); if (!CDROM_CAN(CDC_OPEN_TRAY)) return -ENOSYS; - if (cdi->use_count != 1 || keeplocked) + if (keeplocked + || (supermount_usage_count(dev,cdi->use_count) != 1) + ) return -EBUSY; if (CDROM_CAN(CDC_LOCK)) if ((ret=cdo->lock_door(cdi, 0))) @@ -2373,7 +2414,7 @@ *next_writable += 7; return 0; } -} +} EXPORT_SYMBOL(cdrom_get_disc_info); EXPORT_SYMBOL(cdrom_get_track_info); @@ -2386,6 +2427,7 @@ EXPORT_SYMBOL(cdrom_release); EXPORT_SYMBOL(cdrom_ioctl); EXPORT_SYMBOL(cdrom_media_changed); +EXPORT_SYMBOL(cdrom_mediactl); EXPORT_SYMBOL(cdrom_number_of_slots); EXPORT_SYMBOL(cdrom_select_disc); EXPORT_SYMBOL(cdrom_mode_select); diff -Nur linux-2.4.33-imedia/drivers/cdrom/cdu31a.c linux-2.4.33-imedia-patching/drivers/cdrom/cdu31a.c --- linux-2.4.33-imedia/drivers/cdrom/cdu31a.c 2003-06-13 17:51:32.000000000 +0300 +++ linux-2.4.33-imedia-patching/drivers/cdrom/cdu31a.c 2006-01-26 15:23:06.000000000 +0200 @@ -3190,6 +3190,7 @@ release: cdrom_release, ioctl: cdrom_ioctl, check_media_change: cdrom_media_changed, + mediactl: cdrom_mediactl, }; static struct cdrom_device_ops scd_dops = { diff -Nur linux-2.4.33-imedia/drivers/cdrom/cm206.c linux-2.4.33-imedia-patching/drivers/cdrom/cm206.c --- linux-2.4.33-imedia/drivers/cdrom/cm206.c 2001-10-25 23:58:35.000000000 +0300 +++ linux-2.4.33-imedia-patching/drivers/cdrom/cm206.c 2006-01-26 15:23:06.000000000 +0200 @@ -772,6 +772,7 @@ release: cdrom_release, ioctl: cdrom_ioctl, check_media_change: cdrom_media_changed, + mediactl: cdrom_mediactl, }; /* The new open. The real opening strategy is defined in cdrom.c. */ diff -Nur linux-2.4.33-imedia/drivers/cdrom/mcd.c linux-2.4.33-imedia-patching/drivers/cdrom/mcd.c --- linux-2.4.33-imedia/drivers/cdrom/mcd.c 2001-10-25 23:58:35.000000000 +0300 +++ linux-2.4.33-imedia-patching/drivers/cdrom/mcd.c 2006-01-26 15:23:06.000000000 +0200 @@ -196,6 +196,7 @@ release: cdrom_release, ioctl: cdrom_ioctl, check_media_change: cdrom_media_changed, + mediactl: cdrom_mediactl, }; static struct timer_list mcd_timer; diff -Nur linux-2.4.33-imedia/drivers/cdrom/mcdx.c linux-2.4.33-imedia-patching/drivers/cdrom/mcdx.c --- linux-2.4.33-imedia/drivers/cdrom/mcdx.c 2001-10-25 23:58:35.000000000 +0300 +++ linux-2.4.33-imedia-patching/drivers/cdrom/mcdx.c 2006-01-26 15:23:06.000000000 +0200 @@ -226,6 +226,7 @@ release: cdrom_release, ioctl: cdrom_ioctl, check_media_change: cdrom_media_changed, + mediactl: cdrom_mediactl, }; diff -Nur linux-2.4.33-imedia/drivers/cdrom/sbpcd.c linux-2.4.33-imedia-patching/drivers/cdrom/sbpcd.c --- linux-2.4.33-imedia/drivers/cdrom/sbpcd.c 2001-10-25 23:58:35.000000000 +0300 +++ linux-2.4.33-imedia-patching/drivers/cdrom/sbpcd.c 2006-01-26 15:23:06.000000000 +0200 @@ -5426,6 +5426,7 @@ release: cdrom_release, ioctl: cdrom_ioctl, check_media_change: cdrom_media_changed, + mediactl: cdrom_mediactl, }; /*==========================================================================*/ /* diff -Nur linux-2.4.33-imedia/drivers/ide/ide-cd.c linux-2.4.33-imedia-patching/drivers/ide/ide-cd.c --- linux-2.4.33-imedia/drivers/ide/ide-cd.c 2005-04-04 04:42:19.000000000 +0300 +++ linux-2.4.33-imedia-patching/drivers/ide/ide-cd.c 2006-01-26 15:23:06.000000000 +0200 @@ -3036,6 +3036,11 @@ return cdrom_ioctl(inode, file, cmd, arg); } +int ide_cdrom_mediactl (ide_drive_t *drive, kdev_t dev, int op, int optarg) +{ + return cdrom_mediactl (dev, op, optarg); +} + static int ide_cdrom_open (struct inode *ip, struct file *fp, ide_drive_t *drive) { @@ -3159,6 +3164,7 @@ special: NULL, proc: NULL, init: ide_cdrom_init, + mediactl: ide_cdrom_mediactl, attach: ide_cdrom_attach, ata_prebuilder: NULL, atapi_prebuilder: NULL, diff -Nur linux-2.4.33-imedia/drivers/ide/ide-floppy.c linux-2.4.33-imedia-patching/drivers/ide/ide-floppy.c --- linux-2.4.33-imedia/drivers/ide/ide-floppy.c 2003-08-25 14:44:41.000000000 +0300 +++ linux-2.4.33-imedia-patching/drivers/ide/ide-floppy.c 2006-01-26 15:23:06.000000000 +0200 @@ -97,6 +97,7 @@ #include #include #include +#include #include #include @@ -1681,7 +1682,7 @@ prevent = 0; /* fall through */ case CDROM_LOCKDOOR: - if (drive->usage > 1) + if (supermount_usage_count(inode->i_rdev, drive->usage) > 1) return -EBUSY; /* The IOMEGA Clik! Drive doesn't support this command - no room for an eject mechanism */ @@ -1756,7 +1757,7 @@ #endif /* IDEFLOPPY_DEBUG_LOG */ MOD_INC_USE_COUNT; - if (drive->usage == 1) { + if (supermount_usage_count(inode->i_rdev, drive->usage) == 1) { clear_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, &floppy->flags); /* Just in case */ @@ -1807,10 +1808,12 @@ printk (KERN_INFO "Reached idefloppy_release\n"); #endif /* IDEFLOPPY_DEBUG_LOG */ - if (!drive->usage) { + if (!supermount_usage_count(inode->i_rdev, drive->usage)) { idefloppy_floppy_t *floppy = drive->driver_data; - invalidate_bdev(inode->i_bdev, 0); + /* we do not want that our filesystem goes away */ + if (!dev_is_supermounted(inode->i_rdev)) + invalidate_bdev(inode->i_bdev, 0); /* IOMEGA Clik! drives do not support lock/unlock commands */ if (!test_bit(IDEFLOPPY_CLIK_DRIVE, &floppy->flags)) { @@ -1855,6 +1858,37 @@ } /* + * lock/unlock media + */ +static int idefloppy_mediactl (ide_drive_t *drive, kdev_t dev, int op, int optarg) +{ + idefloppy_pc_t pc; + idefloppy_floppy_t *floppy = drive->driver_data; + + switch (op) { + case MEDIA_LOCK: + case MEDIA_UNLOCK: + if (op == MEDIA_UNLOCK && optarg) { + drive->usage--; + if (drive->usage < 0) + drive->usage = 0; + } + /* IOMEGA Clik! drives do not support lock/unlock commands */ + if (!test_bit(IDEFLOPPY_CLIK_DRIVE, &floppy->flags) + && supermount_usage_count(dev, drive->usage) == 0) { + idefloppy_create_prevent_cmd(&pc, (op == MEDIA_LOCK)); + (void) idefloppy_queue_pc_tail(drive, &pc); + } + if (op == MEDIA_LOCK && optarg) + drive->usage++; + break; + default: + return -ENOSYS; + } + return 0; +} + +/* * idefloppy_identify_device checks if we can support a drive, * based on the ATAPI IDENTIFY command results. */ @@ -2108,6 +2142,7 @@ revalidate: idefloppy_revalidate, pre_reset: NULL, capacity: idefloppy_capacity, + mediactl: idefloppy_mediactl, special: NULL, proc: idefloppy_proc, init: idefloppy_init, diff -Nur linux-2.4.33-imedia/drivers/ide/ide-io.c linux-2.4.33-imedia-patching/drivers/ide/ide-io.c --- linux-2.4.33-imedia/drivers/ide/ide-io.c 2006-01-11 20:29:27.000000000 +0200 +++ linux-2.4.33-imedia-patching/drivers/ide/ide-io.c 2006-01-26 15:23:06.000000000 +0200 @@ -1314,6 +1314,8 @@ ide_do_request(hwgroup, IDE_NO_IRQ); spin_unlock_irqrestore(&io_request_lock, flags); if (action == ide_wait) { + /* make sure IO is not suspended */ + generic_unplug_device(&drive->queue); /* wait for it to be serviced */ wait_for_completion(&wait); /* return -EIO if errors */ diff -Nur linux-2.4.33-imedia/drivers/ide/ide.c linux-2.4.33-imedia-patching/drivers/ide/ide.c --- linux-2.4.33-imedia/drivers/ide/ide.c 2004-08-08 02:26:04.000000000 +0300 +++ linux-2.4.33-imedia-patching/drivers/ide/ide.c 2006-01-26 15:23:06.000000000 +0200 @@ -611,6 +611,18 @@ return 0; } +static int ide_mediactl (kdev_t i_rdev, int op, int optarg) +{ + ide_drive_t *drive; + + if ((drive = ide_info_ptr(i_rdev, 0)) == NULL) + return -ENODEV; + + if (drive->driver != NULL && DRIVER(drive)->mediactl != NULL) + return DRIVER(drive)->mediactl(drive, i_rdev, op, optarg); + return 0; +} + #ifdef CONFIG_PROC_FS ide_proc_entry_t generic_subdriver_entries[] = { { "capacity", S_IFREG|S_IRUGO, proc_ide_read_capacity, NULL }, @@ -2925,7 +2937,8 @@ release: ide_release, ioctl: ide_ioctl, check_media_change: ide_check_media_change, - revalidate: ide_revalidate_disk + revalidate: ide_revalidate_disk, + mediactl: ide_mediactl }}; EXPORT_SYMBOL(ide_fops); diff -Nur linux-2.4.33-imedia/drivers/scsi/ppa.c linux-2.4.33-imedia-patching/drivers/scsi/ppa.c --- linux-2.4.33-imedia/drivers/scsi/ppa.c 2002-11-29 01:53:14.000000000 +0200 +++ linux-2.4.33-imedia-patching/drivers/scsi/ppa.c 2006-01-26 15:23:06.000000000 +0200 @@ -23,6 +23,8 @@ int ppa_release(struct Scsi_Host *); static void ppa_reset_pulse(unsigned int base); +static int no_cable_warning; + typedef struct { struct pardevice *dev; /* Parport device entry */ int base; /* Actual port address */ @@ -217,12 +219,14 @@ } if (nhosts == 0) { if (try_again == 1) { - printk("WARNING - no ppa compatible devices found.\n"); - printk(" As of 31/Aug/1998 Iomega started shipping parallel\n"); - printk(" port ZIP drives with a different interface which is\n"); - printk(" supported by the imm (ZIP Plus) driver. If the\n"); - printk(" cable is marked with \"AutoDetect\", this is what has\n"); - printk(" happened.\n"); + if (!no_cable_warning) { + printk("WARNING - no ppa compatible devices found.\n"); + printk(" As of 31/Aug/1998 Iomega started shipping parallel\n"); + printk(" port ZIP drives with a different interface which is\n"); + printk(" supported by the imm (ZIP Plus) driver. If the\n"); + printk(" cable is marked with \"AutoDetect\", this is what has\n"); + printk(" happened.\n"); + } spin_lock_irq(&io_request_lock); return 0; } @@ -1146,3 +1150,17 @@ return 1; } MODULE_LICENSE("GPL"); +MODULE_PARM(no_cable_warning, "i"); +MODULE_PARM_DESC(no_cable_warning, "Supress annoying cable warning"); + +#ifndef MODULE +static int __init ppa_setup(char *str) +{ + if (!strcmp(str, "no_cable_warning")) + no_cable_warning = 1; + + return 1; +} + +__setup("ppa=", ppa_setup); +#endif diff -Nur linux-2.4.33-imedia/drivers/scsi/sd.c linux-2.4.33-imedia-patching/drivers/scsi/sd.c --- linux-2.4.33-imedia/drivers/scsi/sd.c 2005-11-16 21:12:54.000000000 +0200 +++ linux-2.4.33-imedia-patching/drivers/scsi/sd.c 2006-01-26 15:23:06.000000000 +0200 @@ -43,6 +43,8 @@ #include #include +#include + #include #include @@ -522,7 +524,7 @@ } if (SDev->removable) - if (SDev->access_count==1) + if (supermount_usage_count(inode->i_rdev, SDev->access_count)==1) if (scsi_block_when_processing_errors(SDev)) scsi_ioctl(SDev, SCSI_IOCTL_DOORLOCK, NULL); @@ -551,7 +553,7 @@ SDev->access_count--; if (SDev->removable) { - if (!SDev->access_count) + if (!supermount_usage_count(inode->i_rdev, SDev->access_count)) if (scsi_block_when_processing_errors(SDev)) scsi_ioctl(SDev, SCSI_IOCTL_DOORUNLOCK, NULL); } @@ -562,6 +564,52 @@ return 0; } +/* + * 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(kdev_t full_dev, int op, int optarg) { + int target; + int rc = 0; + Scsi_Device *SDev; + + target = DEVICE_NR(full_dev); + + if (target >= sd_template.nr_dev) { + printk("CD-ROM request error: invalid device.\n"); + return -ENODEV; + }; + + SDev = rscsi_disks[target].device; + + switch (op) { + case MEDIA_LOCK: + if (supermount_usage_count(full_dev, SDev->access_count) == 0) + if (scsi_block_when_processing_errors(SDev)) + rc = scsi_ioctl(SDev, SCSI_IOCTL_DOORLOCK, 0); + if (optarg) + SDev->access_count++; + break; + case MEDIA_UNLOCK: + if (optarg) + SDev->access_count--; + if (SDev->access_count < 0) + SDev->access_count = 0; + + if (supermount_usage_count(full_dev, SDev->access_count) == 0) + if (scsi_block_when_processing_errors(SDev)) + rc = scsi_ioctl(SDev, SCSI_IOCTL_DOORUNLOCK, 0); + break; + default: + rc = -ENOSYS; + break; + } + + return rc; +} + static struct block_device_operations sd_fops = { owner: THIS_MODULE, @@ -569,7 +617,8 @@ release: sd_release, ioctl: sd_ioctl, check_media_change: check_scsidisk_media_change, - revalidate: fop_revalidate_scsidisk + revalidate: fop_revalidate_scsidisk, + mediactl: sd_mediactl, }; /* @@ -715,9 +764,6 @@ printk("SCSI disk request error: invalid device.\n"); return 0; } - if (!SDev->removable) - return 0; - /* * If the device is offline, don't send any commands - just pretend as * if the command failed. If the device ever comes back online, we @@ -731,6 +777,9 @@ * check_disk_change */ } + if (!SDev->removable) + return 0; + /* * Using TEST_UNIT_READY enables differentiation between drive with * no cartridge loaded - NOT READY, drive with changed cartridge - @@ -845,8 +894,9 @@ if( the_result != 0 && ((driver_byte(the_result) & DRIVER_SENSE) != 0) && SRpnt->sr_sense_buffer[2] == UNIT_ATTENTION + && SRpnt->sr_sense_buffer[7] >= 6 && SRpnt->sr_sense_buffer[12] == 0x3A ) { - rscsi_disks[i].capacity = 0x1fffff; + rscsi_disks[i].capacity = 0; sector_size = 512; rscsi_disks[i].device->changed = 1; rscsi_disks[i].ready = 0; @@ -959,29 +1009,40 @@ */ if (the_result) { - printk("%s : READ CAPACITY failed.\n" - "%s : status = %x, message = %02x, host = %d, driver = %02x \n", - nbuff, nbuff, - status_byte(the_result), - msg_byte(the_result), - host_byte(the_result), - driver_byte(the_result) - ); - if (driver_byte(the_result) & DRIVER_SENSE) - print_req_sense("sd", SRpnt); - else - printk("%s : sense not available. \n", nbuff); - - printk("%s : block size assumed to be 512 bytes, disk size 1GB. \n", - nbuff); - rscsi_disks[i].capacity = 0x1fffff; - sector_size = 512; - - /* Set dirty bit for removable devices if not ready - - * sometimes drives will not report this properly. */ - if (rscsi_disks[i].device->removable && - SRpnt->sr_sense_buffer[2] == NOT_READY) + if (rscsi_disks[i].device->removable + && ((driver_byte(the_result) & DRIVER_SENSE) != 0) + && SRpnt->sr_sense_buffer[2] == NOT_READY + && SRpnt->sr_sense_buffer[7] >= 6 + && SRpnt->sr_sense_buffer[12] == 0x3A ) { + rscsi_disks[i].capacity = 0; + sector_size = 512; rscsi_disks[i].device->changed = 1; + rscsi_disks[i].ready = 0; + } else { + printk("%s : READ CAPACITY failed.\n" + "%s : status = %x, message = %02x, host = %d, driver = %02x \n", + nbuff, nbuff, + status_byte(the_result), + msg_byte(the_result), + host_byte(the_result), + driver_byte(the_result) + ); + if (driver_byte(the_result) & DRIVER_SENSE) + print_req_sense("sd", SRpnt); + else + printk("%s : sense not available. \n", nbuff); + + printk("%s : block size assumed to be 512 bytes, disk size 1GB. \n", + nbuff); + rscsi_disks[i].capacity = 0x1fffff; + sector_size = 512; + + /* Set dirty bit for removable devices if not ready - + * sometimes drives will not report this properly. */ + if (rscsi_disks[i].device->removable && + SRpnt->sr_sense_buffer[2] == NOT_READY) + rscsi_disks[i].device->changed = 1; + } } else { /* diff -Nur linux-2.4.33-imedia/drivers/scsi/sr.c linux-2.4.33-imedia-patching/drivers/scsi/sr.c --- linux-2.4.33-imedia/drivers/scsi/sr.c 2003-06-13 17:51:36.000000000 +0300 +++ linux-2.4.33-imedia-patching/drivers/scsi/sr.c 2006-01-26 15:23:06.000000000 +0200 @@ -514,6 +514,7 @@ release: cdrom_release, ioctl: cdrom_ioctl, check_media_change: cdrom_media_changed, + mediactl: cdrom_mediactl, }; static int sr_open(struct cdrom_device_info *cdi, int purpose) diff -Nur linux-2.4.33-imedia/fs/Config.in linux-2.4.33-imedia-patching/fs/Config.in --- linux-2.4.33-imedia/fs/Config.in 2004-11-17 13:54:21.000000000 +0200 +++ linux-2.4.33-imedia-patching/fs/Config.in 2006-01-26 15:23:06.000000000 +0200 @@ -14,6 +14,8 @@ dep_mbool ' Enable reiserfs debug mode' CONFIG_REISERFS_CHECK $CONFIG_REISERFS_FS dep_mbool ' Stats in /proc/fs/reiserfs' CONFIG_REISERFS_PROC_INFO $CONFIG_REISERFS_FS +tristate 'Supermount removable media support' CONFIG_SUPERMOUNT + dep_tristate 'ADFS file system support (EXPERIMENTAL)' CONFIG_ADFS_FS $CONFIG_EXPERIMENTAL dep_mbool ' ADFS write support (DANGEROUS)' CONFIG_ADFS_FS_RW $CONFIG_ADFS_FS $CONFIG_EXPERIMENTAL diff -Nur linux-2.4.33-imedia/fs/Makefile linux-2.4.33-imedia-patching/fs/Makefile --- linux-2.4.33-imedia/fs/Makefile 2004-02-18 15:36:31.000000000 +0200 +++ linux-2.4.33-imedia-patching/fs/Makefile 2006-01-26 15:23:06.000000000 +0200 @@ -58,6 +58,7 @@ subdir-$(CONFIG_ROMFS_FS) += romfs subdir-$(CONFIG_QNX4FS_FS) += qnx4 subdir-$(CONFIG_UDF_FS) += udf +subdir-$(CONFIG_SUPERMOUNT) += supermount subdir-$(CONFIG_AUTOFS_FS) += autofs subdir-$(CONFIG_AUTOFS4_FS) += autofs4 subdir-$(CONFIG_ADFS_FS) += adfs diff -Nur linux-2.4.33-imedia/fs/block_dev.c linux-2.4.33-imedia-patching/fs/block_dev.c --- linux-2.4.33-imedia/fs/block_dev.c 2003-06-13 17:51:37.000000000 +0300 +++ linux-2.4.33-imedia-patching/fs/block_dev.c 2006-01-26 15:23:06.000000000 +0200 @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -273,6 +274,9 @@ memset(bdev, 0, sizeof(*bdev)); sema_init(&bdev->bd_sem, 1); INIT_LIST_HEAD(&bdev->bd_inodes); +#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) + atomic_set(&bdev->bd_supermount, 0); +#endif } } @@ -512,6 +516,7 @@ { int i; const struct block_device_operations * bdops = NULL; + struct super_block *sb = NULL; i = MAJOR(dev); if (i < MAX_BLKDEV) @@ -533,8 +538,36 @@ if (!bdops->check_media_change(dev)) return 0; - if (invalidate_device(dev, 0)) - printk("VFS: busy inodes on changed media.\n"); +#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) + sb = get_super(dev); + if (sb) { + atomic_set(&sb->s_media_changed, 1); + drop_super(sb); + } +#endif + + if (dev_is_supermounted(dev)) { + /* + * 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. + */ + destroy_buffers(dev); + } else { + if (invalidate_device(dev, 0)) + printk("VFS: busy inodes on changed media.\n"); + } if (bdops->revalidate) bdops->revalidate(dev); diff -Nur linux-2.4.33-imedia/fs/ext2/super.c linux-2.4.33-imedia-patching/fs/ext2/super.c --- linux-2.4.33-imedia/fs/ext2/super.c 2004-08-08 02:26:05.000000000 +0300 +++ linux-2.4.33-imedia-patching/fs/ext2/super.c 2006-01-26 15:23:06.000000000 +0200 @@ -274,6 +274,13 @@ || !strcmp (this_char, "quota") || !strcmp (this_char, "usrquota")) /* Don't do anything ;-) */ ; +#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) + /* Silently ignore NLS options */ + else if (!strcmp (this_char, "iocharset") + || !strcmp (this_char, "codepage") + || !strcmp (this_char, "umask")) + /* Don't do anything ;-) */ ; +#endif else { printk ("EXT2-fs: Unrecognized mount option %s\n", this_char); return 0; diff -Nur linux-2.4.33-imedia/fs/namespace.c linux-2.4.33-imedia-patching/fs/namespace.c --- linux-2.4.33-imedia/fs/namespace.c 2004-02-18 15:36:31.000000000 +0200 +++ linux-2.4.33-imedia-patching/fs/namespace.c 2006-01-26 15:23:06.000000000 +0200 @@ -21,8 +21,6 @@ #include #include -struct vfsmount *do_kern_mount(const char *type, int flags, char *name, void *data); -int do_remount_sb(struct super_block *sb, int flags, void * data); void kill_super(struct super_block *sb); extern int __init init_rootfs(void); @@ -732,6 +730,15 @@ if (retval) return retval; +#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) + if (!(flags & (MS_REMOUNT | MS_MOVE)) && + (nd.mnt->mnt_sb->s_type->fs_flags & FS_NO_SUBMNT)) { + retval = -EPERM; + path_release(&nd); + return retval; + } +#endif + if (flags & MS_REMOUNT) retval = do_remount(&nd, flags & ~MS_REMOUNT, mnt_flags, data_page); diff -Nur linux-2.4.33-imedia/fs/super.c linux-2.4.33-imedia-patching/fs/super.c --- linux-2.4.33-imedia/fs/super.c 2003-08-25 14:44:43.000000000 +0300 +++ linux-2.4.33-imedia-patching/fs/super.c 2006-01-26 15:23:06.000000000 +0200 @@ -284,6 +284,9 @@ s->s_op = &empty_sops; s->dq_op = sb_dquot_ops; s->s_qcop = sb_quotactl_ops; +#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) + atomic_set(&s->s_media_changed, 0); +#endif } return s; } @@ -710,7 +713,14 @@ if (old->s_dev != dev) continue; if (old->s_type != fs_type || - ((flags ^ old->s_flags) & MS_RDONLY)) { + ((flags ^ old->s_flags) & MS_RDONLY) +#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) + /* + * disallow double mounting for supermounted device + */ + || ((flags | old->s_flags) & MS_SUPERMOUNTED) +#endif + ) { spin_unlock(&sb_lock); destroy_super(s); goto out1; @@ -797,6 +807,21 @@ if (!type) return ERR_PTR(-ENODEV); +#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) + /* + * sanity checks; supermount relies on these assumptions + */ + if (flags & MS_SUPERMOUNTED) { + sb = ERR_PTR(-EINVAL); + if (type->fs_flags & FS_ODD_RENAME) + goto out; + sb = ERR_PTR(-ENODEV); + if (!(type->fs_flags & FS_REQUIRES_DEV)) + goto out; + sb = ERR_PTR(-ENOMEM); + } +#endif + mnt = alloc_vfsmnt(name); if (!mnt) goto out; diff -Nur linux-2.4.33-imedia/fs/supermount/Makefile linux-2.4.33-imedia-patching/fs/supermount/Makefile --- linux-2.4.33-imedia/fs/supermount/Makefile 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.33-imedia-patching/fs/supermount/Makefile 2006-01-26 15:23:06.000000000 +0200 @@ -0,0 +1,21 @@ +# +# Makefile for the linux supermounting routines. +# + +EXTRA_CFLAGS=-DSUPERMOUNT_DEBUG + +O_TARGET := supermount.o + +obj-y := dentry.o \ + file.o \ + filemap.o \ + init.o \ + mediactl.o \ + namei.o \ + proc.o \ + subfs.o \ + super.o + +obj-m := $(O_TARGET) + +include $(TOPDIR)/Rules.make diff -Nur linux-2.4.33-imedia/fs/supermount/changelog linux-2.4.33-imedia-patching/fs/supermount/changelog --- linux-2.4.33-imedia/fs/supermount/changelog 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.33-imedia-patching/fs/supermount/changelog 2006-01-26 15:23:06.000000000 +0200 @@ -0,0 +1,371 @@ +* ? ? ? Andrey Borzenkov ?.?.? + +* Sun 18 Jan 2004 Andrey Borzenkov 1.2.11a + + - fix "auto" option. Simplification of option processing turned + out to be a mess :( Reported by Bart + +* Mon 29 Dec 2003 Andrey Borzenkov 1.2.11 + + - move test for device offline in sd.c to the top in attempt + to fix USB removale problem + + - simplify options processing + +* Sun 23 Nov 2003 Andrey Borzenkov 1.2.10 + + - remove cdrom "open failed" message (it is now under + CDROM_OPEN flag like all others in cdrom_open). + + - fix deadlock on double mount. get_sb_bdev called bdev->release + while holding sb->s_umount and supermount_usage_count in ->release + tried to do sb_get that tried to down_read(sb->s_umount). + + The fix adds flag to struct block_device to mark it supermounted + (like 2.6 does). + +* Mon 27 Oct 2003 Andrey Borzenkov 1.2.9a + + - update for 2.4.23-pre8 (drivers/ide/ide.c + s/get_info_ptr/ide_info_ptr/) + +* Sat 23 Aug 2003 Andrey Borzenkov 1.2.9 + + - fix "Stale NFS handle" for mountpoint without mounted media (reported + by Danny Tholen). This was introduced in 1.2.8 together with change + of behaviour on unmounted subfs. Now d_revalidate always returns + valid for root unless subfs tells different. + + - /proc/fs/supermount/version is read-only, do not set permissions to + rw + + - explicit is needed in init.c on ppc and possibly + other archs (it was implicitly included on i386). Reported by + Danny Tholen + +* Sun 13 Jul 2003 Andrey Borzenkov 1.2.8 + + - 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. + + - add ENTER/LEAVE macros as they slipped in while merging last + changes from 2.5 branch + + - 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 :) + +* Mon 23 Jun 2003 Andrey Borzenkov 1.2.7a + + - fix SUPERMOUNT_BUG_LOCKED_ON without SUPERMOUNT_DEBUG + + - added warning about multipartition devices and media management + + - added example fstab entry and warning about using real file names + for bdev to supermount.txt + + - remove new_subfs_dentry - it is not used anymore and comments + there were wrong anyway + +* Tue 27 May 2003 Andrey Borzenkov 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 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 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 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 1.2.3 + + - dummy version due to script problems + +* Sun 20 Apr 2003 Andrey Borzenkov 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 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 1.2.0 + + - changed proc command format to + [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 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 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 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 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 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 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 -Nur linux-2.4.33-imedia/fs/supermount/dentry.c linux-2.4.33-imedia-patching/fs/supermount/dentry.c --- linux-2.4.33-imedia/fs/supermount/dentry.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.33-imedia-patching/fs/supermount/dentry.c 2006-01-26 15:23:06.000000000 +0200 @@ -0,0 +1,276 @@ +/* + * 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) + * + * $Id: dentry.c,v 1.12 2003/08/23 16:31:05 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 subd=%p", dentry->d_name.name, subdent); +} + + +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 subdent=%p", dentry->d_name.name, err); + + return err; +} + +/* + * Root dentry is always valid unless subfs decides something else + * cf. file.c:supermount_open() + */ +static int +supermount_d_revalidate(struct dentry *dentry, int flags) +{ + struct super_block *sb = dentry->d_sb; + struct dentry *subd; + int rc = (dentry == sb->s_root); + struct vfsmount *mnt; + + ENTER(sb, "dentry=%s", dentry->d_name.name); + + mnt = subfs_prevent_umount(sb); + if (!mnt) + goto out; + + subd = get_subfs_dentry(dentry); + + if (IS_ERR(subd)) + goto allow_umount; + + rc = 1; + if (subd->d_op && subd->d_op->d_revalidate) + rc = subd->d_op->d_revalidate(subd, flags); + + 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 name=%s rc=%d", dentry->d_name.name, name->name, rc); + + return rc; +} + + +/* + * This runs under dcache_lock so it may not sleep. + */ +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", dentry->d_name.name); + + rc = 1; /* fail by default */ + + sdi = dentry->d_fsdata; + if (!sdi) + goto out; + + subd = sdi->dentry; + if (!subd) + goto out; + + if (subd->d_op && subd->d_op && subd->d_op->d_compare) + rc = subd->d_op->d_compare(subd, name1, name2); + else { /* base on fs/dcache.c:d_lookup */ + if (name1->len == name2->len) + rc = memcmp(name1->name, name2->name, name1->len); + } + + +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 inode=%p", dentry->d_name.name, inode); +} + + +/* + * 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 -Nur linux-2.4.33-imedia/fs/supermount/file.c linux-2.4.33-imedia-patching/fs/supermount/file.c --- linux-2.4.33-imedia/fs/supermount/file.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.33-imedia-patching/fs/supermount/file.c 2006-01-26 15:23:06.000000000 +0200 @@ -0,0 +1,688 @@ +/* + * 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) + * + * $Id: file.c,v 1.15 2003/07/13 19:23:31 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_reada = subfile->f_reada; + file->f_version = subfile->f_version; + + fput(subfile); +out: + LEAVE(sb, "file=%p rc=%lld", file, rc); + + return rc; +} + +static ssize_t +supermount_read(struct file *file, char *buf, size_t count, loff_t * ppos) +{ + struct super_block *sb = file->f_dentry->d_sb; + struct inode *inode = file->f_dentry->d_inode; + struct file *subfile; + int write_on = NEED_WRITE_ATIME(inode); + int rc; + + ENTER(sb, "file=%p", file); + + rc = -ESTALE; + if (subfs_check_disk_change(sb)) + goto out; + + subfile = get_subfs_file(file); + rc = PTR_ERR(subfile); + if (IS_ERR(subfile)) + goto out; + + rc = -EINVAL; + if (!subfile->f_op || !subfile->f_op->read) + goto put_subfile; + + rc = subfs_get_access(inode, write_on); + if (rc) + goto put_subfile; + + rc = subfile->f_op->read(subfile, buf, count, ppos); + subfs_put_access(inode, write_on); + if (rc < 0) + goto put_subfile; + + inode->i_atime = subfile->f_dentry->d_inode->i_atime; + + if (rc > 0) + file->f_pos = subfile->f_pos = *ppos; + +put_subfile: + fput(subfile); +out: + LEAVE(sb, "file=%p rc=%d", file, rc); + + return rc; +} + +static ssize_t +supermount_write(struct file *file, const char *buf, + size_t count, loff_t * ppos) +{ + struct super_block *sb = file->f_dentry->d_sb; + struct file *subfile; + int rc; + + ENTER(sb, "file=%p", file); + + rc = -ESTALE; + if (subfs_check_disk_change(sb)) + goto out; + + subfile = get_subfs_file(file); + rc = PTR_ERR(subfile); + if (IS_ERR(subfile)) + goto out; + + rc = 0; + if (subfile->f_op && subfile->f_op->write) + rc = subfile->f_op->write(subfile, buf, count, ppos); + if (rc > 0) { + struct inode *subinode = subfile->f_dentry->d_inode; + + file->f_pos = subfile->f_pos = *ppos; + file->f_mode = subfile->f_mode; + file->f_dentry->d_inode->i_size = subinode->i_size; + file->f_dentry->d_inode->i_blocks = subinode->i_blocks; + file->f_dentry->d_inode->i_mode = subinode->i_mode; + file->f_dentry->d_inode->i_ctime = subinode->i_ctime; + file->f_dentry->d_inode->i_mtime = subinode->i_mtime; + } + + fput(subfile); +out: + LEAVE(sb, "file=%p rc=%d", file, rc); + + return rc; +} + +int +supermount_readdir(struct file *file, void *buf, filldir_t fill_fn) +{ + struct super_block *sb = file->f_dentry->d_sb; + struct inode *inode = file->f_dentry->d_inode; + struct file *subfile; + int write_on = NEED_WRITE_ATIME(inode); + int fake_readdir = 1; + int rc; + + ENTER(sb, "file=%p", file); + + rc = -ESTALE; + if (subfs_check_disk_change(sb)) + goto out; + + subfile = get_subfs_file(file); + rc = PTR_ERR(subfile); + if (IS_ERR(subfile)) + goto out; + + rc = -ENOTDIR; + if (!subfile->f_op || !subfile->f_op->readdir) + goto put_subfile; + + rc = subfs_get_access(inode, write_on); + if (rc) + goto put_subfile; + + /* FIXME should it go before get_access? */ + fake_readdir = 0; + rc = subfile->f_op->readdir(subfile, buf, fill_fn); + subfs_put_access(inode, write_on); + if (rc) + goto put_subfile; + + inode->i_atime = subfile->f_dentry->d_inode->i_atime; + file->f_pos = subfile->f_pos; + +put_subfile: + fput(subfile); +out: + if (fake_readdir && is_file_fake(file)) { + /* cf. supermount_open */ + rc = 0; + } + LEAVE(sb, "file=%p rc=%d fpos=%lld", file, rc, file->f_pos); + + return rc; +} + +static unsigned int +supermount_poll(struct file *file, struct poll_table_struct *table) +{ + struct super_block *sb = file->f_dentry->d_sb; + struct file *subfile; + int rc; + + ENTER(sb, "file=%p", file); + + rc = -ESTALE; + if (subfs_check_disk_change(sb)) + goto out; + + subfile = get_subfs_file(file); + rc = PTR_ERR(subfile); + if (IS_ERR(subfile)) + goto out; + + rc = DEFAULT_POLLMASK; + if (subfile->f_op && subfile->f_op->poll) + rc = subfile->f_op->poll(subfile, table); + + fput(subfile); +out: + LEAVE(sb, "file=%p rc=%d", file, rc); + + return rc; +} + +static int +supermount_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct super_block *sb = file->f_dentry->d_sb; + struct file *subfile; + struct inode *subinode; + int rc; + + ENTER(sb, "file=%p cmd=%u arg=%lu", file, cmd, arg); + + rc = -ESTALE; + if (subfs_check_disk_change(sb)) + goto out; + + subfile = get_subfs_file(file); + rc = PTR_ERR(subfile); + if (IS_ERR(subfile)) + goto out; + + rc = -ENOTTY; + subinode = subfile->f_dentry->d_inode; + if (subfile->f_op && subfile->f_op->ioctl) + rc = subfile->f_op->ioctl(subinode, subfile, cmd, arg); + + /* flags may have been changed by ioctl */ + if (!rc) + set_inode_flags(file->f_dentry->d_inode, subinode); + + fput(subfile); +out: + LEAVE(sb, "file=%p rc=%d", file, rc); + + return rc; +} + +int +supermount_open(struct inode *inode, struct file *file) +{ + struct super_block *sb = inode->i_sb; + struct dentry *subdent; + struct file *subfile = 0; + struct vfsmount *submnt; + int write_on = file->f_mode & FMODE_WRITE; + int fake_open = 1; + int rc; + + ENTER(sb, "inode=%p file=%p", inode, file); + + rc = -ESTALE; + if (subfs_check_disk_change(sb)) + goto out; + + submnt = subfs_get_mnt(sb); + if (!submnt) + goto out; + + subdent = get_subfs_dentry(file->f_dentry); + rc = PTR_ERR(subdent); + if (IS_ERR(subdent)) + goto put_submnt; + + rc = subfs_get_access(inode, write_on); + if (rc) + goto put_subdent; + + /* + * the following is used to simplify error processing. dentry_open + * automatically does mntput and dput in error case, this may result + * in subfs being destroyed + * We just make sure we need to do mntput exactly once here; + * additionally it guards against accidental remounting of subfs + * until we has cleaned up + */ + submnt = mntget(submnt); + subdent = dget(subdent); + + subfile = dentry_open(subdent, submnt, file->f_flags); + rc = PTR_ERR(subfile); + if (IS_ERR(subfile)) + goto put_access; + /* + * no need to do extra mntput and dput, it is done automatically in + * dentry_open on error + */ + + rc = prepare_file(file, subfile); + if (rc) + goto put_subfile; + + subfile->f_mode = file->f_mode; + /* + * this is needed for mmap to work. In current model vm_area + * is associated with superfile; we never explicitly call + * any vm method with subfile as pointer. But many drivers + * attach private structures to this field and mmap of special + * files on supermount fs won't work without it + */ + file->private_data = subfile->private_data; + /* + * we have real subfile now, do not fake anything + */ + fake_open = 0; + + /* + * Now get rid of extra mntget and dget + */ + goto put_subdent; + + /* + * error cleanup + */ + +put_subfile: + fput(subfile); + subfile = 0; +put_access: + subfs_put_access(inode, write_on); +put_subdent: + dput(subdent); +put_submnt: + mntput(submnt); +out: + if (fake_open && inode == sb->s_root->d_inode) { + /* + * always appear to succeed for root open. It allows active + * monitoring of mountpoint using FAM/dnotify and also is less + * surprising for other programs + */ + rc = init_file_info(file, 1); + if (rc) + rc = -ENOMEM; + } + LEAVE(sb, "inode=%p file=%p rc=%d subfile=0x%p", inode, file, rc, subfile); + + return rc; +} + +static int +supermount_flush(struct file *file) +{ + struct super_block *sb = file->f_dentry->d_sb; + struct file *subfile; + int fake_flush = 1; + int rc; + + ENTER(sb, "file=%p", file); + + rc = -ESTALE; + if (subfs_check_disk_change(sb)) + goto out; + + subfile = get_subfs_file(file); + rc = PTR_ERR(subfile); + if (IS_ERR(subfile)) + goto out; + + rc = 0; + fake_flush = 0; + if (subfile->f_op && subfile->f_op->flush) + rc = subfile->f_op->flush(subfile); + + fput(subfile); +out: + if (fake_flush && is_file_fake(file)) { + /* cf. supermount_open */ + rc = 0; + } + LEAVE(sb, "file=%p rc=%d fake=%d", file, rc, fake_flush); + + return rc; +} + +/* + * if subfile is NULL it has already been released in supermount_clean_files + * together with adjusting open/write counters. Else we do it here. + * + * The reason is, it may be called long after media has been changed + * and we definitely do not want this function to mess up the + * new subfs state. + */ +static int +supermount_release(struct inode *inode, struct file *file) +{ + struct file *subfile = 0; + struct super_block *sb = inode->i_sb; + struct supermount_file_info *sfi = file->f_supermount; + + ENTER(sb, "inode=%p file=%p", inode, file); + + subfs_lock(sb); + /* + * FIXME + * this sucks. But there does not seem to be any way + * to distinguish between ENOMEM on _open (legitimate + * case) and anything else (plain bug) + */ + if (sfi) { + list_del(&sfi->list); + subfile = sfi->file; + sfi->file = 0; + } else + supermount_warning(sb, "no supermount file info attached"); + subfs_unlock(sb); + + if (subfile) { + 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; + 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 -Nur linux-2.4.33-imedia/fs/supermount/filemap.c linux-2.4.33-imedia-patching/fs/supermount/filemap.c --- linux-2.4.33-imedia/fs/supermount/filemap.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.33-imedia-patching/fs/supermount/filemap.c 2006-01-26 15:23:06.000000000 +0200 @@ -0,0 +1,210 @@ +/* + * linux/fs/supermount/filemap.c + * + * Initial version for kernel 2.4.21 (C) 2003 Andrey Borzenkov + * (arvidjaar@mail.ru) + * + * $Id: filemap.c,v 1.5 2003/07/13 19:23:31 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 unused) +{ + 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, unused); + + fput(subfile); +out: + LEAVE(sb, "vm=%p page=%p", area, page); + + return page; +} + +#ifdef HAVE_MPROTECT +static int +supermount_vm_mprotect(struct vm_area_struct *area, unsigned int newflags) +{ + 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 + int rc; + + ENTER(sb, "vm=%p flags=%u", area, newflags); + + 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 = -ENOSYS; + sfi = supermount_f(file); + if (sfi->vm_ops && sfi->vm_ops->mprotect) + rc = sfi->vm_ops->mprotect(area, newflags); + + fput(subfile); +out: + LEAVE(sb, "vm=%p rc=%d", area, rc); + + return rc; +} +#endif + +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; +} + +struct vm_operations_struct supermount_vm_ops = { + .open = supermount_vm_open, + .close = supermount_vm_close, + .nopage = supermount_vm_nopage, +#ifdef HAVE_MPROTECT + .mprotect = supermount_vm_mprotect, +#endif +}; diff -Nur linux-2.4.33-imedia/fs/supermount/init.c linux-2.4.33-imedia-patching/fs/supermount/init.c --- linux-2.4.33-imedia/fs/supermount/init.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.33-imedia-patching/fs/supermount/init.c 2006-01-26 15:23:06.000000000 +0200 @@ -0,0 +1,43 @@ +/* + * linux/fs/supermount/init.c + * + * (C) Copyright 2001-2002 Juan Quintela + * Released unde GPL v2. + * Rewritten for kernel 2.4.21 (C) 2003 Andrey Borzenkov + * (arvidjaar@mail.ru) + * + * $Id: init.c,v 1.7 2003/07/22 17:07:30 bor Exp $ + */ + +#include "supermount.h" +#include + +DECLARE_FSTYPE(supermount_fs_type, "supermount", + supermount_read_super, FS_NO_SUBMNT); + +static int __init +init_supermount_fs(void) +{ + int rc = register_filesystem(&supermount_fs_type); + + if (!rc) { + supermount_proc_register(); + } + + return rc; +} + +static void __exit +exit_supermount_fs(void) +{ + supermount_proc_unregister(); + unregister_filesystem(&supermount_fs_type); +} + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Stephen Tweedie, Alexis Mikhailov, Juan Quintela, Andrey Borzenkov and others"); +MODULE_DESCRIPTION("Supermount"); +MODULE_LICENSE("GPL"); +module_init(init_supermount_fs); +module_exit(exit_supermount_fs); diff -Nur linux-2.4.33-imedia/fs/supermount/mediactl.c linux-2.4.33-imedia-patching/fs/supermount/mediactl.c --- linux-2.4.33-imedia/fs/supermount/mediactl.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.33-imedia-patching/fs/supermount/mediactl.c 2006-01-26 15:23:06.000000000 +0200 @@ -0,0 +1,59 @@ +/* + * 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) + * + * $Id: mediactl.c,v 1.7 2003/11/23 11:27:32 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; + + if (!subfs_is_mounted(sb)) + return; + + subsb = subfs_sb(sb); + bdev = subsb->s_bdev; + + /* FIXME is locking needed here? */ + switch (operation) { + case SUPERMOUNT_INC_COUNT: + SUPERMOUNT_BUG_ON(atomic_read(&bdev->bd_supermount) < 0); + atomic_inc(&bdev->bd_supermount); + return; + case SUPERMOUNT_DEC_COUNT: + SUPERMOUNT_BUG_ON(atomic_read(&bdev->bd_supermount) <= 0); + atomic_dec(&bdev->bd_supermount); + return; + } + + if (!bdev->bd_op->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(); + bdev->bd_op->mediactl(subsb->s_dev, operation, opt); + unlock_kernel(); +} diff -Nur linux-2.4.33-imedia/fs/supermount/namei.c linux-2.4.33-imedia-patching/fs/supermount/namei.c --- linux-2.4.33-imedia/fs/supermount/namei.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.33-imedia-patching/fs/supermount/namei.c 2006-01-26 15:23:06.000000000 +0200 @@ -0,0 +1,1092 @@ +/* + * 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) + * + * $Id: namei.c,v 1.23 2003/07/13 19:23:31 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(sb, subi->i_ino); + if (!inode) + goto out; + + /* inode is marked bad in read_inode if sii allocation failed */ + if (is_bad_inode(inode)) { + iput(inode); + goto out; + } + + 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 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; + + rc = subdir->i_op->create(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; +} + +/** + * 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 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); + + 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, int dev) +{ + struct super_block *sb = dir->i_sb; + struct dentry *subdent; + struct inode *subdir; + int rc; + struct vfsmount *mnt; + + ENTER(sb, "dir=%p dentry=%s", dir, dentry->d_name.name); + + mnt = subfs_go_online(sb); + rc = PTR_ERR(mnt); + if (IS_ERR(mnt)) + goto out; + + subdir = get_subfs_inode(dir); + rc = PTR_ERR(subdir); + if (IS_ERR(subdir)) + goto go_offline; + + rc = -EPERM; + if (!subdir->i_op || !subdir->i_op->mknod) + goto put_subdir; + + subdent = get_subfs_dentry(dentry); + rc = PTR_ERR(subdent); + if (IS_ERR(subdent)) + goto put_subdir; + + rc = subfs_get_access(dir, 1); + if (rc) + goto put_subdent; + + rc = subdir->i_op->mknod(subdir, subdent, mode, dev); + subfs_put_access(dir, 1); + if (rc) + goto put_subdent; + + rc = prepare_inode(dentry, subdent); + if (rc) + goto put_subdent; + + dir->i_mtime = subdir->i_mtime; + dir->i_ctime = subdir->i_ctime; + dir->i_nlink = subdir->i_nlink; + dir->i_size = subdir->i_size; + dir->i_blocks = subdir->i_blocks; + +put_subdent: + dput(subdent); +put_subdir: + iput(subdir); +go_offline: + subfs_go_offline(sb, mnt); +out: + LEAVE(sb, "dir=%p dentry=%s rc=%d", dir, dentry->d_name.name, rc); + + return rc; +} + +static int +supermount_rename(struct inode *olddir, struct dentry *olddentry, + struct inode *newdir, struct dentry *newdentry) +{ + struct super_block *sb = olddir->i_sb; + struct dentry *oldsubdent, *newsubdent; + struct inode *oldsubdir, *newsubdir; + int rc; + struct vfsmount *mnt; + + ENTER(sb, "olddir=%p olddentry=%s newdir=%p newdentry=%s", olddir, olddentry->d_name.name, newdir, newdentry->d_name.name); + + mnt = subfs_go_online(sb); + rc = PTR_ERR(mnt); + if (IS_ERR(mnt)) + goto out; + + oldsubdir = get_subfs_inode(olddir); + rc = PTR_ERR(oldsubdir); + if (IS_ERR(oldsubdir)) + goto go_offline; + + rc = -EPERM; + if (!oldsubdir->i_op || !oldsubdir->i_op->rename) + goto put_old_subdir; + + oldsubdent = get_subfs_dentry(olddentry); + rc = PTR_ERR(oldsubdent); + if (IS_ERR(oldsubdent)) + goto put_old_subdir; + + newsubdir = get_subfs_inode(newdir); + rc = PTR_ERR(newsubdir); + if (IS_ERR(newsubdir)) + goto put_old_subdent; + + newsubdent = get_subfs_dentry(newdentry); + rc = PTR_ERR(newsubdent); + if (IS_ERR(newsubdent)) + goto put_new_subdir; + + /* + * If new file exists it will be implcitly unlinked so + * behave like in unlink case. + * If it does not exist we have two write accesses - for + * both old and new directory, I guess it does not matter + * which one is used in this case + */ + if (newdentry->d_inode) + rc = subfs_get_access(newdentry->d_inode, 1); + else + rc = subfs_get_access(olddir, 1); + if (rc) + goto put_new_subdent; + + rc = oldsubdir->i_op->rename(oldsubdir, oldsubdent, + newsubdir, newsubdent); + if (rc) + goto put_write_access; + + /* + * this is strictly speaking conditional on FS_ODD_RENAME + * flag, but as of this writing this flag is set only + * for NFS or intermezzo and it hopefully goes away sometimes ... + * + * Supermount patch adds code to do_kern_mount that checks + * for this flag and refuses mounting + */ + d_move(oldsubdent, newsubdent); + + olddir->i_mtime = oldsubdir->i_mtime; + olddir->i_ctime = oldsubdir->i_ctime; + olddir->i_nlink = oldsubdir->i_nlink; + olddir->i_size = oldsubdir->i_size; + olddir->i_blocks = oldsubdir->i_blocks; + + newdir->i_mtime = newsubdir->i_mtime; + newdir->i_ctime = newsubdir->i_ctime; + newdir->i_nlink = newsubdir->i_nlink; + newdir->i_size = newsubdir->i_size; + newdir->i_blocks = newsubdir->i_blocks; + + olddentry->d_inode->i_nlink = oldsubdent->d_inode->i_nlink; + olddentry->d_inode->i_ctime = oldsubdent->d_inode->i_ctime; + + if (newdentry->d_inode) { + newdentry->d_inode->i_nlink = newsubdent->d_inode->i_nlink; + newdentry->d_inode->i_ctime = newsubdent->d_inode->i_ctime; + } + +put_write_access: + if (newdentry->d_inode) { + /* cf. supermount_unlink */ + if (newdentry->d_inode->i_nlink) + subfs_put_access(newdentry->d_inode, 1); + } else + subfs_put_access(olddir, 1); +put_new_subdent: + dput(newsubdent); +put_new_subdir: + iput(newsubdir); +put_old_subdent: + dput(oldsubdent); +put_old_subdir: + iput(oldsubdir); +go_offline: + subfs_go_offline(sb, mnt); +out: + LEAVE(sb, "olddentry=%s newdentry=%s rc=%d", olddentry->d_name.name, newdentry->d_name.name, rc); + + return rc; +} + +static int +supermount_readlink(struct dentry *dentry, char *buffer , int buflen) +{ + struct super_block *sb = dentry->d_sb; + struct inode *inode = dentry->d_inode; + struct dentry *subdent; + int write_on = NEED_WRITE_ATIME(inode); + int rc; + struct vfsmount *mnt; + + ENTER(sb, "dentry=%s", dentry->d_name.name); + + mnt = subfs_go_online(sb); + rc = PTR_ERR(mnt); + if (IS_ERR(mnt)) + goto out; + + subdent = get_subfs_dentry(dentry); + rc = PTR_ERR(subdent); + if (IS_ERR(subdent)) + goto go_offline; + + rc = -EINVAL; + if (!subdent->d_inode->i_op || !subdent->d_inode->i_op->readlink) + goto put_subdent; + + rc = subfs_get_access(inode, write_on); + if (rc) + goto put_subdent; + + UPDATE_ATIME(subdent->d_inode); + rc = subdent->d_inode->i_op->readlink(subdent, buffer, buflen); + subfs_put_access(inode, write_on); + inode->i_atime = subdent->d_inode->i_atime; + +put_subdent: + dput(subdent); +go_offline: + subfs_go_offline(sb, mnt); +out: + LEAVE(sb, "dentry=%s rc=%d", dentry->d_name.name, rc); + + return rc; +} + +static int +supermount_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + struct super_block *sb = dentry->d_sb; + struct inode *inode = dentry->d_inode; + struct dentry *subdent; + int write_on = NEED_WRITE_ATIME(inode); + int rc; + struct vfsmount *mnt; + + ENTER(sb, "dentry=%s", dentry->d_name.name); + + mnt = subfs_go_online(sb); + rc = PTR_ERR(mnt); + if (IS_ERR(mnt)) + goto out; + + subdent = get_subfs_dentry(dentry); + rc = PTR_ERR(subdent); + if (IS_ERR(subdent)) + goto go_offline; + + rc = subfs_get_access(inode, write_on); + if (rc) + goto put_subdent; + + UPDATE_ATIME(subdent->d_inode); + 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 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; + if (subi->i_op && subi->i_op->permission) + rc = subi->i_op->permission(subi, mask); + else + rc = vfs_permission(subi, mask); + + 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 = vfs_permission(inode, mask); + } + + LEAVE(sb, "inode=0x%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; +} + +/* + * 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, +}; + +struct inode_operations supermount_symlink_iops = { + .readlink = supermount_readlink, + .follow_link = supermount_follow_link, + .setattr = supermount_setattr, +}; + +struct inode_operations supermount_file_iops = { + .setattr = supermount_setattr, +}; diff -Nur linux-2.4.33-imedia/fs/supermount/proc.c linux-2.4.33-imedia-patching/fs/supermount/proc.c --- linux-2.4.33-imedia/fs/supermount/proc.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.33-imedia-patching/fs/supermount/proc.c 2006-01-26 15:23:06.000000000 +0200 @@ -0,0 +1,287 @@ +/* + * linux/fs/supermount/proc.c + * + * Initial version for kernel 2.4.21 (C) 2003 Andrey Borzenkov + * (arvidjaar@mail.ru) + * + * $Id: proc.c,v 1.8 2003/08/09 11:11:36 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.4\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; + } + SET_MODULE_OWNER(supermount_proc_root); + + supermount_proc_version = create_proc_read_entry("version", + S_IFREG | S_IRUGO | S_IWUSR, + supermount_proc_root, + supermount_proc_version_read, + 0); + + if (supermount_proc_version) + SET_MODULE_OWNER(supermount_proc_version); + else + printk(KERN_ERR + "SUPERMOUNT failed to create /proc/fs/supermount/version"); + + supermount_proc_subfs = create_proc_entry("subfs", + S_IFREG | S_IRUGO, + supermount_proc_root); + + if (supermount_proc_subfs) { + supermount_proc_subfs->proc_fops = + &supermount_proc_subfs_operations; + SET_MODULE_OWNER(supermount_proc_subfs); + } 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 -Nur linux-2.4.33-imedia/fs/supermount/subfs.c linux-2.4.33-imedia-patching/fs/supermount/subfs.c --- linux-2.4.33-imedia/fs/supermount/subfs.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.33-imedia-patching/fs/supermount/subfs.c 2006-01-26 15:23:06.000000000 +0200 @@ -0,0 +1,738 @@ +/* + * 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) + * + * $Id: subfs.c,v 1.35 2004/01/14 20:24:55 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); + + subdent = sdi->dentry; + + /* the following protects against races with d_compare */ + spin_lock(&dcache_lock); + sdi->dentry = 0; + spin_unlock(&dcache_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); + +} + +#define NOTIFY_MASK (DN_ACCESS|DN_MODIFY|DN_CREATE|DN_DELETE|DN_RENAME|DN_ATTRIB) +/* + * 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; + struct supermount_inode_info *sii; + struct inode *subi; + + ENTER(sb); + + list_for_each_safe(ptr, n, &sbi->s_inodes) { + sii = list_entry(ptr, struct supermount_inode_info, list); + + subi = sii->inode; + sii->inode = NULL; + list_del_init(&sii->list); + remove_inode_hash(sii->host); + sii->host->i_mapping = &sii->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; + + /* + * 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); + + LEAVE(sb); + + return rc; +} + +static inline int +subfs_remount_rw(struct super_block *sb) +{ + struct super_block *subsb = subfs_sb(sb); + int rc; + + ENTER(sb); + + rc = do_remount_sb(subsb, subsb->s_flags & ~MS_RDONLY, NULL); + + 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; + 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; + } + + 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; + int rc; + + ENTER(sb); + + subsb = subfs_sb(sb); + rc = atomic_read(&subsb->s_media_changed); + if (!rc) + rc = check_disk_change(subsb->s_dev); + + if (rc) + subfs_umount(sb, SUBFS_UMNT_MEDIA); + + LEAVE(sb, "rc=%d", rc); + + return rc; +} + +/* + * this must really be called subfs_active ... + */ +int +subfs_check_disk_change(struct super_block *sb) +{ + int rc; + + ENTER(sb); + + subfs_lock(sb); + if (subfs_is_mounted(sb)) + rc = __subfs_check_disk_change(sb); + else + rc = 1; + subfs_unlock(sb); + + + LEAVE(sb, "rc=%d", rc); + + return rc; +} + +static int +check_and_remount_subfs(struct super_block *sb) +{ + int rc; + + ENTER(sb); + + if (subfs_is_mounted(sb) && !__subfs_check_disk_change(sb)) { + rc = 0; + goto out; + } + + rc = subfs_mount(sb); + +out: + LEAVE(sb, "rc=%d", rc); + + return rc; +} + +static inline void +subfs_tray_lock(struct super_block *sb) +{ + struct supermount_sb_info *sbi = supermount_sbi(sb); + + ENTER(sb); + + SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->lockcount < 0); + + if (!sbi->lockcount++) + supermount_lock_door(sb); + + LEAVE(sb); +} + +static inline void +subfs_tray_unlock(struct super_block *sb) +{ + struct supermount_sb_info *sbi = supermount_sbi(sb); + + ENTER(sb); + + SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->lockcount <= 0); + + if (!--sbi->lockcount) + supermount_unlock_door(sb); + + LEAVE(sb); +} + +static void +subfs_get_read_access(struct super_block *sb) +{ + struct supermount_sb_info *sbi = supermount_sbi(sb); + + ENTER(sb); + + SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->readcount < 0); + + if (!sbi->readcount++ && sbi->tray_lock == TRAY_LOCK_ALWAYS) + subfs_tray_lock(sb); + + LEAVE(sb); +} + +static int +subfs_get_write_access(struct super_block *sb) +{ + struct supermount_sb_info *sbi = supermount_sbi(sb); + int rc; + + ENTER(sb); + + SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->writecount < 0); + + rc = 0; + if (!sbi->writecount) { + if (sb->s_flags & MS_RDONLY) + rc = -EROFS; + else + rc = subfs_remount_rw(sb); + } + + if (!rc && !sbi->writecount++ && sbi->tray_lock != TRAY_LOCK_NEVER) + subfs_tray_lock(sb); + + LEAVE(sb, "rc=%d", rc); + + return rc; +} + +static void +subfs_put_read_access(struct super_block *sb) +{ + struct supermount_sb_info *sbi = supermount_sbi(sb); + + ENTER(sb); + + SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->readcount <= 0); + + if (!--sbi->readcount && sbi->tray_lock == TRAY_LOCK_ALWAYS) + subfs_tray_unlock(sb); + + LEAVE(sb); +} + +static void +subfs_put_write_access(struct super_block *sb) +{ + struct supermount_sb_info *sbi = supermount_sbi(sb); + + ENTER(sb); + + SUPERMOUNT_BUG_LOCKED_ON(sb, sbi->writecount <= 0); + + if (!--sbi->writecount) { + /* + * no need to fsync, it is done automatically on remount + */ + int rc = subfs_remount_ro(sb); + if (rc) + supermount_error(sb, "failed to remount ro, error = %d", rc); + if (sbi->tray_lock != TRAY_LOCK_NEVER) + subfs_tray_unlock(sb); + } + + LEAVE(sb); +} + +int +subfs_get_access(struct inode *inode, int rw) +{ + struct super_block *sb = inode->i_sb; + int rc = 0; + + ENTER(sb); + + subfs_lock(sb); + if (is_inode_obsolete(inode)) + rc = -ESTALE; + else { + struct supermount_inode_info *sii = supermount_i(inode); + + if (rw) { + SUPERMOUNT_BUG_LOCKED_ON(sb, sii->writecount < 0); + rc = subfs_get_write_access(sb); + if (!rc) + sii->writecount++; + } else { + SUPERMOUNT_BUG_LOCKED_ON(sb, sii->readcount < 0); + subfs_get_read_access(sb); + sii->readcount++; + } + } + subfs_unlock(sb); + + LEAVE(sb, "rc=%d", rc); + + return rc; +} + +void +subfs_put_access(struct inode *inode, int rw) +{ + struct super_block *sb = inode->i_sb; + + ENTER(sb); + + subfs_lock(sb); + if (!is_inode_obsolete(inode)) { + struct supermount_inode_info *sii = supermount_i(inode); + + if (rw) { + SUPERMOUNT_BUG_LOCKED_ON(sb, sii->writecount <= 0); + sii->writecount--; + subfs_put_write_access(sb); + } else { + SUPERMOUNT_BUG_LOCKED_ON(sb, sii->readcount <= 0); + sii->readcount--; + subfs_put_read_access(sb); + } + + } + subfs_unlock(sb); + + LEAVE(sb); +} + +struct vfsmount * +subfs_go_online(struct super_block *sb) +{ + int rc; + struct vfsmount *mnt; + + ENTER(sb); + + subfs_lock(sb); + + rc = check_and_remount_subfs(sb); + mnt = ERR_PTR(rc); + if (!rc) + mnt = mntget(subfs_mnt(sb)); + subfs_unlock(sb); + + LEAVE(sb, "mnt=%p", mnt); + + return mnt; +} + +/* + * we do not need inode here, there is nothing really to check for + */ +void +subfs_go_offline(struct super_block *sb, struct vfsmount *mnt) +{ + ENTER(sb); + + subfs_lock(sb); + + SUPERMOUNT_BUG_LOCKED_ON(sb, !mnt); + + mntput(mnt); + + subfs_unlock(sb); + + 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); + + subfs_lock(sb); + SUPERMOUNT_BUG_LOCKED_ON(sb, !mnt); + mntput(mnt); + subfs_unlock(sb); + + LEAVE(sb); + +} + +struct vfsmount * +subfs_get_mnt(struct super_block *sb) +{ + struct vfsmount *mnt = 0; + + 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 = 0; + + 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 -Nur linux-2.4.33-imedia/fs/supermount/super.c linux-2.4.33-imedia-patching/fs/supermount/super.c --- linux-2.4.33-imedia/fs/supermount/super.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.33-imedia-patching/fs/supermount/super.c 2006-01-26 15:23:06.000000000 +0200 @@ -0,0 +1,647 @@ +/* + * 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) + * + * $Id: super.c,v 1.28 2003/12/29 19:28:04 bor Exp $ + */ + +#define S_DBG_TRACE_CURRENT S_DBG_TRACE_SUPER +#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); +} + +static int +init_inode_info(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + struct supermount_inode_info *sii; + int rc = 0; + + ENTER(sb, "inode=%p", inode); + + sii = kmalloc(sizeof(*sii), GFP_KERNEL); + if (!sii) { + /* + * we can't just destroy_inode here because then pointer + * to garbage will be returned. Instead we check for bad + * inode in namei.c:prepare_inode + */ + make_bad_inode(inode); + rc = 1; + goto out; + } + + memset(sii, 0, sizeof(*sii)); + INIT_LIST_HEAD(&sii->list); + sii->host = inode; + sii->inode = 0; + inode->u.generic_ip = sii; + +out: + LEAVE(sb, "inode=%p rc=%d", inode, rc); + + return rc; +} + +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); +} + +static int +parse_options(char *options, struct super_block *sb) +{ + struct supermount_sb_info *sbi = supermount_sbi(sb); + char *this_char; + char *value; + + if (!options) + return 0; + + while ((this_char = options)) { + if (!strncmp(this_char, "--", 2)) + this_char += 2; + if (!*this_char) + break; + if (*this_char == ',') { + /* An empty option, or the option "--", + introduces options to be passed through to + the subfs */ + sbi->s_data = strdup(++this_char); + if (!sbi->s_data) + return -ENOMEM; + return 0; + } + if ((options = strchr(this_char, ','))) + *options++ = 0; + + if ((value = strchr(this_char, '='))) + *value++ = 0; + + if (!strcmp(this_char, "fs")) { + if (!value || !*value) + return -EINVAL; + + if (!strcmp(value, "auto")) { + sbi->s_type = default_fs_types; + continue; + } + + sbi->s_type = strdup(value); + if (!sbi->s_type) + return -ENOMEM; + } else if (!strcmp(this_char, "dev")) { + sbi->devname = strdup(value); + if (!sbi->devname) + return -ENOMEM; + } else if (!strcmp(this_char, "debug")) { + if (value) + sbi->s_debug = simple_strtoul(value, 0, 0); + else + sbi->s_debug = S_DBG_DEBUG; + } else if (!strcmp(this_char, "tray_lock")) { + if (!value || !*value) + return -EINVAL; + + if (!strcmp(value, "always")) + sbi->tray_lock = TRAY_LOCK_ALWAYS; + else if (!strcmp(value, "onwrite")) + sbi->tray_lock = TRAY_LOCK_ONWRITE; + else if (!strcmp(value, "never")) + sbi->tray_lock = TRAY_LOCK_NEVER; + else + return -EINVAL; + } else if (!strcmp(this_char, "no_tray_lock")) { + /* legacy */ + sbi->tray_lock = TRAY_LOCK_NEVER; + } else if (!strcmp(this_char, "fail_statfs_until_mount")) { + do { /* legacy */ } while (0); + } else { + supermount_error(sb, "Unrecognized mount option \"%s\"", + this_char); + return -EINVAL; + } + } + 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 = NODEV; + 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); + + if (!inode) + return NULL; + + if (init_inode_info(inode)) { + make_bad_inode(inode); + iput(inode); + return NULL; + } + + inode->i_version = 0; + + supermount_init_root_inode(inode); + + return inode; +} + +/* ========================== used in DECLARE_FSTYPE ================*/ + +/* read_super: the main mount() entry point into the VFS layer. */ +struct super_block * +supermount_read_super(struct super_block *sb, void *data, int silent) +{ + struct inode *root_inode; + struct dentry *root; + struct supermount_sb_info *sbi = create_sbi(sb); + + if (!sbi) + goto fail_no_memory; + + sb->u.generic_sbp = sbi; + + if (parse_options((char *) data, sb)) + goto fail_parsing; + + if (!sbi->devname) { + supermount_error(sb, "no dev= option"); + goto fail_parsing; + } + + if (!sbi->s_type) { + sbi->s_type = default_fs_types; + supermount_warning(sb, "no fs= option, assuming fs=auto"); + } + + 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 sb; + +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 NULL; + +} + +/* ======================= super_operations methods ==================== */ + +/* + * we never need to create inode except for the special root inode case. + * Supermount inode is always requested after it is known that subfs inode + * exists. + */ +static void +supermount_read_inode(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + + ENTER(sb, "inode=%p", inode); + + init_inode_info(inode); + + LEAVE(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); + + sb->s_dev = NODEV; + + supermount_proc_remove(sbi); + + LEAVE(sb); + + sb->u.generic_sbp = 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); + + /* it is possible for special case of failed memory allocation */ + if (!sii) + goto out; + + mnt = subfs_prevent_umount(sb); + if (!mnt) + goto free_sii; + + 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); + +free_sii: + kfree(sii); + +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 statfs *buf) +{ + int rc = 0; + struct super_block *subsb; + struct vfsmount *mnt; + + ENTER(sb); + + (void)subfs_check_disk_change(sb); + + mnt = subfs_prevent_umount(sb); + if (!mnt) + goto out; + + subsb = subfs_get_sb(sb); + if (subsb && subsb->s_op && subsb->s_op->statfs) + rc = subsb->s_op->statfs(subsb, buf); + + subfs_allow_umount(sb, mnt); +out: + buf->f_type = SUPERMOUNT_SUPER_MAGIC; + + 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; +} + +/* + * ->alloc_inode: no needed, we do not provide our own memory + * management + * ->destroy_inode: TODO may be for removing inode_info + * ->read_inode2: 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 = { + .read_inode = supermount_read_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 -Nur linux-2.4.33-imedia/fs/supermount/supermount.h linux-2.4.33-imedia-patching/fs/supermount/supermount.h --- linux-2.4.33-imedia/fs/supermount/supermount.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.33-imedia-patching/fs/supermount/supermount.h 2006-01-26 15:23:06.000000000 +0200 @@ -0,0 +1,494 @@ +#ifndef _SUPERMOUNT_I_H +#define _SUPERMOUNT_I_H + +/* + * $Id: supermount.h,v 1.55 2004/01/14 20:24:56 bor Exp $ + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The supermount superblock magic number + */ + +#define SUPERMOUNT_SUPER_MAGIC 0x9fa1 +#define SUPERMOUNT_VERSION "1.2.11a" + +#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(sb, x) do { } while (0) +#define SUPERMOUNT_BUG_LOCKED_ON(x) do { } while (0) + +#define ENTER(sb, args...) do { (void)sb; } while (0) +#define LEAVE(sb, args...) do { (void)sb; } while (0) +#endif /* SUPERMOUNT_DEBUG */ + +#define supermount_warning(sb, ...) \ +do { \ + struct supermount_sb_info *sbi = supermount_sbi(sb); \ + char *dev = sbi->devname ? sbi->devname : "unknown"; \ +\ + printk("%sSUPERMOUNT WARNING [dev=%s] ", KERN_WARNING, dev); \ + printk(__VA_ARGS__); \ + printk("\n"); \ +} while(0) + +#define supermount_error(sb, ...) \ +do { \ + struct supermount_sb_info *sbi = supermount_sbi(sb); \ + char *dev = sbi->devname ? sbi->devname : "unknown"; \ +\ + printk("%sSUPERMOUNT ERROR [dev=%s] ", KERN_ERR, dev); \ + printk(__VA_ARGS__); \ + printk("\n"); \ +} while(0) + +/* + * The following is drived from fs/inode.c:update_atime() + */ +#define NEED_WRITE_ATIME(inode) \ + (!(IS_NOATIME(inode) || \ + (IS_NODIRATIME(inode) && S_ISDIR(inode->i_mode)) || \ + IS_RDONLY(inode))) + + +/* + * supermount super-block data in memory + */ +struct supermount_sb_info; +struct supermount_sb_info { + /* == options == */ + unsigned long s_debug; /* debug flags S_DBG_* */ + char *s_type; /* Type of fs to be sub-mounted */ + char *devname; /* Where to mount the subfs from */ + char *s_data; /* Data to pass when mounting subfs */ + /* == end of options == */ + struct vfsmount *s_undermount; + /* Mount point for subfs */ + int readcount; /* Refcount of read access + on the filesystem */ + int writecount; /* Refcount of write access + on the filesystem */ + int lockcount; /* Refcount of requests to lock tray */ + struct list_head s_inodes; + /* list of active inodes */ + struct list_head s_files; + /* list of active files */ + struct list_head s_dentries; + /* list of active dentries */ + struct semaphore sem; + struct super_block *host; + /* needed for procfs support */ + struct supermount_sb_info *next; + /* list of all supermount fs */ + unsigned int tray_lock:2; + unsigned int disabled:1; + unsigned int rw:1; +}; + +static inline struct supermount_sb_info * +supermount_sbi(struct super_block *sb) +{ + struct supermount_sb_info *sbi; + SUPERMOUNT_BUG_ON(!sb); + SUPERMOUNT_BUG_ON(sb->s_type != &supermount_fs_type); + + sbi = (struct supermount_sb_info *) (sb->u.generic_sbp); + SUPERMOUNT_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; + struct inode *host; + int readcount; + int writecount; +}; + +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)); + SUPERMOUNT_BUG_ON(is_bad_inode(inode) ^ (inode->u.generic_ip == 0)); + + return inode->u.generic_ip; +} + +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 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); + supermount_mediactl(sb, MEDIA_UNLOCK, 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 { \ + printk(KERN_INFO "Supermount version %s for kernel 2.4\n", \ + SUPERMOUNT_VERSION); \ +} 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_read_super(struct super_block *, + void *, int); +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 -Nur linux-2.4.33-imedia/fs/udf/super.c linux-2.4.33-imedia-patching/fs/udf/super.c --- linux-2.4.33-imedia/fs/udf/super.c 2002-08-03 03:39:45.000000000 +0300 +++ linux-2.4.33-imedia-patching/fs/udf/super.c 2006-01-26 15:23:06.000000000 +0200 @@ -268,6 +268,11 @@ uopt->nls_map = load_nls(val); uopt->flags |= (1 << UDF_FLAG_NLS_MAP); } +#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) + /* Silently ignore NLS option */ + else if (!strcmp (opt, "codepage")) + /* Don't do anything ;-) */ ; +#endif #endif else if (!strcmp(opt, "utf8") && !val) uopt->flags |= (1 << UDF_FLAG_UTF8); diff -Nur linux-2.4.33-imedia/include/linux/cdrom.h linux-2.4.33-imedia-patching/include/linux/cdrom.h --- linux-2.4.33-imedia/include/linux/cdrom.h 2006-01-11 19:27:30.000000000 +0200 +++ linux-2.4.33-imedia-patching/include/linux/cdrom.h 2006-01-26 15:23:06.000000000 +0200 @@ -781,6 +781,7 @@ extern int cdrom_release(struct inode *, struct file *); extern int cdrom_ioctl(struct inode *, struct file *, unsigned, unsigned long); extern int cdrom_media_changed(kdev_t); +extern int cdrom_mediactl (kdev_t dev, int op, int optarg); extern int register_cdrom(struct cdrom_device_info *cdi); extern int unregister_cdrom(struct cdrom_device_info *cdi); diff -Nur linux-2.4.33-imedia/include/linux/fs.h linux-2.4.33-imedia-patching/include/linux/fs.h --- linux-2.4.33-imedia/include/linux/fs.h 2006-01-11 19:27:17.000000000 +0200 +++ linux-2.4.33-imedia-patching/include/linux/fs.h 2006-01-26 15:23:06.000000000 +0200 @@ -92,6 +92,7 @@ #define FS_SINGLE 8 /* Filesystem that can have only one superblock */ #define FS_NOMOUNT 16 /* Never mount from userland */ #define FS_LITTER 32 /* Keeps the tree in dcache */ +#define FS_NO_SUBMNT 64 /* Prevent mounting over this filesystem */ #define FS_ODD_RENAME 32768 /* Temporary stuff; will go away as soon * as nfs_rename() will be cleaned up */ @@ -111,6 +112,7 @@ #define MS_MOVE 8192 #define MS_REC 16384 #define MS_VERBOSE 32768 +#define MS_SUPERMOUNTED (1<<17) #define MS_ACTIVE (1<<30) #define MS_NOUSER (1<<31) @@ -435,6 +437,9 @@ const struct block_device_operations *bd_op; struct semaphore bd_sem; /* open/close mutex */ struct list_head bd_inodes; +#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) + atomic_t bd_supermount; +#endif }; struct inode { @@ -585,6 +590,10 @@ /* preallocated helper kiobuf to speedup O_DIRECT */ struct kiobuf *f_iobuf; long f_iobuf_lock; +#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) + void *f_supermount; +#endif + }; extern spinlock_t files_lock; #define file_list_lock() spin_lock(&files_lock); @@ -809,6 +818,10 @@ * non-directories) are allowed, but not unconnected diretories. */ struct semaphore s_nfsd_free_path_sem; +#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) + atomic_t s_media_changed; +#endif + }; /* @@ -851,6 +864,19 @@ int (*check_media_change) (kdev_t); int (*revalidate) (kdev_t); struct module *owner; + int (*mediactl) (kdev_t dev, int op, int optarg); +}; + +/* + * These are the "op" operation codes for mediactl() media control + * calls for device special files + * Some of them are consumed internally by supermount + */ +enum { + MEDIA_LOCK = 1234, /* Lock the drive door */ + MEDIA_UNLOCK, /* Unlock the drive door */ + SUPERMOUNT_INC_COUNT, + SUPERMOUNT_DEC_COUNT, }; /* @@ -1027,6 +1053,8 @@ extern struct vfsmount *kern_mount(struct file_system_type *); extern int may_umount(struct vfsmount *); extern long do_mount(char *, char *, char *, unsigned long, void *); +extern struct vfsmount *do_kern_mount(const char *type, int flags, char *name, void *data); +extern int do_remount_sb(struct super_block *sb, int flags, void * data); #define kern_umount mntput diff -Nur linux-2.4.33-imedia/include/linux/ide.h linux-2.4.33-imedia-patching/include/linux/ide.h --- linux-2.4.33-imedia/include/linux/ide.h 2006-01-11 20:34:50.000000000 +0200 +++ linux-2.4.33-imedia-patching/include/linux/ide.h 2006-01-26 15:23:06.000000000 +0200 @@ -1162,6 +1162,7 @@ ide_startstop_t (*special)(ide_drive_t *); ide_proc_entry_t *proc; int (*init)(void); + int (*mediactl)(ide_drive_t *, kdev_t, int, int); int (*attach)(ide_drive_t *); void (*ata_prebuilder)(ide_drive_t *); void (*atapi_prebuilder)(ide_drive_t *); @@ -1607,6 +1608,7 @@ extern int idedisk_init(void); extern int ide_cdrom_attach(ide_drive_t *); extern int ide_cdrom_init(void); +extern int ide_cdrom_mediactl (ide_drive_t *, kdev_t, int, int); extern int idetape_attach(ide_drive_t *); extern int idetape_init(void); extern int idefloppy_attach(ide_drive_t *); diff -Nur linux-2.4.33-imedia/include/linux/supermount_media.h linux-2.4.33-imedia-patching/include/linux/supermount_media.h --- linux-2.4.33-imedia/include/linux/supermount_media.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-2.4.33-imedia-patching/include/linux/supermount_media.h 2006-01-26 15:23:06.000000000 +0200 @@ -0,0 +1,42 @@ +#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) + +static inline int +supermount_usage_count(kdev_t kdev, int count) +{ + struct block_device *bdev = bdget(kdev_t_to_nr(kdev)); + + count -= atomic_read(&bdev->bd_supermount); + if (count < 0) + count = 0; + bdput(bdev); + + return count; +} + +static inline int +dev_is_supermounted(kdev_t kdev) +{ + int rc = 0; + struct block_device *bdev = bdget(kdev_t_to_nr(kdev)); + + rc = atomic_read(&bdev->bd_supermount) > 0; + bdput(bdev); + + return rc; +} + +#else + +static inline int +supermount_usage_count(kdev_t kdev, int count) +{ + return count; +} + +static inline int +dev_is_supermounted(kdev_t kdev) +{ + return 0; +} + +#endif /* SUPERMOUNT */ diff -Nur linux-2.4.33-imedia/kernel/ksyms.c linux-2.4.33-imedia-patching/kernel/ksyms.c --- linux-2.4.33-imedia/kernel/ksyms.c 2006-01-26 15:21:21.000000000 +0200 +++ linux-2.4.33-imedia-patching/kernel/ksyms.c 2006-01-26 15:23:06.000000000 +0200 @@ -317,6 +317,7 @@ EXPORT_SYMBOL(unregister_chrdev); EXPORT_SYMBOL(register_blkdev); EXPORT_SYMBOL(unregister_blkdev); +EXPORT_SYMBOL(get_blkfops); EXPORT_SYMBOL(tty_register_driver); EXPORT_SYMBOL(tty_unregister_driver); EXPORT_SYMBOL(tty_std_termios); @@ -358,6 +359,8 @@ EXPORT_SYMBOL(kern_mount); EXPORT_SYMBOL(__mntput); EXPORT_SYMBOL(may_umount); +EXPORT_SYMBOL(do_kern_mount); +EXPORT_SYMBOL(do_remount_sb); /* executable format registration */ EXPORT_SYMBOL(register_binfmt);