From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on sa.int.altlinux.org X-Spam-Level: X-Spam-Status: No, score=-1.5 required=5.0 tests=BAYES_00,DNS_FROM_OPENWHOIS autolearn=no version=3.2.5 Date: Sat, 3 Sep 2011 13:41:55 +0300 From: Michael Shigorin To: ALT Linux Sisyphus discussions Message-ID: <20110903104155.GO4289@osdn.org.ua> Mail-Followup-To: ALT Linux Sisyphus discussions References: <20110902203832.GM4289@osdn.org.ua> <4E61FE9B.5090802@complife.ru> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="p8PhoBjPxaQXD0vg" Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <4E61FE9B.5090802@complife.ru> User-Agent: Mutt/1.4.2.1i Subject: Re: [sisyphus] =?koi8-r?b?c3RkLWRlZiwgdW4tZGVmIMkgaGliZXJuYXRl?= X-BeenThere: sisyphus@lists.altlinux.org X-Mailman-Version: 2.1.12 Precedence: list Reply-To: shigorin@gmail.com, ALT Linux Sisyphus discussions List-Id: ALT Linux Sisyphus discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 03 Sep 2011 10:42:02 -0000 Archived-At: List-Archive: List-Post: --p8PhoBjPxaQXD0vg Content-Type: text/plain; charset=koi8-r Content-Disposition: inline Content-Transfer-Encoding: 8bit On Sat, Sep 03, 2011 at 02:16:59PM +0400, Michael A. Kangin wrote: > >Поскольку led@ прикрутил subfs к ядрам>=2.6.39, > Good news everyone! А для P6 будет? Надо попробовать > скрестить его с udisks и закопать уже hal-mount-subfs. Бери: http://fly.osdn.org.ua/~mike/kernel/subfs-0.9.2/ (и на всякий аттачу) -- кто первый успеет, там и будет :) Собирается с 2.6.37+; в 2.6.39 выкинули старый интерфейс, который использовался subfs ранее. Там у него ещё юзерспейс свой с graceful fallback'ом по опциям, неподходящим для конкретной ФС. -- ---- WBR, Michael Shigorin ------ Linux.Kiev http://www.linux.kiev.ua/ --p8PhoBjPxaQXD0vg Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="subfs.c" /* * subfs.c * * Copyright (C) 2003-2004 Eugene S. Weiss * Copyright (C) 2011 Led * * 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 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) #include #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) #include #else #include #endif #ifndef DEFINE_MUTEX #define i_mutex i_sem #define mutex_lock(x) down(x) #define mutex_unlock(x) up(x) #endif #ifndef pr_err #define pr_err(args...) printk(KERN_ERR args) #endif #ifndef current_uid #define current_uid() current->uid #endif #ifndef current_gid #define current_gid() current->gid #endif MODULE_LICENSE("GPL"); MODULE_AUTHOR("Eugene S. Weiss, Led"); #define SUBFS_VER "0.9.2" #define SUBFS_MAGIC 0x2c791058 #define SUBMOUNTD_PATH "/sbin/submountd" #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 inline struct vfsmount *get_root_mnt(struct fs_struct *fs) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) return fs->root.mnt; #else return fs->rootmnt; #endif } /* 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 list_head *entry, *lh; /* Get the head of the global mount list from the init process. */ /* Is there a better way? */ struct list_head *head = &(get_root_mnt(init_task.fs))->mnt_list; /* Go through the list and look for a superblock pointer match. */ list_for_each_safe(entry, lh, head) { struct vfsmount *mnt = list_entry(entry, struct vfsmount, mnt_list); if (mnt->mnt_sb == sb) return mnt; } pr_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) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) struct path path = {.mnt = mnt, .dentry = dentry}; #if defined(MODULE) && !defined(HAVE_SET_FS_PWD) struct path old_pwd; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) spin_lock(&fs->lock); #else write_lock(&fs->lock); #endif old_pwd = fs->pwd; fs->pwd = path; path_get(&path); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) spin_unlock(&fs->lock); #else write_unlock(&fs->lock); #endif if (old_pwd.dentry) path_put(&old_pwd); #else set_fs_pwd(current->fs, &path); #endif #else #if defined(MODULE) && !defined(HAVE_SET_FS_PWD) struct dentry *old_pwd; struct vfsmount *old_pwdmnt; write_lock(&fs->lock); old_pwd = fs->pwd; old_pwdmnt = fs->pwdmnt; fs->pwdmnt = mntget(mnt); fs->pwd = dget(dentry); write_unlock(&fs->lock); if (old_pwd) { dput(old_pwd); mntput(old_pwdmnt); } #else set_fs_pwd(fs, mnt, dentry); #endif #endif } /* 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; rcu_read_lock(); spin_lock_irq(&task->sighand->siglock); sigaddset(&task->pending.signal, SIGCONT); spin_unlock_irq(&task->sighand->siglock); rcu_read_unlock(); set_tsk_thread_flag(task, TIF_SIGPENDING); } /* 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) { if (sfs_mnt->procuid) { uid_t uid = current_uid(); gid_t gid = current_gid(); char *o = kmalloc(strlen(sfs_mnt->options) + 1 + 32 + 1, GFP_KERNEL); if (sfs_mnt->options[0] == '\0') sprintf(o, "uid=%d,gid=%d", uid, gid); else sprintf(o, "%s,uid=%d,gid=%d", sfs_mnt->options, uid, gid); kfree(sfs_mnt->options); sfs_mnt->options = o; } } static char *subfs_d_path(struct vfsmount *mount, char *buf, int size) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) struct path path; path.mnt = mount; path.dentry = path.mnt->mnt_root; return d_path(&path, buf, size); #else return d_path(mount->mnt_mountpoint, mount->mnt_parent, buf, size); #endif } /* 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) { int result = -ENOMEM; char *path_buf = (char *)__get_free_page(GFP_KERNEL); if (path_buf) { char *argv[7]; argv[4] = kmalloc(17, GFP_KERNEL); if (argv[4]) { char *envp[2] = {"HOME=/", NULL}; argv[0] = sfs_mnt->helper_prog; argv[1] = sfs_mnt->device; argv[2] = subfs_d_path(sfs_mnt->mount, path_buf, PAGE_SIZE); argv[3] = sfs_mnt->req_fs; sprintf(argv[4], "%lx", sfs_mnt->flags); add_procuid(sfs_mnt); argv[5] = sfs_mnt->options; argv[6] = NULL; result = call_usermodehelper(sfs_mnt->helper_prog, argv, envp, 1); kfree(argv[4]); if (sfs_mnt->procuid) sfs_mnt->options[strlen(sfs_mnt->options)] = '\0'; } free_page((unsigned long)path_buf); /* 64 bits on some platforms */ } 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) { /* First time: find the vfsmount structure that matches sfs_mnt. */ if (!sfs_mnt->mount) { sfs_mnt->mount = get_subfs_vfsmount(sfs_mnt->sb); if(!sfs_mnt->mount) 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) { int result = mount_real_fs(sfs_mnt); if (result) { pr_err("subfs: " SUBMOUNTD_PATH " unsuccessful attempt to mount media (%d)\n", result); /* Workaround for call_usermodehelper return value bug. */ return ERR_PTR((result < 0) ? result : -ENOMEDIUM); } if (&sfs_mnt->mount->mnt_mounts == sfs_mnt->mount->mnt_mounts.next) { pr_err("subfs: submountd mount failure.\n"); return ERR_PTR(-ENOMEDIUM); } } return list_entry(sfs_mnt->mount->mnt_mounts.next, struct vfsmount, mnt_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; /* This is ugly, but prevents a lockup during mount. */ mutex_unlock(&dir->i_mutex); if (down_interruptible(&sfs_mnt->sem)) /*put the dir sem back down if interrupted*/ mutex_lock(&dir->i_mutex); else { struct vfsmount *child = get_child_mount(sfs_mnt); up(&sfs_mnt->sem); mutex_lock(&dir->i_mutex); if (IS_ERR(child)) return (struct dentry*)child; subfs_send_signal(); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) if (sfs_mnt->mount == current->fs->pwd.mnt) #else if (sfs_mnt->mount == current->fs->pwdmnt) #endif 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; if (!down_interruptible(&sfs_mnt->sem)) { struct vfsmount *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; } static struct file_operations subfs_file_ops = { .open = subfs_open, }; /* Implements the statfs method so df and such will work on the mountpoint. */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17) static int subfs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct subfs_mount *sfs_mnt = dentry->d_sb->s_fs_info; #else static int subfs_statfs(struct super_block *sb, struct kstatfs *buf) { struct subfs_mount *sfs_mnt = sb->s_fs_info; #endif if (!down_interruptible(&sfs_mnt->sem)) { struct vfsmount *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 = 0; ret->i_gid = 0; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ret->i_blksize = PAGE_CACHE_SIZE; #endif ret->i_blocks = 0; ret->i_atime = CURRENT_TIME; ret->i_mtime = CURRENT_TIME; ret->i_ctime = CURRENT_TIME; ret->i_fop = &subfs_file_ops; } return ret; } 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, }; /* 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; 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) { struct dentry *root_dentry; root->i_op = &subfs_dir_inode_operations; root_dentry = d_alloc_root(root); if (root_dentry) { sb->s_root = root_dentry; return 0; } iput(root); } 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 *opt; int len; if (!data) { data = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!data) return -EINVAL; strcpy(data, "fs=auto"); } len = strnlen(data, PAGE_SIZE - 1) + 1; if (strstr(data, "procuid")) sfs_mnt->procuid = 1; sfs_mnt->options = kmalloc(len, GFP_KERNEL); if (!sfs_mnt->options) return -ENOMEM; sfs_mnt->options[0] = '\0'; while ((opt = strsep((char**)(&data), ","))) { char *ptr = strstr(opt, "program="); if (ptr) { kfree(sfs_mnt->helper_prog); sfs_mnt->helper_prog = kstrdup(ptr + 8, GFP_KERNEL); if (!sfs_mnt->helper_prog) return -ENOMEM; } else { ptr = strstr(opt, "fs="); if (ptr) { sfs_mnt->req_fs = kstrdup(ptr + 3, GFP_KERNEL);; if (!sfs_mnt->req_fs) return -ENOMEM; } else { strncat(sfs_mnt->options, opt, 32); strcat(sfs_mnt->options, ","); } } } len = strlen(sfs_mnt->options) - 1; if (len >= 0 && sfs_mnt->options[len] == ',') sfs_mnt->options[len] = '\0'; if (!sfs_mnt->req_fs) { sfs_mnt->req_fs = kstrdup("auto", GFP_KERNEL); if (!sfs_mnt->req_fs) return -ENOMEM; /* 64 bits on some platforms */ } 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. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) static struct dentry *subfs_mount(struct file_system_type *fst, int flags, const char *devname, void *data) { int ret = -ENOMEM; struct subfs_mount *newmount = kzalloc(sizeof(struct subfs_mount), GFP_KERNEL); if (newmount) { newmount->device = kstrdup(devname, GFP_KERNEL); if (!newmount->device) kfree(newmount); else { sema_init(&newmount->sem, 1); newmount->helper_prog = kstrdup(SUBMOUNTD_PATH, GFP_KERNEL); if (newmount->helper_prog) { ret = proc_opts(newmount, data); if (!ret) { struct dentry *root = mount_nodev(fst, flags, data, subfs_fill_super); if (!IS_ERR(root)) { newmount->sb = root->d_sb; newmount->sb->s_fs_info = newmount; } return root; } } } } return ERR_PTR(ret); } #elif LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17) static int subfs_get_super(struct file_system_type *fst, int flags, const char *devname, void *data, struct vfsmount *mnt) { int ret = -ENOMEM; struct subfs_mount *newmount = kzalloc(sizeof(struct subfs_mount), GFP_KERNEL); if (newmount) { newmount->device = kstrdup(devname, GFP_KERNEL); if (!newmount->device) kfree(newmount); else { sema_init(&newmount->sem, 1); newmount->helper_prog = kstrdup(SUBMOUNTD_PATH, GFP_KERNEL); if (newmount->helper_prog) { ret = proc_opts(newmount, data); if (!ret) { ret = get_sb_nodev(fst, flags, data, subfs_fill_super, mnt); if (!ret) { newmount->sb = mnt->mnt_sb; newmount->sb->s_fs_info = newmount; } } } } } return ret; } #else static struct super_block *subfs_get_super(struct file_system_type *fst, int flags, const char *devname, void *data) { int ret = -ENOMEM; struct subfs_mount *newmount = kzalloc(sizeof(struct subfs_mount), GFP_KERNEL); if (newmount) { newmount->device = kstrdup(devname, GFP_KERNEL); if (!newmount->device) kfree(newmount); else { sema_init(&newmount->sem, 1); newmount->helper_prog = kstrdup(SUBMOUNTD_PATH, GFP_KERNEL); if (newmount->helper_prog) { ret = proc_opts(newmount, data); if (!ret) { newmount->sb = get_sb_nodev(fst, flags, data, subfs_fill_super); if (!IS_ERR(newmount->sb)) newmount->sb->s_fs_info = newmount; return newmount->sb; } } } } return ERR_PTR(ret) } #endif static void subfs_kfree(void *p) { if (p) kfree(p); } /* 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) { subfs_kfree(sfs_mnt->device); subfs_kfree(sfs_mnt->options); subfs_kfree(sfs_mnt->req_fs); subfs_kfree(sfs_mnt->helper_prog); kfree(sfs_mnt); sb->s_fs_info = NULL; } kill_litter_super(sb); } static struct file_system_type subfs_type = { .owner = THIS_MODULE, .name = "subfs", #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) .mount = subfs_mount, #else .get_sb = subfs_get_super, #endif .kill_sb = subfs_kill_super, }; static int __init subfs_init(void) { printk(KERN_INFO "subfs " SUBFS_VER "\n"); 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); --p8PhoBjPxaQXD0vg Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=Makefile EXTRA_CFLAGS := -Os obj-m := subfs.o --p8PhoBjPxaQXD0vg--