diff options
Diffstat (limited to 'fs/fuse/dir.c')
| -rw-r--r-- | fs/fuse/dir.c | 145 |
1 files changed, 93 insertions, 52 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 2060bf06b906..fd03330cadeb 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
| @@ -97,7 +97,7 @@ void fuse_invalidate_attr(struct inode *inode) | |||
| 97 | * timeout is unknown (unlink, rmdir, rename and in some cases | 97 | * timeout is unknown (unlink, rmdir, rename and in some cases |
| 98 | * lookup) | 98 | * lookup) |
| 99 | */ | 99 | */ |
| 100 | static void fuse_invalidate_entry_cache(struct dentry *entry) | 100 | void fuse_invalidate_entry_cache(struct dentry *entry) |
| 101 | { | 101 | { |
| 102 | fuse_dentry_settime(entry, 0); | 102 | fuse_dentry_settime(entry, 0); |
| 103 | } | 103 | } |
| @@ -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; |
| @@ -225,7 +224,7 @@ static int invalid_nodeid(u64 nodeid) | |||
| 225 | return !nodeid || nodeid == FUSE_ROOT_ID; | 224 | return !nodeid || nodeid == FUSE_ROOT_ID; |
| 226 | } | 225 | } |
| 227 | 226 | ||
| 228 | static struct dentry_operations fuse_dentry_operations = { | 227 | struct dentry_operations fuse_dentry_operations = { |
| 229 | .d_revalidate = fuse_dentry_revalidate, | 228 | .d_revalidate = fuse_dentry_revalidate, |
| 230 | }; | 229 | }; |
| 231 | 230 | ||
| @@ -239,85 +238,127 @@ int fuse_valid_type(int m) | |||
| 239 | * Add a directory inode to a dentry, ensuring that no other dentry | 238 | * Add a directory inode to a dentry, ensuring that no other dentry |
| 240 | * refers to this inode. Called with fc->inst_mutex. | 239 | * refers to this inode. Called with fc->inst_mutex. |
| 241 | */ | 240 | */ |
| 242 | static int fuse_d_add_directory(struct dentry *entry, struct inode *inode) | 241 | static struct dentry *fuse_d_add_directory(struct dentry *entry, |
| 242 | struct inode *inode) | ||
| 243 | { | 243 | { |
| 244 | struct dentry *alias = d_find_alias(inode); | 244 | struct dentry *alias = d_find_alias(inode); |
| 245 | if (alias) { | 245 | if (alias && !(alias->d_flags & DCACHE_DISCONNECTED)) { |
| 246 | /* This tries to shrink the subtree below alias */ | 246 | /* This tries to shrink the subtree below alias */ |
| 247 | fuse_invalidate_entry(alias); | 247 | fuse_invalidate_entry(alias); |
| 248 | dput(alias); | 248 | dput(alias); |
| 249 | if (!list_empty(&inode->i_dentry)) | 249 | if (!list_empty(&inode->i_dentry)) |
| 250 | return -EBUSY; | 250 | return ERR_PTR(-EBUSY); |
| 251 | } else { | ||
| 252 | dput(alias); | ||
| 251 | } | 253 | } |
| 252 | d_add(entry, inode); | 254 | return d_splice_alias(inode, entry); |
| 253 | return 0; | ||
| 254 | } | 255 | } |
| 255 | 256 | ||
| 256 | 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, |
| 257 | struct nameidata *nd) | 258 | struct fuse_entry_out *outarg, struct inode **inode) |
| 258 | { | 259 | { |
| 259 | int err; | 260 | struct fuse_conn *fc = get_fuse_conn_super(sb); |
| 260 | struct fuse_entry_out outarg; | ||
| 261 | struct inode *inode = NULL; | ||
| 262 | struct fuse_conn *fc = get_fuse_conn(dir); | ||
| 263 | struct fuse_req *req; | 261 | struct fuse_req *req; |
| 264 | struct fuse_req *forget_req; | 262 | struct fuse_req *forget_req; |
| 265 | u64 attr_version; | 263 | u64 attr_version; |
| 264 | int err; | ||
| 266 | 265 | ||
| 267 | if (entry->d_name.len > FUSE_NAME_MAX) | 266 | *inode = NULL; |
| 268 | return ERR_PTR(-ENAMETOOLONG); | 267 | err = -ENAMETOOLONG; |
| 268 | if (name->len > FUSE_NAME_MAX) | ||
| 269 | goto out; | ||
| 269 | 270 | ||
| 270 | req = fuse_get_req(fc); | 271 | req = fuse_get_req(fc); |
| 272 | err = PTR_ERR(req); | ||
| 271 | if (IS_ERR(req)) | 273 | if (IS_ERR(req)) |
| 272 | return ERR_CAST(req); | 274 | goto out; |
| 273 | 275 | ||
| 274 | forget_req = fuse_get_req(fc); | 276 | forget_req = fuse_get_req(fc); |
| 277 | err = PTR_ERR(forget_req); | ||
| 275 | if (IS_ERR(forget_req)) { | 278 | if (IS_ERR(forget_req)) { |
| 276 | fuse_put_request(fc, req); | 279 | fuse_put_request(fc, req); |
| 277 | return ERR_CAST(forget_req); | 280 | goto out; |
| 278 | } | 281 | } |
| 279 | 282 | ||
| 280 | attr_version = fuse_get_attr_version(fc); | 283 | attr_version = fuse_get_attr_version(fc); |
| 281 | 284 | ||
| 282 | fuse_lookup_init(req, dir, entry, &outarg); | 285 | fuse_lookup_init(fc, req, nodeid, name, outarg); |
| 283 | request_send(fc, req); | 286 | request_send(fc, req); |
| 284 | err = req->out.h.error; | 287 | err = req->out.h.error; |
| 285 | fuse_put_request(fc, req); | 288 | fuse_put_request(fc, req); |
| 286 | /* Zero nodeid is same as -ENOENT, but with valid timeout */ | 289 | /* Zero nodeid is same as -ENOENT, but with valid timeout */ |
| 287 | if (!err && outarg.nodeid && | 290 | if (err || !outarg->nodeid) |
| 288 | (invalid_nodeid(outarg.nodeid) || | 291 | goto out_put_forget; |
| 289 | !fuse_valid_type(outarg.attr.mode))) | 292 | |
| 290 | err = -EIO; | 293 | err = -EIO; |
| 291 | if (!err && outarg.nodeid) { | 294 | if (!outarg->nodeid) |
| 292 | inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, | 295 | goto out_put_forget; |
| 293 | &outarg.attr, entry_attr_timeout(&outarg), | 296 | if (!fuse_valid_type(outarg->attr.mode)) |
| 294 | attr_version); | 297 | goto out_put_forget; |
| 295 | if (!inode) { | 298 | |
| 296 | fuse_send_forget(fc, forget_req, outarg.nodeid, 1); | 299 | *inode = fuse_iget(sb, outarg->nodeid, outarg->generation, |
| 297 | return ERR_PTR(-ENOMEM); | 300 | &outarg->attr, entry_attr_timeout(outarg), |
| 298 | } | 301 | attr_version); |
| 302 | err = -ENOMEM; | ||
| 303 | if (!*inode) { | ||
| 304 | fuse_send_forget(fc, forget_req, outarg->nodeid, 1); | ||
| 305 | goto out; | ||
| 299 | } | 306 | } |
| 307 | err = 0; | ||
| 308 | |||
| 309 | out_put_forget: | ||
| 300 | fuse_put_request(fc, forget_req); | 310 | fuse_put_request(fc, forget_req); |
| 301 | if (err && err != -ENOENT) | 311 | out: |
| 302 | 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; | ||
| 303 | 337 | ||
| 304 | if (inode && S_ISDIR(inode->i_mode)) { | 338 | if (inode && S_ISDIR(inode->i_mode)) { |
| 305 | mutex_lock(&fc->inst_mutex); | 339 | mutex_lock(&fc->inst_mutex); |
| 306 | err = fuse_d_add_directory(entry, inode); | 340 | newent = fuse_d_add_directory(entry, inode); |
| 307 | mutex_unlock(&fc->inst_mutex); | 341 | mutex_unlock(&fc->inst_mutex); |
| 308 | if (err) { | 342 | err = PTR_ERR(newent); |
| 309 | iput(inode); | 343 | if (IS_ERR(newent)) |
| 310 | return ERR_PTR(err); | 344 | goto out_iput; |
| 311 | } | 345 | } else { |
| 312 | } else | 346 | newent = d_splice_alias(inode, entry); |
| 313 | d_add(entry, inode); | 347 | } |
| 314 | 348 | ||
| 349 | entry = newent ? newent : entry; | ||
| 315 | entry->d_op = &fuse_dentry_operations; | 350 | entry->d_op = &fuse_dentry_operations; |
| 316 | if (!err) | 351 | if (outarg_valid) |
| 317 | fuse_change_entry_timeout(entry, &outarg); | 352 | fuse_change_entry_timeout(entry, &outarg); |
| 318 | else | 353 | else |
| 319 | fuse_invalidate_entry_cache(entry); | 354 | fuse_invalidate_entry_cache(entry); |
| 320 | return NULL; | 355 | |
| 356 | return newent; | ||
| 357 | |||
| 358 | out_iput: | ||
| 359 | iput(inode); | ||
| 360 | out_err: | ||
| 361 | return ERR_PTR(err); | ||
| 321 | } | 362 | } |
| 322 | 363 | ||
| 323 | /* | 364 | /* |
| @@ -857,7 +898,7 @@ static int fuse_access(struct inode *inode, int mask) | |||
| 857 | return PTR_ERR(req); | 898 | return PTR_ERR(req); |
| 858 | 899 | ||
| 859 | memset(&inarg, 0, sizeof(inarg)); | 900 | memset(&inarg, 0, sizeof(inarg)); |
| 860 | inarg.mask = mask; | 901 | inarg.mask = mask & (MAY_READ | MAY_WRITE | MAY_EXEC); |
| 861 | req->in.h.opcode = FUSE_ACCESS; | 902 | req->in.h.opcode = FUSE_ACCESS; |
| 862 | req->in.h.nodeid = get_node_id(inode); | 903 | req->in.h.nodeid = get_node_id(inode); |
| 863 | req->in.numargs = 1; | 904 | req->in.numargs = 1; |
| @@ -886,7 +927,7 @@ static int fuse_access(struct inode *inode, int mask) | |||
| 886 | * access request is sent. Execute permission is still checked | 927 | * access request is sent. Execute permission is still checked |
| 887 | * locally based on file mode. | 928 | * locally based on file mode. |
| 888 | */ | 929 | */ |
| 889 | static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) | 930 | static int fuse_permission(struct inode *inode, int mask) |
| 890 | { | 931 | { |
| 891 | struct fuse_conn *fc = get_fuse_conn(inode); | 932 | struct fuse_conn *fc = get_fuse_conn(inode); |
| 892 | bool refreshed = false; | 933 | bool refreshed = false; |
| @@ -921,7 +962,7 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) | |||
| 921 | exist. So if permissions are revoked this won't be | 962 | exist. So if permissions are revoked this won't be |
| 922 | noticed immediately, only after the attribute | 963 | noticed immediately, only after the attribute |
| 923 | timeout has expired */ | 964 | timeout has expired */ |
| 924 | } else if (nd && (nd->flags & (LOOKUP_ACCESS | LOOKUP_CHDIR))) { | 965 | } else if (mask & MAY_ACCESS) { |
| 925 | err = fuse_access(inode, mask); | 966 | err = fuse_access(inode, mask); |
| 926 | } else if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) { | 967 | } else if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) { |
| 927 | if (!(inode->i_mode & S_IXUGO)) { | 968 | if (!(inode->i_mode & S_IXUGO)) { |
