Commit fd72faac95d7e47610e981d7ed7b3c1529e55c88

Authored by Miklos Szeredi
Committed by Linus Torvalds
1 parent 31d40d74b4

[PATCH] FUSE: atomic create+open

This patch adds an atomic create+open operation.  This does not yet work if
the file type changes between lookup and create+open, but solves the
permission checking problems for the separte create and open methods.

Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

Showing 5 changed files with 214 additions and 50 deletions Side-by-side Diff

... ... @@ -184,6 +184,13 @@
184 184 fuse_putback_request() */
185 185 for (i = 1; i < FUSE_MAX_OUTSTANDING; i++)
186 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 */
  189 + u64 nodeid = req->in.h.nodeid;
  190 + __fuse_get_request(req);
  191 + fuse_reset_request(req);
  192 + fuse_send_forget(fc, req, nodeid, 1);
  193 + putback = 0;
187 194 }
188 195 if (putback)
189 196 fuse_putback_request(fc, req);
... ... @@ -13,6 +13,7 @@
13 13 #include <linux/gfp.h>
14 14 #include <linux/sched.h>
15 15 #include <linux/namei.h>
  16 +#include <linux/mount.h>
16 17  
17 18 static inline unsigned long time_to_jiffies(unsigned long sec,
18 19 unsigned long nsec)
... ... @@ -134,6 +135,101 @@
134 135 entry->d_time = jiffies - 1;
135 136 }
136 137  
  138 +static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
  139 + struct nameidata *nd)
  140 +{
  141 + int err;
  142 + struct inode *inode;
  143 + struct fuse_conn *fc = get_fuse_conn(dir);
  144 + struct fuse_req *req;
  145 + struct fuse_open_in inarg;
  146 + struct fuse_open_out outopen;
  147 + struct fuse_entry_out outentry;
  148 + struct fuse_inode *fi;
  149 + struct fuse_file *ff;
  150 + struct file *file;
  151 + int flags = nd->intent.open.flags - 1;
  152 +
  153 + err = -ENOSYS;
  154 + if (fc->no_create)
  155 + goto out;
  156 +
  157 + err = -ENAMETOOLONG;
  158 + if (entry->d_name.len > FUSE_NAME_MAX)
  159 + goto out;
  160 +
  161 + err = -EINTR;
  162 + req = fuse_get_request(fc);
  163 + if (!req)
  164 + goto out;
  165 +
  166 + ff = fuse_file_alloc();
  167 + if (!ff)
  168 + goto out_put_request;
  169 +
  170 + flags &= ~O_NOCTTY;
  171 + memset(&inarg, 0, sizeof(inarg));
  172 + inarg.flags = flags;
  173 + inarg.mode = mode;
  174 + req->in.h.opcode = FUSE_CREATE;
  175 + req->in.h.nodeid = get_node_id(dir);
  176 + req->inode = dir;
  177 + req->in.numargs = 2;
  178 + req->in.args[0].size = sizeof(inarg);
  179 + req->in.args[0].value = &inarg;
  180 + req->in.args[1].size = entry->d_name.len + 1;
  181 + req->in.args[1].value = entry->d_name.name;
  182 + req->out.numargs = 2;
  183 + req->out.args[0].size = sizeof(outentry);
  184 + req->out.args[0].value = &outentry;
  185 + req->out.args[1].size = sizeof(outopen);
  186 + req->out.args[1].value = &outopen;
  187 + request_send(fc, req);
  188 + err = req->out.h.error;
  189 + if (err) {
  190 + if (err == -ENOSYS)
  191 + fc->no_create = 1;
  192 + goto out_free_ff;
  193 + }
  194 +
  195 + err = -EIO;
  196 + if (!S_ISREG(outentry.attr.mode))
  197 + goto out_free_ff;
  198 +
  199 + inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation,
  200 + &outentry.attr);
  201 + err = -ENOMEM;
  202 + if (!inode) {
  203 + flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
  204 + ff->fh = outopen.fh;
  205 + fuse_send_release(fc, ff, outentry.nodeid, NULL, flags, 0);
  206 + goto out_put_request;
  207 + }
  208 + fuse_put_request(fc, req);
  209 + entry->d_time = time_to_jiffies(outentry.entry_valid,
  210 + outentry.entry_valid_nsec);
  211 + fi = get_fuse_inode(inode);
  212 + fi->i_time = time_to_jiffies(outentry.attr_valid,
  213 + outentry.attr_valid_nsec);
  214 +
  215 + d_instantiate(entry, inode);
  216 + file = lookup_instantiate_filp(nd, entry, generic_file_open);
  217 + if (IS_ERR(file)) {
  218 + ff->fh = outopen.fh;
  219 + fuse_send_release(fc, ff, outentry.nodeid, inode, flags, 0);
  220 + return PTR_ERR(file);
  221 + }
  222 + fuse_finish_open(inode, file, ff, &outopen);
  223 + return 0;
  224 +
  225 + out_free_ff:
  226 + fuse_file_free(ff);
  227 + out_put_request:
  228 + fuse_put_request(fc, req);
  229 + out:
  230 + return err;
  231 +}
  232 +
