Changes since last update:

- Hold real inodes to fix mmap usage with page cache sharing
 
  - Fix metadata reads at the end of file-backed images with sub-page
    block sizes
 
  - Ensure disallowing superblock_share when device= is specified
 
  - Drop unnecessary .read_folio() checks for file-backed mounts to
    allow tmpfs as a backing fs
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCgAvFiEEQ0A6bDUS9Y+83NPFUXZn5Zlu5qoFAmrg3FERHHhpYW5nQGtl
 cm5lbC5vcmcACgkQUXZn5Zlu5qrsKg//RUS1rgussihehHtUbGyWY+nXc4HZjPTF
 WfKXGtgyqwzfEY7ycqqgJheFxD/z+NFMIR1lrm4P9VcA/mDmHJ3EZavmQGz6bDY7
 SWU6gNS7KQ8mflvRronPRutbXJsB8lI/vlYEqJcIvwe2DBkDTyPTeFET27SgKHk2
 MtcywEQn6ij9b3WHJ45Ou313oBycv1Rn8lXEekwslBZxgZbqQ+7OS/1PXtasypT8
 WQCo70Kux7P5+kqCAk2kQ6v7Vf/IBHpJR6gU8QzEJYULWBTmij+qJyGcXhaz3ngy
 FJkIdX0hkCMFsPPJF+8dmuaf1G/ikCSqMwjOr2NxuZpkHdyyQKDAn4njQ5PUHFP7
 nYg86dIUteguts2p4CG6MUCHiv8gY9gETrr3u2QW04UOHTkwyFfR2nw83HcbEh1R
 XN3lrIIAD25UHZuUqs0pFw8+jaPv2ZKj/HHFFDw5PLtkkSxY42kUVkx5oGofaOI8
 h4h3LFADf5Rkwjwo+pOGdM7m8OgYPZ7U0fLWSHOEOMxbkJCIwI8uCTw3qZBaukBv
 Vu8Zsomt+IFNYmtjVUypOuM/5dhmgcZzEVqEZ77PFgjTKfriVnmLjcCJAupaSBLU
 OS+Ul7HBd+uU7hdgoODdi1uNKZ0xiz34KeAC69M2suRQZEX0R9zTEnN9gPei0qLH
 jBSdCQ7AJBY=
 =VNdm
 -----END PGP SIGNATURE-----

Merge tag 'erofs-for-7.4-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs

Pull erofs fixes from Gao Xiang:
 "These patches address several new reported issues related to page
  cache sharing, the new superblock_share feature, and recent metadata
  path changes for file-backed mounts:

   - Hold real inodes to fix mmap usage with page cache sharing

   - Fix metadata reads at the end of file-backed images with sub-page
     block sizes

   - Ensure disallowing superblock_share when device= is specified

   - Drop unnecessary .read_folio() checks for file-backed mounts to
     allow tmpfs as a backing fs"

* tag 'erofs-for-7.4-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs:
  erofs: drop unnecessary .read_folio() checks for file-backed mounts
  erofs: fix disallowing superblock_share if device= is set
  erofs: fix metadata reads at the end of file-backed images
  erofs: hold real inodes for shared files
This commit is contained in:
Linus Torvalds 2026-10-27 09:20:30 -10:00
commit 83f32c7732
3 changed files with 49 additions and 21 deletions

View File

@ -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;
}

View File

@ -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)

View File

@ -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;