]>
Commit | Line | Data |
---|---|---|
e5e5558e MS |
1 | /* |
2 | FUSE: Filesystem in Userspace | |
3 | Copyright (C) 2001-2005 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/pagemap.h> | |
12 | #include <linux/file.h> | |
13 | #include <linux/gfp.h> | |
14 | #include <linux/sched.h> | |
15 | #include <linux/namei.h> | |
16 | ||
17 | static inline unsigned long time_to_jiffies(unsigned long sec, | |
18 | unsigned long nsec) | |
19 | { | |
20 | struct timespec ts = {sec, nsec}; | |
21 | return jiffies + timespec_to_jiffies(&ts); | |
22 | } | |
23 | ||
24 | static void fuse_lookup_init(struct fuse_req *req, struct inode *dir, | |
25 | struct dentry *entry, | |
26 | struct fuse_entry_out *outarg) | |
27 | { | |
28 | req->in.h.opcode = FUSE_LOOKUP; | |
29 | req->in.h.nodeid = get_node_id(dir); | |
30 | req->inode = dir; | |
31 | req->in.numargs = 1; | |
32 | req->in.args[0].size = entry->d_name.len + 1; | |
33 | req->in.args[0].value = entry->d_name.name; | |
34 | req->out.numargs = 1; | |
35 | req->out.args[0].size = sizeof(struct fuse_entry_out); | |
36 | req->out.args[0].value = outarg; | |
37 | } | |
38 | ||
39 | static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) | |
40 | { | |
41 | if (!entry->d_inode || is_bad_inode(entry->d_inode)) | |
42 | return 0; | |
43 | else if (time_after(jiffies, entry->d_time)) { | |
44 | int err; | |
45 | int version; | |
46 | struct fuse_entry_out outarg; | |
47 | struct inode *inode = entry->d_inode; | |
48 | struct fuse_inode *fi = get_fuse_inode(inode); | |
49 | struct fuse_conn *fc = get_fuse_conn(inode); | |
50 | struct fuse_req *req = fuse_get_request_nonint(fc); | |
51 | if (!req) | |
52 | return 0; | |
53 | ||
54 | fuse_lookup_init(req, entry->d_parent->d_inode, entry, &outarg); | |
55 | request_send_nonint(fc, req); | |
56 | version = req->out.h.unique; | |
57 | err = req->out.h.error; | |
58 | fuse_put_request(fc, req); | |
59 | if (err || outarg.nodeid != get_node_id(inode) || | |
60 | (outarg.attr.mode ^ inode->i_mode) & S_IFMT) | |
61 | return 0; | |
62 | ||
63 | fuse_change_attributes(inode, &outarg.attr); | |
64 | inode->i_version = version; | |
65 | entry->d_time = time_to_jiffies(outarg.entry_valid, | |
66 | outarg.entry_valid_nsec); | |
67 | fi->i_time = time_to_jiffies(outarg.attr_valid, | |
68 | outarg.attr_valid_nsec); | |
69 | } | |
70 | return 1; | |
71 | } | |
72 | ||
73 | static struct dentry_operations fuse_dentry_operations = { | |
74 | .d_revalidate = fuse_dentry_revalidate, | |
75 | }; | |
76 | ||
77 | static int fuse_lookup_iget(struct inode *dir, struct dentry *entry, | |
78 | struct inode **inodep) | |
79 | { | |
80 | int err; | |
81 | int version; | |
82 | struct fuse_entry_out outarg; | |
83 | struct inode *inode = NULL; | |
84 | struct fuse_conn *fc = get_fuse_conn(dir); | |
85 | struct fuse_req *req; | |
86 | ||
87 | if (entry->d_name.len > FUSE_NAME_MAX) | |
88 | return -ENAMETOOLONG; | |
89 | ||
90 | req = fuse_get_request(fc); | |
91 | if (!req) | |
92 | return -ERESTARTNOINTR; | |
93 | ||
94 | fuse_lookup_init(req, dir, entry, &outarg); | |
95 | request_send(fc, req); | |
96 | version = req->out.h.unique; | |
97 | err = req->out.h.error; | |
98 | if (!err) { | |
99 | inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, | |
100 | &outarg.attr, version); | |
101 | if (!inode) { | |
102 | fuse_send_forget(fc, req, outarg.nodeid, version); | |
103 | return -ENOMEM; | |
104 | } | |
105 | } | |
106 | fuse_put_request(fc, req); | |
107 | if (err && err != -ENOENT) | |
108 | return err; | |
109 | ||
110 | if (inode) { | |
111 | struct fuse_inode *fi = get_fuse_inode(inode); | |
112 | entry->d_time = time_to_jiffies(outarg.entry_valid, | |
113 | outarg.entry_valid_nsec); | |
114 | fi->i_time = time_to_jiffies(outarg.attr_valid, | |
115 | outarg.attr_valid_nsec); | |
116 | } | |
117 | ||
118 | entry->d_op = &fuse_dentry_operations; | |
119 | *inodep = inode; | |
120 | return 0; | |
121 | } | |
122 | ||
123 | int fuse_do_getattr(struct inode *inode) | |
124 | { | |
125 | int err; | |
126 | struct fuse_attr_out arg; | |
127 | struct fuse_conn *fc = get_fuse_conn(inode); | |
128 | struct fuse_req *req = fuse_get_request(fc); | |
129 | if (!req) | |
130 | return -ERESTARTNOINTR; | |
131 | ||
132 | req->in.h.opcode = FUSE_GETATTR; | |
133 | req->in.h.nodeid = get_node_id(inode); | |
134 | req->inode = inode; | |
135 | req->out.numargs = 1; | |
136 | req->out.args[0].size = sizeof(arg); | |
137 | req->out.args[0].value = &arg; | |
138 | request_send(fc, req); | |
139 | err = req->out.h.error; | |
140 | fuse_put_request(fc, req); | |
141 | if (!err) { | |
142 | if ((inode->i_mode ^ arg.attr.mode) & S_IFMT) { | |
143 | make_bad_inode(inode); | |
144 | err = -EIO; | |
145 | } else { | |
146 | struct fuse_inode *fi = get_fuse_inode(inode); | |
147 | fuse_change_attributes(inode, &arg.attr); | |
148 | fi->i_time = time_to_jiffies(arg.attr_valid, | |
149 | arg.attr_valid_nsec); | |
150 | } | |
151 | } | |
152 | return err; | |
153 | } | |
154 | ||
155 | static int fuse_revalidate(struct dentry *entry) | |
156 | { | |
157 | struct inode *inode = entry->d_inode; | |
158 | struct fuse_inode *fi = get_fuse_inode(inode); | |
159 | struct fuse_conn *fc = get_fuse_conn(inode); | |
160 | ||
161 | if (get_node_id(inode) == FUSE_ROOT_ID) { | |
162 | if (current->fsuid != fc->user_id) | |
163 | return -EACCES; | |
164 | } else if (time_before_eq(jiffies, fi->i_time)) | |
165 | return 0; | |
166 | ||
167 | return fuse_do_getattr(inode); | |
168 | } | |
169 | ||
170 | static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) | |
171 | { | |
172 | struct fuse_conn *fc = get_fuse_conn(inode); | |
173 | ||
174 | if (current->fsuid != fc->user_id) | |
175 | return -EACCES; | |
176 | else { | |
177 | int mode = inode->i_mode; | |
178 | if ((mask & MAY_WRITE) && IS_RDONLY(inode) && | |
179 | (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) | |
180 | return -EROFS; | |
181 | if ((mask & MAY_EXEC) && !S_ISDIR(mode) && !(mode & S_IXUGO)) | |
182 | return -EACCES; | |
183 | return 0; | |
184 | } | |
185 | } | |
186 | ||
187 | static int parse_dirfile(char *buf, size_t nbytes, struct file *file, | |
188 | void *dstbuf, filldir_t filldir) | |
189 | { | |
190 | while (nbytes >= FUSE_NAME_OFFSET) { | |
191 | struct fuse_dirent *dirent = (struct fuse_dirent *) buf; | |
192 | size_t reclen = FUSE_DIRENT_SIZE(dirent); | |
193 | int over; | |
194 | if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX) | |
195 | return -EIO; | |
196 | if (reclen > nbytes) | |
197 | break; | |
198 | ||
199 | over = filldir(dstbuf, dirent->name, dirent->namelen, | |
200 | file->f_pos, dirent->ino, dirent->type); | |
201 | if (over) | |
202 | break; | |
203 | ||
204 | buf += reclen; | |
205 | nbytes -= reclen; | |
206 | file->f_pos = dirent->off; | |
207 | } | |
208 | ||
209 | return 0; | |
210 | } | |
211 | ||
212 | static int fuse_checkdir(struct file *cfile, struct file *file) | |
213 | { | |
214 | struct inode *inode; | |
215 | if (!cfile) | |
216 | return -EIO; | |
217 | inode = cfile->f_dentry->d_inode; | |
218 | if (!S_ISREG(inode->i_mode)) { | |
219 | fput(cfile); | |
220 | return -EIO; | |
221 | } | |
222 | ||
223 | file->private_data = cfile; | |
224 | return 0; | |
225 | } | |
226 | ||
227 | static int fuse_getdir(struct file *file) | |
228 | { | |
229 | struct inode *inode = file->f_dentry->d_inode; | |
230 | struct fuse_conn *fc = get_fuse_conn(inode); | |
231 | struct fuse_req *req = fuse_get_request(fc); | |
232 | struct fuse_getdir_out_i outarg; | |
233 | int err; | |
234 | ||
235 | if (!req) | |
236 | return -ERESTARTNOINTR; | |
237 | ||
238 | req->in.h.opcode = FUSE_GETDIR; | |
239 | req->in.h.nodeid = get_node_id(inode); | |
240 | req->inode = inode; | |
241 | req->out.numargs = 1; | |
242 | req->out.args[0].size = sizeof(struct fuse_getdir_out); | |
243 | req->out.args[0].value = &outarg; | |
244 | request_send(fc, req); | |
245 | err = req->out.h.error; | |
246 | fuse_put_request(fc, req); | |
247 | if (!err) | |
248 | err = fuse_checkdir(outarg.file, file); | |
249 | return err; | |
250 | } | |
251 | ||
252 | static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) | |
253 | { | |
254 | struct file *cfile = file->private_data; | |
255 | char *buf; | |
256 | int ret; | |
257 | ||
258 | if (!cfile) { | |
259 | ret = fuse_getdir(file); | |
260 | if (ret) | |
261 | return ret; | |
262 | ||
263 | cfile = file->private_data; | |
264 | } | |
265 | ||
266 | buf = (char *) __get_free_page(GFP_KERNEL); | |
267 | if (!buf) | |
268 | return -ENOMEM; | |
269 | ||
270 | ret = kernel_read(cfile, file->f_pos, buf, PAGE_SIZE); | |
271 | if (ret > 0) | |
272 | ret = parse_dirfile(buf, ret, file, dstbuf, filldir); | |
273 | ||
274 | free_page((unsigned long) buf); | |
275 | return ret; | |
276 | } | |
277 | ||
278 | static char *read_link(struct dentry *dentry) | |
279 | { | |
280 | struct inode *inode = dentry->d_inode; | |
281 | struct fuse_conn *fc = get_fuse_conn(inode); | |
282 | struct fuse_req *req = fuse_get_request(fc); | |
283 | char *link; | |
284 | ||
285 | if (!req) | |
286 | return ERR_PTR(-ERESTARTNOINTR); | |
287 | ||
288 | link = (char *) __get_free_page(GFP_KERNEL); | |
289 | if (!link) { | |
290 | link = ERR_PTR(-ENOMEM); | |
291 | goto out; | |
292 | } | |
293 | req->in.h.opcode = FUSE_READLINK; | |
294 | req->in.h.nodeid = get_node_id(inode); | |
295 | req->inode = inode; | |
296 | req->out.argvar = 1; | |
297 | req->out.numargs = 1; | |
298 | req->out.args[0].size = PAGE_SIZE - 1; | |
299 | req->out.args[0].value = link; | |
300 | request_send(fc, req); | |
301 | if (req->out.h.error) { | |
302 | free_page((unsigned long) link); | |
303 | link = ERR_PTR(req->out.h.error); | |
304 | } else | |
305 | link[req->out.args[0].size] = '\0'; | |
306 | out: | |
307 | fuse_put_request(fc, req); | |
308 | return link; | |
309 | } | |
310 | ||
311 | static void free_link(char *link) | |
312 | { | |
313 | if (!IS_ERR(link)) | |
314 | free_page((unsigned long) link); | |
315 | } | |
316 | ||
317 | static void *fuse_follow_link(struct dentry *dentry, struct nameidata *nd) | |
318 | { | |
319 | nd_set_link(nd, read_link(dentry)); | |
320 | return NULL; | |
321 | } | |
322 | ||
323 | static void fuse_put_link(struct dentry *dentry, struct nameidata *nd, void *c) | |
324 | { | |
325 | free_link(nd_get_link(nd)); | |
326 | } | |
327 | ||
328 | static int fuse_dir_open(struct inode *inode, struct file *file) | |
329 | { | |
330 | file->private_data = NULL; | |
331 | return 0; | |
332 | } | |
333 | ||
334 | static int fuse_dir_release(struct inode *inode, struct file *file) | |
335 | { | |
336 | struct file *cfile = file->private_data; | |
337 | ||
338 | if (cfile) | |
339 | fput(cfile); | |
340 | ||
341 | return 0; | |
342 | } | |
343 | ||
344 | static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry, | |
345 | struct kstat *stat) | |
346 | { | |
347 | struct inode *inode = entry->d_inode; | |
348 | int err = fuse_revalidate(entry); | |
349 | if (!err) | |
350 | generic_fillattr(inode, stat); | |
351 | ||
352 | return err; | |
353 | } | |
354 | ||
355 | static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | |
356 | struct nameidata *nd) | |
357 | { | |
358 | struct inode *inode; | |
359 | int err = fuse_lookup_iget(dir, entry, &inode); | |
360 | if (err) | |
361 | return ERR_PTR(err); | |
362 | if (inode && S_ISDIR(inode->i_mode)) { | |
363 | /* Don't allow creating an alias to a directory */ | |
364 | struct dentry *alias = d_find_alias(inode); | |
365 | if (alias && !(alias->d_flags & DCACHE_DISCONNECTED)) { | |
366 | dput(alias); | |
367 | iput(inode); | |
368 | return ERR_PTR(-EIO); | |
369 | } | |
370 | } | |
371 | return d_splice_alias(inode, entry); | |
372 | } | |
373 | ||
374 | static struct inode_operations fuse_dir_inode_operations = { | |
375 | .lookup = fuse_lookup, | |
376 | .permission = fuse_permission, | |
377 | .getattr = fuse_getattr, | |
378 | }; | |
379 | ||
380 | static struct file_operations fuse_dir_operations = { | |
381 | .read = generic_read_dir, | |
382 | .readdir = fuse_readdir, | |
383 | .open = fuse_dir_open, | |
384 | .release = fuse_dir_release, | |
385 | }; | |
386 | ||
387 | static struct inode_operations fuse_common_inode_operations = { | |
388 | .permission = fuse_permission, | |
389 | .getattr = fuse_getattr, | |
390 | }; | |
391 | ||
392 | static struct inode_operations fuse_symlink_inode_operations = { | |
393 | .follow_link = fuse_follow_link, | |
394 | .put_link = fuse_put_link, | |
395 | .readlink = generic_readlink, | |
396 | .getattr = fuse_getattr, | |
397 | }; | |
398 | ||
399 | void fuse_init_common(struct inode *inode) | |
400 | { | |
401 | inode->i_op = &fuse_common_inode_operations; | |
402 | } | |
403 | ||
404 | void fuse_init_dir(struct inode *inode) | |
405 | { | |
406 | inode->i_op = &fuse_dir_inode_operations; | |
407 | inode->i_fop = &fuse_dir_operations; | |
408 | } | |
409 | ||
410 | void fuse_init_symlink(struct inode *inode) | |
411 | { | |
412 | inode->i_op = &fuse_symlink_inode_operations; | |
413 | } |