diff options
author | David Howells <dhowells@redhat.com> | 2019-04-25 09:26:51 -0400 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2019-04-25 09:26:51 -0400 |
commit | 79ddbfa500b37a94fa7501e65ebdd5c0e4c7592d (patch) | |
tree | 1521d6e9276078aaf142ea40ab0f1e156c0fc276 /fs/afs | |
parent | 99987c560046ea178eb5aea793043deea255f185 (diff) |
afs: Implement sillyrename for unlink and rename
Implement sillyrename for AFS unlink and rename, using the NFS variant
implementation as a basis.
Note that the asynchronous file locking extender/releaser has to be
notified with a state change to stop it complaining if there's a race
between that and the actual file deletion.
A tracepoint, afs_silly_rename, is also added to note the silly rename and
the cleanup. The afs_edit_dir tracepoint is given some extra reason
indicators and the afs_flock_ev tracepoint is given a silly-delete file
lock cancellation indicator.
Signed-off-by: David Howells <dhowells@redhat.com>
Diffstat (limited to 'fs/afs')
-rw-r--r-- | fs/afs/Makefile | 1 | ||||
-rw-r--r-- | fs/afs/dir.c | 116 | ||||
-rw-r--r-- | fs/afs/dir_silly.c | 239 | ||||
-rw-r--r-- | fs/afs/flock.c | 2 | ||||
-rw-r--r-- | fs/afs/inode.c | 2 | ||||
-rw-r--r-- | fs/afs/internal.h | 10 | ||||
-rw-r--r-- | fs/afs/super.c | 4 |
7 files changed, 363 insertions, 11 deletions
diff --git a/fs/afs/Makefile b/fs/afs/Makefile index 0738e2bf5193..cbf31f6cd177 100644 --- a/fs/afs/Makefile +++ b/fs/afs/Makefile | |||
@@ -13,6 +13,7 @@ kafs-y := \ | |||
13 | cmservice.o \ | 13 | cmservice.o \ |
14 | dir.o \ | 14 | dir.o \ |
15 | dir_edit.o \ | 15 | dir_edit.o \ |
16 | dir_silly.o \ | ||
16 | dynroot.o \ | 17 | dynroot.o \ |
17 | file.o \ | 18 | file.o \ |
18 | flock.o \ | 19 | flock.o \ |
diff --git a/fs/afs/dir.c b/fs/afs/dir.c index be5d2f932b77..6c8523501639 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c | |||
@@ -26,6 +26,7 @@ static int afs_dir_open(struct inode *inode, struct file *file); | |||
26 | static int afs_readdir(struct file *file, struct dir_context *ctx); | 26 | static int afs_readdir(struct file *file, struct dir_context *ctx); |
27 | static int afs_d_revalidate(struct dentry *dentry, unsigned int flags); | 27 | static int afs_d_revalidate(struct dentry *dentry, unsigned int flags); |
28 | static int afs_d_delete(const struct dentry *dentry); | 28 | static int afs_d_delete(const struct dentry *dentry); |
29 | static void afs_d_iput(struct dentry *dentry, struct inode *inode); | ||
29 | static int afs_lookup_one_filldir(struct dir_context *ctx, const char *name, int nlen, | 30 | static int afs_lookup_one_filldir(struct dir_context *ctx, const char *name, int nlen, |
30 | loff_t fpos, u64 ino, unsigned dtype); | 31 | loff_t fpos, u64 ino, unsigned dtype); |
31 | static int afs_lookup_filldir(struct dir_context *ctx, const char *name, int nlen, | 32 | static int afs_lookup_filldir(struct dir_context *ctx, const char *name, int nlen, |
@@ -85,6 +86,7 @@ const struct dentry_operations afs_fs_dentry_operations = { | |||
85 | .d_delete = afs_d_delete, | 86 | .d_delete = afs_d_delete, |
86 | .d_release = afs_d_release, | 87 | .d_release = afs_d_release, |
87 | .d_automount = afs_d_automount, | 88 | .d_automount = afs_d_automount, |
89 | .d_iput = afs_d_iput, | ||
88 | }; | 90 | }; |
89 | 91 | ||
90 | struct afs_lookup_one_cookie { | 92 | struct afs_lookup_one_cookie { |
@@ -1084,6 +1086,16 @@ zap: | |||
1084 | } | 1086 | } |
1085 | 1087 | ||
1086 | /* | 1088 | /* |
1089 | * Clean up sillyrename files on dentry removal. | ||
1090 | */ | ||
1091 | static void afs_d_iput(struct dentry *dentry, struct inode *inode) | ||
1092 | { | ||
1093 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) | ||
1094 | afs_silly_iput(dentry, inode); | ||
1095 | iput(inode); | ||
1096 | } | ||
1097 | |||
1098 | /* | ||
1087 | * handle dentry release | 1099 | * handle dentry release |
1088 | */ | 1100 | */ |
1089 | void afs_d_release(struct dentry *dentry) | 1101 | void afs_d_release(struct dentry *dentry) |
@@ -1225,6 +1237,12 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry) | |||
1225 | goto error_key; | 1237 | goto error_key; |
1226 | } | 1238 | } |
1227 | 1239 | ||
1240 | if (vnode) { | ||
1241 | ret = down_write_killable(&vnode->rmdir_lock); | ||
1242 | if (ret < 0) | ||
1243 | goto error_key; | ||
1244 | } | ||
1245 | |||
1228 | ret = -ERESTARTSYS; | 1246 | ret = -ERESTARTSYS; |
1229 | if (afs_begin_vnode_operation(&fc, dvnode, key)) { | 1247 | if (afs_begin_vnode_operation(&fc, dvnode, key)) { |
1230 | while (afs_select_fileserver(&fc)) { | 1248 | while (afs_select_fileserver(&fc)) { |
@@ -1243,6 +1261,8 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry) | |||
1243 | } | 1261 | } |
1244 | } | 1262 | } |
1245 | 1263 | ||
1264 | if (vnode) | ||
1265 | up_write(&vnode->rmdir_lock); | ||
1246 | error_key: | 1266 | error_key: |
1247 | key_put(key); | 1267 | key_put(key); |
1248 | error: | 1268 | error: |
@@ -1259,9 +1279,9 @@ error: | |||
1259 | * However, if we didn't have a callback promise outstanding, or it was | 1279 | * However, if we didn't have a callback promise outstanding, or it was |
1260 | * outstanding on a different server, then it won't break it either... | 1280 | * outstanding on a different server, then it won't break it either... |
1261 | */ | 1281 | */ |
1262 | static int afs_dir_remove_link(struct dentry *dentry, struct key *key, | 1282 | int afs_dir_remove_link(struct dentry *dentry, struct key *key, |
1263 | unsigned long d_version_before, | 1283 | unsigned long d_version_before, |
1264 | unsigned long d_version_after) | 1284 | unsigned long d_version_after) |
1265 | { | 1285 | { |
1266 | bool dir_valid; | 1286 | bool dir_valid; |
1267 | int ret = 0; | 1287 | int ret = 0; |
@@ -1308,6 +1328,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) | |||
1308 | struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode = NULL; | 1328 | struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode = NULL; |
1309 | struct key *key; | 1329 | struct key *key; |
1310 | unsigned long d_version = (unsigned long)dentry->d_fsdata; | 1330 | unsigned long d_version = (unsigned long)dentry->d_fsdata; |
1331 | bool need_rehash = false; | ||
1311 | u64 data_version = dvnode->status.data_version; | 1332 | u64 data_version = dvnode->status.data_version; |
1312 | int ret; | 1333 | int ret; |
1313 | 1334 | ||
@@ -1331,6 +1352,21 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) | |||
1331 | goto error_key; | 1352 | goto error_key; |
1332 | } | 1353 | } |
1333 | 1354 | ||
1355 | spin_lock(&dentry->d_lock); | ||
1356 | if (vnode && d_count(dentry) > 1) { | ||
1357 | spin_unlock(&dentry->d_lock); | ||
1358 | /* Start asynchronous writeout of the inode */ | ||
1359 | write_inode_now(d_inode(dentry), 0); | ||
1360 | ret = afs_sillyrename(dvnode, vnode, dentry, key); | ||
1361 | goto error_key; | ||
1362 | } | ||
1363 | if (!d_unhashed(dentry)) { | ||
1364 | /* Prevent a race with RCU lookup. */ | ||
1365 | __d_drop(dentry); | ||
1366 | need_rehash = true; | ||
1367 | } | ||
1368 | spin_unlock(&dentry->d_lock); | ||
1369 | |||
1334 | ret = -ERESTARTSYS; | 1370 | ret = -ERESTARTSYS; |
1335 | if (afs_begin_vnode_operation(&fc, dvnode, key)) { | 1371 | if (afs_begin_vnode_operation(&fc, dvnode, key)) { |
1336 | while (afs_select_fileserver(&fc)) { | 1372 | while (afs_select_fileserver(&fc)) { |
@@ -1362,6 +1398,9 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) | |||
1362 | afs_edit_dir_for_unlink); | 1398 | afs_edit_dir_for_unlink); |
1363 | } | 1399 | } |
1364 | 1400 | ||
1401 | if (need_rehash && ret < 0 && ret != -ENOENT) | ||
1402 | d_rehash(dentry); | ||
1403 | |||
1365 | error_key: | 1404 | error_key: |
1366 | key_put(key); | 1405 | key_put(key); |
1367 | error: | 1406 | error: |
@@ -1582,6 +1621,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
1582 | { | 1621 | { |
1583 | struct afs_fs_cursor fc; | 1622 | struct afs_fs_cursor fc; |
1584 | struct afs_vnode *orig_dvnode, *new_dvnode, *vnode; | 1623 | struct afs_vnode *orig_dvnode, *new_dvnode, *vnode; |
1624 | struct dentry *tmp = NULL, *rehash = NULL; | ||
1625 | struct inode *new_inode; | ||
1585 | struct key *key; | 1626 | struct key *key; |
1586 | u64 orig_data_version, new_data_version; | 1627 | u64 orig_data_version, new_data_version; |
1587 | bool new_negative = d_is_negative(new_dentry); | 1628 | bool new_negative = d_is_negative(new_dentry); |
@@ -1590,6 +1631,10 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
1590 | if (flags) | 1631 | if (flags) |
1591 | return -EINVAL; | 1632 | return -EINVAL; |
1592 | 1633 | ||
1634 | /* Don't allow silly-rename files be moved around. */ | ||
1635 | if (old_dentry->d_flags & DCACHE_NFSFS_RENAMED) | ||
1636 | return -EINVAL; | ||
1637 | |||
1593 | vnode = AFS_FS_I(d_inode(old_dentry)); | 1638 | vnode = AFS_FS_I(d_inode(old_dentry)); |
1594 | orig_dvnode = AFS_FS_I(old_dir); | 1639 | orig_dvnode = AFS_FS_I(old_dir); |
1595 | new_dvnode = AFS_FS_I(new_dir); | 1640 | new_dvnode = AFS_FS_I(new_dir); |
@@ -1608,12 +1653,48 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
1608 | goto error; | 1653 | goto error; |
1609 | } | 1654 | } |
1610 | 1655 | ||
1656 | /* For non-directories, check whether the target is busy and if so, | ||
1657 | * make a copy of the dentry and then do a silly-rename. If the | ||
1658 | * silly-rename succeeds, the copied dentry is hashed and becomes the | ||
1659 | * new target. | ||
1660 | */ | ||
1661 | if (d_is_positive(new_dentry) && !d_is_dir(new_dentry)) { | ||
1662 | /* To prevent any new references to the target during the | ||
1663 | * rename, we unhash the dentry in advance. | ||
1664 | */ | ||
1665 | if (!d_unhashed(new_dentry)) { | ||
1666 | d_drop(new_dentry); | ||
1667 | rehash = new_dentry; | ||
1668 | } | ||
1669 | |||
1670 | if (d_count(new_dentry) > 2) { | ||
1671 | /* copy the target dentry's name */ | ||
1672 | ret = -ENOMEM; | ||
1673 | tmp = d_alloc(new_dentry->d_parent, | ||
1674 | &new_dentry->d_name); | ||
1675 | if (!tmp) | ||
1676 | goto error_rehash; | ||
1677 | |||
1678 | ret = afs_sillyrename(new_dvnode, | ||
1679 | AFS_FS_I(d_inode(new_dentry)), | ||
1680 | new_dentry, key); | ||
1681 | if (ret) | ||
1682 | goto error_rehash; | ||
1683 | |||
1684 | new_dentry = tmp; | ||
1685 | rehash = NULL; | ||
1686 | new_negative = true; | ||
1687 | orig_data_version = orig_dvnode->status.data_version; | ||
1688 | new_data_version = new_dvnode->status.data_version; | ||
1689 | } | ||
1690 | } | ||
1691 | |||
1611 | ret = -ERESTARTSYS; | 1692 | ret = -ERESTARTSYS; |
1612 | if (afs_begin_vnode_operation(&fc, orig_dvnode, key)) { | 1693 | if (afs_begin_vnode_operation(&fc, orig_dvnode, key)) { |
1613 | if (orig_dvnode != new_dvnode) { | 1694 | if (orig_dvnode != new_dvnode) { |
1614 | if (mutex_lock_interruptible_nested(&new_dvnode->io_lock, 1) < 0) { | 1695 | if (mutex_lock_interruptible_nested(&new_dvnode->io_lock, 1) < 0) { |
1615 | afs_end_vnode_operation(&fc); | 1696 | afs_end_vnode_operation(&fc); |
1616 | goto error_key; | 1697 | goto error_rehash; |
1617 | } | 1698 | } |
1618 | } | 1699 | } |
1619 | while (afs_select_fileserver(&fc)) { | 1700 | while (afs_select_fileserver(&fc)) { |
@@ -1630,25 +1711,42 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
1630 | mutex_unlock(&new_dvnode->io_lock); | 1711 | mutex_unlock(&new_dvnode->io_lock); |
1631 | ret = afs_end_vnode_operation(&fc); | 1712 | ret = afs_end_vnode_operation(&fc); |
1632 | if (ret < 0) | 1713 | if (ret < 0) |
1633 | goto error_key; | 1714 | goto error_rehash; |
1634 | } | 1715 | } |
1635 | 1716 | ||
1636 | if (ret == 0) { | 1717 | if (ret == 0) { |
1718 | if (rehash) | ||
1719 | d_rehash(rehash); | ||
1637 | if (test_bit(AFS_VNODE_DIR_VALID, &orig_dvnode->flags)) | 1720 | if (test_bit(AFS_VNODE_DIR_VALID, &orig_dvnode->flags)) |
1638 | afs_edit_dir_remove(orig_dvnode, &old_dentry->d_name, | 1721 | afs_edit_dir_remove(orig_dvnode, &old_dentry->d_name, |
1639 | afs_edit_dir_for_rename); | 1722 | afs_edit_dir_for_rename_0); |
1640 | 1723 | ||
1641 | if (!new_negative && | 1724 | if (!new_negative && |
1642 | test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags)) | 1725 | test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags)) |
1643 | afs_edit_dir_remove(new_dvnode, &new_dentry->d_name, | 1726 | afs_edit_dir_remove(new_dvnode, &new_dentry->d_name, |
1644 | afs_edit_dir_for_rename); | 1727 | afs_edit_dir_for_rename_1); |
1645 | 1728 | ||
1646 | if (test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags)) | 1729 | if (test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags)) |
1647 | afs_edit_dir_add(new_dvnode, &new_dentry->d_name, | 1730 | afs_edit_dir_add(new_dvnode, &new_dentry->d_name, |
1648 | &vnode->fid, afs_edit_dir_for_rename); | 1731 | &vnode->fid, afs_edit_dir_for_rename_2); |
1732 | |||
1733 | new_inode = d_inode(new_dentry); | ||
1734 | if (new_inode) { | ||
1735 | spin_lock(&new_inode->i_lock); | ||
1736 | if (new_inode->i_nlink > 0) | ||
1737 | drop_nlink(new_inode); | ||
1738 | spin_unlock(&new_inode->i_lock); | ||
1739 | } | ||
1740 | d_move(old_dentry, new_dentry); | ||
1741 | goto error_tmp; | ||
1649 | } | 1742 | } |
1650 | 1743 | ||
1651 | error_key: | 1744 | error_rehash: |
1745 | if (rehash) | ||
1746 | d_rehash(rehash); | ||
1747 | error_tmp: | ||
1748 | if (tmp) | ||
1749 | dput(tmp); | ||
1652 | key_put(key); | 1750 | key_put(key); |
1653 | error: | 1751 | error: |
1654 | _leave(" = %d", ret); | 1752 | _leave(" = %d", ret); |
diff --git a/fs/afs/dir_silly.c b/fs/afs/dir_silly.c new file mode 100644 index 000000000000..f6f89fdab6b2 --- /dev/null +++ b/fs/afs/dir_silly.c | |||
@@ -0,0 +1,239 @@ | |||
1 | /* AFS silly rename handling | ||
2 | * | ||
3 | * Copyright (C) 2019 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * - Derived from NFS's sillyrename. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public Licence | ||
9 | * as published by the Free Software Foundation; either version | ||
10 | * 2 of the Licence, or (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/fs.h> | ||
15 | #include <linux/namei.h> | ||
16 | #include <linux/fsnotify.h> | ||
17 | #include "internal.h" | ||
18 | |||
19 | /* | ||
20 | * Actually perform the silly rename step. | ||
21 | */ | ||
22 | static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode, | ||
23 | struct dentry *old, struct dentry *new, | ||
24 | struct key *key) | ||
25 | { | ||
26 | struct afs_fs_cursor fc; | ||
27 | u64 dir_data_version = dvnode->status.data_version; | ||
28 | int ret = -ERESTARTSYS; | ||
29 | |||
30 | _enter("%pd,%pd", old, new); | ||
31 | |||
32 | trace_afs_silly_rename(vnode, false); | ||
33 | if (afs_begin_vnode_operation(&fc, dvnode, key)) { | ||
34 | while (afs_select_fileserver(&fc)) { | ||
35 | fc.cb_break = afs_calc_vnode_cb_break(dvnode); | ||
36 | afs_fs_rename(&fc, old->d_name.name, | ||
37 | dvnode, new->d_name.name, | ||
38 | dir_data_version, dir_data_version); | ||
39 | } | ||
40 | |||
41 | afs_vnode_commit_status(&fc, dvnode, fc.cb_break); | ||
42 | ret = afs_end_vnode_operation(&fc); | ||
43 | } | ||
44 | |||
45 | if (ret == 0) { | ||
46 | spin_lock(&old->d_lock); | ||
47 | old->d_flags |= DCACHE_NFSFS_RENAMED; | ||
48 | spin_unlock(&old->d_lock); | ||
49 | if (dvnode->silly_key != key) { | ||
50 | key_put(dvnode->silly_key); | ||
51 | dvnode->silly_key = key_get(key); | ||
52 | } | ||
53 | |||
54 | if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) | ||
55 | afs_edit_dir_remove(dvnode, &old->d_name, | ||
56 | afs_edit_dir_for_silly_0); | ||
57 | if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) | ||
58 | afs_edit_dir_add(dvnode, &new->d_name, | ||
59 | &vnode->fid, afs_edit_dir_for_silly_1); | ||
60 | |||
61 | /* vfs_unlink and the like do not issue this when a file is | ||
62 | * sillyrenamed, so do it here. | ||
63 | */ | ||
64 | fsnotify_nameremove(old, 0); | ||
65 | } | ||
66 | |||
67 | _leave(" = %d", ret); | ||
68 | return ret; | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * afs_sillyrename - Perform a silly-rename of a dentry | ||
73 | * | ||
74 | * AFS is stateless and the server doesn't know when the client is holding a | ||
75 | * file open. To prevent application problems when a file is unlinked while | ||
76 | * it's still open, the client performs a "silly-rename". That is, it renames | ||
77 | * the file to a hidden file in the same directory, and only performs the | ||
78 | * unlink once the last reference to it is put. | ||
79 | * | ||
80 | * The final cleanup is done during dentry_iput. | ||
81 | */ | ||
82 | int afs_sillyrename(struct afs_vnode *dvnode, struct afs_vnode *vnode, | ||
83 | struct dentry *dentry, struct key *key) | ||
84 | { | ||
85 | static unsigned int sillycounter; | ||
86 | struct dentry *sdentry = NULL; | ||
87 | unsigned char silly[16]; | ||
88 | int ret = -EBUSY; | ||
89 | |||
90 | _enter(""); | ||
91 | |||
92 | /* We don't allow a dentry to be silly-renamed twice. */ | ||
93 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) | ||
94 | return -EBUSY; | ||
95 | |||
96 | sdentry = NULL; | ||
97 | do { | ||
98 | int slen; | ||
99 | |||
100 | dput(sdentry); | ||
101 | sillycounter++; | ||
102 | |||
103 | /* Create a silly name. Note that the ".__afs" prefix is | ||
104 | * understood by the salvager and must not be changed. | ||
105 | */ | ||
106 | slen = scnprintf(silly, sizeof(silly), ".__afs%04X", sillycounter); | ||
107 | sdentry = lookup_one_len(silly, dentry->d_parent, slen); | ||
108 | |||
109 | /* N.B. Better to return EBUSY here ... it could be dangerous | ||
110 | * to delete the file while it's in use. | ||
111 | */ | ||
112 | if (IS_ERR(sdentry)) | ||
113 | goto out; | ||
114 | } while (!d_is_negative(sdentry)); | ||
115 | |||
116 | ihold(&vnode->vfs_inode); | ||
117 | |||
118 | ret = afs_do_silly_rename(dvnode, vnode, dentry, sdentry, key); | ||
119 | switch (ret) { | ||
120 | case 0: | ||
121 | /* The rename succeeded. */ | ||
122 | d_move(dentry, sdentry); | ||
123 | break; | ||
124 | case -ERESTARTSYS: | ||
125 | /* The result of the rename is unknown. Play it safe by forcing | ||
126 | * a new lookup. | ||
127 | */ | ||
128 | d_drop(dentry); | ||
129 | d_drop(sdentry); | ||
130 | } | ||
131 | |||
132 | iput(&vnode->vfs_inode); | ||
133 | dput(sdentry); | ||
134 | out: | ||
135 | _leave(" = %d", ret); | ||
136 | return ret; | ||
137 | } | ||
138 | |||
139 | /* | ||
140 | * Tell the server to remove a sillyrename file. | ||
141 | */ | ||
142 | static int afs_do_silly_unlink(struct afs_vnode *dvnode, struct afs_vnode *vnode, | ||
143 | struct dentry *dentry, struct key *key) | ||
144 | { | ||
145 | struct afs_fs_cursor fc; | ||
146 | u64 dir_data_version = dvnode->status.data_version; | ||
147 | int ret = -ERESTARTSYS; | ||
148 | |||
149 | _enter(""); | ||
150 | |||
151 | trace_afs_silly_rename(vnode, true); | ||
152 | if (afs_begin_vnode_operation(&fc, dvnode, key)) { | ||
153 | while (afs_select_fileserver(&fc)) { | ||
154 | fc.cb_break = afs_calc_vnode_cb_break(dvnode); | ||
155 | |||
156 | if (test_bit(AFS_SERVER_FL_IS_YFS, &fc.cbi->server->flags) && | ||
157 | !test_bit(AFS_SERVER_FL_NO_RM2, &fc.cbi->server->flags)) { | ||
158 | yfs_fs_remove_file2(&fc, vnode, dentry->d_name.name, | ||
159 | dir_data_version); | ||
160 | if (fc.ac.error != -ECONNABORTED || | ||
161 | fc.ac.abort_code != RXGEN_OPCODE) | ||
162 | continue; | ||
163 | set_bit(AFS_SERVER_FL_NO_RM2, &fc.cbi->server->flags); | ||
164 | } | ||
165 | |||
166 | afs_fs_remove(&fc, vnode, dentry->d_name.name, false, | ||
167 | dir_data_version); | ||
168 | } | ||
169 | |||
170 | afs_vnode_commit_status(&fc, dvnode, fc.cb_break); | ||
171 | ret = afs_end_vnode_operation(&fc); | ||
172 | if (ret == 0) { | ||
173 | drop_nlink(&vnode->vfs_inode); | ||
174 | if (vnode->vfs_inode.i_nlink == 0) { | ||
175 | set_bit(AFS_VNODE_DELETED, &vnode->flags); | ||
176 | clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); | ||
177 | } | ||
178 | } | ||
179 | if (ret == 0 && | ||
180 | test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) | ||
181 | afs_edit_dir_remove(dvnode, &dentry->d_name, | ||
182 | afs_edit_dir_for_unlink); | ||
183 | } | ||
184 | |||
185 | _leave(" = %d", ret); | ||
186 | return ret; | ||
187 | } | ||
188 | |||
189 | /* | ||
190 | * Remove sillyrename file on iput. | ||
191 | */ | ||
192 | int afs_silly_iput(struct dentry *dentry, struct inode *inode) | ||
193 | { | ||
194 | struct afs_vnode *dvnode = AFS_FS_I(d_inode(dentry->d_parent)); | ||
195 | struct afs_vnode *vnode = AFS_FS_I(inode); | ||
196 | struct dentry *alias; | ||
197 | int ret; | ||
198 | |||
199 | DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); | ||
200 | |||
201 | _enter("%p{%pd},%llx", dentry, dentry, vnode->fid.vnode); | ||
202 | |||
203 | down_read(&dvnode->rmdir_lock); | ||
204 | |||
205 | alias = d_alloc_parallel(dentry->d_parent, &dentry->d_name, &wq); | ||
206 | if (IS_ERR(alias)) { | ||
207 | up_read(&dvnode->rmdir_lock); | ||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | if (!d_in_lookup(alias)) { | ||
212 | /* We raced with lookup... See if we need to transfer the | ||
213 | * sillyrename information to the aliased dentry. | ||
214 | */ | ||
215 | ret = 0; | ||
216 | spin_lock(&alias->d_lock); | ||
217 | if (d_really_is_positive(alias) && | ||
218 | !(alias->d_flags & DCACHE_NFSFS_RENAMED)) { | ||
219 | alias->d_flags |= DCACHE_NFSFS_RENAMED; | ||
220 | ret = 1; | ||
221 | } | ||
222 | spin_unlock(&alias->d_lock); | ||
223 | up_read(&dvnode->rmdir_lock); | ||
224 | dput(alias); | ||
225 | return ret; | ||
226 | } | ||
227 | |||
228 | /* Stop lock-release from complaining. */ | ||
229 | spin_lock(&vnode->lock); | ||
230 | vnode->lock_state = AFS_VNODE_LOCK_DELETED; | ||
231 | trace_afs_flock_ev(vnode, NULL, afs_flock_silly_delete, 0); | ||
232 | spin_unlock(&vnode->lock); | ||
233 | |||
234 | afs_do_silly_unlink(dvnode, vnode, dentry, dvnode->silly_key); | ||
235 | up_read(&dvnode->rmdir_lock); | ||
236 | d_lookup_done(alias); | ||
237 | dput(alias); | ||
238 | return 1; | ||
239 | } | ||
diff --git a/fs/afs/flock.c b/fs/afs/flock.c index 742038a21ef7..325bf731d8dd 100644 --- a/fs/afs/flock.c +++ b/fs/afs/flock.c | |||
@@ -300,7 +300,7 @@ again: | |||
300 | /* attempt to release the server lock; if it fails, we just | 300 | /* attempt to release the server lock; if it fails, we just |
301 | * wait 5 minutes and it'll expire anyway */ | 301 | * wait 5 minutes and it'll expire anyway */ |
302 | ret = afs_release_lock(vnode, vnode->lock_key); | 302 | ret = afs_release_lock(vnode, vnode->lock_key); |
303 | if (ret < 0) { | 303 | if (ret < 0 && vnode->lock_state != AFS_VNODE_LOCK_DELETED) { |
304 | trace_afs_flock_ev(vnode, NULL, afs_flock_release_fail, | 304 | trace_afs_flock_ev(vnode, NULL, afs_flock_release_fail, |
305 | ret); | 305 | ret); |
306 | printk(KERN_WARNING "AFS:" | 306 | printk(KERN_WARNING "AFS:" |
diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 9cedc3fc1b77..3eef20ff285b 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c | |||
@@ -543,6 +543,8 @@ void afs_evict_inode(struct inode *inode) | |||
543 | #endif | 543 | #endif |
544 | 544 | ||
545 | afs_put_permits(rcu_access_pointer(vnode->permit_cache)); | 545 | afs_put_permits(rcu_access_pointer(vnode->permit_cache)); |
546 | key_put(vnode->silly_key); | ||
547 | vnode->silly_key = NULL; | ||
546 | key_put(vnode->lock_key); | 548 | key_put(vnode->lock_key); |
547 | vnode->lock_key = NULL; | 549 | vnode->lock_key = NULL; |
548 | _leave(""); | 550 | _leave(""); |
diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 5eb6be3f73b2..20fd44de26ac 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h | |||
@@ -621,6 +621,8 @@ struct afs_vnode { | |||
621 | struct afs_permits __rcu *permit_cache; /* cache of permits so far obtained */ | 621 | struct afs_permits __rcu *permit_cache; /* cache of permits so far obtained */ |
622 | struct mutex io_lock; /* Lock for serialising I/O on this mutex */ | 622 | struct mutex io_lock; /* Lock for serialising I/O on this mutex */ |
623 | struct rw_semaphore validate_lock; /* lock for validating this vnode */ | 623 | struct rw_semaphore validate_lock; /* lock for validating this vnode */ |
624 | struct rw_semaphore rmdir_lock; /* Lock for rmdir vs sillyrename */ | ||
625 | struct key *silly_key; /* Silly rename key */ | ||
624 | spinlock_t wb_lock; /* lock for wb_keys */ | 626 | spinlock_t wb_lock; /* lock for wb_keys */ |
625 | spinlock_t lock; /* waitqueue/flags lock */ | 627 | spinlock_t lock; /* waitqueue/flags lock */ |
626 | unsigned long flags; | 628 | unsigned long flags; |
@@ -866,6 +868,7 @@ extern const struct address_space_operations afs_dir_aops; | |||
866 | extern const struct dentry_operations afs_fs_dentry_operations; | 868 | extern const struct dentry_operations afs_fs_dentry_operations; |
867 | 869 | ||
868 | extern void afs_d_release(struct dentry *); | 870 | extern void afs_d_release(struct dentry *); |
871 | extern int afs_dir_remove_link(struct dentry *, struct key *, unsigned long, unsigned long); | ||
869 | 872 | ||
870 | /* | 873 | /* |
871 | * dir_edit.c | 874 | * dir_edit.c |
@@ -875,6 +878,13 @@ extern void afs_edit_dir_add(struct afs_vnode *, struct qstr *, struct afs_fid * | |||
875 | extern void afs_edit_dir_remove(struct afs_vnode *, struct qstr *, enum afs_edit_dir_reason); | 878 | extern void afs_edit_dir_remove(struct afs_vnode *, struct qstr *, enum afs_edit_dir_reason); |
876 | 879 | ||
877 | /* | 880 | /* |
881 | * dir_silly.c | ||
882 | */ | ||
883 | extern int afs_sillyrename(struct afs_vnode *, struct afs_vnode *, | ||
884 | struct dentry *, struct key *); | ||
885 | extern int afs_silly_iput(struct dentry *, struct inode *); | ||
886 | |||
887 | /* | ||
878 | * dynroot.c | 888 | * dynroot.c |
879 | */ | 889 | */ |
880 | extern const struct file_operations afs_dynroot_file_operations; | 890 | extern const struct file_operations afs_dynroot_file_operations; |
diff --git a/fs/afs/super.c b/fs/afs/super.c index 5adf012b8e27..6438849a75c4 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c | |||
@@ -45,7 +45,7 @@ struct file_system_type afs_fs_type = { | |||
45 | .init_fs_context = afs_init_fs_context, | 45 | .init_fs_context = afs_init_fs_context, |
46 | .parameters = &afs_fs_parameters, | 46 | .parameters = &afs_fs_parameters, |
47 | .kill_sb = afs_kill_super, | 47 | .kill_sb = afs_kill_super, |
48 | .fs_flags = 0, | 48 | .fs_flags = FS_RENAME_DOES_D_MOVE, |
49 | }; | 49 | }; |
50 | MODULE_ALIAS_FS("afs"); | 50 | MODULE_ALIAS_FS("afs"); |
51 | 51 | ||
@@ -656,6 +656,8 @@ static struct inode *afs_alloc_inode(struct super_block *sb) | |||
656 | vnode->cb_type = 0; | 656 | vnode->cb_type = 0; |
657 | vnode->lock_state = AFS_VNODE_LOCK_NONE; | 657 | vnode->lock_state = AFS_VNODE_LOCK_NONE; |
658 | 658 | ||
659 | init_rwsem(&vnode->rmdir_lock); | ||
660 | |||
659 | _leave(" = %p", &vnode->vfs_inode); | 661 | _leave(" = %p", &vnode->vfs_inode); |
660 | return &vnode->vfs_inode; | 662 | return &vnode->vfs_inode; |
661 | } | 663 | } |