diff --git a/fs/erofs/ishare.c b/fs/erofs/ishare.c index db32d16517a5..ea146fa8a27c 100644 --- a/fs/erofs/ishare.c +++ b/fs/erofs/ishare.c @@ -30,9 +30,18 @@ static int erofs_ishare_iget5_set(struct inode *inode, void *data) return 0; } +static int erofs_ishare_backing_release(struct inode *inode, struct file *file) +{ + iput(file->private_data); + file->private_data = NULL; + return 0; +} + bool erofs_ishare_fill_inode(struct inode *inode) { - static const struct file_operations empty_fops = {}; + static const struct file_operations backing_fops = { + .release = erofs_ishare_backing_release, + }; struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb); const struct address_space_operations *aops; struct erofs_inode *vi = EROFS_I(inode); @@ -50,7 +59,7 @@ 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_fop = &backing_fops; si->i_mapping->a_ops = aops; si->i_mode = 0444 | S_IFREG; si->i_size = inode->i_size; @@ -102,16 +111,24 @@ static int erofs_ishare_file_open(struct inode *inode, struct file *file) .mnt = erofs_ishare_mnt, .dentry = EROFS_I(inode)->sharedentry, }; - struct file *rf; + struct file *sf; if (file->f_flags & O_DIRECT) return -EINVAL; - rf = backing_file_open(file, file->f_flags | O_NOATIME, + sf = backing_file_open(file, file->f_flags | O_NOATIME, &sharedpath, current_cred()); - if (IS_ERR(rf)) - return PTR_ERR(rf); - file->private_data = rf; + if (IS_ERR(sf)) + return PTR_ERR(sf); + /* + * A VMA only holds a reference to the shared file so that the original + * file may be released: the shared file must therefore hold a reference + * to one of the real inodes ("inode" passed here), and that reference + * will be released by erofs_ishare_backing_release(). + */ + ihold(inode); + sf->private_data = inode; + file->private_data = sf; return 0; } @@ -125,13 +142,13 @@ static int erofs_ishare_file_release(struct inode *inode, struct file *file) static ssize_t erofs_ishare_file_read_iter(struct kiocb *iocb, struct iov_iter *to) { - struct file *realfile = iocb->ki_filp->private_data; + struct file *sf = iocb->ki_filp->private_data; struct kiocb dedup_iocb; ssize_t nread; if (!iov_iter_count(to)) return 0; - kiocb_clone(&dedup_iocb, iocb, realfile); + kiocb_clone(&dedup_iocb, iocb, sf); nread = filemap_read(&dedup_iocb, to, 0); iocb->ki_pos = dedup_iocb.ki_pos; return nread; @@ -139,12 +156,12 @@ 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; + struct file *sf = file->private_data; int err; - vma_set_file(vma, realfile); + vma_set_file(vma, sf); - err = security_mmap_backing_file(vma, realfile, file); + err = security_mmap_backing_file(vma, sf, file); if (err) return err; @@ -178,7 +195,7 @@ const struct file_operations erofs_ishare_fops = { struct inode *erofs_real_inode(struct inode *inode, bool *need_iput) { struct erofs_inode *vi, *vi_share; - struct inode *realinode; + struct inode *realinode = NULL; *need_iput = false; if (inode->i_sb != erofs_ishare_mnt->mnt_sb)