Skip to content

Commit 7cef3c8

Browse files
committed
erofs: separate plain and compressed filesystems formally
The EROFS on-disk format uses a tiny, plain metadata design that prioritizes performance and minimizes complex inconsistencies against common writable disk filesystems (almost all serious metadata inconsistency cannot happen in well-designed immutable filesystems like EROFS). EROFS deliberately avoids artificial design flaws to eliminate serious security risks from untrusted remote sources by design, although human-made implementation bugs can still happen sometimes. Currently, there is no strict check to prevent compressed inodes, especially LZ4-compressed inodes, from being read in plain filesystems. Starting with erofs-utils 1.0 and Linux 5.3, LZ4_0PADDING sb feature is automatically enabled for LZ4-compressed EROFS images to support in-place decompression. Furthermore, since Linux 5.4 LTS is no longer supported, we no longer need to handle ancient LZ4-compressed EROFS images generated by erofs-utils prior to 1.0. To formally distinguish different filesystem types for improved security: - Use the presence of LZ4_0PADDING or a non-zero `dsb->u1.lz4_max_distance` as a marker for compressed filesystems containing LZ4-compressed inodes only; - For other algorithms, use `dsb->u1.available_compr_algs` bitmap. Note: LZ4_0PADDING has been supported since Linux 5.4 (the first formal kernel version), so exposing it via sysfs is no longer necessary and is now deprecated (but remain it for five more years until 2031): `dsb->u1` has been strictly non-zero for all EROFS images containing compressed inodes starting with erofs-utils v1.3 and it is actually a much better marker for compressed filesystems. Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
1 parent 72558e2 commit 7cef3c8

7 files changed

Lines changed: 39 additions & 45 deletions

File tree

Documentation/ABI/testing/sysfs-fs-erofs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ Date: November 2021
33
Contact: "Huang Jianan" <huangjianan@oppo.com>
44
Description: Shows all enabled kernel features.
55
Supported features:
6-
zero_padding, compr_cfgs, big_pcluster, chunked_file,
7-
device_table, compr_head2, sb_chksum, ztailpacking,
8-
dedupe, fragments, 48bit, metabox.
6+
compr_cfgs, big_pcluster, chunked_file, device_table,
7+
compr_head2, sb_chksum, ztailpacking, dedupe, fragments,
8+
48bit, metabox.
99

1010
What: /sys/fs/erofs/<disk>/sync_decompress
1111
Date: November 2021

fs/erofs/decompressor.c

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ static int z_erofs_load_lz4_config(struct super_block *sb,
3434
}
3535
} else {
3636
distance = le16_to_cpu(dsb->u1.lz4_max_distance);
37+
if (!distance && !erofs_sb_has_lz4_0padding(sbi))
38+
return 0;
3739
sbi->lz4.max_pclusterblks = 1;
40+
sbi->available_compr_algs = 1 << Z_EROFS_COMPRESSION_LZ4;
3841
}
3942

