afs: Fix unlink to handle YFS.RemoveFile2 better
authorDavid Howells <dhowells@redhat.com>
Tue, 14 May 2019 11:29:11 +0000 (12:29 +0100)
committerDavid Howells <dhowells@redhat.com>
Thu, 16 May 2019 21:23:21 +0000 (22:23 +0100)
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: 30062bd13e36 ("afs: Implement YFS support in the fs client")
Signed-off-by: David Howells <dhowells@redhat.com>
fs/afs/afs.h
fs/afs/dir.c
fs/afs/fsclient.c
fs/afs/inode.c
fs/afs/internal.h
fs/afs/yfsclient.c

index 819678bd8becc9601c38881c67b54b3c4acc9bb1..a7d3f902a91cc2bbf2bfbf6426c6cfcc8f65971b 100644 (file)
@@ -150,7 +150,9 @@ struct afs_file_status {
 struct afs_status_cb {
        struct afs_file_status  status;
        struct afs_callback     callback;
+       bool                    have_status;    /* True if status record was retrieved */
        bool                    have_cb;        /* True if cb record was retrieved */
+       bool                    have_error;     /* True if status.abort_code indicates an error */
 };
 
 /*
index 338c2260b0a05bab52186d2d352686df06d16b1d..d1b3736a3bbded2a93281ca4f47e5e6fd9fd42be 100644 (file)
@@ -1299,32 +1299,27 @@ error:
  * 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...
  */
-int afs_dir_remove_link(struct dentry *dentry, struct key *key,
-                       unsigned long d_version_before,
-                       unsigned long d_version_after)
+static int afs_dir_remove_link(struct afs_vnode *dvnode, struct dentry *dentry,
+                              struct key *key)
 {
-       bool dir_valid;
        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)) {
                struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry));
 
                if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
                        /* 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);
                        if (vnode->vfs_inode.i_nlink == 0) {
                                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;
                } else {
-                       clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
+                       afs_break_callback(vnode);
 
                        if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
                                kdebug("AFS_VNODE_DELETED");
@@ -1348,7 +1343,6 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
        struct afs_status_cb *scb;
        struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode = NULL;
        struct key *key;
-       unsigned long d_version = (unsigned long)dentry->d_fsdata;
        bool need_rehash = false;
        int ret;
 
@@ -1395,20 +1389,16 @@ static int afs_unlink(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;
+               afs_dataversion_t data_version_2 = vnode->status.data_version;
 
                while (afs_select_fileserver(&fc)) {
                        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) &&
                            !test_bit(AFS_SERVER_FL_NO_RM2, &fc.cbi->server->flags)) {
                                yfs_fs_remove_file2(&fc, vnode, dentry->d_name.name,
                                                    &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 ||
                                    fc.ac.abort_code != RXGEN_OPCODE)
                                        continue;
@@ -1420,11 +1410,11 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
 
                afs_vnode_commit_status(&fc, dvnode, fc.cb_break,
                                        &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);
-               if (ret == 0)
-                       ret = afs_dir_remove_link(
-                               dentry, key, d_version,
-                               (unsigned long)dvnode->status.data_version);
+               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,
index 89b684c957b9917e6c84f4c1d9f3833e541d9561..48298408d6ac7a944285a0f273e23b4925f8def4 100644 (file)
@@ -83,6 +83,7 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp,
                         * case.
                         */
                        status->abort_code = abort_code;
+                       scb->have_error = true;
                        return 0;
                }
 
@@ -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_hi) << 32;
        status->data_version = data_version;
+       scb->have_status = true;
 
        *_bp = (const void *)*_bp + sizeof(*xdr);
        return 0;
index 37c5de793353c2baf8242ad663b53159d54842f2..e1a523d2e378bd5e60a6b7ff5062c16acdad569d 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/namei.h>
 #include <linux/iversion.h>
 #include "internal.h"
+#include "afs_fs.h"
 
 static const struct inode_operations afs_symlink_inode_operations = {
        .get_link       = page_get_link,
@@ -271,13 +272,22 @@ void afs_vnode_commit_status(struct afs_fs_cursor *fc,
 
        write_seqlock(&vnode->cb_lock);
 
-       afs_apply_status(fc, vnode, scb, expected_version);
-       if (scb->have_cb)
-               afs_apply_callback(fc, vnode, scb, cb_break);
+       if (scb->have_error) {
+               if (scb->status.abort_code == VNOVNODE) {
+                       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);
 
-       if (fc->ac.error == 0)
+       if (fc->ac.error == 0 && scb->have_status)
                afs_cache_permit(vnode, fc->key, cb_break, scb);
 }
 
index 3dbb1e840dfdc27c2a58eea48b886132cf65cf16..f80ca638e70ffd84d7d13b759d977be9ca18ef42 100644 (file)
@@ -904,7 +904,6 @@ extern const struct address_space_operations afs_dir_aops;
 extern const struct dentry_operations afs_fs_dentry_operations;
 
 extern void afs_d_release(struct dentry *);
-extern int afs_dir_remove_link(struct dentry *, struct key *, unsigned long, unsigned long);
 
 /*
  * dir_edit.c
index c8f71fc9920b1be4fa55c9d4bc25788c22f7bf8d..10de675dc6fcaa0839a997bc1636434787cde0ee 100644 (file)
@@ -195,6 +195,7 @@ static int xdr_decode_YFSFetchStatus(const __be32 **_bp,
        if (status->abort_code != 0) {
                if (status->abort_code == VNOVNODE)
                        status->nlink = 0;
+               scb->have_error = true;
                return 0;
        }
 
@@ -222,6 +223,7 @@ static int xdr_decode_YFSFetchStatus(const __be32 **_bp,
        status->mtime_server    = xdr_to_time(xdr->mtime_server);
        status->size            = xdr_to_u64(xdr->size);
        status->data_version    = xdr_to_u64(xdr->data_version);
+       scb->have_status        = true;
 
        *_bp += xdr_size(xdr);
        return 0;