diff --git a/fs/erofs/fileio.c b/fs/erofs/fileio.c index c36a90a1a4cb..e13293ec70de 100644 --- a/fs/erofs/fileio.c +++ b/fs/erofs/fileio.c @@ -142,17 +142,29 @@ int erofs_read_meta_folio(struct file *file, struct folio *folio) .dev = { .m_pa = folio_pos(folio), }, }; struct inode *inode = folio_inode(folio); + size_t len = folio_size(folio); + loff_t pos, isize; int err; err = erofs_map_dev(inode->i_sb, &io.dev); if (err) return err; + pos = io.dev.m_dif->fsoff + io.dev.m_pa; + isize = i_size_read(file_inode(io.dev.m_dif->file)); + if (pos + folio_size(folio) > isize) { + len = isize > pos ? isize - pos : 0; + folio_zero_segment(folio, len, folio_size(folio)); + if (!len) { + folio_end_read(folio, true); + return 0; + } + } + io.rq = erofs_fileio_rq_alloc(inode->i_sb, io.dev.m_dif->file); io.rq->bio.bi_end_io = erofs_fileio_meta_endio; - io.rq->bio.bi_iter.bi_sector = - (io.dev.m_dif->fsoff + io.dev.m_pa) >> 9; - bio_add_folio_nofail(&io.rq->bio, folio, folio_size(folio), 0); + io.rq->bio.bi_iter.bi_sector = pos >> 9; + bio_add_folio_nofail(&io.rq->bio, folio, len, 0); erofs_fileio_rq_submit(io.rq); return 0; } 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) diff --git a/fs/erofs/super.c b/fs/erofs/super.c index 16fa3b853f1b..ce05557bd998 100644 --- a/fs/erofs/super.c +++ b/fs/erofs/super.c @@ -827,7 +827,8 @@ static int erofs_sb_share_test_super(struct super_block *sb, return 0; return file_inode(sbi->dif0.file) == file_inode(new_sbi->dif0.file) && sbi->dif0.fsoff == new_sbi->dif0.fsoff && - !memcmp(&sbi->opt, &new_sbi->opt, sizeof(sbi->opt)); + !memcmp(&sbi->opt, &new_sbi->opt, sizeof(sbi->opt)) && + !new_sbi->devs->extra_devices; } static int erofs_file_get_tree(struct fs_context *fc) @@ -850,8 +851,7 @@ static int erofs_fc_get_tree(struct fs_context *fc) errorfc(fc, "source fd option not supported"); return -EINVAL; } - if (!S_ISREG(file_inode(sbi->dif0.file)->i_mode) || - !sbi->dif0.file->f_mapping->a_ops->read_folio) { + if (!S_ISREG(file_inode(sbi->dif0.file)->i_mode)) { errorfc(fc, "source is unsupported"); return -EINVAL; } @@ -871,8 +871,7 @@ static int erofs_fc_get_tree(struct fs_context *fc) return PTR_ERR(file); sbi->dif0.file = file; - if (S_ISREG(file_inode(sbi->dif0.file)->i_mode) && - sbi->dif0.file->f_mapping->a_ops->read_folio) + if (S_ISREG(file_inode(sbi->dif0.file)->i_mode)) return erofs_file_get_tree(fc); } return ret;