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