137 233 static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
138 234 struct inode *dir, struct dentry *entry,
139 235 int mode)
... ... @@ -208,6 +304,12 @@
208 304 static int fuse_create(struct inode *dir, struct dentry *entry, int mode,
209 305 struct nameidata *nd)
210 306 {
  307 + if (nd && (nd->flags & LOOKUP_CREATE)) {
  308 + int err = fuse_create_open(dir, entry, mode, nd);
  309 + if (err != -ENOSYS)
  310 + return err;
  311 + /* Fall back on mknod */
  312 + }
211 313 return fuse_mknod(dir, entry, mode, 0);
212 314 }
213 315  
... ... @@ -767,7 +869,9 @@
767 869 struct nameidata *nd)
768 870 {
769 871 struct inode *inode;
770   - int err = fuse_lookup_iget(dir, entry, &inode);
  872 + int err;
  873 +
  874 + err = fuse_lookup_iget(dir, entry, &inode);
771 875 if (err)
772 876 return ERR_PTR(err);
773 877 if (inode && S_ISDIR(inode->i_mode)) {
... ... @@ -14,11 +14,69 @@
14 14  
15 15 static struct file_operations fuse_direct_io_file_operations;
16 16  
17   -int fuse_open_common(struct inode *inode, struct file *file, int isdir)
  17 +static int fuse_send_open(struct inode *inode, struct file *file, int isdir,
  18 + struct fuse_open_out *outargp)
18 19 {
19 20 struct fuse_conn *fc = get_fuse_conn(inode);
20   - struct fuse_req *req;
21 21 struct fuse_open_in inarg;
  22 + struct fuse_req *req;
  23 + int err;
  24 +
  25 + req = fuse_get_request(fc);
  26 + if (!req)
  27 + return -EINTR;
  28 +
  29 + memset(&inarg, 0, sizeof(inarg));
  30 + inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
  31 + req->in.h.opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN;
  32 + req->in.h.nodeid = get_node_id(inode);
  33 + req->inode = inode;
  34 + req->in.numargs = 1;
  35 + req->in.args[0].size = sizeof(inarg);
  36 + req->in.args[0].value = &inarg;
  37 + req->out.numargs = 1;
  38 + req->out.args[0].size = sizeof(*outargp);
  39 + req->out.args[0].value = outargp;
  40 + request_send(fc, req);
  41 + err = req->out.h.error;
  42 + fuse_put_request(fc, req);
  43 +
  44 + return err;
  45 +}
  46 +
  47 +struct fuse_file *fuse_file_alloc(void)
  48 +{
  49 + struct fuse_file *ff;
  50 + ff = kmalloc(sizeof(struct fuse_file), GFP_KERNEL);
  51 + if (ff) {
  52 + ff->release_req = fuse_request_alloc();
  53 + if (!ff->release_req) {
  54 + kfree(ff);
  55 + ff = NULL;
  56 + }
  57 + }
  58 + return ff;
  59 +}
  60 +
  61 +void fuse_file_free(struct fuse_file *ff)
  62 +{
  63 + fuse_request_free(ff->release_req);
  64 + kfree(ff);
  65 +}
  66 +
  67 +void fuse_finish_open(struct inode *inode, struct file *file,
  68 + struct fuse_file *ff, struct fuse_open_out *outarg)
  69 +{
  70 + if (outarg->open_flags & FOPEN_DIRECT_IO)
  71 + file->f_op = &fuse_direct_io_file_operations;
  72 + if (!(outarg->open_flags & FOPEN_KEEP_CACHE))
  73 + invalidate_inode_pages(inode->i_mapping);
  74 + ff->fh = outarg->fh;
  75 + file->private_data = ff;
  76 +}
  77 +
  78 +int fuse_open_common(struct inode *inode, struct file *file, int isdir)
  79 +{
22 80 struct fuse_open_out outarg;
23 81 struct fuse_file *ff;
24 82 int err;
25 83  
26 84  
27 85  
28 86  
29 87  
30 88  
31 89  
32 90  
33 91  
... ... @@ -34,73 +92,53 @@
34 92 /* If opening the root node, no lookup has been performed on
35 93 it, so the attributes must be refreshed */
36 94 if (get_node_id(inode) == FUSE_ROOT_ID) {
37   - int err = fuse_do_getattr(inode);
  95 + err = fuse_do_getattr(inode);
38 96 if (err)
39 97 return err;
40 98 }
41 99  
42   - req = fuse_get_request(fc);
43   - if (!req)
44   - return -EINTR;
45   -
46   - err = -ENOMEM;
47   - ff = kmalloc(sizeof(struct fuse_file), GFP_KERNEL);
  100 + ff = fuse_file_alloc();
48 101 if (!ff)
49   - goto out_put_request;
  102 + return -ENOMEM;
50 103  
51   - ff->release_req = fuse_request_alloc();
52   - if (!ff->release_req) {
53   - kfree(ff);
54   - goto out_put_request;
  104 + err = fuse_send_open(inode, file, isdir, &outarg);
  105 + if (err)
  106 + fuse_file_free(ff);
  107 + else {
  108 + if (isdir)
  109 + outarg.open_flags &= ~FOPEN_DIRECT_IO;
  110 + fuse_finish_open(inode, file, ff, &outarg);
55 111 }
56 112  
57   - memset(&inarg, 0, sizeof(inarg));
58   - inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
59   - req->in.h.opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN;
60   - req->in.h.nodeid = get_node_id(inode);
61   - req->inode = inode;
62   - req->in.numargs = 1;
63   - req->in.args[0].size = sizeof(inarg);
64   - req->in.args[0].value = &inarg;
65   - req->out.numargs = 1;
66   - req->out.args[0].size = sizeof(outarg);
67   - req->out.args[0].value = &outarg;
68   - request_send(fc, req);
69   - err = req->out.h.error;
70   - if (err) {
71   - fuse_request_free(ff->release_req);
72   - kfree(ff);
73   - } else {
74   - if (!isdir && (outarg.open_flags & FOPEN_DIRECT_IO))
75   - file->f_op = &fuse_direct_io_file_operations;
76   - if (!(outarg.open_flags & FOPEN_KEEP_CACHE))
77   - invalidate_inode_pages(inode->i_mapping);
78   - ff->fh = outarg.fh;
79   - file->private_data = ff;
80   - }
81   -
82   - out_put_request:
83   - fuse_put_request(fc, req);
84 113 return err;
85 114 }
86 115  
87   -int fuse_release_common(struct inode *inode, struct file *file, int isdir)
  116 +void fuse_send_release(struct fuse_conn *fc, struct fuse_file *ff,
  117 + u64 nodeid, struct inode *inode, int flags, int isdir)
88 118 {
89   - struct fuse_conn *fc = get_fuse_conn(inode);
90   - struct fuse_file *ff = file->private_data;
91   - struct fuse_req *req = ff->release_req;
  119 + struct fuse_req * req = ff->release_req;
92 120 struct fuse_release_in *inarg = &req->misc.release_in;
93 121  
94 122 inarg->fh = ff->fh;
95   - inarg->flags = file->f_flags & ~O_EXCL;
  123 + inarg->flags = flags;
96 124 req->in.h.opcode = isdir ? FUSE_RELEASEDIR : FUSE_RELEASE;
97   - req->in.h.nodeid = get_node_id(inode);
  125 + req->in.h.nodeid = nodeid;
98 126 req->inode = inode;
99 127 req->in.numargs = 1;
100 128 req->in.args[0].size = sizeof(struct fuse_release_in);
101 129 req->in.args[0].value = inarg;
102 130 request_send_background(fc, req);
103 131 kfree(ff);
  132 +}
  133 +
  134 +int fuse_release_common(struct inode *inode, struct file *file, int isdir)
  135 +{
  136 + struct fuse_file *ff = file->private_data;
  137 + if (ff) {
  138 + struct fuse_conn *fc = get_fuse_conn(inode);
  139 + u64 nodeid = get_node_id(inode);
  140 + fuse_send_release(fc, ff, nodeid, inode, file->f_flags, isdir);
  141 + }
104 142  
105 143 /* Return value is ignored by VFS */
106 144 return 0;
... ... @@ -269,6 +269,9 @@
269 269 /** Is access not implemented by fs? */
270 270 unsigned no_access : 1;
271 271  
  272 + /** Is create not implemented by fs? */
  273 + unsigned no_create : 1;
  274 +
272 275 /** Backing dev info */
273 276 struct backing_dev_info bdi;
274 277 };
... ... @@ -339,6 +342,17 @@
339 342 * Send OPEN or OPENDIR request
340 343 */
341 344 int fuse_open_common(struct inode *inode, struct file *file, int isdir);
  345 +
  346 +struct fuse_file *fuse_file_alloc(void);
  347 +void fuse_file_free(struct fuse_file *ff);
  348 +void fuse_finish_open(struct inode *inode, struct file *file,
  349 + struct fuse_file *ff, struct fuse_open_out *outarg);
  350 +
  351 +/**
  352 + * Send a RELEASE request
  353 + */
  354 +void fuse_send_release(struct fuse_conn *fc, struct fuse_file *ff,
  355 + u64 nodeid, struct inode *inode, int flags, int isdir);
342 356  
343 357 /**
344 358 * Send RELEASE or RELEASEDIR request
include/linux/fuse.h
... ... @@ -100,7 +100,8 @@
100 100 FUSE_READDIR = 28,
101 101 FUSE_RELEASEDIR = 29,
102 102 FUSE_FSYNCDIR = 30,
103   - FUSE_ACCESS = 34
  103 + FUSE_ACCESS = 34,
  104 + FUSE_CREATE = 35
104 105 };
105 106  
106 107 /* Conservative buffer size for the client */
... ... @@ -158,7 +159,7 @@
158 159  
159 160 struct fuse_open_in {
160 161 __u32 flags;
161   - __u32 padding;
  162 + __u32 mode;
162 163 };
163 164  
164 165 struct fuse_open_out {