diff options
author | David Howells <dhowells@redhat.com> | 2007-04-26 18:59:35 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2007-04-26 18:59:35 -0400 |
commit | 260a980317dac80182dd76140cf67c6e81d6d3dd (patch) | |
tree | 84f3e919fd33be56aad4fc57f5cb844df1a6b952 | |
parent | c35eccb1f614954b10cba3f74b7c301993b2f42e (diff) |
[AFS]: Add "directory write" support.
Add support for the create, link, symlink, unlink, mkdir, rmdir and
rename VFS operations to the in-kernel AFS filesystem.
Also:
(1) Fix dentry and inode revalidation. d_revalidate should only look at
state of the dentry. Revalidation of the contents of an inode pointed to
by a dentry is now separate.
(2) Fix afs_lookup() to hash negative dentries as well as positive ones.
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | fs/afs/afs.h | 26 | ||||
-rw-r--r-- | fs/afs/afs_fs.h | 11 | ||||
-rw-r--r-- | fs/afs/callback.c | 36 | ||||
-rw-r--r-- | fs/afs/dir.c | 676 | ||||
-rw-r--r-- | fs/afs/file.c | 7 | ||||
-rw-r--r-- | fs/afs/fsclient.c | 625 | ||||
-rw-r--r-- | fs/afs/inode.c | 115 | ||||
-rw-r--r-- | fs/afs/internal.h | 54 | ||||
-rw-r--r-- | fs/afs/misc.c | 21 | ||||
-rw-r--r-- | fs/afs/security.c | 31 | ||||
-rw-r--r-- | fs/afs/server.c | 2 | ||||
-rw-r--r-- | fs/afs/super.c | 6 | ||||
-rw-r--r-- | fs/afs/vnode.c | 422 | ||||
-rw-r--r-- | fs/afs/volume.c | 14 |
14 files changed, 1778 insertions, 268 deletions
diff --git a/fs/afs/afs.h b/fs/afs/afs.h index d959092aaf4b..52d0752265b8 100644 --- a/fs/afs/afs.h +++ b/fs/afs/afs.h | |||
@@ -106,19 +106,37 @@ struct afs_file_status { | |||
106 | 106 | ||
107 | afs_file_type_t type; /* file type */ | 107 | afs_file_type_t type; /* file type */ |
108 | unsigned nlink; /* link count */ | 108 | unsigned nlink; /* link count */ |
109 | size_t size; /* file size */ | 109 | u64 size; /* file size */ |
110 | afs_dataversion_t data_version; /* current data version */ | 110 | afs_dataversion_t data_version; /* current data version */ |
111 | unsigned author; /* author ID */ | 111 | u32 author; /* author ID */ |
112 | unsigned owner; /* owner ID */ | 112 | u32 owner; /* owner ID */ |
113 | u32 group; /* group ID */ | ||
113 | afs_access_t caller_access; /* access rights for authenticated caller */ | 114 | afs_access_t caller_access; /* access rights for authenticated caller */ |
114 | afs_access_t anon_access; /* access rights for unauthenticated caller */ | 115 | afs_access_t anon_access; /* access rights for unauthenticated caller */ |
115 | umode_t mode; /* UNIX mode */ | 116 | umode_t mode; /* UNIX mode */ |
116 | struct afs_fid parent; /* parent file ID */ | 117 | struct afs_fid parent; /* parent dir ID for non-dirs only */ |
117 | time_t mtime_client; /* last time client changed data */ | 118 | time_t mtime_client; /* last time client changed data */ |
118 | time_t mtime_server; /* last time server changed data */ | 119 | time_t mtime_server; /* last time server changed data */ |
119 | }; | 120 | }; |
120 | 121 | ||
121 | /* | 122 | /* |
123 | * AFS file status change request | ||
124 | */ | ||
125 | struct afs_store_status { | ||
126 | u32 mask; /* which bits of the struct are set */ | ||
127 | u32 mtime_client; /* last time client changed data */ | ||
128 | u32 owner; /* owner ID */ | ||
129 | u32 group; /* group ID */ | ||
130 | umode_t mode; /* UNIX mode */ | ||
131 | }; | ||
132 | |||
133 | #define AFS_SET_MTIME 0x01 /* set the mtime */ | ||
134 | #define AFS_SET_OWNER 0x02 /* set the owner ID */ | ||
135 | #define AFS_SET_GROUP 0x04 /* set the group ID (unsupported?) */ | ||
136 | #define AFS_SET_MODE 0x08 /* set the UNIX mode */ | ||
137 | #define AFS_SET_SEG_SIZE 0x10 /* set the segment size (unsupported) */ | ||
138 | |||
139 | /* | ||
122 | * AFS volume synchronisation information | 140 | * AFS volume synchronisation information |
123 | */ | 141 | */ |
124 | struct afs_volsync { | 142 | struct afs_volsync { |
diff --git a/fs/afs/afs_fs.h b/fs/afs/afs_fs.h index fd385954f21f..89e0d1650a72 100644 --- a/fs/afs/afs_fs.h +++ b/fs/afs/afs_fs.h | |||
@@ -16,12 +16,19 @@ | |||
16 | #define FS_SERVICE 1 /* AFS File Service ID */ | 16 | #define FS_SERVICE 1 /* AFS File Service ID */ |
17 | 17 | ||
18 | enum AFS_FS_Operations { | 18 | enum AFS_FS_Operations { |
19 | FSFETCHSTATUS = 132, /* AFS Fetch file status */ | ||
20 | FSFETCHDATA = 130, /* AFS Fetch file data */ | 19 | FSFETCHDATA = 130, /* AFS Fetch file data */ |
20 | FSFETCHSTATUS = 132, /* AFS Fetch file status */ | ||
21 | FSREMOVEFILE = 136, /* AFS Remove a file */ | ||
22 | FSCREATEFILE = 137, /* AFS Create a file */ | ||
23 | FSRENAME = 138, /* AFS Rename or move a file or directory */ | ||
24 | FSSYMLINK = 139, /* AFS Create a symbolic link */ | ||
25 | FSLINK = 140, /* AFS Create a hard link */ | ||
26 | FSMAKEDIR = 141, /* AFS Create a directory */ | ||
27 | FSREMOVEDIR = 142, /* AFS Remove a directory */ | ||
21 | FSGIVEUPCALLBACKS = 147, /* AFS Discard callback promises */ | 28 | FSGIVEUPCALLBACKS = 147, /* AFS Discard callback promises */ |
22 | FSGETVOLUMEINFO = 148, /* AFS Get root volume information */ | 29 | FSGETVOLUMEINFO = 148, /* AFS Get root volume information */ |
23 | FSGETROOTVOLUME = 151, /* AFS Get root volume name */ | 30 | FSGETROOTVOLUME = 151, /* AFS Get root volume name */ |
24 | FSLOOKUP = 161 /* AFS lookup file in directory */ | 31 | FSLOOKUP = 161, /* AFS lookup file in directory */ |
25 | }; | 32 | }; |
26 | 33 | ||
27 | enum AFS_FS_Errors { | 34 | enum AFS_FS_Errors { |
diff --git a/fs/afs/callback.c b/fs/afs/callback.c index e674bebbb8b1..639399f0ab6f 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c | |||
@@ -44,7 +44,8 @@ void afs_init_callback_state(struct afs_server *server) | |||
44 | while (!RB_EMPTY_ROOT(&server->cb_promises)) { | 44 | while (!RB_EMPTY_ROOT(&server->cb_promises)) { |
45 | vnode = rb_entry(server->cb_promises.rb_node, | 45 | vnode = rb_entry(server->cb_promises.rb_node, |
46 | struct afs_vnode, cb_promise); | 46 | struct afs_vnode, cb_promise); |
47 | printk("\nUNPROMISE on %p\n", vnode); | 47 | _debug("UNPROMISE { vid=%x vn=%u uq=%u}", |
48 | vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique); | ||
48 | rb_erase(&vnode->cb_promise, &server->cb_promises); | 49 | rb_erase(&vnode->cb_promise, &server->cb_promises); |
49 | vnode->cb_promised = false; | 50 | vnode->cb_promised = false; |
50 | } | 51 | } |
@@ -68,7 +69,7 @@ void afs_broken_callback_work(struct work_struct *work) | |||
68 | 69 | ||
69 | /* we're only interested in dealing with a broken callback on *this* | 70 | /* we're only interested in dealing with a broken callback on *this* |
70 | * vnode and only if no-one else has dealt with it yet */ | 71 | * vnode and only if no-one else has dealt with it yet */ |
71 | if (!mutex_trylock(&vnode->cb_broken_lock)) | 72 | if (!mutex_trylock(&vnode->validate_lock)) |
72 | return; /* someone else is dealing with it */ | 73 | return; /* someone else is dealing with it */ |
73 | 74 | ||
74 | if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) { | 75 | if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) { |
@@ -84,13 +85,14 @@ void afs_broken_callback_work(struct work_struct *work) | |||
84 | /* if the vnode's data version number changed then its contents | 85 | /* if the vnode's data version number changed then its contents |
85 | * are different */ | 86 | * are different */ |
86 | if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) { | 87 | if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) { |
87 | _debug("zap data"); | 88 | _debug("zap data {%x:%u}", |
89 | vnode->fid.vid, vnode->fid.vnode); | ||
88 | invalidate_remote_inode(&vnode->vfs_inode); | 90 | invalidate_remote_inode(&vnode->vfs_inode); |
89 | } | 91 | } |
90 | } | 92 | } |
91 | 93 | ||
92 | out: | 94 | out: |
93 | mutex_unlock(&vnode->cb_broken_lock); | 95 | mutex_unlock(&vnode->validate_lock); |
94 | 96 | ||
95 | /* avoid the potential race whereby the mutex_trylock() in this | 97 | /* avoid the potential race whereby the mutex_trylock() in this |
96 | * function happens again between the clear_bit() and the | 98 | * function happens again between the clear_bit() and the |
@@ -252,6 +254,32 @@ static void afs_do_give_up_callback(struct afs_server *server, | |||
252 | } | 254 | } |
253 | 255 | ||
254 | /* | 256 | /* |
257 | * discard the callback on a deleted item | ||
258 | */ | ||
259 | void afs_discard_callback_on_delete(struct afs_vnode *vnode) | ||
260 | { | ||
261 | struct afs_server *server = vnode->server; | ||
262 | |||
263 | _enter("%d", vnode->cb_promised); | ||
264 | |||
265 | if (!vnode->cb_promised) { | ||
266 | _leave(" [not promised]"); | ||
267 | return; | ||
268 | } | ||
269 | |||
270 | ASSERT(server != NULL); | ||
271 | |||
272 | spin_lock(&server->cb_lock); | ||
273 | if (vnode->cb_promised) { | ||
274 | ASSERT(server->cb_promises.rb_node != NULL); | ||
275 | rb_erase(&vnode->cb_promise, &server->cb_promises); | ||
276 | vnode->cb_promised = false; | ||
277 | } | ||
278 | spin_unlock(&server->cb_lock); | ||
279 | _leave(""); | ||
280 | } | ||
281 | |||
282 | /* | ||
255 | * give up the callback registered for a vnode on the file server when the | 283 | * give up the callback registered for a vnode on the file server when the |
256 | * inode is being cleared | 284 | * inode is being cleared |
257 | */ | 285 | */ |
diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 87368417e4d3..dbbe75d6023b 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c | |||
@@ -18,40 +18,50 @@ | |||
18 | #include <linux/ctype.h> | 18 | #include <linux/ctype.h> |
19 | #include "internal.h" | 19 | #include "internal.h" |
20 | 20 | ||
21 | static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry, | 21 | static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, |
22 | struct nameidata *nd); | 22 | struct nameidata *nd); |
23 | static int afs_dir_open(struct inode *inode, struct file *file); | 23 | static int afs_dir_open(struct inode *inode, struct file *file); |
24 | static int afs_dir_readdir(struct file *file, void *dirent, filldir_t filldir); | 24 | static int afs_readdir(struct file *file, void *dirent, filldir_t filldir); |
25 | static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd); | 25 | static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd); |
26 | static int afs_d_delete(struct dentry *dentry); | 26 | static int afs_d_delete(struct dentry *dentry); |
27 | static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen, | 27 | static void afs_d_release(struct dentry *dentry); |
28 | static int afs_lookup_filldir(void *_cookie, const char *name, int nlen, | ||
28 | loff_t fpos, u64 ino, unsigned dtype); | 29 | loff_t fpos, u64 ino, unsigned dtype); |
30 | static int afs_create(struct inode *dir, struct dentry *dentry, int mode, | ||
31 | struct nameidata *nd); | ||
32 | static int afs_mkdir(struct inode *dir, struct dentry *dentry, int mode); | ||
33 | static int afs_rmdir(struct inode *dir, struct dentry *dentry); | ||
34 | static int afs_unlink(struct inode *dir, struct dentry *dentry); | ||
35 | static int afs_link(struct dentry *from, struct inode *dir, | ||
36 | struct dentry *dentry); | ||
37 | static int afs_symlink(struct inode *dir, struct dentry *dentry, | ||
38 | const char *content); | ||
39 | static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, | ||
40 | struct inode *new_dir, struct dentry *new_dentry); | ||
29 | 41 | ||
30 | const struct file_operations afs_dir_file_operations = { | 42 | const struct file_operations afs_dir_file_operations = { |
31 | .open = afs_dir_open, | 43 | .open = afs_dir_open, |
32 | .release = afs_release, | 44 | .release = afs_release, |
33 | .readdir = afs_dir_readdir, | 45 | .readdir = afs_readdir, |
34 | }; | 46 | }; |
35 | 47 | ||
36 | const struct inode_operations afs_dir_inode_operations = { | 48 | const struct inode_operations afs_dir_inode_operations = { |
37 | .lookup = afs_dir_lookup, | 49 | .create = afs_create, |
50 | .lookup = afs_lookup, | ||
51 | .link = afs_link, | ||
52 | .unlink = afs_unlink, | ||
53 | .symlink = afs_symlink, | ||
54 | .mkdir = afs_mkdir, | ||
55 | .rmdir = afs_rmdir, | ||
56 | .rename = afs_rename, | ||
38 | .permission = afs_permission, | 57 | .permission = afs_permission, |
39 | .getattr = afs_inode_getattr, | 58 | .getattr = afs_inode_getattr, |
40 | #if 0 /* TODO */ | ||
41 | .create = afs_dir_create, | ||
42 | .link = afs_dir_link, | ||
43 | .unlink = afs_dir_unlink, | ||
44 | .symlink = afs_dir_symlink, | ||
45 | .mkdir = afs_dir_mkdir, | ||
46 | .rmdir = afs_dir_rmdir, | ||
47 | .mknod = afs_dir_mknod, | ||
48 | .rename = afs_dir_rename, | ||
49 | #endif | ||
50 | }; | 59 | }; |
51 | 60 | ||
52 | static struct dentry_operations afs_fs_dentry_operations = { | 61 | static struct dentry_operations afs_fs_dentry_operations = { |
53 | .d_revalidate = afs_d_revalidate, | 62 | .d_revalidate = afs_d_revalidate, |
54 | .d_delete = afs_d_delete, | 63 | .d_delete = afs_d_delete, |
64 | .d_release = afs_d_release, | ||
55 | }; | 65 | }; |
56 | 66 | ||
57 | #define AFS_DIR_HASHTBL_SIZE 128 | 67 | #define AFS_DIR_HASHTBL_SIZE 128 |
@@ -103,7 +113,7 @@ struct afs_dir_page { | |||
103 | union afs_dir_block blocks[PAGE_SIZE / sizeof(union afs_dir_block)]; | 113 | union afs_dir_block blocks[PAGE_SIZE / sizeof(union afs_dir_block)]; |
104 | }; | 114 | }; |
105 | 115 | ||
106 | struct afs_dir_lookup_cookie { | 116 | struct afs_lookup_cookie { |
107 | struct afs_fid fid; | 117 | struct afs_fid fid; |
108 | const char *name; | 118 | const char *name; |
109 | size_t nlen; | 119 | size_t nlen; |
@@ -299,7 +309,7 @@ static int afs_dir_iterate_block(unsigned *fpos, | |||
299 | nlen, | 309 | nlen, |
300 | blkoff + offset * sizeof(union afs_dirent), | 310 | blkoff + offset * sizeof(union afs_dirent), |
301 | ntohl(dire->u.vnode), | 311 | ntohl(dire->u.vnode), |
302 | filldir == afs_dir_lookup_filldir ? | 312 | filldir == afs_lookup_filldir ? |
303 | ntohl(dire->u.unique) : DT_UNKNOWN); | 313 | ntohl(dire->u.unique) : DT_UNKNOWN); |
304 | if (ret < 0) { | 314 | if (ret < 0) { |
305 | _leave(" = 0 [full]"); | 315 | _leave(" = 0 [full]"); |
@@ -379,7 +389,7 @@ out: | |||
379 | /* | 389 | /* |
380 | * read an AFS directory | 390 | * read an AFS directory |
381 | */ | 391 | */ |
382 | static int afs_dir_readdir(struct file *file, void *cookie, filldir_t filldir) | 392 | static int afs_readdir(struct file *file, void *cookie, filldir_t filldir) |
383 | { | 393 | { |
384 | unsigned fpos; | 394 | unsigned fpos; |
385 | int ret; | 395 | int ret; |
@@ -403,10 +413,10 @@ static int afs_dir_readdir(struct file *file, void *cookie, filldir_t filldir) | |||
403 | * - if afs_dir_iterate_block() spots this function, it'll pass the FID | 413 | * - if afs_dir_iterate_block() spots this function, it'll pass the FID |
404 | * uniquifier through dtype | 414 | * uniquifier through dtype |
405 | */ | 415 | */ |
406 | static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen, | 416 | static int afs_lookup_filldir(void *_cookie, const char *name, int nlen, |
407 | loff_t fpos, u64 ino, unsigned dtype) | 417 | loff_t fpos, u64 ino, unsigned dtype) |
408 | { | 418 | { |
409 | struct afs_dir_lookup_cookie *cookie = _cookie; | 419 | struct afs_lookup_cookie *cookie = _cookie; |
410 | 420 | ||
411 | _enter("{%s,%Zu},%s,%u,,%llu,%u", | 421 | _enter("{%s,%Zu},%s,%u,,%llu,%u", |
412 | cookie->name, cookie->nlen, name, nlen, ino, dtype); | 422 | cookie->name, cookie->nlen, name, nlen, ino, dtype); |
@@ -430,11 +440,12 @@ static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen, | |||
430 | 440 | ||
431 | /* | 441 | /* |
432 | * do a lookup in a directory | 442 | * do a lookup in a directory |
443 | * - just returns the FID the dentry name maps to if found | ||
433 | */ | 444 | */ |
434 | static int afs_do_lookup(struct inode *dir, struct dentry *dentry, | 445 | static int afs_do_lookup(struct inode *dir, struct dentry *dentry, |
435 | struct afs_fid *fid, struct key *key) | 446 | struct afs_fid *fid, struct key *key) |
436 | { | 447 | { |
437 | struct afs_dir_lookup_cookie cookie; | 448 | struct afs_lookup_cookie cookie; |
438 | struct afs_super_info *as; | 449 | struct afs_super_info *as; |
439 | unsigned fpos; | 450 | unsigned fpos; |
440 | int ret; | 451 | int ret; |
@@ -450,7 +461,7 @@ static int afs_do_lookup(struct inode *dir, struct dentry *dentry, | |||
450 | cookie.found = 0; | 461 | cookie.found = 0; |
451 | 462 | ||
452 | fpos = 0; | 463 | fpos = 0; |
453 | ret = afs_dir_iterate(dir, &fpos, &cookie, afs_dir_lookup_filldir, | 464 | ret = afs_dir_iterate(dir, &fpos, &cookie, afs_lookup_filldir, |
454 | key); | 465 | key); |
455 | if (ret < 0) { | 466 | if (ret < 0) { |
456 | _leave(" = %d [iter]", ret); | 467 | _leave(" = %d [iter]", ret); |
@@ -471,8 +482,8 @@ static int afs_do_lookup(struct inode *dir, struct dentry *dentry, | |||
471 | /* | 482 | /* |
472 | * look up an entry in a directory | 483 | * look up an entry in a directory |
473 | */ | 484 | */ |
474 | static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry, | 485 | static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, |
475 | struct nameidata *nd) | 486 | struct nameidata *nd) |
476 | { | 487 | { |
477 | struct afs_vnode *vnode; | 488 | struct afs_vnode *vnode; |
478 | struct afs_fid fid; | 489 | struct afs_fid fid; |
@@ -480,14 +491,18 @@ static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry, | |||
480 | struct key *key; | 491 | struct key *key; |
481 | int ret; | 492 | int ret; |
482 | 493 | ||
483 | _enter("{%lu},%p{%s}", dir->i_ino, dentry, dentry->d_name.name); | 494 | vnode = AFS_FS_I(dir); |
495 | |||
496 | _enter("{%x:%d},%p{%s},", | ||
497 | vnode->fid.vid, vnode->fid.vnode, dentry, dentry->d_name.name); | ||
498 | |||
499 | ASSERTCMP(dentry->d_inode, ==, NULL); | ||
484 | 500 | ||
485 | if (dentry->d_name.len > 255) { | 501 | if (dentry->d_name.len > 255) { |
486 | _leave(" = -ENAMETOOLONG"); | 502 | _leave(" = -ENAMETOOLONG"); |
487 | return ERR_PTR(-ENAMETOOLONG); | 503 | return ERR_PTR(-ENAMETOOLONG); |
488 | } | 504 | } |
489 | 505 | ||
490 | vnode = AFS_FS_I(dir); | ||
491 | if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { | 506 | if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { |
492 | _leave(" = -ESTALE"); | 507 | _leave(" = -ESTALE"); |
493 | return ERR_PTR(-ESTALE); | 508 | return ERR_PTR(-ESTALE); |
@@ -499,15 +514,28 @@ static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry, | |||
499 | return ERR_PTR(PTR_ERR(key)); | 514 | return ERR_PTR(PTR_ERR(key)); |
500 | } | 515 | } |
501 | 516 | ||
517 | ret = afs_validate(vnode, key); | ||
518 | if (ret < 0) { | ||
519 | key_put(key); | ||
520 | _leave(" = %d [val]", ret); | ||
521 | return ERR_PTR(ret); | ||
522 | } | ||
523 | |||
502 | ret = afs_do_lookup(dir, dentry, &fid, key); | 524 | ret = afs_do_lookup(dir, dentry, &fid, key); |
503 | if (ret < 0) { | 525 | if (ret < 0) { |
504 | key_put(key); | 526 | key_put(key); |
527 | if (ret == -ENOENT) { | ||
528 | d_add(dentry, NULL); | ||
529 | _leave(" = NULL [negative]"); | ||
530 | return NULL; | ||
531 | } | ||
505 | _leave(" = %d [do]", ret); | 532 | _leave(" = %d [do]", ret); |
506 | return ERR_PTR(ret); | 533 | return ERR_PTR(ret); |
507 | } | 534 | } |
535 | dentry->d_fsdata = (void *)(unsigned long) vnode->status.data_version; | ||
508 | 536 | ||
509 | /* instantiate the dentry */ | 537 | /* instantiate the dentry */ |
510 | inode = afs_iget(dir->i_sb, key, &fid); | 538 | inode = afs_iget(dir->i_sb, key, &fid, NULL, NULL); |
511 | key_put(key); | 539 | key_put(key); |
512 | if (IS_ERR(inode)) { | 540 | if (IS_ERR(inode)) { |
513 | _leave(" = %ld", PTR_ERR(inode)); | 541 | _leave(" = %ld", PTR_ERR(inode)); |
@@ -527,105 +555,64 @@ static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry, | |||
527 | } | 555 | } |
528 | 556 | ||
529 | /* | 557 | /* |
530 | * propagate changed and modified flags on a directory to all the children of | ||
531 | * that directory as they may indicate that the ACL on the dir has changed, | ||
532 | * potentially rendering the child inaccessible or that a file has been deleted | ||
533 | * or renamed | ||
534 | */ | ||
535 | static void afs_propagate_dir_changes(struct dentry *dir) | ||
536 | { | ||
537 | struct dentry *child; | ||
538 | bool c, m; | ||
539 | |||
540 | c = test_bit(AFS_VNODE_CHANGED, &AFS_FS_I(dir->d_inode)->flags); | ||
541 | m = test_bit(AFS_VNODE_MODIFIED, &AFS_FS_I(dir->d_inode)->flags); | ||
542 | |||
543 | _enter("{%d,%d}", c, m); | ||
544 | |||
545 | spin_lock(&dir->d_lock); | ||
546 | |||
547 | list_for_each_entry(child, &dir->d_subdirs, d_u.d_child) { | ||
548 | if (child->d_inode) { | ||
549 | struct afs_vnode *vnode; | ||
550 | |||
551 | _debug("tag %s", child->d_name.name); | ||
552 | vnode = AFS_FS_I(child->d_inode); | ||
553 | if (c) | ||
554 | set_bit(AFS_VNODE_DIR_CHANGED, &vnode->flags); | ||
555 | if (m) | ||
556 | set_bit(AFS_VNODE_DIR_MODIFIED, &vnode->flags); | ||
557 | } | ||
558 | } | ||
559 | |||
560 | spin_unlock(&dir->d_lock); | ||
561 | } | ||
562 | |||
563 | /* | ||
564 | * check that a dentry lookup hit has found a valid entry | 558 | * check that a dentry lookup hit has found a valid entry |
565 | * - NOTE! the hit can be a negative hit too, so we can't assume we have an | 559 | * - NOTE! the hit can be a negative hit too, so we can't assume we have an |
566 | * inode | 560 | * inode |
567 | * - there are several things we need to check | ||
568 | * - parent dir data changes (rm, rmdir, rename, mkdir, create, link, | ||
569 | * symlink) | ||
570 | * - parent dir metadata changed (security changes) | ||
571 | * - dentry data changed (write, truncate) | ||
572 | * - dentry metadata changed (security changes) | ||
573 | */ | 561 | */ |
574 | static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd) | 562 | static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd) |
575 | { | 563 | { |
576 | struct afs_vnode *vnode; | 564 | struct afs_vnode *vnode, *dir; |
577 | struct afs_fid fid; | 565 | struct afs_fid fid; |
578 | struct dentry *parent; | 566 | struct dentry *parent; |
579 | struct inode *inode, *dir; | ||
580 | struct key *key; | 567 | struct key *key; |
568 | void *dir_version; | ||
581 | int ret; | 569 | int ret; |
582 | 570 | ||
583 | vnode = AFS_FS_I(dentry->d_inode); | 571 | vnode = AFS_FS_I(dentry->d_inode); |
584 | 572 | ||
585 | _enter("{sb=%p n=%s fl=%lx},", | 573 | if (dentry->d_inode) |
586 | dentry->d_sb, dentry->d_name.name, vnode->flags); | 574 | _enter("{v={%x:%u} n=%s fl=%lx},", |
575 | vnode->fid.vid, vnode->fid.vnode, dentry->d_name.name, | ||
576 | vnode->flags); | ||
577 | else | ||
578 | _enter("{neg n=%s}", dentry->d_name.name); | ||
587 | 579 | ||
588 | key = afs_request_key(vnode->volume->cell); | 580 | key = afs_request_key(AFS_FS_S(dentry->d_sb)->volume->cell); |
589 | if (IS_ERR(key)) | 581 | if (IS_ERR(key)) |
590 | key = NULL; | 582 | key = NULL; |
591 | 583 | ||
592 | /* lock down the parent dentry so we can peer at it */ | 584 | /* lock down the parent dentry so we can peer at it */ |
593 | parent = dget_parent(dentry); | 585 | parent = dget_parent(dentry); |
594 | 586 | if (!parent->d_inode) | |
595 | dir = parent->d_inode; | ||
596 | inode = dentry->d_inode; | ||
597 | |||
598 | /* handle a negative dentry */ | ||
599 | if (!inode) | ||
600 | goto out_bad; | 587 | goto out_bad; |
601 | 588 | ||
602 | /* handle a bad inode */ | 589 | dir = AFS_FS_I(parent->d_inode); |
603 | if (is_bad_inode(inode)) { | ||
604 | printk("kAFS: afs_d_revalidate: %s/%s has bad inode\n", | ||
605 | parent->d_name.name, dentry->d_name.name); | ||
606 | goto out_bad; | ||
607 | } | ||
608 | 590 | ||
609 | /* check that this dirent still exists if the directory's contents were | 591 | /* validate the parent directory */ |
610 | * modified */ | 592 | if (test_bit(AFS_VNODE_MODIFIED, &dir->flags)) |
611 | if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dir)->flags)) { | 593 | afs_validate(dir, key); |
594 | |||
595 | if (test_bit(AFS_VNODE_DELETED, &dir->flags)) { | ||
612 | _debug("%s: parent dir deleted", dentry->d_name.name); | 596 | _debug("%s: parent dir deleted", dentry->d_name.name); |
613 | goto out_bad; | 597 | goto out_bad; |
614 | } | 598 | } |
615 | 599 | ||
616 | if (test_and_clear_bit(AFS_VNODE_DIR_MODIFIED, &vnode->flags)) { | 600 | dir_version = (void *) (unsigned long) dir->status.data_version; |
617 | /* rm/rmdir/rename may have occurred */ | 601 | if (dentry->d_fsdata == dir_version) |
618 | _debug("dir modified"); | 602 | goto out_valid; /* the dir contents are unchanged */ |
619 | 603 | ||
620 | /* search the directory for this vnode */ | 604 | _debug("dir modified"); |
621 | ret = afs_do_lookup(dir, dentry, &fid, key); | 605 | |
622 | if (ret == -ENOENT) { | 606 | /* search the directory for this vnode */ |
623 | _debug("%s: dirent not found", dentry->d_name.name); | 607 | ret = afs_do_lookup(&dir->vfs_inode, dentry, &fid, key); |
624 | goto not_found; | 608 | switch (ret) { |
625 | } | 609 | case 0: |
626 | if (ret < 0) { | 610 | /* the filename maps to something */ |
627 | _debug("failed to iterate dir %s: %d", | 611 | if (!dentry->d_inode) |
628 | parent->d_name.name, ret); | 612 | goto out_bad; |
613 | if (is_bad_inode(dentry->d_inode)) { | ||
614 | printk("kAFS: afs_d_revalidate: %s/%s has bad inode\n", | ||
615 | parent->d_name.name, dentry->d_name.name); | ||
629 | goto out_bad; | 616 | goto out_bad; |
630 | } | 617 | } |
631 | 618 | ||
@@ -639,56 +626,35 @@ static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd) | |||
639 | } | 626 | } |
640 | 627 | ||
641 | /* if the vnode ID uniqifier has changed, then the file has | 628 | /* if the vnode ID uniqifier has changed, then the file has |
642 | * been deleted */ | 629 | * been deleted and replaced, and the original vnode ID has |
630 | * been reused */ | ||
643 | if (fid.unique != vnode->fid.unique) { | 631 | if (fid.unique != vnode->fid.unique) { |
644 | _debug("%s: file deleted (uq %u -> %u I:%lu)", | 632 | _debug("%s: file deleted (uq %u -> %u I:%lu)", |
645 | dentry->d_name.name, fid.unique, | 633 | dentry->d_name.name, fid.unique, |
646 | vnode->fid.unique, inode->i_version); | 634 | vnode->fid.unique, dentry->d_inode->i_version); |
647 | spin_lock(&vnode->lock); | 635 | spin_lock(&vnode->lock); |
648 | set_bit(AFS_VNODE_DELETED, &vnode->flags); | 636 | set_bit(AFS_VNODE_DELETED, &vnode->flags); |
649 | spin_unlock(&vnode->lock); | 637 | spin_unlock(&vnode->lock); |
650 | invalidate_remote_inode(inode); | 638 | goto not_found; |
651 | goto out_bad; | ||
652 | } | 639 | } |
653 | } | 640 | goto out_valid; |
654 | 641 | ||
655 | /* if the directory's metadata were changed then the security may be | 642 | case -ENOENT: |
656 | * different and we may no longer have access */ | 643 | /* the filename is unknown */ |
657 | mutex_lock(&vnode->cb_broken_lock); | 644 | _debug("%s: dirent not found", dentry->d_name.name); |
658 | 645 | if (dentry->d_inode) | |
659 | if (test_and_clear_bit(AFS_VNODE_DIR_CHANGED, &vnode->flags) || | 646 | goto not_found; |
660 | test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) { | 647 | goto out_valid; |
661 | _debug("%s: changed", dentry->d_name.name); | ||
662 | set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); | ||
663 | if (afs_vnode_fetch_status(vnode, NULL, key) < 0) { | ||
664 | mutex_unlock(&vnode->cb_broken_lock); | ||
665 | goto out_bad; | ||
666 | } | ||
667 | } | ||
668 | 648 | ||
669 | if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { | 649 | default: |
670 | _debug("%s: file already deleted", dentry->d_name.name); | 650 | _debug("failed to iterate dir %s: %d", |
671 | mutex_unlock(&vnode->cb_broken_lock); | 651 | parent->d_name.name, ret); |
672 | goto out_bad; | 652 | goto out_bad; |
673 | } | 653 | } |
674 | 654 | ||
675 | /* if the vnode's data version number changed then its contents are | ||
676 | * different */ | ||
677 | if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) { | ||
678 | _debug("zap data"); | ||
679 | invalidate_remote_inode(inode); | ||
680 | } | ||
681 | |||
682 | if (S_ISDIR(inode->i_mode) && | ||
683 | (test_bit(AFS_VNODE_CHANGED, &vnode->flags) || | ||
684 | test_bit(AFS_VNODE_MODIFIED, &vnode->flags))) | ||
685 | afs_propagate_dir_changes(dentry); | ||
686 | |||
687 | clear_bit(AFS_VNODE_CHANGED, &vnode->flags); | ||
688 | clear_bit(AFS_VNODE_MODIFIED, &vnode->flags); | ||
689 | mutex_unlock(&vnode->cb_broken_lock); | ||
690 | |||
691 | out_valid: | 655 | out_valid: |
656 | dentry->d_fsdata = dir_version; | ||
657 | out_skip: | ||
692 | dput(parent); | 658 | dput(parent); |
693 | key_put(key); | 659 | key_put(key); |
694 | _leave(" = 1 [valid]"); | 660 | _leave(" = 1 [valid]"); |
@@ -701,10 +667,10 @@ not_found: | |||
701 | spin_unlock(&dentry->d_lock); | 667 | spin_unlock(&dentry->d_lock); |
702 | 668 | ||
703 | out_bad: | 669 | out_bad: |
704 | if (inode) { | 670 | if (dentry->d_inode) { |
705 | /* don't unhash if we have submounts */ | 671 | /* don't unhash if we have submounts */ |
706 | if (have_submounts(dentry)) | 672 | if (have_submounts(dentry)) |
707 | goto out_valid; | 673 | goto out_skip; |
708 | } | 674 | } |
709 | 675 | ||
710 | _debug("dropping dentry %s/%s", | 676 | _debug("dropping dentry %s/%s", |
@@ -742,3 +708,433 @@ zap: | |||
742 | _leave(" = 1 [zap]"); | 708 | _leave(" = 1 [zap]"); |
743 | return 1; | 709 | return 1; |
744 | } | 710 | } |
711 | |||
712 | /* | ||
713 | * handle dentry release | ||
714 | */ | ||
715 | static void afs_d_release(struct dentry *dentry) | ||
716 | { | ||
717 | _enter("%s", dentry->d_name.name); | ||
718 | } | ||
719 | |||
720 | /* | ||
721 | * create a directory on an AFS filesystem | ||
722 | */ | ||
723 | static int afs_mkdir(struct inode *dir, struct dentry *dentry, int mode) | ||
724 | { | ||
725 | struct afs_file_status status; | ||
726 | struct afs_callback cb; | ||
727 | struct afs_server *server; | ||
728 | struct afs_vnode *dvnode, *vnode; | ||
729 | struct afs_fid fid; | ||
730 | struct inode *inode; | ||
731 | struct key *key; | ||
732 | int ret; | ||
733 | |||
734 | dvnode = AFS_FS_I(dir); | ||
735 | |||
736 | _enter("{%x:%d},{%s},%o", | ||
737 | dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name, mode); | ||
738 | |||
739 | ret = -ENAMETOOLONG; | ||
740 | if (dentry->d_name.len > 255) | ||
741 | goto error; | ||
742 | |||
743 | key = afs_request_key(dvnode->volume->cell); | ||
744 | if (IS_ERR(key)) { | ||
745 | ret = PTR_ERR(key); | ||
746 | goto error; | ||
747 | } | ||
748 | |||
749 | mode |= S_IFDIR; | ||
750 | ret = afs_vnode_create(dvnode, key, dentry->d_name.name, | ||
751 | mode, &fid, &status, &cb, &server); | ||
752 | if (ret < 0) | ||
753 | goto mkdir_error; | ||
754 | |||
755 | inode = afs_iget(dir->i_sb, key, &fid, &status, &cb); | ||
756 | if (IS_ERR(inode)) { | ||
757 | /* ENOMEM at a really inconvenient time - just abandon the new | ||
758 | * directory on the server */ | ||
759 | ret = PTR_ERR(inode); | ||
760 | goto iget_error; | ||
761 | } | ||
762 | |||
763 | /* apply the status report we've got for the new vnode */ | ||
764 | vnode = AFS_FS_I(inode); | ||
765 | spin_lock(&vnode->lock); | ||
766 | vnode->update_cnt++; | ||
767 | spin_unlock(&vnode->lock); | ||
768 | afs_vnode_finalise_status_update(vnode, server); | ||
769 | afs_put_server(server); | ||
770 | |||
771 | d_instantiate(dentry, inode); | ||
772 | if (d_unhashed(dentry)) { | ||
773 | _debug("not hashed"); | ||
774 | d_rehash(dentry); | ||
775 | } | ||
776 | key_put(key); | ||
777 | _leave(" = 0"); | ||
778 | return 0; | ||
779 | |||
780 | iget_error: | ||
781 | afs_put_server(server); | ||
782 | mkdir_error: | ||
783 | key_put(key); | ||
784 | error: | ||
785 | d_drop(dentry); | ||
786 | _leave(" = %d", ret); | ||
787 | return ret; | ||
788 | } | ||
789 | |||
790 | /* | ||
791 | * remove a directory from an AFS filesystem | ||
792 | */ | ||
793 | static int afs_rmdir(struct inode *dir, struct dentry *dentry) | ||
794 | { | ||
795 | struct afs_vnode *dvnode, *vnode; | ||
796 | struct key *key; | ||
797 | int ret; | ||
798 | |||
799 | dvnode = AFS_FS_I(dir); | ||
800 | |||
801 | _enter("{%x:%d},{%s}", | ||
802 | dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name); | ||
803 | |||
804 | ret = -ENAMETOOLONG; | ||
805 | if (dentry->d_name.len > 255) | ||
806 | goto error; | ||
807 | |||
808 | key = afs_request_key(dvnode->volume->cell); | ||
809 | if (IS_ERR(key)) { | ||
810 | ret = PTR_ERR(key); | ||
811 | goto error; | ||
812 | } | ||
813 | |||
814 | ret = afs_vnode_remove(dvnode, key, dentry->d_name.name, true); | ||
815 | if (ret < 0) | ||
816 | goto rmdir_error; | ||
817 | |||
818 | if (dentry->d_inode) { | ||
819 | vnode = AFS_FS_I(dentry->d_inode); | ||
820 | clear_nlink(&vnode->vfs_inode); | ||
821 | set_bit(AFS_VNODE_DELETED, &vnode->flags); | ||
822 | afs_discard_callback_on_delete(vnode); | ||
823 | } | ||
824 | |||
825 | key_put(key); | ||
826 | _leave(" = 0"); | ||
827 | return 0; | ||
828 | |||
829 | rmdir_error: | ||
830 | key_put(key); | ||
831 | error: | ||
832 | _leave(" = %d", ret); | ||
833 | return ret; | ||
834 | } | ||
835 | |||
836 | /* | ||
837 | * remove a file from an AFS filesystem | ||
838 | */ | ||
839 | static int afs_unlink(struct inode *dir, struct dentry *dentry) | ||
840 | { | ||
841 | struct afs_vnode *dvnode, *vnode; | ||
842 | struct key *key; | ||
843 | int ret; | ||
844 | |||
845 | dvnode = AFS_FS_I(dir); | ||
846 | |||
847 | _enter("{%x:%d},{%s}", | ||
848 | dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name); | ||
849 | |||
850 | ret = -ENAMETOOLONG; | ||
851 | if (dentry->d_name.len > 255) | ||
852 | goto error; | ||
853 | |||
854 | key = afs_request_key(dvnode->volume->cell); | ||
855 | if (IS_ERR(key)) { | ||
856 | ret = PTR_ERR(key); | ||
857 | goto error; | ||
858 | } | ||
859 | |||
860 | if (dentry->d_inode) { | ||
861 | vnode = AFS_FS_I(dentry->d_inode); | ||
862 | |||
863 | /* make sure we have a callback promise on the victim */ | ||
864 | ret = afs_validate(vnode, key); | ||
865 | if (ret < 0) | ||
866 | goto error; | ||
867 | } | ||
868 | |||
869 | ret = afs_vnode_remove(dvnode, key, dentry->d_name.name, false); | ||
870 | if (ret < 0) | ||
871 | goto remove_error; | ||
872 | |||
873 | if (dentry->d_inode) { | ||
874 | /* if the file wasn't deleted due to excess hard links, the | ||
875 | * fileserver will break the callback promise on the file - if | ||
876 | * it had one - before it returns to us, and if it was deleted, | ||
877 | * it won't | ||
878 | * | ||
879 | * however, if we didn't have a callback promise outstanding, | ||
880 | * or it was outstanding on a different server, then it won't | ||
881 | * break it either... | ||
882 | */ | ||
883 | vnode = AFS_FS_I(dentry->d_inode); | ||
884 | if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) | ||
885 | _debug("AFS_VNODE_DELETED"); | ||
886 | if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) | ||
887 | _debug("AFS_VNODE_CB_BROKEN"); | ||
888 | set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); | ||
889 | ret = afs_validate(vnode, key); | ||
890 | _debug("nlink %d [val %d]", vnode->vfs_inode.i_nlink, ret); | ||
891 | } | ||
892 | |||
893 | key_put(key); | ||
894 | _leave(" = 0"); | ||
895 | return 0; | ||
896 | |||
897 | remove_error: | ||
898 | key_put(key); | ||
899 | error: | ||
900 | _leave(" = %d", ret); | ||
901 | return ret; | ||
902 | } | ||
903 | |||
904 | /* | ||
905 | * create a regular file on an AFS filesystem | ||
906 | */ | ||
907 | static int afs_create(struct inode *dir, struct dentry *dentry, int mode, | ||
908 | struct nameidata *nd) | ||
909 | { | ||
910 | struct afs_file_status status; | ||
911 | struct afs_callback cb; | ||
912 | struct afs_server *server; | ||
913 | struct afs_vnode *dvnode, *vnode; | ||
914 | struct afs_fid fid; | ||
915 | struct inode *inode; | ||
916 | struct key *key; | ||
917 | int ret; | ||
918 | |||
919 | dvnode = AFS_FS_I(dir); | ||
920 | |||
921 | _enter("{%x:%d},{%s},%o,", | ||
922 | dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name, mode); | ||
923 | |||
924 | ret = -ENAMETOOLONG; | ||
925 | if (dentry->d_name.len > 255) | ||
926 | goto error; | ||
927 | |||
928 | key = afs_request_key(dvnode->volume->cell); | ||
929 | if (IS_ERR(key)) { | ||
930 | ret = PTR_ERR(key); | ||
931 | goto error; | ||
932 | } | ||
933 | |||
934 | mode |= S_IFREG; | ||
935 | ret = afs_vnode_create(dvnode, key, dentry->d_name.name, | ||
936 | mode, &fid, &status, &cb, &server); | ||
937 | if (ret < 0) | ||
938 | goto create_error; | ||
939 | |||
940 | inode = afs_iget(dir->i_sb, key, &fid, &status, &cb); | ||
941 | if (IS_ERR(inode)) { | ||
942 | /* ENOMEM at a really inconvenient time - just abandon the new | ||
943 | * directory on the server */ | ||
944 | ret = PTR_ERR(inode); | ||
945 | goto iget_error; | ||
946 | } | ||
947 | |||
948 | /* apply the status report we've got for the new vnode */ | ||
949 | vnode = AFS_FS_I(inode); | ||
950 | spin_lock(&vnode->lock); | ||
951 | vnode->update_cnt++; | ||
952 | spin_unlock(&vnode->lock); | ||
953 | afs_vnode_finalise_status_update(vnode, server); | ||
954 | afs_put_server(server); | ||
955 | |||
956 | d_instantiate(dentry, inode); | ||
957 | if (d_unhashed(dentry)) { | ||
958 | _debug("not hashed"); | ||
959 | d_rehash(dentry); | ||
960 | } | ||
961 | key_put(key); | ||
962 | _leave(" = 0"); | ||
963 | return 0; | ||
964 | |||
965 | iget_error: | ||
966 | afs_put_server(server); | ||
967 | create_error: | ||
968 | key_put(key); | ||
969 | error: | ||
970 | d_drop(dentry); | ||
971 | _leave(" = %d", ret); | ||
972 | return ret; | ||
973 | } | ||
974 | |||
975 | /* | ||
976 | * create a hard link between files in an AFS filesystem | ||
977 | */ | ||
978 | static int afs_link(struct dentry *from, struct inode *dir, | ||
979 | struct dentry *dentry) | ||
980 | { | ||
981 | struct afs_vnode *dvnode, *vnode; | ||
982 | struct key *key; | ||
983 | int ret; | ||
984 | |||
985 | vnode = AFS_FS_I(from->d_inode); | ||
986 | dvnode = AFS_FS_I(dir); | ||
987 | |||
988 | _enter("{%x:%d},{%x:%d},{%s}", | ||
989 | vnode->fid.vid, vnode->fid.vnode, | ||
990 | dvnode->fid.vid, dvnode->fid.vnode, | ||
991 | dentry->d_name.name); | ||
992 | |||
993 | ret = -ENAMETOOLONG; | ||
994 | if (dentry->d_name.len > 255) | ||
995 | goto error; | ||
996 | |||
997 | key = afs_request_key(dvnode->volume->cell); | ||
998 | if (IS_ERR(key)) { | ||
999 | ret = PTR_ERR(key); | ||
1000 | goto error; | ||
1001 | } | ||
1002 | |||
1003 | ret = afs_vnode_link(dvnode, vnode, key, dentry->d_name.name); | ||
1004 | if (ret < 0) | ||
1005 | goto link_error; | ||
1006 | |||
1007 | atomic_inc(&vnode->vfs_inode.i_count); | ||
1008 | d_instantiate(dentry, &vnode->vfs_inode); | ||
1009 | key_put(key); | ||
1010 | _leave(" = 0"); | ||
1011 | return 0; | ||
1012 | |||
1013 | link_error: | ||
1014 | key_put(key); | ||
1015 | error: | ||
1016 | d_drop(dentry); | ||
1017 | _leave(" = %d", ret); | ||
1018 | return ret; | ||
1019 | } | ||
1020 | |||
1021 | /* | ||
1022 | * create a symlink in an AFS filesystem | ||
1023 | */ | ||
1024 | static int afs_symlink(struct inode *dir, struct dentry *dentry, | ||
1025 | const char *content) | ||
1026 | { | ||
1027 | struct afs_file_status status; | ||
1028 | struct afs_server *server; | ||
1029 | struct afs_vnode *dvnode, *vnode; | ||
1030 | struct afs_fid fid; | ||
1031 | struct inode *inode; | ||
1032 | struct key *key; | ||
1033 | int ret; | ||
1034 | |||
1035 | dvnode = AFS_FS_I(dir); | ||
1036 | |||
1037 | _enter("{%x:%d},{%s},%s", | ||
1038 | dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name, | ||
1039 | content); | ||
1040 | |||
1041 | ret = -ENAMETOOLONG; | ||
1042 | if (dentry->d_name.len > 255) | ||
1043 | goto error; | ||
1044 | |||
1045 | ret = -EINVAL; | ||
1046 | if (strlen(content) > 1023) | ||
1047 | goto error; | ||
1048 | |||
1049 | key = afs_request_key(dvnode->volume->cell); | ||
1050 | if (IS_ERR(key)) { | ||
1051 | ret = PTR_ERR(key); | ||
1052 | goto error; | ||
1053 | } | ||
1054 | |||
1055 | ret = afs_vnode_symlink(dvnode, key, dentry->d_name.name, content, | ||
1056 | &fid, &status, &server); | ||
1057 | if (ret < 0) | ||
1058 | goto create_error; | ||
1059 | |||
1060 | inode = afs_iget(dir->i_sb, key, &fid, &status, NULL); | ||
1061 | if (IS_ERR(inode)) { | ||
1062 | /* ENOMEM at a really inconvenient time - just abandon the new | ||
1063 | * directory on the server */ | ||
1064 | ret = PTR_ERR(inode); | ||
1065 | goto iget_error; | ||
1066 | } | ||
1067 | |||
1068 | /* apply the status report we've got for the new vnode */ | ||
1069 | vnode = AFS_FS_I(inode); | ||
1070 | spin_lock(&vnode->lock); | ||
1071 | vnode->update_cnt++; | ||
1072 | spin_unlock(&vnode->lock); | ||
1073 | afs_vnode_finalise_status_update(vnode, server); | ||
1074 | afs_put_server(server); | ||
1075 | |||
1076 | d_instantiate(dentry, inode); | ||
1077 | if (d_unhashed(dentry)) { | ||
1078 | _debug("not hashed"); | ||
1079 | d_rehash(dentry); | ||
1080 | } | ||
1081 | key_put(key); | ||
1082 | _leave(" = 0"); | ||
1083 | return 0; | ||
1084 | |||
1085 | iget_error: | ||
1086 | afs_put_server(server); | ||
1087 | create_error: | ||
1088 | key_put(key); | ||
1089 | error: | ||
1090 | d_drop(dentry); | ||
1091 | _leave(" = %d", ret); | ||
1092 | return ret; | ||
1093 | } | ||
1094 | |||
1095 | /* | ||
1096 | * rename a file in an AFS filesystem and/or move it between directories | ||
1097 | */ | ||
1098 | static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, | ||
1099 | struct inode *new_dir, struct dentry *new_dentry) | ||
1100 | { | ||
1101 | struct afs_vnode *orig_dvnode, *new_dvnode, *vnode; | ||
1102 | struct key *key; | ||
1103 | int ret; | ||
1104 | |||
1105 | vnode = AFS_FS_I(old_dentry->d_inode); | ||
1106 | orig_dvnode = AFS_FS_I(old_dir); | ||
1107 | new_dvnode = AFS_FS_I(new_dir); | ||
1108 | |||
1109 | _enter("{%x:%d},{%x:%d},{%x:%d},{%s}", | ||
1110 | orig_dvnode->fid.vid, orig_dvnode->fid.vnode, | ||
1111 | vnode->fid.vid, vnode->fid.vnode, | ||
1112 | new_dvnode->fid.vid, new_dvnode->fid.vnode, | ||
1113 | new_dentry->d_name.name); | ||
1114 | |||
1115 | ret = -ENAMETOOLONG; | ||
1116 | if (new_dentry->d_name.len > 255) | ||
1117 | goto error; | ||
1118 | |||
1119 | key = afs_request_key(orig_dvnode->volume->cell); | ||
1120 | if (IS_ERR(key)) { | ||
1121 | ret = PTR_ERR(key); | ||
1122 | goto error; | ||
1123 | } | ||
1124 | |||
1125 | ret = afs_vnode_rename(orig_dvnode, new_dvnode, key, | ||
1126 | old_dentry->d_name.name, | ||
1127 | new_dentry->d_name.name); | ||
1128 | if (ret < 0) | ||
1129 | goto rename_error; | ||
1130 | key_put(key); | ||
1131 | _leave(" = 0"); | ||
1132 | return 0; | ||
1133 | |||
1134 | rename_error: | ||
1135 | key_put(key); | ||
1136 | error: | ||
1137 | d_drop(new_dentry); | ||
1138 | _leave(" = %d", ret); | ||
1139 | return ret; | ||
1140 | } | ||
diff --git a/fs/afs/file.c b/fs/afs/file.c index 101bbb8c0d8b..ae256498f4f7 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c | |||
@@ -50,6 +50,7 @@ int afs_open(struct inode *inode, struct file *file) | |||
50 | { | 50 | { |
51 | struct afs_vnode *vnode = AFS_FS_I(inode); | 51 | struct afs_vnode *vnode = AFS_FS_I(inode); |
52 | struct key *key; | 52 | struct key *key; |
53 | int ret; | ||
53 | 54 | ||
54 | _enter("{%x:%x},", vnode->fid.vid, vnode->fid.vnode); | 55 | _enter("{%x:%x},", vnode->fid.vid, vnode->fid.vnode); |
55 | 56 | ||
@@ -59,6 +60,12 @@ int afs_open(struct inode *inode, struct file *file) | |||
59 | return PTR_ERR(key); | 60 | return PTR_ERR(key); |
60 | } | 61 | } |
61 | 62 | ||
63 | ret = afs_validate(vnode, key); | ||
64 | if (ret < 0) { | ||
65 | _leave(" = %d [val]", ret); | ||
66 | return ret; | ||
67 | } | ||
68 | |||
62 | file->private_data = key; | 69 | file->private_data = key; |
63 | _leave(" = 0"); | 70 | _leave(" = 0"); |
64 | return 0; | 71 | return 0; |
diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index 321b489aa90f..f036b4cc51a6 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c | |||
@@ -16,14 +16,28 @@ | |||
16 | #include "afs_fs.h" | 16 | #include "afs_fs.h" |
17 | 17 | ||
18 | /* | 18 | /* |
19 | * decode an AFSFid block | ||
20 | */ | ||
21 | static void xdr_decode_AFSFid(const __be32 **_bp, struct afs_fid *fid) | ||
22 | { | ||
23 | const __be32 *bp = *_bp; | ||
24 | |||
25 | fid->vid = ntohl(*bp++); | ||
26 | fid->vnode = ntohl(*bp++); | ||
27 | fid->unique = ntohl(*bp++); | ||
28 | *_bp = bp; | ||
29 | } | ||
30 | |||
31 | /* | ||
19 | * decode an AFSFetchStatus block | 32 | * decode an AFSFetchStatus block |
20 | */ | 33 | */ |
21 | static void xdr_decode_AFSFetchStatus(const __be32 **_bp, | 34 | static void xdr_decode_AFSFetchStatus(const __be32 **_bp, |
35 | struct afs_file_status *status, | ||
22 | struct afs_vnode *vnode) | 36 | struct afs_vnode *vnode) |
23 | { | 37 | { |
24 | const __be32 *bp = *_bp; | 38 | const __be32 *bp = *_bp; |
25 | umode_t mode; | 39 | umode_t mode; |
26 | u64 data_version; | 40 | u64 data_version, size; |
27 | u32 changed = 0; /* becomes non-zero if ctime-type changes seen */ | 41 | u32 changed = 0; /* becomes non-zero if ctime-type changes seen */ |
28 | 42 | ||
29 | #define EXTRACT(DST) \ | 43 | #define EXTRACT(DST) \ |
@@ -33,55 +47,68 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp, | |||
33 | DST = x; \ | 47 | DST = x; \ |
34 | } while (0) | 48 | } while (0) |
35 | 49 | ||
36 | vnode->status.if_version = ntohl(*bp++); | 50 | status->if_version = ntohl(*bp++); |
37 | EXTRACT(vnode->status.type); | 51 | EXTRACT(status->type); |
38 | vnode->status.nlink = ntohl(*bp++); | 52 | EXTRACT(status->nlink); |
39 | EXTRACT(vnode->status.size); | 53 | size = ntohl(*bp++); |
40 | data_version = ntohl(*bp++); | 54 | data_version = ntohl(*bp++); |
41 | EXTRACT(vnode->status.author); | 55 | EXTRACT(status->author); |
42 | EXTRACT(vnode->status.owner); | 56 | EXTRACT(status->owner); |
43 | EXTRACT(vnode->status.caller_access); /* call ticket dependent */ | 57 | EXTRACT(status->caller_access); /* call ticket dependent */ |
44 | EXTRACT(vnode->status.anon_access); | 58 | EXTRACT(status->anon_access); |
45 | EXTRACT(vnode->status.mode); | 59 | EXTRACT(status->mode); |
46 | vnode->status.parent.vid = vnode->fid.vid; | 60 | EXTRACT(status->parent.vnode); |
47 | EXTRACT(vnode->status.parent.vnode); | 61 | EXTRACT(status->parent.unique); |
48 | EXTRACT(vnode->status.parent.unique); | ||
49 | bp++; /* seg size */ | 62 | bp++; /* seg size */ |
50 | vnode->status.mtime_client = ntohl(*bp++); | 63 | status->mtime_client = ntohl(*bp++); |
51 | vnode->status.mtime_server = ntohl(*bp++); | 64 | status->mtime_server = ntohl(*bp++); |
52 | bp++; /* group */ | 65 | EXTRACT(status->group); |
53 | bp++; /* sync counter */ | 66 | bp++; /* sync counter */ |
54 | data_version |= (u64) ntohl(*bp++) << 32; | 67 | data_version |= (u64) ntohl(*bp++) << 32; |
55 | bp++; /* spare2 */ | 68 | bp++; /* lock count */ |
56 | bp++; /* spare3 */ | 69 | size |= (u64) ntohl(*bp++) << 32; |
57 | bp++; /* spare4 */ | 70 | bp++; /* spare 4 */ |
58 | *_bp = bp; | 71 | *_bp = bp; |
59 | 72 | ||
60 | if (changed) { | 73 | if (size != status->size) { |
61 | _debug("vnode changed"); | 74 | status->size = size; |
62 | set_bit(AFS_VNODE_CHANGED, &vnode->flags); | 75 | changed |= true; |
63 | vnode->vfs_inode.i_uid = vnode->status.owner; | ||
64 | vnode->vfs_inode.i_size = vnode->status.size; | ||
65 | vnode->vfs_inode.i_version = vnode->fid.unique; | ||
66 | |||
67 | vnode->status.mode &= S_IALLUGO; | ||
68 | mode = vnode->vfs_inode.i_mode; | ||
69 | mode &= ~S_IALLUGO; | ||
70 | mode |= vnode->status.mode; | ||
71 | vnode->vfs_inode.i_mode = mode; | ||
72 | } | 76 | } |
77 | status->mode &= S_IALLUGO; | ||
73 | 78 | ||
74 | _debug("vnode time %lx, %lx", | 79 | _debug("vnode time %lx, %lx", |
75 | vnode->status.mtime_client, vnode->status.mtime_server); | 80 | status->mtime_client, status->mtime_server); |
76 | vnode->vfs_inode.i_ctime.tv_sec = vnode->status.mtime_server; | 81 | |
77 | vnode->vfs_inode.i_mtime = vnode->vfs_inode.i_ctime; | 82 | if (vnode) { |
78 | vnode->vfs_inode.i_atime = vnode->vfs_inode.i_ctime; | 83 | status->parent.vid = vnode->fid.vid; |
79 | 84 | if (changed && !test_bit(AFS_VNODE_UNSET, &vnode->flags)) { | |
80 | if (vnode->status.data_version != data_version) { | 85 | _debug("vnode changed"); |
81 | _debug("vnode modified %llx", data_version); | 86 | i_size_write(&vnode->vfs_inode, size); |
82 | vnode->status.data_version = data_version; | 87 | vnode->vfs_inode.i_uid = status->owner; |
83 | set_bit(AFS_VNODE_MODIFIED, &vnode->flags); | 88 | vnode->vfs_inode.i_gid = status->group; |
84 | set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags); | 89 | vnode->vfs_inode.i_version = vnode->fid.unique; |
90 | vnode->vfs_inode.i_nlink = status->nlink; | ||
91 | |||
92 | mode = vnode->vfs_inode.i_mode; | ||
93 | mode &= ~S_IALLUGO; | ||
94 | mode |= status->mode; | ||
95 | barrier(); | ||
96 | vnode->vfs_inode.i_mode = mode; | ||
97 | } | ||
98 | |||
99 | vnode->vfs_inode.i_ctime.tv_sec = status->mtime_server; | ||
100 | vnode->vfs_inode.i_mtime = vnode->vfs_inode.i_ctime; | ||
101 | vnode->vfs_inode.i_atime = vnode->vfs_inode.i_ctime; | ||
102 | } | ||
103 | |||
104 | if (status->data_version != data_version) { | ||
105 | status->data_version = data_version; | ||
106 | if (vnode && !test_bit(AFS_VNODE_UNSET, &vnode->flags)) { | ||
107 | _debug("vnode modified %llx on {%x:%u}", | ||
108 | data_version, vnode->fid.vid, vnode->fid.vnode); | ||
109 | set_bit(AFS_VNODE_MODIFIED, &vnode->flags); | ||
110 | set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags); | ||
111 | } | ||
85 | } | 112 | } |
86 | } | 113 | } |
87 | 114 | ||
@@ -99,6 +126,17 @@ static void xdr_decode_AFSCallBack(const __be32 **_bp, struct afs_vnode *vnode) | |||
99 | *_bp = bp; | 126 | *_bp = bp; |
100 | } | 127 | } |
101 | 128 | ||
129 | static void xdr_decode_AFSCallBack_raw(const __be32 **_bp, | ||
130 | struct afs_callback *cb) | ||
131 | { | ||
132 | const __be32 *bp = *_bp; | ||
133 | |||
134 | cb->version = ntohl(*bp++); | ||
135 | cb->expiry = ntohl(*bp++); | ||
136 | cb->type = ntohl(*bp++); | ||
137 | *_bp = bp; | ||
138 | } | ||
139 | |||
102 | /* | 140 | /* |
103 | * decode an AFSVolSync block | 141 | * decode an AFSVolSync block |
104 | */ | 142 | */ |
@@ -122,6 +160,7 @@ static void xdr_decode_AFSVolSync(const __be32 **_bp, | |||
122 | static int afs_deliver_fs_fetch_status(struct afs_call *call, | 160 | static int afs_deliver_fs_fetch_status(struct afs_call *call, |
123 | struct sk_buff *skb, bool last) | 161 | struct sk_buff *skb, bool last) |
124 | { | 162 | { |
163 | struct afs_vnode *vnode = call->reply; | ||
125 | const __be32 *bp; | 164 | const __be32 *bp; |
126 | 165 | ||
127 | _enter(",,%u", last); | 166 | _enter(",,%u", last); |
@@ -135,8 +174,8 @@ static int afs_deliver_fs_fetch_status(struct afs_call *call, | |||
135 | 174 | ||
136 | /* unmarshall the reply once we've received all of it */ | 175 | /* unmarshall the reply once we've received all of it */ |
137 | bp = call->buffer; | 176 | bp = call->buffer; |
138 | xdr_decode_AFSFetchStatus(&bp, call->reply); | 177 | xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode); |
139 | xdr_decode_AFSCallBack(&bp, call->reply); | 178 | xdr_decode_AFSCallBack(&bp, vnode); |
140 | if (call->reply2) | 179 | if (call->reply2) |
141 | xdr_decode_AFSVolSync(&bp, call->reply2); | 180 | xdr_decode_AFSVolSync(&bp, call->reply2); |
142 | 181 | ||
@@ -166,9 +205,10 @@ int afs_fs_fetch_file_status(struct afs_server *server, | |||
166 | struct afs_call *call; | 205 | struct afs_call *call; |
167 | __be32 *bp; | 206 | __be32 *bp; |
168 | 207 | ||
169 | _enter(",%x,,,", key_serial(key)); | 208 | _enter(",%x,{%x:%d},,", |
209 | key_serial(key), vnode->fid.vid, vnode->fid.vnode); | ||
170 | 210 | ||
171 | call = afs_alloc_flat_call(&afs_RXFSFetchStatus, 16, 120); | 211 | call = afs_alloc_flat_call(&afs_RXFSFetchStatus, 16, (21 + 3 + 6) * 4); |
172 | if (!call) | 212 | if (!call) |
173 | return -ENOMEM; | 213 | return -ENOMEM; |
174 | 214 | ||
@@ -194,6 +234,7 @@ int afs_fs_fetch_file_status(struct afs_server *server, | |||
194 | static int afs_deliver_fs_fetch_data(struct afs_call *call, | 234 | static int afs_deliver_fs_fetch_data(struct afs_call *call, |
195 | struct sk_buff *skb, bool last) | 235 | struct sk_buff *skb, bool last) |
196 | { | 236 | { |
237 | struct afs_vnode *vnode = call->reply; | ||
197 | const __be32 *bp; | 238 | const __be32 *bp; |
198 | struct page *page; | 239 | struct page *page; |
199 | void *buffer; | 240 | void *buffer; |
@@ -248,7 +289,8 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call, | |||
248 | 289 | ||
249 | /* extract the metadata */ | 290 | /* extract the metadata */ |
250 | case 3: | 291 | case 3: |
251 | ret = afs_extract_data(call, skb, last, call->buffer, 120); | 292 | ret = afs_extract_data(call, skb, last, call->buffer, |
293 | (21 + 3 + 6) * 4); | ||
252 | switch (ret) { | 294 | switch (ret) { |
253 | case 0: break; | 295 | case 0: break; |
254 | case -EAGAIN: return 0; | 296 | case -EAGAIN: return 0; |
@@ -256,8 +298,8 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call, | |||
256 | } | 298 | } |
257 | 299 | ||
258 | bp = call->buffer; | 300 | bp = call->buffer; |
259 | xdr_decode_AFSFetchStatus(&bp, call->reply); | 301 | xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode); |
260 | xdr_decode_AFSCallBack(&bp, call->reply); | 302 | xdr_decode_AFSCallBack(&bp, vnode); |
261 | if (call->reply2) | 303 | if (call->reply2) |
262 | xdr_decode_AFSVolSync(&bp, call->reply2); | 304 | xdr_decode_AFSVolSync(&bp, call->reply2); |
263 | 305 | ||
@@ -296,7 +338,6 @@ int afs_fs_fetch_data(struct afs_server *server, | |||
296 | struct afs_vnode *vnode, | 338 | struct afs_vnode *vnode, |
297 | off_t offset, size_t length, | 339 | off_t offset, size_t length, |
298 | struct page *buffer, | 340 | struct page *buffer, |
299 | struct afs_volsync *volsync, | ||
300 | const struct afs_wait_mode *wait_mode) | 341 | const struct afs_wait_mode *wait_mode) |
301 | { | 342 | { |
302 | struct afs_call *call; | 343 | struct afs_call *call; |
@@ -304,13 +345,13 @@ int afs_fs_fetch_data(struct afs_server *server, | |||
304 | 345 | ||
305 | _enter(""); | 346 | _enter(""); |
306 | 347 | ||
307 | call = afs_alloc_flat_call(&afs_RXFSFetchData, 24, 120); | 348 | call = afs_alloc_flat_call(&afs_RXFSFetchData, 24, (21 + 3 + 6) * 4); |
308 | if (!call) | 349 | if (!call) |
309 | return -ENOMEM; | 350 | return -ENOMEM; |
310 | 351 | ||
311 | call->key = key; | 352 | call->key = key; |
312 | call->reply = vnode; | 353 | call->reply = vnode; |
313 | call->reply2 = volsync; | 354 | call->reply2 = NULL; /* volsync */ |
314 | call->reply3 = buffer; | 355 | call->reply3 = buffer; |
315 | call->service_id = FS_SERVICE; | 356 | call->service_id = FS_SERVICE; |
316 | call->port = htons(AFS_FS_PORT); | 357 | call->port = htons(AFS_FS_PORT); |
@@ -411,3 +452,485 @@ int afs_fs_give_up_callbacks(struct afs_server *server, | |||
411 | 452 | ||
412 | return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); | 453 | return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); |
413 | } | 454 | } |
455 | |||
456 | /* | ||
457 | * deliver reply data to an FS.CreateFile or an FS.MakeDir | ||
458 | */ | ||
459 | static int afs_deliver_fs_create_vnode(struct afs_call *call, | ||
460 | struct sk_buff *skb, bool last) | ||
461 | { | ||
462 | struct afs_vnode *vnode = call->reply; | ||
463 | const __be32 *bp; | ||
464 | |||
465 | _enter("{%u},{%u},%d", call->unmarshall, skb->len, last); | ||
466 | |||
467 | afs_transfer_reply(call, skb); | ||
468 | if (!last) | ||
469 | return 0; | ||
470 | |||
471 | if (call->reply_size != call->reply_max) | ||
472 | return -EBADMSG; | ||
473 | |||
474 | /* unmarshall the reply once we've received all of it */ | ||
475 | bp = call->buffer; | ||
476 | xdr_decode_AFSFid(&bp, call->reply2); | ||
477 | xdr_decode_AFSFetchStatus(&bp, call->reply3, NULL); | ||
478 | xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode); | ||
479 | xdr_decode_AFSCallBack_raw(&bp, call->reply4); | ||
480 | /* xdr_decode_AFSVolSync(&bp, call->replyX); */ | ||
481 | |||
482 | _leave(" = 0 [done]"); | ||
483 | return 0; | ||
484 | } | ||
485 | |||
486 | /* | ||
487 | * FS.CreateFile and FS.MakeDir operation type | ||
488 | */ | ||
489 | static const struct afs_call_type afs_RXFSCreateXXXX = { | ||
490 | .name = "FS.CreateXXXX", | ||
491 | .deliver = afs_deliver_fs_create_vnode, | ||
492 | .abort_to_error = afs_abort_to_error, | ||
493 | .destructor = afs_flat_call_destructor, | ||
494 | }; | ||
495 | |||
496 | /* | ||
497 | * create a file or make a directory | ||
498 | */ | ||
499 | int afs_fs_create(struct afs_server *server, | ||
500 | struct key *key, | ||
501 | struct afs_vnode *vnode, | ||
502 | const char *name, | ||
503 | umode_t mode, | ||
504 | struct afs_fid *newfid, | ||
505 | struct afs_file_status *newstatus, | ||
506 | struct afs_callback *newcb, | ||
507 | const struct afs_wait_mode *wait_mode) | ||
508 | { | ||
509 | struct afs_call *call; | ||
510 | size_t namesz, reqsz, padsz; | ||
511 | __be32 *bp; | ||
512 | |||
513 | _enter(""); | ||
514 | |||
515 | namesz = strlen(name); | ||
516 | padsz = (4 - (namesz & 3)) & 3; | ||
517 | reqsz = (5 * 4) + namesz + padsz + (6 * 4); | ||
518 | |||
519 | call = afs_alloc_flat_call(&afs_RXFSCreateXXXX, reqsz, | ||
520 | (3 + 21 + 21 + 3 + 6) * 4); | ||
521 | if (!call) | ||
522 | return -ENOMEM; | ||
523 | |||
524 | call->key = key; | ||
525 | call->reply = vnode; | ||
526 | call->reply2 = newfid; | ||
527 | call->reply3 = newstatus; | ||
528 | call->reply4 = newcb; | ||
529 | call->service_id = FS_SERVICE; | ||
530 | call->port = htons(AFS_FS_PORT); | ||
531 | |||
532 | /* marshall the parameters */ | ||
533 | bp = call->request; | ||
534 | *bp++ = htonl(S_ISDIR(mode) ? FSMAKEDIR : FSCREATEFILE); | ||
535 | *bp++ = htonl(vnode->fid.vid); | ||
536 | *bp++ = htonl(vnode->fid.vnode); | ||
537 | *bp++ = htonl(vnode->fid.unique); | ||
538 | *bp++ = htonl(namesz); | ||
539 | memcpy(bp, name, namesz); | ||
540 | bp = (void *) bp + namesz; | ||
541 | if (padsz > 0) { | ||
542 | memset(bp, 0, padsz); | ||
543 | bp = (void *) bp + padsz; | ||
544 | } | ||
545 | *bp++ = htonl(AFS_SET_MODE); | ||
546 | *bp++ = 0; /* mtime */ | ||
547 | *bp++ = 0; /* owner */ | ||
548 | *bp++ = 0; /* group */ | ||
549 | *bp++ = htonl(mode & S_IALLUGO); /* unix mode */ | ||
550 | *bp++ = 0; /* segment size */ | ||
551 | |||
552 | return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); | ||
553 | } | ||
554 | |||
555 | /* | ||
556 | * deliver reply data to an FS.RemoveFile or FS.RemoveDir | ||
557 | */ | ||
558 | static int afs_deliver_fs_remove(struct afs_call *call, | ||
559 | struct sk_buff *skb, bool last) | ||
560 | { | ||
561 | struct afs_vnode *vnode = call->reply; | ||
562 | const __be32 *bp; | ||
563 | |||
564 | _enter("{%u},{%u},%d", call->unmarshall, skb->len, last); | ||
565 | |||
566 | afs_transfer_reply(call, skb); | ||
567 | if (!last) | ||
568 | return 0; | ||
569 | |||
570 | if (call->reply_size != call->reply_max) | ||
571 | return -EBADMSG; | ||
572 | |||
573 | /* unmarshall the reply once we've received all of it */ | ||
574 | bp = call->buffer; | ||
575 | xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode); | ||
576 | /* xdr_decode_AFSVolSync(&bp, call->replyX); */ | ||
577 | |||
578 | _leave(" = 0 [done]"); | ||
579 | return 0; | ||
580 | } | ||
581 | |||
582 | /* | ||
583 | * FS.RemoveDir/FS.RemoveFile operation type | ||
584 | */ | ||
585 | static const struct afs_call_type afs_RXFSRemoveXXXX = { | ||
586 | .name = "FS.RemoveXXXX", | ||
587 | .deliver = afs_deliver_fs_remove, | ||
588 | .abort_to_error = afs_abort_to_error, | ||
589 | .destructor = afs_flat_call_destructor, | ||
590 | }; | ||
591 | |||
592 | /* | ||
593 | * remove a file or directory | ||
594 | */ | ||
595 | int afs_fs_remove(struct afs_server *server, | ||
596 | struct key *key, | ||
597 | struct afs_vnode *vnode, | ||
598 | const char *name, | ||
599 | bool isdir, | ||
600 | const struct afs_wait_mode *wait_mode) | ||
601 | { | ||
602 | struct afs_call *call; | ||
603 | size_t namesz, reqsz, padsz; | ||
604 | __be32 *bp; | ||
605 | |||
606 | _enter(""); | ||
607 | |||
608 | namesz = strlen(name); | ||
609 | padsz = (4 - (namesz & 3)) & 3; | ||
610 | reqsz = (5 * 4) + namesz + padsz; | ||
611 | |||
612 | call = afs_alloc_flat_call(&afs_RXFSRemoveXXXX, reqsz, (21 + 6) * 4); | ||
613 | if (!call) | ||
614 | return -ENOMEM; | ||
615 | |||
616 | call->key = key; | ||
617 | call->reply = vnode; | ||
618 | call->service_id = FS_SERVICE; | ||
619 | call->port = htons(AFS_FS_PORT); | ||
620 | |||
621 | /* marshall the parameters */ | ||
622 | bp = call->request; | ||
623 | *bp++ = htonl(isdir ? FSREMOVEDIR : FSREMOVEFILE); | ||
624 | *bp++ = htonl(vnode->fid.vid); | ||
625 | *bp++ = htonl(vnode->fid.vnode); | ||
626 | *bp++ = htonl(vnode->fid.unique); | ||
627 | *bp++ = htonl(namesz); | ||
628 | memcpy(bp, name, namesz); | ||
629 | bp = (void *) bp + namesz; | ||
630 | if (padsz > 0) { | ||
631 | memset(bp, 0, padsz); | ||
632 | bp = (void *) bp + padsz; | ||
633 | } | ||
634 | |||
635 | return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); | ||
636 | } | ||
637 | |||
638 | /* | ||
639 | * deliver reply data to an FS.Link | ||
640 | */ | ||
641 | static int afs_deliver_fs_link(struct afs_call *call, | ||
642 | struct sk_buff *skb, bool last) | ||
643 | { | ||
644 | struct afs_vnode *dvnode = call->reply, *vnode = call->reply2; | ||
645 | const __be32 *bp; | ||
646 | |||
647 | _enter("{%u},{%u},%d", call->unmarshall, skb->len, last); | ||
648 | |||
649 | afs_transfer_reply(call, skb); | ||
650 | if (!last) | ||
651 | return 0; | ||
652 | |||
653 | if (call->reply_size != call->reply_max) | ||
654 | return -EBADMSG; | ||
655 | |||
656 | /* unmarshall the reply once we've received all of it */ | ||
657 | bp = call->buffer; | ||
658 | xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode); | ||
659 | xdr_decode_AFSFetchStatus(&bp, &dvnode->status, dvnode); | ||
660 | /* xdr_decode_AFSVolSync(&bp, call->replyX); */ | ||
661 | |||
662 | _leave(" = 0 [done]"); | ||
663 | return 0; | ||
664 | } | ||
665 | |||
666 | /* | ||
667 | * FS.Link operation type | ||
668 | */ | ||
669 | static const struct afs_call_type afs_RXFSLink = { | ||
670 | .name = "FS.Link", | ||
671 | .deliver = afs_deliver_fs_link, | ||
672 | .abort_to_error = afs_abort_to_error, | ||
673 | .destructor = afs_flat_call_destructor, | ||
674 | }; | ||
675 | |||
676 | /* | ||
677 | * make a hard link | ||
678 | */ | ||
679 | int afs_fs_link(struct afs_server *server, | ||
680 | struct key *key, | ||
681 | struct afs_vnode *dvnode, | ||
682 | struct afs_vnode *vnode, | ||
683 | const char *name, | ||
684 | const struct afs_wait_mode *wait_mode) | ||
685 | { | ||
686 | struct afs_call *call; | ||
687 | size_t namesz, reqsz, padsz; | ||
688 | __be32 *bp; | ||
689 | |||
690 | _enter(""); | ||
691 | |||
692 | namesz = strlen(name); | ||
693 | padsz = (4 - (namesz & 3)) & 3; | ||
694 | reqsz = (5 * 4) + namesz + padsz + (3 * 4); | ||
695 | |||
696 | call = afs_alloc_flat_call(&afs_RXFSLink, reqsz, (21 + 21 + 6) * 4); | ||
697 | if (!call) | ||
698 | return -ENOMEM; | ||
699 | |||
700 | call->key = key; | ||
701 | call->reply = dvnode; | ||
702 | call->reply2 = vnode; | ||
703 | call->service_id = FS_SERVICE; | ||
704 | call->port = htons(AFS_FS_PORT); | ||
705 | |||
706 | /* marshall the parameters */ | ||
707 | bp = call->request; | ||
708 | *bp++ = htonl(FSLINK); | ||
709 | *bp++ = htonl(dvnode->fid.vid); | ||
710 | *bp++ = htonl(dvnode->fid.vnode); | ||
711 | *bp++ = htonl(dvnode->fid.unique); | ||
712 | *bp++ = htonl(namesz); | ||
713 | memcpy(bp, name, namesz); | ||
714 | bp = (void *) bp + namesz; | ||
715 | if (padsz > 0) { | ||
716 | memset(bp, 0, padsz); | ||
717 | bp = (void *) bp + padsz; | ||
718 | } | ||
719 | *bp++ = htonl(vnode->fid.vid); | ||
720 | *bp++ = htonl(vnode->fid.vnode); | ||
721 | *bp++ = htonl(vnode->fid.unique); | ||
722 | |||
723 | return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); | ||
724 | } | ||
725 | |||
726 | /* | ||
727 | * deliver reply data to an FS.Symlink | ||
728 | */ | ||
729 | static int afs_deliver_fs_symlink(struct afs_call *call, | ||
730 | struct sk_buff *skb, bool last) | ||
731 | { | ||
732 | struct afs_vnode *vnode = call->reply; | ||
733 | const __be32 *bp; | ||
734 | |||
735 | _enter("{%u},{%u},%d", call->unmarshall, skb->len, last); | ||
736 | |||
737 | afs_transfer_reply(call, skb); | ||
738 | if (!last) | ||
739 | return 0; | ||
740 | |||
741 | if (call->reply_size != call->reply_max) | ||
742 | return -EBADMSG; | ||
743 | |||
744 | /* unmarshall the reply once we've received all of it */ | ||
745 | bp = call->buffer; | ||
746 | xdr_decode_AFSFid(&bp, call->reply2); | ||
747 | xdr_decode_AFSFetchStatus(&bp, call->reply3, NULL); | ||
748 | xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode); | ||
749 | /* xdr_decode_AFSVolSync(&bp, call->replyX); */ | ||
750 | |||
751 | _leave(" = 0 [done]"); | ||
752 | return 0; | ||
753 | } | ||
754 | |||
755 | /* | ||
756 | * FS.Symlink operation type | ||
757 | */ | ||
758 | static const struct afs_call_type afs_RXFSSymlink = { | ||
759 | .name = "FS.Symlink", | ||
760 | .deliver = afs_deliver_fs_symlink, | ||
761 | .abort_to_error = afs_abort_to_error, | ||
762 | .destructor = afs_flat_call_destructor, | ||
763 | }; | ||
764 | |||
765 | /* | ||
766 | * create a symbolic link | ||
767 | */ | ||
768 | int afs_fs_symlink(struct afs_server *server, | ||
769 | struct key *key, | ||
770 | struct afs_vnode *vnode, | ||
771 | const char *name, | ||
772 | const char *contents, | ||
773 | struct afs_fid *newfid, | ||
774 | struct afs_file_status *newstatus, | ||
775 | const struct afs_wait_mode *wait_mode) | ||
776 | { | ||
777 | struct afs_call *call; | ||
778 | size_t namesz, reqsz, padsz, c_namesz, c_padsz; | ||
779 | __be32 *bp; | ||
780 | |||
781 | _enter(""); | ||
782 | |||
783 | namesz = strlen(name); | ||
784 | padsz = (4 - (namesz & 3)) & 3; | ||
785 | |||
786 | c_namesz = strlen(contents); | ||
787 | c_padsz = (4 - (c_namesz & 3)) & 3; | ||
788 | |||
789 | reqsz = (6 * 4) + namesz + padsz + c_namesz + c_padsz + (6 * 4); | ||
790 | |||
791 | call = afs_alloc_flat_call(&afs_RXFSSymlink, reqsz, | ||
792 | (3 + 21 + 21 + 6) * 4); | ||
793 | if (!call) | ||
794 | return -ENOMEM; | ||
795 | |||
796 | call->key = key; | ||
797 | call->reply = vnode; | ||
798 | call->reply2 = newfid; | ||
799 | call->reply3 = newstatus; | ||
800 | call->service_id = FS_SERVICE; | ||
801 | call->port = htons(AFS_FS_PORT); | ||
802 | |||
803 | /* marshall the parameters */ | ||
804 | bp = call->request; | ||
805 | *bp++ = htonl(FSSYMLINK); | ||
806 | *bp++ = htonl(vnode->fid.vid); | ||
807 | *bp++ = htonl(vnode->fid.vnode); | ||
808 | *bp++ = htonl(vnode->fid.unique); | ||
809 | *bp++ = htonl(namesz); | ||
810 | memcpy(bp, name, namesz); | ||
811 | bp = (void *) bp + namesz; | ||
812 | if (padsz > 0) { | ||
813 | memset(bp, 0, padsz); | ||
814 | bp = (void *) bp + padsz; | ||
815 | } | ||
816 | *bp++ = htonl(c_namesz); | ||
817 | memcpy(bp, contents, c_namesz); | ||
818 | bp = (void *) bp + c_namesz; | ||
819 | if (c_padsz > 0) { | ||
820 | memset(bp, 0, c_padsz); | ||
821 | bp = (void *) bp + c_padsz; | ||
822 | } | ||
823 | *bp++ = htonl(AFS_SET_MODE); | ||
824 | *bp++ = 0; /* mtime */ | ||
825 | *bp++ = 0; /* owner */ | ||
826 | *bp++ = 0; /* group */ | ||
827 | *bp++ = htonl(S_IRWXUGO); /* unix mode */ | ||
828 | *bp++ = 0; /* segment size */ | ||
829 | |||
830 | return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); | ||
831 | } | ||
832 | |||
833 | /* | ||
834 | * deliver reply data to an FS.Rename | ||
835 | */ | ||
836 | static int afs_deliver_fs_rename(struct afs_call *call, | ||
837 | struct sk_buff *skb, bool last) | ||
838 | { | ||
839 | struct afs_vnode *orig_dvnode = call->reply, *new_dvnode = call->reply2; | ||
840 | const __be32 *bp; | ||
841 | |||
842 | _enter("{%u},{%u},%d", call->unmarshall, skb->len, last); | ||
843 | |||
844 | afs_transfer_reply(call, skb); | ||
845 | if (!last) | ||
846 | return 0; | ||
847 | |||
848 | if (call->reply_size != call->reply_max) | ||
849 | return -EBADMSG; | ||
850 | |||
851 | /* unmarshall the reply once we've received all of it */ | ||
852 | bp = call->buffer; | ||
853 | xdr_decode_AFSFetchStatus(&bp, &orig_dvnode->status, orig_dvnode); | ||
854 | if (new_dvnode != orig_dvnode) | ||
855 | xdr_decode_AFSFetchStatus(&bp, &new_dvnode->status, new_dvnode); | ||
856 | /* xdr_decode_AFSVolSync(&bp, call->replyX); */ | ||
857 | |||
858 | _leave(" = 0 [done]"); | ||
859 | return 0; | ||
860 | } | ||
861 | |||
862 | /* | ||
863 | * FS.Rename operation type | ||
864 | */ | ||
865 | static const struct afs_call_type afs_RXFSRename = { | ||
866 | .name = "FS.Rename", | ||
867 | .deliver = afs_deliver_fs_rename, | ||
868 | .abort_to_error = afs_abort_to_error, | ||
869 | .destructor = afs_flat_call_destructor, | ||
870 | }; | ||
871 | |||
872 | /* | ||
873 | * create a symbolic link | ||
874 | */ | ||
875 | int afs_fs_rename(struct afs_server *server, | ||
876 | struct key *key, | ||
877 | struct afs_vnode *orig_dvnode, | ||
878 | const char *orig_name, | ||
879 | struct afs_vnode *new_dvnode, | ||
880 | const char *new_name, | ||
881 | const struct afs_wait_mode *wait_mode) | ||
882 | { | ||
883 | struct afs_call *call; | ||
884 | size_t reqsz, o_namesz, o_padsz, n_namesz, n_padsz; | ||
885 | __be32 *bp; | ||
886 | |||
887 | _enter(""); | ||
888 | |||
889 | o_namesz = strlen(orig_name); | ||
890 | o_padsz = (4 - (o_namesz & 3)) & 3; | ||
891 | |||
892 | n_namesz = strlen(new_name); | ||
893 | n_padsz = (4 - (n_namesz & 3)) & 3; | ||
894 | |||
895 | reqsz = (4 * 4) + | ||
896 | 4 + o_namesz + o_padsz + | ||
897 | (3 * 4) + | ||
898 | 4 + n_namesz + n_padsz; | ||
899 | |||
900 | call = afs_alloc_flat_call(&afs_RXFSRename, reqsz, (21 + 21 + 6) * 4); | ||
901 | if (!call) | ||
902 | return -ENOMEM; | ||
903 | |||
904 | call->key = key; | ||
905 | call->reply = orig_dvnode; | ||
906 | call->reply2 = new_dvnode; | ||
907 | call->service_id = FS_SERVICE; | ||
908 | call->port = htons(AFS_FS_PORT); | ||
909 | |||
910 | /* marshall the parameters */ | ||
911 | bp = call->request; | ||
912 | *bp++ = htonl(FSRENAME); | ||
913 | *bp++ = htonl(orig_dvnode->fid.vid); | ||
914 | *bp++ = htonl(orig_dvnode->fid.vnode); | ||
915 | *bp++ = htonl(orig_dvnode->fid.unique); | ||
916 | *bp++ = htonl(o_namesz); | ||
917 | memcpy(bp, orig_name, o_namesz); | ||
918 | bp = (void *) bp + o_namesz; | ||
919 | if (o_padsz > 0) { | ||
920 | memset(bp, 0, o_padsz); | ||
921 | bp = (void *) bp + o_padsz; | ||
922 | } | ||
923 | |||
924 | *bp++ = htonl(new_dvnode->fid.vid); | ||
925 | *bp++ = htonl(new_dvnode->fid.vnode); | ||
926 | *bp++ = htonl(new_dvnode->fid.unique); | ||
927 | *bp++ = htonl(n_namesz); | ||
928 | memcpy(bp, new_name, n_namesz); | ||
929 | bp = (void *) bp + n_namesz; | ||
930 | if (n_padsz > 0) { | ||
931 | memset(bp, 0, n_padsz); | ||
932 | bp = (void *) bp + n_padsz; | ||
933 | } | ||
934 | |||
935 | return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); | ||
936 | } | ||
diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 227336228299..56ca8581b37f 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c | |||
@@ -33,7 +33,7 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key) | |||
33 | { | 33 | { |
34 | struct inode *inode = AFS_VNODE_TO_I(vnode); | 34 | struct inode *inode = AFS_VNODE_TO_I(vnode); |
35 | 35 | ||
36 | _debug("FS: ft=%d lk=%d sz=%Zu ver=%Lu mod=%hu", | 36 | _debug("FS: ft=%d lk=%d sz=%llu ver=%Lu mod=%hu", |
37 | vnode->status.type, | 37 | vnode->status.type, |
38 | vnode->status.nlink, | 38 | vnode->status.nlink, |
39 | vnode->status.size, | 39 | vnode->status.size, |
@@ -115,8 +115,9 @@ static int afs_iget5_set(struct inode *inode, void *opaque) | |||
115 | /* | 115 | /* |
116 | * inode retrieval | 116 | * inode retrieval |
117 | */ | 117 | */ |
118 | inline struct inode *afs_iget(struct super_block *sb, struct key *key, | 118 | struct inode *afs_iget(struct super_block *sb, struct key *key, |
119 | struct afs_fid *fid) | 119 | struct afs_fid *fid, struct afs_file_status *status, |
120 | struct afs_callback *cb) | ||
120 | { | 121 | { |
121 | struct afs_iget_data data = { .fid = *fid }; | 122 | struct afs_iget_data data = { .fid = *fid }; |
122 | struct afs_super_info *as; | 123 | struct afs_super_info *as; |
@@ -156,16 +157,37 @@ inline struct inode *afs_iget(struct super_block *sb, struct key *key, | |||
156 | &vnode->cache); | 157 | &vnode->cache); |
157 | #endif | 158 | #endif |
158 | 159 | ||
159 | /* okay... it's a new inode */ | 160 | if (!status) { |
160 | set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); | 161 | /* it's a remotely extant inode */ |
161 | ret = afs_vnode_fetch_status(vnode, NULL, key); | 162 | set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); |
162 | if (ret < 0) | 163 | ret = afs_vnode_fetch_status(vnode, NULL, key); |
163 | goto bad_inode; | 164 | if (ret < 0) |
165 | goto bad_inode; | ||
166 | } else { | ||
167 | /* it's an inode we just created */ | ||
168 | memcpy(&vnode->status, status, sizeof(vnode->status)); | ||
169 | |||
170 | if (!cb) { | ||
171 | /* it's a symlink we just created (the fileserver | ||
172 | * didn't give us a callback) */ | ||
173 | vnode->cb_version = 0; | ||
174 | vnode->cb_expiry = 0; | ||
175 | vnode->cb_type = 0; | ||
176 | vnode->cb_expires = get_seconds(); | ||
177 | } else { | ||
178 | vnode->cb_version = cb->version; | ||
179 | vnode->cb_expiry = cb->expiry; | ||
180 | vnode->cb_type = cb->type; | ||
181 | vnode->cb_expires = vnode->cb_expiry + get_seconds(); | ||
182 | } | ||
183 | } | ||
184 | |||
164 | ret = afs_inode_map_status(vnode, key); | 185 | ret = afs_inode_map_status(vnode, key); |
165 | if (ret < 0) | 186 | if (ret < 0) |
166 | goto bad_inode; | 187 | goto bad_inode; |
167 | 188 | ||
168 | /* success */ | 189 | /* success */ |
190 | clear_bit(AFS_VNODE_UNSET, &vnode->flags); | ||
169 | inode->i_flags |= S_NOATIME; | 191 | inode->i_flags |= S_NOATIME; |
170 | unlock_new_inode(inode); | 192 | unlock_new_inode(inode); |
171 | _leave(" = %p [CB { v=%u t=%u }]", inode, vnode->cb_version, vnode->cb_type); | 193 | _leave(" = %p [CB { v=%u t=%u }]", inode, vnode->cb_version, vnode->cb_type); |
@@ -182,6 +204,78 @@ bad_inode: | |||
182 | } | 204 | } |
183 | 205 | ||
184 | /* | 206 | /* |
207 | * validate a vnode/inode | ||
208 | * - there are several things we need to check | ||
209 | * - parent dir data changes (rm, rmdir, rename, mkdir, create, link, | ||
210 | * symlink) | ||
211 | * - parent dir metadata changed (security changes) | ||
212 | * - dentry data changed (write, truncate) | ||
213 | * - dentry metadata changed (security changes) | ||
214 | */ | ||
215 | int afs_validate(struct afs_vnode *vnode, struct key *key) | ||
216 | { | ||
217 | int ret; | ||
218 | |||
219 | _enter("{v={%x:%u} fl=%lx},%x", | ||
220 | vnode->fid.vid, vnode->fid.vnode, vnode->flags, | ||
221 | key_serial(key)); | ||
222 | |||
223 | if (vnode->cb_promised && | ||
224 | !test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) && | ||
225 | !test_bit(AFS_VNODE_MODIFIED, &vnode->flags) && | ||
226 | !test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) { | ||
227 | if (vnode->cb_expires < get_seconds() + 10) { | ||
228 | _debug("callback expired"); | ||
229 | set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); | ||
230 | } else { | ||
231 | goto valid; | ||
232 | } | ||
233 | } | ||
234 | |||
235 | if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) | ||
236 | goto valid; | ||
237 | |||
238 | mutex_lock(&vnode->validate_lock); | ||
239 | |||
240 | /* if the promise has expired, we need to check the server again to get | ||
241 | * a new promise - note that if the (parent) directory's metadata was | ||
242 | * changed then the security may be different and we may no longer have | ||
243 | * access */ | ||
244 | if (!vnode->cb_promised || | ||
245 | test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) { | ||
246 | _debug("not promised"); | ||
247 | ret = afs_vnode_fetch_status(vnode, NULL, key); | ||
248 | if (ret < 0) | ||
249 | goto error_unlock; | ||
250 | _debug("new promise [fl=%lx]", vnode->flags); | ||
251 | } | ||
252 | |||
253 | if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { | ||
254 | _debug("file already deleted"); | ||
255 | ret = -ESTALE; | ||
256 | goto error_unlock; | ||
257 | } | ||
258 | |||
259 | /* if the vnode's data version number changed then its contents are | ||
260 | * different */ | ||
261 | if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) { | ||
262 | _debug("zap data {%x:%d}", vnode->fid.vid, vnode->fid.vnode); | ||
263 | invalidate_remote_inode(&vnode->vfs_inode); | ||
264 | } | ||
265 | |||
266 | clear_bit(AFS_VNODE_MODIFIED, &vnode->flags); | ||
267 | mutex_unlock(&vnode->validate_lock); | ||
268 | valid: | ||
269 | _leave(" = 0"); | ||
270 | return 0; | ||
271 | |||
272 | error_unlock: | ||
273 | mutex_unlock(&vnode->validate_lock); | ||
274 | _leave(" = %d", ret); | ||
275 | return ret; | ||
276 | } | ||
277 | |||
278 | /* | ||
185 | * read the attributes of an inode | 279 | * read the attributes of an inode |
186 | */ | 280 | */ |
187 | int afs_inode_getattr(struct vfsmount *mnt, struct dentry *dentry, | 281 | int afs_inode_getattr(struct vfsmount *mnt, struct dentry *dentry, |
@@ -207,9 +301,10 @@ void afs_clear_inode(struct inode *inode) | |||
207 | 301 | ||
208 | vnode = AFS_FS_I(inode); | 302 | vnode = AFS_FS_I(inode); |
209 | 303 | ||
210 | _enter("ino=%lu { vn=%08x v=%u x=%u t=%u }", | 304 | _enter("{%x:%d.%d} v=%u x=%u t=%u }", |
211 | inode->i_ino, | 305 | vnode->fid.vid, |
212 | vnode->fid.vnode, | 306 | vnode->fid.vnode, |
307 | vnode->fid.unique, | ||
213 | vnode->cb_version, | 308 | vnode->cb_version, |
214 | vnode->cb_expiry, | 309 | vnode->cb_expiry, |
215 | vnode->cb_type); | 310 | vnode->cb_type); |
diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 6120d4bd19e0..73bfa0b2d99e 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h | |||
@@ -80,6 +80,7 @@ struct afs_call { | |||
80 | void *reply; /* reply buffer (first part) */ | 80 | void *reply; /* reply buffer (first part) */ |
81 | void *reply2; /* reply buffer (second part) */ | 81 | void *reply2; /* reply buffer (second part) */ |
82 | void *reply3; /* reply buffer (third part) */ | 82 | void *reply3; /* reply buffer (third part) */ |
83 | void *reply4; /* reply buffer (fourth part) */ | ||
83 | enum { /* call state */ | 84 | enum { /* call state */ |
84 | AFS_CALL_REQUESTING, /* request is being sent for outgoing call */ | 85 | AFS_CALL_REQUESTING, /* request is being sent for outgoing call */ |
85 | AFS_CALL_AWAIT_REPLY, /* awaiting reply to outgoing call */ | 86 | AFS_CALL_AWAIT_REPLY, /* awaiting reply to outgoing call */ |
@@ -300,19 +301,18 @@ struct afs_vnode { | |||
300 | #endif | 301 | #endif |
301 | struct afs_permits *permits; /* cache of permits so far obtained */ | 302 | struct afs_permits *permits; /* cache of permits so far obtained */ |
302 | struct mutex permits_lock; /* lock for altering permits list */ | 303 | struct mutex permits_lock; /* lock for altering permits list */ |
304 | struct mutex validate_lock; /* lock for validating this vnode */ | ||
303 | wait_queue_head_t update_waitq; /* status fetch waitqueue */ | 305 | wait_queue_head_t update_waitq; /* status fetch waitqueue */ |
304 | unsigned update_cnt; /* number of outstanding ops that will update the | 306 | int update_cnt; /* number of outstanding ops that will update the |
305 | * status */ | 307 | * status */ |
306 | spinlock_t lock; /* waitqueue/flags lock */ | 308 | spinlock_t lock; /* waitqueue/flags lock */ |
307 | unsigned long flags; | 309 | unsigned long flags; |
308 | #define AFS_VNODE_CB_BROKEN 0 /* set if vnode's callback was broken */ | 310 | #define AFS_VNODE_CB_BROKEN 0 /* set if vnode's callback was broken */ |
309 | #define AFS_VNODE_CHANGED 1 /* set if vnode's metadata changed */ | 311 | #define AFS_VNODE_UNSET 1 /* set if vnode attributes not yet set */ |
310 | #define AFS_VNODE_MODIFIED 2 /* set if vnode's data modified */ | 312 | #define AFS_VNODE_MODIFIED 2 /* set if vnode's data modified */ |
311 | #define AFS_VNODE_ZAP_DATA 3 /* set if vnode's data should be invalidated */ | 313 | #define AFS_VNODE_ZAP_DATA 3 /* set if vnode's data should be invalidated */ |
312 | #define AFS_VNODE_DELETED 4 /* set if vnode deleted on server */ | 314 | #define AFS_VNODE_DELETED 4 /* set if vnode deleted on server */ |
313 | #define AFS_VNODE_MOUNTPOINT 5 /* set if vnode is a mountpoint symlink */ | 315 | #define AFS_VNODE_MOUNTPOINT 5 /* set if vnode is a mountpoint symlink */ |
314 | #define AFS_VNODE_DIR_CHANGED 6 /* set if vnode's parent dir metadata changed */ | ||
315 | #define AFS_VNODE_DIR_MODIFIED 7 /* set if vnode's parent dir data modified */ | ||
316 | 316 | ||
317 | long acl_order; /* ACL check count (callback break count) */ | 317 | long acl_order; /* ACL check count (callback break count) */ |
318 | 318 | ||
@@ -320,7 +320,6 @@ struct afs_vnode { | |||
320 | struct rb_node server_rb; /* link in server->fs_vnodes */ | 320 | struct rb_node server_rb; /* link in server->fs_vnodes */ |
321 | struct rb_node cb_promise; /* link in server->cb_promises */ | 321 | struct rb_node cb_promise; /* link in server->cb_promises */ |
322 | struct work_struct cb_broken_work; /* work to be done on callback break */ | 322 | struct work_struct cb_broken_work; /* work to be done on callback break */ |
323 | struct mutex cb_broken_lock; /* lock against multiple attempts to fix break */ | ||
324 | time_t cb_expires; /* time at which callback expires */ | 323 | time_t cb_expires; /* time at which callback expires */ |
325 | time_t cb_expires_at; /* time used to order cb_promise */ | 324 | time_t cb_expires_at; /* time used to order cb_promise */ |
326 | unsigned cb_version; /* callback version */ | 325 | unsigned cb_version; /* callback version */ |
@@ -388,6 +387,7 @@ extern void afs_init_callback_state(struct afs_server *); | |||
388 | extern void afs_broken_callback_work(struct work_struct *); | 387 | extern void afs_broken_callback_work(struct work_struct *); |
389 | extern void afs_break_callbacks(struct afs_server *, size_t, | 388 | extern void afs_break_callbacks(struct afs_server *, size_t, |
390 | struct afs_callback[]); | 389 | struct afs_callback[]); |
390 | extern void afs_discard_callback_on_delete(struct afs_vnode *); | ||
391 | extern void afs_give_up_callback(struct afs_vnode *); | 391 | extern void afs_give_up_callback(struct afs_vnode *); |
392 | extern void afs_dispatch_give_up_callbacks(struct work_struct *); | 392 | extern void afs_dispatch_give_up_callbacks(struct work_struct *); |
393 | extern void afs_flush_callback_breaks(struct afs_server *); | 393 | extern void afs_flush_callback_breaks(struct afs_server *); |
@@ -448,14 +448,34 @@ extern int afs_fs_give_up_callbacks(struct afs_server *, | |||
448 | const struct afs_wait_mode *); | 448 | const struct afs_wait_mode *); |
449 | extern int afs_fs_fetch_data(struct afs_server *, struct key *, | 449 | extern int afs_fs_fetch_data(struct afs_server *, struct key *, |
450 | struct afs_vnode *, off_t, size_t, struct page *, | 450 | struct afs_vnode *, off_t, size_t, struct page *, |
451 | struct afs_volsync *, | ||
452 | const struct afs_wait_mode *); | 451 | const struct afs_wait_mode *); |
452 | extern int afs_fs_create(struct afs_server *, struct key *, | ||
453 | struct afs_vnode *, const char *, umode_t, | ||
454 | struct afs_fid *, struct afs_file_status *, | ||
455 | struct afs_callback *, | ||
456 | const struct afs_wait_mode *); | ||
457 | extern int afs_fs_remove(struct afs_server *, struct key *, | ||
458 | struct afs_vnode *, const char *, bool, | ||
459 | const struct afs_wait_mode *); | ||
460 | extern int afs_fs_link(struct afs_server *, struct key *, struct afs_vnode *, | ||
461 | struct afs_vnode *, const char *, | ||
462 | const struct afs_wait_mode *); | ||
463 | extern int afs_fs_symlink(struct afs_server *, struct key *, | ||
464 | struct afs_vnode *, const char *, const char *, | ||
465 | struct afs_fid *, struct afs_file_status *, | ||
466 | const struct afs_wait_mode *); | ||
467 | extern int afs_fs_rename(struct afs_server *, struct key *, | ||
468 | struct afs_vnode *, const char *, | ||
469 | struct afs_vnode *, const char *, | ||
470 | const struct afs_wait_mode *); | ||
453 | 471 | ||
454 | /* | 472 | /* |
455 | * inode.c | 473 | * inode.c |
456 | */ | 474 | */ |
457 | extern struct inode *afs_iget(struct super_block *, struct key *, | 475 | extern struct inode *afs_iget(struct super_block *, struct key *, |
458 | struct afs_fid *); | 476 | struct afs_fid *, struct afs_file_status *, |
477 | struct afs_callback *); | ||
478 | extern int afs_validate(struct afs_vnode *, struct key *); | ||
459 | extern int afs_inode_getattr(struct vfsmount *, struct dentry *, | 479 | extern int afs_inode_getattr(struct vfsmount *, struct dentry *, |
460 | struct kstat *); | 480 | struct kstat *); |
461 | extern void afs_zap_permits(struct rcu_head *); | 481 | extern void afs_zap_permits(struct rcu_head *); |
@@ -522,7 +542,11 @@ extern int afs_permission(struct inode *, int, struct nameidata *); | |||
522 | */ | 542 | */ |
523 | extern spinlock_t afs_server_peer_lock; | 543 | extern spinlock_t afs_server_peer_lock; |
524 | 544 | ||
525 | #define afs_get_server(S) do { atomic_inc(&(S)->usage); } while(0) | 545 | #define afs_get_server(S) \ |
546 | do { \ | ||
547 | _debug("GET SERVER %d", atomic_read(&(S)->usage)); \ | ||
548 | atomic_inc(&(S)->usage); \ | ||
549 | } while(0) | ||
526 | 550 | ||
527 | extern struct afs_server *afs_lookup_server(struct afs_cell *, | 551 | extern struct afs_server *afs_lookup_server(struct afs_cell *, |
528 | const struct in_addr *); | 552 | const struct in_addr *); |
@@ -588,10 +612,24 @@ static inline struct inode *AFS_VNODE_TO_I(struct afs_vnode *vnode) | |||
588 | return &vnode->vfs_inode; | 612 | return &vnode->vfs_inode; |
589 | } | 613 | } |
590 | 614 | ||
615 | extern void afs_vnode_finalise_status_update(struct afs_vnode *, | ||
616 | struct afs_server *); | ||
591 | extern int afs_vnode_fetch_status(struct afs_vnode *, struct afs_vnode *, | 617 | extern int afs_vnode_fetch_status(struct afs_vnode *, struct afs_vnode *, |
592 | struct key *); | 618 | struct key *); |
593 | extern int afs_vnode_fetch_data(struct afs_vnode *, struct key *, | 619 | extern int afs_vnode_fetch_data(struct afs_vnode *, struct key *, |
594 | off_t, size_t, struct page *); | 620 | off_t, size_t, struct page *); |
621 | extern int afs_vnode_create(struct afs_vnode *, struct key *, const char *, | ||
622 | umode_t, struct afs_fid *, struct afs_file_status *, | ||
623 | struct afs_callback *, struct afs_server **); | ||
624 | extern int afs_vnode_remove(struct afs_vnode *, struct key *, const char *, | ||
625 | bool); | ||
626 | extern int afs_vnode_link(struct afs_vnode *, struct afs_vnode *, struct key *, | ||
627 | const char *); | ||
628 | extern int afs_vnode_symlink(struct afs_vnode *, struct key *, const char *, | ||
629 | const char *, struct afs_fid *, | ||
630 | struct afs_file_status *, struct afs_server **); | ||
631 | extern int afs_vnode_rename(struct afs_vnode *, struct afs_vnode *, | ||
632 | struct key *, const char *, const char *); | ||
595 | 633 | ||
596 | /* | 634 | /* |
597 | * volume.c | 635 | * volume.c |
diff --git a/fs/afs/misc.c b/fs/afs/misc.c index 98e9276c46a2..cdb9792d8161 100644 --- a/fs/afs/misc.c +++ b/fs/afs/misc.c | |||
@@ -22,6 +22,7 @@ int afs_abort_to_error(u32 abort_code) | |||
22 | { | 22 | { |
23 | switch (abort_code) { | 23 | switch (abort_code) { |
24 | case 13: return -EACCES; | 24 | case 13: return -EACCES; |
25 | case 30: return -EROFS; | ||
25 | case VSALVAGE: return -EIO; | 26 | case VSALVAGE: return -EIO; |
26 | case VNOVNODE: return -ENOENT; | 27 | case VNOVNODE: return -ENOENT; |
27 | case VNOVOL: return -ENOMEDIUM; | 28 | case VNOVOL: return -ENOMEDIUM; |
@@ -33,6 +34,24 @@ int afs_abort_to_error(u32 abort_code) | |||
33 | case VOVERQUOTA: return -EDQUOT; | 34 | case VOVERQUOTA: return -EDQUOT; |
34 | case VBUSY: return -EBUSY; | 35 | case VBUSY: return -EBUSY; |
35 | case VMOVED: return -ENXIO; | 36 | case VMOVED: return -ENXIO; |
36 | default: return -EIO; | 37 | case 0x2f6df0c: return -EACCES; |
38 | case 0x2f6df0f: return -EBUSY; | ||
39 | case 0x2f6df10: return -EEXIST; | ||
40 | case 0x2f6df11: return -EXDEV; | ||
41 | case 0x2f6df13: return -ENOTDIR; | ||
42 | case 0x2f6df14: return -EISDIR; | ||
43 | case 0x2f6df15: return -EINVAL; | ||
44 | case 0x2f6df1a: return -EFBIG; | ||
45 | case 0x2f6df1b: return -ENOSPC; | ||
46 | case 0x2f6df1d: return -EROFS; | ||
47 | case 0x2f6df1e: return -EMLINK; | ||
48 | case 0x2f6df20: return -EDOM; | ||
49 | case 0x2f6df21: return -ERANGE; | ||
50 | case 0x2f6df22: return -EDEADLK; | ||
51 | case 0x2f6df23: return -ENAMETOOLONG; | ||
52 | case 0x2f6df24: return -ENOLCK; | ||
53 | case 0x2f6df26: return -ENOTEMPTY; | ||
54 | case 0x2f6df78: return -EDQUOT; | ||
55 | default: return -EREMOTEIO; | ||
37 | } | 56 | } |
38 | } | 57 | } |
diff --git a/fs/afs/security.c b/fs/afs/security.c index cbdd7f7162fa..f9f424d80458 100644 --- a/fs/afs/security.c +++ b/fs/afs/security.c | |||
@@ -92,7 +92,7 @@ static struct afs_vnode *afs_get_auth_inode(struct afs_vnode *vnode, | |||
92 | ASSERT(auth_inode != NULL); | 92 | ASSERT(auth_inode != NULL); |
93 | } else { | 93 | } else { |
94 | auth_inode = afs_iget(vnode->vfs_inode.i_sb, key, | 94 | auth_inode = afs_iget(vnode->vfs_inode.i_sb, key, |
95 | &vnode->status.parent); | 95 | &vnode->status.parent, NULL, NULL); |
96 | if (IS_ERR(auth_inode)) | 96 | if (IS_ERR(auth_inode)) |
97 | return ERR_PTR(PTR_ERR(auth_inode)); | 97 | return ERR_PTR(PTR_ERR(auth_inode)); |
98 | } | 98 | } |
@@ -288,7 +288,8 @@ int afs_permission(struct inode *inode, int mask, struct nameidata *nd) | |||
288 | struct key *key; | 288 | struct key *key; |
289 | int ret; | 289 | int ret; |
290 | 290 | ||
291 | _enter("{%x:%x},%x,", vnode->fid.vid, vnode->fid.vnode, mask); | 291 | _enter("{{%x:%x},%lx},%x,", |
292 | vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask); | ||
292 | 293 | ||
293 | key = afs_request_key(vnode->volume->cell); | 294 | key = afs_request_key(vnode->volume->cell); |
294 | if (IS_ERR(key)) { | 295 | if (IS_ERR(key)) { |
@@ -296,13 +297,19 @@ int afs_permission(struct inode *inode, int mask, struct nameidata *nd) | |||
296 | return PTR_ERR(key); | 297 | return PTR_ERR(key); |
297 | } | 298 | } |
298 | 299 | ||
300 | /* if the promise has expired, we need to check the server again */ | ||
301 | if (!vnode->cb_promised) { | ||
302 | _debug("not promised"); | ||
303 | ret = afs_vnode_fetch_status(vnode, NULL, key); | ||
304 | if (ret < 0) | ||
305 | goto error; | ||
306 | _debug("new promise [fl=%lx]", vnode->flags); | ||
307 | } | ||
308 | |||
299 | /* check the permits to see if we've got one yet */ | 309 | /* check the permits to see if we've got one yet */ |
300 | ret = afs_check_permit(vnode, key, &access); | 310 | ret = afs_check_permit(vnode, key, &access); |
301 | if (ret < 0) { | 311 | if (ret < 0) |
302 | key_put(key); | 312 | goto error; |
303 | _leave(" = %d [check]", ret); | ||
304 | return ret; | ||
305 | } | ||
306 | 313 | ||
307 | /* interpret the access mask */ | 314 | /* interpret the access mask */ |
308 | _debug("REQ %x ACC %x on %s", | 315 | _debug("REQ %x ACC %x on %s", |
@@ -336,10 +343,14 @@ int afs_permission(struct inode *inode, int mask, struct nameidata *nd) | |||
336 | } | 343 | } |
337 | 344 | ||
338 | key_put(key); | 345 | key_put(key); |
339 | return generic_permission(inode, mask, NULL); | 346 | ret = generic_permission(inode, mask, NULL); |
347 | _leave(" = %d", ret); | ||
348 | return ret; | ||
340 | 349 | ||
341 | permission_denied: | 350 | permission_denied: |
351 | ret = -EACCES; | ||
352 | error: | ||
342 | key_put(key); | 353 | key_put(key); |
343 | _leave(" = -EACCES"); | 354 | _leave(" = %d", ret); |
344 | return -EACCES; | 355 | return ret; |
345 | } | 356 | } |
diff --git a/fs/afs/server.c b/fs/afs/server.c index bde6125c2f22..96bb23b476a2 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c | |||
@@ -223,6 +223,8 @@ void afs_put_server(struct afs_server *server) | |||
223 | 223 | ||
224 | _enter("%p{%d}", server, atomic_read(&server->usage)); | 224 | _enter("%p{%d}", server, atomic_read(&server->usage)); |
225 | 225 | ||
226 | _debug("PUT SERVER %d", atomic_read(&server->usage)); | ||
227 | |||
226 | ASSERTCMP(atomic_read(&server->usage), >, 0); | 228 | ASSERTCMP(atomic_read(&server->usage), >, 0); |
227 | 229 | ||
228 | if (likely(!atomic_dec_and_test(&server->usage))) { | 230 | if (likely(!atomic_dec_and_test(&server->usage))) { |
diff --git a/fs/afs/super.c b/fs/afs/super.c index 497350a5463b..cebd03c91f57 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c | |||
@@ -331,7 +331,7 @@ static int afs_fill_super(struct super_block *sb, void *data) | |||
331 | fid.vid = as->volume->vid; | 331 | fid.vid = as->volume->vid; |
332 | fid.vnode = 1; | 332 | fid.vnode = 1; |
333 | fid.unique = 1; | 333 | fid.unique = 1; |
334 | inode = afs_iget(sb, params->key, &fid); | 334 | inode = afs_iget(sb, params->key, &fid, NULL, NULL); |
335 | if (IS_ERR(inode)) | 335 | if (IS_ERR(inode)) |
336 | goto error_inode; | 336 | goto error_inode; |
337 | 337 | ||
@@ -473,9 +473,9 @@ static void afs_i_init_once(void *_vnode, struct kmem_cache *cachep, | |||
473 | inode_init_once(&vnode->vfs_inode); | 473 | inode_init_once(&vnode->vfs_inode); |
474 | init_waitqueue_head(&vnode->update_waitq); | 474 | init_waitqueue_head(&vnode->update_waitq); |
475 | mutex_init(&vnode->permits_lock); | 475 | mutex_init(&vnode->permits_lock); |
476 | mutex_init(&vnode->validate_lock); | ||
476 | spin_lock_init(&vnode->lock); | 477 | spin_lock_init(&vnode->lock); |
477 | INIT_WORK(&vnode->cb_broken_work, afs_broken_callback_work); | 478 | INIT_WORK(&vnode->cb_broken_work, afs_broken_callback_work); |
478 | mutex_init(&vnode->cb_broken_lock); | ||
479 | } | 479 | } |
480 | } | 480 | } |
481 | 481 | ||
@@ -497,7 +497,7 @@ static struct inode *afs_alloc_inode(struct super_block *sb) | |||
497 | 497 | ||
498 | vnode->volume = NULL; | 498 | vnode->volume = NULL; |
499 | vnode->update_cnt = 0; | 499 | vnode->update_cnt = 0; |
500 | vnode->flags = 0; | 500 | vnode->flags = 1 << AFS_VNODE_UNSET; |
501 | vnode->cb_promised = false; | 501 | vnode->cb_promised = false; |
502 | 502 | ||
503 | return &vnode->vfs_inode; | 503 | return &vnode->vfs_inode; |
diff --git a/fs/afs/vnode.c b/fs/afs/vnode.c index 160097619ec7..a1904ab8426a 100644 --- a/fs/afs/vnode.c +++ b/fs/afs/vnode.c | |||
@@ -30,7 +30,7 @@ static noinline bool dump_tree_aux(struct rb_node *node, struct rb_node *parent, | |||
30 | bad = dump_tree_aux(node->rb_left, node, depth + 2, '/'); | 30 | bad = dump_tree_aux(node->rb_left, node, depth + 2, '/'); |
31 | 31 | ||
32 | vnode = rb_entry(node, struct afs_vnode, cb_promise); | 32 | vnode = rb_entry(node, struct afs_vnode, cb_promise); |
33 | kdebug("%c %*.*s%c%p {%d}", | 33 | _debug("%c %*.*s%c%p {%d}", |
34 | rb_is_red(node) ? 'R' : 'B', | 34 | rb_is_red(node) ? 'R' : 'B', |
35 | depth, depth, "", lr, | 35 | depth, depth, "", lr, |
36 | vnode, vnode->cb_expires_at); | 36 | vnode, vnode->cb_expires_at); |
@@ -47,7 +47,7 @@ static noinline bool dump_tree_aux(struct rb_node *node, struct rb_node *parent, | |||
47 | 47 | ||
48 | static noinline void dump_tree(const char *name, struct afs_server *server) | 48 | static noinline void dump_tree(const char *name, struct afs_server *server) |
49 | { | 49 | { |
50 | kenter("%s", name); | 50 | _enter("%s", name); |
51 | if (dump_tree_aux(server->cb_promises.rb_node, NULL, 0, '-')) | 51 | if (dump_tree_aux(server->cb_promises.rb_node, NULL, 0, '-')) |
52 | BUG(); | 52 | BUG(); |
53 | } | 53 | } |
@@ -187,47 +187,61 @@ static void afs_vnode_deleted_remotely(struct afs_vnode *vnode) | |||
187 | spin_unlock(&server->cb_lock); | 187 | spin_unlock(&server->cb_lock); |
188 | } | 188 | } |
189 | 189 | ||
190 | spin_lock(&vnode->server->fs_lock); | ||
191 | rb_erase(&vnode->server_rb, &vnode->server->fs_vnodes); | ||
192 | spin_unlock(&vnode->server->fs_lock); | ||
193 | |||
194 | vnode->server = NULL; | ||
190 | afs_put_server(server); | 195 | afs_put_server(server); |
191 | } | 196 | } |
192 | 197 | ||
193 | /* | 198 | /* |
194 | * finish off updating the recorded status of a file | 199 | * finish off updating the recorded status of a file after a successful |
200 | * operation completion | ||
195 | * - starts callback expiry timer | 201 | * - starts callback expiry timer |
196 | * - adds to server's callback list | 202 | * - adds to server's callback list |
197 | */ | 203 | */ |
198 | static void afs_vnode_finalise_status_update(struct afs_vnode *vnode, | 204 | void afs_vnode_finalise_status_update(struct afs_vnode *vnode, |
199 | struct afs_server *server, | 205 | struct afs_server *server) |
200 | int ret) | ||
201 | { | 206 | { |
202 | struct afs_server *oldserver = NULL; | 207 | struct afs_server *oldserver = NULL; |
203 | 208 | ||
204 | _enter("%p,%p,%d", vnode, server, ret); | 209 | _enter("%p,%p", vnode, server); |
210 | |||
211 | spin_lock(&vnode->lock); | ||
212 | clear_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); | ||
213 | afs_vnode_note_promise(vnode, server); | ||
214 | vnode->update_cnt--; | ||
215 | ASSERTCMP(vnode->update_cnt, >=, 0); | ||
216 | spin_unlock(&vnode->lock); | ||
217 | |||
218 | wake_up_all(&vnode->update_waitq); | ||
219 | afs_put_server(oldserver); | ||
220 | _leave(""); | ||
221 | } | ||
222 | |||
223 | /* | ||
224 | * finish off updating the recorded status of a file after an operation failed | ||
225 | */ | ||
226 | static void afs_vnode_status_update_failed(struct afs_vnode *vnode, int ret) | ||
227 | { | ||
228 | _enter("%p,%d", vnode, ret); | ||
205 | 229 | ||
206 | spin_lock(&vnode->lock); | 230 | spin_lock(&vnode->lock); |
207 | 231 | ||
208 | clear_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); | 232 | clear_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); |
209 | 233 | ||
210 | switch (ret) { | 234 | if (ret == -ENOENT) { |
211 | case 0: | ||
212 | afs_vnode_note_promise(vnode, server); | ||
213 | break; | ||
214 | case -ENOENT: | ||
215 | /* the file was deleted on the server */ | 235 | /* the file was deleted on the server */ |
216 | _debug("got NOENT from server - marking file deleted"); | 236 | _debug("got NOENT from server - marking file deleted"); |
217 | afs_vnode_deleted_remotely(vnode); | 237 | afs_vnode_deleted_remotely(vnode); |
218 | break; | ||
219 | default: | ||
220 | break; | ||
221 | } | 238 | } |
222 | 239 | ||
223 | vnode->update_cnt--; | 240 | vnode->update_cnt--; |
224 | 241 | ASSERTCMP(vnode->update_cnt, >=, 0); | |
225 | spin_unlock(&vnode->lock); | 242 | spin_unlock(&vnode->lock); |
226 | 243 | ||
227 | wake_up_all(&vnode->update_waitq); | 244 | wake_up_all(&vnode->update_waitq); |
228 | |||
229 | afs_put_server(oldserver); | ||
230 | |||
231 | _leave(""); | 245 | _leave(""); |
232 | } | 246 | } |
233 | 247 | ||
@@ -275,8 +289,12 @@ int afs_vnode_fetch_status(struct afs_vnode *vnode, | |||
275 | return 0; | 289 | return 0; |
276 | } | 290 | } |
277 | 291 | ||
292 | ASSERTCMP(vnode->update_cnt, >=, 0); | ||
293 | |||
278 | if (vnode->update_cnt > 0) { | 294 | if (vnode->update_cnt > 0) { |
279 | /* someone else started a fetch */ | 295 | /* someone else started a fetch */ |
296 | _debug("wait on fetch %d", vnode->update_cnt); | ||
297 | |||
280 | set_current_state(TASK_UNINTERRUPTIBLE); | 298 | set_current_state(TASK_UNINTERRUPTIBLE); |
281 | ASSERT(myself.func != NULL); | 299 | ASSERT(myself.func != NULL); |
282 | add_wait_queue(&vnode->update_waitq, &myself); | 300 | add_wait_queue(&vnode->update_waitq, &myself); |
@@ -325,7 +343,7 @@ get_anyway: | |||
325 | /* pick a server to query */ | 343 | /* pick a server to query */ |
326 | server = afs_volume_pick_fileserver(vnode); | 344 | server = afs_volume_pick_fileserver(vnode); |
327 | if (IS_ERR(server)) | 345 | if (IS_ERR(server)) |
328 | return PTR_ERR(server); | 346 | goto no_server; |
329 | 347 | ||
330 | _debug("USING SERVER: %p{%08x}", | 348 | _debug("USING SERVER: %p{%08x}", |
331 | server, ntohl(server->addr.s_addr)); | 349 | server, ntohl(server->addr.s_addr)); |
@@ -336,17 +354,34 @@ get_anyway: | |||
336 | } while (!afs_volume_release_fileserver(vnode, server, ret)); | 354 | } while (!afs_volume_release_fileserver(vnode, server, ret)); |
337 | 355 | ||
338 | /* adjust the flags */ | 356 | /* adjust the flags */ |
339 | if (ret == 0 && auth_vnode) | 357 | if (ret == 0) { |
340 | afs_cache_permit(vnode, key, acl_order); | 358 | _debug("adjust"); |
341 | afs_vnode_finalise_status_update(vnode, server, ret); | 359 | if (auth_vnode) |
360 | afs_cache_permit(vnode, key, acl_order); | ||
361 | afs_vnode_finalise_status_update(vnode, server); | ||
362 | afs_put_server(server); | ||
363 | } else { | ||
364 | _debug("failed [%d]", ret); | ||
365 | afs_vnode_status_update_failed(vnode, ret); | ||
366 | } | ||
342 | 367 | ||
343 | _leave(" = %d", ret); | 368 | ASSERTCMP(vnode->update_cnt, >=, 0); |
369 | |||
370 | _leave(" = %d [cnt %d]", ret, vnode->update_cnt); | ||
344 | return ret; | 371 | return ret; |
372 | |||
373 | no_server: | ||
374 | spin_lock(&vnode->lock); | ||
375 | vnode->update_cnt--; | ||
376 | ASSERTCMP(vnode->update_cnt, >=, 0); | ||
377 | spin_unlock(&vnode->lock); | ||
378 | _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt); | ||
379 | return PTR_ERR(server); | ||
345 | } | 380 | } |
346 | 381 | ||
347 | /* | 382 | /* |
348 | * fetch file data from the volume | 383 | * fetch file data from the volume |
349 | * - TODO implement caching and server failover | 384 | * - TODO implement caching |
350 | */ | 385 | */ |
351 | int afs_vnode_fetch_data(struct afs_vnode *vnode, struct key *key, | 386 | int afs_vnode_fetch_data(struct afs_vnode *vnode, struct key *key, |
352 | off_t offset, size_t length, struct page *page) | 387 | off_t offset, size_t length, struct page *page) |
@@ -372,18 +407,349 @@ int afs_vnode_fetch_data(struct afs_vnode *vnode, struct key *key, | |||
372 | /* pick a server to query */ | 407 | /* pick a server to query */ |
373 | server = afs_volume_pick_fileserver(vnode); | 408 | server = afs_volume_pick_fileserver(vnode); |
374 | if (IS_ERR(server)) | 409 | if (IS_ERR(server)) |
375 | return PTR_ERR(server); | 410 | goto no_server; |
376 | 411 | ||
377 | _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); | 412 | _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); |
378 | 413 | ||
379 | ret = afs_fs_fetch_data(server, key, vnode, offset, length, | 414 | ret = afs_fs_fetch_data(server, key, vnode, offset, length, |
380 | page, NULL, &afs_sync_call); | 415 | page, &afs_sync_call); |
381 | 416 | ||
382 | } while (!afs_volume_release_fileserver(vnode, server, ret)); | 417 | } while (!afs_volume_release_fileserver(vnode, server, ret)); |
383 | 418 | ||
384 | /* adjust the flags */ | 419 | /* adjust the flags */ |
385 | afs_vnode_finalise_status_update(vnode, server, ret); | 420 | if (ret == 0) { |
421 | afs_vnode_finalise_status_update(vnode, server); | ||
422 | afs_put_server(server); | ||
423 | } else { | ||
424 | afs_vnode_status_update_failed(vnode, ret); | ||
425 | } | ||
386 | 426 | ||
387 | _leave(" = %d", ret); | 427 | _leave(" = %d", ret); |
388 | return ret; | 428 | return ret; |
429 | |||
430 | no_server: | ||
431 | spin_lock(&vnode->lock); | ||
432 | vnode->update_cnt--; | ||
433 | ASSERTCMP(vnode->update_cnt, >=, 0); | ||
434 | spin_unlock(&vnode->lock); | ||
435 | return PTR_ERR(server); | ||
436 | } | ||
437 | |||
438 | /* | ||
439 | * make a file or a directory | ||
440 | */ | ||
441 | int afs_vnode_create(struct afs_vnode *vnode, struct key *key, | ||
442 | const char *name, umode_t mode, struct afs_fid *newfid, | ||
443 | struct afs_file_status *newstatus, | ||
444 | struct afs_callback *newcb, struct afs_server **_server) | ||
445 | { | ||
446 | struct afs_server *server; | ||
447 | int ret; | ||
448 | |||
449 | _enter("%s{%u,%u,%u},%x,%s,,", | ||
450 | vnode->volume->vlocation->vldb.name, | ||
451 | vnode->fid.vid, | ||
452 | vnode->fid.vnode, | ||
453 | vnode->fid.unique, | ||
454 | key_serial(key), | ||
455 | name); | ||
456 | |||
457 | /* this op will fetch the status on the directory we're creating in */ | ||
458 | spin_lock(&vnode->lock); | ||
459 | vnode->update_cnt++; | ||
460 | spin_unlock(&vnode->lock); | ||
461 | |||
462 | do { | ||
463 | /* pick a server to query */ | ||
464 | server = afs_volume_pick_fileserver(vnode); | ||
465 | if (IS_ERR(server)) | ||
466 | goto no_server; | ||
467 | |||
468 | _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); | ||
469 | |||
470 | ret = afs_fs_create(server, key, vnode, name, mode, newfid, | ||
471 | newstatus, newcb, &afs_sync_call); | ||
472 | |||
473 | } while (!afs_volume_release_fileserver(vnode, server, ret)); | ||
474 | |||
475 | /* adjust the flags */ | ||
476 | if (ret == 0) { | ||
477 | afs_vnode_finalise_status_update(vnode, server); | ||
478 | *_server = server; | ||
479 | } else { | ||
480 | afs_vnode_status_update_failed(vnode, ret); | ||
481 | *_server = NULL; | ||
482 | } | ||
483 | |||
484 | _leave(" = %d [cnt %d]", ret, vnode->update_cnt); | ||
485 | return ret; | ||
486 | |||
487 | no_server: | ||
488 | spin_lock(&vnode->lock); | ||
489 | vnode->update_cnt--; | ||
490 | ASSERTCMP(vnode->update_cnt, >=, 0); | ||
491 | spin_unlock(&vnode->lock); | ||
492 | _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt); | ||
493 | return PTR_ERR(server); | ||
494 | } | ||
495 | |||
496 | /* | ||
497 | * remove a file or directory | ||
498 | */ | ||
499 | int afs_vnode_remove(struct afs_vnode *vnode, struct key *key, const char *name, | ||
500 | bool isdir) | ||
501 | { | ||
502 | struct afs_server *server; | ||
503 | int ret; | ||
504 | |||
505 | _enter("%s{%u,%u,%u},%x,%s", | ||
506 | vnode->volume->vlocation->vldb.name, | ||
507 | vnode->fid.vid, | ||
508 | vnode->fid.vnode, | ||
509 | vnode->fid.unique, | ||
510 | key_serial(key), | ||
511 | name); | ||
512 | |||
513 | /* this op will fetch the status on the directory we're removing from */ | ||
514 | spin_lock(&vnode->lock); | ||
515 | vnode->update_cnt++; | ||
516 | spin_unlock(&vnode->lock); | ||
517 | |||
518 | do { | ||
519 | /* pick a server to query */ | ||
520 | server = afs_volume_pick_fileserver(vnode); | ||
521 | if (IS_ERR(server)) | ||
522 | goto no_server; | ||
523 | |||
524 | _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); | ||
525 | |||
526 | ret = afs_fs_remove(server, key, vnode, name, isdir, | ||
527 | &afs_sync_call); | ||
528 | |||
529 | } while (!afs_volume_release_fileserver(vnode, server, ret)); | ||
530 | |||
531 | /* adjust the flags */ | ||
532 | if (ret == 0) { | ||
533 | afs_vnode_finalise_status_update(vnode, server); | ||
534 | afs_put_server(server); | ||
535 | } else { | ||
536 | afs_vnode_status_update_failed(vnode, ret); | ||
537 | } | ||
538 | |||
539 | _leave(" = %d [cnt %d]", ret, vnode->update_cnt); | ||
540 | return ret; | ||
541 | |||
542 | no_server: | ||
543 | spin_lock(&vnode->lock); | ||
544 | vnode->update_cnt--; | ||
545 | ASSERTCMP(vnode->update_cnt, >=, 0); | ||
546 | spin_unlock(&vnode->lock); | ||
547 | _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt); | ||
548 | return PTR_ERR(server); | ||
549 | } | ||
550 | |||
551 | /* | ||
552 | * create a hard link | ||
553 | */ | ||
554 | extern int afs_vnode_link(struct afs_vnode *dvnode, struct afs_vnode *vnode, | ||
555 | struct key *key, const char *name) | ||
556 | { | ||
557 | struct afs_server *server; | ||
558 | int ret; | ||
559 | |||
560 | _enter("%s{%u,%u,%u},%s{%u,%u,%u},%x,%s", | ||
561 | dvnode->volume->vlocation->vldb.name, | ||
562 | dvnode->fid.vid, | ||
563 | dvnode->fid.vnode, | ||
564 | dvnode->fid.unique, | ||
565 | vnode->volume->vlocation->vldb.name, | ||
566 | vnode->fid.vid, | ||
567 | vnode->fid.vnode, | ||
568 | vnode->fid.unique, | ||
569 | key_serial(key), | ||
570 | name); | ||
571 | |||
572 | /* this op will fetch the status on the directory we're removing from */ | ||
573 | spin_lock(&vnode->lock); | ||
574 | vnode->update_cnt++; | ||
575 | spin_unlock(&vnode->lock); | ||
576 | spin_lock(&dvnode->lock); | ||
577 | dvnode->update_cnt++; | ||
578 | spin_unlock(&dvnode->lock); | ||
579 | |||
580 | do { | ||
581 | /* pick a server to query */ | ||
582 | server = afs_volume_pick_fileserver(dvnode); | ||
583 | if (IS_ERR(server)) | ||
584 | goto no_server; | ||
585 | |||
586 | _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); | ||
587 | |||
588 | ret = afs_fs_link(server, key, dvnode, vnode, name, | ||
589 | &afs_sync_call); | ||
590 | |||
591 | } while (!afs_volume_release_fileserver(dvnode, server, ret)); | ||
592 | |||
593 | /* adjust the flags */ | ||
594 | if (ret == 0) { | ||
595 | afs_vnode_finalise_status_update(vnode, server); | ||
596 | afs_vnode_finalise_status_update(dvnode, server); | ||
597 | afs_put_server(server); | ||
598 | } else { | ||
599 | afs_vnode_status_update_failed(vnode, ret); | ||
600 | afs_vnode_status_update_failed(dvnode, ret); | ||
601 | } | ||
602 | |||
603 | _leave(" = %d [cnt %d]", ret, vnode->update_cnt); | ||
604 | return ret; | ||
605 | |||
606 | no_server: | ||
607 | spin_lock(&vnode->lock); | ||
608 | vnode->update_cnt--; | ||
609 | ASSERTCMP(vnode->update_cnt, >=, 0); | ||
610 | spin_unlock(&vnode->lock); | ||
611 | spin_lock(&dvnode->lock); | ||
612 | dvnode->update_cnt--; | ||
613 | ASSERTCMP(dvnode->update_cnt, >=, 0); | ||
614 | spin_unlock(&dvnode->lock); | ||
615 | _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt); | ||
616 | return PTR_ERR(server); | ||
617 | } | ||
618 | |||
619 | /* | ||
620 | * create a symbolic link | ||
621 | */ | ||
622 | int afs_vnode_symlink(struct afs_vnode *vnode, struct key *key, | ||
623 | const char *name, const char *content, | ||
624 | struct afs_fid *newfid, | ||
625 | struct afs_file_status *newstatus, | ||
626 | struct afs_server **_server) | ||
627 | { | ||
628 | struct afs_server *server; | ||
629 | int ret; | ||
630 | |||
631 | _enter("%s{%u,%u,%u},%x,%s,%s,,,", | ||
632 | vnode->volume->vlocation->vldb.name, | ||
633 | vnode->fid.vid, | ||
634 | vnode->fid.vnode, | ||
635 | vnode->fid.unique, | ||
636 | key_serial(key), | ||
637 | name, content); | ||
638 | |||
639 | /* this op will fetch the status on the directory we're creating in */ | ||
640 | spin_lock(&vnode->lock); | ||
641 | vnode->update_cnt++; | ||
642 | spin_unlock(&vnode->lock); | ||
643 | |||
644 | do { | ||
645 | /* pick a server to query */ | ||
646 | server = afs_volume_pick_fileserver(vnode); | ||
647 | if (IS_ERR(server)) | ||
648 | goto no_server; | ||
649 | |||
650 | _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); | ||
651 | |||
652 | ret = afs_fs_symlink(server, key, vnode, name, content, | ||
653 | newfid, newstatus, &afs_sync_call); | ||
654 | |||
655 | } while (!afs_volume_release_fileserver(vnode, server, ret)); | ||
656 | |||
657 | /* adjust the flags */ | ||
658 | if (ret == 0) { | ||
659 | afs_vnode_finalise_status_update(vnode, server); | ||
660 | *_server = server; | ||
661 | } else { | ||
662 | afs_vnode_status_update_failed(vnode, ret); | ||
663 | *_server = NULL; | ||
664 | } | ||
665 | |||
666 | _leave(" = %d [cnt %d]", ret, vnode->update_cnt); | ||
667 | return ret; | ||
668 | |||
669 | no_server: | ||
670 | spin_lock(&vnode->lock); | ||
671 | vnode->update_cnt--; | ||
672 | ASSERTCMP(vnode->update_cnt, >=, 0); | ||
673 | spin_unlock(&vnode->lock); | ||
674 | _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt); | ||
675 | return PTR_ERR(server); | ||
676 | } | ||
677 | |||
678 | /* | ||
679 | * rename a file | ||
680 | */ | ||
681 | int afs_vnode_rename(struct afs_vnode *orig_dvnode, | ||
682 | struct afs_vnode *new_dvnode, | ||
683 | struct key *key, | ||
684 | const char *orig_name, | ||
685 | const char *new_name) | ||
686 | { | ||
687 | struct afs_server *server; | ||
688 | int ret; | ||
689 | |||
690 | _enter("%s{%u,%u,%u},%s{%u,%u,%u},%x,%s,%s", | ||
691 | orig_dvnode->volume->vlocation->vldb.name, | ||
692 | orig_dvnode->fid.vid, | ||
693 | orig_dvnode->fid.vnode, | ||
694 | orig_dvnode->fid.unique, | ||
695 | new_dvnode->volume->vlocation->vldb.name, | ||
696 | new_dvnode->fid.vid, | ||
697 | new_dvnode->fid.vnode, | ||
698 | new_dvnode->fid.unique, | ||
699 | key_serial(key), | ||
700 | orig_name, | ||
701 | new_name); | ||
702 | |||
703 | /* this op will fetch the status on both the directories we're dealing | ||
704 | * with */ | ||
705 | spin_lock(&orig_dvnode->lock); | ||
706 | orig_dvnode->update_cnt++; | ||
707 | spin_unlock(&orig_dvnode->lock); | ||
708 | if (new_dvnode != orig_dvnode) { | ||
709 | spin_lock(&new_dvnode->lock); | ||
710 | new_dvnode->update_cnt++; | ||
711 | spin_unlock(&new_dvnode->lock); | ||
712 | } | ||
713 | |||
714 | do { | ||
715 | /* pick a server to query */ | ||
716 | server = afs_volume_pick_fileserver(orig_dvnode); | ||
717 | if (IS_ERR(server)) | ||
718 | goto no_server; | ||
719 | |||
720 | _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); | ||
721 | |||
722 | ret = afs_fs_rename(server, key, orig_dvnode, orig_name, | ||
723 | new_dvnode, new_name, &afs_sync_call); | ||
724 | |||
725 | } while (!afs_volume_release_fileserver(orig_dvnode, server, ret)); | ||
726 | |||
727 | /* adjust the flags */ | ||
728 | if (ret == 0) { | ||
729 | afs_vnode_finalise_status_update(orig_dvnode, server); | ||
730 | if (new_dvnode != orig_dvnode) | ||
731 | afs_vnode_finalise_status_update(new_dvnode, server); | ||
732 | afs_put_server(server); | ||
733 | } else { | ||
734 | afs_vnode_status_update_failed(orig_dvnode, ret); | ||
735 | if (new_dvnode != orig_dvnode) | ||
736 | afs_vnode_status_update_failed(new_dvnode, ret); | ||
737 | } | ||
738 | |||
739 | _leave(" = %d [cnt %d]", ret, orig_dvnode->update_cnt); | ||
740 | return ret; | ||
741 | |||
742 | no_server: | ||
743 | spin_lock(&orig_dvnode->lock); | ||
744 | orig_dvnode->update_cnt--; | ||
745 | ASSERTCMP(orig_dvnode->update_cnt, >=, 0); | ||
746 | spin_unlock(&orig_dvnode->lock); | ||
747 | if (new_dvnode != orig_dvnode) { | ||
748 | spin_lock(&new_dvnode->lock); | ||
749 | new_dvnode->update_cnt--; | ||
750 | ASSERTCMP(new_dvnode->update_cnt, >=, 0); | ||
751 | spin_unlock(&new_dvnode->lock); | ||
752 | } | ||
753 | _leave(" = %ld [cnt %d]", PTR_ERR(server), orig_dvnode->update_cnt); | ||
754 | return PTR_ERR(server); | ||
389 | } | 755 | } |
diff --git a/fs/afs/volume.c b/fs/afs/volume.c index 15e13678c216..dd160cada45d 100644 --- a/fs/afs/volume.c +++ b/fs/afs/volume.c | |||
@@ -295,6 +295,7 @@ struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *vnode) | |||
295 | * - releases the ref on the server struct that was acquired by picking | 295 | * - releases the ref on the server struct that was acquired by picking |
296 | * - records result of using a particular server to access a volume | 296 | * - records result of using a particular server to access a volume |
297 | * - return 0 to try again, 1 if okay or to issue error | 297 | * - return 0 to try again, 1 if okay or to issue error |
298 | * - the caller must release the server struct if result was 0 | ||
298 | */ | 299 | */ |
299 | int afs_volume_release_fileserver(struct afs_vnode *vnode, | 300 | int afs_volume_release_fileserver(struct afs_vnode *vnode, |
300 | struct afs_server *server, | 301 | struct afs_server *server, |
@@ -312,7 +313,8 @@ int afs_volume_release_fileserver(struct afs_vnode *vnode, | |||
312 | case 0: | 313 | case 0: |
313 | server->fs_act_jif = jiffies; | 314 | server->fs_act_jif = jiffies; |
314 | server->fs_state = 0; | 315 | server->fs_state = 0; |
315 | break; | 316 | _leave(""); |
317 | return 1; | ||
316 | 318 | ||
317 | /* the fileserver denied all knowledge of the volume */ | 319 | /* the fileserver denied all knowledge of the volume */ |
318 | case -ENOMEDIUM: | 320 | case -ENOMEDIUM: |
@@ -377,14 +379,12 @@ int afs_volume_release_fileserver(struct afs_vnode *vnode, | |||
377 | server->fs_act_jif = jiffies; | 379 | server->fs_act_jif = jiffies; |
378 | case -ENOMEM: | 380 | case -ENOMEM: |
379 | case -ENONET: | 381 | case -ENONET: |
380 | break; | 382 | /* tell the caller to accept the result */ |
383 | afs_put_server(server); | ||
384 | _leave(" [local failure]"); | ||
385 | return 1; | ||
381 | } | 386 | } |
382 | 387 | ||
383 | /* tell the caller to accept the result */ | ||
384 | afs_put_server(server); | ||
385 | _leave(""); | ||
386 | return 1; | ||
387 | |||
388 | /* tell the caller to loop around and try the next server */ | 388 | /* tell the caller to loop around and try the next server */ |
389 | try_next_server_upw: | 389 | try_next_server_upw: |
390 | up_write(&volume->server_sem); | 390 | up_write(&volume->server_sem); |