Skip to content

Commit

Permalink
Merge tag 'afs-fixes-20200413' of git://git.kernel.org/pub/scm/linux/…
Browse files Browse the repository at this point in the history
…kernel/git/dhowells/linux-fs

Pull AFS fixes from David Howells:

 - Fix the decoding of fetched file status records so that the xdr
   pointer is advanced under all circumstances.

 - Fix the decoding of a fetched file status record that indicates an
   inline abort (ie. an error) so that it sets the flag saying the
   decoder stored the abort code.

 - Fix the decoding of the result of the rename operation so that it
   doesn't skip the decoding of the second fetched file status (ie. that
   of the dest dir) in the case that the source and dest dirs were the
   same as this causes the xdr pointer not to be advanced, leading to
   incorrect decoding of subsequent parts of the reply.

 - Fix the dump of a bad YFSFetchStatus record to dump the full length.

 - Fix a race between local editing of directory contents and accessing
   the dir for reading or d_revalidate by using the same lock in both.

 - Fix afs_d_revalidate() to not accidentally reverse the version on a
   dentry when it's meant to be bringing it forward.

* tag 'afs-fixes-20200413' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs:
  afs: Fix afs_d_validate() to set the right directory version
  afs: Fix race between post-modification dir edit and readdir/d_revalidate
  afs: Fix length of dump of bad YFSFetchStatus record
  afs: Fix rename operation status delivery
  afs: Fix decoding of inline abort codes from version 1 status records
  afs: Fix missing XDR advance in xdr_decode_{AFS,YFS}FSFetchStatus()
  • Loading branch information
Linus Torvalds committed Apr 14, 2020
2 parents ac4075b + 40fc810 commit f4cd666
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 71 deletions.
108 changes: 67 additions & 41 deletions fs/afs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -1032,7 +1032,7 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
struct dentry *parent;
struct inode *inode;
struct key *key;
afs_dataversion_t dir_version;
afs_dataversion_t dir_version, invalid_before;
long de_version;
int ret;

Expand Down Expand Up @@ -1084,8 +1084,8 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
if (de_version == (long)dir_version)
goto out_valid_noupdate;

dir_version = dir->invalid_before;
if (de_version - (long)dir_version >= 0)
invalid_before = dir->invalid_before;
if (de_version - (long)invalid_before >= 0)
goto out_valid;

_debug("dir modified");
Expand Down Expand Up @@ -1275,6 +1275,7 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
struct afs_fs_cursor fc;
struct afs_vnode *dvnode = AFS_FS_I(dir);
struct key *key;
afs_dataversion_t data_version;
int ret;

mode |= S_IFDIR;
Expand All @@ -1295,7 +1296,7 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)

ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
afs_dataversion_t data_version = dvnode->status.data_version + 1;
data_version = dvnode->status.data_version + 1;

while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
Expand All @@ -1316,10 +1317,14 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
goto error_key;
}

if (ret == 0 &&
test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
afs_edit_dir_add(dvnode, &dentry->d_name, &iget_data.fid,
afs_edit_dir_for_create);
if (ret == 0) {
down_write(&dvnode->validate_lock);
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
dvnode->status.data_version == data_version)
afs_edit_dir_add(dvnode, &dentry->d_name, &iget_data.fid,
afs_edit_dir_for_create);
up_write(&dvnode->validate_lock);
}

key_put(key);
kfree(scb);
Expand Down Expand Up @@ -1360,6 +1365,7 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
struct afs_fs_cursor fc;
struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode = NULL;
struct key *key;
afs_dataversion_t data_version;
int ret;

_enter("{%llx:%llu},{%pd}",
Expand Down Expand Up @@ -1391,7 +1397,7 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)

ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
afs_dataversion_t data_version = dvnode->status.data_version + 1;
data_version = dvnode->status.data_version + 1;

while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
Expand All @@ -1404,9 +1410,12 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
ret = afs_end_vnode_operation(&fc);
if (ret == 0) {
afs_dir_remove_subdir(dentry);
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
down_write(&dvnode->validate_lock);
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
dvnode->status.data_version == data_version)
afs_edit_dir_remove(dvnode, &dentry->d_name,
afs_edit_dir_for_rmdir);
up_write(&dvnode->validate_lock);
}
}

