[sisyphus] std-def, un-def и hibernate
Michael Shigorin
mike на osdn.org.ua
Сб Сен 3 10:41:55 UTC 2011
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 <mike на altlinux.ru>
------ Linux.Kiev http://www.linux.kiev.ua/
----------- следующая часть -----------
/*
* subfs.c
*
* Copyright (C) 2003-2004 Eugene S. Weiss <eweiss на sbclobal.net>
* Copyright (C) 2011 Led <led на massivesolutions.co.uk>
*
* Distributed under the terms of the GNU General Public License version 2
* or above.
*/
#include <linux/module.h>
#include <linux/pagemap.h>
#include <linux/fs.h>
#include <linux/mount.h>
#include <linux/fs_struct.h>
#include <linux/spinlock_types.h>
#include <linux/namei.h>
#include <linux/dcache.h>
#include <linux/sysfs.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
#include <linux/nsproxy.h>
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
#include <linux/mnt_namespace.h>
#else
#include <linux/namespace.h>
#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);
----------- следующая часть -----------
EXTRA_CFLAGS := -Os
obj-m := subfs.o
Подробная информация о списке рассылки Sisyphus