diff options
Diffstat (limited to 'fs/erofs/ishare.c')
| -rw-r--r-- | fs/erofs/ishare.c | 144 |
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) |
