diff options
Diffstat (limited to 'fs/nfs/unlink.c')
-rw-r--r-- | fs/nfs/unlink.c | 85 |
1 files changed, 84 insertions, 1 deletions
diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index 2f84adaad427..c3ce865294f1 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c | |||
@@ -13,9 +13,12 @@ | |||
13 | #include <linux/nfs_fs.h> | 13 | #include <linux/nfs_fs.h> |
14 | #include <linux/sched.h> | 14 | #include <linux/sched.h> |
15 | #include <linux/wait.h> | 15 | #include <linux/wait.h> |
16 | #include <linux/namei.h> | ||
16 | 17 | ||
17 | #include "internal.h" | 18 | #include "internal.h" |
18 | #include "nfs4_fs.h" | 19 | #include "nfs4_fs.h" |
20 | #include "iostat.h" | ||
21 | #include "delegation.h" | ||
19 | 22 | ||
20 | struct nfs_unlinkdata { | 23 | struct nfs_unlinkdata { |
21 | struct hlist_node list; | 24 | struct hlist_node list; |
@@ -244,7 +247,7 @@ void nfs_unblock_sillyrename(struct dentry *dentry) | |||
244 | * @dir: parent directory of dentry | 247 | * @dir: parent directory of dentry |
245 | * @dentry: dentry to unlink | 248 | * @dentry: dentry to unlink |
246 | */ | 249 | */ |
247 | int | 250 | static int |
248 | nfs_async_unlink(struct inode *dir, struct dentry *dentry) | 251 | nfs_async_unlink(struct inode *dir, struct dentry *dentry) |
249 | { | 252 | { |
250 | struct nfs_unlinkdata *data; | 253 | struct nfs_unlinkdata *data; |
@@ -303,3 +306,83 @@ nfs_complete_unlink(struct dentry *dentry, struct inode *inode) | |||
303 | if (data != NULL && (NFS_STALE(inode) || !nfs_call_unlink(dentry, data))) | 306 | if (data != NULL && (NFS_STALE(inode) || !nfs_call_unlink(dentry, data))) |
304 | nfs_free_unlinkdata(data); | 307 | nfs_free_unlinkdata(data); |
305 | } | 308 | } |
309 | |||
310 | /** | ||
311 | * nfs_sillyrename - Perform a silly-rename of a dentry | ||
312 | * @dir: inode of directory that contains dentry | ||
313 | * @dentry: dentry to be sillyrenamed | ||
314 | * | ||
315 | * NFSv2/3 is stateless and the server doesn't know when the client is | ||
316 | * holding a file open. To prevent application problems when a file is | ||
317 | * unlinked while it's still open, the client performs a "silly-rename". | ||
318 | * That is, it renames the file to a hidden file in the same directory, | ||
319 | * and only performs the unlink once the last reference to it is put. | ||
320 | * | ||
321 | * The final cleanup is done during dentry_iput. | ||
322 | */ | ||
323 | int | ||
324 | nfs_sillyrename(struct inode *dir, struct dentry *dentry) | ||
325 | { | ||
326 | static unsigned int sillycounter; | ||
327 | const int fileidsize = sizeof(NFS_FILEID(dentry->d_inode))*2; | ||
328 | const int countersize = sizeof(sillycounter)*2; | ||
329 | const int slen = sizeof(".nfs")+fileidsize+countersize-1; | ||
330 | char silly[slen+1]; | ||
331 | struct qstr qsilly; | ||
332 | struct dentry *sdentry; | ||
333 | int error = -EIO; | ||
334 | |||
335 | dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n", | ||
336 | dentry->d_parent->d_name.name, dentry->d_name.name, | ||
337 | atomic_read(&dentry->d_count)); | ||
338 | nfs_inc_stats(dir, NFSIOS_SILLYRENAME); | ||
339 | |||
340 | /* | ||
341 | * We don't allow a dentry to be silly-renamed twice. | ||
342 | */ | ||
343 | error = -EBUSY; | ||
344 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) | ||
345 | goto out; | ||
346 | |||
347 | sprintf(silly, ".nfs%*.*Lx", | ||
348 | fileidsize, fileidsize, | ||
349 | (unsigned long long)NFS_FILEID(dentry->d_inode)); | ||
350 | |||
351 | /* Return delegation in anticipation of the rename */ | ||
352 | nfs_inode_return_delegation(dentry->d_inode); | ||
353 | |||
354 | sdentry = NULL; | ||
355 | do { | ||
356 | char *suffix = silly + slen - countersize; | ||
357 | |||
358 | dput(sdentry); | ||
359 | sillycounter++; | ||
360 | sprintf(suffix, "%*.*x", countersize, countersize, sillycounter); | ||
361 | |||
362 | dfprintk(VFS, "NFS: trying to rename %s to %s\n", | ||
363 | dentry->d_name.name, silly); | ||
364 | |||
365 | sdentry = lookup_one_len(silly, dentry->d_parent, slen); | ||
366 | /* | ||
367 | * N.B. Better to return EBUSY here ... it could be | ||
368 | * dangerous to delete the file while it's in use. | ||
369 | */ | ||
370 | if (IS_ERR(sdentry)) | ||
371 | goto out; | ||
372 | } while (sdentry->d_inode != NULL); /* need negative lookup */ | ||
373 | |||
374 | qsilly.name = silly; | ||
375 | qsilly.len = strlen(silly); | ||
376 | error = NFS_PROTO(dir)->rename(dir, &dentry->d_name, dir, &qsilly); | ||
377 | if (dentry->d_inode) | ||
378 | nfs_mark_for_revalidate(dentry->d_inode); | ||
379 | if (!error) { | ||
380 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); | ||
381 | d_move(dentry, sdentry); | ||
382 | error = nfs_async_unlink(dir, dentry); | ||
383 | /* If we return 0 we don't unlink */ | ||
384 | } | ||
385 | dput(sdentry); | ||
386 | out: | ||
387 | return error; | ||
388 | } | ||