mirror of
https://github.com/torvalds/linux.git
synced 2026-07-29 18:51:21 +02:00
erofs: ensure valid f_path for page cache sharing
Previously, backing files for page cache sharing were set up with
f_path left as NULL (only f_inode was valid). It worked, but a recent
mincore fix relies on f_path.mnt and crashes (found by "erofs/028" on
7.2-rc4):
BUG: kernel NULL pointer dereference, address: 0000000000000018
#PF: supervisor read access in kernel mode
#PF: error_code(0x0000) - not-present page
PGD 0 P4D 0
Oops: Oops: 0000 [#1] SMP PTI
CPU: 3 UID: 0 PID: 675528 Comm: fincore Not tainted 7.2.0-rc4-00002-g[]-dirty #1 PREEMPT(lazy)
Hardware name: Red Hat KVM, BIOS 1.16.0-4.al8 04/01/2014
RIP: 0010:__do_sys_mincore+0xc0/0x2c0
...
Specify valid paths using valid disconnected dentries together with
erofs_ishare_mnt instead of leaving f_path empty, so they are more
like real backing files in a pseudo filesystem and standard
backing_file_open() can be used directly.
Fixes: e187bc02f8 ("mm: do file ownership checks with the proper mount idmap")
Acked-by: Hongbo Li <hongbohbli@tencent.com>
Signed-off-by: Gao Xiang <xiang@kernel.org>
This commit is contained in:
parent
97ab5aa517
commit
96b2dbbe58
|
|
@ -189,6 +189,7 @@ config EROFS_FS_PCPU_KTHREAD_HIPRI
|
|||
config EROFS_FS_PAGE_CACHE_SHARE
|
||||
bool "EROFS page cache share support (experimental)"
|
||||
depends on EROFS_FS && EROFS_FS_XATTR
|
||||
select FS_STACK
|
||||
help
|
||||
This enables page cache sharing among inodes with identical
|
||||
content fingerprints on the same machine.
|
||||
|
|
|
|||
|
|
@ -288,8 +288,8 @@ struct erofs_inode {
|
|||
struct erofs_inode_fingerprint fingerprint;
|
||||
spinlock_t ishare_lock;
|
||||
};
|
||||
/* for each real inode */
|
||||
struct inode *sharedinode;
|
||||
/* for each real filesystem inode */
|
||||
struct dentry *sharedentry;
|
||||
};
|
||||
#endif
|
||||
/* the corresponding vfs inode */
|
||||
|
|
|
|||
|
|
@ -2,14 +2,13 @@
|
|||
/*
|
||||
* Copyright (C) 2024, Alibaba Cloud
|
||||
*/
|
||||
#include <linux/backing-file.h>
|
||||
#include <linux/xxhash.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/security.h>
|
||||
#include "internal.h"
|
||||
#include "xattr.h"
|
||||
|
||||
#include "../internal.h"
|
||||
|
||||
static struct vfsmount *erofs_ishare_mnt;
|
||||
|
||||
static int erofs_ishare_iget5_eq(struct inode *inode, void *data)
|
||||
|
|
@ -33,10 +32,12 @@ static int erofs_ishare_iget5_set(struct inode *inode, void *data)
|
|||
|
||||
bool erofs_ishare_fill_inode(struct inode *inode)
|
||||
{
|
||||
static const struct file_operations empty_fops = {};
|
||||
struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb);
|
||||
const struct address_space_operations *aops;
|
||||
struct erofs_inode *vi = EROFS_I(inode);
|
||||
struct erofs_inode_fingerprint fp;
|
||||
struct dentry *sd;
|
||||
struct inode *si;
|
||||
|
||||
aops = erofs_get_aops(inode);
|
||||
|
|
@ -49,7 +50,9 @@ bool erofs_ishare_fill_inode(struct inode *inode)
|
|||
xxh32(fp.opaque, fp.size, 0),
|
||||
erofs_ishare_iget5_eq, erofs_ishare_iget5_set, &fp);
|
||||
if (si && (inode_state_read_once(si) & I_NEW)) {
|
||||
si->i_fop = &empty_fops;
|
||||
si->i_mapping->a_ops = aops;
|
||||
si->i_mode = 0444 | S_IFREG;
|
||||
si->i_size = inode->i_size;
|
||||
unlock_new_inode(si);
|
||||
} else {
|
||||
|
|
@ -65,7 +68,10 @@ bool erofs_ishare_fill_inode(struct inode *inode)
|
|||
return false;
|
||||
}
|
||||
}
|
||||
vi->sharedinode = si;
|
||||
sd = d_obtain_alias(si); /* disconnected denties for sharedinodes */
|
||||
if (IS_ERR(sd))
|
||||
return false;
|
||||
vi->sharedentry = sd;
|
||||
INIT_LIST_HEAD(&vi->ishare_list);
|
||||
spin_lock(&EROFS_I(si)->ishare_lock);
|
||||
list_add(&vi->ishare_list, &EROFS_I(si)->ishare_list);
|
||||
|
|
@ -75,48 +81,40 @@ bool erofs_ishare_fill_inode(struct inode *inode)
|
|||
|
||||
void erofs_ishare_free_inode(struct inode *inode)
|
||||
{
|
||||
struct erofs_inode *vi = EROFS_I(inode);
|
||||
struct inode *sharedinode = vi->sharedinode;
|
||||
struct erofs_inode *vi = EROFS_I(inode), *svi;
|
||||
|
||||
if (!sharedinode)
|
||||
if (!vi->sharedentry)
|
||||
return;
|
||||
spin_lock(&EROFS_I(sharedinode)->ishare_lock);
|
||||
svi = EROFS_I(d_inode(vi->sharedentry));
|
||||
spin_lock(&svi->ishare_lock);
|
||||
list_del(&vi->ishare_list);
|
||||
spin_unlock(&EROFS_I(sharedinode)->ishare_lock);
|
||||
iput(sharedinode);
|
||||
vi->sharedinode = NULL;
|
||||
spin_unlock(&svi->ishare_lock);
|
||||
dput(vi->sharedentry);
|
||||
vi->sharedentry = NULL;
|
||||
}
|
||||
|
||||
static int erofs_ishare_file_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct inode *sharedinode = EROFS_I(inode)->sharedinode;
|
||||
struct file *realfile;
|
||||
struct path sharedpath = {
|
||||
.mnt = erofs_ishare_mnt,
|
||||
.dentry = EROFS_I(inode)->sharedentry,
|
||||
};
|
||||
struct file *rf;
|
||||
|
||||
if (file->f_flags & O_DIRECT)
|
||||
return -EINVAL;
|
||||
realfile = alloc_empty_backing_file(O_RDONLY|O_NOATIME, current_cred(),
|
||||
file);
|
||||
if (IS_ERR(realfile))
|
||||
return PTR_ERR(realfile);
|
||||
ihold(sharedinode);
|
||||
realfile->f_op = &erofs_file_fops;
|
||||
realfile->f_inode = sharedinode;
|
||||
realfile->f_mapping = sharedinode->i_mapping;
|
||||
path_get(&file->f_path);
|
||||
backing_file_set_user_path(realfile, &file->f_path);
|
||||
|
||||
file_ra_state_init(&realfile->f_ra, file->f_mapping);
|
||||
realfile->private_data = EROFS_I(inode);
|
||||
file->private_data = realfile;
|
||||
rf = backing_file_open(file, file->f_flags | O_NOATIME,
|
||||
&sharedpath, current_cred());
|
||||
if (IS_ERR(rf))
|
||||
return PTR_ERR(rf);
|
||||
file->private_data = rf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int erofs_ishare_file_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct file *realfile = file->private_data;
|
||||
|
||||
iput(realfile->f_inode);
|
||||
fput(realfile);
|
||||
fput(file->private_data);
|
||||
file->private_data = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user