Commit a38a7558 authored by David Howells's avatar David Howells

afs: Fix unlink to handle YFS.RemoveFile2 better

Make use of the status update for the target file that the YFS.RemoveFile2
RPC op returns to correctly update the vnode as to whether the file was
actually deleted or just had nlink reduced.

Fixes: 30062bd1 ("afs: Implement YFS support in the fs client")
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parent 61c347ba
...@@ -150,7 +150,9 @@ struct afs_file_status { ...@@ -150,7 +150,9 @@ struct afs_file_status {
struct afs_status_cb { struct afs_status_cb {
struct afs_file_status status; struct afs_file_status status;
struct afs_callback callback; struct afs_callback callback;
bool have_status; /* True if status record was retrieved */
bool have_cb; /* True if cb record was retrieved */ bool have_cb; /* True if cb record was retrieved */
bool have_error; /* True if status.abort_code indicates an error */
}; };
/* /*
......
...@@ -1299,32 +1299,27 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -1299,32 +1299,27 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
* However, if we didn't have a callback promise outstanding, or it was * However, if we didn't have a callback promise outstanding, or it was
* outstanding on a different server, then it won't break it either... * outstanding on a different server, then it won't break it either...
*/ */
int afs_dir_remove_link(struct dentry *dentry, struct key *key, static int afs_dir_remove_link(struct afs_vnode *dvnode, struct dentry *dentry,
unsigned long d_version_before, struct key *key)
unsigned long d_version_after)
{ {
bool dir_valid;
int ret = 0; int ret = 0;
/* There were no intervening changes on the server if the version
* number we got back was incremented by exactly 1.
*/
dir_valid = (d_version_after == d_version_before + 1);
if (d_really_is_positive(dentry)) { if (d_really_is_positive(dentry)) {
struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry)); struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry));
if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
/* Already done */ /* Already done */
} else if (dir_valid) { } else if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) {
write_seqlock(&vnode->cb_lock);
drop_nlink(&vnode->vfs_inode); drop_nlink(&vnode->vfs_inode);
if (vnode->vfs_inode.i_nlink == 0) { if (vnode->vfs_inode.i_nlink == 0) {
set_bit(AFS_VNODE_DELETED, &vnode->flags); set_bit(AFS_VNODE_DELETED, &vnode->flags);
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); __afs_break_callback(vnode);
} }
write_sequnlock(&vnode->cb_lock);
ret = 0; ret = 0;
} else { } else {
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); afs_break_callback(vnode);
if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
kdebug("AFS_VNODE_DELETED"); kdebug("AFS_VNODE_DELETED");
...@@ -1348,7 +1343,6 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -1348,7 +1343,6 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
struct afs_status_cb *scb; struct afs_status_cb *scb;
struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode = NULL; struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode = NULL;
struct key *key; struct key *key;
unsigned long d_version = (unsigned long)dentry->d_fsdata;
bool need_rehash = false; bool need_rehash = false;
int ret; int ret;
...@@ -1395,20 +1389,16 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -1395,20 +1389,16 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
ret = -ERESTARTSYS; ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, dvnode, key, true)) { if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
afs_dataversion_t data_version = dvnode->status.data_version + 1; afs_dataversion_t data_version = dvnode->status.data_version + 1;
afs_dataversion_t data_version_2 = vnode->status.data_version;
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);
fc.cb_break_2 = afs_calc_vnode_cb_break(vnode);
if (test_bit(AFS_SERVER_FL_IS_YFS, &fc.cbi->server->flags) && if (test_bit(AFS_SERVER_FL_IS_YFS, &fc.cbi->server->flags) &&
!test_bit(AFS_SERVER_FL_NO_RM2, &fc.cbi->server->flags)) { !test_bit(AFS_SERVER_FL_NO_RM2, &fc.cbi->server->flags)) {
yfs_fs_remove_file2(&fc, vnode, dentry->d_name.name, yfs_fs_remove_file2(&fc, vnode, dentry->d_name.name,
&scb[0], &scb[1]); &scb[0], &scb[1]);
if (fc.ac.error == 0 &&
scb[1].status.abort_code == VNOVNODE) {
set_bit(AFS_VNODE_DELETED, &vnode->flags);
afs_break_callback(vnode);
}
if (fc.ac.error != -ECONNABORTED || if (fc.ac.error != -ECONNABORTED ||
fc.ac.abort_code != RXGEN_OPCODE) fc.ac.abort_code != RXGEN_OPCODE)
continue; continue;
...@@ -1420,11 +1410,11 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -1420,11 +1410,11 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
afs_vnode_commit_status(&fc, dvnode, fc.cb_break, afs_vnode_commit_status(&fc, dvnode, fc.cb_break,
&data_version, &scb[0]); &data_version, &scb[0]);
afs_vnode_commit_status(&fc, vnode, fc.cb_break_2,
&data_version_2, &scb[1]);
ret = afs_end_vnode_operation(&fc); ret = afs_end_vnode_operation(&fc);
if (ret == 0) if (ret == 0 && !(scb[1].have_status || scb[1].have_error))
ret = afs_dir_remove_link( ret = afs_dir_remove_link(dvnode, dentry, key);
dentry, key, d_version,
(unsigned long)dvnode->status.data_version);
if (ret == 0 && if (ret == 0 &&
test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
afs_edit_dir_remove(dvnode, &dentry->d_name, afs_edit_dir_remove(dvnode, &dentry->d_name,
......
...@@ -83,6 +83,7 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp, ...@@ -83,6 +83,7 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp,
* case. * case.
*/ */
status->abort_code = abort_code; status->abort_code = abort_code;
scb->have_error = true;
return 0; return 0;
} }
...@@ -127,6 +128,7 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp, ...@@ -127,6 +128,7 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp,
data_version = (u64)ntohl(xdr->data_version_lo); data_version = (u64)ntohl(xdr->data_version_lo);
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;
*_bp = (const void *)*_bp + sizeof(*xdr); *_bp = (const void *)*_bp + sizeof(*xdr);
return 0; return 0;
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/iversion.h> #include <linux/iversion.h>
#include "internal.h" #include "internal.h"
#include "afs_fs.h"
static const struct inode_operations afs_symlink_inode_operations = { static const struct inode_operations afs_symlink_inode_operations = {
.get_link = page_get_link, .get_link = page_get_link,
...@@ -271,13 +272,22 @@ void afs_vnode_commit_status(struct afs_fs_cursor *fc, ...@@ -271,13 +272,22 @@ void afs_vnode_commit_status(struct afs_fs_cursor *fc,
write_seqlock(&vnode->cb_lock); write_seqlock(&vnode->cb_lock);
afs_apply_status(fc, vnode, scb, expected_version); if (scb->have_error) {
if (scb->have_cb) if (scb->status.abort_code == VNOVNODE) {
afs_apply_callback(fc, vnode, scb, cb_break); set_bit(AFS_VNODE_DELETED, &vnode->flags);
clear_nlink(&vnode->vfs_inode);
__afs_break_callback(vnode);
}
} else {
if (scb->have_status)
afs_apply_status(fc, vnode, scb, expected_version);
if (scb->have_cb)
afs_apply_callback(fc, vnode, scb, cb_break);
}
write_sequnlock(&vnode->cb_lock); write_sequnlock(&vnode->cb_lock);
if (fc->ac.error == 0) if (fc->ac.error == 0 && scb->have_status)
afs_cache_permit(vnode, fc->key, cb_break, scb); afs_cache_permit(vnode, fc->key, cb_break, scb);
} }
......
...@@ -904,7 +904,6 @@ extern const struct address_space_operations afs_dir_aops; ...@@ -904,7 +904,6 @@ extern const struct address_space_operations afs_dir_aops;
extern const struct dentry_operations afs_fs_dentry_operations; extern const struct dentry_operations afs_fs_dentry_operations;
extern void afs_d_release(struct dentry *); extern void afs_d_release(struct dentry *);
extern int afs_dir_remove_link(struct dentry *, struct key *, unsigned long, unsigned long);
/* /*
* dir_edit.c * dir_edit.c
......
...@@ -195,6 +195,7 @@ static int xdr_decode_YFSFetchStatus(const __be32 **_bp, ...@@ -195,6 +195,7 @@ static int xdr_decode_YFSFetchStatus(const __be32 **_bp,
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;
return 0; return 0;
} }
...@@ -222,6 +223,7 @@ static int xdr_decode_YFSFetchStatus(const __be32 **_bp, ...@@ -222,6 +223,7 @@ static int xdr_decode_YFSFetchStatus(const __be32 **_bp,
status->mtime_server = xdr_to_time(xdr->mtime_server); status->mtime_server = xdr_to_time(xdr->mtime_server);
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;
*_bp += xdr_size(xdr); *_bp += xdr_size(xdr);
return 0; return 0;
......
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