Commit | Line | Data |
---|---|---|
29b24f6c | 1 | // SPDX-License-Identifier: GPL-2.0-only |
ba2b77a8 | 2 | /* |
ba2b77a8 | 3 | * Copyright (C) 2017-2018 HUAWEI, Inc. |
592e7cd0 | 4 | * https://www.huawei.com/ |
2b5379f7 | 5 | * Copyright (C) 2021, Alibaba Cloud |
ba2b77a8 GX |
6 | */ |
7 | #include <linux/module.h> | |
8 | #include <linux/buffer_head.h> | |
9 | #include <linux/statfs.h> | |
10 | #include <linux/parser.h> | |
b17500a0 | 11 | #include <linux/seq_file.h> |
b858a484 | 12 | #include <linux/crc32c.h> |
f57a3fe4 CY |
13 | #include <linux/fs_context.h> |
14 | #include <linux/fs_parser.h> | |
06252e9c | 15 | #include <linux/dax.h> |
3e917cc3 | 16 | #include <linux/exportfs.h> |
6af7b483 | 17 | #include "xattr.h" |
ba2b77a8 | 18 | |
13f06f48 CY |
19 | #define CREATE_TRACE_POINTS |
20 | #include <trace/events/erofs.h> | |
21 | ||
ba2b77a8 GX |
22 | static struct kmem_cache *erofs_inode_cachep __read_mostly; |
23 | ||
4f761fa2 GX |
24 | void _erofs_err(struct super_block *sb, const char *function, |
25 | const char *fmt, ...) | |
26 | { | |
27 | struct va_format vaf; | |
28 | va_list args; | |
29 | ||
30 | va_start(args, fmt); | |
31 | ||
32 | vaf.fmt = fmt; | |
33 | vaf.va = &args; | |
34 | ||
35 | pr_err("(device %s): %s: %pV", sb->s_id, function, &vaf); | |
36 | va_end(args); | |
37 | } | |
38 | ||
39 | void _erofs_info(struct super_block *sb, const char *function, | |
40 | const char *fmt, ...) | |
41 | { | |
42 | struct va_format vaf; | |
43 | va_list args; | |
44 | ||
45 | va_start(args, fmt); | |
46 | ||
47 | vaf.fmt = fmt; | |
48 | vaf.va = &args; | |
49 | ||
50 | pr_info("(device %s): %pV", sb->s_id, &vaf); | |
51 | va_end(args); | |
52 | } | |
53 | ||
b858a484 PS |
54 | static int erofs_superblock_csum_verify(struct super_block *sb, void *sbdata) |
55 | { | |
56 | struct erofs_super_block *dsb; | |
57 | u32 expected_crc, crc; | |
58 | ||
59 | dsb = kmemdup(sbdata + EROFS_SUPER_OFFSET, | |
60 | EROFS_BLKSIZ - EROFS_SUPER_OFFSET, GFP_KERNEL); | |
61 | if (!dsb) | |
62 | return -ENOMEM; | |
63 | ||
64 | expected_crc = le32_to_cpu(dsb->checksum); | |
65 | dsb->checksum = 0; | |
66 | /* to allow for x86 boot sectors and other oddities. */ | |
67 | crc = crc32c(~0, dsb, EROFS_BLKSIZ - EROFS_SUPER_OFFSET); | |
68 | kfree(dsb); | |
69 | ||
70 | if (crc != expected_crc) { | |
71 | erofs_err(sb, "invalid checksum 0x%08x, 0x%08x expected", | |
72 | crc, expected_crc); | |
73 | return -EBADMSG; | |
74 | } | |
75 | return 0; | |
76 | } | |
77 | ||
99634bf3 | 78 | static void erofs_inode_init_once(void *ptr) |
ba2b77a8 | 79 | { |
a5876e24 | 80 | struct erofs_inode *vi = ptr; |
ba2b77a8 GX |
81 | |
82 | inode_init_once(&vi->vfs_inode); | |
83 | } | |
84 | ||
99634bf3 | 85 | static struct inode *erofs_alloc_inode(struct super_block *sb) |
ba2b77a8 | 86 | { |
a5876e24 | 87 | struct erofs_inode *vi = |
fd60b288 | 88 | alloc_inode_sb(sb, erofs_inode_cachep, GFP_KERNEL); |
ba2b77a8 | 89 | |
e2ff9f15 | 90 | if (!vi) |
ba2b77a8 GX |
91 | return NULL; |
92 | ||
93 | /* zero out everything except vfs_inode */ | |
a5876e24 | 94 | memset(vi, 0, offsetof(struct erofs_inode, vfs_inode)); |
ba2b77a8 GX |
95 | return &vi->vfs_inode; |
96 | } | |
97 | ||
99634bf3 | 98 | static void erofs_free_inode(struct inode *inode) |
ba2b77a8 | 99 | { |
a5876e24 | 100 | struct erofs_inode *vi = EROFS_I(inode); |
ba2b77a8 | 101 | |
a2c75c81 GX |
102 | /* be careful of RCU symlink path */ |
103 | if (inode->i_op == &erofs_fast_symlink_iops) | |
ba2b77a8 | 104 | kfree(inode->i_link); |
ba2b77a8 GX |
105 | kfree(vi->xattr_shared_xattrs); |
106 | ||
107 | kmem_cache_free(erofs_inode_cachep, vi); | |
108 | } | |
109 | ||
5efe5137 | 110 | static bool check_layout_compatibility(struct super_block *sb, |
0259f209 | 111 | struct erofs_super_block *dsb) |
5efe5137 | 112 | { |
0259f209 | 113 | const unsigned int feature = le32_to_cpu(dsb->feature_incompat); |
5efe5137 | 114 | |
426a9308 | 115 | EROFS_SB(sb)->feature_incompat = feature; |
5efe5137 GX |
116 | |
117 | /* check if current kernel meets all mandatory requirements */ | |
426a9308 | 118 | if (feature & (~EROFS_ALL_FEATURE_INCOMPAT)) { |
4f761fa2 GX |
119 | erofs_err(sb, |
120 | "unidentified incompatible feature %x, please upgrade kernel version", | |
121 | feature & ~EROFS_ALL_FEATURE_INCOMPAT); | |
5efe5137 GX |
122 | return false; |
123 | } | |
124 | return true; | |
125 | } | |
126 | ||
14373711 GX |
127 | #ifdef CONFIG_EROFS_FS_ZIP |
128 | /* read variable-sized metadata, offset will be aligned by 4-byte */ | |
2b5379f7 | 129 | static void *erofs_read_metadata(struct super_block *sb, struct erofs_buf *buf, |
14373711 GX |
130 | erofs_off_t *offset, int *lengthp) |
131 | { | |
14373711 GX |
132 | u8 *buffer, *ptr; |
133 | int len, i, cnt; | |
14373711 GX |
134 | |
135 | *offset = round_up(*offset, 4); | |
2b5379f7 GX |
136 | ptr = erofs_read_metabuf(buf, sb, erofs_blknr(*offset), EROFS_KMAP); |
137 | if (IS_ERR(ptr)) | |
138 | return ptr; | |
14373711 | 139 | |
14373711 GX |
140 | len = le16_to_cpu(*(__le16 *)&ptr[erofs_blkoff(*offset)]); |
141 | if (!len) | |
142 | len = U16_MAX + 1; | |
143 | buffer = kmalloc(len, GFP_KERNEL); | |
2b5379f7 GX |
144 | if (!buffer) |
145 | return ERR_PTR(-ENOMEM); | |
14373711 GX |
146 | *offset += sizeof(__le16); |
147 | *lengthp = len; | |
148 | ||
149 | for (i = 0; i < len; i += cnt) { | |
150 | cnt = min(EROFS_BLKSIZ - (int)erofs_blkoff(*offset), len - i); | |
2b5379f7 GX |
151 | ptr = erofs_read_metabuf(buf, sb, erofs_blknr(*offset), |
152 | EROFS_KMAP); | |
153 | if (IS_ERR(ptr)) { | |
154 | kfree(buffer); | |
155 | return ptr; | |
14373711 GX |
156 | } |
157 | memcpy(buffer + i, ptr + erofs_blkoff(*offset), cnt); | |
158 | *offset += cnt; | |
159 | } | |
14373711 | 160 | return buffer; |
14373711 GX |
161 | } |
162 | ||
163 | static int erofs_load_compr_cfgs(struct super_block *sb, | |
164 | struct erofs_super_block *dsb) | |
165 | { | |
2b5379f7 GX |
166 | struct erofs_sb_info *sbi = EROFS_SB(sb); |
167 | struct erofs_buf buf = __EROFS_BUF_INITIALIZER; | |
14373711 GX |
168 | unsigned int algs, alg; |
169 | erofs_off_t offset; | |
2b5379f7 | 170 | int size, ret = 0; |
14373711 | 171 | |
14373711 | 172 | sbi->available_compr_algs = le16_to_cpu(dsb->u1.available_compr_algs); |
14373711 GX |
173 | if (sbi->available_compr_algs & ~Z_EROFS_ALL_COMPR_ALGS) { |
174 | erofs_err(sb, "try to load compressed fs with unsupported algorithms %x", | |
175 | sbi->available_compr_algs & ~Z_EROFS_ALL_COMPR_ALGS); | |
176 | return -EINVAL; | |
177 | } | |
178 | ||
179 | offset = EROFS_SUPER_OFFSET + sbi->sb_size; | |
14373711 | 180 | alg = 0; |
14373711 GX |
181 | for (algs = sbi->available_compr_algs; algs; algs >>= 1, ++alg) { |
182 | void *data; | |
183 | ||
184 | if (!(algs & 1)) | |
185 | continue; | |
186 | ||
2b5379f7 | 187 | data = erofs_read_metadata(sb, &buf, &offset, &size); |
14373711 GX |
188 | if (IS_ERR(data)) { |
189 | ret = PTR_ERR(data); | |
2b5379f7 | 190 | break; |
14373711 GX |
191 | } |
192 | ||
193 | switch (alg) { | |
194 | case Z_EROFS_COMPRESSION_LZ4: | |
195 | ret = z_erofs_load_lz4_config(sb, dsb, data, size); | |
196 | break; | |
622ceadd GX |
197 | case Z_EROFS_COMPRESSION_LZMA: |
198 | ret = z_erofs_load_lzma_config(sb, dsb, data, size); | |
199 | break; | |
14373711 GX |
200 | default: |
201 | DBG_BUGON(1); | |
202 | ret = -EFAULT; | |
203 | } | |
204 | kfree(data); | |
205 | if (ret) | |
2b5379f7 | 206 | break; |
14373711 | 207 | } |
2b5379f7 | 208 | erofs_put_metabuf(&buf); |
14373711 GX |
209 | return ret; |
210 | } | |
211 | #else | |
212 | static int erofs_load_compr_cfgs(struct super_block *sb, | |
213 | struct erofs_super_block *dsb) | |
214 | { | |
215 | if (dsb->u1.available_compr_algs) { | |
216 | erofs_err(sb, "try to load compressed fs when compression is disabled"); | |
217 | return -EINVAL; | |
218 | } | |
219 | return 0; | |
220 | } | |
221 | #endif | |
222 | ||
dfeab2e9 GX |
223 | static int erofs_init_devices(struct super_block *sb, |
224 | struct erofs_super_block *dsb) | |
225 | { | |
226 | struct erofs_sb_info *sbi = EROFS_SB(sb); | |
227 | unsigned int ondisk_extradevs; | |
228 | erofs_off_t pos; | |
2b5379f7 | 229 | struct erofs_buf buf = __EROFS_BUF_INITIALIZER; |
dfeab2e9 GX |
230 | struct erofs_device_info *dif; |
231 | struct erofs_deviceslot *dis; | |
232 | void *ptr; | |
233 | int id, err = 0; | |
234 | ||
235 | sbi->total_blocks = sbi->primarydevice_blocks; | |
236 | if (!erofs_sb_has_device_table(sbi)) | |
237 | ondisk_extradevs = 0; | |
238 | else | |
239 | ondisk_extradevs = le16_to_cpu(dsb->extra_devices); | |
240 | ||
241 | if (ondisk_extradevs != sbi->devs->extra_devices) { | |
242 | erofs_err(sb, "extra devices don't match (ondisk %u, given %u)", | |
243 | ondisk_extradevs, sbi->devs->extra_devices); | |
244 | return -EINVAL; | |
245 | } | |
246 | if (!ondisk_extradevs) | |
247 | return 0; | |
248 | ||
249 | sbi->device_id_mask = roundup_pow_of_two(ondisk_extradevs + 1) - 1; | |
250 | pos = le16_to_cpu(dsb->devt_slotoff) * EROFS_DEVT_SLOT_SIZE; | |
251 | down_read(&sbi->devs->rwsem); | |
252 | idr_for_each_entry(&sbi->devs->tree, dif, id) { | |
dfeab2e9 GX |
253 | struct block_device *bdev; |
254 | ||
2b5379f7 GX |
255 | ptr = erofs_read_metabuf(&buf, sb, erofs_blknr(pos), |
256 | EROFS_KMAP); | |
257 | if (IS_ERR(ptr)) { | |
258 | err = PTR_ERR(ptr); | |
259 | break; | |
dfeab2e9 GX |
260 | } |
261 | dis = ptr + erofs_blkoff(pos); | |
262 | ||
955b478e JX |
263 | if (erofs_is_fscache_mode(sb)) { |
264 | err = erofs_fscache_register_cookie(sb, &dif->fscache, | |
265 | dif->path, false); | |
266 | if (err) | |
267 | break; | |
268 | } else { | |
93b856bb JX |
269 | bdev = blkdev_get_by_path(dif->path, |
270 | FMODE_READ | FMODE_EXCL, | |
271 | sb->s_type); | |
272 | if (IS_ERR(bdev)) { | |
273 | err = PTR_ERR(bdev); | |
274 | break; | |
275 | } | |
276 | dif->bdev = bdev; | |
277 | dif->dax_dev = fs_dax_get_by_bdev(bdev, | |
278 | &dif->dax_part_off); | |
dfeab2e9 | 279 | } |
93b856bb | 280 | |
dfeab2e9 GX |
281 | dif->blocks = le32_to_cpu(dis->blocks); |
282 | dif->mapped_blkaddr = le32_to_cpu(dis->mapped_blkaddr); | |
283 | sbi->total_blocks += dif->blocks; | |
284 | pos += EROFS_DEVT_SLOT_SIZE; | |
285 | } | |
dfeab2e9 | 286 | up_read(&sbi->devs->rwsem); |
2b5379f7 | 287 | erofs_put_metabuf(&buf); |
dfeab2e9 GX |
288 | return err; |
289 | } | |
290 | ||
99634bf3 | 291 | static int erofs_read_superblock(struct super_block *sb) |
ba2b77a8 GX |
292 | { |
293 | struct erofs_sb_info *sbi; | |
ed6e0401 | 294 | struct erofs_buf buf = __EROFS_BUF_INITIALIZER; |
0259f209 | 295 | struct erofs_super_block *dsb; |
7dd68b14 | 296 | unsigned int blkszbits; |
fe7c2423 | 297 | void *data; |
ba2b77a8 GX |
298 | int ret; |
299 | ||
ed6e0401 JX |
300 | data = erofs_read_metabuf(&buf, sb, 0, EROFS_KMAP); |
301 | if (IS_ERR(data)) { | |
4f761fa2 | 302 | erofs_err(sb, "cannot read erofs superblock"); |
ed6e0401 | 303 | return PTR_ERR(data); |
ba2b77a8 GX |
304 | } |
305 | ||
306 | sbi = EROFS_SB(sb); | |
fe7c2423 | 307 | dsb = (struct erofs_super_block *)(data + EROFS_SUPER_OFFSET); |
ba2b77a8 GX |
308 | |
309 | ret = -EINVAL; | |
0259f209 | 310 | if (le32_to_cpu(dsb->magic) != EROFS_SUPER_MAGIC_V1) { |
4f761fa2 | 311 | erofs_err(sb, "cannot find valid erofs superblock"); |
ba2b77a8 GX |
312 | goto out; |
313 | } | |
314 | ||
b858a484 | 315 | sbi->feature_compat = le32_to_cpu(dsb->feature_compat); |
de06a6a3 | 316 | if (erofs_sb_has_sb_chksum(sbi)) { |
b858a484 PS |
317 | ret = erofs_superblock_csum_verify(sb, data); |
318 | if (ret) | |
319 | goto out; | |
320 | } | |
321 | ||
0508c1ad | 322 | ret = -EINVAL; |
0259f209 | 323 | blkszbits = dsb->blkszbits; |
ba2b77a8 | 324 | /* 9(512 bytes) + LOG_SECTORS_PER_BLOCK == LOG_BLOCK_SIZE */ |
8d8a09b0 | 325 | if (blkszbits != LOG_BLOCK_SIZE) { |
bde54529 GX |
326 | erofs_err(sb, "blkszbits %u isn't supported on this platform", |
327 | blkszbits); | |
ba2b77a8 GX |
328 | goto out; |
329 | } | |
330 | ||
0259f209 | 331 | if (!check_layout_compatibility(sb, dsb)) |
5efe5137 GX |
332 | goto out; |
333 | ||
14373711 GX |
334 | sbi->sb_size = 128 + dsb->sb_extslots * EROFS_SB_EXTSLOT_SIZE; |
335 | if (sbi->sb_size > EROFS_BLKSIZ) { | |
336 | erofs_err(sb, "invalid sb_extslots %u (more than a fs block)", | |
337 | sbi->sb_size); | |
338 | goto out; | |
339 | } | |
dfeab2e9 | 340 | sbi->primarydevice_blocks = le32_to_cpu(dsb->blocks); |
0259f209 | 341 | sbi->meta_blkaddr = le32_to_cpu(dsb->meta_blkaddr); |
b17500a0 | 342 | #ifdef CONFIG_EROFS_FS_XATTR |
0259f209 | 343 | sbi->xattr_blkaddr = le32_to_cpu(dsb->xattr_blkaddr); |
b17500a0 | 344 | #endif |
8a765682 | 345 | sbi->islotbits = ilog2(sizeof(struct erofs_inode_compact)); |
0259f209 GX |
346 | sbi->root_nid = le16_to_cpu(dsb->root_nid); |
347 | sbi->inos = le64_to_cpu(dsb->inos); | |
ba2b77a8 | 348 | |
0259f209 GX |
349 | sbi->build_time = le64_to_cpu(dsb->build_time); |
350 | sbi->build_time_nsec = le32_to_cpu(dsb->build_time_nsec); | |
ba2b77a8 | 351 | |
0259f209 | 352 | memcpy(&sb->s_uuid, dsb->uuid, sizeof(dsb->uuid)); |
ba2b77a8 | 353 | |
0259f209 GX |
354 | ret = strscpy(sbi->volume_name, dsb->volume_name, |
355 | sizeof(dsb->volume_name)); | |
a64d9493 | 356 | if (ret < 0) { /* -E2BIG */ |
4f761fa2 | 357 | erofs_err(sb, "bad volume name without NIL terminator"); |
a64d9493 GX |
358 | ret = -EFSCORRUPTED; |
359 | goto out; | |
360 | } | |
5d50538f HJ |
361 | |
362 | /* parse on-disk compression configurations */ | |
14373711 GX |
363 | if (erofs_sb_has_compr_cfgs(sbi)) |
364 | ret = erofs_load_compr_cfgs(sb, dsb); | |
365 | else | |
366 | ret = z_erofs_load_lz4_config(sb, dsb, NULL, 0); | |
dfeab2e9 GX |
367 | if (ret < 0) |
368 | goto out; | |
369 | ||
370 | /* handle multiple devices */ | |
371 | ret = erofs_init_devices(sb, dsb); | |
ab92184f YH |
372 | |
373 | if (erofs_sb_has_ztailpacking(sbi)) | |
374 | erofs_info(sb, "EXPERIMENTAL compressed inline data feature in use. Use at your own risk!"); | |
ba2b77a8 | 375 | out: |
ed6e0401 | 376 | erofs_put_metabuf(&buf); |
ba2b77a8 GX |
377 | return ret; |
378 | } | |
379 | ||
4279f3f9 | 380 | /* set up default EROFS parameters */ |
f57a3fe4 | 381 | static void erofs_default_options(struct erofs_fs_context *ctx) |
ba2b77a8 | 382 | { |
5fb76bb0 | 383 | #ifdef CONFIG_EROFS_FS_ZIP |
e6242465 GX |
384 | ctx->opt.cache_strategy = EROFS_ZIP_CACHE_READAROUND; |
385 | ctx->opt.max_sync_decompress_pages = 3; | |
40452ffc | 386 | ctx->opt.sync_decompress = EROFS_SYNC_DECOMPRESS_AUTO; |
5fb76bb0 | 387 | #endif |
b17500a0 | 388 | #ifdef CONFIG_EROFS_FS_XATTR |
e6242465 | 389 | set_opt(&ctx->opt, XATTR_USER); |
b17500a0 | 390 | #endif |
b17500a0 | 391 | #ifdef CONFIG_EROFS_FS_POSIX_ACL |
e6242465 | 392 | set_opt(&ctx->opt, POSIX_ACL); |
b17500a0 | 393 | #endif |
ba2b77a8 GX |
394 | } |
395 | ||
396 | enum { | |
b17500a0 | 397 | Opt_user_xattr, |
b17500a0 | 398 | Opt_acl, |
4279f3f9 | 399 | Opt_cache_strategy, |
06252e9c GX |
400 | Opt_dax, |
401 | Opt_dax_enum, | |
dfeab2e9 | 402 | Opt_device, |
ba2b77a8 GX |
403 | Opt_err |
404 | }; | |
405 | ||
f57a3fe4 CY |
406 | static const struct constant_table erofs_param_cache_strategy[] = { |
407 | {"disabled", EROFS_ZIP_CACHE_DISABLED}, | |
408 | {"readahead", EROFS_ZIP_CACHE_READAHEAD}, | |
409 | {"readaround", EROFS_ZIP_CACHE_READAROUND}, | |
410 | {} | |
ba2b77a8 GX |
411 | }; |
412 | ||
06252e9c GX |
413 | static const struct constant_table erofs_dax_param_enums[] = { |
414 | {"always", EROFS_MOUNT_DAX_ALWAYS}, | |
415 | {"never", EROFS_MOUNT_DAX_NEVER}, | |
416 | {} | |
417 | }; | |
418 | ||
f57a3fe4 CY |
419 | static const struct fs_parameter_spec erofs_fs_parameters[] = { |
420 | fsparam_flag_no("user_xattr", Opt_user_xattr), | |
421 | fsparam_flag_no("acl", Opt_acl), | |
422 | fsparam_enum("cache_strategy", Opt_cache_strategy, | |
423 | erofs_param_cache_strategy), | |
06252e9c GX |
424 | fsparam_flag("dax", Opt_dax), |
425 | fsparam_enum("dax", Opt_dax_enum, erofs_dax_param_enums), | |
dfeab2e9 | 426 | fsparam_string("device", Opt_device), |
f57a3fe4 CY |
427 | {} |
428 | }; | |
ba2b77a8 | 429 | |
06252e9c GX |
430 | static bool erofs_fc_set_dax_mode(struct fs_context *fc, unsigned int mode) |
431 | { | |
432 | #ifdef CONFIG_FS_DAX | |
433 | struct erofs_fs_context *ctx = fc->fs_private; | |
434 | ||
435 | switch (mode) { | |
436 | case EROFS_MOUNT_DAX_ALWAYS: | |
437 | warnfc(fc, "DAX enabled. Warning: EXPERIMENTAL, use at your own risk"); | |
e6242465 GX |
438 | set_opt(&ctx->opt, DAX_ALWAYS); |
439 | clear_opt(&ctx->opt, DAX_NEVER); | |
06252e9c GX |
440 | return true; |
441 | case EROFS_MOUNT_DAX_NEVER: | |
e6242465 GX |
442 | set_opt(&ctx->opt, DAX_NEVER); |
443 | clear_opt(&ctx->opt, DAX_ALWAYS); | |
06252e9c GX |
444 | return true; |
445 | default: | |
446 | DBG_BUGON(1); | |
447 | return false; | |
448 | } | |
449 | #else | |
450 | errorfc(fc, "dax options not supported"); | |
451 | return false; | |
452 | #endif | |
453 | } | |
454 | ||
f57a3fe4 CY |
455 | static int erofs_fc_parse_param(struct fs_context *fc, |
456 | struct fs_parameter *param) | |
457 | { | |
dfeab2e9 | 458 | struct erofs_fs_context *ctx = fc->fs_private; |
f57a3fe4 | 459 | struct fs_parse_result result; |
dfeab2e9 GX |
460 | struct erofs_device_info *dif; |
461 | int opt, ret; | |
ba2b77a8 | 462 | |
f57a3fe4 CY |
463 | opt = fs_parse(fc, erofs_fs_parameters, param, &result); |
464 | if (opt < 0) | |
465 | return opt; | |
ba2b77a8 | 466 | |
f57a3fe4 CY |
467 | switch (opt) { |
468 | case Opt_user_xattr: | |
b17500a0 | 469 | #ifdef CONFIG_EROFS_FS_XATTR |
f57a3fe4 | 470 | if (result.boolean) |
e6242465 | 471 | set_opt(&ctx->opt, XATTR_USER); |
f57a3fe4 | 472 | else |
e6242465 | 473 | clear_opt(&ctx->opt, XATTR_USER); |
b17500a0 | 474 | #else |
f57a3fe4 | 475 | errorfc(fc, "{,no}user_xattr options not supported"); |
b17500a0 | 476 | #endif |
f57a3fe4 CY |
477 | break; |
478 | case Opt_acl: | |
b17500a0 | 479 | #ifdef CONFIG_EROFS_FS_POSIX_ACL |
f57a3fe4 | 480 | if (result.boolean) |
e6242465 | 481 | set_opt(&ctx->opt, POSIX_ACL); |
f57a3fe4 | 482 | else |
e6242465 | 483 | clear_opt(&ctx->opt, POSIX_ACL); |
b17500a0 | 484 | #else |
f57a3fe4 | 485 | errorfc(fc, "{,no}acl options not supported"); |
b17500a0 | 486 | #endif |
f57a3fe4 CY |
487 | break; |
488 | case Opt_cache_strategy: | |
489 | #ifdef CONFIG_EROFS_FS_ZIP | |
e6242465 | 490 | ctx->opt.cache_strategy = result.uint_32; |
f57a3fe4 CY |
491 | #else |
492 | errorfc(fc, "compression not supported, cache_strategy ignored"); | |
493 | #endif | |
494 | break; | |
06252e9c GX |
495 | case Opt_dax: |
496 | if (!erofs_fc_set_dax_mode(fc, EROFS_MOUNT_DAX_ALWAYS)) | |
497 | return -EINVAL; | |
498 | break; | |
499 | case Opt_dax_enum: | |
500 | if (!erofs_fc_set_dax_mode(fc, result.uint_32)) | |
501 | return -EINVAL; | |
502 | break; | |
dfeab2e9 GX |
503 | case Opt_device: |
504 | dif = kzalloc(sizeof(*dif), GFP_KERNEL); | |
505 | if (!dif) | |
506 | return -ENOMEM; | |
507 | dif->path = kstrdup(param->string, GFP_KERNEL); | |
508 | if (!dif->path) { | |
509 | kfree(dif); | |
510 | return -ENOMEM; | |
511 | } | |
512 | down_write(&ctx->devs->rwsem); | |
513 | ret = idr_alloc(&ctx->devs->tree, dif, 0, 0, GFP_KERNEL); | |
514 | up_write(&ctx->devs->rwsem); | |
515 | if (ret < 0) { | |
516 | kfree(dif->path); | |
517 | kfree(dif); | |
518 | return ret; | |
519 | } | |
520 | ++ctx->devs->extra_devices; | |
521 | break; | |
f57a3fe4 CY |
522 | default: |
523 | return -ENOPARAM; | |
ba2b77a8 GX |
524 | } |
525 | return 0; | |
526 | } | |
527 | ||
4279f3f9 | 528 | #ifdef CONFIG_EROFS_FS_ZIP |
105d4ad8 GX |
529 | static const struct address_space_operations managed_cache_aops; |
530 | ||
99634bf3 | 531 | static int erofs_managed_cache_releasepage(struct page *page, gfp_t gfp_mask) |
105d4ad8 GX |
532 | { |
533 | int ret = 1; /* 0 - busy */ | |
534 | struct address_space *const mapping = page->mapping; | |
535 | ||
8b987bca GX |
536 | DBG_BUGON(!PageLocked(page)); |
537 | DBG_BUGON(mapping->a_ops != &managed_cache_aops); | |
105d4ad8 GX |
538 | |
539 | if (PagePrivate(page)) | |
d252ff3d | 540 | ret = erofs_try_to_free_cached_page(page); |
105d4ad8 GX |
541 | |
542 | return ret; | |
543 | } | |
544 | ||
9f2731d6 GX |
545 | /* |
546 | * It will be called only on inode eviction. In case that there are still some | |
547 | * decompression requests in progress, wait with rescheduling for a bit here. | |
548 | * We could introduce an extra locking instead but it seems unnecessary. | |
549 | */ | |
39653e69 MWO |
550 | static void erofs_managed_cache_invalidate_folio(struct folio *folio, |
551 | size_t offset, size_t length) | |
105d4ad8 | 552 | { |
39653e69 | 553 | const size_t stop = length + offset; |
105d4ad8 | 554 | |
39653e69 | 555 | DBG_BUGON(!folio_test_locked(folio)); |
105d4ad8 | 556 | |
8b987bca | 557 | /* Check for potential overflow in debug mode */ |
39653e69 | 558 | DBG_BUGON(stop > folio_size(folio) || stop < length); |
105d4ad8 | 559 | |
39653e69 MWO |
560 | if (offset == 0 && stop == folio_size(folio)) |
561 | while (!erofs_managed_cache_releasepage(&folio->page, GFP_NOFS)) | |
105d4ad8 GX |
562 | cond_resched(); |
563 | } | |
564 | ||
565 | static const struct address_space_operations managed_cache_aops = { | |
99634bf3 | 566 | .releasepage = erofs_managed_cache_releasepage, |
39653e69 | 567 | .invalidate_folio = erofs_managed_cache_invalidate_folio, |
105d4ad8 GX |
568 | }; |
569 | ||
8f7acdae | 570 | static int erofs_init_managed_cache(struct super_block *sb) |
105d4ad8 | 571 | { |
8f7acdae GX |
572 | struct erofs_sb_info *const sbi = EROFS_SB(sb); |
573 | struct inode *const inode = new_inode(sb); | |
105d4ad8 | 574 | |
8d8a09b0 | 575 | if (!inode) |
8f7acdae | 576 | return -ENOMEM; |
105d4ad8 GX |
577 | |
578 | set_nlink(inode, 1); | |
579 | inode->i_size = OFFSET_MAX; | |
580 | ||
581 | inode->i_mapping->a_ops = &managed_cache_aops; | |
9f2731d6 | 582 | mapping_set_gfp_mask(inode->i_mapping, GFP_NOFS); |
8f7acdae GX |
583 | sbi->managed_cache = inode; |
584 | return 0; | |
105d4ad8 | 585 | } |
8f7acdae GX |
586 | #else |
587 | static int erofs_init_managed_cache(struct super_block *sb) { return 0; } | |
105d4ad8 GX |
588 | #endif |
589 | ||
3e917cc3 HL |
590 | static struct inode *erofs_nfs_get_inode(struct super_block *sb, |
591 | u64 ino, u32 generation) | |
592 | { | |
593 | return erofs_iget(sb, ino, false); | |
594 | } | |
595 | ||
596 | static struct dentry *erofs_fh_to_dentry(struct super_block *sb, | |
597 | struct fid *fid, int fh_len, int fh_type) | |
598 | { | |
599 | return generic_fh_to_dentry(sb, fid, fh_len, fh_type, | |
600 | erofs_nfs_get_inode); | |
601 | } | |
602 | ||
603 | static struct dentry *erofs_fh_to_parent(struct super_block *sb, | |
604 | struct fid *fid, int fh_len, int fh_type) | |
605 | { | |
606 | return generic_fh_to_parent(sb, fid, fh_len, fh_type, | |
607 | erofs_nfs_get_inode); | |
608 | } | |
609 | ||
610 | static struct dentry *erofs_get_parent(struct dentry *child) | |
611 | { | |
612 | erofs_nid_t nid; | |
613 | unsigned int d_type; | |
614 | int err; | |
615 | ||
616 | err = erofs_namei(d_inode(child), &dotdot_name, &nid, &d_type); | |
617 | if (err) | |
618 | return ERR_PTR(err); | |
619 | return d_obtain_alias(erofs_iget(child->d_sb, nid, d_type == FT_DIR)); | |
620 | } | |
621 | ||
622 | static const struct export_operations erofs_export_ops = { | |
623 | .fh_to_dentry = erofs_fh_to_dentry, | |
624 | .fh_to_parent = erofs_fh_to_parent, | |
625 | .get_parent = erofs_get_parent, | |
626 | }; | |
627 | ||
f57a3fe4 | 628 | static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc) |
ba2b77a8 GX |
629 | { |
630 | struct inode *inode; | |
631 | struct erofs_sb_info *sbi; | |
f57a3fe4 | 632 | struct erofs_fs_context *ctx = fc->fs_private; |
8f7acdae | 633 | int err; |
ba2b77a8 | 634 | |
8f7acdae | 635 | sb->s_magic = EROFS_SUPER_MAGIC; |
37c90c5f JX |
636 | sb->s_flags |= SB_RDONLY | SB_NOATIME; |
637 | sb->s_maxbytes = MAX_LFS_FILESIZE; | |
638 | sb->s_op = &erofs_sops; | |
8f7acdae | 639 | |
a9f69bd5 | 640 | sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); |
8d8a09b0 | 641 | if (!sbi) |
8f7acdae | 642 | return -ENOMEM; |
ba2b77a8 | 643 | |
8f7acdae | 644 | sb->s_fs_info = sbi; |
e6242465 | 645 | sbi->opt = ctx->opt; |
dfeab2e9 GX |
646 | sbi->devs = ctx->devs; |
647 | ctx->devs = NULL; | |
648 | ||
93b856bb JX |
649 | if (erofs_is_fscache_mode(sb)) { |
650 | sb->s_blocksize = EROFS_BLKSIZ; | |
651 | sb->s_blocksize_bits = LOG_BLOCK_SIZE; | |
c6be2bd0 JX |
652 | |
653 | err = erofs_fscache_register_fs(sb); | |
654 | if (err) | |
655 | return err; | |
37c90c5f JX |
656 | |
657 | err = erofs_fscache_register_cookie(sb, &sbi->s_fscache, | |
658 | sbi->opt.fsid, true); | |
659 | if (err) | |
660 | return err; | |
c665b394 JX |
661 | |
662 | err = super_setup_bdi(sb); | |
663 | if (err) | |
664 | return err; | |
93b856bb JX |
665 | } else { |
666 | if (!sb_set_blocksize(sb, EROFS_BLKSIZ)) { | |
667 | erofs_err(sb, "failed to set erofs blksize"); | |
668 | return -EINVAL; | |
669 | } | |
670 | ||
671 | sbi->dax_dev = fs_dax_get_by_bdev(sb->s_bdev, | |
672 | &sbi->dax_part_off); | |
673 | } | |
674 | ||
99634bf3 | 675 | err = erofs_read_superblock(sb); |
ba2b77a8 | 676 | if (err) |
8f7acdae | 677 | return err; |
ba2b77a8 | 678 | |
7b0800d0 CH |
679 | if (test_opt(&sbi->opt, DAX_ALWAYS)) { |
680 | BUILD_BUG_ON(EROFS_BLKSIZ != PAGE_SIZE); | |
681 | ||
682 | if (!sbi->dax_dev) { | |
683 | errorfc(fc, "DAX unsupported by block device. Turning off DAX."); | |
684 | clear_opt(&sbi->opt, DAX_ALWAYS); | |
685 | } | |
06252e9c | 686 | } |
ba2b77a8 | 687 | |
37c90c5f | 688 | sb->s_time_gran = 1; |
b17500a0 | 689 | sb->s_xattr = erofs_xattr_handlers; |
3e917cc3 | 690 | sb->s_export_op = &erofs_export_ops; |
e7cda1ee | 691 | |
e6242465 | 692 | if (test_opt(&sbi->opt, POSIX_ACL)) |
516c115c GX |
693 | sb->s_flags |= SB_POSIXACL; |
694 | else | |
695 | sb->s_flags &= ~SB_POSIXACL; | |
696 | ||
e7e9a307 | 697 | #ifdef CONFIG_EROFS_FS_ZIP |
64094a04 | 698 | xa_init(&sbi->managed_pslots); |
e7e9a307 GX |
699 | #endif |
700 | ||
ba2b77a8 GX |
701 | /* get the root inode */ |
702 | inode = erofs_iget(sb, ROOT_NID(sbi), true); | |
8f7acdae GX |
703 | if (IS_ERR(inode)) |
704 | return PTR_ERR(inode); | |
ba2b77a8 | 705 | |
8d8a09b0 | 706 | if (!S_ISDIR(inode->i_mode)) { |
4f761fa2 GX |
707 | erofs_err(sb, "rootino(nid %llu) is not a directory(i_mode %o)", |
708 | ROOT_NID(sbi), inode->i_mode); | |
94832d93 | 709 | iput(inode); |
8f7acdae | 710 | return -EINVAL; |
ba2b77a8 GX |
711 | } |
712 | ||
713 | sb->s_root = d_make_root(inode); | |
8d8a09b0 | 714 | if (!sb->s_root) |
8f7acdae | 715 | return -ENOMEM; |
ba2b77a8 | 716 | |
22fe04a7 | 717 | erofs_shrinker_register(sb); |
8f7acdae GX |
718 | /* sb->s_umount is already locked, SB_ACTIVE and SB_BORN are not set */ |
719 | err = erofs_init_managed_cache(sb); | |
8d8a09b0 | 720 | if (err) |
8f7acdae | 721 | return err; |
2497ee41 | 722 | |
168e9a76 HJ |
723 | err = erofs_register_sysfs(sb); |
724 | if (err) | |
725 | return err; | |
726 | ||
f57a3fe4 CY |
727 | erofs_info(sb, "mounted with root inode @ nid %llu.", ROOT_NID(sbi)); |
728 | return 0; | |
729 | } | |
730 | ||
731 | static int erofs_fc_get_tree(struct fs_context *fc) | |
732 | { | |
733 | return get_tree_bdev(fc, erofs_fc_fill_super); | |
734 | } | |
735 | ||
736 | static int erofs_fc_reconfigure(struct fs_context *fc) | |
737 | { | |
738 | struct super_block *sb = fc->root->d_sb; | |
739 | struct erofs_sb_info *sbi = EROFS_SB(sb); | |
740 | struct erofs_fs_context *ctx = fc->fs_private; | |
741 | ||
742 | DBG_BUGON(!sb_rdonly(sb)); | |
743 | ||
e6242465 | 744 | if (test_opt(&ctx->opt, POSIX_ACL)) |
f57a3fe4 CY |
745 | fc->sb_flags |= SB_POSIXACL; |
746 | else | |
747 | fc->sb_flags &= ~SB_POSIXACL; | |
748 | ||
e6242465 | 749 | sbi->opt = ctx->opt; |
f57a3fe4 CY |
750 | |
751 | fc->sb_flags |= SB_RDONLY; | |
ba2b77a8 | 752 | return 0; |
8f7acdae GX |
753 | } |
754 | ||
dfeab2e9 GX |
755 | static int erofs_release_device_info(int id, void *ptr, void *data) |
756 | { | |
757 | struct erofs_device_info *dif = ptr; | |
758 | ||
759 | fs_put_dax(dif->dax_dev); | |
760 | if (dif->bdev) | |
761 | blkdev_put(dif->bdev, FMODE_READ | FMODE_EXCL); | |
955b478e | 762 | erofs_fscache_unregister_cookie(&dif->fscache); |
dfeab2e9 GX |
763 | kfree(dif->path); |
764 | kfree(dif); | |
765 | return 0; | |
766 | } | |
767 | ||
768 | static void erofs_free_dev_context(struct erofs_dev_context *devs) | |
769 | { | |
770 | if (!devs) | |
771 | return; | |
772 | idr_for_each(&devs->tree, &erofs_release_device_info, NULL); | |
773 | idr_destroy(&devs->tree); | |
774 | kfree(devs); | |
775 | } | |
776 | ||
f57a3fe4 CY |
777 | static void erofs_fc_free(struct fs_context *fc) |
778 | { | |
dfeab2e9 GX |
779 | struct erofs_fs_context *ctx = fc->fs_private; |
780 | ||
781 | erofs_free_dev_context(ctx->devs); | |
782 | kfree(ctx); | |
f57a3fe4 CY |
783 | } |
784 | ||
785 | static const struct fs_context_operations erofs_context_ops = { | |
786 | .parse_param = erofs_fc_parse_param, | |
787 | .get_tree = erofs_fc_get_tree, | |
788 | .reconfigure = erofs_fc_reconfigure, | |
789 | .free = erofs_fc_free, | |
790 | }; | |
791 | ||
792 | static int erofs_init_fs_context(struct fs_context *fc) | |
8f7acdae | 793 | { |
dfeab2e9 | 794 | struct erofs_fs_context *ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); |
f57a3fe4 | 795 | |
dfeab2e9 GX |
796 | if (!ctx) |
797 | return -ENOMEM; | |
798 | ctx->devs = kzalloc(sizeof(struct erofs_dev_context), GFP_KERNEL); | |
799 | if (!ctx->devs) { | |
800 | kfree(ctx); | |
801 | return -ENOMEM; | |
802 | } | |
803 | fc->fs_private = ctx; | |
f57a3fe4 | 804 | |
dfeab2e9 GX |
805 | idr_init(&ctx->devs->tree); |
806 | init_rwsem(&ctx->devs->rwsem); | |
807 | erofs_default_options(ctx); | |
f57a3fe4 | 808 | fc->ops = &erofs_context_ops; |
f57a3fe4 | 809 | return 0; |
ba2b77a8 GX |
810 | } |
811 | ||
812 | /* | |
813 | * could be triggered after deactivate_locked_super() | |
814 | * is called, thus including umount and failed to initialize. | |
815 | */ | |
8f7acdae | 816 | static void erofs_kill_sb(struct super_block *sb) |
ba2b77a8 | 817 | { |
8f7acdae GX |
818 | struct erofs_sb_info *sbi; |
819 | ||
820 | WARN_ON(sb->s_magic != EROFS_SUPER_MAGIC); | |
ba2b77a8 | 821 | |
8f7acdae GX |
822 | kill_block_super(sb); |
823 | ||
824 | sbi = EROFS_SB(sb); | |
e2ff9f15 | 825 | if (!sbi) |
ba2b77a8 | 826 | return; |
dfeab2e9 GX |
827 | |
828 | erofs_free_dev_context(sbi->devs); | |
06252e9c | 829 | fs_put_dax(sbi->dax_dev); |
37c90c5f | 830 | erofs_fscache_unregister_cookie(&sbi->s_fscache); |
c6be2bd0 | 831 | erofs_fscache_unregister_fs(sb); |
8f7acdae GX |
832 | kfree(sbi); |
833 | sb->s_fs_info = NULL; | |
834 | } | |
ba2b77a8 | 835 | |
8f7acdae GX |
836 | /* called when ->s_root is non-NULL */ |
837 | static void erofs_put_super(struct super_block *sb) | |
838 | { | |
839 | struct erofs_sb_info *const sbi = EROFS_SB(sb); | |
ba2b77a8 | 840 | |
8f7acdae | 841 | DBG_BUGON(!sbi); |
ba2b77a8 | 842 | |
168e9a76 | 843 | erofs_unregister_sysfs(sb); |
22fe04a7 | 844 | erofs_shrinker_unregister(sb); |
4279f3f9 | 845 | #ifdef CONFIG_EROFS_FS_ZIP |
105d4ad8 | 846 | iput(sbi->managed_cache); |
8f7acdae | 847 | sbi->managed_cache = NULL; |
105d4ad8 | 848 | #endif |
37c90c5f | 849 | erofs_fscache_unregister_cookie(&sbi->s_fscache); |
ba2b77a8 GX |
850 | } |
851 | ||
ba2b77a8 GX |
852 | static struct file_system_type erofs_fs_type = { |
853 | .owner = THIS_MODULE, | |
854 | .name = "erofs", | |
f57a3fe4 | 855 | .init_fs_context = erofs_init_fs_context, |
8f7acdae | 856 | .kill_sb = erofs_kill_sb, |
6c459b78 | 857 | .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP, |
ba2b77a8 GX |
858 | }; |
859 | MODULE_ALIAS_FS("erofs"); | |
860 | ||
861 | static int __init erofs_module_init(void) | |
862 | { | |
863 | int err; | |
864 | ||
865 | erofs_check_ondisk_layout_definitions(); | |
ba2b77a8 | 866 | |
1c2dfbf9 | 867 | erofs_inode_cachep = kmem_cache_create("erofs_inode", |
a5876e24 | 868 | sizeof(struct erofs_inode), 0, |
1c2dfbf9 | 869 | SLAB_RECLAIM_ACCOUNT, |
99634bf3 | 870 | erofs_inode_init_once); |
1c2dfbf9 GX |
871 | if (!erofs_inode_cachep) { |
872 | err = -ENOMEM; | |
ba2b77a8 | 873 | goto icache_err; |
1c2dfbf9 | 874 | } |
ba2b77a8 | 875 | |
22fe04a7 | 876 | err = erofs_init_shrinker(); |
a1581312 GX |
877 | if (err) |
878 | goto shrinker_err; | |
879 | ||
622ceadd GX |
880 | err = z_erofs_lzma_init(); |
881 | if (err) | |
882 | goto lzma_err; | |
883 | ||
52488734 | 884 | erofs_pcpubuf_init(); |
3883a79a GX |
885 | err = z_erofs_init_zip_subsystem(); |
886 | if (err) | |
887 | goto zip_err; | |
3883a79a | 888 | |
168e9a76 HJ |
889 | err = erofs_init_sysfs(); |
890 | if (err) | |
891 | goto sysfs_err; | |
892 | ||
ba2b77a8 GX |
893 | err = register_filesystem(&erofs_fs_type); |
894 | if (err) | |
895 | goto fs_err; | |
896 | ||
ba2b77a8 GX |
897 | return 0; |
898 | ||
899 | fs_err: | |
168e9a76 HJ |
900 | erofs_exit_sysfs(); |
901 | sysfs_err: | |
3883a79a GX |
902 | z_erofs_exit_zip_subsystem(); |
903 | zip_err: | |
622ceadd GX |
904 | z_erofs_lzma_exit(); |
905 | lzma_err: | |
22fe04a7 | 906 | erofs_exit_shrinker(); |
a1581312 | 907 | shrinker_err: |
1c2dfbf9 | 908 | kmem_cache_destroy(erofs_inode_cachep); |
ba2b77a8 GX |
909 | icache_err: |
910 | return err; | |
911 | } | |
912 | ||
913 | static void __exit erofs_module_exit(void) | |
914 | { | |
915 | unregister_filesystem(&erofs_fs_type); | |
1c2dfbf9 | 916 | |
622ceadd | 917 | /* Ensure all RCU free inodes / pclusters are safe to be destroyed. */ |
1c2dfbf9 | 918 | rcu_barrier(); |
622ceadd | 919 | |
168e9a76 | 920 | erofs_exit_sysfs(); |
622ceadd GX |
921 | z_erofs_exit_zip_subsystem(); |
922 | z_erofs_lzma_exit(); | |
923 | erofs_exit_shrinker(); | |
1c2dfbf9 | 924 | kmem_cache_destroy(erofs_inode_cachep); |
52488734 | 925 | erofs_pcpubuf_exit(); |
ba2b77a8 GX |
926 | } |
927 | ||
928 | /* get filesystem statistics */ | |
929 | static int erofs_statfs(struct dentry *dentry, struct kstatfs *buf) | |
930 | { | |
931 | struct super_block *sb = dentry->d_sb; | |
932 | struct erofs_sb_info *sbi = EROFS_SB(sb); | |
93b856bb JX |
933 | u64 id = 0; |
934 | ||
935 | if (!erofs_is_fscache_mode(sb)) | |
936 | id = huge_encode_dev(sb->s_bdev->bd_dev); | |
ba2b77a8 GX |
937 | |
938 | buf->f_type = sb->s_magic; | |
939 | buf->f_bsize = EROFS_BLKSIZ; | |
dfeab2e9 | 940 | buf->f_blocks = sbi->total_blocks; |
ba2b77a8 GX |
941 | buf->f_bfree = buf->f_bavail = 0; |
942 | ||
943 | buf->f_files = ULLONG_MAX; | |
944 | buf->f_ffree = ULLONG_MAX - sbi->inos; | |
945 | ||
946 | buf->f_namelen = EROFS_NAME_LEN; | |
947 | ||
6d1349c7 | 948 | buf->f_fsid = u64_to_fsid(id); |
ba2b77a8 GX |
949 | return 0; |
950 | } | |
951 | ||
952 | static int erofs_show_options(struct seq_file *seq, struct dentry *root) | |
953 | { | |
06252e9c | 954 | struct erofs_sb_info *sbi = EROFS_SB(root->d_sb); |
e6242465 | 955 | struct erofs_mount_opts *opt = &sbi->opt; |
b17500a0 GX |
956 | |
957 | #ifdef CONFIG_EROFS_FS_XATTR | |
e6242465 | 958 | if (test_opt(opt, XATTR_USER)) |
b17500a0 GX |
959 | seq_puts(seq, ",user_xattr"); |
960 | else | |
961 | seq_puts(seq, ",nouser_xattr"); | |
962 | #endif | |
963 | #ifdef CONFIG_EROFS_FS_POSIX_ACL | |
e6242465 | 964 | if (test_opt(opt, POSIX_ACL)) |
b17500a0 GX |
965 | seq_puts(seq, ",acl"); |
966 | else | |
967 | seq_puts(seq, ",noacl"); | |
9c07b3b3 | 968 | #endif |
4279f3f9 | 969 | #ifdef CONFIG_EROFS_FS_ZIP |
e6242465 | 970 | if (opt->cache_strategy == EROFS_ZIP_CACHE_DISABLED) |
4279f3f9 | 971 | seq_puts(seq, ",cache_strategy=disabled"); |
e6242465 | 972 | else if (opt->cache_strategy == EROFS_ZIP_CACHE_READAHEAD) |
4279f3f9 | 973 | seq_puts(seq, ",cache_strategy=readahead"); |
e6242465 | 974 | else if (opt->cache_strategy == EROFS_ZIP_CACHE_READAROUND) |
4279f3f9 | 975 | seq_puts(seq, ",cache_strategy=readaround"); |
4279f3f9 | 976 | #endif |
e6242465 | 977 | if (test_opt(opt, DAX_ALWAYS)) |
06252e9c | 978 | seq_puts(seq, ",dax=always"); |
e6242465 | 979 | if (test_opt(opt, DAX_NEVER)) |
06252e9c | 980 | seq_puts(seq, ",dax=never"); |
ba2b77a8 GX |
981 | return 0; |
982 | } | |
983 | ||
ba2b77a8 GX |
984 | const struct super_operations erofs_sops = { |
985 | .put_super = erofs_put_super, | |
99634bf3 GX |
986 | .alloc_inode = erofs_alloc_inode, |
987 | .free_inode = erofs_free_inode, | |
ba2b77a8 GX |
988 | .statfs = erofs_statfs, |
989 | .show_options = erofs_show_options, | |
ba2b77a8 GX |
990 | }; |
991 | ||
992 | module_init(erofs_module_init); | |
993 | module_exit(erofs_module_exit); | |
994 | ||
995 | MODULE_DESCRIPTION("Enhanced ROM File System"); | |
bc33d9f3 | 996 | MODULE_AUTHOR("Gao Xiang, Chao Yu, Miao Xie, CONSUMER BG, HUAWEI Inc."); |
ba2b77a8 | 997 | MODULE_LICENSE("GPL"); |