forked from analogdevicesinc/linux
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[PATCH] fuse: add control filesystem
Add a control filesystem to fuse, replacing the attributes currently exported through sysfs. An empty directory '/sys/fs/fuse/connections' is still created in sysfs, and mounting the control filesystem here provides backward compatibility. Advantages of the control filesystem over the previous solution: - allows the object directory and the attributes to be owned by the filesystem owner, hence letting unpriviled users abort the filesystem connection - does not suffer from module unload race [[email protected]: fix this fs for recent dhowells depredations] [[email protected]: fix 64-bit printk warnings] Signed-off-by: Miklos Szeredi <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
- Loading branch information
Showing
6 changed files
with
331 additions
and
115 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
/* | ||
FUSE: Filesystem in Userspace | ||
Copyright (C) 2001-2006 Miklos Szeredi <[email protected]> | ||
This program can be distributed under the terms of the GNU GPL. | ||
See the file COPYING. | ||
*/ | ||
|
||
#include "fuse_i.h" | ||
|
||
#include <linux/init.h> | ||
#include <linux/module.h> | ||
|
||
#define FUSE_CTL_SUPER_MAGIC 0x65735543 | ||
|
||
/* | ||
* This is non-NULL when the single instance of the control filesystem | ||
* exists. Protected by fuse_mutex | ||
*/ | ||
static struct super_block *fuse_control_sb; | ||
|
||
static struct fuse_conn *fuse_ctl_file_conn_get(struct file *file) | ||
{ | ||
struct fuse_conn *fc; | ||
mutex_lock(&fuse_mutex); | ||
fc = file->f_dentry->d_inode->u.generic_ip; | ||
if (fc) | ||
fc = fuse_conn_get(fc); | ||
mutex_unlock(&fuse_mutex); | ||
return fc; | ||
} | ||
|
||
static ssize_t fuse_conn_abort_write(struct file *file, const char __user *buf, | ||
size_t count, loff_t *ppos) | ||
{ | ||
struct fuse_conn *fc = fuse_ctl_file_conn_get(file); | ||
if (fc) { | ||
fuse_abort_conn(fc); | ||
fuse_conn_put(fc); | ||
} | ||
return count; | ||
} | ||
|
||
static ssize_t fuse_conn_waiting_read(struct file *file, char __user *buf, | ||
size_t len, loff_t *ppos) | ||
{ | ||
char tmp[32]; | ||
size_t size; | ||
|
||
if (!*ppos) { | ||
struct fuse_conn *fc = fuse_ctl_file_conn_get(file); | ||
if (!fc) | ||
return 0; | ||
|
||
file->private_data=(void *)(long)atomic_read(&fc->num_waiting); | ||
fuse_conn_put(fc); | ||
} | ||
size = sprintf(tmp, "%ld\n", (long)file->private_data); | ||
return simple_read_from_buffer(buf, len, ppos, tmp, size); | ||
} | ||
|
||
static const struct file_operations fuse_ctl_abort_ops = { | ||
.open = nonseekable_open, | ||
.write = fuse_conn_abort_write, | ||
}; | ||
|
||
static const struct file_operations fuse_ctl_waiting_ops = { | ||
.open = nonseekable_open, | ||
.read = fuse_conn_waiting_read, | ||
}; | ||
|
||
static struct dentry *fuse_ctl_add_dentry(struct dentry *parent, | ||
struct fuse_conn *fc, | ||
const char *name, | ||
int mode, int nlink, | ||
struct inode_operations *iop, | ||
const struct file_operations *fop) | ||
{ | ||
struct dentry *dentry; | ||
struct inode *inode; | ||
|
||
BUG_ON(fc->ctl_ndents >= FUSE_CTL_NUM_DENTRIES); | ||
dentry = d_alloc_name(parent, name); | ||
if (!dentry) | ||
return NULL; | ||
|
||
fc->ctl_dentry[fc->ctl_ndents++] = dentry; | ||
inode = new_inode(fuse_control_sb); | ||
if (!inode) | ||
return NULL; | ||
|
||
inode->i_mode = mode; | ||
inode->i_uid = fc->user_id; | ||
inode->i_gid = fc->group_id; | ||
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; | ||
/* setting ->i_op to NULL is not allowed */ | ||
if (iop) | ||
inode->i_op = iop; | ||
inode->i_fop = fop; | ||
inode->i_nlink = nlink; | ||
inode->u.generic_ip = fc; | ||
d_add(dentry, inode); | ||
return dentry; | ||
} | ||
|
||
/* | ||
* Add a connection to the control filesystem (if it exists). Caller | ||
* must host fuse_mutex | ||
*/ | ||
int fuse_ctl_add_conn(struct fuse_conn *fc) | ||
{ | ||
struct dentry *parent; | ||
char name[32]; | ||
|
||
if (!fuse_control_sb) | ||
return 0; | ||
|
||
parent = fuse_control_sb->s_root; | ||
parent->d_inode->i_nlink++; | ||
sprintf(name, "%llu", (unsigned long long) fc->id); | ||
parent = fuse_ctl_add_dentry(parent, fc, name, S_IFDIR | 0500, 2, | ||
&simple_dir_inode_operations, | ||
&simple_dir_operations); | ||
if (!parent) | ||
goto err; | ||
|
||
if (!fuse_ctl_add_dentry(parent, fc, "waiting", S_IFREG | 0400, 1, | ||
NULL, &fuse_ctl_waiting_ops) || | ||
!fuse_ctl_add_dentry(parent, fc, "abort", S_IFREG | 0200, 1, | ||
NULL, &fuse_ctl_abort_ops)) | ||
goto err; | ||
|
||
return 0; | ||
|
||
err: | ||
fuse_ctl_remove_conn(fc); | ||
return -ENOMEM; | ||
} | ||
|
||
/* | ||
* Remove a connection from the control filesystem (if it exists). | ||
* Caller must host fuse_mutex | ||
*/ | ||
void fuse_ctl_remove_conn(struct fuse_conn *fc) | ||
{ | ||
int i; | ||
|
||
if (!fuse_control_sb) | ||
return; | ||
|
||
for (i = fc->ctl_ndents - 1; i >= 0; i--) { | ||
struct dentry *dentry = fc->ctl_dentry[i]; | ||
dentry->d_inode->u.generic_ip = NULL; | ||
d_drop(dentry); | ||
dput(dentry); | ||
} | ||
fuse_control_sb->s_root->d_inode->i_nlink--; | ||
} | ||
|
||
static int fuse_ctl_fill_super(struct super_block *sb, void *data, int silent) | ||
{ | ||
struct tree_descr empty_descr = {""}; | ||
struct fuse_conn *fc; | ||
int err; | ||
|
||
err = simple_fill_super(sb, FUSE_CTL_SUPER_MAGIC, &empty_descr); | ||
if (err) | ||
return err; | ||
|
||
mutex_lock(&fuse_mutex); | ||
BUG_ON(fuse_control_sb); | ||
fuse_control_sb = sb; | ||
list_for_each_entry(fc, &fuse_conn_list, entry) { | ||
err = fuse_ctl_add_conn(fc); | ||
if (err) { | ||
fuse_control_sb = NULL; | ||
mutex_unlock(&fuse_mutex); | ||
return err; | ||
} | ||
} | ||
mutex_unlock(&fuse_mutex); | ||
|
||
return 0; | ||
} | ||
|
||
static int fuse_ctl_get_sb(struct file_system_type *fs_type, int flags, | ||
const char *dev_name, void *raw_data, | ||
struct vfsmount *mnt) | ||
{ | ||
return get_sb_single(fs_type, flags, raw_data, | ||
fuse_ctl_fill_super, mnt); | ||
} | ||
|
||
static void fuse_ctl_kill_sb(struct super_block *sb) | ||
{ | ||
mutex_lock(&fuse_mutex); | ||
fuse_control_sb = NULL; | ||
mutex_unlock(&fuse_mutex); | ||
|
||
kill_litter_super(sb); | ||
} | ||
|
||
static struct file_system_type fuse_ctl_fs_type = { | ||
.owner = THIS_MODULE, | ||
.name = "fusectl", | ||
.get_sb = fuse_ctl_get_sb, | ||
.kill_sb = fuse_ctl_kill_sb, | ||
}; | ||
|
||
int __init fuse_ctl_init(void) | ||
{ | ||
return register_filesystem(&fuse_ctl_fs_type); | ||
} | ||
|
||
void fuse_ctl_cleanup(void) | ||
{ | ||
unregister_filesystem(&fuse_ctl_fs_type); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.