| From 7eb556ee0feff30083fbc8dcfa5fff0f6d6db31b Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Wed, 8 Apr 2020 20:56:20 +0100 |
| Subject: [PATCH] afs: Fix rename operation status delivery |
| |
| commit b98f0ec91c42d87a70da42726b852ac8d78a3257 upstream. |
| |
| The afs_deliver_fs_rename() and yfs_deliver_fs_rename() functions both only |
| decode the second file status returned unless the parent directories are |
| different - unfortunately, this means that the xdr pointer isn't advanced |
| and the volsync record will be read incorrectly in such an instance. |
| |
| Fix this by always decoding the second status into the second |
| status/callback block which wasn't being used if the dirs were the same. |
| |
| The afs_update_dentry_version() calls that update the directory data |
| version numbers on the dentries can then unconditionally use the second |
| status record as this will always reflect the state of the destination dir |
| (the two records will be identical if the destination dir is the same as |
| the source dir) |
| |
| Fixes: 260a980317da ("[AFS]: Add "directory write" support.") |
| Fixes: 30062bd13e36 ("afs: Implement YFS support in the fs client") |
| Signed-off-by: David Howells <dhowells@redhat.com> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/fs/afs/dir.c b/fs/afs/dir.c |
| index c616637671f0..047de45bfcce 100644 |
| --- a/fs/afs/dir.c |
| +++ b/fs/afs/dir.c |
| @@ -1843,7 +1843,6 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, |
| 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; |
| |
| @@ -1855,7 +1854,6 @@ 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)) { |
| @@ -1863,7 +1861,7 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, |
| 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, |
| @@ -1908,13 +1906,8 @@ 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); |
| goto error_tmp; |
| } |
| diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c |
| index 00ef33bd3464..f1cfbccfbdd6 100644 |
| --- a/fs/afs/fsclient.c |
| +++ b/fs/afs/fsclient.c |
| @@ -987,16 +987,16 @@ static int afs_deliver_fs_rename(struct afs_call *call) |
| if (ret < 0) |
| return ret; |
| |
| - /* unmarshall the reply once we've received all of it */ |
| + /* If the two dirs are the same, we have two copies of the same status |
| + * report, so we just decode it twice. |
| + */ |
| bp = call->buffer; |
| ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_dir_scb); |
| if (ret < 0) |
| return ret; |
| - if (call->out_dir_scb != call->out_scb) { |
| - ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); |
| - if (ret < 0) |
| - return ret; |
| - } |
| + ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); |
| + if (ret < 0) |
| + return ret; |
| xdr_decode_AFSVolSync(&bp, call->out_volsync); |
| |
| _leave(" = 0 [done]"); |
| diff --git a/fs/afs/yfsclient.c b/fs/afs/yfsclient.c |
| index 505f9cb298dc..f6a11527b4eb 100644 |
| --- a/fs/afs/yfsclient.c |
| +++ b/fs/afs/yfsclient.c |
| @@ -1155,11 +1155,9 @@ static int yfs_deliver_fs_rename(struct afs_call *call) |
| ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb); |
| if (ret < 0) |
| return ret; |
| - if (call->out_dir_scb != call->out_scb) { |
| - ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); |
| - if (ret < 0) |
| - return ret; |
| - } |
| + ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); |
| + if (ret < 0) |
| + return ret; |
| |
| xdr_decode_YFSVolSync(&bp, call->out_volsync); |
| _leave(" = 0 [done]"); |
| -- |
| 2.7.4 |
| |