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,
+};