Expand Down Expand Up @@ -1544,10 +1553,15 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
ret = afs_end_vnode_operation(&fc);
if (ret == 0 && !(scb[1].have_status || scb[1].have_error))
ret = afs_dir_remove_link(dvnode, dentry, key);
if (ret == 0 &&
test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
afs_edit_dir_remove(dvnode, &dentry->d_name,
afs_edit_dir_for_unlink);

if (ret == 0) {
down_write(&dvnode->validate_lock);
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
dvnode->status.data_version == data_version)
afs_edit_dir_remove(dvnode, &dentry->d_name,
afs_edit_dir_for_unlink);
up_write(&dvnode->validate_lock);
}
}

if (need_rehash && ret < 0 && ret != -ENOENT)
Expand All @@ -1573,6 +1587,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
struct afs_status_cb *scb;
struct afs_vnode *dvnode = AFS_FS_I(dir);
struct key *key;
afs_dataversion_t data_version;
int ret;

mode |= S_IFREG;
Expand All @@ -1597,7 +1612,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,

ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
afs_dataversion_t data_version = dvnode->status.data_version + 1;
data_version = dvnode->status.data_version + 1;

while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
Expand All @@ -1618,9 +1633,12 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
goto error_key;
}

if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
down_write(&dvnode->validate_lock);
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
dvnode->status.data_version == data_version)
afs_edit_dir_add(dvnode, &dentry->d_name, &iget_data.fid,
afs_edit_dir_for_create);
up_write(&dvnode->validate_lock);

kfree(scb);
key_put(key);
Expand Down Expand Up @@ -1648,6 +1666,7 @@ static int afs_link(struct dentry *from, struct inode *dir,
struct afs_vnode *dvnode = AFS_FS_I(dir);
struct afs_vnode *vnode = AFS_FS_I(d_inode(from));
struct key *key;
afs_dataversion_t data_version;
int ret;

_enter("{%llx:%llu},{%llx:%llu},{%pd}",
Expand All @@ -1672,7 +1691,7 @@ static int afs_link(struct dentry *from, struct inode *dir,

ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
afs_dataversion_t data_version = dvnode->status.data_version + 1;
data_version = dvnode->status.data_version + 1;

if (mutex_lock_interruptible_nested(&vnode->io_lock, 1) < 0) {
afs_end_vnode_operation(&fc);
Expand Down Expand Up @@ -1702,9 +1721,12 @@ static int afs_link(struct dentry *from, struct inode *dir,
goto error_key;
}

if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
down_write(&dvnode->validate_lock);
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
dvnode->status.data_version == data_version)
afs_edit_dir_add(dvnode, &dentry->d_name, &vnode->fid,
afs_edit_dir_for_link);
up_write(&dvnode->validate_lock);

key_put(key);
kfree(scb);
Expand Down Expand Up @@ -1732,6 +1754,7 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
struct afs_status_cb *scb;
struct afs_vnode *dvnode = AFS_FS_I(dir);
struct key *key;
afs_dataversion_t data_version;
int ret;

_enter("{%llx:%llu},{%pd},%s",
Expand Down Expand Up @@ -1759,7 +1782,7 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,

ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
afs_dataversion_t data_version = dvnode->status.data_version + 1;
data_version = dvnode->status.data_version + 1;

while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
Expand All @@ -1780,9 +1803,12 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
goto error_key;
}

if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
down_write(&dvnode->validate_lock);
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
dvnode->status.data_version == data_version)
afs_edit_dir_add(dvnode, &dentry->d_name, &iget_data.fid,
afs_edit_dir_for_symlink);
up_write(&dvnode->validate_lock);

key_put(key);
kfree(scb);
Expand Down Expand Up @@ -1812,6 +1838,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct dentry *tmp = NULL, *rehash = NULL;
struct inode *new_inode;
struct key *key;
afs_dataversion_t orig_data_version;
afs_dataversion_t new_data_version;
bool new_negative = d_is_negative(new_dentry);
int ret;

Expand Down Expand Up @@ -1890,10 +1918,6 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,

ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, orig_dvnode, key, true)) {
afs_dataversion_t orig_data_version;
afs_dataversion_t new_data_version;
struct afs_status_cb *new_scb = &scb[1];

orig_data_version = orig_dvnode->status.data_version + 1;

if (orig_dvnode != new_dvnode) {
Expand All @@ -1904,15 +1928,14 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
new_data_version = new_dvnode->status.data_version + 1;
} else {
new_data_version = orig_data_version;
new_scb = &scb[0];
}

while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(orig_dvnode);
fc.cb_break_2 = afs_calc_vnode_cb_break(new_dvnode);
afs_fs_rename(&fc, old_dentry->d_name.name,
new_dvnode, new_dentry->d_name.name,
&scb[0], new_scb);
&scb[0], &scb[1]);
}

