diff options
Diffstat (limited to 'fs/fuse')
-rw-r--r-- | fs/fuse/dev.c | 72 | ||||
-rw-r--r-- | fs/fuse/dir.c | 278 | ||||
-rw-r--r-- | fs/fuse/file.c | 49 | ||||
-rw-r--r-- | fs/fuse/fuse_i.h | 12 | ||||
-rw-r--r-- | fs/fuse/inode.c | 14 |
5 files changed, 276 insertions, 149 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 8f873e621f41..e08ab4702d97 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c | |||
@@ -148,6 +148,26 @@ void fuse_release_background(struct fuse_req *req) | |||
148 | spin_unlock(&fuse_lock); | 148 | spin_unlock(&fuse_lock); |
149 | } | 149 | } |
150 | 150 | ||
151 | static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req) | ||
152 | { | ||
153 | int i; | ||
154 | struct fuse_init_out *arg = &req->misc.init_out; | ||
155 | |||
156 | if (arg->major != FUSE_KERNEL_VERSION) | ||
157 | fc->conn_error = 1; | ||
158 | else { | ||
159 | fc->minor = arg->minor; | ||
160 | fc->max_write = arg->minor < 5 ? 4096 : arg->max_write; | ||
161 | } | ||
162 | |||
163 | /* After INIT reply is received other requests can go | ||
164 | out. So do (FUSE_MAX_OUTSTANDING - 1) number of | ||
165 | up()s on outstanding_sem. The last up() is done in | ||
166 | fuse_putback_request() */ | ||
167 | for (i = 1; i < FUSE_MAX_OUTSTANDING; i++) | ||
168 | up(&fc->outstanding_sem); | ||
169 | } | ||
170 | |||
151 | /* | 171 | /* |
152 | * This function is called when a request is finished. Either a reply | 172 | * This function is called when a request is finished. Either a reply |
153 | * has arrived or it was interrupted (and not yet sent) or some error | 173 | * has arrived or it was interrupted (and not yet sent) or some error |
@@ -172,19 +192,9 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req) | |||
172 | up_read(&fc->sbput_sem); | 192 | up_read(&fc->sbput_sem); |
173 | } | 193 | } |
174 | wake_up(&req->waitq); | 194 | wake_up(&req->waitq); |
175 | if (req->in.h.opcode == FUSE_INIT) { | 195 | if (req->in.h.opcode == FUSE_INIT) |
176 | int i; | 196 | process_init_reply(fc, req); |
177 | 197 | else if (req->in.h.opcode == FUSE_RELEASE && req->inode == NULL) { | |
178 | if (req->misc.init_in_out.major != FUSE_KERNEL_VERSION) | ||
179 | fc->conn_error = 1; | ||
180 | |||
181 | /* After INIT reply is received other requests can go | ||
182 | out. So do (FUSE_MAX_OUTSTANDING - 1) number of | ||
183 | up()s on outstanding_sem. The last up() is done in | ||
184 | fuse_putback_request() */ | ||
185 | for (i = 1; i < FUSE_MAX_OUTSTANDING; i++) | ||
186 | up(&fc->outstanding_sem); | ||
187 | } else if (req->in.h.opcode == FUSE_RELEASE && req->inode == NULL) { | ||
188 | /* Special case for failed iget in CREATE */ | 198 | /* Special case for failed iget in CREATE */ |
189 | u64 nodeid = req->in.h.nodeid; | 199 | u64 nodeid = req->in.h.nodeid; |
190 | __fuse_get_request(req); | 200 | __fuse_get_request(req); |
@@ -357,7 +367,7 @@ void fuse_send_init(struct fuse_conn *fc) | |||
357 | /* This is called from fuse_read_super() so there's guaranteed | 367 | /* This is called from fuse_read_super() so there's guaranteed |
358 | to be a request available */ | 368 | to be a request available */ |
359 | struct fuse_req *req = do_get_request(fc); | 369 | struct fuse_req *req = do_get_request(fc); |
360 | struct fuse_init_in_out *arg = &req->misc.init_in_out; | 370 | struct fuse_init_in *arg = &req->misc.init_in; |
361 | arg->major = FUSE_KERNEL_VERSION; | 371 | arg->major = FUSE_KERNEL_VERSION; |
362 | arg->minor = FUSE_KERNEL_MINOR_VERSION; | 372 | arg->minor = FUSE_KERNEL_MINOR_VERSION; |
363 | req->in.h.opcode = FUSE_INIT; | 373 | req->in.h.opcode = FUSE_INIT; |
@@ -365,8 +375,12 @@ void fuse_send_init(struct fuse_conn *fc) | |||
365 | req->in.args[0].size = sizeof(*arg); | 375 | req->in.args[0].size = sizeof(*arg); |
366 | req->in.args[0].value = arg; | 376 | req->in.args[0].value = arg; |
367 | req->out.numargs = 1; | 377 | req->out.numargs = 1; |
368 | req->out.args[0].size = sizeof(*arg); | 378 | /* Variable length arguement used for backward compatibility |
369 | req->out.args[0].value = arg; | 379 | with interface version < 7.5. Rest of init_out is zeroed |
380 | by do_get_request(), so a short reply is not a problem */ | ||
381 | req->out.argvar = 1; | ||
382 | req->out.args[0].size = sizeof(struct fuse_init_out); | ||
383 | req->out.args[0].value = &req->misc.init_out; | ||
370 | request_send_background(fc, req); | 384 | request_send_background(fc, req); |
371 | } | 385 | } |
372 | 386 | ||
@@ -615,6 +629,7 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov, | |||
615 | struct fuse_copy_state cs; | 629 | struct fuse_copy_state cs; |
616 | unsigned reqsize; | 630 | unsigned reqsize; |
617 | 631 | ||
632 | restart: | ||
618 | spin_lock(&fuse_lock); | 633 | spin_lock(&fuse_lock); |
619 | fc = file->private_data; | 634 | fc = file->private_data; |
620 | err = -EPERM; | 635 | err = -EPERM; |
@@ -630,20 +645,25 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov, | |||
630 | 645 | ||
631 | req = list_entry(fc->pending.next, struct fuse_req, list); | 646 | req = list_entry(fc->pending.next, struct fuse_req, list); |
632 | list_del_init(&req->list); | 647 | list_del_init(&req->list); |
633 | spin_unlock(&fuse_lock); | ||
634 | 648 | ||
635 | in = &req->in; | 649 | in = &req->in; |
636 | reqsize = req->in.h.len; | 650 | reqsize = in->h.len; |
637 | fuse_copy_init(&cs, 1, req, iov, nr_segs); | 651 | /* If request is too large, reply with an error and restart the read */ |
638 | err = -EINVAL; | 652 | if (iov_length(iov, nr_segs) < reqsize) { |
639 | if (iov_length(iov, nr_segs) >= reqsize) { | 653 | req->out.h.error = -EIO; |
640 | err = fuse_copy_one(&cs, &in->h, sizeof(in->h)); | 654 | /* SETXATTR is special, since it may contain too large data */ |
641 | if (!err) | 655 | if (in->h.opcode == FUSE_SETXATTR) |
642 | err = fuse_copy_args(&cs, in->numargs, in->argpages, | 656 | req->out.h.error = -E2BIG; |
643 | (struct fuse_arg *) in->args, 0); | 657 | request_end(fc, req); |
658 | goto restart; | ||
644 | } | 659 | } |
660 | spin_unlock(&fuse_lock); | ||
661 | fuse_copy_init(&cs, 1, req, iov, nr_segs); | ||
662 | err = fuse_copy_one(&cs, &in->h, sizeof(in->h)); | ||
663 | if (!err) | ||
664 | err = fuse_copy_args(&cs, in->numargs, in->argpages, | ||
665 | (struct fuse_arg *) in->args, 0); | ||
645 | fuse_copy_finish(&cs); | 666 | fuse_copy_finish(&cs); |
646 | |||
647 | spin_lock(&fuse_lock); | 667 | spin_lock(&fuse_lock); |
648 | req->locked = 0; | 668 | req->locked = 0; |
649 | if (!err && req->interrupted) | 669 | if (!err && req->interrupted) |
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 51f5da652771..417bcee466f6 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
@@ -13,8 +13,16 @@ | |||
13 | #include <linux/gfp.h> | 13 | #include <linux/gfp.h> |
14 | #include <linux/sched.h> | 14 | #include <linux/sched.h> |
15 | #include <linux/namei.h> | 15 | #include <linux/namei.h> |
16 | #include <linux/mount.h> | ||
17 | 16 | ||
17 | /* | ||
18 | * FUSE caches dentries and attributes with separate timeout. The | ||
19 | * time in jiffies until the dentry/attributes are valid is stored in | ||
20 | * dentry->d_time and fuse_inode->i_time respectively. | ||
21 | */ | ||
22 | |||
23 | /* | ||
24 | * Calculate the time in jiffies until a dentry/attributes are valid | ||
25 | */ | ||
18 | static inline unsigned long time_to_jiffies(unsigned long sec, | 26 | static inline unsigned long time_to_jiffies(unsigned long sec, |
19 | unsigned long nsec) | 27 | unsigned long nsec) |
20 | { | 28 | { |
@@ -22,6 +30,50 @@ static inline unsigned long time_to_jiffies(unsigned long sec, | |||
22 | return jiffies + timespec_to_jiffies(&ts); | 30 | return jiffies + timespec_to_jiffies(&ts); |
23 | } | 31 | } |
24 | 32 | ||
33 | /* | ||
34 | * Set dentry and possibly attribute timeouts from the lookup/mk* | ||
35 | * replies | ||
36 | */ | ||
37 | static void fuse_change_timeout(struct dentry *entry, struct fuse_entry_out *o) | ||
38 | { | ||
39 | entry->d_time = time_to_jiffies(o->entry_valid, o->entry_valid_nsec); | ||
40 | if (entry->d_inode) | ||
41 | get_fuse_inode(entry->d_inode)->i_time = | ||
42 | time_to_jiffies(o->attr_valid, o->attr_valid_nsec); | ||
43 | } | ||
44 | |||
45 | /* | ||
46 | * Mark the attributes as stale, so that at the next call to | ||
47 | * ->getattr() they will be fetched from userspace | ||
48 | */ | ||
49 | void fuse_invalidate_attr(struct inode *inode) | ||
50 | { | ||
51 | get_fuse_inode(inode)->i_time = jiffies - 1; | ||
52 | } | ||
53 | |||
54 | /* | ||
55 | * Just mark the entry as stale, so that a next attempt to look it up | ||
56 | * will result in a new lookup call to userspace | ||
57 | * | ||
58 | * This is called when a dentry is about to become negative and the | ||
59 | * timeout is unknown (unlink, rmdir, rename and in some cases | ||
60 | * lookup) | ||
61 | */ | ||
62 | static void fuse_invalidate_entry_cache(struct dentry *entry) | ||
63 | { | ||
64 | entry->d_time = jiffies - 1; | ||
65 | } | ||
66 | |||
67 | /* | ||
68 | * Same as fuse_invalidate_entry_cache(), but also try to remove the | ||
69 | * dentry from the hash | ||
70 | */ | ||
71 | static void fuse_invalidate_entry(struct dentry *entry) | ||
72 | { | ||
73 | d_invalidate(entry); | ||
74 | fuse_invalidate_entry_cache(entry); | ||
75 | } | ||
76 | |||
25 | static void fuse_lookup_init(struct fuse_req *req, struct inode *dir, | 77 | static void fuse_lookup_init(struct fuse_req *req, struct inode *dir, |
26 | struct dentry *entry, | 78 | struct dentry *entry, |
27 | struct fuse_entry_out *outarg) | 79 | struct fuse_entry_out *outarg) |
@@ -37,17 +89,34 @@ static void fuse_lookup_init(struct fuse_req *req, struct inode *dir, | |||
37 | req->out.args[0].value = outarg; | 89 | req->out.args[0].value = outarg; |
38 | } | 90 | } |
39 | 91 | ||
92 | /* | ||
93 | * Check whether the dentry is still valid | ||
94 | * | ||
95 | * If the entry validity timeout has expired and the dentry is | ||
96 | * positive, try to redo the lookup. If the lookup results in a | ||
97 | * different inode, then let the VFS invalidate the dentry and redo | ||
98 | * the lookup once more. If the lookup results in the same inode, | ||
99 | * then refresh the attributes, timeouts and mark the dentry valid. | ||
100 | */ | ||
40 | static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) | 101 | static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) |
41 | { | 102 | { |
42 | if (!entry->d_inode || is_bad_inode(entry->d_inode)) | 103 | struct inode *inode = entry->d_inode; |
104 | |||
105 | if (inode && is_bad_inode(inode)) | ||
43 | return 0; | 106 | return 0; |
44 | else if (time_after(jiffies, entry->d_time)) { | 107 | else if (time_after(jiffies, entry->d_time)) { |
45 | int err; | 108 | int err; |
46 | struct fuse_entry_out outarg; | 109 | struct fuse_entry_out outarg; |
47 | struct inode *inode = entry->d_inode; | 110 | struct fuse_conn *fc; |
48 | struct fuse_inode *fi = get_fuse_inode(inode); | 111 | struct fuse_req *req; |
49 | struct fuse_conn *fc = get_fuse_conn(inode); | 112 | |
50 | struct fuse_req *req = fuse_get_request(fc); | 113 | /* Doesn't hurt to "reset" the validity timeout */ |
114 | fuse_invalidate_entry_cache(entry); | ||
115 | if (!inode) | ||
116 | return 0; | ||
117 | |||
118 | fc = get_fuse_conn(inode); | ||
119 | req = fuse_get_request(fc); | ||
51 | if (!req) | 120 | if (!req) |
52 | return 0; | 121 | return 0; |
53 | 122 | ||
@@ -55,6 +124,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) | |||
55 | request_send(fc, req); | 124 | request_send(fc, req); |
56 | err = req->out.h.error; | 125 | err = req->out.h.error; |
57 | if (!err) { | 126 | if (!err) { |
127 | struct fuse_inode *fi = get_fuse_inode(inode); | ||
58 | if (outarg.nodeid != get_node_id(inode)) { | 128 | if (outarg.nodeid != get_node_id(inode)) { |
59 | fuse_send_forget(fc, req, outarg.nodeid, 1); | 129 | fuse_send_forget(fc, req, outarg.nodeid, 1); |
60 | return 0; | 130 | return 0; |
@@ -66,18 +136,18 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) | |||
66 | return 0; | 136 | return 0; |
67 | 137 | ||
68 | fuse_change_attributes(inode, &outarg.attr); | 138 | fuse_change_attributes(inode, &outarg.attr); |
69 | entry->d_time = time_to_jiffies(outarg.entry_valid, | 139 | fuse_change_timeout(entry, &outarg); |
70 | outarg.entry_valid_nsec); | ||
71 | fi->i_time = time_to_jiffies(outarg.attr_valid, | ||
72 | outarg.attr_valid_nsec); | ||
73 | } | 140 | } |
74 | return 1; | 141 | return 1; |
75 | } | 142 | } |
76 | 143 | ||
144 | /* | ||
145 | * Check if there's already a hashed alias of this directory inode. | ||
146 | * If yes, then lookup and mkdir must not create a new alias. | ||
147 | */ | ||
77 | static int dir_alias(struct inode *inode) | 148 | static int dir_alias(struct inode *inode) |
78 | { | 149 | { |
79 | if (S_ISDIR(inode->i_mode)) { | 150 | if (S_ISDIR(inode->i_mode)) { |
80 | /* Don't allow creating an alias to a directory */ | ||
81 | struct dentry *alias = d_find_alias(inode); | 151 | struct dentry *alias = d_find_alias(inode); |
82 | if (alias) { | 152 | if (alias) { |
83 | dput(alias); | 153 | dput(alias); |
@@ -96,8 +166,14 @@ static struct dentry_operations fuse_dentry_operations = { | |||
96 | .d_revalidate = fuse_dentry_revalidate, | 166 | .d_revalidate = fuse_dentry_revalidate, |
97 | }; | 167 | }; |
98 | 168 | ||
99 | static int fuse_lookup_iget(struct inode *dir, struct dentry *entry, | 169 | static inline int valid_mode(int m) |
100 | struct inode **inodep) | 170 | { |
171 | return S_ISREG(m) || S_ISDIR(m) || S_ISLNK(m) || S_ISCHR(m) || | ||
172 | S_ISBLK(m) || S_ISFIFO(m) || S_ISSOCK(m); | ||
173 | } | ||
174 | |||
175 | static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | ||
176 | struct nameidata *nd) | ||
101 | { | 177 | { |
102 | int err; | 178 | int err; |
103 | struct fuse_entry_out outarg; | 179 | struct fuse_entry_out outarg; |
@@ -106,53 +182,49 @@ static int fuse_lookup_iget(struct inode *dir, struct dentry *entry, | |||
106 | struct fuse_req *req; | 182 | struct fuse_req *req; |
107 | 183 | ||
108 | if (entry->d_name.len > FUSE_NAME_MAX) | 184 | if (entry->d_name.len > FUSE_NAME_MAX) |
109 | return -ENAMETOOLONG; | 185 | return ERR_PTR(-ENAMETOOLONG); |
110 | 186 | ||
111 | req = fuse_get_request(fc); | 187 | req = fuse_get_request(fc); |
112 | if (!req) | 188 | if (!req) |
113 | return -EINTR; | 189 | return ERR_PTR(-EINTR); |
114 | 190 | ||
115 | fuse_lookup_init(req, dir, entry, &outarg); | 191 | fuse_lookup_init(req, dir, entry, &outarg); |
116 | request_send(fc, req); | 192 | request_send(fc, req); |
117 | err = req->out.h.error; | 193 | err = req->out.h.error; |
118 | if (!err && invalid_nodeid(outarg.nodeid)) | 194 | if (!err && ((outarg.nodeid && invalid_nodeid(outarg.nodeid)) || |
195 | !valid_mode(outarg.attr.mode))) | ||
119 | err = -EIO; | 196 | err = -EIO; |
120 | if (!err) { | 197 | if (!err && outarg.nodeid) { |
121 | inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, | 198 | inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, |
122 | &outarg.attr); | 199 | &outarg.attr); |
123 | if (!inode) { | 200 | if (!inode) { |
124 | fuse_send_forget(fc, req, outarg.nodeid, 1); | 201 | fuse_send_forget(fc, req, outarg.nodeid, 1); |
125 | return -ENOMEM; | 202 | return ERR_PTR(-ENOMEM); |
126 | } | 203 | } |
127 | } | 204 | } |
128 | fuse_put_request(fc, req); | 205 | fuse_put_request(fc, req); |
129 | if (err && err != -ENOENT) | 206 | if (err && err != -ENOENT) |
130 | return err; | 207 | return ERR_PTR(err); |
131 | 208 | ||
132 | if (inode) { | 209 | if (inode && dir_alias(inode)) { |
133 | struct fuse_inode *fi = get_fuse_inode(inode); | 210 | iput(inode); |
134 | entry->d_time = time_to_jiffies(outarg.entry_valid, | 211 | return ERR_PTR(-EIO); |
135 | outarg.entry_valid_nsec); | ||
136 | fi->i_time = time_to_jiffies(outarg.attr_valid, | ||
137 | outarg.attr_valid_nsec); | ||
138 | } | 212 | } |
139 | 213 | d_add(entry, inode); | |
140 | entry->d_op = &fuse_dentry_operations; | 214 | entry->d_op = &fuse_dentry_operations; |
141 | *inodep = inode; | 215 | if (!err) |
142 | return 0; | 216 | fuse_change_timeout(entry, &outarg); |
143 | } | 217 | else |
144 | 218 | fuse_invalidate_entry_cache(entry); | |
145 | void fuse_invalidate_attr(struct inode *inode) | 219 | return NULL; |
146 | { | ||
147 | get_fuse_inode(inode)->i_time = jiffies - 1; | ||
148 | } | ||
149 | |||
150 | static void fuse_invalidate_entry(struct dentry *entry) | ||
151 | { | ||
152 | d_invalidate(entry); | ||
153 | entry->d_time = jiffies - 1; | ||
154 | } | 220 | } |
155 | 221 | ||
222 | /* | ||
223 | * Atomic create+open operation | ||
224 | * | ||
225 | * If the filesystem doesn't support this, then fall back to separate | ||
226 | * 'mknod' + 'open' requests. | ||
227 | */ | ||
156 | static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | 228 | static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, |
157 | struct nameidata *nd) | 229 | struct nameidata *nd) |
158 | { | 230 | { |
@@ -163,7 +235,6 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | |||
163 | struct fuse_open_in inarg; | 235 | struct fuse_open_in inarg; |
164 | struct fuse_open_out outopen; | 236 | struct fuse_open_out outopen; |
165 | struct fuse_entry_out outentry; | 237 | struct fuse_entry_out outentry; |
166 | struct fuse_inode *fi; | ||
167 | struct fuse_file *ff; | 238 | struct fuse_file *ff; |
168 | struct file *file; | 239 | struct file *file; |
169 | int flags = nd->intent.open.flags - 1; | 240 | int flags = nd->intent.open.flags - 1; |
@@ -172,10 +243,6 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | |||
172 | if (fc->no_create) | 243 | if (fc->no_create) |
173 | goto out; | 244 | goto out; |
174 | 245 | ||
175 | err = -ENAMETOOLONG; | ||
176 | if (entry->d_name.len > FUSE_NAME_MAX) | ||
177 | goto out; | ||
178 | |||
179 | err = -EINTR; | 246 | err = -EINTR; |
180 | req = fuse_get_request(fc); | 247 | req = fuse_get_request(fc); |
181 | if (!req) | 248 | if (!req) |
@@ -220,17 +287,15 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | |||
220 | if (!inode) { | 287 | if (!inode) { |
221 | flags &= ~(O_CREAT | O_EXCL | O_TRUNC); | 288 | flags &= ~(O_CREAT | O_EXCL | O_TRUNC); |
222 | ff->fh = outopen.fh; | 289 | ff->fh = outopen.fh; |
290 | /* Special release, with inode = NULL, this will | ||
291 | trigger a 'forget' request when the release is | ||
292 | complete */ | ||
223 | fuse_send_release(fc, ff, outentry.nodeid, NULL, flags, 0); | 293 | fuse_send_release(fc, ff, outentry.nodeid, NULL, flags, 0); |
224 | goto out_put_request; | 294 | goto out_put_request; |
225 | } | 295 | } |
226 | fuse_put_request(fc, req); | 296 | fuse_put_request(fc, req); |
227 | entry->d_time = time_to_jiffies(outentry.entry_valid, | ||
228 | outentry.entry_valid_nsec); | ||
229 | fi = get_fuse_inode(inode); | ||
230 | fi->i_time = time_to_jiffies(outentry.attr_valid, | ||
231 | outentry.attr_valid_nsec); | ||
232 | |||
233 | d_instantiate(entry, inode); | 297 | d_instantiate(entry, inode); |
298 | fuse_change_timeout(entry, &outentry); | ||
234 | file = lookup_instantiate_filp(nd, entry, generic_file_open); | 299 | file = lookup_instantiate_filp(nd, entry, generic_file_open); |
235 | if (IS_ERR(file)) { | 300 | if (IS_ERR(file)) { |
236 | ff->fh = outopen.fh; | 301 | ff->fh = outopen.fh; |
@@ -248,13 +313,15 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | |||
248 | return err; | 313 | return err; |
249 | } | 314 | } |
250 | 315 | ||
316 | /* | ||
317 | * Code shared between mknod, mkdir, symlink and link | ||
318 | */ | ||
251 | static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, | 319 | static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, |
252 | struct inode *dir, struct dentry *entry, | 320 | struct inode *dir, struct dentry *entry, |
253 | int mode) | 321 | int mode) |
254 | { | 322 | { |
255 | struct fuse_entry_out outarg; | 323 | struct fuse_entry_out outarg; |
256 | struct inode *inode; | 324 | struct inode *inode; |
257 | struct fuse_inode *fi; | ||
258 | int err; | 325 | int err; |
259 | 326 | ||
260 | req->in.h.nodeid = get_node_id(dir); | 327 | req->in.h.nodeid = get_node_id(dir); |
@@ -268,10 +335,13 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, | |||
268 | fuse_put_request(fc, req); | 335 | fuse_put_request(fc, req); |
269 | return err; | 336 | return err; |
270 | } | 337 | } |
271 | if (invalid_nodeid(outarg.nodeid)) { | 338 | err = -EIO; |
272 | fuse_put_request(fc, req); | 339 | if (invalid_nodeid(outarg.nodeid)) |
273 | return -EIO; | 340 | goto out_put_request; |
274 | } | 341 | |
342 | if ((outarg.attr.mode ^ mode) & S_IFMT) | ||
343 | goto out_put_request; | ||
344 | |||
275 | inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, | 345 | inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, |
276 | &outarg.attr); | 346 | &outarg.attr); |
277 | if (!inode) { | 347 | if (!inode) { |
@@ -280,22 +350,19 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, | |||
280 | } | 350 | } |
281 | fuse_put_request(fc, req); | 351 | fuse_put_request(fc, req); |
282 | 352 | ||
283 | /* Don't allow userspace to do really stupid things... */ | 353 | if (dir_alias(inode)) { |
284 | if (((inode->i_mode ^ mode) & S_IFMT) || dir_alias(inode)) { | ||
285 | iput(inode); | 354 | iput(inode); |
286 | return -EIO; | 355 | return -EIO; |
287 | } | 356 | } |
288 | 357 | ||
289 | entry->d_time = time_to_jiffies(outarg.entry_valid, | ||
290 | outarg.entry_valid_nsec); | ||
291 | |||
292 | fi = get_fuse_inode(inode); | ||
293 | fi->i_time = time_to_jiffies(outarg.attr_valid, | ||
294 | outarg.attr_valid_nsec); | ||
295 | |||
296 | d_instantiate(entry, inode); | 358 | d_instantiate(entry, inode); |
359 | fuse_change_timeout(entry, &outarg); | ||
297 | fuse_invalidate_attr(dir); | 360 | fuse_invalidate_attr(dir); |
298 | return 0; | 361 | return 0; |
362 | |||
363 | out_put_request: | ||
364 | fuse_put_request(fc, req); | ||
365 | return err; | ||
299 | } | 366 | } |
300 | 367 | ||
301 | static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode, | 368 | static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode, |
@@ -355,12 +422,7 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry, | |||
355 | { | 422 | { |
356 | struct fuse_conn *fc = get_fuse_conn(dir); | 423 | struct fuse_conn *fc = get_fuse_conn(dir); |
357 | unsigned len = strlen(link) + 1; | 424 | unsigned len = strlen(link) + 1; |
358 | struct fuse_req *req; | 425 | struct fuse_req *req = fuse_get_request(fc); |
359 | |||
360 | if (len > FUSE_SYMLINK_MAX) | ||
361 | return -ENAMETOOLONG; | ||
362 | |||
363 | req = fuse_get_request(fc); | ||
364 | if (!req) | 426 | if (!req) |
365 | return -EINTR; | 427 | return -EINTR; |
366 | 428 | ||
@@ -399,6 +461,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry) | |||
399 | inode->i_nlink = 0; | 461 | inode->i_nlink = 0; |
400 | fuse_invalidate_attr(inode); | 462 | fuse_invalidate_attr(inode); |
401 | fuse_invalidate_attr(dir); | 463 | fuse_invalidate_attr(dir); |
464 | fuse_invalidate_entry_cache(entry); | ||
402 | } else if (err == -EINTR) | 465 | } else if (err == -EINTR) |
403 | fuse_invalidate_entry(entry); | 466 | fuse_invalidate_entry(entry); |
404 | return err; | 467 | return err; |
@@ -424,6 +487,7 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry) | |||
424 | if (!err) { | 487 | if (!err) { |
425 | entry->d_inode->i_nlink = 0; | 488 | entry->d_inode->i_nlink = 0; |
426 | fuse_invalidate_attr(dir); | 489 | fuse_invalidate_attr(dir); |
490 | fuse_invalidate_entry_cache(entry); | ||
427 | } else if (err == -EINTR) | 491 | } else if (err == -EINTR) |
428 | fuse_invalidate_entry(entry); | 492 | fuse_invalidate_entry(entry); |
429 | return err; | 493 | return err; |
@@ -459,6 +523,10 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent, | |||
459 | fuse_invalidate_attr(olddir); | 523 | fuse_invalidate_attr(olddir); |
460 | if (olddir != newdir) | 524 | if (olddir != newdir) |
461 | fuse_invalidate_attr(newdir); | 525 | fuse_invalidate_attr(newdir); |
526 | |||
527 | /* newent will end up negative */ | ||
528 | if (newent->d_inode) | ||
529 | fuse_invalidate_entry_cache(newent); | ||
462 | } else if (err == -EINTR) { | 530 | } else if (err == -EINTR) { |
463 | /* If request was interrupted, DEITY only knows if the | 531 | /* If request was interrupted, DEITY only knows if the |
464 | rename actually took place. If the invalidation | 532 | rename actually took place. If the invalidation |
@@ -566,6 +634,15 @@ static int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task) | |||
566 | return 0; | 634 | return 0; |
567 | } | 635 | } |
568 | 636 | ||
637 | /* | ||
638 | * Check whether the inode attributes are still valid | ||
639 | * | ||
640 | * If the attribute validity timeout has expired, then fetch the fresh | ||
641 | * attributes with a 'getattr' request | ||
642 | * | ||
643 | * I'm not sure why cached attributes are never returned for the root | ||
644 | * inode, this is probably being too cautious. | ||
645 | */ | ||
569 | static int fuse_revalidate(struct dentry *entry) | 646 | static int fuse_revalidate(struct dentry *entry) |
570 | { | 647 | { |
571 | struct inode *inode = entry->d_inode; | 648 | struct inode *inode = entry->d_inode; |
@@ -613,6 +690,19 @@ static int fuse_access(struct inode *inode, int mask) | |||
613 | return err; | 690 | return err; |
614 | } | 691 | } |
615 | 692 | ||
693 | /* | ||
694 | * Check permission. The two basic access models of FUSE are: | ||
695 | * | ||
696 | * 1) Local access checking ('default_permissions' mount option) based | ||
697 | * on file mode. This is the plain old disk filesystem permission | ||
698 | * modell. | ||
699 | * | ||
700 | * 2) "Remote" access checking, where server is responsible for | ||
701 | * checking permission in each inode operation. An exception to this | ||
702 | * is if ->permission() was invoked from sys_access() in which case an | ||
703 | * access request is sent. Execute permission is still checked | ||
704 | * locally based on file mode. | ||
705 | */ | ||
616 | static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) | 706 | static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) |
617 | { | 707 | { |
618 | struct fuse_conn *fc = get_fuse_conn(inode); | 708 | struct fuse_conn *fc = get_fuse_conn(inode); |
@@ -631,14 +721,10 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) | |||
631 | err = generic_permission(inode, mask, NULL); | 721 | err = generic_permission(inode, mask, NULL); |
632 | } | 722 | } |
633 | 723 | ||
634 | /* FIXME: Need some mechanism to revoke permissions: | 724 | /* Note: the opposite of the above test does not |
635 | currently if the filesystem suddenly changes the | 725 | exist. So if permissions are revoked this won't be |
636 | file mode, we will not be informed about it, and | 726 | noticed immediately, only after the attribute |
637 | continue to allow access to the file/directory. | 727 | timeout has expired */ |
638 | |||
639 | This is actually not so grave, since the user can | ||
640 | simply keep access to the file/directory anyway by | ||
641 | keeping it open... */ | ||
642 | 728 | ||
643 | return err; | 729 | return err; |
644 | } else { | 730 | } else { |
@@ -691,7 +777,12 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) | |||
691 | struct page *page; | 777 | struct page *page; |
692 | struct inode *inode = file->f_dentry->d_inode; | 778 | struct inode *inode = file->f_dentry->d_inode; |
693 | struct fuse_conn *fc = get_fuse_conn(inode); | 779 | struct fuse_conn *fc = get_fuse_conn(inode); |
694 | struct fuse_req *req = fuse_get_request(fc); | 780 | struct fuse_req *req; |
781 | |||
782 | if (is_bad_inode(inode)) | ||
783 | return -EIO; | ||
784 | |||
785 | req = fuse_get_request(fc); | ||
695 | if (!req) | 786 | if (!req) |
696 | return -EINTR; | 787 | return -EINTR; |
697 | 788 | ||
@@ -806,6 +897,15 @@ static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg) | |||
806 | } | 897 | } |
807 | } | 898 | } |
808 | 899 | ||
900 | /* | ||
901 | * Set attributes, and at the same time refresh them. | ||
902 | * | ||
903 | * Truncation is slightly complicated, because the 'truncate' request | ||
904 | * may fail, in which case we don't want to touch the mapping. | ||
905 | * vmtruncate() doesn't allow for this case. So do the rlimit | ||
906 | * checking by hand and call vmtruncate() only after the file has | ||
907 | * actually been truncated. | ||
908 | */ | ||
809 | static int fuse_setattr(struct dentry *entry, struct iattr *attr) | 909 | static int fuse_setattr(struct dentry *entry, struct iattr *attr) |
810 | { | 910 | { |
811 | struct inode *inode = entry->d_inode; | 911 | struct inode *inode = entry->d_inode; |
@@ -883,23 +983,6 @@ static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry, | |||
883 | return err; | 983 | return err; |
884 | } | 984 | } |
885 | 985 | ||
886 | static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | ||
887 | struct nameidata *nd) | ||
888 | { | ||
889 | struct inode *inode; | ||
890 | int err; | ||
891 | |||
892 | err = fuse_lookup_iget(dir, entry, &inode); | ||
893 | if (err) | ||
894 | return ERR_PTR(err); | ||
895 | if (inode && dir_alias(inode)) { | ||
896 | iput(inode); | ||
897 | return ERR_PTR(-EIO); | ||
898 | } | ||
899 | d_add(entry, inode); | ||
900 | return NULL; | ||
901 | } | ||
902 | |||
903 | static int fuse_setxattr(struct dentry *entry, const char *name, | 986 | static int fuse_setxattr(struct dentry *entry, const char *name, |
904 | const void *value, size_t size, int flags) | 987 | const void *value, size_t size, int flags) |
905 | { | 988 | { |
@@ -909,9 +992,6 @@ static int fuse_setxattr(struct dentry *entry, const char *name, | |||
909 | struct fuse_setxattr_in inarg; | 992 | struct fuse_setxattr_in inarg; |
910 | int err; | 993 | int err; |
911 | 994 | ||
912 | if (size > FUSE_XATTR_SIZE_MAX) | ||
913 | return -E2BIG; | ||
914 | |||
915 | if (fc->no_setxattr) | 995 | if (fc->no_setxattr) |
916 | return -EOPNOTSUPP; | 996 | return -EOPNOTSUPP; |
917 | 997 | ||
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 2ca86141d13a..05dedddf4289 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c | |||
@@ -163,6 +163,9 @@ static int fuse_flush(struct file *file) | |||
163 | struct fuse_flush_in inarg; | 163 | struct fuse_flush_in inarg; |
164 | int err; | 164 | int err; |
165 | 165 | ||
166 | if (is_bad_inode(inode)) | ||
167 | return -EIO; | ||
168 | |||
166 | if (fc->no_flush) | 169 | if (fc->no_flush) |
167 | return 0; | 170 | return 0; |
168 | 171 | ||
@@ -199,6 +202,9 @@ int fuse_fsync_common(struct file *file, struct dentry *de, int datasync, | |||
199 | struct fuse_fsync_in inarg; | 202 | struct fuse_fsync_in inarg; |
200 | int err; | 203 | int err; |
201 | 204 | ||
205 | if (is_bad_inode(inode)) | ||
206 | return -EIO; | ||
207 | |||
202 | if ((!isdir && fc->no_fsync) || (isdir && fc->no_fsyncdir)) | 208 | if ((!isdir && fc->no_fsync) || (isdir && fc->no_fsyncdir)) |
203 | return 0; | 209 | return 0; |
204 | 210 | ||
@@ -272,16 +278,22 @@ static int fuse_readpage(struct file *file, struct page *page) | |||
272 | { | 278 | { |
273 | struct inode *inode = page->mapping->host; | 279 | struct inode *inode = page->mapping->host; |
274 | struct fuse_conn *fc = get_fuse_conn(inode); | 280 | struct fuse_conn *fc = get_fuse_conn(inode); |
275 | loff_t pos = (loff_t) page->index << PAGE_CACHE_SHIFT; | 281 | struct fuse_req *req; |
276 | struct fuse_req *req = fuse_get_request(fc); | 282 | int err; |
277 | int err = -EINTR; | 283 | |
284 | err = -EIO; | ||
285 | if (is_bad_inode(inode)) | ||
286 | goto out; | ||
287 | |||
288 | err = -EINTR; | ||
289 | req = fuse_get_request(fc); | ||
278 | if (!req) | 290 | if (!req) |
279 | goto out; | 291 | goto out; |
280 | 292 | ||
281 | req->out.page_zeroing = 1; | 293 | req->out.page_zeroing = 1; |
282 | req->num_pages = 1; | 294 | req->num_pages = 1; |
283 | req->pages[0] = page; | 295 | req->pages[0] = page; |
284 | fuse_send_read(req, file, inode, pos, PAGE_CACHE_SIZE); | 296 | fuse_send_read(req, file, inode, page_offset(page), PAGE_CACHE_SIZE); |
285 | err = req->out.h.error; | 297 | err = req->out.h.error; |
286 | fuse_put_request(fc, req); | 298 | fuse_put_request(fc, req); |
287 | if (!err) | 299 | if (!err) |
@@ -295,7 +307,7 @@ static int fuse_readpage(struct file *file, struct page *page) | |||
295 | static int fuse_send_readpages(struct fuse_req *req, struct file *file, | 307 | static int fuse_send_readpages(struct fuse_req *req, struct file *file, |
296 | struct inode *inode) | 308 | struct inode *inode) |
297 | { | 309 | { |
298 | loff_t pos = (loff_t) req->pages[0]->index << PAGE_CACHE_SHIFT; | 310 | loff_t pos = page_offset(req->pages[0]); |
299 | size_t count = req->num_pages << PAGE_CACHE_SHIFT; | 311 | size_t count = req->num_pages << PAGE_CACHE_SHIFT; |
300 | unsigned i; | 312 | unsigned i; |
301 | req->out.page_zeroing = 1; | 313 | req->out.page_zeroing = 1; |
@@ -345,6 +357,10 @@ static int fuse_readpages(struct file *file, struct address_space *mapping, | |||
345 | struct fuse_conn *fc = get_fuse_conn(inode); | 357 | struct fuse_conn *fc = get_fuse_conn(inode); |
346 | struct fuse_readpages_data data; | 358 | struct fuse_readpages_data data; |
347 | int err; | 359 | int err; |
360 | |||
361 | if (is_bad_inode(inode)) | ||
362 | return -EIO; | ||
363 | |||
348 | data.file = file; | 364 | data.file = file; |
349 | data.inode = inode; | 365 | data.inode = inode; |
350 | data.req = fuse_get_request(fc); | 366 | data.req = fuse_get_request(fc); |
@@ -402,8 +418,13 @@ static int fuse_commit_write(struct file *file, struct page *page, | |||
402 | unsigned count = to - offset; | 418 | unsigned count = to - offset; |
403 | struct inode *inode = page->mapping->host; | 419 | struct inode *inode = page->mapping->host; |
404 | struct fuse_conn *fc = get_fuse_conn(inode); | 420 | struct fuse_conn *fc = get_fuse_conn(inode); |
405 | loff_t pos = ((loff_t) page->index << PAGE_CACHE_SHIFT) + offset; | 421 | loff_t pos = page_offset(page) + offset; |
406 | struct fuse_req *req = fuse_get_request(fc); | 422 | struct fuse_req *req; |
423 | |||
424 | if (is_bad_inode(inode)) | ||
425 | return -EIO; | ||
426 | |||
427 | req = fuse_get_request(fc); | ||
407 | if (!req) | 428 | if (!req) |
408 | return -EINTR; | 429 | return -EINTR; |
409 | 430 | ||
@@ -454,7 +475,7 @@ static int fuse_get_user_pages(struct fuse_req *req, const char __user *buf, | |||
454 | 475 | ||
455 | nbytes = min(nbytes, (unsigned) FUSE_MAX_PAGES_PER_REQ << PAGE_SHIFT); | 476 | nbytes = min(nbytes, (unsigned) FUSE_MAX_PAGES_PER_REQ << PAGE_SHIFT); |
456 | npages = (nbytes + offset + PAGE_SIZE - 1) >> PAGE_SHIFT; | 477 | npages = (nbytes + offset + PAGE_SIZE - 1) >> PAGE_SHIFT; |
457 | npages = min(npages, FUSE_MAX_PAGES_PER_REQ); | 478 | npages = min(max(npages, 1), FUSE_MAX_PAGES_PER_REQ); |
458 | down_read(¤t->mm->mmap_sem); | 479 | down_read(¤t->mm->mmap_sem); |
459 | npages = get_user_pages(current, current->mm, user_addr, npages, write, | 480 | npages = get_user_pages(current, current->mm, user_addr, npages, write, |
460 | 0, req->pages, NULL); | 481 | 0, req->pages, NULL); |
@@ -475,12 +496,16 @@ static ssize_t fuse_direct_io(struct file *file, const char __user *buf, | |||
475 | size_t nmax = write ? fc->max_write : fc->max_read; | 496 | size_t nmax = write ? fc->max_write : fc->max_read; |
476 | loff_t pos = *ppos; | 497 | loff_t pos = *ppos; |
477 | ssize_t res = 0; | 498 | ssize_t res = 0; |
478 | struct fuse_req *req = fuse_get_request(fc); | 499 | struct fuse_req *req; |
500 | |||
501 | if (is_bad_inode(inode)) | ||
502 | return -EIO; | ||
503 | |||
504 | req = fuse_get_request(fc); | ||
479 | if (!req) | 505 | if (!req) |
480 | return -EINTR; | 506 | return -EINTR; |
481 | 507 | ||
482 | while (count) { | 508 | while (count) { |
483 | size_t tmp; | ||
484 | size_t nres; | 509 | size_t nres; |
485 | size_t nbytes = min(count, nmax); | 510 | size_t nbytes = min(count, nmax); |
486 | int err = fuse_get_user_pages(req, buf, nbytes, !write); | 511 | int err = fuse_get_user_pages(req, buf, nbytes, !write); |
@@ -488,8 +513,8 @@ static ssize_t fuse_direct_io(struct file *file, const char __user *buf, | |||
488 | res = err; | 513 | res = err; |
489 | break; | 514 | break; |
490 | } | 515 | } |
491 | tmp = (req->num_pages << PAGE_SHIFT) - req->page_offset; | 516 | nbytes = (req->num_pages << PAGE_SHIFT) - req->page_offset; |
492 | nbytes = min(nbytes, tmp); | 517 | nbytes = min(count, nbytes); |
493 | if (write) | 518 | if (write) |
494 | nres = fuse_send_write(req, file, inode, pos, nbytes); | 519 | nres = fuse_send_write(req, file, inode, pos, nbytes); |
495 | else | 520 | else |
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 0ea5301f86be..74c8d098a14a 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h | |||
@@ -21,6 +21,9 @@ | |||
21 | /** If more requests are outstanding, then the operation will block */ | 21 | /** If more requests are outstanding, then the operation will block */ |
22 | #define FUSE_MAX_OUTSTANDING 10 | 22 | #define FUSE_MAX_OUTSTANDING 10 |
23 | 23 | ||
24 | /** It could be as large as PATH_MAX, but would that have any uses? */ | ||
25 | #define FUSE_NAME_MAX 1024 | ||
26 | |||
24 | /** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem | 27 | /** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem |
25 | module will check permissions based on the file mode. Otherwise no | 28 | module will check permissions based on the file mode. Otherwise no |
26 | permission checking is done in the kernel */ | 29 | permission checking is done in the kernel */ |
@@ -108,9 +111,6 @@ struct fuse_out { | |||
108 | struct fuse_arg args[3]; | 111 | struct fuse_arg args[3]; |
109 | }; | 112 | }; |
110 | 113 | ||
111 | struct fuse_req; | ||
112 | struct fuse_conn; | ||
113 | |||
114 | /** | 114 | /** |
115 | * A request to the client | 115 | * A request to the client |
116 | */ | 116 | */ |
@@ -159,7 +159,8 @@ struct fuse_req { | |||
159 | union { | 159 | union { |
160 | struct fuse_forget_in forget_in; | 160 | struct fuse_forget_in forget_in; |
161 | struct fuse_release_in release_in; | 161 | struct fuse_release_in release_in; |
162 | struct fuse_init_in_out init_in_out; | 162 | struct fuse_init_in init_in; |
163 | struct fuse_init_out init_out; | ||
163 | } misc; | 164 | } misc; |
164 | 165 | ||
165 | /** page vector */ | 166 | /** page vector */ |
@@ -272,6 +273,9 @@ struct fuse_conn { | |||
272 | /** Is create not implemented by fs? */ | 273 | /** Is create not implemented by fs? */ |
273 | unsigned no_create : 1; | 274 | unsigned no_create : 1; |
274 | 275 | ||
276 | /** Negotiated minor version */ | ||
277 | unsigned minor; | ||
278 | |||
275 | /** Backing dev info */ | 279 | /** Backing dev info */ |
276 | struct backing_dev_info bdi; | 280 | struct backing_dev_info bdi; |
277 | }; | 281 | }; |
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index e69a546844d0..04c80cc957a3 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c | |||
@@ -135,12 +135,8 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) | |||
135 | fuse_init_common(inode); | 135 | fuse_init_common(inode); |
136 | init_special_inode(inode, inode->i_mode, | 136 | init_special_inode(inode, inode->i_mode, |
137 | new_decode_dev(attr->rdev)); | 137 | new_decode_dev(attr->rdev)); |
138 | } else { | 138 | } else |
139 | /* Don't let user create weird files */ | 139 | BUG(); |
140 | inode->i_mode = S_IFREG; | ||
141 | fuse_init_common(inode); | ||
142 | fuse_init_file_inode(inode); | ||
143 | } | ||
144 | } | 140 | } |
145 | 141 | ||
146 | static int fuse_inode_eq(struct inode *inode, void *_nodeidp) | 142 | static int fuse_inode_eq(struct inode *inode, void *_nodeidp) |
@@ -218,6 +214,7 @@ static void convert_fuse_statfs(struct kstatfs *stbuf, struct fuse_kstatfs *attr | |||
218 | { | 214 | { |
219 | stbuf->f_type = FUSE_SUPER_MAGIC; | 215 | stbuf->f_type = FUSE_SUPER_MAGIC; |
220 | stbuf->f_bsize = attr->bsize; | 216 | stbuf->f_bsize = attr->bsize; |
217 | stbuf->f_frsize = attr->frsize; | ||
221 | stbuf->f_blocks = attr->blocks; | 218 | stbuf->f_blocks = attr->blocks; |
222 | stbuf->f_bfree = attr->bfree; | 219 | stbuf->f_bfree = attr->bfree; |
223 | stbuf->f_bavail = attr->bavail; | 220 | stbuf->f_bavail = attr->bavail; |
@@ -238,10 +235,12 @@ static int fuse_statfs(struct super_block *sb, struct kstatfs *buf) | |||
238 | if (!req) | 235 | if (!req) |
239 | return -EINTR; | 236 | return -EINTR; |
240 | 237 | ||
238 | memset(&outarg, 0, sizeof(outarg)); | ||
241 | req->in.numargs = 0; | 239 | req->in.numargs = 0; |
242 | req->in.h.opcode = FUSE_STATFS; | 240 | req->in.h.opcode = FUSE_STATFS; |
243 | req->out.numargs = 1; | 241 | req->out.numargs = 1; |
244 | req->out.args[0].size = sizeof(outarg); | 242 | req->out.args[0].size = |
243 | fc->minor < 4 ? FUSE_COMPAT_STATFS_SIZE : sizeof(outarg); | ||
245 | req->out.args[0].value = &outarg; | 244 | req->out.args[0].value = &outarg; |
246 | request_send(fc, req); | 245 | request_send(fc, req); |
247 | err = req->out.h.error; | 246 | err = req->out.h.error; |
@@ -482,7 +481,6 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) | |||
482 | fc->max_read = d.max_read; | 481 | fc->max_read = d.max_read; |
483 | if (fc->max_read / PAGE_CACHE_SIZE < fc->bdi.ra_pages) | 482 | if (fc->max_read / PAGE_CACHE_SIZE < fc->bdi.ra_pages) |
484 | fc->bdi.ra_pages = fc->max_read / PAGE_CACHE_SIZE; | 483 | fc->bdi.ra_pages = fc->max_read / PAGE_CACHE_SIZE; |
485 | fc->max_write = FUSE_MAX_IN / 2; | ||
486 | 484 | ||
487 | err = -ENOMEM; | 485 | err = -ENOMEM; |
488 | root = get_root_inode(sb, d.rootmode); | 486 | root = get_root_inode(sb, d.rootmode); |