--- linux/arch/i386/Makefile.~1~ Thu Sep 4 11:22:18 1980 +++ linux/arch/i386/Makefile Mon Nov 24 19:51:14 1997 @@ -60,6 +60,7 @@ endif CFLAGS := $(CFLAGS) -pipe +#CFLAGS := $(CFLAGS) -save-temps ifdef CONFIG_M386 CFLAGS := $(CFLAGS) -m386 -DCPU=386 --- linux/drivers/block/amiflop.c.~1~ Thu May 16 07:05:10 1996 +++ linux/drivers/block/amiflop.c Mon Nov 24 19:51:14 1997 @@ -1685,6 +1685,7 @@ NULL, /* fasync */ amiga_floppy_change, /* check_media_change */ NULL, /* revalidate */ + NULL /* mediactl */ }; static void fd_block_done(int irq, struct pt_regs *fp, void *dummy) --- linux/drivers/block/ataflop.c.~1~ Thu May 16 07:05:10 1996 +++ linux/drivers/block/ataflop.c Mon Nov 24 19:51:14 1997 @@ -1844,6 +1844,7 @@ NULL, /* fasync */ check_floppy_change, /* media_change */ floppy_revalidate, /* revalidate */ + NULL /* mediactl */ }; int atari_floppy_init (void) --- linux/drivers/block/floppy.c.~1~ Sun Aug 10 23:19:01 1997 +++ linux/drivers/block/floppy.c Mon Nov 24 19:51:14 1997 @@ -3736,6 +3736,7 @@ NULL, /* fasync */ check_floppy_change, /* media_change */ floppy_revalidate, /* revalidate */ + NULL /* mediactl */ }; /* --- linux/drivers/block/hd.c.~1~ Fri Mar 1 05:50:39 1996 +++ linux/drivers/block/hd.c Mon Nov 24 19:51:14 1997 @@ -1044,7 +1044,11 @@ NULL, /* mmap */ hd_open, /* open */ hd_release, /* release */ - block_fsync /* fsync */ + block_fsync, /* fsync */ + NULL, /* fasync */ + NULL, /* media_change */ + NULL, /* revalidate */ + NULL /* mediactl */ }; int hd_init(void) --- linux/drivers/block/ide-cd.c.~1~ Sun Oct 19 21:26:02 1997 +++ linux/drivers/block/ide-cd.c Mon Nov 24 19:51:14 1997 @@ -2588,6 +2588,19 @@ } +int ide_cdrom_mediactl (ide_drive_t *drive, int op, int optarg) +{ + switch (op) { + case MEDIA_LOCK: + case MEDIA_UNLOCK: + return cdrom_lockdoor (drive, op == MEDIA_LOCK, NULL); + default: + return -ENOSYS; + } + return 0; +} + + int ide_cdrom_open (struct inode *ip, struct file *fp, ide_drive_t *drive) { /* no write access */ --- linux/drivers/block/ide-tape.c.~1~ Fri Dec 13 23:14:52 1996 +++ linux/drivers/block/ide-tape.c Mon Nov 24 19:51:14 1997 @@ -1012,7 +1012,8 @@ NULL, /* fsync */ NULL, /* fasync */ NULL, /* check_media_change */ - NULL /* revalidate */ + NULL, /* revalidate */ + NULL /* mediactl */ }; --- linux/drivers/block/ide.c.~1~ Sun Oct 19 21:26:02 1997 +++ linux/drivers/block/ide.c Mon Nov 24 19:51:14 1997 @@ -2355,6 +2355,19 @@ return 0; } +static int ide_mediactl (kdev_t i_rdev, int op, int optarg) +{ + ide_drive_t *drive; + + if ((drive = get_info_ptr(i_rdev)) == NULL) + return -ENODEV; +#ifdef CONFIG_BLK_DEV_IDECD + if (drive->media == ide_cdrom) + return ide_cdrom_mediactl (drive, op, optarg); +#endif /* CONFIG_BLK_DEV_IDECD */ + return 0; +} + void ide_fixstring (byte *s, const int bytecount, const int byteswap) { byte *p = s, *end = &s[bytecount & ~1]; /* bytecount must be even */ @@ -3467,10 +3480,11 @@ NULL, /* mmap */ ide_open, /* open */ ide_release, /* release */ - block_fsync /* fsync */ - ,NULL, /* fasync */ + block_fsync, /* fsync */ + NULL, /* fasync */ ide_check_media_change, /* check_media_change */ - revalidate_disk /* revalidate */ + revalidate_disk, /* revalidate */ + ide_mediactl /* mediactl */ }; #ifdef CONFIG_PCI --- linux/drivers/block/ide.h.~1~ Sun Oct 19 21:26:02 1997 +++ linux/drivers/block/ide.h Mon Nov 24 19:54:27 1997 @@ -651,6 +651,7 @@ void ide_do_rw_cdrom (ide_drive_t *, unsigned long); int ide_cdrom_ioctl (ide_drive_t *, struct inode *, struct file *, unsigned int, unsigned long); int ide_cdrom_check_media_change (ide_drive_t *); +int ide_cdrom_mediactl (ide_drive_t *, int, int); int ide_cdrom_open (struct inode *, struct file *, ide_drive_t *); void ide_cdrom_release (struct inode *, struct file *, ide_drive_t *); void ide_cdrom_setup (ide_drive_t *); --- linux/drivers/block/loop.c.~1~ Sun Oct 19 21:26:03 1997 +++ linux/drivers/block/loop.c Mon Nov 24 19:51:14 1997 @@ -529,7 +529,12 @@ lo_ioctl, /* ioctl */ NULL, /* mmap */ lo_open, /* open */ - lo_release /* release */ + lo_release, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* media_change */ + NULL, /* revalidate */ + NULL /* mediactl */ }; /* --- linux/drivers/block/md.c.~1~ Thu Jul 4 10:59:23 1996 +++ linux/drivers/block/md.c Mon Nov 24 19:51:14 1997 @@ -400,7 +400,11 @@ NULL, md_open, md_release, - block_fsync + block_fsync, + NULL, + NULL, + NULL, + NULL }; int md_map (int minor, kdev_t *rdev, unsigned long *rsector, unsigned long size) --- linux/drivers/cdrom/aztcd.c.~1~ Thu Jun 6 19:04:12 1996 +++ linux/drivers/cdrom/aztcd.c Mon Nov 24 19:51:14 1997 @@ -348,7 +348,8 @@ NULL, /* fsync */ NULL, /* fasync*/ check_aztcd_media_change, /*media change*/ - NULL /* revalidate*/ + NULL, /* revalidate*/ + NULL /* mediactl */ }; /* Aztcd State Machine: Controls Drive Operating State */ --- linux/drivers/cdrom/cdrom.c.~1~ Thu Sep 4 11:22:54 1980 +++ linux/drivers/cdrom/cdrom.c Mon Nov 24 19:51:15 1997 @@ -29,6 +29,8 @@ int cdrom_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg); int cdrom_media_changed(kdev_t dev); +int cdrom_mediactl (kdev_t dev, int op, int optarg); + struct file_operations cdrom_fops = { @@ -44,10 +46,11 @@ NULL, /* fsync */ NULL, /* fasync */ cdrom_media_changed, /* media_change */ - NULL /* revalidate */ + NULL, /* revalidate */ + cdrom_mediactl /* mediactl */ }; -static struct cdrom_device_ops *cdromdevs[MAX_BLKDEV] = { +struct cdrom_device_ops *cdromdevs[MAX_BLKDEV] = { NULL, }; @@ -221,6 +224,23 @@ return -EINVAL; return media_changed(dev, 0); } + +int cdrom_mediactl (kdev_t dev, int op, int optarg) +{ + struct cdrom_device_ops *cdo = cdromdevs[MAJOR(dev)]; + switch (op) { + case MEDIA_LOCK: + case MEDIA_UNLOCK: + if (cdo->capability & ~cdo->mask & CDC_LOCK && + cdo->options & CDO_LOCK) + cdo->lock_door(dev, (op == MEDIA_LOCK)); + break; + default: + return -ENOSYS; + } + return 0; +} + /* Requests to the low-level drivers will /always/ be done in the following format convention: --- linux/drivers/cdrom/cdu31a.c.~1~ Sun Aug 10 23:19:02 1997 +++ linux/drivers/cdrom/cdu31a.c Mon Nov 24 19:51:15 1997 @@ -2955,7 +2955,8 @@ NULL, /* fsync */ NULL, /* fasync */ scd_disk_change, /* media_change */ - NULL /* revalidate */ + NULL, /* revalidate */ + NULL /* mediactl */ }; --- linux/drivers/cdrom/gscd.c.~1~ Sun May 12 19:13:45 1996 +++ linux/drivers/cdrom/gscd.c Mon Nov 24 19:51:15 1997 @@ -162,7 +162,8 @@ NULL, /* fsync */ NULL, /* fasync*/ check_gscd_med_chg, /* media change */ - NULL /* revalidate */ + NULL, /* revalidate */ + NULL /* mediactl */ }; /* --- linux/drivers/cdrom/mcd.c.~1~ Fri Dec 13 23:14:54 1996 +++ linux/drivers/cdrom/mcd.c Mon Nov 24 19:51:15 1997 @@ -1166,7 +1166,8 @@ NULL, /* fsync */ NULL, /* fasync */ check_mcd_change, /* media change */ - NULL /* revalidate */ + NULL, /* revalidate */ + NULL /* mediactl */ }; --- linux/drivers/cdrom/mcdx.c.~1~ Thu Sep 4 11:22:22 1980 +++ linux/drivers/cdrom/mcdx.c Mon Nov 24 19:51:15 1997 @@ -276,7 +276,7 @@ static struct file_operations mcdx_fops = { NULL, /* lseek - use kernel default */ block_read, /* read - general block-dev read */ - block_write, /* write - general block-dev write */ + block_write, /* write - general block-dev write */ NULL, /* no readdir */ NULL, /* no select */ mcdx_ioctl, /* ioctl() */ @@ -286,7 +286,8 @@ NULL, /* fsync */ NULL, /* fasync */ check_mcdx_media_change, /* media_change */ - NULL /* revalidate */ + NULL, /* revalidate */ + NULL /* mediactl */ }; /* KERNEL INTERFACE FUNCTIONS **************************************/ --- linux/drivers/cdrom/optcd.c.~1~ Thu Jun 6 19:05:02 1996 +++ linux/drivers/cdrom/optcd.c Mon Nov 24 19:51:15 1997 @@ -2008,7 +2008,8 @@ NULL, /* fsync */ NULL, /* fasync */ opt_media_change, /* media change */ - NULL /* revalidate */ + NULL, /* revalidate */ + NULL /* mediactl */ }; --- linux/drivers/cdrom/sbpcd.c.~1~ Thu Sep 4 11:23:25 1980 +++ linux/drivers/cdrom/sbpcd.c Mon Nov 24 19:51:15 1997 @@ -5252,7 +5252,8 @@ NULL, /* fsync */ NULL, /* fasync */ sbpcd_chk_disk_change, /* media_change */ - NULL /* revalidate */ + NULL, /* revalidate */ + NULL /* mediactl */ }; /*==========================================================================*/ /* --- linux/drivers/cdrom/sjcd.c.~1~ Fri Apr 12 07:49:33 1996 +++ linux/drivers/cdrom/sjcd.c Mon Nov 24 19:51:15 1997 @@ -1427,7 +1427,8 @@ NULL, /* fsync */ NULL, /* fasync */ sjcd_disk_change, /* media change */ - NULL /* revalidate */ + NULL, /* revalidate */ + NULL /* mediactl */ }; /* --- linux/drivers/cdrom/sonycd535.c.~1~ Tue Apr 2 06:43:06 1996 +++ linux/drivers/cdrom/sonycd535.c Mon Nov 24 19:51:15 1997 @@ -1464,19 +1464,20 @@ static struct file_operations cdu_fops = { - NULL, /* lseek - default */ - block_read, /* read - general block-dev read */ + NULL, /* lseek - default */ + block_read, /* read - general block-dev read */ block_write, /* write - general block-dev write */ - NULL, /* readdir - bad */ - NULL, /* select */ - cdu_ioctl, /* ioctl */ - NULL, /* mmap */ - cdu_open, /* open */ + NULL, /* readdir - bad */ + NULL, /* select */ + cdu_ioctl, /* ioctl */ + NULL, /* mmap */ + cdu_open, /* open */ cdu_release, /* release */ - NULL, /* fsync */ - NULL, /* fasync */ - cdu535_check_media_change, /* check media change */ - NULL /* revalidate */ + NULL, /* fsync */ + NULL, /* fasync */ + cdu535_check_media_change, /* check media change */ + NULL, /* revalidate */ + NULL /* mediactl */ }; /* --- linux/drivers/scsi/sd.c.~1~ Sun Oct 19 21:26:19 1997 +++ linux/drivers/scsi/sd.c Mon Nov 24 19:51:15 1997 @@ -178,7 +178,8 @@ block_fsync, /* fsync */ NULL, /* fasync */ check_scsidisk_media_change, /* Disk change */ - fop_revalidate_scsidisk /* revalidate */ + fop_revalidate_scsidisk, /* revalidate */ + NULL /* mediactl */ }; static struct gendisk sd_gendisk = { --- linux/drivers/scsi/sr.c.~1~ Mon Nov 24 19:50:44 1997 +++ linux/drivers/scsi/sr.c Mon Nov 24 19:51:15 1997 @@ -66,6 +66,7 @@ void requeue_sr_request (Scsi_Cmnd * SCpnt); static int check_cdrom_media_change(kdev_t); +static int cdrom_mediactl(kdev_t, int, int); static void sr_release(struct inode * inode, struct file * file) { @@ -95,7 +96,8 @@ NULL, /* fsync */ NULL, /* fasync */ check_cdrom_media_change, /* Disk change */ - NULL /* revalidate */ + NULL, /* revalidate */ + cdrom_mediactl /* mediactl */ }; /* @@ -141,6 +143,35 @@ if (retval) scsi_CDs[target].needs_sector_size = 1; }; return retval; +} + +/* + * This function performs media control operations. Currently the + * only functions used are MEDIA_LOCK and MEDIA_UNLOCK, to lock and + * unlock the drive door. + */ + +int cdrom_mediactl(kdev_t full_dev, int op, int optarg) { + int target; + struct inode inode; + + target = MINOR(full_dev); + + if (target >= sr_template.nr_dev) { + printk("CD-ROM request error: invalid device.\n"); + return -ENODEV; + }; + + inode.i_rdev = full_dev; /* This is all we really need here */ + + switch (op) { + case MEDIA_LOCK: + return sr_ioctl(&inode, NULL, SCSI_IOCTL_DOORLOCK, 0); + case MEDIA_UNLOCK: + return sr_ioctl(&inode, NULL, SCSI_IOCTL_DOORLOCK, 0); + default: + return -ENOSYS; + } } /* --- linux/fs/Config.in.~1~ Sun Oct 19 21:26:21 1997 +++ linux/fs/Config.in Mon Nov 24 19:51:15 1997 @@ -5,6 +5,7 @@ comment 'Filesystems' bool 'Quota support' CONFIG_QUOTA +bool 'Supermount removable media support' CONFIG_SUPERMOUNT tristate 'Minix fs support' CONFIG_MINIX_FS tristate 'Extended fs support' CONFIG_EXT_FS tristate 'Second extended fs support' CONFIG_EXT2_FS --- linux/fs/Makefile.~1~ Sun Oct 19 21:26:21 1997 +++ linux/fs/Makefile Mon Nov 24 19:51:15 1997 @@ -17,7 +17,7 @@ MOD_LIST_NAME := FS_MODULES ALL_SUB_DIRS = minix ext ext2 fat msdos vfat proc isofs nfs xiafs umsdos \ - hpfs sysv smbfs ncpfs ufs affs autofs + hpfs sysv smbfs ncpfs ufs affs autofs supermount ifeq ($(CONFIG_QUOTA),y) O_OBJS += dquot.o @@ -139,6 +139,14 @@ ifeq ($(CONFIG_HPFS_FS),m) MOD_SUB_DIRS += hpfs endif +endif + +ifeq ($(CONFIG_SUPERMOUNT),y) +SUB_DIRS += supermount +#else +# ifeq ($(CONFIG_SUPERMOUNT),m) +# MOD_SUB_DIRS += supermount +# endif endif ifeq ($(CONFIG_UFS_FS),y) --- linux/fs/devices.c.~1~ Mon May 13 05:36:19 1996 +++ linux/fs/devices.c Mon Nov 24 19:51:15 1997 @@ -187,15 +187,14 @@ } /* - * This routine checks whether a removable media has been changed, - * and invalidates all buffer-cache-entries in that case. This - * is a relatively slow routine, so we have to try to minimize using - * it. Thus it is called only upon a 'mount' or 'open'. This - * is the best way of combining speed and utility, I think. - * People changing diskettes in the middle of an operation deserve - * to loose :-) - */ -int check_disk_change(kdev_t dev) + * These routines checks whether a removable media has been changed, + * and (for check_disk_change only) invalidate all + * buffer-cache-entries in that case. This is a relatively slow + * routine, so we have to try to minimize using it. Thus it is called + * only upon a 'mount' or 'open'. This is the best way of combining + * speed and utility, I think. People changing diskettes in the + * middle of an operation deserve to loose :-) */ +int query_disk_change(kdev_t dev) { int i; struct file_operations * fops; @@ -208,17 +207,37 @@ if (!fops->check_media_change(dev)) return 0; + return 1; +} + +int check_disk_change(kdev_t dev) +{ + if (!query_disk_change(dev)) + return 0; printk(KERN_DEBUG "VFS: Disk change detected on device %s\n", kdevname(dev)); + invalidate_media(dev); + return 1; +} + +void invalidate_media(kdev_t dev) +{ + int i; + struct file_operations * fops; + + i = MAJOR(dev); + if (i >= MAX_BLKDEV) + return; + fops = blkdevs[i].fops; for (i=0 ; irevalidate) + if (fops && fops->revalidate) fops->revalidate(dev); - return 1; + return; } /* --- linux/fs/ext2/super.c.~1~ Thu Sep 4 11:21:52 1980 +++ linux/fs/ext2/super.c Mon Nov 24 19:51:15 1997 @@ -270,12 +270,24 @@ static void ext2_setup_super (struct super_block * sb, struct ext2_super_block * es) { + int skip_tests = 0; + if (es->s_rev_level > EXT2_MAX_SUPP_REV) { printk ("EXT2-fs warning: revision level too high, " "forcing read/only mode\n"); sb->s_flags |= MS_RDONLY; } if (!(sb->s_flags & MS_RDONLY)) { + /* + * If we repeatedly remount the fs read-write then + * readonly, then only update the superblock mount + * fields the first time it is read-write. Bypass it + * later times. + */ + if ((sb->u.ext2_sb.s_mount_state & EXT2_CHECKED_FS) && + !(sb->u.ext2_sb.s_mount_state & EXT2_ERROR_FS)) + skip_tests = 1; + if (!(sb->u.ext2_sb.s_mount_state & EXT2_VALID_FS)) printk ("EXT2-fs warning: mounting unchecked fs, " "running e2fsck is recommended\n"); @@ -283,17 +295,20 @@ printk ("EXT2-fs warning: mounting fs with errors, " "running e2fsck is recommended\n"); else if (es->s_max_mnt_count >= 0 && - es->s_mnt_count >= (unsigned short) es->s_max_mnt_count) + es->s_mnt_count >= (unsigned short) es->s_max_mnt_count && + !skip_tests) printk ("EXT2-fs warning: maximal mount count reached, " "running e2fsck is recommended\n"); else if (es->s_checkinterval && - (es->s_lastcheck + es->s_checkinterval <= CURRENT_TIME)) + (es->s_lastcheck + es->s_checkinterval <= CURRENT_TIME) && + !skip_tests) printk ("EXT2-fs warning: checktime reached, " "running e2fsck is recommended\n"); es->s_state &= ~EXT2_VALID_FS; if (!es->s_max_mnt_count) es->s_max_mnt_count = EXT2_DFL_MAX_MNT_COUNT; - es->s_mnt_count++; + if (!skip_tests) + es->s_mnt_count++; es->s_mtime = CURRENT_TIME; mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); sb->s_dirt = 1; @@ -310,6 +325,12 @@ ext2_check_blocks_bitmap (sb); ext2_check_inodes_bitmap (sb); } + /* + * If all went OK, then skip the mount warnings and + * mount count update next time we get here. + */ + if (!(sb->u.ext2_sb.s_mount_state & EXT2_ERROR_FS)) + sb->u.ext2_sb.s_mount_state |= EXT2_CHECKED_FS; } } --- linux/fs/filesystems.c.~1~ Sun Oct 19 21:26:23 1997 +++ linux/fs/filesystems.c Mon Nov 24 19:51:15 1997 @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -40,6 +41,11 @@ callable = 0; device_setup(); + +#ifdef CONFIG_SUPERMOUNT + register_filesystem(&(struct file_system_type) + {supermount_read_super, "supermount", 0, NULL}); +#endif binfmt_setup(); --- linux/fs/inode.c.~1~ Mon Nov 24 19:50:45 1997 +++ linux/fs/inode.c Mon Nov 24 19:51:15 1997 @@ -243,6 +243,8 @@ if (inode == mount_root && inode->i_count == (inode->i_mount != inode ? 1 : 2)) continue; + if (IS_UNBOUND(inode)) + continue; return 0; } return 1; @@ -277,6 +279,9 @@ } inode->i_lock = 1; inode->i_sb->s_op->write_inode(inode); + if (inode->i_shadow && inode->i_shadow->i_shadow_op && + inode->i_shadow->i_shadow_op->write) + inode->i_shadow->i_shadow_op->write(inode->i_shadow); unlock_inode(inode); } @@ -375,6 +380,23 @@ attr->ia_mtime = attr->ia_ctime; } + /* + * For shadowed inodes, we need to keep the stats of the + * shadowing inode and the subinode in sync, so we always + * perform the change on the visible inode itself. + * + * We have to do this after updating attr and before checking + * EROFS, so that the shadowing filesystem can complete the + * attr update itself, possibly doing an automatic remounting + * of the filesystem to read/write. + */ + + if (inode->i_shadow) + return notify_change(inode->i_shadow, attr); + + if (IS_RDONLY(inode)) + return -EROFS; + if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->notify_change) return inode->i_sb->s_op->notify_change(inode, attr); @@ -438,6 +460,13 @@ } } +static inline void release_shadow(struct inode * inode) { + /* Shadow-release should be atomic. */ + struct inode * tmp; + tmp = inode->i_shadow; + inode->i_shadow = 0; +} + void iput(struct inode * inode) { if (!inode) @@ -446,7 +475,7 @@ if (!inode->i_count) { printk("VFS: iput: trying to free free inode\n"); printk("VFS: device %s, inode %lu, mode=0%07o\n", - kdevname(inode->i_rdev), inode->i_ino, inode->i_mode); + kdevname(inode->i_dev), inode->i_ino, inode->i_mode); return; } if (inode->i_pipe) @@ -465,6 +494,30 @@ free_page(page); } + if (inode->i_shadow) { + /* + * Inode is going away --- break the link to its shadow + * inode. It is an error for the shadow not to have a + * release function here: the shadow must either be able + * to break the link on demand, or it shouldn't be + * letting us iput the hidden inode in the first place. + */ + if (!inode->i_shadow->i_shadow_op || + !inode->i_shadow->i_shadow_op->release) + panic ("shadow inode %s:%lu has no release method", + kdevname(inode->i_dev), + inode->i_shadow->i_ino); + else + inode->i_shadow->i_shadow_op->release(inode->i_shadow); + /* + * If the release blocked, then somebody else may have + * now come in and started using the inode again. In + * fact, it might even be shadowed again by now. Best + * to start over. + */ + goto repeat; + } + if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->put_inode) { inode->i_sb->s_op->put_inode(inode); if (!inode->i_nlink) --- linux/fs/namei.c.~1~ Sun Oct 19 21:26:24 1997 +++ linux/fs/namei.c Mon Nov 24 19:51:15 1997 @@ -102,6 +102,13 @@ { int mode = inode->i_mode; + /* A shadowed inode may need to be enabled for write access */ + if (mask & S_IWOTH) { + int retval = make_shadow_writable(inode); + if (retval) + return retval; + } + if (inode->i_op && inode->i_op->permission) return inode->i_op->permission(inode, mask); else if ((mask & S_IWOTH) && IS_RDONLY(inode) && @@ -141,12 +148,24 @@ return -ETXTBSY; } } + if (inode->i_shadow && inode->i_shadow->i_shadow_op && + inode->i_shadow->i_shadow_op->get_write_access) { + int rc; + rc = inode->i_shadow->i_shadow_op->get_write_access + (inode->i_shadow); + if (rc) + return rc; + } inode->i_writecount++; return 0; } void put_write_access(struct inode * inode) { + if (inode->i_shadow && inode->i_shadow->i_shadow_op && + inode->i_shadow->i_shadow_op->put_write_access) + inode->i_shadow->i_shadow_op->put_write_access + (inode->i_shadow); inode->i_writecount--; } --- linux/fs/open.c.~1~ Thu Sep 4 11:23:12 1980 +++ linux/fs/open.c Mon Nov 24 19:51:15 1997 @@ -169,6 +169,8 @@ error = namei(filename,&inode); if (error) return error; + + make_shadow_writable(inode); if (IS_RDONLY(inode)) { iput(inode); return -EROFS; @@ -211,6 +213,8 @@ error = namei(filename,&inode); if (error) return error; + + make_shadow_writable(inode); if (IS_RDONLY(inode)) { iput(inode); return -EROFS; @@ -337,8 +341,6 @@ return -EBADF; if (!(inode = file->f_inode)) return -ENOENT; - if (IS_RDONLY(inode)) - return -EROFS; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) return -EPERM; if (mode == (mode_t) -1) @@ -358,10 +360,6 @@ error = namei(filename,&inode); if (error) return error; - if (IS_RDONLY(inode)) { - iput(inode); - return -EROFS; - } if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) { iput(inode); return -EPERM; @@ -387,6 +385,7 @@ return -EBADF; if (!(inode = file->f_inode)) return -ENOENT; + make_shadow_writable(inode); if (IS_RDONLY(inode)) return -EROFS; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) @@ -438,6 +437,7 @@ error = lnamei(filename,&inode); if (error) return error; + make_shadow_writable(inode); if (IS_RDONLY(inode)) { iput(inode); return -EROFS; @@ -610,6 +610,13 @@ filp->f_inode = NULL; if (filp->f_mode & FMODE_WRITE) put_write_access(inode); + if (inode->i_shadow && inode->i_shadow->i_shadow_op && + inode->i_shadow->i_shadow_op->release_file) + { + inode->i_shadow->i_shadow_op->release_file + (inode->i_shadow, filp); + } + iput(inode); } --- linux/fs/read_write.c.~1~ Thu Sep 4 11:23:12 1980 +++ linux/fs/read_write.c Mon Nov 24 19:51:15 1997 @@ -19,9 +19,17 @@ asmlinkage long sys_lseek(unsigned int fd, off_t offset, unsigned int origin) { struct file * file; + + if (fd >= NR_OPEN || !(file=current->files->fd[fd])) + return -EBADF; + return do_lseek(file, offset, origin); +} + +long do_lseek(struct file *file, off_t offset, unsigned int origin) +{ long tmp = -1; - if (fd >= NR_OPEN || !(file=current->files->fd[fd]) || !(file->f_inode)) + if (!file->f_inode) return -EBADF; if (origin > 2) return -EINVAL; --- linux/fs/super.c.~1~ Sun Oct 19 21:26:24 1997 +++ linux/fs/super.c Mon Nov 24 19:51:16 1997 @@ -50,8 +50,6 @@ extern int root_mountflags; -static int do_remount_sb(struct super_block *sb, int flags, char * data); - /* this is initialized in init/main.c */ kdev_t ROOT_DEV; @@ -633,21 +631,12 @@ * functions, they should be faked here. -- jrs */ -asmlinkage int sys_umount(char * name) +int do_umounti(struct inode * inode) { - struct inode * inode; kdev_t dev; int retval; struct inode dummy_inode; - if (!suser()) - return -EPERM; - retval = namei(name, &inode); - if (retval) { - retval = lnamei(name, &inode); - if (retval) - return retval; - } if (S_ISBLK(inode->i_mode)) { dev = inode->i_rdev; if (IS_NODEV(inode)) { @@ -686,50 +675,55 @@ return 0; } -/* - * do_mount() does the actual mounting after sys_mount has done the ugly - * parameter parsing. When enough time has gone by, and everything uses the - * new mount() parameters, sys_mount() can then be cleaned up. +asmlinkage int sys_umount(char * name) +{ + struct inode * inode; + int retval; + + if (!suser()) + return -EPERM; + retval = namei(name,&inode); + if (retval) { + retval = lnamei(name,&inode); + if (retval) + return retval; + } + return do_umounti(inode); +} + +/* + * do_mountdev() does the actual mounting after sys_mount has done the + * ugly parameter parsing. When enough time has gone by, and + * everything uses the new mount() parameters, sys_mount() can then be + * cleaned up. * * We cannot mount a filesystem if it has active, used, or dirty inodes. * We also have to flush all inode-data for this device, as the new mount - * might need new info. + * might need new info. */ -int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const char * type, int flags, void * data) +int do_mountdev(kdev_t dev, + struct inode *dir_i, + const char * dev_name, + const char * dir_name, + const char * type, + int flags, + void * data) { - struct inode * dir_i; struct super_block * sb; struct vfsmount *vfsmnt; - int error; - if (!(flags & MS_RDONLY) && dev && is_read_only(dev)) - return -EACCES; - /*flags |= MS_RDONLY;*/ - error = namei(dir_name, &dir_i); - if (error) - return error; - if (dir_i->i_count != 1 || dir_i->i_mount) { - iput(dir_i); + if (dir_i->i_count != 1 || dir_i->i_mount) return -EBUSY; - } - if (!S_ISDIR(dir_i->i_mode)) { - iput(dir_i); + if (!S_ISDIR(dir_i->i_mode)) return -ENOTDIR; - } - if (!fs_may_mount(dev)) { - iput(dir_i); + if (!fs_may_mount(dev)) return -EBUSY; - } sb = read_super(dev,type,flags,data,0); - if (!sb) { - iput(dir_i); + if (!sb) return -EINVAL; - } - if (sb->s_covered) { - iput(dir_i); + if (sb->s_covered) return -EBUSY; - } vfsmnt = add_vfsmnt(dev, dev_name, dir_name); if (vfsmnt) { vfsmnt->mnt_sb = sb; @@ -741,13 +735,33 @@ } +int do_mount(kdev_t dev, + const char * dev_name, + const char * dir_name, + const char * type, + int flags, + void * data) +{ + int error; + struct inode * dir_i; + + error = namei(dir_name, &dir_i); + if (error) + return error; + error = do_mountdev(dev, dir_i, dev_name, dir_name, type, flags, data); + if (error) + iput(dir_i); + return error; +} + + /* * Alters the mount flags of a mounted file system. Only the mount point * is used as a reference - file system type and the device are ignored. * FS-specific mount options can't be altered by remounting. */ -static int do_remount_sb(struct super_block *sb, int flags, char *data) +int do_remount_sb(struct super_block *sb, int flags, char *data) { int retval; struct vfsmount *vfsmnt; @@ -798,14 +812,23 @@ if (!data) return 0; - vma = find_vma(current->mm, (unsigned long) data); - if (!vma || (unsigned long) data < vma->vm_start) - return -EFAULT; - if (!(vma->vm_flags & VM_READ)) - return -EFAULT; - i = vma->vm_end - (unsigned long) data; - if (PAGE_SIZE <= (unsigned long) i) + /* + * Supermount calls the mount code from kernel space, so don't + * validate the mount data if it is already in kernel address + * space. + */ + if (get_fs() != get_ds()) { + vma = find_vma(current->mm, (unsigned long) data); + if (!vma || (unsigned long) data < vma->vm_start) + return -EFAULT; + if (!(vma->vm_flags & VM_READ)) + return -EFAULT; + i = vma->vm_end - (unsigned long) data; + if (PAGE_SIZE <= (unsigned long) i) + i = PAGE_SIZE-1; + } else { i = PAGE_SIZE-1; + } if (!(page = __get_free_page(GFP_KERNEL))) { return -ENOMEM; } @@ -829,15 +852,10 @@ * version that didn't understand them. */ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type, - unsigned long new_flags, void * data) + unsigned long new_flags, void * data) { - struct file_system_type * fstype; - struct inode * inode; - struct file_operations * fops; - kdev_t dev; + struct inode * dir_inode; int retval; - const char * t; - unsigned long flags = 0; unsigned long page = 0; if (!suser()) @@ -853,6 +871,29 @@ free_page(page); return retval; } + retval = namei(dir_name, &dir_inode); + if (retval) + return retval; + retval = do_mounti(dir_inode,dir_name,dev_name,type,new_flags,data); + if (retval) + iput(dir_inode); + return retval; +} + +/* Mount on a given inode. Don't iput() the mount point! */ +int do_mounti(struct inode * dir_inode, const char * dir_name, + const char * dev_name, const char * type, + unsigned long new_flags, const void * data) +{ + struct file_system_type * fstype; + struct inode * inode; + struct file_operations * fops; + dev_t dev; + int retval; + const char * t; + unsigned long flags = 0; + unsigned long page = 0; + retval = copy_mount_options (type, &page); if (retval < 0) return retval; @@ -910,7 +951,8 @@ return retval; } } - retval = do_mount(dev,dev_name,dir_name,t,flags,(void *) page); + retval = do_mountdev(dev,dir_inode,dev_name,dir_name, + t,flags,(void *) page); free_page(page); if (retval && !fstype->requires_dev) put_unnamed_dev(dev); --- linux/fs/supermount/Makefile.~1~ Mon Nov 24 19:51:16 1997 +++ linux/fs/supermount/Makefile Mon Nov 24 19:51:16 1997 @@ -0,0 +1,14 @@ +# +# Makefile for the linux supermounting routines. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +O_TARGET := supermount.o +O_OBJS := file.o inode.o namei.o super.o shadow.o +M_OBJS := $(O_TARGET) + +include $(TOPDIR)/Rules.make --- linux/fs/supermount/TODO.~1~ Mon Nov 24 19:51:16 1997 +++ linux/fs/supermount/TODO Mon Nov 24 19:51:16 1997 @@ -0,0 +1,40 @@ +Notes: + +TODO: + + Supermount directory inodes do auto-remount as root of the subfs. Make + a mount option to restrict this behaviour to the root? + + Make supermount_attach a special case of read_hidden_inode, not the other + way around. + + Replace shadow inodes with shadow superblock? Do release that way? + + Unmount on suspend? OK iff inode numbers on open directories will + be the same next time around! (Normally true.) Bad for performance, but + maybe necessary for ext2fs, for example. + +Done::: + + Sigh. We can't rely on the one-to-one mapping of superinode to + subinode i_ino numbers any more. Why not? Well, what happens if we + remove and remount a medium? We can end up with one process holding + a handle to an old, obsolete superinode, and a new process opening a + subinode with the same i_ino. Well, we can do a quadratic hash on + the superinode numbers to avoid collisions. Think about this! + + * What happens when we do get an inode collision? We rehash to make + another superinode the current one; the old inode remains as a + placeholder. But then, what if the old inode becomes released? A + subsequent attach may place the subinode under that old inode + instead of the new superinode. + + OK: There is only ever one hidden/shadow connection between super + and sub inodes. If that is broken, we can reestablish it from any + superinode we want to. Previously valid superinodes will have to + rely on i_subino to get to their subinode (the subinode i_ino will + NOT change behind our backs, hopefully). Such mappings should + only ever be one way, from superinode to subinode, done by + subiget(). + + Special handling of root directory to do auto remount. --- linux/fs/supermount/dir.c.~1~ Mon Nov 24 19:51:16 1997 +++ linux/fs/supermount/dir.c Mon Nov 24 19:51:16 1997 @@ -0,0 +1,235 @@ +/* + * linux/fs/supermount/dir.c + * + * 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 + */ + +#include + +#include +#include +#include +#include +#include + +static int supermount_dir_open (struct inode *, struct file *); +static int supermount_dir_lseek (struct inode *, struct file *, off_t, int); +static int supermount_dir_read (struct inode *, struct file *, char *, int); +static int supermount_readdir (struct inode *, struct file *, void *, + filldir_t); +static int supermount_dir_ioctl (struct inode *, struct file *, unsigned int, + unsigned long); +static int supermount_dir_fsync (struct inode *, struct file *); +static void supermount_dir_release (struct inode *, struct file *); + + + +static struct file_operations supermount_dir_operations = { + supermount_dir_lseek, /* lseek */ + supermount_dir_read, /* read */ + NULL, /* write - bad */ + supermount_readdir, /* readdir */ + NULL, /* select - default */ + supermount_dir_ioctl, /* ioctl */ + NULL, /* mmap */ + supermount_dir_open, /* open */ + supermount_dir_release, /* release */ + supermount_dir_fsync, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL /* revalidate */ +}; + +/* + * directories can handle most operations... supermount/namei.c just + * passes them through to the underlying subfs, except for lookup(). + */ +struct inode_operations supermount_dir_iops = { + &supermount_dir_operations, /* default directory file-ops */ + supermount_create, /* create */ + supermount_lookup, /* lookup */ + supermount_link, /* link */ + supermount_unlink, /* unlink */ + supermount_symlink, /* symlink */ + supermount_mkdir, /* mkdir */ + supermount_rmdir, /* rmdir */ + supermount_mknod, /* mknod */ + supermount_rename, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + NULL, /* truncate */ + supermount_permission, /* permission */ + NULL /* smap */ +}; + + +/* + * When we open() a directory (for readdir), just shadow the file with + * a file open to the subinode, and let that handle all the work. + */ + +static int supermount_dir_open (struct inode * inode, struct file * file) +{ + struct inode * subi; + struct file * subfile; + + int rc; + + if (file->f_mode & 2) + return -EPERM; + + supermount_debug ("dir_open inode %ld\n", inode->i_ino); + + subfile = get_empty_filp(); + if (!subfile) + return -ENFILE; + file->private_data = (void *) subfile; + + subi = subiget(inode); + if (!subi) { + subfile->f_count--; + return -ENOENT; + } + + subfile->f_inode = subi; + subfile->f_op = subi->i_op ? subi->i_op->default_file_ops : NULL; + subfile->f_pos = 0; + subfile->f_reada = 0; + subfile->f_flags = file->f_flags; + subfile->f_mode = file->f_mode; + + if (subfile->f_op && subfile->f_op->open) { + rc = file->f_op->open(subi, file); + if (rc) { + iput(subi); + subfile->f_count--; + return rc; + } + } + return 0; +} + + + +/* + * Pass on an lseek() to the underlying subfs + */ + +static int supermount_dir_lseek (struct inode *inode, struct file *file, + off_t offset, int origin) +{ + struct file *subfile = (struct file *) file->private_data; + struct inode *subinode = inode->u.supermount_i.i_hidden; + + if (!subinode) { + supermount_panic (inode->i_sb, "supermount_dir_lseek", + "No hidden inode on open directory"); + } + + return do_lseek(subfile, offset, origin); +} + +static int supermount_dir_read (struct inode *inode, struct file *file, + char *buf, int size) +{ + struct file *subfile = (struct file *) file->private_data; + struct inode *subinode = inode->u.supermount_i.i_hidden; + + if (!subinode) { + supermount_panic (inode->i_sb, "supermount_dir_read", + "No hidden inode on open directory"); + } + + if (subfile->f_op && subfile->f_op->read) + return subfile->f_op->read(subinode, subfile, buf, size); + return -EINVAL; +} + + +static int supermount_readdir (struct inode *inode, struct file *file, + void * buf, filldir_t fill_fn) +{ + struct file *subfile = (struct file *) file->private_data; + struct inode *subinode = inode->u.supermount_i.i_hidden; + + if (!subinode) { + supermount_panic (inode->i_sb, "supermount_readdir", + "No hidden inode on open directory"); + } + + if (subfile->f_op && subfile->f_op->readdir) + return subfile->f_op->readdir(subinode, subfile, + buf, fill_fn); + return -EINVAL; +} + + +static int supermount_dir_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct file *subfile = (struct file *) file->private_data; + struct inode *subinode = inode->u.supermount_i.i_hidden; + + if (!subinode) { + supermount_panic (inode->i_sb, "supermount_ioctl", + "No hidden inode on open directory"); + } + + if (subfile->f_op && subfile->f_op->ioctl) + return subfile->f_op->ioctl(subinode, subfile, cmd, arg); + return -EINVAL; +} + + +static int supermount_dir_fsync (struct inode *inode, struct file *file) +{ + struct file *subfile = (struct file *) file->private_data; + struct inode *subinode = inode->u.supermount_i.i_hidden; + + if (!subinode) { + supermount_panic (inode->i_sb, "supermount_ioctl", + "No hidden inode on open directory"); + } + + if (subfile->f_op && subfile->f_op->fsync) + return subfile->f_op->fsync(subinode, subfile); + return -EINVAL; +} + + +static void supermount_dir_release (struct inode *inode, struct file *file) +{ + struct file *subfile = (struct file *) file->private_data; + struct inode *subinode = inode->u.supermount_i.i_hidden; + + if (!subinode) { + supermount_panic (inode->i_sb, "supermount_ioctl", + "No hidden inode on open directory"); + } + + close_fp (subfile); + + /* + * There is one very special case we need to deal with here. + * If our root directory has just been closed (eg. after an + * ls), then we now need to check whether it was the last open + * reference to the directory, and if so, quiesce the + * subfilesystem. + */ + + if (!subfs_is_active(inode->i_sb)) { + supermount_go_inactive (inode->i_sb); + } +} --- linux/fs/supermount/file.c.~1~ Mon Nov 24 19:51:16 1997 +++ linux/fs/supermount/file.c Mon Nov 24 19:51:16 1997 @@ -0,0 +1,331 @@ +/* + * linux/fs/supermount/file.c + * + * 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 + */ + +#include + +#include +#include +#include +#include +#include + +static int supermount_dir_open (struct inode *, struct file *); +static int supermount_file_open (struct inode *, struct file *); +static int supermount_file_lseek (struct inode *, struct file *, off_t, int); +static int supermount_dir_read (struct inode *, struct file *, char *, int); +static int supermount_readdir (struct inode *, struct file *, void *, + filldir_t); +static int supermount_dir_ioctl (struct inode *, struct file *, unsigned int, + unsigned long); +static int supermount_dir_fsync (struct inode *, struct file *); +static void supermount_dir_release (struct inode *, struct file *); + + + +static struct file_operations supermount_dir_operations = { + supermount_file_lseek, /* lseek */ + supermount_dir_read, /* read */ + NULL, /* write - bad */ + supermount_readdir, /* readdir */ + NULL, /* select - default */ + supermount_dir_ioctl, /* ioctl */ + NULL, /* mmap */ + supermount_dir_open, /* open */ + supermount_dir_release, /* release */ + supermount_dir_fsync, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL /* mediactl */ +}; + +static struct file_operations supermount_file_operations = { + NULL, /* lseek */ + NULL, /* read */ + NULL, /* write - bad */ + NULL, /* readdir */ + NULL, /* select - default */ + NULL, /* ioctl */ + NULL, /* mmap */ + supermount_file_open, /* open */ + NULL, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL /* mediactl */ +}; + +/* + * directories can handle most operations... supermount/namei.c just + * passes them through to the underlying subfs, except for lookup(). + */ +struct inode_operations supermount_file_iops = { + &supermount_file_operations, /* default directory file-ops */ + supermount_create, /* create */ + supermount_lookup, /* lookup */ + supermount_link, /* link */ + supermount_unlink, /* unlink */ + supermount_symlink, /* symlink */ + supermount_mkdir, /* mkdir */ + supermount_rmdir, /* rmdir */ + supermount_mknod, /* mknod */ + supermount_rename, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + NULL, /* truncate */ + supermount_permission, /* permission */ + NULL /* smap */ +}; + + +/* + * supermount regular files can do almost nothing. The only reason they + * exist is to assign a default file ops to trap file opens. + */ +struct inode_operations supermount_dir_iops = { + &supermount_dir_operations, /* default directory file-ops */ + supermount_create, /* create */ + supermount_lookup, /* lookup */ + supermount_link, /* link */ + supermount_unlink, /* unlink */ + supermount_symlink, /* symlink */ + supermount_mkdir, /* mkdir */ + supermount_rmdir, /* rmdir */ + supermount_mknod, /* mknod */ + supermount_rename, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + NULL, /* truncate */ + supermount_permission, /* permission */ + NULL /* smap */ +}; + + +/* + * When we open() a directory (for readdir), create a separate subfile + * linked to the existing file, so that we can pass all directory + * operations down to the subfilesystem. + */ + +static int supermount_dir_open (struct inode * inode, struct file * file) +{ + struct inode * subi; + struct file * subfile; + + int rc; + + if (file->f_mode & FMODE_WRITE) + return -EPERM; + + supermount_debug ("dir_open inode %ld\n", inode->i_ino); + + subfile = get_empty_filp(); + if (!subfile) + return -ENFILE; + file->private_data = (void *) subfile; + + subi = subiget(inode); + if (!subi) { + subfile->f_count--; + return -ENOENT; + } + + subfile->f_inode = subi; + subfile->f_op = subi->i_op ? subi->i_op->default_file_ops : NULL; + subfile->f_pos = 0; + subfile->f_reada = 0; + subfile->f_flags = file->f_flags; + subfile->f_mode = file->f_mode; + + if (subfile->f_op && subfile->f_op->open) { + rc = file->f_op->open(subi, file); + if (rc) { + iput(subi); + subfile->f_count--; + return rc; + } + } + return 0; +} + + +/* + * When we open() a regular file, just shadow the file with a file + * open to the subinode, and let that handle all the work. + */ + +static int supermount_file_open (struct inode * inode, struct file * file) +{ + struct inode * subi; + + int rc; + + supermount_dump_inode (inode); + + /* + * The subinode should be attached: lookup() returns an attached + * pair of inodes, and no deattachment should occur while there + * is still a reference against the parent. + */ + subi = inode->u.supermount_i.i_hidden; + if (!subi) + return -ENOENT; + + /* + * Try to get write access to the subfs if appropriate + */ + + if (file->f_mode & FMODE_WRITE) { + rc = supermount_get_write_access (inode->i_sb); + if (rc) + return rc; + } + + /* + * Now rewrite the existing file object to point to the + * subfilesystem's inode instead, and send the open call + * through to the subfs + */ + file->f_inode = subi; + file->f_op = subi->i_op ? subi->i_op->default_file_ops : NULL; + if (file->f_op && file->f_op->open) { + rc = file->f_op->open(subi, file); + if (rc) { + if (file->f_mode & FMODE_WRITE) + supermount_put_write_access (inode->i_sb); + iput(subi); + return rc; + } + } + iput(inode); + return 0; +} + + +/* + * Pass on an lseek() to the underlying subfs + */ + +static int supermount_file_lseek (struct inode *inode, struct file *file, + off_t offset, int origin) +{ + struct file *subfile = (struct file *) file->private_data; + struct inode *subinode = inode->u.supermount_i.i_hidden; + + if (!subinode) { + supermount_panic (inode->i_sb, "supermount_dir_lseek", + "No hidden inode on open directory"); + } + + return do_lseek(subfile, offset, origin); +} + +static int supermount_dir_read (struct inode *inode, struct file *file, + char *buf, int size) +{ + struct file *subfile = (struct file *) file->private_data; + struct inode *subinode = inode->u.supermount_i.i_hidden; + + if (!subinode) { + supermount_panic (inode->i_sb, "supermount_dir_read", + "No hidden inode on open directory"); + } + + if (subfile->f_op && subfile->f_op->read) + return subfile->f_op->read(subinode, subfile, buf, size); + return -EINVAL; +} + + +static int supermount_readdir (struct inode *inode, struct file *file, + void * buf, filldir_t fill_fn) +{ + struct file *subfile = (struct file *) file->private_data; + struct inode *subinode = inode->u.supermount_i.i_hidden; + + if (!subinode) { + supermount_panic (inode->i_sb, "supermount_readdir", + "No hidden inode on open directory"); + } + + if (subfile->f_op && subfile->f_op->readdir) + return subfile->f_op->readdir(subinode, subfile, + buf, fill_fn); + return -EINVAL; +} + + +static int supermount_dir_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct file *subfile = (struct file *) file->private_data; + struct inode *subinode = inode->u.supermount_i.i_hidden; + + if (!subinode) { + supermount_panic (inode->i_sb, "supermount_ioctl", + "No hidden inode on open directory"); + } + + if (subfile->f_op && subfile->f_op->ioctl) + return subfile->f_op->ioctl(subinode, subfile, cmd, arg); + return -EINVAL; +} + + +static int supermount_dir_fsync (struct inode *inode, struct file *file) +{ + struct file *subfile = (struct file *) file->private_data; + struct inode *subinode = inode->u.supermount_i.i_hidden; + + if (!subinode) { + supermount_panic (inode->i_sb, "supermount_ioctl", + "No hidden inode on open directory"); + } + + if (subfile->f_op && subfile->f_op->fsync) + return subfile->f_op->fsync(subinode, subfile); + return -EINVAL; +} + + +static void supermount_dir_release (struct inode *inode, struct file *file) +{ + struct file *subfile = (struct file *) file->private_data; + struct inode *subinode = inode->u.supermount_i.i_hidden; + + if (!subinode) { + supermount_panic (inode->i_sb, "supermount_ioctl", + "No hidden inode on open directory"); + } + + close_fp (subfile); + + /* + * There is one very special case we need to deal with here. + * If our root directory has just been closed (eg. after an + * ls), then we now need to check whether it was the last open + * reference to the directory, and if so, quiesce the + * subfilesystem. + */ + + if (!subfs_is_active(inode->i_sb)) { + supermount_go_inactive (inode->i_sb); + } +} --- linux/fs/supermount/inode.c.~1~ Mon Nov 24 19:51:16 1997 +++ linux/fs/supermount/inode.c Mon Nov 24 19:51:16 1997 @@ -0,0 +1,252 @@ +/* + * linux/fs/supermount/inode.c + * + * 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/inode.c + * Copyright (C) 1992, 1993, 1994, 1995 Remy Card + * and 1993 Stephen Tweedie + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * We've got a superinode here: do an iget on the appropriate subfs inode + * + * Revalidate the media first if necessary, and return NULL if we can't + * get a subinode (media may no longer be loaded). + */ + +struct inode * subiget(struct inode * inode) +{ + struct inode * tmp; + supermount_debug("subiget inode %ld\n", inode->i_ino); + + if (!S_ISDIR(inode->i_mode)) + return 0; + if (supermount_media_check(inode->i_sb)) { + supermount_debug("media is invalid\n"); + return 0; + } + + if (!subfs_is_active(inode->i_sb)) { + supermount_debug ("going online.\n"); + inode->i_sb->u.supermount_sb.s_state = + SUPERMOUNT_ONLINE; + } + + if (inode_is_obsolete(inode)) { + /* For obsolete directory with new mounted media --- + return the new root subinode. */ + if (inode->u.supermount_i.i_hidden) + supermount_panic (inode->i_sb, "subiget", + "Hidden inode on obsolete inode %ld", + inode->i_ino); + tmp = inode->i_sb->u.supermount_sb.s_subfs->s_mounted; + inode->u.supermount_i.i_subino = tmp->i_ino; + tmp->i_count++; + supermount_debug("mapping obsolete inode %ld to " + "subroot inode %ld\n", + inode->i_ino, tmp->i_ino); + return tmp; + } + + if (inode->u.supermount_i.i_hidden) { + inode->u.supermount_i.i_hidden->i_count++; + supermount_debug("inode %ld/%ld, found hidden, " + "count now %d/%d\n", + inode->i_ino, + inode->u.supermount_i.i_hidden->i_ino, + inode->i_count, + inode->u.supermount_i.i_hidden->i_count); + return inode->u.supermount_i.i_hidden; + } + supermount_read_hidden_inode(inode); + if (!inode->u.supermount_i.i_hidden) { + supermount_debug("inode %ld, no subinode, count=%d\n", + inode->i_ino, inode->i_count); + return 0; + } + + supermount_debug("inode %ld/%ld, found new, " + "count now %d/%d\n", + inode->i_ino, + inode->u.supermount_i.i_hidden->i_ino, + inode->i_count, + inode->u.supermount_i.i_hidden->i_count); + return inode->u.supermount_i.i_hidden; +} + +/* Supermount open/close inode. These functions maintain the + superblock reference counts of active inodes; the subfs is + suspended when that count reaches zero. + + Files are opened on any significant reference, and are not closed + until they become fully dereferenced (during the last iput). + Directories, on the other hand, are always closed unless they are + active and opened; a directory referenced as CWD is not open. + + The difference is in the way the application gets given inodes. + For dirs and symlinks, it will be given the superinode to deal + with; for other file types, we return the subinode. */ + + +void supermount_iopen(struct inode * inode) +{ + supermount_dump_inode(inode); + switch (inode->i_sb->u.supermount_sb.s_state) { + case SUPERMOUNT_UNMOUNTED: + supermount_panic (inode->i_sb, "supermount_iopen", + "opening inode on unmounted subfs"); + case SUPERMOUNT_SUSPENDED: + supermount_debug("locking door.\n"); + supermount_lock_door(inode->i_sb, 1); + break; + default: + } + + if (!inode->u.supermount_i.i_counted) { + supermount_debug("Opened inode %ld\n", inode->i_ino); + inode->u.supermount_i.i_counted = 1; + if (!subfs_is_active(inode->i_sb)) { + supermount_debug ("going online.\n"); + inode->i_sb->u.supermount_sb.s_state = + SUPERMOUNT_ONLINE; + } + inode->i_sb->u.supermount_sb.s_opencount++; + } +} + +void supermount_iclose(struct inode * inode) +{ + supermount_dump_inode(inode); + if (inode->u.supermount_i.i_counted) { + supermount_debug("Closed inode %ld\n", inode->i_ino); + inode->u.supermount_i.i_counted = 0; + inode->i_sb->u.supermount_sb.s_opencount--; + if (!subfs_is_active(inode->i_sb)) + supermount_go_inactive(inode->i_sb); + } +} + +void supermount_read_inode (struct inode * inode) +{ + supermount_dump_inode(inode); + + inode->i_shadow_op = &supermount_shadow_iops; + inode->u.supermount_i.i_sb_version = + inode->i_sb->u.supermount_sb.s_version; + + switch (inode->i_ino) { + case SUPERMOUNT_ROOT_INO: + if (inode->i_sb->u.supermount_sb.s_state != + SUPERMOUNT_UNMOUNTED) { + supermount_panic (inode->i_sb, "supermount_read_inode", + "Help - trying to read root while " + "already mounted!"); + } + /* Fall through */ + case SUPERMOUNT_HIDDEN_INO: + inode->i_mode = inode->i_sb->u.supermount_sb.s_default_mode; + inode->i_uid = 0; + inode->i_gid = 0; + inode->i_nlink = 1; + inode->i_size = 0; + inode->i_atime = CURRENT_TIME; + inode->i_ctime = CURRENT_TIME; + inode->i_mtime = CURRENT_TIME; + inode->i_blksize = inode->i_sb->s_blocksize; + inode->i_blocks = 0; + inode->i_version = ++event; + inode->i_op = (inode->i_ino == SUPERMOUNT_ROOT_INO) + ? &supermount_dir_iops : NULL; + return; + default: + ; + } +} + +void supermount_write_inode (struct inode * inode) +{ + struct super_block *tmp; + tmp = inode->i_sb->u.supermount_sb.s_subfs; + + supermount_dump_inode(inode); + + if (!inode->i_dirt) + return; + + inode->i_dirt = 0; + if (inode->u.supermount_i.i_hidden && + tmp && tmp->s_op && tmp->s_op->write_inode) + tmp->s_op->write_inode(inode->u.supermount_i.i_hidden); + + mark_subfs_dirty(inode->i_sb); +} + +/* + * Release a supermount inode on its last deref (i_count == 1). + */ + +void supermount_put_inode (struct inode * inode) +{ + supermount_dump_inode(inode); + + if (inode->u.supermount_i.i_hidden) { + supermount_panic (inode->i_sb, "supermount_put_inode", + "Hidden inode still present on " + "released inode"); + } +} + +int supermount_permission (struct inode * inode, int mask) +{ + unsigned short mode; + struct inode *subi; + + supermount_debug("inode %ld, mask 0%o\n", inode->i_ino, mask); + + subi = subiget(inode); + if (!subi) + return -EIO; + if (subi->i_op && subi->i_op->permission) { + int rc = subi->i_op->permission(subi, mask); + iput(subi); + return rc; + } + + mode = inode->i_mode; + /* + * Special case, access is always granted for root + */ + if (fsuser()) { + iput(subi); + return 0; + } else if (current->fsuid == inode->i_uid) + mode >>= 6; + else if (in_group_p (inode->i_gid)) + mode >>= 3; + iput(subi); + if (((mode & mask & S_IRWXO) == mask)) + return 0; + else + return -EACCES; +} --- linux/fs/supermount/namei.c.~1~ Mon Nov 24 19:51:16 1997 +++ linux/fs/supermount/namei.c Mon Nov 24 19:51:16 1997 @@ -0,0 +1,447 @@ +/* + * linux/fs/supermount/namei.c + * + * Copyright (C) 1995 + * Stephen Tweedie (sct@dcs.ed.ac.uk) + * + * from + * + * linux/fs/minix/namei.c + * Copyright (C) 1991, 1992 Linus Torvalds + * + * and + * + * linux/fs/ext2/namei.c + * Copyright (C) 1992, 1993, 1994, 1995 Remy Card + */ + +/* This file handles almost all of the normal filesystem running of + supermount. We don't have to deal to much with the subfs + interface, since we just pass subinodes out to the application on + demand. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Attach a superinode to a subinode, and prepare a result for the + * upper VFS layers. We return the superinode for directories, and + * the subinode for other file types. + */ + +static void attach_and_prepare(struct super_block *sb, + struct inode *subi, struct inode **result) +{ + struct inode *superi; + superi = supermount_attach(sb, subi); + /* We have now done iget() on superi and subi. */ + + /* + * If the subfs inode is a directory then we need to deal with + * it ourselves, and only give back the superinode to the + * application. Otherwise, we can just return the subfs inode. + */ + + if (S_ISDIR(subi->i_mode)) { + superi->i_op = &supermount_dir_iops; + iput(subi); /* Don't want to keep this around any more */ + *result = superi; + return; + } else { + /* + * Not a directory-related inode, so we return the subfs + * inode to the upper layers. We still need the + * supermount inode as an attachment to the subinode in + * that case, though, to maintain reference counts in + * the supermount superblock. The backlink from the + * subinode to the superinode does this for us, so we + * can safely iput the superinode right now. + */ + + iput(superi); + *result = subi; + return; + } +} + +int supermount_lookup (struct inode * dir, const char * name, int len, + struct inode ** result) +{ + struct inode *subi, *subdir; + int rc; + + supermount_debug ("inode = %ld, name = %*s\n", + dir->i_ino, len, name); + + *result = NULL; + if (!dir) + return -ENOENT; + if (!S_ISDIR(dir->i_mode)) { + iput(dir); + return -ENOENT; + } + if (!(subdir = subiget(dir))) { + /* This now becomes the *easy* case. :-) */ + iput (dir); + return -EIO; + } + + rc = subdir->i_op->lookup(subdir, name, len, &subi); + supermount_go_inactive(dir->i_sb); + iput (dir); + if (rc) + return rc; + /* It worked, so now create a shadow supermount inode for the + result... */ + attach_and_prepare(dir->i_sb, subi, result); + return 0; +} + +int supermount_create (struct inode * dir,const char * name, int len, int mode, + struct inode ** result) +{ + struct inode * inode, * subi; + int rc; + + supermount_debug ("inode = %ld, name = %*s, mode = %o\n", + dir->i_ino, len, name, mode); + + *result = NULL; + if (!dir) + return -ENOENT; + inode = subiget(dir); + if (!inode) { + iput(dir); + return -ENOENT; + } + + if (!inode->i_op || !inode->i_op->create) { + iput(dir); + iput(inode); + return -EPERM; + } + + rc = supermount_get_write_access(dir->i_sb); + if (rc) { + iput(dir); + iput(inode); + return rc; + } + + rc = inode->i_op->create(inode, name, len, mode, &subi); + mark_subfs_dirty(dir->i_sb); + supermount_put_write_access(dir->i_sb); + supermount_go_inactive(dir->i_sb); + iput(dir); + if (rc) + return rc; + attach_and_prepare(dir->i_sb, subi, result); + return 0; +} + +int supermount_mknod (struct inode * dir, const char * name, int len, int mode, + int rdev) +{ + struct inode * inode; + int rc; + + supermount_debug ("inode = %ld, name = %*s, mode = %o\n", + dir->i_ino, len, name, mode); + + if (!dir) + return -ENOENT; + inode = subiget(dir); + if (!inode) { + iput(dir); + return -ENOENT; + } + if (!inode->i_op || !inode->i_op->mknod) { + iput(dir); + iput(inode); + return -EPERM; + } + + rc = supermount_get_write_access(inode->i_sb); + if (rc) { + iput(dir); + iput(inode); + return rc; + } + + inode->i_count++; /* See comment in supermount_rename() */ + rc = inode->i_op->mknod(inode, name, len, mode, rdev); + mark_subfs_dirty(dir->i_sb); + supermount_put_write_access(dir->i_sb); + iput(inode); + supermount_go_inactive(dir->i_sb); + iput(dir); + return rc; +} + +int supermount_mkdir (struct inode * dir, const char * name, int len, int mode) +{ + struct inode * inode; + int rc; + + supermount_debug ("inode = %ld, name = %*s, mode = %o\n", + dir->i_ino, len, name, mode); + + if (!dir) + return -ENOENT; + inode = subiget(dir); + if (!inode) { + iput(dir); + return -ENOENT; + } + if (!inode->i_op || !inode->i_op->mkdir) { + iput(dir); + iput(inode); + return -EPERM; + } + + rc = supermount_get_write_access(dir->i_sb); + if (rc) { + iput(dir); + iput(inode); + return rc; + } + + inode->i_count++; /* See comment in supermount_rename() */ + rc = inode->i_op->mkdir(inode, name, len, mode); + mark_subfs_dirty(dir->i_sb); + supermount_put_write_access(dir->i_sb); + iput(inode); + supermount_go_inactive(dir->i_sb); + iput(dir); + return rc; +} + +int supermount_rmdir (struct inode * dir, const char * name, int len) +{ + struct inode * inode; + int rc; + + supermount_debug ("inode = %ld, name = %*s\n", + dir->i_ino, len, name); + + if (!dir) + return -ENOENT; + inode = subiget(dir); + if (!inode) { + iput(dir); + return -ENOENT; + } + if (!inode->i_op || !inode->i_op->rmdir) { + iput(dir); + iput(inode); + return -EPERM; + } + + rc = supermount_get_write_access(dir->i_sb); + if (rc) { + iput(dir); + iput(inode); + return rc; + } + + inode->i_count++; /* See comment in supermount_rename() */ + rc = inode->i_op->rmdir(inode, name, len); + mark_subfs_dirty(dir->i_sb); + supermount_put_write_access(dir->i_sb); + iput(inode); + supermount_go_inactive(dir->i_sb); + iput(dir); + return rc; +} + +int supermount_unlink (struct inode * dir, const char * name, int len) +{ + struct inode * inode; + int rc; + + supermount_debug ("inode = %ld, name = %*s\n", + dir->i_ino, len, name); + + if (!dir) + return -ENOENT; + inode = subiget(dir); + if (!inode) { + iput(dir); + return -ENOENT; + } + if (!inode->i_op || !inode->i_op->unlink) { + iput(dir); + iput(inode); + return -EPERM; + } + + rc = supermount_get_write_access(dir->i_sb); + if (rc) { + iput(dir); + iput(inode); + return rc; + } + + inode->i_count++; /* See comment in supermount_rename() */ + rc = inode->i_op->unlink(inode, name, len); + mark_subfs_dirty(dir->i_sb); + supermount_put_write_access(dir->i_sb); + iput(inode); + supermount_go_inactive(dir->i_sb); + iput(dir); + return rc; +} + +int supermount_symlink (struct inode * dir, const char * name, int len, + const char * symname) +{ + struct inode * inode; + int rc; + + supermount_debug ("inode = %ld, name = %*s, link = %s\n", + dir->i_ino, len, name, symname); + + if (!dir) + return -ENOENT; + inode = subiget(dir); + if (!inode) { + iput(dir); + return -ENOENT; + } + if (!inode->i_op || !inode->i_op->symlink) { + iput(dir); + iput(inode); + return -EPERM; + } + + rc = supermount_get_write_access(dir->i_sb); + if (rc) { + iput(dir); + iput(inode); + return rc; + } + + inode->i_count++; /* See comment in supermount_rename() */ + rc = inode->i_op->symlink(inode, name, len, symname); + mark_subfs_dirty(dir->i_sb); + supermount_put_write_access(dir->i_sb); + iput(inode); + supermount_go_inactive(dir->i_sb); + iput(dir); + return rc; +} + +/* This probably won't work because higher levels will complain about + cross-device links. */ +int supermount_link (struct inode * oldinode, struct inode * dir, + const char * name, int len) +{ + struct inode * inode; + int rc; + + supermount_debug ("inode = %ld, name = %*s, link to inode %ld\n", + dir->i_ino, len, name, oldinode->i_ino); + + if (!dir) + return -ENOENT; + inode = subiget(dir); + if (!inode) { + iput(dir); + return -ENOENT; + } + if (!inode->i_op || !inode->i_op->link) { + iput(dir); + iput(inode); + return -EPERM; + } + + rc = supermount_get_write_access(dir->i_sb); + if (rc) { + iput(dir); + iput(inode); + return rc; + } + + oldinode->i_count++; /* See comment in supermount_rename() */ + rc = inode->i_op->link(oldinode, inode, name, len); + mark_subfs_dirty(dir->i_sb); + supermount_put_write_access(dir->i_sb); + iput(oldinode); + supermount_go_inactive(dir->i_sb); + iput(dir); + return rc; +} + +int supermount_rename (struct inode * old_dir, const char * old_name, + int old_len, + struct inode * new_dir, const char * new_name, + int new_len, int must_be_dir) +{ + struct inode * old_subi, * new_subi; + int rc; + + supermount_debug ("from inode %ld, %*s to inode %ld, %*s\n", + old_dir->i_ino, old_len, old_name, + new_dir->i_ino, new_len, new_name); + + if (!old_dir || !new_dir) + return -ENOENT; + old_subi = subiget(old_dir); + if (!old_subi) { + iput(old_dir); + iput(new_dir); + return -ENOENT; + } + new_subi = subiget(new_dir); + if (!new_subi) { + iput(old_dir); + iput(new_dir); + iput(old_subi); + return -ENOENT; + } + + if (!old_subi->i_op || !old_subi->i_op->rename) { + iput(old_dir); + iput(new_dir); + iput(old_subi); + iput(new_subi); + return -EPERM; + } + + rc = supermount_get_write_access(old_dir->i_sb); + if (rc) { + iput(old_dir); + iput(new_dir); + iput(old_subi); + iput(new_subi); + return rc; + } + + /* + * rename will do an iput() on both of the subinodes, leaving + * us with handles only on the superinodes. That is a problem, + * because if our only handles are superinode directories then + * we try to deactivate the subfs --- and we aren't allowed to + * do that while we have write access outstanding. So, keep + * an extra reference count on a subinode to defer going offline + * until we have given back the write access. + */ + old_subi->i_count++; + rc = old_subi->i_op->rename(old_subi, old_name, old_len, + new_subi, new_name, new_len, + must_be_dir); + mark_subfs_dirty(old_dir->i_sb); + supermount_put_write_access(old_dir->i_sb); + iput(old_subi); + supermount_go_inactive(old_dir->i_sb); + iput(old_dir); + iput(new_dir); + return rc; +} --- linux/fs/supermount/shadow.c.~1~ Mon Nov 24 19:51:16 1997 +++ linux/fs/supermount/shadow.c Mon Nov 24 19:51:16 1997 @@ -0,0 +1,318 @@ +/* + * linux/fs/supermount/shadow.c + * + * Copyright (C) 1997 + * Stephen Tweedie (sct@dcs.ed.ac.uk) + * + * Manage shadow inodes. + * + * Supermount maintains shadow inodes: supermount inodes which shadow + * inodes in the physically mounted subfilesystem. The attachment of + * subinodes to their shadow superinodes is managed in this file. We + * create the attachment on demand, and deattach the pair whenever the + * last reference to a subinode is released. + * + * The VFS also passes calls up to an inode's shadow when certain + * interesting events occur, such incrementing or decrementing the write + * access count on a subinode. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +static int supermount_shadow_open_file (struct inode *, struct file *); +static void supermount_shadow_write (struct inode *); +static void supermount_shadow_release_inode (struct inode *); +static void supermount_shadow_release_file (struct inode *, struct file *); +static int supermount_shadow_test_write_access (struct inode *); +static int supermount_shadow_get_write_access (struct inode *); +static void supermount_shadow_put_write_access (struct inode *); + + +/* + * Deal with the shadow interface. The shadow inode ops are called by + * the VFS whenever anything appropriately interesting happens to a + * shadowed inode. NOTE: The event has happened to the shadowed inode, + * but it is the shadowing inode which gets the shadow callback. + */ + +struct inode_shadow_operations supermount_shadow_iops = { + supermount_shadow_open_file, + supermount_shadow_write, + supermount_shadow_release_inode, + supermount_shadow_release_file, + supermount_shadow_test_write_access, + supermount_shadow_get_write_access, + supermount_shadow_put_write_access, +}; + + +/* + * First of all, deal with the attachment and deattachment of shadow inodes. + * + * supermount_attach() creates and attaches an appropriate superinode + * (shadowing inode) to a given subinode. + * + * The reference count on the superinode returned is incremented. If a + * new attachment occurs, the refcount is incremented again to reflect + * the new backlink from the subinode. + */ + +struct inode * supermount_attach(struct super_block *sb, struct inode *subi) +{ + struct inode *superi; + unsigned long new_ino, incr=1237; + + supermount_debug ("inode = %ld\n", subi->i_ino); + + if (subi->i_shadow) { + supermount_debug ("return existing shadow %ld, count %d/%d\n", + subi->i_shadow->i_ino, + subi->i_shadow->i_count, subi->i_count); + subi->i_shadow->i_count++; + return subi->i_shadow; + } + + /* + * The rules are a little different at the root. + * + * We never try to do attachment here: instead, just take for + * granted that we can get the superfilesystem's own root inode. + */ + if (subi == sb->u.supermount_sb.s_subfs->s_mounted) { + superi = sb->s_mounted; + supermount_debug ("return existing superfs root %ld, " + "count %d/%d\n", + superi->i_ino, + superi->i_count, + superi->i_count); + superi->i_count++; + return superi; + } + + /* + * Find a new superinode, avoiding collisions with old + * obsolete superinodes by a quadratic hash. + */ + new_ino = subi->i_ino; + for ( ; (superi = iget(sb, new_ino)) && superi->i_count > 1 && + inode_is_obsolete(superi); ) { + iput(superi); + new_ino += incr; + new_ino &= 0xffffff; + incr <<= 1; incr++; + } + + /* + * We now have a new superinode which has been refcounted by + * one. Next we have to attach the subinode to it. + */ + + superi->u.supermount_i.i_subino = subi->i_ino; + + /* + * If we found a previously obsolete inode which is now no + * longer used, we can safely remove the obsolesence flag. + */ + if (inode_is_obsolete(superi)) { + superi->u.supermount_i.i_sb_version = + superi->i_sb->u.supermount_sb.s_version; + } + + /* + * We have now done iget() on superi and subi. Doing the + * attachment may incur another iget() on the subinode. + */ + if (!superi->u.supermount_i.i_hidden) { + supermount_read_hidden_inode(superi); + if (!superi->u.supermount_i.i_hidden) { + supermount_panic (superi->i_sb, "supermount_attach", + "read_hidden_inode didn't " + "attach inode"); + } + /* + * Reading the hidden inode refcounts the subinode + * again. Dispose of that refcount now, since we don't + * need it --- we came into this routine already holding + * one. + */ + iput(subi); + } + return superi; +} + + +/* + * Take a given superinode and read an appropriate subinode for it. The + * i_ino number for the subinode is taken from the supermount i_subino + * field if that is non-zero, and defaults to the inode number of the + * superinode if it is zero. + * + * The new subinode is attached to the superinode, and the refcount of + * the superinode is incremented to reflect this. The subinode's + * refcount is also incremented: the function acts as an implicit iget() + * on the subinode. + */ + +void supermount_read_hidden_inode (struct inode * superi) +{ + struct super_block * sb; + struct inode * subi; + + supermount_dump_inode(superi); + supermount_media_check(superi->i_sb); + + if (superi->i_sb->u.supermount_sb.s_state == + SUPERMOUNT_UNMOUNTED) { + supermount_panic (superi->i_sb, "read_hidden_inode", + "Trying to read inode on unmounted media"); + } + sb=superi->i_sb->u.supermount_sb.s_subfs; + + /* + * If the superinode is obsolete, there's no point in trying to + * do any attachment. + */ + if (inode_is_obsolete(superi)) { + if (superi->u.supermount_i.i_subino) + supermount_panic (superi->i_sb, "read_hidden_inode", + "Obsolete inode is " + "already attached"); + return; + } + + /* + * The root inode is a bit special. For that one, the subinode + * is the root of the subfs, and we can't guarantee its inode + * number in advance. Otherwise, just lookup the inode number + * we're given to find the appropriate subinode. + */ + if (superi->i_ino == SUPERMOUNT_ROOT_INO) { + subi = sb->s_mounted; + subi->i_count++; + } else { + if (!superi->u.supermount_i.i_subino) + superi->u.supermount_i.i_subino = superi->i_ino; + subi = iget(sb, superi->u.supermount_i.i_subino); + if (!subi) + return; + } + + /* + * If the inode is already linked to its superinode, don't do + * any further processing. Everything is fine. + */ + if (subi->i_shadow) + return; + + subi->i_shadow = superi; + superi->u.supermount_i.i_hidden = subi; + superi->i_count++; + supermount_iopen(superi); + + 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 = ++event; + + if (S_ISDIR(superi->i_mode)) + superi->i_op = &supermount_dir_iops; + else + superi->i_op = NULL; +} + + + +/* +*************** +*/ + +int supermount_shadow_open_file (struct inode *inode, struct file *filp) +{ + int retval; + + supermount_dump_inode (inode); + + if (filp->f_mode & FMODE_WRITE) { + retval = supermount_get_write_access(inode->i_sb); + if (retval) + return retval; + mark_subfs_dirty(inode->i_sb); + } + return 0; +} + +void supermount_shadow_write (struct inode *inode) +{ + mark_subfs_dirty(inode->i_sb); +} + +void supermount_shadow_release_inode (struct inode *inode) +{ + supermount_dump_inode(inode); + if (inode->u.supermount_i.i_hidden) + supermount_debug("subinode = %ld\n", + inode->u.supermount_i.i_hidden->i_ino); + + inode->u.supermount_i.i_hidden->i_shadow = NULL; + inode->u.supermount_i.i_hidden = NULL; + supermount_iclose(inode); + iput(inode); +} + + +/* + * When we get a file release callback, check to see whether the file + * was opened for write. If it was, then we need to release a write + * access against the superinode. + * + * Note that there is not necessarily a subinode any longer. If we had + * a directory file open for readdir, then by this point the file will + * have been closed and the directory deattached from the subfs. + */ + +static void supermount_shadow_release_file (struct inode *inode, + struct file *filp) +{ + supermount_dump_inode(inode); +} + + +static int supermount_shadow_test_write_access (struct inode *inode) +{ + supermount_dump_inode(inode); + return supermount_test_write_access(inode->i_sb); +} + +static int supermount_shadow_get_write_access (struct inode *inode) +{ + supermount_dump_inode(inode); + return supermount_get_write_access(inode->i_sb); +} + +static void supermount_shadow_put_write_access (struct inode *inode) +{ + supermount_dump_inode(inode); + mark_subfs_dirty(inode->i_sb); + supermount_put_write_access(inode->i_sb); +} + + --- linux/fs/supermount/super.c.~1~ Mon Nov 24 19:51:16 1997 +++ linux/fs/supermount/super.c Mon Nov 24 19:51:16 1997 @@ -0,0 +1,714 @@ +/* + * linux/fs/supermount/super.c + * + * 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 + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* From fs/super.c */ +extern int do_umount(kdev_t); + +/* From fs/devices.c */ +extern struct file_operations * get_blkfops(unsigned int major); + +#ifdef SUPERMOUNT_DEBUG +char supermount_debug_enable = 0; +#endif + +static struct super_operations supermount_sops = { + supermount_read_inode, + supermount_notify_change, + supermount_write_inode, + supermount_put_inode, /* put_inode */ + supermount_put_super, + supermount_write_super, + supermount_statfs, + NULL, /* supermount_remount */ +}; + +static char error_buf[1024]; + +/* Mount and mount the hidden fs */ +static void umount_subfs(struct super_block *); + +void supermount_error (struct super_block * sb, const char * function, + const char * fmt, ...) +{ + va_list args; + + va_start (args, fmt); + vsprintf (error_buf, fmt, args); + va_end (args); + printk (KERN_CRIT "SUPERMOUNT error (device %d/%d): %s: %s\n", + MAJOR(sb->s_dev), MINOR(sb->s_dev), function, error_buf); + /* @@ Do we want to do any special error handling here? */ +} + +NORET_TYPE void supermount_panic (struct super_block * sb, + const char * function, + const char * fmt, ...) +{ + va_list args; + + va_start (args, fmt); + vsprintf (error_buf, fmt, args); + va_end (args); + panic ("SUPERMOUNT panic (device %d/%d): %s: %s\n", + MAJOR(sb->s_dev), MINOR(sb->s_dev), function, error_buf); +} + +void supermount_warning (struct super_block * sb, const char * function, + const char * fmt, ...) +{ + va_list args; + + va_start (args, fmt); + vsprintf (error_buf, fmt, args); + va_end (args); + printk (KERN_WARNING "SUPERMOUNT-fs warning (device %d/%d): %s: %s\n", + MAJOR(sb->s_dev), MINOR(sb->s_dev), function, error_buf); +} + +/* Release the superblock and any resources it has reserved */ +void supermount_put_super (struct super_block * sb) +{ + supermount_debug ("Dismounting superfs\n"); + + sb->u.supermount_sb.s_dismount = 1; + + lock_super (sb); + if (sb->u.supermount_sb.s_state != SUPERMOUNT_UNMOUNTED) + umount_subfs (sb); + sb->s_dev = 0; + unlock_super (sb); +} + + +static int copy_option(const char **option, const char *val) +{ + char *tmp; + supermount_debug ("assigning value \"%s\"\n", val); + if (!val || !*val) + return -EINVAL; + tmp = (char *) kmalloc(1 + strlen(val), GFP_KERNEL); + if (!tmp) + return -ENOMEM; + strcpy(tmp, val); + *option = tmp; + return 0; +} + +/* + * This function has been shamelessly adapted from the msdos fs + */ +static int parse_options (char * options, struct super_block *sb) +{ + char * this_char; + char * value; + int rc; + + if (!options) + return 0; + while ((this_char = options)) { + if (!strncmp(this_char, "--,", 3)) + this_char += 2; + if (*this_char == ',') { + /* An empty option, or the option "--", + introduces options to be passed through to + the subfs */ + supermount_debug ("assigning remainder\n"); + return copy_option(&sb->u.supermount_sb.s_data, + ++this_char); + } + if ((options = strchr (this_char, ','))) + *options++ = 0; + + if ((value = strchr (this_char, '=')) != NULL) + *value++ = 0; + + supermount_debug ("parsing option \"%s\"\n", this_char); + if (!strcmp (this_char, "fs")) { + rc = copy_option(&sb->u.supermount_sb.s_type, value); + if (rc) return rc; + } else if (!strcmp (this_char, "dev")) { + rc = copy_option(&sb->u.supermount_sb.s_devname, + value); + if (rc) return rc; + } else if (!strcmp (this_char, "debug")) { + supermount_debug_enable = 1; + } else { + printk ("supermount: " + "Unrecognized mount option %s\n", this_char); + return -EINVAL; + } + } + return 0; +} + +/* read_super: the main mount() entry point into the VFS layer. */ +struct super_block * supermount_read_super (struct super_block * sb, + void * data, + int silent) +{ + sb->s_blocksize = 1024; + sb->s_blocksize_bits = 10; + sb->s_magic = SUPERMOUNT_SUPER_MAGIC; + sb->s_op = &supermount_sops; + unlock_super(sb); + sb->u.supermount_sb.s_state = SUPERMOUNT_UNMOUNTED; + sb->u.supermount_sb.s_default_mode = 0777 | S_IFDIR; + sb->u.supermount_sb.s_mflags = sb->s_flags & (MS_REMOUNT - 1); + sb->u.supermount_sb.s_data = NULL; + sb->u.supermount_sb.s_undermount = NULL; + sb->u.supermount_sb.s_subfs = NULL; + sb->u.supermount_sb.s_opencount = 0; + sb->u.supermount_sb.s_writecount = 0; + sb->u.supermount_sb.s_dirty = 0; + sb->u.supermount_sb.s_rw = 0; + sb->u.supermount_sb.s_dismount = 0; + sb->u.supermount_sb.s_type = NULL; + sb->u.supermount_sb.s_devname = NULL; + + if (parse_options ((char *) data, sb)) { + sb->s_dev = 0; + return NULL; + } + if (!sb->u.supermount_sb.s_type) + copy_option(&sb->u.supermount_sb.s_type, "msdos"); + if (!sb->u.supermount_sb.s_devname) + copy_option(&sb->u.supermount_sb.s_devname, "/dev/fd0"); + + sb->s_flags = sb->u.supermount_sb.s_mflags; + if (!(sb->s_mounted = iget(sb, SUPERMOUNT_ROOT_INO))) { + sb->s_dev = 0; + supermount_error (sb, "supermount_read_super", + "get root inode failed\n"); + return NULL; + } + return sb; +} + +void supermount_write_super (struct super_block * sb) +{ + supermount_media_check(sb); + if (sb->u.supermount_sb.s_state != SUPERMOUNT_UNMOUNTED && + sb->u.supermount_sb.s_rw) { + struct super_block *tmp = sb->u.supermount_sb.s_subfs; + if (tmp && tmp->s_op && tmp->s_op->write_super) + tmp->s_op->write_super(tmp); + } + sb->s_dirt = 0; +} + +#if 0 +int supermount_remount (struct super_block * sb, int * flags, char * data) +{ +} +#endif /* 0 */ + +void supermount_statfs (struct super_block * sb, struct statfs * buf, + int bufsize) +{ + unsigned short fs; + struct statfs tmp; + supermount_media_check(sb); + + if (sb->u.supermount_sb.s_state != SUPERMOUNT_UNMOUNTED) { + struct super_block * tmpsb = + sb->u.supermount_sb.s_undermount->i_mount->i_sb; + fs = get_fs(); + set_fs(KERNEL_DS); + if (tmpsb->s_op && tmpsb->s_op->statfs) + tmpsb->s_op->statfs(tmpsb, &tmp, sizeof(tmp)); + set_fs(fs); + } else { + tmp.f_bsize = sb->s_blocksize; + tmp.f_blocks = 0; + tmp.f_bfree = 0; + tmp.f_bavail = 0; + tmp.f_files = 0; + tmp.f_ffree = 0; + tmp.f_namelen = 0; + } + tmp.f_type = SUPERMOUNT_SUPER_MAGIC; + memcpy_tofs(buf, &tmp, bufsize); +} + +/* + * When we get any attribute changes, we need to propogate them down + * to the subinode. + */ +int supermount_notify_change(struct inode *inode, struct iattr *attr) +{ + struct inode * subi; + int rc; + + supermount_dump_inode (inode); + + /* + * If the inode is a directory, then we may not have a + * subinode: we try to fetch one the hard way. Otherwise, + * namei() should have returned the subinode to the VFS, and + * we can guarantee that the inode we've got here should have + * a hidden inode already (and subiget doesn't work for non- + * directories anyway). + */ + if (S_ISDIR(inode->i_mode)) { + subi = subiget (inode); + if (!subi) + return -ENOENT; + } else { + subi = inode->u.supermount_i.i_hidden; + if (!subi) + supermount_panic (inode->i_sb, + "supermount_notify_change", + "Notify change called with no " + "hidden inode on inode %ld", + inode->i_ino); + } + + rc = supermount_get_write_access(inode->i_sb); + if (rc) + goto error; + + if (subi->i_sb && subi->i_sb->s_op && + subi->i_sb->s_op->notify_change) { + rc = subi->i_sb->s_op->notify_change(subi, attr); + } else { + rc = inode_change_ok(subi, attr); + if (!rc) + inode_setattr(subi, attr); + } + + if (!rc) { + /* + * 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. + */ + mark_subfs_dirty(inode->i_sb); + inode_setattr(inode, attr); + } + + supermount_put_write_access(inode->i_sb); + supermount_go_inactive(inode->i_sb); +error: + if (S_ISDIR(inode->i_mode)) + iput (subi); + return rc; +} + + +/* Check for media change, but without invalidating buffers and inodes */ +static int just_check_disk_change(dev_t dev) +{ + if (!query_disk_change(dev)) + return 0; + supermount_debug("Disk change detected on device %d/%d\n", + MAJOR(dev), MINOR(dev)); + return 1; +} + +static void umount_subfs(struct super_block *sb) +{ + int retval; + struct inode *inode, *subi; + + supermount_debug("Trying to unmount device %s.\n", + sb->u.supermount_sb.s_devname); + + /* Detach the subfs from the supermount root inode */ + inode = sb->s_mounted; + /* Inode may not be set if we are currently unmounting the superfs */ + if (inode) { + subi = inode->u.supermount_i.i_hidden; + if (subi) { + subi->i_shadow = 0; + inode->u.supermount_i.i_hidden = 0; + iput(inode); + } + } + + if (sb->u.supermount_sb.s_subfs->s_mounted->i_count != 1) + supermount_panic (sb, "umount_subfs", + "subfs root i_count = %d, should be 1\n", + sb->u.supermount_sb.s_subfs->s_mounted->i_count); + if (sb->u.supermount_sb.s_undermount->i_count != 1) + supermount_panic (sb, "umount_subfs", + "undermount i_count = %d, should be 1\n", + sb->u.supermount_sb.s_subfs->s_mounted->i_count); + + sb->u.supermount_sb.s_subfs->s_mounted->i_count++; + sb->u.supermount_sb.s_state = SUPERMOUNT_UNMOUNTED; + retval = do_umounti(sb->u.supermount_sb.s_subfs->s_mounted); + if (retval) + supermount_panic (sb, "umount_subfs", + "Help - can't umount subfs!!!"); + supermount_debug("subfs is unmounted.\n"); + sb->u.supermount_sb.s_undermount = 0; + sb->u.supermount_sb.s_version = ++event; + sb->u.supermount_sb.s_subfs = 0; + sb->s_flags = sb->u.supermount_sb.s_mflags; + if (inode) + inode->i_mode = sb->u.supermount_sb.s_default_mode; +} + +/* Return 0 (OK) if medium is valid. */ +int supermount_media_check(struct super_block * sb) +{ + int retval; + unsigned short fs; + + /* If we are dismounting, never return a success value. */ + if (sb->u.supermount_sb.s_dismount) + return 1; + + /* If there are still files open, we never bother checking the + medium. */ + if (sb->u.supermount_sb.s_subfs && + subfs_is_active(sb)) { + supermount_debug ("subfs is active.\n"); + return 0; + } + + supermount_debug ("starting (%sactive now)\n", + (sb->u.supermount_sb.s_subfs && + subfs_is_active(sb)) ? + "" : "not "); + + lock_super(sb); + + /* Are we checking for mount or unmount/remount? */ + if (sb->u.supermount_sb.s_state != SUPERMOUNT_UNMOUNTED) { + /* Already mounted --- check for disk change or + disk not present */ + int dev = sb->u.supermount_sb.s_subfs->s_dev; + if (!just_check_disk_change(dev)) { + unlock_super(sb); + return 0; + } + + /* We have a disk change! Unmount the subfs */ + supermount_debug ("trying to unmount\n"); + umount_subfs(sb); + /* The call to just_check_disk_change may clear the media- + changed flag on the device, so we need to force a media + invalidation. We don't want to do this before unmounting + the subfs, naturally! */ + invalidate_media(dev); + /* Now we are unmounted; fall through to the next check. */ + } + + if (sb->u.supermount_sb.s_state != SUPERMOUNT_UNMOUNTED) { + unlock_super(sb); + return 0; + } + + /* OK, we're unmounted now --- can we remount? Please? */ + if (!sb->u.supermount_sb.s_undermount) { + sb->u.supermount_sb.s_undermount = + iget(sb, SUPERMOUNT_HIDDEN_INO); + if (!sb->u.supermount_sb.s_undermount) + supermount_panic (sb, "supermount_media_check", + "Can't get root hidden inode!"); + sb->u.supermount_sb.s_undermount->i_flags |= S_UNBOUND; + } + + sb->u.supermount_sb.s_version = ++event; + sb->s_mounted->u.supermount_i.i_sb_version = event; + supermount_debug ("trying to mount\n"); + supermount_debug ("fs=%s, dev=%s, data=%s, flags=%08lx\n", + sb->u.supermount_sb.s_type, + sb->u.supermount_sb.s_devname, + sb->u.supermount_sb.s_data, + sb->s_flags); + sb->u.supermount_sb.s_rw = 0; + fs = get_fs(); + set_fs(KERNEL_DS); + sb->s_flags = sb->u.supermount_sb.s_mflags; + retval = do_mounti(sb->u.supermount_sb.s_undermount, + "", + sb->u.supermount_sb.s_devname, + sb->u.supermount_sb.s_type, + sb->s_flags | MS_MGC_VAL | MS_RDONLY, + sb->u.supermount_sb.s_data); + set_fs(fs); + + unlock_super(sb); + if (retval) { + sb->s_flags = sb->u.supermount_sb.s_mflags; + iput(sb->u.supermount_sb.s_undermount); + sb->u.supermount_sb.s_undermount = 0; + supermount_debug ("Mount failed, errno %d\n", -retval); + return 1; + } + /* Hey --- success!!! */ + sb->u.supermount_sb.s_subfs = sb->u.supermount_sb.s_undermount + ->i_mount->i_sb; + if (!sb->u.supermount_sb.s_subfs->s_mounted) + supermount_panic (sb, "supermount_media_check", + "Mounted subfs has no root inode!"); + sb->u.supermount_sb.s_state = SUPERMOUNT_SUSPENDED; + supermount_debug ("Mount succeeded!\n"); + + /* + * For counting open inodes on the subfs, we rely on the fact + * that the root inode is always exactly one count. So make + * sure we guarantee it is always open! + */ + supermount_iopen(sb->s_mounted); + sb->s_mounted->i_flags |= S_UNBOUND; + return 0; +} + +/* + * Check whether an inode still belongs to valid medium. + */ +int supermount_check_inode(struct inode * inode) +{ + supermount_dump_inode(inode); + + if (supermount_media_check(inode->i_sb)) + return 1; + if (inode_is_obsolete(inode)) { + /* + * What happens if we remount an old disk, and get back + * the same superinode number? subiget() will handle it + * through the i_subino field, and supermount_attach + * will avoid reusing the old inode. + */ + return 1; + } + return 0; +} + +/* + * 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_lock_door (struct super_block * sb, int lock) +{ + kdev_t dev; + int i; + struct file_operations * fops; + + dev = sb->u.supermount_sb.s_subfs->s_dev; + i = MAJOR(dev); + if (i >= MAX_BLKDEV || (fops = get_blkfops(i)) == NULL) + return; + if (fops->mediactl == NULL) + return; + fops->mediactl(dev, lock ? MEDIA_LOCK : MEDIA_UNLOCK, 0); +} + +/* + * Try to put the filesystem into writable mode for the duration of an + * open(O_RDWR) or an inode operation requiring write access. Don't + * actually obtain a write refcount to the superfs, but do keep the + * subfs writable if the test succeeds. + */ + +int supermount_test_write_access (struct super_block * sb) +{ + int retval = 0; + + supermount_debug ("wcount currently %d\n", + sb->u.supermount_sb.s_writecount); + + /* + * We can skip the test (and just return success) if the + * subfs is already known to be writable. + */ + if (!sb->u.supermount_sb.s_rw) { + retval = supermount_get_write_access (sb); + if (!retval) + sb->u.supermount_sb.s_writecount--; + } + + return retval; +} + +/* + * Try to put the filesystem into writable mode for the duration of + * an open(O_RDWR) or an inode operation requiring write access. + */ + +int supermount_get_write_access (struct super_block * sb) +{ + int retval = 0; + + supermount_debug ("wcount currently %d\n", + sb->u.supermount_sb.s_writecount); + + lock_super (sb); + + if (sb->u.supermount_sb.s_writecount == 0) { + /* + * We may already have write access to the filesystem + */ + if (!sb->u.supermount_sb.s_rw) { + if (sb->s_flags & MS_RDONLY) + retval = -EROFS; + else { + supermount_debug ("remounting read/write\n"); + + retval = do_remount_sb + (sb->u.supermount_sb.s_subfs, + sb->s_flags & MS_RMT_MASK, + NULL); + } + } + if (!retval && !subfs_is_active(sb)) { + supermount_debug ("going online.\n"); + sb->u.supermount_sb.s_state = SUPERMOUNT_ONLINE; + } + } + + if (!retval) { + sb->u.supermount_sb.s_writecount++; + sb->u.supermount_sb.s_rw = 1; + } + + supermount_debug ("supermount_get_write_access, " + "wcount bumped to %d\n", + sb->u.supermount_sb.s_writecount); + + unlock_super (sb); + return retval; +} + +/* + * Once we're done, release write access. + */ +void supermount_put_write_access (struct super_block * sb) +{ + supermount_debug ("wcount currently %d\n", + sb->u.supermount_sb.s_writecount); + + if (!sb->u.supermount_sb.s_writecount) { + supermount_panic (sb, "supermount_put_write_access", + "filesystem not write accessed"); + } + + if (!sb->u.supermount_sb.s_rw) { + supermount_panic (sb, "supermount_put_write_access", + "filesystem not read/write"); + } + + sb->u.supermount_sb.s_writecount--; + +} + +/* + * Check the state of the supermount filesystem after it has been + * released by some inode. If it is no longer active, do the necessary + * cleanup: fsync the disk, remount it readonly if appropriate, and + * unlock the drive door if possible. + */ + +void supermount_go_inactive(struct super_block *sb) +{ + supermount_debug("Checking state, currently %d\n", + sb->u.supermount_sb.s_state); + if (sb->u.supermount_sb.s_state != SUPERMOUNT_ONLINE) + return; + + if (!subfs_is_active(sb)) { + supermount_debug("going offline.\n"); + sb->u.supermount_sb.s_state = + SUPERMOUNT_SUSPENDED; + + /* + * Do an initial fsync_dev. We do this before locking the + * superblock to minimise the duration of the lock. + */ + if (subfs_is_dirty(sb)) { + mark_subfs_clean(sb); + fsync_dev(sb->u.supermount_sb.s_subfs->s_dev); + } + + /* + * We need to recheck the state of the filesystem now. + * It may have become active again. If not, we'll do + * some cleanup under the superblock lock to tidy things + * up. */ + lock_super(sb); + + if (subfs_is_active(sb) || + sb->u.supermount_sb.s_state != SUPERMOUNT_SUSPENDED) { + unlock_super(sb); + return; + } + + /* + * First thing is, has the filesystem been made + * read-write since it became active? If so, now is the + * time to retire it back to read-only state. + */ + + if (sb->u.supermount_sb.s_writecount) { + supermount_panic (sb, "supermount_go_inactive", + "idle filesystem is write accessed"); + } + + if (sb->u.supermount_sb.s_rw) { + int retval; + + /* + * Yup, it's read-write --- remount it. + */ + + supermount_debug ("remounting readonly\n"); + + retval = do_remount_sb (sb->u.supermount_sb.s_subfs, + (sb->s_flags & MS_RMT_MASK) + | MS_RDONLY, + NULL); + if (retval) + supermount_panic (sb, "supermount_go_inactive", + "Error %d remounting " + "subfs readonly", retval); + + fsync_dev(sb->u.supermount_sb.s_subfs->s_dev); + + sb->u.supermount_sb.s_rw = 0; + } + + + /* + * We can now afford to unlock the drive door. + */ + supermount_debug("unlocking door.\n"); + supermount_lock_door(sb, 0); + + unlock_super(sb); + } +} + --- linux/include/linux/ext2_fs.h.~1~ Sun Oct 19 21:26:27 1997 +++ linux/include/linux/ext2_fs.h Mon Nov 24 19:53:38 1997 @@ -283,6 +283,7 @@ */ #define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */ #define EXT2_ERROR_FS 0x0002 /* Errors detected */ +#define EXT2_CHECKED_FS 0x0004 /* Already done a fs check */ /* * Mount flags --- linux/include/linux/fs.h.~1~ Mon Nov 24 19:50:46 1997 +++ linux/include/linux/fs.h Mon Nov 24 19:53:38 1997 @@ -75,6 +75,10 @@ #define S_IMMUTABLE 512 /* Immutable file */ #define MS_NOATIME 1024 /* Do not update access times. */ #define S_BAD_INODE 2048 /* Marker for unreadable inodes */ +#define S_UNBOUND 4096 /* inode is not bound to user space, + and so the filesystem may be + unmounted even if this inode is in + use. */ /* * Flags that can be altered by MS_REMOUNT @@ -106,6 +110,7 @@ #define IS_APPEND(inode) ((inode)->i_flags & S_APPEND) #define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE) #define IS_NOATIME(inode) ((inode)->i_flags & MS_NOATIME) +#define IS_UNBOUND(inode) ((inode)->i_flags & S_UNBOUND) #define UPDATE_ATIME(inode) \ if (!IS_NOATIME(inode) && !IS_RDONLY(inode)) { \ @@ -226,6 +231,7 @@ } #include +#include #include #include #include @@ -294,6 +300,8 @@ unsigned long i_nrpages; struct semaphore i_sem; struct inode_operations *i_op; + struct inode * i_shadow; + struct inode_shadow_operations * i_shadow_op; struct super_block *i_sb; struct wait_queue *i_wait; struct file_lock *i_flock; @@ -316,6 +324,7 @@ unsigned short i_writecount; union { struct pipe_inode_info pipe_i; + struct supermount_inode_info supermount_i; struct minix_inode_info minix_i; struct ext_inode_info ext_i; struct ext2_inode_info ext2_i; @@ -422,6 +431,7 @@ extern int fasync_helper(struct inode *, struct file *, int, struct fasync_struct **); +#include #include #include #include @@ -451,6 +461,7 @@ struct inode * s_mounted; struct wait_queue * s_wait; union { + struct supermount_sb_info supermount_sb; struct minix_sb_info minix_sb; struct ext_sb_info ext_sb; struct ext2_sb_info ext2_sb; @@ -473,6 +484,16 @@ * to have different dirent layouts depending on the binary type. */ typedef int (*filldir_t)(void *, const char *, int, off_t, ino_t); + +/* + * These are the "op" operation codes for mediactl() media control + * calls for device special files + */ +enum { + MEDIA_LOCK = 1234, /* Lock the drive door */ + MEDIA_UNLOCK /* Unlock the drive door */ +}; + struct file_operations { int (*lseek) (struct inode *, struct file *, off_t, int); @@ -488,6 +509,7 @@ int (*fasync) (struct inode *, struct file *, int); int (*check_media_change) (kdev_t dev); int (*revalidate) (kdev_t dev); + int (*mediactl) (kdev_t dev, int op, int optarg); }; struct inode_operations { @@ -511,6 +533,16 @@ int (*smap) (struct inode *,int); }; +struct inode_shadow_operations { + int (*open_file) (struct inode *, struct file *); + void (*write) (struct inode *); + void (*release) (struct inode *); + void (*release_file) (struct inode *, struct file *); + int (*test_write_access) (struct inode *); + int (*get_write_access) (struct inode *); + void (*put_write_access) (struct inode *); +}; + struct super_operations { void (*read_inode) (struct inode *); int (*notify_change) (struct inode *, struct iattr *); @@ -617,9 +649,11 @@ } extern int check_disk_change(kdev_t dev); +extern int query_disk_change(kdev_t dev); extern void invalidate_inodes(kdev_t dev); extern void invalidate_inode_pages(struct inode *); extern void invalidate_buffers(kdev_t dev); +extern void invalidate_media(kdev_t dev); extern int floppy_is_wp(int minor); extern void sync_inodes(kdev_t dev); extern void sync_dev(kdev_t dev); @@ -681,6 +715,13 @@ extern void show_buffers(void); extern void mount_root(void); +extern int do_mounti(struct inode *_inode, const char *, const char *, + const char *, unsigned long, const void *); +extern int do_umounti(struct inode *); +extern int do_remount_sb(struct super_block *sb, int flags, char * data); + +extern long do_lseek(struct file *, off_t, unsigned int); + #ifdef CONFIG_BLK_DEV_INITRD extern kdev_t real_root_dev; @@ -706,6 +747,21 @@ extern inline struct inode * iget(struct super_block * sb,int nr) { return __iget(sb, nr, 1); +} + +static inline int make_shadow_writable(struct inode *inode) +{ + int err = 0; + + /* A shadowed inode may need to be enabled for write access */ + if (inode->i_shadow && inode->i_shadow->i_shadow_op && + inode->i_shadow->i_shadow_op->test_write_access) { + err = inode->i_shadow->i_shadow_op + ->test_write_access(inode->i_shadow); + if (err) + iput(inode); + } + return err; } /* kludge to get SCSI modules working */ --- linux/include/linux/supermount_fs.h.~1~ Mon Nov 24 19:51:16 1997 +++ linux/include/linux/supermount_fs.h Mon Nov 24 19:57:58 1997 @@ -0,0 +1,198 @@ +/* + * linux/include/linux/supermount_fs.h + * + * Defines and strutures for the dynamic remounting of removable media + * + * Copyright (C) 1995 + * Stephen Tweedie (sct@dcs.ed.ac.uk) + * + * from + * + * linux/include/linux/minix_fs.h + * Copyright (C) 1991, 1992 Linus Torvalds + * + * and + * + * linux/include/linux/ext2_fs.h + * Copyright (C) 1992, 1993, 1994, 1995 Remy Card + */ + +#ifndef _LINUX_SUPERMOUNT_FS_H +#define _LINUX_SUPERMOUNT_FS_H + +#include + +#define SUPERMOUNT_DEBUG + +#define SUPERMOUNT_VERSION "0.5" + +/* + * Debug code + */ +#ifdef SUPERMOUNT_DEBUG + extern char supermount_debug_enable; + #include + + #define supermount_debug(f, a...) \ + { \ + if (supermount_debug_enable) { \ + printk ("SUPERMOUNT DEBUG (%s:%s, %d): ", \ + __FILE__, __FUNCTION__, __LINE__); \ + printk (f, ## a); \ + } \ + } + + #define supermount_dump_inode(i) \ + supermount_debug("inode= %ld, sub=%ld, count=%d/%d%s%s\n", \ + (i)->i_ino, \ + ((i)->u.supermount_i.i_hidden ? \ + (i)->u.supermount_i.i_hidden->i_ino : 0), \ + (i)->i_count, \ + ((i)->u.supermount_i.i_hidden ? \ + (i)->u.supermount_i.i_hidden->i_count : -1), \ + (i)->i_dirt ? ", dirty" : "", \ + inode_is_obsolete((i)) ? ", obsolete" : "") + +#else + #define supermount_debug(f, a...) /**/ + #define supermount_dump_inode(i) +#endif + +/* + * Special inodes numbers + */ +#define SUPERMOUNT_HIDDEN_INO 0 /* Hidden inode for + sub-mounting */ +#define SUPERMOUNT_ROOT_INO 1 /* Root inode */ + +/* + * The supermount superblock magic number + */ +#define SUPERMOUNT_SUPER_MAGIC 0x9fa1 + +#ifdef __KERNEL__ +/* + * Function prototypes + */ + +/* + * Ok, these declarations are also in but none of the + * supermount source programs needs to include it so they are duplicated here. + */ +#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) +# define NORET_TYPE __volatile__ +# define ATTRIB_NORET /**/ +# define NORET_AND /**/ +#else +# define NORET_TYPE /**/ +# define ATTRIB_NORET __attribute__((noreturn)) +# define NORET_AND noreturn, +#endif + +/* How to test if a supermount filesystem is active or not: */ + + +static inline int subfs_is_active(struct super_block *sb) +{ + /* + * The subfs is always active if it is write-accessed. (We + * don't resort to inode counts in that case since we can be + * write-active even if we are only accessing directories, in + * the case of such operations as renames.) Otherwise, it is + * deemed inactive iff only the root is open, and its i_count is + * precisely one. + */ + return sb->u.supermount_sb.s_writecount || + (sb->u.supermount_sb.s_state != SUPERMOUNT_UNMOUNTED && + (sb->u.supermount_sb.s_opencount > 1 || + sb->u.supermount_sb.s_undermount->i_mount->i_count > 1)); +} + +/* How to test if an inode is obsolete: */ +static inline int inode_is_obsolete(struct inode *inode) +{ + return (inode->u.supermount_i.i_sb_version != + inode->i_sb->u.supermount_sb.s_version); +} + +/* Manage the subfs dirty flag */ +static inline void mark_subfs_dirty(struct super_block *sb) +{ + sb->u.supermount_sb.s_dirty = 1; +} +static inline void mark_subfs_clean(struct super_block *sb) +{ + sb->u.supermount_sb.s_dirty = 0; +} +static inline int subfs_is_dirty(struct super_block *sb) +{ + return (sb->u.supermount_sb.s_dirty); +} + +/* shadow.c */ +extern void supermount_read_hidden_inode (struct inode *); + +/* inode.c */ +extern struct inode * subiget(struct inode *); +extern void supermount_iopen(struct inode *); +extern void supermount_iclose(struct inode *); +extern struct inode * supermount_attach(struct super_block *, struct inode *); +extern void supermount_read_inode (struct inode *); +extern void supermount_write_inode (struct inode *); +extern void supermount_put_inode (struct inode *); +extern int supermount_notify_change(struct inode *, struct iattr *); +extern void supermount_put_inode (struct inode *); +extern int supermount_permission (struct inode *, int mask); + +/* namei.c */ +extern void supermount_release (struct inode *, struct file *); +extern int supermount_lookup (struct inode *,const char *, int, struct inode **); +extern int supermount_create (struct inode *,const char *, int, int, + struct inode **); +extern int supermount_mkdir (struct inode *, const char *, int, int); +extern int supermount_rmdir (struct inode *, const char *, int); +extern int supermount_unlink (struct inode *, const char *, int); +extern int supermount_symlink (struct inode *, const char *, int, const char *); +extern int supermount_link (struct inode *, struct inode *, const char *, int); +extern int supermount_mknod (struct inode *, const char *, int, int, int); +extern int supermount_rename (struct inode *, const char *, int, + struct inode *, const char *, int, int); + +/* super.c */ +extern void supermount_error (struct super_block *, const char *, const char *, ...) + __attribute__ ((format (printf, 3, 4))); +extern NORET_TYPE void supermount_panic (struct super_block *, const char *, + const char *, ...) + __attribute__ ((NORET_AND format (printf, 3, 4))); +extern void supermount_warning (struct super_block *, const char *, const char *, ...) + __attribute__ ((format (printf, 3, 4))); +extern void supermount_put_super (struct super_block *); +extern void supermount_write_super (struct super_block *); +extern int supermount_remount (struct super_block *, int *, char *); +extern struct super_block * supermount_read_super (struct super_block *,void *,int); +extern void supermount_statfs (struct super_block *, struct statfs *, int); +extern int supermount_media_check (struct super_block *); +extern int supermount_check_inode (struct inode *); +extern void supermount_lock_door (struct super_block *, int); +extern int supermount_test_write_access (struct super_block *); +extern int supermount_get_write_access (struct super_block *); +extern void supermount_put_write_access (struct super_block *); +extern void supermount_go_inactive(struct super_block *); + +/* dir.c */ +extern struct inode_operations supermount_dir_iops; +extern struct inode_operations supermount_file_iops; +extern struct inode_operations supermount_root_iops; + +/* inode.c */ +extern struct inode_shadow_operations supermount_shadow_iops; + +/* file.c */ +/* extern struct inode_operations supermount_shadow_file_iops; */ + +/* symlink.c */ +extern struct inode_operations supermount_symlink_iops; + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_SUPERMOUNT_FS_H */ --- linux/include/linux/supermount_fs_i.h.~1~ Mon Nov 24 19:51:16 1997 +++ linux/include/linux/supermount_fs_i.h Mon Nov 24 19:51:16 1997 @@ -0,0 +1,23 @@ +/* + * linux/include/linux/supermount_fs_i.h + * + * In-core inode structure for the dynamic remounting of removable media + * + * Copyright (C) 1995 + * Stephen Tweedie (sct@dcs.ed.ac.uk) + * + */ + +#ifndef _LINUX_SUPERMOUNT_FS_I_H +#define _LINUX_SUPERMOUNT_FS_I_H + +struct supermount_inode_info { + struct inode * i_hidden; + int i_sb_version; + int i_subino; + int i_counted;/* Has this inode been counted in + the superblock reference counts + yet? */ +}; + +#endif /* _LINUX_SUPERMOUNT_FS_I_H */ --- linux/include/linux/supermount_fs_sb.h.~1~ Mon Nov 24 19:51:16 1997 +++ linux/include/linux/supermount_fs_sb.h Mon Nov 24 19:51:16 1997 @@ -0,0 +1,48 @@ +/* + * linux/include/linux/supermount_fs_sb.h + * + * In-core superblock struct for the dynamic remounting of removable media + * + * Copyright (C) 1995 + * Stephen Tweedie (sct@dcs.ed.ac.uk) + * + */ + +#ifndef _LINUX_SUPERMOUNT_FS_SB_H +#define _LINUX_SUPERMOUNT_FS_SB_H + +typedef enum { + SUPERMOUNT_UNMOUNTED, /* No media mounted */ + SUPERMOUNT_SUSPENDED, /* Mounted but suspended because + no files open */ + SUPERMOUNT_ONLINE, /* Mounted and active */ +} sm_state_t; + +/* + * supermount super-block data in memory + */ +struct supermount_sb_info { + sm_state_t s_state; + int s_version; /* Used to indicate obsolete inodes */ + mode_t s_default_mode; /* Default mode for supermount root */ + + const char * s_type; /* Type of fs to be sub-mounted */ + const char * s_devname; /* Where to mount the subfs */ + int s_mflags; /* Flags to pass when mounting subfs */ + const char * s_data; /* Data to pass when mounting subfs */ + + struct inode * s_undermount; /* Mount point for subfs */ + struct super_block * s_subfs; /* The submounted fs sb */ + int s_opencount; /* Refcount of opened inodes + (an inode in use as cwd + does not count as open) */ + int s_writecount; /* Refcount of write accesses + on the filesystem */ + + /* Flags */ + int s_dirty : 1; /* Do we need to fsync() the subfs? */ + int s_rw : 1; /* Is the subfs mounted read/write? */ + int s_dismount : 1; /* Filesystem is umounting */ +}; + +#endif /* _LINUX_SUPERMOUNT_FS_SB_H */