diff options
Diffstat (limited to 'fs/fuse/control.c')
-rw-r--r-- | fs/fuse/control.c | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/fs/fuse/control.c b/fs/fuse/control.c new file mode 100644 index 000000000000..a3bce3a77253 --- /dev/null +++ b/fs/fuse/control.c | |||
@@ -0,0 +1,218 @@ | |||
1 | /* | ||
2 | FUSE: Filesystem in Userspace | ||
3 | Copyright (C) 2001-2006 Miklos Szeredi <miklos@szeredi.hu> | ||
4 | |||
5 | This program can be distributed under the terms of the GNU GPL. | ||
6 | See the file COPYING. | ||
7 | */ | ||
8 | |||
9 | #include "fuse_i.h" | ||
10 | |||
11 | #include <linux/init.h> | ||
12 | #include <linux/module.h> | ||
13 | |||
14 | #define FUSE_CTL_SUPER_MAGIC 0x65735543 | ||
15 | |||
16 | /* | ||
17 | * This is non-NULL when the single instance of the control filesystem | ||
18 | * exists. Protected by fuse_mutex | ||
19 | */ | ||
20 | static struct super_block *fuse_control_sb; | ||
21 | |||
22 | static struct fuse_conn *fuse_ctl_file_conn_get(struct file *file) | ||
23 | { | ||
24 | struct fuse_conn *fc; | ||
25 | mutex_lock(&fuse_mutex); | ||
26 | fc = file->f_dentry->d_inode->u.generic_ip; | ||
27 | if (fc) | ||
28 | fc = fuse_conn_get(fc); | ||
29 | mutex_unlock(&fuse_mutex); | ||
30 | return fc; | ||
31 | } | ||
32 | |||
33 | static ssize_t fuse_conn_abort_write(struct file *file, const char __user *buf, | ||
34 | size_t count, loff_t *ppos) | ||
35 | { | ||
36 | struct fuse_conn *fc = fuse_ctl_file_conn_get(file); | ||
37 | if (fc) { | ||
38 | fuse_abort_conn(fc); | ||
39 | fuse_conn_put(fc); | ||
40 | } | ||
41 | return count; | ||
42 | } | ||
43 | |||
44 | static ssize_t fuse_conn_waiting_read(struct file *file, char __user *buf, | ||
45 | size_t len, loff_t *ppos) | ||
46 | { | ||
47 | char tmp[32]; | ||
48 | size_t size; | ||
49 | |||
50 | if (!*ppos) { | ||
51 | struct fuse_conn *fc = fuse_ctl_file_conn_get(file); | ||
52 | if (!fc) | ||
53 | return 0; | ||
54 | |||
55 | file->private_data=(void *)(long)atomic_read(&fc->num_waiting); | ||
56 | fuse_conn_put(fc); | ||
57 | } | ||
58 | size = sprintf(tmp, "%ld\n", (long)file->private_data); | ||
59 | return simple_read_from_buffer(buf, len, ppos, tmp, size); | ||
60 | } | ||
61 | |||
62 | static const struct file_operations fuse_ctl_abort_ops = { | ||
63 | .open = nonseekable_open, | ||
64 | .write = fuse_conn_abort_write, | ||
65 | }; | ||
66 | |||
67 | static const struct file_operations fuse_ctl_waiting_ops = { | ||
68 | .open = nonseekable_open, | ||
69 | .read = fuse_conn_waiting_read, | ||
70 | }; | ||
71 | |||
72 | static struct dentry *fuse_ctl_add_dentry(struct dentry *parent, | ||
73 | struct fuse_conn *fc, | ||
74 | const char *name, | ||
75 | int mode, int nlink, | ||
76 | struct inode_operations *iop, | ||
77 | const struct file_operations *fop) | ||
78 | { | ||
79 | struct dentry *dentry; | ||
80 | struct inode *inode; | ||
81 | |||
82 | BUG_ON(fc->ctl_ndents >= FUSE_CTL_NUM_DENTRIES); | ||
83 | dentry = d_alloc_name(parent, name); | ||
84 | if (!dentry) | ||
85 | return NULL; | ||
86 | |||
87 | fc->ctl_dentry[fc->ctl_ndents++] = dentry; | ||
88 | inode = new_inode(fuse_control_sb); | ||
89 | if (!inode) | ||
90 | return NULL; | ||
91 | |||
92 | inode->i_mode = mode; | ||
93 | inode->i_uid = fc->user_id; | ||
94 | inode->i_gid = fc->group_id; | ||
95 | inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; | ||
96 | /* setting ->i_op to NULL is not allowed */ | ||
97 | if (iop) | ||
98 | inode->i_op = iop; | ||
99 | inode->i_fop = fop; | ||
100 | inode->i_nlink = nlink; | ||
101 | inode->u.generic_ip = fc; | ||
102 | d_add(dentry, inode); | ||
103 | return dentry; | ||
104 | } | ||
105 | |||
106 | /* | ||
107 | * Add a connection to the control filesystem (if it exists). Caller | ||
108 | * must host fuse_mutex | ||
109 | */ | ||
110 | int fuse_ctl_add_conn(struct fuse_conn *fc) | ||
111 | { | ||
112 | struct dentry *parent; | ||
113 | char name[32]; | ||
114 | |||
115 | if (!fuse_control_sb) | ||
116 | return 0; | ||
117 | |||
118 | parent = fuse_control_sb->s_root; | ||
119 | parent->d_inode->i_nlink++; | ||
120 | sprintf(name, "%llu", (unsigned long long) fc->id); | ||
121 | parent = fuse_ctl_add_dentry(parent, fc, name, S_IFDIR | 0500, 2, | ||
122 | &simple_dir_inode_operations, | ||
123 | &simple_dir_operations); | ||
124 | if (!parent) | ||
125 | goto err; | ||
126 | |||
127 | if (!fuse_ctl_add_dentry(parent, fc, "waiting", S_IFREG | 0400, 1, | ||
128 | NULL, &fuse_ctl_waiting_ops) || | ||
129 | !fuse_ctl_add_dentry(parent, fc, "abort", S_IFREG | 0200, 1, | ||
130 | NULL, &fuse_ctl_abort_ops)) | ||
131 | goto err; | ||
132 | |||
133 | return 0; | ||
134 | |||
135 | err: | ||
136 | fuse_ctl_remove_conn(fc); | ||
137 | return -ENOMEM; | ||
138 | } | ||
139 | |||
140 | /* | ||
141 | * Remove a connection from the control filesystem (if it exists). | ||
142 | * Caller must host fuse_mutex | ||
143 | */ | ||
144 | void fuse_ctl_remove_conn(struct fuse_conn *fc) | ||
145 | { | ||
146 | int i; | ||
147 | |||
148 | if (!fuse_control_sb) | ||
149 | return; | ||
150 | |||
151 | for (i = fc->ctl_ndents - 1; i >= 0; i--) { | ||
152 | struct dentry *dentry = fc->ctl_dentry[i]; | ||
153 | dentry->d_inode->u.generic_ip = NULL; | ||
154 | d_drop(dentry); | ||
155 | dput(dentry); | ||
156 | } | ||
157 | fuse_control_sb->s_root->d_inode->i_nlink--; | ||
158 | } | ||
159 | |||
160 | static int fuse_ctl_fill_super(struct super_block *sb, void *data, int silent) | ||
161 | { | ||
162 | struct tree_descr empty_descr = {""}; | ||
163 | struct fuse_conn *fc; | ||
164 | int err; | ||
165 | |||
166 | err = simple_fill_super(sb, FUSE_CTL_SUPER_MAGIC, &empty_descr); | ||
167 | if (err) | ||
168 | return err; | ||
169 | |||
170 | mutex_lock(&fuse_mutex); | ||
171 | BUG_ON(fuse_control_sb); | ||
172 | fuse_control_sb = sb; | ||
173 | list_for_each_entry(fc, &fuse_conn_list, entry) { | ||
174 | err = fuse_ctl_add_conn(fc); | ||
175 | if (err) { | ||
176 | fuse_control_sb = NULL; | ||
177 | mutex_unlock(&fuse_mutex); | ||
178 | return err; | ||
179 | } | ||
180 | } | ||
181 | mutex_unlock(&fuse_mutex); | ||
182 | |||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static int fuse_ctl_get_sb(struct file_system_type *fs_type, int flags, | ||
187 | const char *dev_name, void *raw_data, | ||
188 | struct vfsmount *mnt) | ||
189 | { | ||
190 | return get_sb_single(fs_type, flags, raw_data, | ||
191 | fuse_ctl_fill_super, mnt); | ||
192 | } | ||
193 | |||
194 | static void fuse_ctl_kill_sb(struct super_block *sb) | ||
195 | { | ||
196 | mutex_lock(&fuse_mutex); | ||
197 | fuse_control_sb = NULL; | ||
198 | mutex_unlock(&fuse_mutex); | ||
199 | |||
200 | kill_litter_super(sb); | ||
201 | } | ||
202 | |||
203 | static struct file_system_type fuse_ctl_fs_type = { | ||
204 | .owner = THIS_MODULE, | ||
205 | .name = "fusectl", | ||
206 | .get_sb = fuse_ctl_get_sb, | ||
207 | .kill_sb = fuse_ctl_kill_sb, | ||
208 | }; | ||
209 | |||
210 | int __init fuse_ctl_init(void) | ||
211 | { | ||
212 | return register_filesystem(&fuse_ctl_fs_type); | ||
213 | } | ||
214 | |||
215 | void fuse_ctl_cleanup(void) | ||
216 | { | ||
217 | unregister_filesystem(&fuse_ctl_fs_type); | ||
218 | } | ||