diff options
| -rw-r--r-- | fs/fuse/dir.c | 117 |
1 files changed, 77 insertions, 40 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index be5450dd6387..51d0035ff07e 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
| @@ -112,18 +112,16 @@ static void fuse_invalidate_entry(struct dentry *entry) | |||
| 112 | fuse_invalidate_entry_cache(entry); | 112 | fuse_invalidate_entry_cache(entry); |
| 113 | } | 113 | } |
| 114 | 114 | ||
| 115 | static void fuse_lookup_init(struct fuse_req *req, struct inode *dir, | 115 | static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_req *req, |
| 116 | struct dentry *entry, | 116 | u64 nodeid, struct qstr *name, |
| 117 | struct fuse_entry_out *outarg) | 117 | struct fuse_entry_out *outarg) |
| 118 | { | 118 | { |
| 119 | struct fuse_conn *fc = get_fuse_conn(dir); | ||
| 120 | |||
| 121 | memset(outarg, 0, sizeof(struct fuse_entry_out)); | 119 | memset(outarg, 0, sizeof(struct fuse_entry_out)); |
| 122 | req->in.h.opcode = FUSE_LOOKUP; | 120 | req->in.h.opcode = FUSE_LOOKUP; |
| 123 | req->in.h.nodeid = get_node_id(dir); | 121 | req->in.h.nodeid = nodeid; |
| 124 | req->in.numargs = 1; | 122 | req->in.numargs = 1; |
| 125 | req->in.args[0].size = entry->d_name.len + 1; | 123 | req->in.args[0].size = name->len + 1; |
| 126 | req->in.args[0].value = entry->d_name.name; | 124 | req->in.args[0].value = name->name; |
| 127 | req->out.numargs = 1; | 125 | req->out.numargs = 1; |
| 128 | if (fc->minor < 9) | 126 | if (fc->minor < 9) |
| 129 | req->out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE; | 127 | req->out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE; |
| @@ -189,7 +187,8 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) | |||
| 189 | attr_version = fuse_get_attr_version(fc); | 187 | attr_version = fuse_get_attr_version(fc); |
| 190 | 188 | ||
| 191 | parent = dget_parent(entry); | 189 | parent = dget_parent(entry); |
| 192 | fuse_lookup_init(req, parent->d_inode, entry, &outarg); | 190 | fuse_lookup_init(fc, req, get_node_id(parent->d_inode), |
| 191 | &entry->d_name, &outarg); | ||
| 193 | request_send(fc, req); | 192 | request_send(fc, req); |
| 194 | dput(parent); | 193 | dput(parent); |
| 195 | err = req->out.h.error; | 194 | err = req->out.h.error; |
| @@ -255,73 +254,111 @@ static struct dentry *fuse_d_add_directory(struct dentry *entry, | |||
| 255 | return d_splice_alias(inode, entry); | 254 | return d_splice_alias(inode, entry); |
| 256 | } | 255 | } |
| 257 | 256 | ||
| 258 | static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | 257 | int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct qstr *name, |
| 259 | struct nameidata *nd) | 258 | struct fuse_entry_out *outarg, struct inode **inode) |
| 260 | { | 259 | { |
| 261 | int err; | 260 | struct fuse_conn *fc = get_fuse_conn_super(sb); |
| 262 | struct fuse_entry_out outarg; | ||
| 263 | struct inode *inode = NULL; | ||
| 264 | struct dentry *newent; | ||
| 265 | struct fuse_conn *fc = get_fuse_conn(dir); | ||
| 266 | struct fuse_req *req; | 261 | struct fuse_req *req; |
| 267 | struct fuse_req *forget_req; | 262 | struct fuse_req *forget_req; |
| 268 | u64 attr_version; | 263 | u64 attr_version; |
| 264 | int err; | ||
| 269 | 265 | ||
| 270 | if (entry->d_name.len > FUSE_NAME_MAX) | 266 | *inode = NULL; |
| 271 | return ERR_PTR(-ENAMETOOLONG); | 267 | err = -ENAMETOOLONG; |
| 268 | if (name->len > FUSE_NAME_MAX) | ||
| 269 | goto out; | ||
| 272 | 270 | ||
| 273 | req = fuse_get_req(fc); | 271 | req = fuse_get_req(fc); |
| 272 | err = PTR_ERR(req); | ||
| 274 | if (IS_ERR(req)) | 273 | if (IS_ERR(req)) |
| 275 | return ERR_CAST(req); | 274 | goto out; |
| 276 | 275 | ||
| 277 | forget_req = fuse_get_req(fc); | 276 | forget_req = fuse_get_req(fc); |
| 277 | err = PTR_ERR(forget_req); | ||
| 278 | if (IS_ERR(forget_req)) { | 278 | if (IS_ERR(forget_req)) { |
| 279 | fuse_put_request(fc, req); | 279 | fuse_put_request(fc, req); |
| 280 | return ERR_CAST(forget_req); | 280 | goto out; |
| 281 | } | 281 | } |
| 282 | 282 | ||
| 283 | attr_version = fuse_get_attr_version(fc); | 283 | attr_version = fuse_get_attr_version(fc); |
| 284 | 284 | ||
| 285 | fuse_lookup_init(req, dir, entry, &outarg); | 285 | fuse_lookup_init(fc, req, nodeid, name, outarg); |
| 286 | request_send(fc, req); | 286 | request_send(fc, req); |
| 287 | err = req->out.h.error; | 287 | err = req->out.h.error; |
| 288 | fuse_put_request(fc, req); | 288 | fuse_put_request(fc, req); |
| 289 | /* Zero nodeid is same as -ENOENT, but with valid timeout */ | 289 | /* Zero nodeid is same as -ENOENT, but with valid timeout */ |
| 290 | if (!err && outarg.nodeid && | 290 | if (err || !outarg->nodeid) |
| 291 | (invalid_nodeid(outarg.nodeid) || | 291 | goto out_put_forget; |
| 292 | !fuse_valid_type(outarg.attr.mode))) | 292 | |
| 293 | err = -EIO; | 293 | err = -EIO; |
| 294 | if (!err && outarg.nodeid) { | 294 | if (!outarg->nodeid) |
| 295 | inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, | 295 | goto out_put_forget; |
| 296 | &outarg.attr, entry_attr_timeout(&outarg), | 296 | if (!fuse_valid_type(outarg->attr.mode)) |
| 297 | attr_version); | 297 | goto out_put_forget; |
| 298 | if (!inode) { | 298 | |
| 299 | fuse_send_forget(fc, forget_req, outarg.nodeid, 1); | 299 | *inode = fuse_iget(sb, outarg->nodeid, outarg->generation, |
| 300 | return ERR_PTR(-ENOMEM); | 300 | &outarg->attr, entry_attr_timeout(outarg), |
| 301 | } | 301 | attr_version); |
| 302 | err = -ENOMEM; | ||
| 303 | if (!*inode) { | ||
| 304 | fuse_send_forget(fc, forget_req, outarg->nodeid, 1); | ||
| 305 | goto out; | ||
| 302 | } | 306 | } |
| 307 | err = 0; | ||
| 308 | |||
| 309 | out_put_forget: | ||
| 303 | fuse_put_request(fc, forget_req); | 310 | fuse_put_request(fc, forget_req); |
| 304 | if (err && err != -ENOENT) | 311 | out: |
| 305 | return ERR_PTR(err); | 312 | return err; |
| 313 | } | ||
| 314 | |||
| 315 | static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | ||
| 316 | struct nameidata *nd) | ||
| 317 | { | ||
| 318 | int err; | ||
| 319 | struct fuse_entry_out outarg; | ||
| 320 | struct inode *inode; | ||
| 321 | struct dentry *newent; | ||
| 322 | struct fuse_conn *fc = get_fuse_conn(dir); | ||
| 323 | bool outarg_valid = true; | ||
| 324 | |||
| 325 | err = fuse_lookup_name(dir->i_sb, get_node_id(dir), &entry->d_name, | ||
| 326 | &outarg, &inode); | ||
| 327 | if (err == -ENOENT) { | ||
| 328 | outarg_valid = false; | ||
| 329 | err = 0; | ||
| 330 | } | ||
| 331 | if (err) | ||
| 332 | goto out_err; | ||
| 333 | |||
| 334 | err = -EIO; | ||
| 335 | if (inode && get_node_id(inode) == FUSE_ROOT_ID) | ||
| 336 | goto out_iput; | ||
| 306 | 337 | ||
| 307 | if (inode && S_ISDIR(inode->i_mode)) { | 338 | if (inode && S_ISDIR(inode->i_mode)) { |
| 308 | mutex_lock(&fc->inst_mutex); | 339 | mutex_lock(&fc->inst_mutex); |
| 309 | newent = fuse_d_add_directory(entry, inode); | 340 | newent = fuse_d_add_directory(entry, inode); |
| 310 | mutex_unlock(&fc->inst_mutex); | 341 | mutex_unlock(&fc->inst_mutex); |
| 311 | if (IS_ERR(newent)) { | 342 | err = PTR_ERR(newent); |
| 312 | iput(inode); | 343 | if (IS_ERR(newent)) |
| 313 | return newent; | 344 | goto out_iput; |
| 314 | } | 345 | } else { |
| 315 | } else | ||
| 316 | newent = d_splice_alias(inode, entry); | 346 | newent = d_splice_alias(inode, entry); |
| 347 | } | ||
| 317 | 348 | ||
| 318 | entry = newent ? newent : entry; | 349 | entry = newent ? newent : entry; |
| 319 | entry->d_op = &fuse_dentry_operations; | 350 | entry->d_op = &fuse_dentry_operations; |
| 320 | if (!err) | 351 | if (outarg_valid) |
| 321 | fuse_change_entry_timeout(entry, &outarg); | 352 | fuse_change_entry_timeout(entry, &outarg); |
| 322 | else | 353 | else |
| 323 | fuse_invalidate_entry_cache(entry); | 354 | fuse_invalidate_entry_cache(entry); |
| 355 | |||
| 324 | return newent; | 356 | return newent; |
| 357 | |||
| 358 | out_iput: | ||
| 359 | iput(inode); | ||
| 360 | out_err: | ||
| 361 | return ERR_PTR(err); | ||
| 325 | } | 362 | } |
| 326 | 363 | ||
| 327 | /* | 364 | /* |
