diff -urN linux-2.6.25.4.orig/fs/Kconfig linux-2.6.25.4/fs/Kconfig --- linux-2.6.25.4.orig/fs/Kconfig 2008-04-18 01:20:10 +0300 +++ linux-2.6.25.4/fs/Kconfig 2008-05-25 10:08:00 +0300 @@ -598,6 +598,17 @@ local network, you probably do not need an automounter, and can say N here. +config SUBFS_FS + tristate "Kernel subfs support" + help + The subfs is a tool to automatically mount file systems on demand. + + To use the subfs you need the user-space tools from + . + + To compile this support as a module, choose M here: the module will be + called subfs. + config FUSE_FS tristate "Filesystem in Userspace support" help diff -urN linux-2.6.25.4.orig/fs/Makefile linux-2.6.25.4/fs/Makefile --- linux-2.6.25.4.orig/fs/Makefile 2008-04-18 01:20:10 +0300 +++ linux-2.6.25.4/fs/Makefile 2008-05-25 10:08:00 +0300 @@ -105,6 +105,7 @@ obj-$(CONFIG_QNX4FS_FS) += qnx4/ obj-$(CONFIG_AUTOFS_FS) += autofs/ obj-$(CONFIG_AUTOFS4_FS) += autofs4/ +obj-$(CONFIG_SUBFS_FS) += subfs.o obj-$(CONFIG_ADFS_FS) += adfs/ obj-$(CONFIG_FUSE_FS) += fuse/ obj-$(CONFIG_UDF_FS) += udf/ diff -urN linux-2.6.25.4.orig/fs/subfs.c linux-2.6.25.4/fs/subfs.c --- linux-2.6.25.4.orig/fs/subfs.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4/fs/subfs.c 2008-05-25 10:12:11 +0300 @@ -0,0 +1,468 @@ +/* + * subfs.c + * + * Copyright (C) 2003-2004 Eugene S. Weiss + * + * Distributed under the terms of the GNU General Public License version 2 + * or above. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "subfs.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Eugene S. Weiss"); + + +/* get_subfs_vfsmount tries to find the vfsmount structure assigned to + * the pending mount. It looks for a vfsmount structure matching the + * superblock pointer sent. This is not ideal, but I don't know of + * another way to find the structure without altering the code in the + * mount routines. + */ +static struct vfsmount *get_subfs_vfsmount(struct super_block *sb) +{ + struct fs_struct *init_fs; + struct list_head *entry, *head, *lh; + struct vfsmount *mnt; + + /* Get the head of the global mount list from the init process. */ + /* Is there a better way? */ + init_fs = init_task.fs; + head = &init_fs->root.mnt->mnt_list; + + /* Go through the list and look for a superblock pointer match. */ + list_for_each_safe(entry, lh, head) { + mnt = list_entry(entry, struct vfsmount, mnt_list); + if (mnt->mnt_sb == sb) + return mnt; + } + ERR("subfs: Unable to find mount structure for superblock.\n"); + return NULL; /* If there was no match */ +} + + +/* Same as set_fs_pwd from namespace.c. There's a problem with the + * symbol. When it is fixed, discard this. + * Replace the fs->{pwdmnt,pwd} with {mnt,dentry}. Put the old values. + * It can block. Requires the big lock held. + */ +static void subfs_set_fs_pwd(struct fs_struct *fs, struct vfsmount *mnt, + struct dentry *dentry) +{ + struct dentry *old_pwd; + struct vfsmount *old_pwdmnt; + + write_lock(&fs->lock); + old_pwd = fs->pwd.dentry; + old_pwdmnt = fs->pwd.mnt; + fs->pwd.mnt = mntget(mnt); + fs->pwd.dentry = dget(dentry); + write_unlock(&fs->lock); + + if (old_pwd) { + dput(old_pwd); + mntput(old_pwdmnt); + } +} + + +/* Quickly sends an ignored signal to the signal handling system. This + * causes the system to restart the system call when it receives the + * -ERESTARTSYS error. + */ +static void subfs_send_signal(void) +{ + struct task_struct *task = current; + int signal = SIGCONT; + + rcu_read_lock(); + spin_lock_irq(&task->sighand->siglock); + sigaddset(&task->pending.signal, signal); + spin_unlock_irq(&task->sighand->siglock); + rcu_read_unlock(); + set_tsk_thread_flag(task, TIF_SIGPENDING); + return; +} + + +/* If the option "procuid" is chosen when subfs is mounted, the uid + * and gid numbers for the current process will bea dded to the mount + * option line. Hence, non-unix filesystems will be mounted with + * that ownership. + */ +static void add_procuid(struct subfs_mount *sfs_mnt) +{ + struct task_struct *task = current; + char *o = kmalloc(strlen(sfs_mnt->options) + 1 + 32 + 1, GFP_KERNEL); + + if (sfs_mnt->options[0] == '\0') + sprintf(o, "uid=%d,gid=%d", task->uid, task->gid); + else + sprintf(o, "%s,uid=%d,gid=%d", sfs_mnt->options, task->uid, task->gid); + + kfree(sfs_mnt->options); + sfs_mnt->options = o; +} + + +/* This routine calls the /sbin/submountd program to mount the + * appropriate filesystem on top of the subfs mount. Returns + * 0 if the userspace program exited normally, or an error if + * it did not. + */ +static int mount_real_fs(struct subfs_mount *sfs_mnt) +{ + char *argv[7] = + { sfs_mnt->helper_prog, NULL, NULL, NULL, NULL, NULL, NULL }; + char *envp[2] = { "HOME=/", NULL }; + char *path_buf; + int result, len = 0; + struct path p; + + argv[1] = sfs_mnt->device; + path_buf = (char *) __get_free_page(GFP_KERNEL); + if (!path_buf) + return -ENOMEM; + p.mnt = sfs_mnt->mount; + p.dentry = p.mnt->mnt_root; + argv[2] = d_path(&p, path_buf, PAGE_SIZE); + argv[3] = sfs_mnt->req_fs; + if (!(argv[4] = kmalloc(17, GFP_KERNEL))) { + free_page((unsigned long) path_buf); + return -ENOMEM; /* 64 bits on some platforms */ + } + sprintf(argv[4], "%lx", sfs_mnt->flags); + len = strlen(sfs_mnt->options); + if (sfs_mnt->procuid) + add_procuid(sfs_mnt); + argv[5] = sfs_mnt->options; + result = call_usermodehelper(sfs_mnt->helper_prog, argv, envp, 1); + free_page((unsigned long) path_buf); + kfree(argv[4]); + if (sfs_mnt->procuid) + sfs_mnt->options[len] = '\0'; + return result; +} + + +/* This routine returns a pointer to the filesystem mounted on top + * of the subfs mountpoint, or an error pointer if it was unable to. + */ +static struct vfsmount *get_child_mount(struct subfs_mount *sfs_mnt) +{ + struct vfsmount *child; + int result; + /* First time: find the vfsmount structure that matches sfs_mnt. */ + if (!sfs_mnt->mount) { + if(!(sfs_mnt->mount = get_subfs_vfsmount(sfs_mnt->sb))) + return ERR_PTR(-ENOMEDIUM); + sfs_mnt->flags = sfs_mnt->sb->s_flags; + if (sfs_mnt->mount->mnt_flags & MNT_NOSUID) + sfs_mnt->flags |= MS_NOSUID; + if (sfs_mnt->mount->mnt_flags & MNT_NODEV) + sfs_mnt->flags |= MS_NODEV; + if (sfs_mnt->mount->mnt_flags & MNT_NOEXEC) + sfs_mnt->flags |= MS_NOEXEC; + } + /* Check to see if a child mount does not already exist. */ + if (&sfs_mnt->mount->mnt_mounts == sfs_mnt->mount->mnt_mounts.next) { + result = mount_real_fs(sfs_mnt); + if (result) { + ERR("subfs: "SUBMOUNTD_PATH" unsuccessful attempt to mount media (%d)\n", + result); + /* Workaround for call_usermodehelper return value bug. */ + if(result < 0) + return ERR_PTR(result); + return ERR_PTR(-ENOMEDIUM); + } + if (&sfs_mnt->mount->mnt_mounts + == sfs_mnt->mount->mnt_mounts.next) { + ERR("subfs: submountd mount failure.\n"); + return ERR_PTR(-ENOMEDIUM); + } + } + child = list_entry(sfs_mnt->mount->mnt_mounts.next, + struct vfsmount, mnt_child); + return child; +} + + +/* Implements the lookup method for subfs. Tries to get the child + * mount. If it succeeds, it emits a signal and returns + * -ERESTARSYS. If it receives an error, it passes it on to the + * system. It raises the semaphore in the directory inode before mounting + * because the mount routine also calls lookup, and hence a function is + * calling itself from within semaphore protected code. Only the semaphore + * on the subfs pseudo-directory is effected, so this isn't deadly. + */ +static struct dentry *subfs_lookup(struct inode *dir, + struct dentry *dentry, struct nameidata *nd) +{ + struct subfs_mount *sfs_mnt = dir->i_sb->s_fs_info; + struct vfsmount *child; + + /* This is ugly, but prevents a lockup during mount. */ + mutex_unlock(&dir->i_mutex); + if (down_interruptible(&sfs_mnt->sem)) { + mutex_lock(&dir->i_mutex); /* put the dir sem back down if interrupted */ + return ERR_PTR(-ERESTARTSYS); + } + child = get_child_mount(sfs_mnt); + up(&sfs_mnt->sem); + mutex_lock(&dir->i_mutex); + if (IS_ERR(child)) + return (void *) child; + subfs_send_signal(); + if (sfs_mnt->mount == current->fs->pwd.mnt) + subfs_set_fs_pwd(current->fs, child, child->mnt_root); + return ERR_PTR(-ERESTARTSYS); +} + + +/* Implements the open method for subfs. Tries to get the child mount + * for the subfs mountpoint which is being opened. Returns -ERESTARTSYS + * and emits an ignored signal to the calling process if it succeeds, + * or passes the error message received if it fails. + */ +static int subfs_open(struct inode *inode, struct file *filp) +{ + struct subfs_mount *sfs_mnt = filp->f_dentry->d_sb->s_fs_info; + struct vfsmount *child; + if (down_interruptible(&sfs_mnt->sem)) + return -ERESTARTSYS; + child = get_child_mount(sfs_mnt); + up(&sfs_mnt->sem); + if (IS_ERR(child)) + return PTR_ERR(child); + subfs_send_signal(); + if (sfs_mnt->mount == current->fs->pwd.mnt) + subfs_set_fs_pwd(current->fs, child, child->mnt_root); + return -ERESTARTSYS; +} + + +/* Implements the statfs method so df and such will work on the mountpoint. + */ +static int subfs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + struct subfs_mount *sfs_mnt = dentry->d_sb->s_fs_info; + struct vfsmount *child; + if (down_interruptible(&sfs_mnt->sem)) + return -ERESTARTSYS; + child = get_child_mount(sfs_mnt); + up(&sfs_mnt->sem); + if (IS_ERR(child)) + return PTR_ERR(child); + subfs_send_signal(); + return -ERESTARTSYS; +} + + +/* Creates the inodes for subfs superblocks. + */ +static struct inode *subfs_make_inode(struct super_block *sb, int mode) +{ + struct inode *ret = new_inode(sb); + + if (ret) { + ret->i_mode = mode; + ret->i_uid = ret->i_gid = 0; + ret->i_blocks = 0; + ret->i_atime = ret->i_mtime = ret->i_ctime = CURRENT_TIME; + ret->i_fop = &subfs_file_ops; + } + return ret; +} + + +/* Fills the fields for the superblock created when subfs is mounted. + */ +static int subfs_fill_super(struct super_block *sb, void *data, int silent) +{ + struct inode *root; + struct dentry *root_dentry; + + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + sb->s_magic = SUBFS_MAGIC; + sb->s_op = &subfs_s_ops; + root = subfs_make_inode(sb, S_IFDIR|ROOT_MODE); + if (!root) + goto out; + root->i_op = &subfs_dir_inode_operations; + root_dentry = d_alloc_root(root); + if (!root_dentry) + goto out_iput; + sb->s_root = root_dentry; + return 0; + out_iput: + iput(root); + out: + return -ENOMEM; +} + + +/* Parse the options string and remove submount specific options + * and store the appropriate data. + */ +static int proc_opts(struct subfs_mount *sfs_mnt, void *data) +{ + char *opts = data, *opt, *ptr, *fs = NULL, *prog; + int len; + + if (!opts) { + if (!(data = opts = kmalloc(PAGE_SIZE, GFP_KERNEL))) + return -EINVAL; + strcat(opts, "fs=auto"); + } + len = strnlen(opts, PAGE_SIZE - 1) + 1; + if (strstr(opts, "procuid")) + sfs_mnt->procuid = 1; + if (!(sfs_mnt->options = kmalloc(len, GFP_KERNEL))) + return -ENOMEM; + sfs_mnt->options[0] = '\0'; + while ((opt = strsep(&opts, ","))) { + if ((ptr = strstr(opt, "program="))) { + if (!(prog = kmalloc((strlen(ptr) - 7), GFP_KERNEL))) + return -ENOMEM; + strcpy(prog, (ptr + 8)); + kfree(sfs_mnt->helper_prog); + sfs_mnt->helper_prog = prog; + } + else if ((ptr = strstr(opt, "fs="))) { + if (!(fs = kmalloc((strlen(ptr) - 2), GFP_KERNEL))) + return -ENOMEM; + strcpy(fs, (ptr + 3)); + sfs_mnt->req_fs = fs; + } + else if ((ptr = strstr(opt, "procuid"))) { + } else { + strncat(sfs_mnt->options, opt, 32); + strcat(sfs_mnt->options, ","); + } + } + if((len = strlen(sfs_mnt->options)) && (sfs_mnt->options[len-1] == ',')) + sfs_mnt->options[len-1] = '\0'; + if ( !sfs_mnt->req_fs ){ + if (!(sfs_mnt->req_fs = kmalloc(5, GFP_KERNEL))) + return -ENOMEM; /* 64 bits on some platforms */ + strcpy(sfs_mnt->req_fs, "auto" ); + } + return 0; +} + + + +/* subfs_get_super is the subfs implementation of the get_sb method on + * the file_system_type structure. It should only be called in the + * case of a mount. It creates a new subfs_mount structure, fills + * the fields of the structure, except for the mount structure, and then + * calls a generic get_sb function. The superblock pointer is stored on + * the subfs_mount structure, and returned to the calling function. The + * subfs_mount structure is pointed to by the s_fs_info field of the + * superblock structure. + */ +static int subfs_get_super(struct file_system_type *fst, + int flags, const char *devname, void *data, + struct vfsmount *mnt) +{ + char *device; + struct subfs_mount *newmount; + int ret; + + if (!(newmount = kmalloc(sizeof(struct subfs_mount), GFP_KERNEL))) { + ret = -ENOMEM; + goto err; + } + newmount->req_fs = NULL; + newmount->sb = NULL; + newmount->mount = NULL; + newmount->procuid = 0; + sema_init(&newmount->sem, 1); + if (!(device = kmalloc((strlen(devname) + 1), GFP_KERNEL))) { + ret = -ENOMEM; + goto err; + } + strcpy(device, devname); + newmount->device = device; + if (!(newmount->helper_prog = + kmalloc(sizeof(SUBMOUNTD_PATH), GFP_KERNEL))) { + ret = -ENOMEM; + goto err; + } + strcpy(newmount->helper_prog, SUBMOUNTD_PATH); + if ((ret = proc_opts(newmount, data))) + goto err; + ret = get_sb_nodev(fst, flags, data, subfs_fill_super, mnt); + if (ret) + goto err; + newmount->sb = mnt->mnt_sb; + newmount->sb->s_fs_info = newmount; + return ret; + +err: + return ret; +} + + +/* subfs_kill_super is the subfs implementation of the kill_sb method. + * It should be called only on umount. It cleans up the appropriate + * subfs_mount structure and then calls a generic function to actually + * clean up the superblock structure. + */ +static void subfs_kill_super(struct super_block *sb) +{ + struct subfs_mount *sfs_mnt = sb->s_fs_info; + + if(sfs_mnt) { + if (sfs_mnt->device) + kfree(sfs_mnt->device); + if (sfs_mnt->options) + kfree(sfs_mnt->options); + if (sfs_mnt->req_fs) + kfree(sfs_mnt->req_fs); + if (sfs_mnt->helper_prog) + kfree(sfs_mnt->helper_prog); + kfree(sfs_mnt); + sb->s_fs_info = NULL; + } + kill_litter_super(sb); + return; +} + + +static int __init subfs_init(void) +{ + printk(KERN_INFO "subfs %s\n", SUBFS_VER); + return register_filesystem(&subfs_type); +} + +static void __exit subfs_exit(void) +{ + printk(KERN_INFO "subfs exiting.\n"); + unregister_filesystem(&subfs_type); +} + +module_init(subfs_init); +module_exit(subfs_exit); diff -urN linux-2.6.25.4.orig/fs/subfs.h linux-2.6.25.4/fs/subfs.h --- linux-2.6.25.4.orig/fs/subfs.h 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4/fs/subfs.h 2008-05-25 10:08:00 +0300 @@ -0,0 +1,76 @@ +/* + * subfs.h + * + * Copyright (C) 2003-2004 Eugene S. Weiss + * + * Distributed under the terms of the GNU General Public License version 2 + * or above. + */ + + +#define SUBFS_MAGIC 0x2c791058 + +#define SUBMOUNTD_PATH "/sbin/submountd" + +#define ERR(args...) \ + printk(KERN_ERR args) + +#define SUBFS_VER "0.9" + +#define ROOT_MODE 0777 + +struct subfs_mount { + char *device; + char *options; + char *req_fs; + char *helper_prog; + unsigned long flags; + struct super_block *sb; + struct vfsmount *mount; + struct semaphore sem; + int procuid; +}; + + +static void subfs_kill_super(struct super_block *sb); + +static int subfs_get_super(struct file_system_type *fst, + int flags, const char *devname, void *data, struct vfsmount *mnt); +static int subfs_statfs(struct dentry *dentry, struct kstatfs *buf); + +static struct vfsmount *get_subfs_vfsmount(struct super_block *sb); +static int subfs_fill_super(struct super_block *sb, void *data, + int silent); +static struct inode *subfs_make_inode(struct super_block *sb, int mode); +static int subfs_open(struct inode *inode, struct file *filp); +static struct dentry *subfs_lookup(struct inode *dir, + struct dentry *dentry, struct nameidata *nd); +static struct vfsmount *get_child_mount(struct subfs_mount *sfs_mnt); +static int mount_real_fs(struct subfs_mount *sfs_mnt); +static void subfs_send_signal(void); +static void subfs_set_fs_pwd(struct fs_struct *fs, struct vfsmount *mnt, + struct dentry *dentry); + + +static struct file_system_type subfs_type = { + .owner = THIS_MODULE, + .name = "subfs", + .get_sb = subfs_get_super, + .kill_sb = subfs_kill_super, +}; + + +static struct super_operations subfs_s_ops = { + .statfs = subfs_statfs, + .drop_inode = generic_delete_inode, +}; + + +static struct inode_operations subfs_dir_inode_operations = { + .lookup = subfs_lookup, +}; + + +static struct file_operations subfs_file_ops = { + .open = subfs_open, +};