afs_vnode_commit_status(&fc, orig_dvnode, fc.cb_break,
Expand All @@ -1930,18 +1953,25 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (ret == 0) {
if (rehash)
d_rehash(rehash);
if (test_bit(AFS_VNODE_DIR_VALID, &orig_dvnode->flags))
afs_edit_dir_remove(orig_dvnode, &old_dentry->d_name,
afs_edit_dir_for_rename_0);
down_write(&orig_dvnode->validate_lock);
if (test_bit(AFS_VNODE_DIR_VALID, &orig_dvnode->flags) &&
orig_dvnode->status.data_version == orig_data_version)
afs_edit_dir_remove(orig_dvnode, &old_dentry->d_name,
afs_edit_dir_for_rename_0);
if (orig_dvnode != new_dvnode) {
up_write(&orig_dvnode->validate_lock);

if (!new_negative &&
test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags))
afs_edit_dir_remove(new_dvnode, &new_dentry->d_name,
afs_edit_dir_for_rename_1);
down_write(&new_dvnode->validate_lock);
}
if (test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags) &&
orig_dvnode->status.data_version == new_data_version) {
if (!new_negative)
afs_edit_dir_remove(new_dvnode, &new_dentry->d_name,
afs_edit_dir_for_rename_1);

if (test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags))
afs_edit_dir_add(new_dvnode, &new_dentry->d_name,
&vnode->fid, afs_edit_dir_for_rename_2);
}

new_inode = d_inode(new_dentry);
if (new_inode) {
Expand All @@ -1957,14 +1987,10 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
* Note that if we ever implement RENAME_EXCHANGE, we'll have
* to update both dentries with opposing dir versions.
*/
if (new_dvnode != orig_dvnode) {
afs_update_dentry_version(&fc, old_dentry, &scb[1]);
afs_update_dentry_version(&fc, new_dentry, &scb[1]);
} else {
afs_update_dentry_version(&fc, old_dentry, &scb[0]);
afs_update_dentry_version(&fc, new_dentry, &scb[0]);
}
afs_update_dentry_version(&fc, old_dentry, &scb[1]);
afs_update_dentry_version(&fc, new_dentry, &scb[1]);
d_move(old_dentry, new_dentry);
up_write(&new_dvnode->validate_lock);
goto error_tmp;
}

Expand Down
22 changes: 15 additions & 7 deletions fs/afs/dir_silly.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode
{
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
afs_dataversion_t dir_data_version;
int ret = -ERESTARTSYS;

_enter("%pd,%pd", old, new);
Expand All @@ -31,7 +32,7 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode

trace_afs_silly_rename(vnode, false);
if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
afs_dataversion_t dir_data_version = dvnode->status.data_version + 1;
dir_data_version = dvnode->status.data_version + 1;

while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
Expand All @@ -54,12 +55,15 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode
dvnode->silly_key = key_get(key);
}

if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
down_write(&dvnode->validate_lock);
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
dvnode->status.data_version == dir_data_version) {
afs_edit_dir_remove(dvnode, &old->d_name,
afs_edit_dir_for_silly_0);
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
afs_edit_dir_add(dvnode, &new->d_name,
&vnode->fid, afs_edit_dir_for_silly_1);
}
up_write(&dvnode->validate_lock);
}

kfree(scb);
Expand Down Expand Up @@ -181,10 +185,14 @@ static int afs_do_silly_unlink(struct afs_vnode *dvnode, struct afs_vnode *vnode
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
}
}
if (ret == 0 &&
test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
afs_edit_dir_remove(dvnode, &dentry->d_name,
afs_edit_dir_for_unlink);
if (ret == 0) {
down_write(&dvnode->validate_lock);
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
dvnode->status.data_version == dir_data_version)
afs_edit_dir_remove(dvnode, &dentry->d_name,
afs_edit_dir_for_unlink);
up_write(&dvnode->validate_lock);
}
}

kfree(scb);
Expand Down
Loading

0 comments on commit f4cd666

Please sign in to comment.