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