diff options
Diffstat (limited to 'fs/nfs/unlink.c')
-rw-r--r-- | fs/nfs/unlink.c | 195 |
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 | ||
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 | } |