4043
sbi->lz4.max_distance_pages = distance ?
@@ -198,26 +201,22 @@ const char *z_erofs_fixup_insize(struct z_erofs_decompress_req *rq,
198201
static const char *__z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq,
199202
u8 *dst)
200203
{
201-
bool zeropadded = erofs_sb_has_zero_padding(EROFS_SB(rq->sb));
202204
bool may_inplace = false;
203205
unsigned int inputmargin;
204206
u8 *out, *headpage, *src;
205207
const char *reason;
206208
int ret, maptype;
207209

208210
headpage = kmap_local_page(*rq->in);
209-
/* LZ4 decompression inplace is only safe if zero_padding is enabled */
210-
if (zeropadded) {
211-
reason = z_erofs_fixup_insize(rq, headpage + rq->pageofs_in,
212-
min_t(unsigned int, rq->inputsize,
213-
rq->sb->s_blocksize - rq->pageofs_in));
214-
if (reason) {
215-
kunmap_local(headpage);
216-
return reason;
217-
}
218-
may_inplace = !((rq->pageofs_in + rq->inputsize) &
219-
(rq->sb->s_blocksize - 1));
211+
reason = z_erofs_fixup_insize(rq, headpage + rq->pageofs_in,
212+
min_t(unsigned int, rq->inputsize,
213+
rq->sb->s_blocksize - rq->pageofs_in));
214+
if (reason) {
215+
kunmap_local(headpage);
216+
return reason;
220217
}
218+
may_inplace = !((rq->pageofs_in + rq->inputsize) &
219+
(rq->sb->s_blocksize - 1));
221220

222221
inputmargin = rq->pageofs_in;
223222
src = z_erofs_lz4_handle_overlap(rq, headpage, dst, &inputmargin,
@@ -226,8 +225,7 @@ static const char *__z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq,
226225
return ERR_CAST(src);
227226

228227
out = dst + rq->pageofs_out;
229-
/* legacy format could compress extra data in a pcluster. */
230-
if (rq->partial_decoding || !zeropadded)
228+
if (rq->partial_decoding)
231229
ret = LZ4_decompress_safe_partial(src + inputmargin, out,
232230
rq->inputsize, rq->outputsize, rq->outputsize);
233231
else
@@ -454,10 +452,8 @@ int z_erofs_parse_cfgs(struct super_block *sb, struct erofs_super_block *dsb)
454452
erofs_off_t offset;
455453
int size, ret = 0;
456454

457-
if (!erofs_sb_has_compr_cfgs(sbi)) {
458-
sbi->available_compr_algs = 1 << Z_EROFS_COMPRESSION_LZ4;
455+
if (!erofs_sb_has_compr_cfgs(sbi))
459456
return z_erofs_load_lz4_config(sb, dsb, NULL, 0);
460-
}
461457

462458
algs = le16_to_cpu(dsb->u1.available_compr_algs);
463459
sbi->available_compr_algs = algs;

fs/erofs/erofs_fs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
* Any bits that aren't in EROFS_ALL_FEATURE_INCOMPAT should
2424
* be incompatible with this kernel version.
2525
*/
26-
#define EROFS_FEATURE_INCOMPAT_ZERO_PADDING 0x00000001
26+
#define EROFS_FEATURE_INCOMPAT_LZ4_0PADDING 0x00000001
2727
#define EROFS_FEATURE_INCOMPAT_COMPR_CFGS 0x00000002
2828
#define EROFS_FEATURE_INCOMPAT_BIG_PCLUSTER 0x00000002
2929
#define EROFS_FEATURE_INCOMPAT_CHUNKED_FILE 0x00000004

fs/erofs/inode.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,11 +183,17 @@ static int erofs_read_inode(struct inode *inode)
183183
goto err_out;
184184
}
185185

186-
if (erofs_inode_is_data_compressed(vi->datalayout))
187-
inode->i_blocks = le32_to_cpu(copied.i_u.blocks_lo) <<
188-
(sb->s_blocksize_bits - 9);
189-
else
186+
if (!erofs_inode_is_data_compressed(vi->datalayout)) {
190187
inode->i_blocks = round_up(inode->i_size, sb->s_blocksize) >> 9;
188+
} else if (!IS_ENABLED(CONFIG_EROFS_FS_ZIP) || !sbi->available_compr_algs) {
189+
erofs_err(sb, "compressed inode (nid %llu) is invalid in a plain filesystem",
190+
vi->nid);
191+
err = -EFSCORRUPTED;
192+
goto err_out;
193+
} else {
194+
inode->i_blocks = le32_to_cpu(copied.i_u.blocks_lo) <<
195+
(sb->s_blocksize_bits - 9);
196+
}
191197

192198
if (vi->datalayout == EROFS_INODE_CHUNK_BASED) {
193199
/* fill chunked inode summary info */

fs/erofs/internal.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@ struct erofs_sb_info {
114114

115115
unsigned int sync_decompress; /* strategy for sync decompression */
116116
unsigned int shrinker_run_no;
117-
u16 available_compr_algs;
118117

119118
/* pseudo inode to manage cached pages */
120119
struct inode *managed_cache;
@@ -154,6 +153,7 @@ struct erofs_sb_info {
154153
char *volume_name;
155154
u32 feature_compat;
156155
u32 feature_incompat;
156+
u16 available_compr_algs;
157157

158158
/* sysfs support */
159159
struct kobject s_kobj; /* /sys/fs/erofs/<devname> */
@@ -221,7 +221,7 @@ static inline bool erofs_sb_has_##name(struct erofs_sb_info *sbi) \
221221
return sbi->feature_##compat & EROFS_FEATURE_##feature; \
222222
}
223223

224-
EROFS_FEATURE_FUNCS(zero_padding, incompat, INCOMPAT_ZERO_PADDING)
224+
EROFS_FEATURE_FUNCS(lz4_0padding, incompat, INCOMPAT_LZ4_0PADDING)
225225
EROFS_FEATURE_FUNCS(compr_cfgs, incompat, INCOMPAT_COMPR_CFGS)
226226
EROFS_FEATURE_FUNCS(big_pcluster, incompat, INCOMPAT_BIG_PCLUSTER)
227227
EROFS_FEATURE_FUNCS(chunked_file, incompat, INCOMPAT_CHUNKED_FILE)
@@ -530,7 +530,6 @@ void z_erofs_put_gbuf(void *ptr);
530530
int z_erofs_gbuf_growsize(unsigned int nrpages);
531531
int __init z_erofs_gbuf_init(void);
532532
void z_erofs_gbuf_exit(void);
533-
int z_erofs_parse_cfgs(struct super_block *sb, struct erofs_super_block *dsb);
534533
#else
535534
static inline void erofs_shrinker_register(struct super_block *sb) {}
536535
static inline void erofs_shrinker_unregister(struct super_block *sb) {}
@@ -540,6 +539,7 @@ static inline int z_erofs_init_subsystem(void) { return 0; }
540539
static inline void z_erofs_exit_subsystem(void) {}
541540
static inline int z_erofs_init_super(struct super_block *sb) { return 0; }
542541
#endif /* !CONFIG_EROFS_FS_ZIP */
542+
int z_erofs_parse_cfgs(struct super_block *sb, struct erofs_super_block *dsb);
543543

544544
#ifdef CONFIG_EROFS_FS_BACKED_BY_FILE
545545
struct bio *erofs_fileio_bio_alloc(struct erofs_map_dev *mdev);

fs/erofs/super.c

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -122,18 +122,6 @@ void *erofs_read_metadata(struct super_block *sb, struct erofs_buf *buf,
122122
return buffer;
123123
}
124124

125-
#ifndef CONFIG_EROFS_FS_ZIP
126-
static int z_erofs_parse_cfgs(struct super_block *sb,
127-
struct erofs_super_block *dsb)
128-
{
129-
if (!dsb->u1.available_compr_algs)
130-
return 0;
131-
132-
erofs_err(sb, "compression disabled, unable to mount compressed EROFS");
133-
return -EOPNOTSUPP;
134-
}
135-
#endif
136-
137125
static int erofs_init_device(struct erofs_buf *buf, struct super_block *sb,
138126
struct erofs_device_info *dif, erofs_off_t *pos)
139127
{
@@ -363,10 +351,16 @@ static int erofs_read_superblock(struct super_block *sb)
363351
}
364352
}
365353

366-
/* parse on-disk compression configurations */
367-
ret = z_erofs_parse_cfgs(sb, dsb);
368-
if (ret < 0)
354+
if (IS_ENABLED(CONFIG_EROFS_FS_ZIP)) {
355+
ret = z_erofs_parse_cfgs(sb, dsb);
356+
if (ret < 0)
357+
goto out;
358+
} else if (dsb->u1.available_compr_algs ||
359+
erofs_sb_has_lz4_0padding(sbi)) {
360+
erofs_err(sb, "compression disabled, unable to mount compressed EROFS");
361+
ret = -EOPNOTSUPP;
369362
goto out;
363+
}
370364

371365
ret = erofs_scan_devices(sb, dsb);
372366

fs/erofs/sysfs.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ static struct attribute *erofs_attrs[] = {
8686
ATTRIBUTE_GROUPS(erofs);
8787

8888
/* Features this copy of erofs supports */
89-
EROFS_ATTR_FEATURE(zero_padding);
9089
EROFS_ATTR_FEATURE(compr_cfgs);
9190
EROFS_ATTR_FEATURE(big_pcluster);
9291
EROFS_ATTR_FEATURE(chunked_file);
@@ -100,7 +99,6 @@ EROFS_ATTR_FEATURE(48bit);
10099
EROFS_ATTR_FEATURE(metabox);
101100

102101
static struct attribute *erofs_feat_attrs[] = {
103-
ATTR_LIST(zero_padding),
104102
ATTR_LIST(compr_cfgs),
105103
ATTR_LIST(big_pcluster),
106104
ATTR_LIST(chunked_file),

0 commit comments

Comments
 (0)