]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
fs/9p: fix create-unlink-getattr idiom
authorEric Van Hensbergen <ericvh@gmail.com>
Wed, 23 Sep 2020 14:11:43 +0000 (22:11 +0800)
committerDominique Martinet <asmadeus@codewreck.org>
Tue, 3 Nov 2020 08:29:31 +0000 (09:29 +0100)
Fixes several outstanding bug reports of not being able to getattr from an
open file after an unlink.  This patch cleans up transient fids on an unlink
and will search open fids on a client if it detects a dentry that appears to
have been unlinked.  This search is necessary because fstat does not pass fd
information through the VFS API to the filesystem, only the dentry which for
9p has an imperfect match to fids.

Inherent in this patch is also a fix for the qid handling on create/open
which apparently wasn't being set correctly and was necessary for the search
to succeed.

A possible optimization over this fix is to include accounting of open fids
with the inode in the private data (in a similar fashion to the way we track
transient fids with dentries).  This would allow a much quicker search for
a matching open fid.

(changed v9fs_fid_find_global to v9fs_fid_find_inode in comment)

Link: http://lkml.kernel.org/r/20200923141146.90046-2-jianyong.wu@arm.com
Signed-off-by: Eric Van Hensbergen <ericvh@gmail.com>
Signed-off-by: Greg Kurz <gkurz@linux.vnet.ibm.com>
Signed-off-by: Jianyong Wu <jianyong.wu@arm.com>
Signed-off-by: Dominique Martinet <asmadeus@codewreck.org>
fs/9p/fid.c
fs/9p/vfs_inode.c
net/9p/client.c

index 3d681a2c27316f6dc8dcf433c015b8f78707ba9d..3304984c0fad70e87ee22a70b7c320274c3c7bb7 100644 (file)
@@ -38,6 +38,33 @@ void v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid)
        spin_unlock(&dentry->d_lock);
 }
 
+/**
+ * v9fs_fid_find_inode - search for a fid off of the client list
+ * @inode: return a fid pointing to a specific inode
+ * @uid: return a fid belonging to the specified user
+ *
+ */
+
+static struct p9_fid *v9fs_fid_find_inode(struct inode *inode, kuid_t uid)
+{
+       struct p9_client *clnt = v9fs_inode2v9ses(inode)->clnt;
+       struct p9_fid *fid, *fidptr, *ret = NULL;
+       unsigned long flags;
+
+       p9_debug(P9_DEBUG_VFS, " inode: %p\n", inode);
+
+       spin_lock_irqsave(&clnt->lock, flags);
+       list_for_each_entry_safe(fid, fidptr, &clnt->fidlist, flist) {
+               if (uid_eq(fid->uid, uid) &&
+                  (inode->i_ino == v9fs_qid2ino(&fid->qid))) {
+                       ret = fid;
+                       break;
+               }
+       }
+       spin_unlock_irqrestore(&clnt->lock, flags);
+       return ret;
+}
+
 /**
  * v9fs_fid_find - retrieve a fid that belongs to the specified uid
  * @dentry: dentry to look for fid in
@@ -65,6 +92,9 @@ static struct p9_fid *v9fs_fid_find(struct dentry *dentry, kuid_t uid, int any)
                        }
                }
                spin_unlock(&dentry->d_lock);
+       } else {
+               if (dentry->d_inode)
+                       ret = v9fs_fid_find_inode(dentry->d_inode, uid);
        }
 
        return ret;
index ae0c38ad1fcbeea4cc1fcb86fff5b27c40295847..31c2fddabb82abcb2b693016a935aa8b49aec7ed 100644 (file)
@@ -570,6 +570,10 @@ static int v9fs_remove(struct inode *dir, struct dentry *dentry, int flags)
 
                v9fs_invalidate_inode_attr(inode);
                v9fs_invalidate_inode_attr(dir);
+
+               /* invalidate all fids associated with dentry */
+               /* NOTE: This will not include open fids */
+               dentry->d_op->d_release(dentry);
        }
        return retval;
 }
index 09f1ec589b80ba4e7c487864196b1eb2eca33692..1a3f72bf45fc1f267c82456a5c6e9bc8e2dd7d5d 100644 (file)
@@ -1219,7 +1219,7 @@ struct p9_fid *p9_client_walk(struct p9_fid *oldfid, uint16_t nwname,
        if (nwname)
                memmove(&fid->qid, &wqids[nwqids - 1], sizeof(struct p9_qid));
        else
-               fid->qid = oldfid->qid;
+               memmove(&fid->qid, &oldfid->qid, sizeof(struct p9_qid));
 
        kfree(wqids);
        return fid;
@@ -1272,6 +1272,7 @@ int p9_client_open(struct p9_fid *fid, int mode)
                p9_is_proto_dotl(clnt) ? "RLOPEN" : "ROPEN",  qid.type,
                (unsigned long long)qid.path, qid.version, iounit);
 
+       memmove(&fid->qid, &qid, sizeof(struct p9_qid));
        fid->mode = mode;
        fid->iounit = iounit;
 
@@ -1317,6 +1318,7 @@ int p9_client_create_dotl(struct p9_fid *ofid, const char *name, u32 flags, u32
                        (unsigned long long)qid->path,
                        qid->version, iounit);
 
+       memmove(&ofid->qid, qid, sizeof(struct p9_qid));
        ofid->mode = mode;
        ofid->iounit = iounit;
 
@@ -1362,6 +1364,7 @@ int p9_client_fcreate(struct p9_fid *fid, const char *name, u32 perm, int mode,
                                (unsigned long long)qid.path,
                                qid.version, iounit);
 
+       memmove(&fid->qid, &qid, sizeof(struct p9_qid));
        fid->mode = mode;
        fid->iounit = iounit;