Skip to content

Commit 79ddbfa

Browse files
committed
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>
1 parent 99987c5 commit 79ddbfa

File tree

8 files changed

+395
-13
lines changed

8 files changed

+395
-13
lines changed

fs/afs/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ kafs-y := \
1313
cmservice.o \
1414
dir.o \
1515
dir_edit.o \
16+
dir_silly.o \
1617
dynroot.o \
1718
file.o \
1819
flock.o \

fs/afs/dir.c

Lines changed: 107 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ static int afs_dir_open(struct inode *inode, struct file *file);
2626
static int afs_readdir(struct file *file, struct dir_context *ctx);
2727
static int afs_d_revalidate(struct dentry *dentry, unsigned int flags);
2828
static int afs_d_delete(const struct dentry *dentry);
29+
static void afs_d_iput(struct dentry *dentry, struct inode *inode);
2930
static int afs_lookup_one_filldir(struct dir_context *ctx, const char *name, int nlen,
3031
loff_t fpos, u64 ino, unsigned dtype);
3132
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 = {
8586
.d_delete = afs_d_delete,
8687
.d_release = afs_d_release,
8788
.d_automount = afs_d_automount,
89+
.d_iput = afs_d_iput,
8890
};
8991

9092
struct afs_lookup_one_cookie {
@@ -1083,6 +1085,16 @@ static int afs_d_delete(const struct dentry *dentry)
10831085
return 1;
10841086
}
10851087

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+
10861098
/*
10871099
* handle dentry release
10881100
*/
@@ -1225,6 +1237,12 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
12251237
goto error_key;
12261238
}
12271239

1240+
if (vnode) {
1241+
ret = down_write_killable(&vnode->rmdir_lock);
1242+
if (ret < 0)
1243+
goto error_key;
1244+
}
1245+
12281246
ret = -ERESTARTSYS;
12291247
if (afs_begin_vnode_operation(&fc, dvnode, key)) {
12301248
while (afs_select_fileserver(&fc)) {
@@ -1243,6 +1261,8 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
12431261
}
12441262
}
12451263

1264+
if (vnode)
1265+
up_write(&vnode->rmdir_lock);
12461266
error_key:
12471267
key_put(key);
12481268
error:
@@ -1259,9 +1279,9 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
12591279
* However, if we didn't have a callback promise outstanding, or it was
12601280
* outstanding on a different server, then it won't break it either...
12611281
*/
1262-
static int afs_dir_remove_link(struct dentry *dentry, struct key *key,
1263-
unsigned long d_version_before,
1264-
unsigned long d_version_after)
1282+
int afs_dir_remove_link(struct dentry *dentry, struct key *key,
1283+
unsigned long d_version_before,
1284+
unsigned long d_version_after)
12651285
{
12661286
bool dir_valid;
12671287
int ret = 0;
@@ -1308,6 +1328,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
13081328
struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode = NULL;
13091329
struct key *key;
13101330
unsigned long d_version = (unsigned long)dentry->d_fsdata;
1331+
bool need_rehash = false;
13111332
u64 data_version = dvnode->status.data_version;
13121333
int ret;
13131334

@@ -1331,6 +1352,21 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
13311352
goto error_key;
13321353
}
13331354

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+
13341370
ret = -ERESTARTSYS;
13351371
if (afs_begin_vnode_operation(&fc, dvnode, key)) {
13361372
while (afs_select_fileserver(&fc)) {
@@ -1362,6 +1398,9 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
13621398
afs_edit_dir_for_unlink);
13631399
}
13641400

1401+
if (need_rehash && ret < 0 && ret != -ENOENT)
1402+
d_rehash(dentry);
1403+
13651404
error_key:
13661405
key_put(key);
13671406
error:
@@ -1582,6 +1621,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
15821621
{
15831622
struct afs_fs_cursor fc;
15841623
struct afs_vnode *orig_dvnode, *new_dvnode, *vnode;
1624+
struct dentry *tmp = NULL, *rehash = NULL;
1625+
struct inode *new_inode;
15851626
struct key *key;
15861627
u64 orig_data_version, new_data_version;
15871628
bool new_negative = d_is_negative(new_dentry);
@@ -1590,6 +1631,10 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
15901631
if (flags)
15911632
return -EINVAL;
15921633

1634+
/* Don't allow silly-rename files be moved around. */
1635+
if (old_dentry->d_flags & DCACHE_NFSFS_RENAMED)
1636+
return -EINVAL;
1637+
15931638
vnode = AFS_FS_I(d_inode(old_dentry));
15941639
orig_dvnode = AFS_FS_I(old_dir);
15951640
new_dvnode = AFS_FS_I(new_dir);
@@ -1608,12 +1653,48 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
16081653
goto error;
16091654
}
16101655

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+
16111692
ret = -ERESTARTSYS;
16121693
if (afs_begin_vnode_operation(&fc, orig_dvnode, key)) {
16131694
if (orig_dvnode != new_dvnode) {
16141695
if (mutex_lock_interruptible_nested(&new_dvnode->io_lock, 1) < 0) {
16151696
afs_end_vnode_operation(&fc);
1616-
goto error_key;
1697+
goto error_rehash;
16171698
}
16181699
}
16191700
while (afs_select_fileserver(&fc)) {
@@ -1630,25 +1711,42 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
16301711
mutex_unlock(&new_dvnode->io_lock);
16311712
ret = afs_end_vnode_operation(&fc);
16321713
if (ret < 0)
1633-
goto error_key;
1714+
goto error_rehash;
16341715
}
16351716

16361717
if (ret == 0) {
1718+
if (rehash)
1719+
d_rehash(rehash);
16371720
if (test_bit(AFS_VNODE_DIR_VALID, &orig_dvnode->flags))
16381721
afs_edit_dir_remove(orig_dvnode, &old_dentry->d_name,
1639-
afs_edit_dir_for_rename);
1722+
afs_edit_dir_for_rename_0);
16401723

16411724
if (!new_negative &&
16421725
test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags))
16431726
afs_edit_dir_remove(new_dvnode, &new_dentry->d_name,
1644-
afs_edit_dir_for_rename);
1727+
afs_edit_dir_for_rename_1);
16451728

16461729
if (test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags))
16471730
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;
16491742
}
16501743

1651-
error_key:
1744+
error_rehash:
1745+
if (rehash)
1746+
d_rehash(rehash);
1747+
error_tmp:
1748+
if (tmp)
1749+
dput(tmp);
16521750
key_put(key);
16531751
error:
16541752
_leave(" = %d", ret);

0 commit comments

Comments
 (0)