Commit b44686c8 authored by Gao Xiang's avatar Gao Xiang
Browse files

erofs: fix large fragment handling



Fragments aren't limited by Z_EROFS_PCLUSTER_MAX_DSIZE. However, if
a fragment's logical length is larger than Z_EROFS_PCLUSTER_MAX_DSIZE
but the fragment is not the whole inode, it currently returns
-EOPNOTSUPP because m_flags has the wrong EROFS_MAP_ENCODED flag set.
It is not intended by design but should be rare, as it can only be
reproduced by mkfs with `-Eall-fragments` in a specific case.

Let's normalize fragment m_flags using the new EROFS_MAP_FRAGMENT.

Reported-by: default avatarAxel Fontaine <axel@axelfontaine.com>
Closes: https://github.com/erofs/erofs-utils/issues/23


Fixes: 7c3ca183 ("erofs: restrict pcluster size limitations")
Signed-off-by: default avatarGao Xiang <hsiangkao@linux.alibaba.com>
Link: https://lore.kernel.org/r/20250711195826.3601157-1-hsiangkao@linux.alibaba.com
parent d31fbdc4
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -315,10 +315,12 @@ static inline struct folio *erofs_grab_folio_nowait(struct address_space *as,
/* The length of extent is full */
#define EROFS_MAP_FULL_MAPPED	0x0008
/* Located in the special packed inode */
#define EROFS_MAP_FRAGMENT	0x0010
#define __EROFS_MAP_FRAGMENT	0x0010
/* The extent refers to partial decompressed data */
#define EROFS_MAP_PARTIAL_REF	0x0020

#define EROFS_MAP_FRAGMENT	(EROFS_MAP_MAPPED | __EROFS_MAP_FRAGMENT)

struct erofs_map_blocks {
	struct erofs_buf buf;

+1 −1
Original line number Diff line number Diff line
@@ -1034,7 +1034,7 @@ static int z_erofs_scan_folio(struct z_erofs_frontend *f,
		if (!(map->m_flags & EROFS_MAP_MAPPED)) {
			folio_zero_segment(folio, cur, end);
			tight = false;
		} else if (map->m_flags & EROFS_MAP_FRAGMENT) {
		} else if (map->m_flags & __EROFS_MAP_FRAGMENT) {
			erofs_off_t fpos = offset + cur - map->m_la;

			err = z_erofs_read_fragment(inode->i_sb, folio, cur,
+4 −5
Original line number Diff line number Diff line
@@ -413,8 +413,7 @@ static int z_erofs_map_blocks_fo(struct inode *inode,
	    !vi->z_tailextent_headlcn) {
		map->m_la = 0;
		map->m_llen = inode->i_size;
		map->m_flags = EROFS_MAP_MAPPED |
			EROFS_MAP_FULL_MAPPED | EROFS_MAP_FRAGMENT;
		map->m_flags = EROFS_MAP_FRAGMENT;
		return 0;
	}
	initial_lcn = ofs >> lclusterbits;
@@ -489,7 +488,7 @@ static int z_erofs_map_blocks_fo(struct inode *inode,
			goto unmap_out;
		}
	} else if (fragment && m.lcn == vi->z_tailextent_headlcn) {
		map->m_flags |= EROFS_MAP_FRAGMENT;
		map->m_flags = EROFS_MAP_FRAGMENT;
	} else {
		map->m_pa = erofs_pos(sb, m.pblk);
		err = z_erofs_get_extent_compressedlen(&m, initial_lcn);
@@ -617,7 +616,7 @@ static int z_erofs_map_blocks_ext(struct inode *inode,
	if (lstart < lend) {
		map->m_la = lstart;
		if (last && (vi->z_advise & Z_EROFS_ADVISE_FRAGMENT_PCLUSTER)) {
			map->m_flags |= EROFS_MAP_MAPPED | EROFS_MAP_FRAGMENT;
			map->m_flags = EROFS_MAP_FRAGMENT;
			vi->z_fragmentoff = map->m_plen;
			if (recsz > offsetof(struct z_erofs_extent, pstart_lo))
				vi->z_fragmentoff |= map->m_pa << 32;
@@ -797,7 +796,7 @@ static int z_erofs_iomap_begin_report(struct inode *inode, loff_t offset,
	iomap->length = map.m_llen;
	if (map.m_flags & EROFS_MAP_MAPPED) {
		iomap->type = IOMAP_MAPPED;
		iomap->addr = map.m_flags & EROFS_MAP_FRAGMENT ?
		iomap->addr = map.m_flags & __EROFS_MAP_FRAGMENT ?
			      IOMAP_NULL_ADDR : map.m_pa;
	} else {
		iomap->type = IOMAP_HOLE;