9p-for-5.19-rc4: fid refcount and fscache fixes

This contains a couple of 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
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE/IPbcYBuWt0zoYhOq06b7GqY5nAFAmKynEUACgkQq06b7GqY
 5nBOwQ//c1AoCuzt8gXefaBy9dDvaq/Cg5a339bUGmsvRJS8dHWTx2/HO7ncf3wE
 59uRh+ipLxXmHTkkLz13JtaVAFQ2HYlxKwmyvakBIjGVgDC+IYm9vkPHb2Z2yIBY
 D6XTuNufnb+/lrqekrmHiT2+eJOi2MhxPNyjXUAML7KKny6LpzdwymF/KIEsCbR8
 EbRrSf+KTnCssIfJlrZUbbk2UkbW18uG/V1MgThN3rgj+bgG/oB+lU6BELCIOQc2
 +0io2dg+ZgfJIK2fpBKF64vK2ILMSNEJ8obkfWgqOyI/LBOya38Z/cSbuzPMBwZd
 P2A2zQmjp8oYSbXM8EGaSFTXix28Lxljk5vvT/xbEipzyUU3UZAPJJE6UX9M66UF
 d/FHA8ljDVuRrknM0yDv5sqBYRB8uuEBtUiKGBO6k5zPTn0A7oEzEviryMCiEUF5
 1fbe/PWrFLnZMB2hWZ1aiY0tyopivp67zo6mRY/qehCihb/QlpiVNLGCC1e3eMdu
 FHPR3pSD1B5jFurOB8Wn1zUMjsZsnIjvpOET4WiP9pU9SJpOCd2fsAo69POHZVfA
 NIJxZ9MqW+3/eK+7CDmwnJLhTNRvvrQmTH55Ex61HTcn+2KFIqizCr/I6sQUl/g0
 teAB8T5UlS6+nDDWfZouUiXcm0He2C56RyJOCYlagHD1qYm//Gg=
 =2yZw
 -----END PGP SIGNATURE-----

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
This commit is contained in:
Linus Torvalds 2022-06-22 08:09:49 -05:00
commit 3abc3ae553
4 changed files with 29 additions and 17 deletions

View File

@ -152,7 +152,7 @@ static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry,
const unsigned char **wnames, *uname;
int i, n, l, clone, access;
struct v9fs_session_info *v9ses;
struct p9_fid *fid, *old_fid = NULL;
struct p9_fid *fid, *old_fid;
v9ses = v9fs_dentry2v9ses(dentry);
access = v9ses->flags & V9FS_ACCESS_MASK;
@ -194,13 +194,12 @@ static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry,
if (IS_ERR(fid))
return fid;
refcount_inc(&fid->count);
v9fs_fid_add(dentry->d_sb->s_root, fid);
}
/* If we are root ourself just return that */
if (dentry->d_sb->s_root == dentry) {
refcount_inc(&fid->count);
if (dentry->d_sb->s_root == dentry)
return fid;
}
/*
* Do a multipath walk with attached root.
* 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,
fid = ERR_PTR(n);
goto err_out;
}
old_fid = fid;
clone = 1;
i = 0;
while (i < n) {
@ -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
*/
fid = p9_client_walk(fid, l, &wnames[i], clone);
/* non-cloning walk will return the same fid */
if (fid != old_fid) {
p9_client_clunk(old_fid);
old_fid = fid;
}
if (IS_ERR(fid)) {
if (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);
}
kfree(wnames);
goto err_out;
}
old_fid = fid;
i += l;
clone = 0;
}

View File

@ -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)
{
struct inode *inode = file_inode(file);
struct v9fs_inode *v9inode = V9FS_I(inode);
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);
rreq->netfs_priv = fid;
return 0;

View File

@ -1251,15 +1251,15 @@ static const char *v9fs_vfs_get_link(struct dentry *dentry,
return ERR_PTR(-ECHILD);
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);
fid = v9fs_fid_lookup(dentry);
if (IS_ERR(fid))
return ERR_CAST(fid);
if (!v9fs_proto_dotu(v9ses))
return ERR_PTR(-EBADF);
st = p9_client_stat(fid);
p9_client_clunk(fid);
if (IS_ERR(st))

View File

@ -274,6 +274,7 @@ v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry,
if (IS_ERR(ofid)) {
err = PTR_ERR(ofid);
p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
p9_client_clunk(dfid);
goto out;
}
@ -285,6 +286,7 @@ v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry,
if (err) {
p9_debug(P9_DEBUG_VFS, "Failed to get acl values in creat %d\n",
err);
p9_client_clunk(dfid);
goto error;
}
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,
if (err < 0) {
p9_debug(P9_DEBUG_VFS, "p9_client_open_dotl failed in creat %d\n",
err);
p9_client_clunk(dfid);
goto error;
}
v9fs_invalidate_inode_attr(dir);