diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2007-07-14 15:39:58 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2007-07-19 15:21:39 -0400 |
commit | e4eff1a622edd6ab7b73acd5d8763aa2fa3fee49 (patch) | |
tree | 257d6675733d4af122a77054281e1d7d5062d904 | |
parent | 4fdc17b2a7f4d9db5b08e0f963d0027f714e4104 (diff) |
SUNRPC: Clean up the sillyrename code
Fix a couple of bugs:
- Don't rely on the parent dentry still being valid when the call completes.
Fixes a race with shrink_dcache_for_umount_subtree()
- Don't remove the file if the filehandle has been labelled as stale.
Fix a couple of inefficiencies
- Remove the global list of sillyrenamed files. Instead we can cache the
sillyrename information in the dentry->d_fsdata
- Move common code from unlink_setup/unlink_done into fs/nfs/unlink.c
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r-- | fs/nfs/dir.c | 4 | ||||
-rw-r--r-- | fs/nfs/nfs3proc.c | 38 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 50 | ||||
-rw-r--r-- | fs/nfs/proc.c | 26 | ||||
-rw-r--r-- | fs/nfs/unlink.c | 195 | ||||
-rw-r--r-- | include/linux/nfs_fs.h | 4 | ||||
-rw-r--r-- | include/linux/nfs_xdr.h | 5 |
7 files changed, 120 insertions, 202 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 0fa1dbcdadb9..ea97408e423e 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -869,7 +869,7 @@ static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode) | |||
869 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { | 869 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { |
870 | lock_kernel(); | 870 | lock_kernel(); |
871 | drop_nlink(inode); | 871 | drop_nlink(inode); |
872 | nfs_complete_unlink(dentry); | 872 | nfs_complete_unlink(dentry, inode); |
873 | unlock_kernel(); | 873 | unlock_kernel(); |
874 | } | 874 | } |
875 | /* When creating a negative dentry, we want to renew d_time */ | 875 | /* When creating a negative dentry, we want to renew d_time */ |
@@ -1411,7 +1411,7 @@ static int nfs_sillyrename(struct inode *dir, struct dentry *dentry) | |||
1411 | nfs_renew_times(dentry); | 1411 | nfs_renew_times(dentry); |
1412 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); | 1412 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); |
1413 | d_move(dentry, sdentry); | 1413 | d_move(dentry, sdentry); |
1414 | error = nfs_async_unlink(dentry); | 1414 | error = nfs_async_unlink(dir, dentry); |
1415 | /* If we return 0 we don't unlink */ | 1415 | /* If we return 0 we don't unlink */ |
1416 | } | 1416 | } |
1417 | dput(sdentry); | 1417 | dput(sdentry); |
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index eac07f2c99dd..c7ca5d70870b 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c | |||
@@ -370,41 +370,21 @@ nfs3_proc_remove(struct inode *dir, struct qstr *name) | |||
370 | return status; | 370 | return status; |
371 | } | 371 | } |
372 | 372 | ||
373 | static int | 373 | static void |
374 | nfs3_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, struct qstr *name) | 374 | nfs3_proc_unlink_setup(struct rpc_message *msg, struct inode *dir) |
375 | { | 375 | { |
376 | struct unlinkxdr { | ||
377 | struct nfs_removeargs arg; | ||
378 | struct nfs_removeres res; | ||
379 | } *ptr; | ||
380 | |||
381 | ptr = kmalloc(sizeof(*ptr), GFP_KERNEL); | ||
382 | if (!ptr) | ||
383 | return -ENOMEM; | ||
384 | ptr->arg.fh = NFS_FH(dir->d_inode); | ||
385 | ptr->arg.name.name = name->name; | ||
386 | ptr->arg.name.len = name->len; | ||
387 | nfs_fattr_init(&ptr->res.dir_attr); | ||
388 | msg->rpc_proc = &nfs3_procedures[NFS3PROC_REMOVE]; | 376 | msg->rpc_proc = &nfs3_procedures[NFS3PROC_REMOVE]; |
389 | msg->rpc_argp = &ptr->arg; | ||
390 | msg->rpc_resp = &ptr->res; | ||
391 | return 0; | ||
392 | } | 377 | } |
393 | 378 | ||
394 | static int | 379 | static int |
395 | nfs3_proc_unlink_done(struct dentry *dir, struct rpc_task *task) | 380 | nfs3_proc_unlink_done(struct rpc_task *task, struct inode *dir) |
396 | { | 381 | { |
397 | struct rpc_message *msg = &task->tk_msg; | 382 | struct nfs_removeres *res; |
398 | struct nfs_fattr *dir_attr; | 383 | if (nfs3_async_handle_jukebox(task, dir)) |
399 | 384 | return 0; | |
400 | if (nfs3_async_handle_jukebox(task, dir->d_inode)) | 385 | res = task->tk_msg.rpc_resp; |
401 | return 1; | 386 | nfs_post_op_update_inode(dir, &res->dir_attr); |
402 | if (msg->rpc_argp) { | 387 | return 1; |
403 | dir_attr = &((struct nfs_removeres*)msg->rpc_resp)->dir_attr; | ||
404 | nfs_post_op_update_inode(dir->d_inode, dir_attr); | ||
405 | kfree(msg->rpc_argp); | ||
406 | } | ||
407 | return 0; | ||
408 | } | 388 | } |
409 | 389 | ||
410 | static int | 390 | static int |
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 23dc25dbc6fa..6ca2795ccd9c 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -1962,48 +1962,26 @@ static int nfs4_proc_remove(struct inode *dir, struct qstr *name) | |||
1962 | return err; | 1962 | return err; |
1963 | } | 1963 | } |
1964 | 1964 | ||
1965 | struct unlink_desc { | 1965 | static void nfs4_proc_unlink_setup(struct rpc_message *msg, struct inode *dir) |
1966 | struct nfs_removeargs args; | ||
1967 | struct nfs_removeres res; | ||
1968 | }; | ||
1969 | |||
1970 | static int nfs4_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, | ||
1971 | struct qstr *name) | ||
1972 | { | 1966 | { |
1973 | struct nfs_server *server = NFS_SERVER(dir->d_inode); | 1967 | struct nfs_server *server = NFS_SERVER(dir); |
1974 | struct unlink_desc *up; | 1968 | struct nfs_removeargs *args = msg->rpc_argp; |
1969 | struct nfs_removeres *res = msg->rpc_resp; | ||
1975 | 1970 | ||
1976 | up = kmalloc(sizeof(*up), GFP_KERNEL); | 1971 | args->bitmask = server->attr_bitmask; |
1977 | if (!up) | 1972 | res->server = server; |
1978 | return -ENOMEM; | ||
1979 | |||
1980 | up->args.fh = NFS_FH(dir->d_inode); | ||
1981 | up->args.name.len = name->len; | ||
1982 | up->args.name.name = name->name; | ||
1983 | up->args.bitmask = server->attr_bitmask; | ||
1984 | up->res.server = server; | ||
1985 | nfs_fattr_init(&up->res.dir_attr); | ||
1986 | |||
1987 | msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE]; | 1973 | msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE]; |
1988 | msg->rpc_argp = &up->args; | ||
1989 | msg->rpc_resp = &up->res; | ||
1990 | return 0; | ||
1991 | } | 1974 | } |
1992 | 1975 | ||
1993 | static int nfs4_proc_unlink_done(struct dentry *dir, struct rpc_task *task) | 1976 | static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir) |
1994 | { | 1977 | { |
1995 | struct rpc_message *msg = &task->tk_msg; | 1978 | struct nfs_removeres *res = task->tk_msg.rpc_resp; |
1996 | struct unlink_desc *up; | 1979 | |
1997 | 1980 | if (nfs4_async_handle_error(task, res->server) == -EAGAIN) | |
1998 | if (msg->rpc_resp != NULL) { | 1981 | return 0; |
1999 | up = container_of(msg->rpc_resp, struct unlink_desc, res); | 1982 | update_changeattr(dir, &res->cinfo); |
2000 | update_changeattr(dir->d_inode, &up->res.cinfo); | 1983 | nfs_post_op_update_inode(dir, &res->dir_attr); |
2001 | nfs_post_op_update_inode(dir->d_inode, &up->res.dir_attr); | 1984 | return 1; |
2002 | kfree(up); | ||
2003 | msg->rpc_resp = NULL; | ||
2004 | msg->rpc_argp = NULL; | ||
2005 | } | ||
2006 | return 0; | ||
2007 | } | 1985 | } |
2008 | 1986 | ||
2009 | static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name, | 1987 | static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name, |
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 3b3eb692e0f4..845cdde1d8b7 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c | |||
@@ -291,32 +291,16 @@ nfs_proc_remove(struct inode *dir, struct qstr *name) | |||
291 | return status; | 291 | return status; |
292 | } | 292 | } |
293 | 293 | ||
294 | static int | 294 | static void |
295 | nfs_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, struct qstr *name) | 295 | nfs_proc_unlink_setup(struct rpc_message *msg, struct inode *dir) |
296 | { | 296 | { |
297 | struct nfs_removeargs *arg; | ||
298 | |||
299 | arg = kmalloc(sizeof(*arg), GFP_KERNEL); | ||
300 | if (!arg) | ||
301 | return -ENOMEM; | ||
302 | arg->fh = NFS_FH(dir->d_inode); | ||
303 | arg->name.name = name->name; | ||
304 | arg->name.len = name->len; | ||
305 | msg->rpc_proc = &nfs_procedures[NFSPROC_REMOVE]; | 297 | msg->rpc_proc = &nfs_procedures[NFSPROC_REMOVE]; |
306 | msg->rpc_argp = arg; | ||
307 | return 0; | ||
308 | } | 298 | } |
309 | 299 | ||
310 | static int | 300 | static int nfs_proc_unlink_done(struct rpc_task *task, struct inode *dir) |
311 | nfs_proc_unlink_done(struct dentry *dir, struct rpc_task *task) | ||
312 | { | 301 | { |
313 | struct rpc_message *msg = &task->tk_msg; | 302 | nfs_mark_for_revalidate(dir); |
314 | 303 | return 1; | |
315 | if (msg->rpc_argp) { | ||
316 | nfs_mark_for_revalidate(dir->d_inode); | ||
317 | kfree(msg->rpc_argp); | ||
318 | } | ||
319 | return 0; | ||
320 | } | 304 | } |
321 | 305 | ||
322 | static int | 306 | static int |
diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index 0e28189c2151..045ab805c17f 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c | |||
@@ -3,7 +3,6 @@ | |||
3 | * | 3 | * |
4 | * nfs sillydelete handling | 4 | * nfs sillydelete handling |
5 | * | 5 | * |
6 | * NOTE: we rely on holding the BKL for list manipulation protection. | ||
7 | */ | 6 | */ |
8 | 7 | ||
9 | #include <linux/slab.h> | 8 | #include <linux/slab.h> |
@@ -15,46 +14,23 @@ | |||
15 | 14 | ||
16 | 15 | ||
17 | struct nfs_unlinkdata { | 16 | struct nfs_unlinkdata { |
18 | struct nfs_unlinkdata *next; | 17 | struct nfs_removeargs args; |
19 | struct dentry *dir, *dentry; | 18 | struct nfs_removeres res; |
20 | struct qstr name; | 19 | struct inode *dir; |
21 | struct rpc_task task; | ||
22 | struct rpc_cred *cred; | 20 | struct rpc_cred *cred; |
23 | unsigned int count; | ||
24 | }; | 21 | }; |
25 | 22 | ||
26 | static struct nfs_unlinkdata *nfs_deletes; | ||
27 | static RPC_WAITQ(nfs_delete_queue, "nfs_delete_queue"); | ||
28 | |||
29 | /** | ||
30 | * nfs_detach_unlinkdata - Remove asynchronous unlink from global list | ||
31 | * @data: pointer to descriptor | ||
32 | */ | ||
33 | static inline void | ||
34 | nfs_detach_unlinkdata(struct nfs_unlinkdata *data) | ||
35 | { | ||
36 | struct nfs_unlinkdata **q; | ||
37 | |||
38 | for (q = &nfs_deletes; *q != NULL; q = &((*q)->next)) { | ||
39 | if (*q == data) { | ||
40 | *q = data->next; | ||
41 | break; | ||
42 | } | ||
43 | } | ||
44 | } | ||
45 | |||
46 | /** | 23 | /** |
47 | * nfs_put_unlinkdata - release data from a sillydelete operation. | 24 | * nfs_free_unlinkdata - release data from a sillydelete operation. |
48 | * @data: pointer to unlink structure. | 25 | * @data: pointer to unlink structure. |
49 | */ | 26 | */ |
50 | static void | 27 | static void |
51 | nfs_put_unlinkdata(struct nfs_unlinkdata *data) | 28 | nfs_free_unlinkdata(struct nfs_unlinkdata *data) |
52 | { | 29 | { |
53 | if (--data->count == 0) { | 30 | iput(data->dir); |
54 | nfs_detach_unlinkdata(data); | 31 | put_rpccred(data->cred); |
55 | kfree(data->name.name); | 32 | kfree(data->args.name.name); |
56 | kfree(data); | 33 | kfree(data); |
57 | } | ||
58 | } | 34 | } |
59 | 35 | ||
60 | #define NAME_ALLOC_LEN(len) ((len+16) & ~15) | 36 | #define NAME_ALLOC_LEN(len) ((len+16) & ~15) |
@@ -63,50 +39,36 @@ nfs_put_unlinkdata(struct nfs_unlinkdata *data) | |||
63 | * @dentry: pointer to dentry | 39 | * @dentry: pointer to dentry |
64 | * @data: nfs_unlinkdata | 40 | * @data: nfs_unlinkdata |
65 | */ | 41 | */ |
66 | static inline void | 42 | static int nfs_copy_dname(struct dentry *dentry, struct nfs_unlinkdata *data) |
67 | nfs_copy_dname(struct dentry *dentry, struct nfs_unlinkdata *data) | ||
68 | { | 43 | { |
69 | char *str; | 44 | char *str; |
70 | int len = dentry->d_name.len; | 45 | int len = dentry->d_name.len; |
71 | 46 | ||
72 | str = kmalloc(NAME_ALLOC_LEN(len), GFP_KERNEL); | 47 | str = kmemdup(dentry->d_name.name, NAME_ALLOC_LEN(len), GFP_KERNEL); |
73 | if (!str) | 48 | if (!str) |
74 | return; | 49 | return -ENOMEM; |
75 | memcpy(str, dentry->d_name.name, len); | 50 | data->args.name.len = len; |
76 | if (!data->name.len) { | 51 | data->args.name.name = str; |
77 | data->name.len = len; | 52 | return 0; |
78 | data->name.name = str; | ||
79 | } else | ||
80 | kfree(str); | ||
81 | } | 53 | } |
82 | 54 | ||
83 | /** | 55 | /** |
84 | * nfs_async_unlink_init - Initialize the RPC info | 56 | * nfs_async_unlink_init - Initialize the RPC info |
85 | * @task: rpc_task of the sillydelete | 57 | * task: rpc_task of the sillydelete |
86 | * | ||
87 | * We delay initializing RPC info until after the call to dentry_iput() | ||
88 | * in order to minimize races against rename(). | ||
89 | */ | 58 | */ |
90 | static void nfs_async_unlink_init(struct rpc_task *task, void *calldata) | 59 | static void nfs_async_unlink_init(struct rpc_task *task, void *calldata) |
91 | { | 60 | { |
92 | struct nfs_unlinkdata *data = calldata; | 61 | struct nfs_unlinkdata *data = calldata; |
93 | struct dentry *dir = data->dir; | 62 | struct inode *dir = data->dir; |
94 | struct rpc_message msg = { | 63 | struct rpc_message msg = { |
95 | .rpc_cred = data->cred, | 64 | .rpc_argp = &data->args, |
65 | .rpc_resp = &data->res, | ||
66 | .rpc_cred = data->cred, | ||
96 | }; | 67 | }; |
97 | int status = -ENOENT; | ||
98 | |||
99 | if (!data->name.len) | ||
100 | goto out_err; | ||
101 | 68 | ||
102 | status = NFS_PROTO(dir->d_inode)->unlink_setup(&msg, dir, &data->name); | 69 | nfs_begin_data_update(dir); |
103 | if (status < 0) | 70 | NFS_PROTO(dir)->unlink_setup(&msg, dir); |
104 | goto out_err; | ||
105 | nfs_begin_data_update(dir->d_inode); | ||
106 | rpc_call_setup(task, &msg, 0); | 71 | rpc_call_setup(task, &msg, 0); |
107 | return; | ||
108 | out_err: | ||
109 | rpc_exit(task, status); | ||
110 | } | 72 | } |
111 | 73 | ||
112 | /** | 74 | /** |
@@ -117,19 +79,13 @@ static void nfs_async_unlink_init(struct rpc_task *task, void *calldata) | |||
117 | */ | 79 | */ |
118 | static void nfs_async_unlink_done(struct rpc_task *task, void *calldata) | 80 | static void nfs_async_unlink_done(struct rpc_task *task, void *calldata) |
119 | { | 81 | { |
120 | struct nfs_unlinkdata *data = calldata; | 82 | struct nfs_unlinkdata *data = calldata; |
121 | struct dentry *dir = data->dir; | 83 | struct inode *dir = data->dir; |
122 | struct inode *dir_i; | 84 | |
123 | 85 | if (!NFS_PROTO(dir)->unlink_done(task, dir)) | |
124 | if (!dir) | 86 | rpc_restart_call(task); |
125 | return; | 87 | else |
126 | dir_i = dir->d_inode; | 88 | nfs_end_data_update(dir); |
127 | nfs_end_data_update(dir_i); | ||
128 | if (NFS_PROTO(dir_i)->unlink_done(dir, task)) | ||
129 | return; | ||
130 | put_rpccred(data->cred); | ||
131 | data->cred = NULL; | ||
132 | dput(dir); | ||
133 | } | 89 | } |
134 | 90 | ||
135 | /** | 91 | /** |
@@ -142,7 +98,7 @@ static void nfs_async_unlink_done(struct rpc_task *task, void *calldata) | |||
142 | static void nfs_async_unlink_release(void *calldata) | 98 | static void nfs_async_unlink_release(void *calldata) |
143 | { | 99 | { |
144 | struct nfs_unlinkdata *data = calldata; | 100 | struct nfs_unlinkdata *data = calldata; |
145 | nfs_put_unlinkdata(data); | 101 | nfs_free_unlinkdata(data); |
146 | } | 102 | } |
147 | 103 | ||
148 | static const struct rpc_call_ops nfs_unlink_ops = { | 104 | static const struct rpc_call_ops nfs_unlink_ops = { |
@@ -151,73 +107,94 @@ static const struct rpc_call_ops nfs_unlink_ops = { | |||
151 | .rpc_release = nfs_async_unlink_release, | 107 | .rpc_release = nfs_async_unlink_release, |
152 | }; | 108 | }; |
153 | 109 | ||
110 | static int nfs_call_unlink(struct dentry *dentry, struct nfs_unlinkdata *data) | ||
111 | { | ||
112 | struct rpc_task *task; | ||
113 | struct dentry *parent; | ||
114 | struct inode *dir; | ||
115 | |||
116 | if (nfs_copy_dname(dentry, data) < 0) | ||
117 | goto out_free; | ||
118 | |||
119 | parent = dget_parent(dentry); | ||
120 | if (parent == NULL) | ||
121 | goto out_free; | ||
122 | dir = igrab(parent->d_inode); | ||
123 | dput(parent); | ||
124 | if (dir == NULL) | ||
125 | goto out_free; | ||
126 | |||
127 | data->dir = dir; | ||
128 | data->args.fh = NFS_FH(dir); | ||
129 | nfs_fattr_init(&data->res.dir_attr); | ||
130 | |||
131 | task = rpc_run_task(NFS_CLIENT(dir), RPC_TASK_ASYNC, &nfs_unlink_ops, data); | ||
132 | if (!IS_ERR(task)) | ||
133 | rpc_put_task(task); | ||
134 | return 1; | ||
135 | out_free: | ||
136 | return 0; | ||
137 | } | ||
138 | |||
154 | /** | 139 | /** |
155 | * nfs_async_unlink - asynchronous unlinking of a file | 140 | * nfs_async_unlink - asynchronous unlinking of a file |
141 | * @dir: parent directory of dentry | ||
156 | * @dentry: dentry to unlink | 142 | * @dentry: dentry to unlink |
157 | */ | 143 | */ |
158 | int | 144 | int |
159 | nfs_async_unlink(struct dentry *dentry) | 145 | nfs_async_unlink(struct inode *dir, struct dentry *dentry) |
160 | { | 146 | { |
161 | struct dentry *dir = dentry->d_parent; | 147 | struct nfs_unlinkdata *data; |
162 | struct nfs_unlinkdata *data; | 148 | int status = -ENOMEM; |
163 | struct rpc_clnt *clnt = NFS_CLIENT(dir->d_inode); | ||
164 | int status = -ENOMEM; | ||
165 | 149 | ||
166 | data = kzalloc(sizeof(*data), GFP_KERNEL); | 150 | data = kzalloc(sizeof(*data), GFP_KERNEL); |
167 | if (!data) | 151 | if (data == NULL) |
168 | goto out; | 152 | goto out; |
169 | 153 | ||
170 | data->cred = rpcauth_lookupcred(clnt->cl_auth, 0); | 154 | data->cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0); |
171 | if (IS_ERR(data->cred)) { | 155 | if (IS_ERR(data->cred)) { |
172 | status = PTR_ERR(data->cred); | 156 | status = PTR_ERR(data->cred); |
173 | goto out_free; | 157 | goto out_free; |
174 | } | 158 | } |
175 | data->dir = dget(dir); | ||
176 | data->dentry = dentry; | ||
177 | |||
178 | data->next = nfs_deletes; | ||
179 | nfs_deletes = data; | ||
180 | data->count = 1; | ||
181 | |||
182 | rpc_init_task(&data->task, clnt, RPC_TASK_ASYNC, &nfs_unlink_ops, data); | ||
183 | 159 | ||
160 | status = -EBUSY; | ||
184 | spin_lock(&dentry->d_lock); | 161 | spin_lock(&dentry->d_lock); |
162 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) | ||
163 | goto out_unlock; | ||
185 | dentry->d_flags |= DCACHE_NFSFS_RENAMED; | 164 | dentry->d_flags |= DCACHE_NFSFS_RENAMED; |
165 | dentry->d_fsdata = data; | ||
186 | spin_unlock(&dentry->d_lock); | 166 | spin_unlock(&dentry->d_lock); |
187 | 167 | return 0; | |
188 | rpc_sleep_on(&nfs_delete_queue, &data->task, NULL, NULL); | 168 | out_unlock: |
189 | status = 0; | 169 | spin_unlock(&dentry->d_lock); |
190 | out: | 170 | put_rpccred(data->cred); |
191 | return status; | ||
192 | out_free: | 171 | out_free: |
193 | kfree(data); | 172 | kfree(data); |
173 | out: | ||
194 | return status; | 174 | return status; |
195 | } | 175 | } |
196 | 176 | ||
197 | /** | 177 | /** |
198 | * nfs_complete_unlink - Initialize completion of the sillydelete | 178 | * nfs_complete_unlink - Initialize completion of the sillydelete |
199 | * @dentry: dentry to delete | 179 | * @dentry: dentry to delete |
180 | * @inode: inode | ||
200 | * | 181 | * |
201 | * Since we're most likely to be called by dentry_iput(), we | 182 | * Since we're most likely to be called by dentry_iput(), we |
202 | * only use the dentry to find the sillydelete. We then copy the name | 183 | * only use the dentry to find the sillydelete. We then copy the name |
203 | * into the qstr. | 184 | * into the qstr. |
204 | */ | 185 | */ |
205 | void | 186 | void |
206 | nfs_complete_unlink(struct dentry *dentry) | 187 | nfs_complete_unlink(struct dentry *dentry, struct inode *inode) |
207 | { | 188 | { |
208 | struct nfs_unlinkdata *data; | 189 | struct nfs_unlinkdata *data = NULL; |
209 | 190 | ||
210 | for(data = nfs_deletes; data != NULL; data = data->next) { | ||
211 | if (dentry == data->dentry) | ||
212 | break; | ||
213 | } | ||
214 | if (!data) | ||
215 | return; | ||
216 | data->count++; | ||
217 | nfs_copy_dname(dentry, data); | ||
218 | spin_lock(&dentry->d_lock); | 191 | spin_lock(&dentry->d_lock); |
219 | dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; | 192 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { |
193 | dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; | ||
194 | data = dentry->d_fsdata; | ||
195 | } | ||
220 | spin_unlock(&dentry->d_lock); | 196 | spin_unlock(&dentry->d_lock); |
221 | rpc_wake_up_task(&data->task); | 197 | |
222 | nfs_put_unlinkdata(data); | 198 | if (data != NULL && (NFS_STALE(inode) || !nfs_call_unlink(dentry, data))) |
199 | nfs_free_unlinkdata(data); | ||
223 | } | 200 | } |
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index c098ae194f79..9ba4aec37c50 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h | |||
@@ -407,8 +407,8 @@ extern void nfs_release_automount_timer(void); | |||
407 | /* | 407 | /* |
408 | * linux/fs/nfs/unlink.c | 408 | * linux/fs/nfs/unlink.c |
409 | */ | 409 | */ |
410 | extern int nfs_async_unlink(struct dentry *); | 410 | extern int nfs_async_unlink(struct inode *dir, struct dentry *dentry); |
411 | extern void nfs_complete_unlink(struct dentry *); | 411 | extern void nfs_complete_unlink(struct dentry *dentry, struct inode *); |
412 | 412 | ||
413 | /* | 413 | /* |
414 | * linux/fs/nfs/write.c | 414 | * linux/fs/nfs/write.c |
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 7babcb16300b..cf74a4db84a5 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h | |||
@@ -791,9 +791,8 @@ struct nfs_rpc_ops { | |||
791 | int (*create) (struct inode *, struct dentry *, | 791 | int (*create) (struct inode *, struct dentry *, |
792 | struct iattr *, int, struct nameidata *); | 792 | struct iattr *, int, struct nameidata *); |
793 | int (*remove) (struct inode *, struct qstr *); | 793 | int (*remove) (struct inode *, struct qstr *); |
794 | int (*unlink_setup) (struct rpc_message *, | 794 | void (*unlink_setup) (struct rpc_message *, struct inode *dir); |
795 | struct dentry *, struct qstr *); | 795 | int (*unlink_done) (struct rpc_task *, struct inode *); |
796 | int (*unlink_done) (struct dentry *, struct rpc_task *); | ||
797 | int (*rename) (struct inode *, struct qstr *, | 796 | int (*rename) (struct inode *, struct qstr *, |
798 | struct inode *, struct qstr *); | 797 | struct inode *, struct qstr *); |
799 | int (*link) (struct inode *, struct inode *, struct qstr *); | 798 | int (*link) (struct inode *, struct inode *, struct qstr *); |