diff options
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/dir.c | 70 | ||||
-rw-r--r-- | fs/nfs/unlink.c | 85 |
2 files changed, 84 insertions, 71 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 1e9e18813ad7..86f1d41c6078 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -1498,76 +1498,6 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry) | |||
1498 | return error; | 1498 | return error; |
1499 | } | 1499 | } |
1500 | 1500 | ||
1501 | static int nfs_sillyrename(struct inode *dir, struct dentry *dentry) | ||
1502 | { | ||
1503 | static unsigned int sillycounter; | ||
1504 | const int fileidsize = sizeof(NFS_FILEID(dentry->d_inode))*2; | ||
1505 | const int countersize = sizeof(sillycounter)*2; | ||
1506 | const int slen = sizeof(".nfs")+fileidsize+countersize-1; | ||
1507 | char silly[slen+1]; | ||
1508 | struct qstr qsilly; | ||
1509 | struct dentry *sdentry; | ||
1510 | int error = -EIO; | ||
1511 | |||
1512 | dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n", | ||
1513 | dentry->d_parent->d_name.name, dentry->d_name.name, | ||
1514 | atomic_read(&dentry->d_count)); | ||
1515 | nfs_inc_stats(dir, NFSIOS_SILLYRENAME); | ||
1516 | |||
1517 | /* | ||
1518 | * We don't allow a dentry to be silly-renamed twice. | ||
1519 | */ | ||
1520 | error = -EBUSY; | ||
1521 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) | ||
1522 | goto out; | ||
1523 | |||
1524 | sprintf(silly, ".nfs%*.*Lx", | ||
1525 | fileidsize, fileidsize, | ||
1526 | (unsigned long long)NFS_FILEID(dentry->d_inode)); | ||
1527 | |||
1528 | /* Return delegation in anticipation of the rename */ | ||
1529 | nfs_inode_return_delegation(dentry->d_inode); | ||
1530 | |||
1531 | sdentry = NULL; | ||
1532 | do { | ||
1533 | char *suffix = silly + slen - countersize; | ||
1534 | |||
1535 | dput(sdentry); | ||
1536 | sillycounter++; | ||
1537 | sprintf(suffix, "%*.*x", countersize, countersize, sillycounter); | ||
1538 | |||
1539 | dfprintk(VFS, "NFS: trying to rename %s to %s\n", | ||
1540 | dentry->d_name.name, silly); | ||
1541 | |||
1542 | sdentry = lookup_one_len(silly, dentry->d_parent, slen); | ||
1543 | /* | ||
1544 | * N.B. Better to return EBUSY here ... it could be | ||
1545 | * dangerous to delete the file while it's in use. | ||
1546 | */ | ||
1547 | if (IS_ERR(sdentry)) | ||
1548 | goto out; | ||
1549 | } while(sdentry->d_inode != NULL); /* need negative lookup */ | ||
1550 | |||
1551 | qsilly.name = silly; | ||
1552 | qsilly.len = strlen(silly); | ||
1553 | if (dentry->d_inode) { | ||
1554 | error = NFS_PROTO(dir)->rename(dir, &dentry->d_name, | ||
1555 | dir, &qsilly); | ||
1556 | nfs_mark_for_revalidate(dentry->d_inode); | ||
1557 | } else | ||
1558 | error = NFS_PROTO(dir)->rename(dir, &dentry->d_name, | ||
1559 | dir, &qsilly); | ||
1560 | if (!error) { | ||
1561 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); | ||
1562 | d_move(dentry, sdentry); | ||
1563 | error = nfs_async_unlink(dir, dentry); | ||
1564 | /* If we return 0 we don't unlink */ | ||
1565 | } | ||
1566 | dput(sdentry); | ||
1567 | out: | ||
1568 | return error; | ||
1569 | } | ||
1570 | |||
1571 | /* | 1501 | /* |
1572 | * Remove a file after making sure there are no pending writes, | 1502 | * Remove a file after making sure there are no pending writes, |
1573 | * and after checking that the file has only one user. | 1503 | * and after checking that the file has only one user. |
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 | } | ||