Commit f4cd6668 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'afs-fixes-20200413' of git://git.kernel.org/pub/scm/linux/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()
parents ac4075bc 40fc8102
This diff is collapsed.
...@@ -21,6 +21,7 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode ...@@ -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_fs_cursor fc;
struct afs_status_cb *scb; struct afs_status_cb *scb;
afs_dataversion_t dir_data_version;
int ret = -ERESTARTSYS; int ret = -ERESTARTSYS;
_enter("%pd,%pd", old, new); _enter("%pd,%pd", old, new);
...@@ -31,7 +32,7 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode ...@@ -31,7 +32,7 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode
trace_afs_silly_rename(vnode, false); trace_afs_silly_rename(vnode, false);
if (afs_begin_vnode_operation(&fc, dvnode, key, true)) { 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)) { while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(dvnode); fc.cb_break = afs_calc_vnode_cb_break(dvnode);
...@@ -54,12 +55,15 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode ...@@ -54,12 +55,15 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode
dvnode->silly_key = key_get(key); 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_remove(dvnode, &old->d_name,
afs_edit_dir_for_silly_0); afs_edit_dir_for_silly_0);
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
afs_edit_dir_add(dvnode, &new->d_name, afs_edit_dir_add(dvnode, &new->d_name,
&vnode->fid, afs_edit_dir_for_silly_1); &vnode->fid, afs_edit_dir_for_silly_1);
}
up_write(&dvnode->validate_lock);
} }
kfree(scb); kfree(scb);
...@@ -181,10 +185,14 @@ static int afs_do_silly_unlink(struct afs_vnode *dvnode, struct afs_vnode *vnode ...@@ -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); clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
} }
} }
if (ret == 0 && if (ret == 0) {
test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) down_write(&dvnode->validate_lock);
afs_edit_dir_remove(dvnode, &dentry->d_name, if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
afs_edit_dir_for_unlink); 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); kfree(scb);
......
...@@ -65,6 +65,7 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp, ...@@ -65,6 +65,7 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp,
bool inline_error = (call->operation_ID == afs_FS_InlineBulkStatus); bool inline_error = (call->operation_ID == afs_FS_InlineBulkStatus);
u64 data_version, size; u64 data_version, size;
u32 type, abort_code; u32 type, abort_code;
int ret;
abort_code = ntohl(xdr->abort_code); abort_code = ntohl(xdr->abort_code);
...@@ -78,7 +79,7 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp, ...@@ -78,7 +79,7 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp,
*/ */
status->abort_code = abort_code; status->abort_code = abort_code;
scb->have_error = true; scb->have_error = true;
return 0; goto good;
} }
pr_warn("Unknown AFSFetchStatus version %u\n", ntohl(xdr->if_version)); pr_warn("Unknown AFSFetchStatus version %u\n", ntohl(xdr->if_version));
...@@ -87,7 +88,8 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp, ...@@ -87,7 +88,8 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp,
if (abort_code != 0 && inline_error) { if (abort_code != 0 && inline_error) {
status->abort_code = abort_code; status->abort_code = abort_code;
return 0; scb->have_error = true;
goto good;
} }
type = ntohl(xdr->type); type = ntohl(xdr->type);
...@@ -123,13 +125,16 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp, ...@@ -123,13 +125,16 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp,
data_version |= (u64)ntohl(xdr->data_version_hi) << 32; data_version |= (u64)ntohl(xdr->data_version_hi) << 32;
status->data_version = data_version; status->data_version = data_version;
scb->have_status = true; scb->have_status = true;
good:
ret = 0;
advance:
*_bp = (const void *)*_bp + sizeof(*xdr); *_bp = (const void *)*_bp + sizeof(*xdr);
return 0; return ret;
bad: bad:
xdr_dump_bad(*_bp); xdr_dump_bad(*_bp);
return afs_protocol_error(call, -EBADMSG, afs_eproto_bad_status); ret = afs_protocol_error(call, -EBADMSG, afs_eproto_bad_status);
goto advance;
} }
static time64_t xdr_decode_expiry(struct afs_call *call, u32 expiry) static time64_t xdr_decode_expiry(struct afs_call *call, u32 expiry)
...@@ -981,16 +986,16 @@ static int afs_deliver_fs_rename(struct afs_call *call) ...@@ -981,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]");
......
...@@ -165,15 +165,15 @@ static void xdr_dump_bad(const __be32 *bp) ...@@ -165,15 +165,15 @@ static void xdr_dump_bad(const __be32 *bp)
int i; int i;
pr_notice("YFS XDR: Bad status record\n"); pr_notice("YFS XDR: Bad status record\n");
for (i = 0; i < 5 * 4 * 4; i += 16) { for (i = 0; i < 6 * 4 * 4; i += 16) {
memcpy(x, bp, 16); memcpy(x, bp, 16);
bp += 4; bp += 4;
pr_notice("%03x: %08x %08x %08x %08x\n", pr_notice("%03x: %08x %08x %08x %08x\n",
i, ntohl(x[0]), ntohl(x[1]), ntohl(x[2]), ntohl(x[3])); i, ntohl(x[0]), ntohl(x[1]), ntohl(x[2]), ntohl(x[3]));
} }
memcpy(x, bp, 4); memcpy(x, bp, 8);
pr_notice("0x50: %08x\n", ntohl(x[0])); pr_notice("0x60: %08x %08x\n", ntohl(x[0]), ntohl(x[1]));
} }
/* /*
...@@ -186,13 +186,14 @@ static int xdr_decode_YFSFetchStatus(const __be32 **_bp, ...@@ -186,13 +186,14 @@ static int xdr_decode_YFSFetchStatus(const __be32 **_bp,
const struct yfs_xdr_YFSFetchStatus *xdr = (const void *)*_bp; const struct yfs_xdr_YFSFetchStatus *xdr = (const void *)*_bp;
struct afs_file_status *status = &scb->status; struct afs_file_status *status = &scb->status;
u32 type; u32 type;
int ret;
status->abort_code = ntohl(xdr->abort_code); status->abort_code = ntohl(xdr->abort_code);
if (status->abort_code != 0) { if (status->abort_code != 0) {
if (status->abort_code == VNOVNODE) if (status->abort_code == VNOVNODE)
status->nlink = 0; status->nlink = 0;
scb->have_error = true; scb->have_error = true;
return 0; goto good;
} }
type = ntohl(xdr->type); type = ntohl(xdr->type);
...@@ -220,13 +221,16 @@ static int xdr_decode_YFSFetchStatus(const __be32 **_bp, ...@@ -220,13 +221,16 @@ static int xdr_decode_YFSFetchStatus(const __be32 **_bp,
status->size = xdr_to_u64(xdr->size); status->size = xdr_to_u64(xdr->size);
status->data_version = xdr_to_u64(xdr->data_version); status->data_version = xdr_to_u64(xdr->data_version);
scb->have_status = true; scb->have_status = true;
good:
ret = 0;
advance:
*_bp += xdr_size(xdr); *_bp += xdr_size(xdr);
return 0; return ret;
bad: bad:
xdr_dump_bad(*_bp); xdr_dump_bad(*_bp);
return afs_protocol_error(call, -EBADMSG, afs_eproto_bad_status); ret = afs_protocol_error(call, -EBADMSG, afs_eproto_bad_status);
goto advance;
} }
/* /*
...@@ -1153,11 +1157,9 @@ static int yfs_deliver_fs_rename(struct afs_call *call) ...@@ -1153,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