aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/unlink.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfs/unlink.c')
-rw-r--r--fs/nfs/unlink.c195
1 files changed, 86 insertions, 109 deletions
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
17struct nfs_unlinkdata { 16struct 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
26static struct nfs_unlinkdata *nfs_deletes;
27static 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 */
33static inline void
34nfs_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 */
50static void 27static void
51nfs_put_unlinkdata(struct nfs_unlinkdata *data) 28nfs_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 */
66static inline void 42static int nfs_copy_dname(struct dentry *dentry, struct nfs_unlinkdata *data)
67nfs_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 */
90static void nfs_async_unlink_init(struct rpc_task *task, void *calldata) 59static 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 */
118static void nfs_async_unlink_done(struct rpc_task *task, void *calldata) 80static 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)
142static void nfs_async_unlink_release(void *calldata) 98static 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
148static const struct rpc_call_ops nfs_unlink_ops = { 104static 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
110static 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;
135out_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 */
158int 144int
159nfs_async_unlink(struct dentry *dentry) 145nfs_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); 168out_unlock:
189 status = 0; 169 spin_unlock(&dentry->d_lock);
190 out: 170 put_rpccred(data->cred);
191 return status;
192out_free: 171out_free:
193 kfree(data); 172 kfree(data);
173out:
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 */
205void 186void
206nfs_complete_unlink(struct dentry *dentry) 187nfs_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}