summaryrefslogtreecommitdiff
path: root/fs/erofs/ishare.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/erofs/ishare.c')
-rw-r--r--fs/erofs/ishare.c144
1 files changed, 78 insertions, 66 deletions
diff --git a/fs/erofs/ishare.c b/fs/erofs/ishare.c
index 829d50d5c717..a1cf2aff8d32 100644
--- a/fs/erofs/ishare.c
+++ b/fs/erofs/ishare.c
@@ -2,21 +2,15 @@
/*
* 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 inline bool erofs_is_ishare_inode(struct inode *inode)
-{
- /* assumed FS_ONDEMAND is excluded with FS_PAGE_CACHE_SHARE feature */
- return inode->i_sb->s_type == &erofs_anon_fs_type;
-}
-
static int erofs_ishare_iget5_eq(struct inode *inode, void *data)
{
struct erofs_inode_fingerprint *fp1 = &EROFS_I(inode)->fingerprint;
@@ -38,96 +32,90 @@ 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);
- struct erofs_inode *vi = EROFS_I(inode);
const struct address_space_operations *aops;
+ struct erofs_inode *vi = EROFS_I(inode);
struct erofs_inode_fingerprint fp;
- struct inode *sharedinode;
- unsigned long hash;
+ struct dentry *sd;
+ struct inode *si;
- aops = erofs_get_aops(inode, true);
+ aops = erofs_get_aops(inode);
if (IS_ERR(aops))
return false;
if (erofs_xattr_fill_inode_fingerprint(&fp, inode, sbi->domain_id))
return false;
- hash = xxh32(fp.opaque, fp.size, 0);
- sharedinode = iget5_locked(erofs_ishare_mnt->mnt_sb, hash,
- erofs_ishare_iget5_eq, erofs_ishare_iget5_set,
- &fp);
- if (!sharedinode) {
- kfree(fp.opaque);
- return false;
- }
- if (inode_state_read_once(sharedinode) & I_NEW) {
- sharedinode->i_mapping->a_ops = aops;
- sharedinode->i_size = vi->vfs_inode.i_size;
- unlock_new_inode(sharedinode);
+ si = iget5_locked(erofs_ishare_mnt->mnt_sb,
+ 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;
+ mapping_set_large_folios(si->i_mapping);
+ unlock_new_inode(si);
} else {
kfree(fp.opaque);
- if (aops != sharedinode->i_mapping->a_ops) {
- iput(sharedinode);
+ if (!si || aops != si->i_mapping->a_ops) {
+ iput(si);
return false;
}
- if (sharedinode->i_size != vi->vfs_inode.i_size) {
- _erofs_printk(inode->i_sb, KERN_WARNING
- "size(%lld:%lld) not matches for the same fingerprint\n",
- vi->vfs_inode.i_size, sharedinode->i_size);
- iput(sharedinode);
+ if (si->i_size != inode->i_size) {
+ erofs_warn(inode->i_sb, "i_size mismatch (%lld != %lld) for the same fingerprint",
+ inode->i_size, si->i_size);
+ iput(si);
return false;
}
}
- vi->sharedinode = sharedinode;
+ 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(sharedinode)->ishare_lock);
- list_add(&vi->ishare_list, &EROFS_I(sharedinode)->ishare_list);
- spin_unlock(&EROFS_I(sharedinode)->ishare_lock);
+ spin_lock(&EROFS_I(si)->ishare_lock);
+ list_add(&vi->ishare_list, &EROFS_I(si)->ishare_list);
+ spin_unlock(&EROFS_I(si)->ishare_lock);
return true;
}
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());
- 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;
}
@@ -150,11 +138,24 @@ static ssize_t erofs_ishare_file_read_iter(struct kiocb *iocb,
static int erofs_ishare_mmap(struct file *file, struct vm_area_struct *vma)
{
struct file *realfile = file->private_data;
+ int err;
vma_set_file(vma, realfile);
+
+ err = security_mmap_backing_file(vma, realfile, file);
+ if (err)
+ return err;
+
return generic_file_readonly_mmap(file, vma);
}
+static ssize_t erofs_ishare_splice_read(struct file *in, loff_t *ppos,
+ struct pipe_inode_info *pipe,
+ size_t len, unsigned int flags)
+{
+ return filemap_splice_read(in->private_data, ppos, pipe, len, flags);
+}
+
static int erofs_ishare_fadvise(struct file *file, loff_t offset,
loff_t len, int advice)
{
@@ -163,12 +164,12 @@ static int erofs_ishare_fadvise(struct file *file, loff_t offset,
const struct file_operations erofs_ishare_fops = {
.open = erofs_ishare_file_open,
- .llseek = generic_file_llseek,
+ .llseek = erofs_file_llseek,
.read_iter = erofs_ishare_file_read_iter,
.mmap = erofs_ishare_mmap,
.release = erofs_ishare_file_release,
.get_unmapped_area = thp_get_unmapped_area,
- .splice_read = filemap_splice_read,
+ .splice_read = erofs_ishare_splice_read,
.fadvise = erofs_ishare_fadvise,
};
@@ -178,7 +179,7 @@ struct inode *erofs_real_inode(struct inode *inode, bool *need_iput)
struct inode *realinode;
*need_iput = false;
- if (!erofs_is_ishare_inode(inode))
+ if (inode->i_sb != erofs_ishare_mnt->mnt_sb)
return inode;
vi_share = EROFS_I(inode);
@@ -200,8 +201,19 @@ struct inode *erofs_real_inode(struct inode *inode, bool *need_iput)
int __init erofs_init_ishare(void)
{
- erofs_ishare_mnt = kern_mount(&erofs_anon_fs_type);
- return PTR_ERR_OR_ZERO(erofs_ishare_mnt);
+ struct vfsmount *mnt;
+ int ret;
+
+ mnt = kern_mount(&erofs_anon_fs_type);
+ if (IS_ERR(mnt))
+ return PTR_ERR(mnt);
+ /* generic_fadvise() doesn't work if s_bdi == &noop_backing_dev_info */
+ ret = super_setup_bdi(mnt->mnt_sb);
+ if (ret)
+ kern_unmount(mnt);
+ else
+ erofs_ishare_mnt = mnt;
+ return ret;
}
void erofs_exit_ishare(void)