Commit b98f0ec9 authored by David Howells's avatar David Howells

afs: Fix rename operation status delivery

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: 260a9803 ("[AFS]: Add "directory write" support.")
Fixes: 30062bd1 ("afs: Implement YFS support in the fs client")
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parent 3e0d9892
...@@ -1892,7 +1892,6 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1892,7 +1892,6 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (afs_begin_vnode_operation(&fc, orig_dvnode, key, true)) { if (afs_begin_vnode_operation(&fc, orig_dvnode, key, true)) {
afs_dataversion_t orig_data_version; afs_dataversion_t orig_data_version;
afs_dataversion_t new_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; orig_data_version = orig_dvnode->status.data_version + 1;
...@@ -1904,7 +1903,6 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1904,7 +1903,6 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
new_data_version = new_dvnode->status.data_version + 1; new_data_version = new_dvnode->status.data_version + 1;
} else { } else {
new_data_version = orig_data_version; new_data_version = orig_data_version;
new_scb = &scb[0];
} }
while (afs_select_fileserver(&fc)) { while (afs_select_fileserver(&fc)) {
...@@ -1912,7 +1910,7 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1912,7 +1910,7 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
fc.cb_break_2 = afs_calc_vnode_cb_break(new_dvnode); fc.cb_break_2 = afs_calc_vnode_cb_break(new_dvnode);
afs_fs_rename(&fc, old_dentry->d_name.name, afs_fs_rename(&fc, old_dentry->d_name.name,
new_dvnode, new_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, afs_vnode_commit_status(&fc, orig_dvnode, fc.cb_break,
...@@ -1957,13 +1955,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1957,13 +1955,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
* Note that if we ever implement RENAME_EXCHANGE, we'll have * Note that if we ever implement RENAME_EXCHANGE, we'll have
* to update both dentries with opposing dir versions. * 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, old_dentry, &scb[1]); afs_update_dentry_version(&fc, new_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]);
}
d_move(old_dentry, new_dentry); d_move(old_dentry, new_dentry);
goto error_tmp; goto error_tmp;
} }
......
...@@ -986,16 +986,16 @@ static int afs_deliver_fs_rename(struct afs_call *call) ...@@ -986,16 +986,16 @@ static int afs_deliver_fs_rename(struct afs_call *call)
if (ret < 0) if (ret < 0)
return ret; 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; bp = call->buffer;
ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_dir_scb); ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_dir_scb);
if (ret < 0) if (ret < 0)
return ret; return ret;
if (call->out_dir_scb != call->out_scb) { ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_scb);
ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_scb); if (ret < 0)
if (ret < 0) return ret;
return ret;
}
xdr_decode_AFSVolSync(&bp, call->out_volsync); xdr_decode_AFSVolSync(&bp, call->out_volsync);
_leave(" = 0 [done]"); _leave(" = 0 [done]");
......
...@@ -1157,11 +1157,9 @@ static int yfs_deliver_fs_rename(struct afs_call *call) ...@@ -1157,11 +1157,9 @@ static int yfs_deliver_fs_rename(struct afs_call *call)
ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb); ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb);
if (ret < 0) if (ret < 0)
return ret; return ret;
if (call->out_dir_scb != call->out_scb) { ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb);
ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb); if (ret < 0)
if (ret < 0) return ret;
return ret;
}
xdr_decode_YFSVolSync(&bp, call->out_volsync); xdr_decode_YFSVolSync(&bp, call->out_volsync);
_leave(" = 0 [done]"); _leave(" = 0 [done]");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment