Commit 3abc3ae5 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag '9p-for-5.19-rc4' of https://github.com/martinetd/linux

Pull 9pfs fixes from Dominique Martinet:
 "A couple of fid refcount and fscache fixes:

   - fid refcounting was incorrect in some corner cases and would leak
     resources, only freed at umount time. The first three commits fix
     three such cases

   - 'cache=loose' or fscache was broken when trying to write a partial
     page to a file with no read permission since the rework a few
     releases ago.

     The fix taken here is just to restore old behavior of using the
     special 'writeback_fid' for such reads, which is open as root/RDWR
     and such not get complains that we try to read on a WRONLY fid.

     Long-term it'd be nice to get rid of this and not issue the read at
     all (skip cache?) in such cases, but that direction hasn't
     progressed"

* tag '9p-for-5.19-rc4' of https://github.com/martinetd/linux:
  9p: fix EBADF errors in cached mode
  9p: Fix refcounting during full path walks for fid lookups
  9p: fix fid refcount leak in v9fs_vfs_get_link
  9p: fix fid refcount leak in v9fs_vfs_atomic_open_dotl
parents ca1fdab7 b0017602
...@@ -152,7 +152,7 @@ static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry, ...@@ -152,7 +152,7 @@ static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry,
const unsigned char **wnames, *uname; const unsigned char **wnames, *uname;
int i, n, l, clone, access; int i, n, l, clone, access;
struct v9fs_session_info *v9ses; struct v9fs_session_info *v9ses;
struct p9_fid *fid, *old_fid = NULL; struct p9_fid *fid, *old_fid;
v9ses = v9fs_dentry2v9ses(dentry); v9ses = v9fs_dentry2v9ses(dentry);
access = v9ses->flags & V9FS_ACCESS_MASK; access = v9ses->flags & V9FS_ACCESS_MASK;
...@@ -194,13 +194,12 @@ static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry, ...@@ -194,13 +194,12 @@ static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry,
if (IS_ERR(fid)) if (IS_ERR(fid))
return fid; return fid;
refcount_inc(&fid->count);
v9fs_fid_add(dentry->d_sb->s_root, fid); v9fs_fid_add(dentry->d_sb->s_root, fid);
} }
/* If we are root ourself just return that */ /* If we are root ourself just return that */
if (dentry->d_sb->s_root == dentry) { if (dentry->d_sb->s_root == dentry)
refcount_inc(&fid->count);
return fid; return fid;
}
/* /*
* Do a multipath walk with attached root. * Do a multipath walk with attached root.
* When walking parent we need to make sure we * When walking parent we need to make sure we
...@@ -212,6 +211,7 @@ static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry, ...@@ -212,6 +211,7 @@ static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry,
fid = ERR_PTR(n); fid = ERR_PTR(n);
goto err_out; goto err_out;
} }
old_fid = fid;
clone = 1; clone = 1;
i = 0; i = 0;
while (i < n) { while (i < n) {
...@@ -221,19 +221,15 @@ static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry, ...@@ -221,19 +221,15 @@ static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry,
* walk to ensure none of the patch component change * walk to ensure none of the patch component change
*/ */
fid = p9_client_walk(fid, l, &wnames[i], clone); fid = p9_client_walk(fid, l, &wnames[i], clone);
if (IS_ERR(fid)) { /* non-cloning walk will return the same fid */
if (old_fid) { if (fid != old_fid) {
/*
* If we fail, clunk fid which are mapping
* to path component and not the last component
* of the path.
*/
p9_client_clunk(old_fid); p9_client_clunk(old_fid);
old_fid = fid;
} }
if (IS_ERR(fid)) {
kfree(wnames); kfree(wnames);
goto err_out; goto err_out;
} }
old_fid = fid;
i += l; i += l;
clone = 0; clone = 0;
} }
......
...@@ -58,8 +58,21 @@ static void v9fs_issue_read(struct netfs_io_subrequest *subreq) ...@@ -58,8 +58,21 @@ static void v9fs_issue_read(struct netfs_io_subrequest *subreq)
*/ */
static int v9fs_init_request(struct netfs_io_request *rreq, struct file *file) static int v9fs_init_request(struct netfs_io_request *rreq, struct file *file)
{ {
struct inode *inode = file_inode(file);
struct v9fs_inode *v9inode = V9FS_I(inode);
struct p9_fid *fid = file->private_data; struct p9_fid *fid = file->private_data;
BUG_ON(!fid);
/* we might need to read from a fid that was opened write-only
* for read-modify-write of page cache, use the writeback fid
* for that */
if (rreq->origin == NETFS_READ_FOR_WRITE &&
(fid->mode & O_ACCMODE) == O_WRONLY) {
fid = v9inode->writeback_fid;
BUG_ON(!fid);
}
refcount_inc(&fid->count); refcount_inc(&fid->count);
rreq->netfs_priv = fid; rreq->netfs_priv = fid;
return 0; return 0;
......
...@@ -1251,15 +1251,15 @@ static const char *v9fs_vfs_get_link(struct dentry *dentry, ...@@ -1251,15 +1251,15 @@ static const char *v9fs_vfs_get_link(struct dentry *dentry,
return ERR_PTR(-ECHILD); return ERR_PTR(-ECHILD);
v9ses = v9fs_dentry2v9ses(dentry); v9ses = v9fs_dentry2v9ses(dentry);
fid = v9fs_fid_lookup(dentry); if (!v9fs_proto_dotu(v9ses))
return ERR_PTR(-EBADF);
p9_debug(P9_DEBUG_VFS, "%pd\n", dentry); p9_debug(P9_DEBUG_VFS, "%pd\n", dentry);
fid = v9fs_fid_lookup(dentry);
if (IS_ERR(fid)) if (IS_ERR(fid))
return ERR_CAST(fid); return ERR_CAST(fid);
if (!v9fs_proto_dotu(v9ses))
return ERR_PTR(-EBADF);
st = p9_client_stat(fid); st = p9_client_stat(fid);
p9_client_clunk(fid); p9_client_clunk(fid);
if (IS_ERR(st)) if (IS_ERR(st))
......
...@@ -274,6 +274,7 @@ v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry, ...@@ -274,6 +274,7 @@ v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry,
if (IS_ERR(ofid)) { if (IS_ERR(ofid)) {
err = PTR_ERR(ofid); err = PTR_ERR(ofid);
p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err); p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
p9_client_clunk(dfid);
goto out; goto out;
} }
...@@ -285,6 +286,7 @@ v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry, ...@@ -285,6 +286,7 @@ v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry,
if (err) { if (err) {
p9_debug(P9_DEBUG_VFS, "Failed to get acl values in creat %d\n", p9_debug(P9_DEBUG_VFS, "Failed to get acl values in creat %d\n",
err); err);
p9_client_clunk(dfid);
goto error; goto error;
} }
err = p9_client_create_dotl(ofid, name, v9fs_open_to_dotl_flags(flags), err = p9_client_create_dotl(ofid, name, v9fs_open_to_dotl_flags(flags),
...@@ -292,6 +294,7 @@ v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry, ...@@ -292,6 +294,7 @@ v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry,
if (err < 0) { if (err < 0) {
p9_debug(P9_DEBUG_VFS, "p9_client_open_dotl failed in creat %d\n", p9_debug(P9_DEBUG_VFS, "p9_client_open_dotl failed in creat %d\n",
err); err);
p9_client_clunk(dfid);
goto error; goto error;
} }
v9fs_invalidate_inode_attr(dir); v9fs_invalidate_inode_attr(dir);
......
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