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> |
fb176750 | 13 | #include <linux/backing-dev.h> |
6af7b483 | 14 | #include "xattr.h" |
ba2b77a8 | 15 | |
13f06f48 CY |
16 | #define CREATE_TRACE_POINTS |
17 | #include <trace/events/erofs.h> | |
18 | ||
ba2b77a8 GX |
19 | static struct kmem_cache *erofs_inode_cachep __read_mostly; |
20 | ||
90655ee2 | 21 | void _erofs_printk(struct super_block *sb, const char *fmt, ...) |
4f761fa2 GX |
22 | { |
23 | struct va_format vaf; | |
24 | va_list args; | |
90655ee2 | 25 | int level; |
4f761fa2 GX |
26 | |
27 | va_start(args, fmt); | |
28 | ||
90655ee2 GH |
29 | level = printk_get_level(fmt); |
30 | vaf.fmt = printk_skip_level(fmt); | |
4f761fa2 | 31 | vaf.va = &args; |
aa12a790 | 32 | if (sb) |
90655ee2 GH |
33 | printk("%c%cerofs (device %s): %pV", |
34 | KERN_SOH_ASCII, level, sb->s_id, &vaf); | |
aa12a790 | 35 | else |
90655ee2 | 36 | printk("%c%cerofs: %pV", KERN_SOH_ASCII, level, &vaf); |
4f761fa2 GX |
37 | va_end(args); |
38 | } | |
39 | ||
b858a484 PS |
40 | static int erofs_superblock_csum_verify(struct super_block *sb, void *sbdata) |
41 | { | |
54ab25d0 GX |
42 | struct erofs_super_block *dsb = sbdata + EROFS_SUPER_OFFSET; |
43 | u32 len = 1 << EROFS_SB(sb)->blkszbits, crc; | |
b858a484 | 44 | |
3acea5fc JX |
45 | if (len > EROFS_SUPER_OFFSET) |
46 | len -= EROFS_SUPER_OFFSET; | |
54ab25d0 GX |
47 | len -= offsetof(struct erofs_super_block, checksum) + |
48 | sizeof(dsb->checksum); | |
3acea5fc | 49 | |
54ab25d0 GX |
50 | /* skip .magic(pre-verified) and .checksum(0) fields */ |
51 | crc = crc32c(0x5045B54A, (&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; | |
b858a484 PS |
57 | } |
58 | ||
99634bf3 | 59 | static void erofs_inode_init_once(void *ptr) |
ba2b77a8 | 60 | { |
a5876e24 | 61 | struct erofs_inode *vi = ptr; |
ba2b77a8 GX |
62 | |
63 | inode_init_once(&vi->vfs_inode); | |
64 | } | |
65 | ||
99634bf3 | 66 | static struct inode *erofs_alloc_inode(struct super_block *sb) |
ba2b77a8 | 67 | { |
a5876e24 | 68 | struct erofs_inode *vi = |
fd60b288 | 69 | alloc_inode_sb(sb, erofs_inode_cachep, GFP_KERNEL); |
ba2b77a8 | 70 | |
e2ff9f15 | 71 | if (!vi) |
ba2b77a8 GX |
72 | return NULL; |
73 | ||
74 | /* zero out everything except vfs_inode */ | |
a5876e24 | 75 | memset(vi, 0, offsetof(struct erofs_inode, vfs_inode)); |
ba2b77a8 GX |
76 | return &vi->vfs_inode; |
77 | } | |
78 | ||
99634bf3 | 79 | static void erofs_free_inode(struct inode *inode) |
ba2b77a8 | 80 | { |
a5876e24 | 81 | struct erofs_inode *vi = EROFS_I(inode); |
ba2b77a8 | 82 | |
a2c75c81 | 83 | if (inode->i_op == &erofs_fast_symlink_iops) |
ba2b77a8 | 84 | kfree(inode->i_link); |
ba2b77a8 | 85 | kfree(vi->xattr_shared_xattrs); |
ba2b77a8 GX |
86 | kmem_cache_free(erofs_inode_cachep, vi); |
87 | } | |
88 | ||
14373711 | 89 | /* read variable-sized metadata, offset will be aligned by 4-byte */ |
9e382914 JX |
90 | void *erofs_read_metadata(struct super_block *sb, struct erofs_buf *buf, |
91 | erofs_off_t *offset, int *lengthp) | |
14373711 | 92 | { |
14373711 GX |
93 | u8 *buffer, *ptr; |
94 | int len, i, cnt; | |
14373711 GX |
95 | |
96 | *offset = round_up(*offset, 4); | |
706e50e4 | 97 | ptr = erofs_bread(buf, *offset, true); |
2b5379f7 GX |
98 | if (IS_ERR(ptr)) |
99 | return ptr; | |
14373711 | 100 | |
469ad583 | 101 | len = le16_to_cpu(*(__le16 *)ptr); |
14373711 GX |
102 | if (!len) |
103 | len = U16_MAX + 1; | |
104 | buffer = kmalloc(len, GFP_KERNEL); | |
2b5379f7 GX |
105 | if (!buffer) |
106 | return ERR_PTR(-ENOMEM); | |
14373711 GX |
107 | *offset += sizeof(__le16); |
108 | *lengthp = len; | |
109 | ||
110 | for (i = 0; i < len; i += cnt) { | |
3acea5fc JX |
111 | cnt = min_t(int, sb->s_blocksize - erofs_blkoff(sb, *offset), |
112 | len - i); | |
706e50e4 | 113 | ptr = erofs_bread(buf, *offset, true); |
2b5379f7 GX |
114 | if (IS_ERR(ptr)) { |
115 | kfree(buffer); | |
116 | return ptr; | |
14373711 | 117 | } |
469ad583 | 118 | memcpy(buffer + i, ptr, cnt); |
14373711 GX |
119 | *offset += cnt; |
120 | } | |
14373711 | 121 | return buffer; |
14373711 GX |
122 | } |
123 | ||
efb4fb02 GX |
124 | #ifndef CONFIG_EROFS_FS_ZIP |
125 | static int z_erofs_parse_cfgs(struct super_block *sb, | |
126 | struct erofs_super_block *dsb) | |
14373711 | 127 | { |
efb4fb02 GX |
128 | if (!dsb->u1.available_compr_algs) |
129 | return 0; | |
14373711 | 130 | |
efb4fb02 GX |
131 | erofs_err(sb, "compression disabled, unable to mount compressed EROFS"); |
132 | return -EOPNOTSUPP; | |
14373711 GX |
133 | } |
134 | #endif | |
135 | ||
ba73eadd JX |
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); | |
e1de2da0 | 140 | struct erofs_fscache *fscache; |
ba73eadd | 141 | struct erofs_deviceslot *dis; |
fb176750 | 142 | struct file *file; |
ba73eadd | 143 | |
706e50e4 | 144 | dis = erofs_read_metabuf(buf, sb, *pos, true); |
076d965e AV |
145 | if (IS_ERR(dis)) |
146 | return PTR_ERR(dis); | |
ba73eadd | 147 | |
f939aeea | 148 | if (!sbi->devs->flatdev && !dif->path) { |
ba73eadd JX |
149 | if (!dis->tag[0]) { |
150 | erofs_err(sb, "empty device tag @ pos %llu", *pos); | |
151 | return -EINVAL; | |
152 | } | |
153 | dif->path = kmemdup_nul(dis->tag, sizeof(dis->tag), GFP_KERNEL); | |
154 | if (!dif->path) | |
155 | return -ENOMEM; | |
156 | } | |
157 | ||
158 | if (erofs_is_fscache_mode(sb)) { | |
27f2a2dc | 159 | fscache = erofs_fscache_register_cookie(sb, dif->path, 0); |
e1de2da0 JZ |
160 | if (IS_ERR(fscache)) |
161 | return PTR_ERR(fscache); | |
162 | dif->fscache = fscache; | |
8b465fec | 163 | } else if (!sbi->devs->flatdev) { |
fb176750 GX |
164 | file = erofs_is_fileio_mode(sbi) ? |
165 | filp_open(dif->path, O_RDONLY | O_LARGEFILE, 0) : | |
166 | bdev_file_open_by_path(dif->path, | |
167 | BLK_OPEN_READ, sb->s_type, NULL); | |
9748f2f5 SY |
168 | if (IS_ERR(file)) { |
169 | if (file == ERR_PTR(-ENOTBLK)) | |
170 | return -EINVAL; | |
fb176750 | 171 | return PTR_ERR(file); |
9748f2f5 | 172 | } |
fb176750 | 173 | |
416a8b2c | 174 | if (!erofs_is_fileio_mode(sbi)) { |
fb176750 GX |
175 | dif->dax_dev = fs_dax_get_by_bdev(file_bdev(file), |
176 | &dif->dax_part_off, NULL, NULL); | |
416a8b2c GX |
177 | } else if (!S_ISREG(file_inode(file)->i_mode)) { |
178 | fput(file); | |
179 | return -EINVAL; | |
180 | } | |
181 | dif->file = file; | |
ba73eadd JX |
182 | } |
183 | ||
61ba89b5 GX |
184 | dif->blocks = le32_to_cpu(dis->blocks_lo); |
185 | dif->uniaddr = le32_to_cpu(dis->uniaddr_lo); | |
ba73eadd JX |
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, | |
dfeab2e9 GX |
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; | |
2b5379f7 | 197 | struct erofs_buf buf = __EROFS_BUF_INITIALIZER; |
dfeab2e9 | 198 | struct erofs_device_info *dif; |
dfeab2e9 GX |
199 | int id, err = 0; |
200 | ||
7b00af2c | 201 | sbi->total_blocks = sbi->dif0.blocks; |
dfeab2e9 GX |
202 | if (!erofs_sb_has_device_table(sbi)) |
203 | ondisk_extradevs = 0; | |
204 | else | |
205 | ondisk_extradevs = le16_to_cpu(dsb->extra_devices); | |
206 | ||
ba73eadd JX |
207 | if (sbi->devs->extra_devices && |
208 | ondisk_extradevs != sbi->devs->extra_devices) { | |
dfeab2e9 GX |
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 | ||
8b465fec JZ |
216 | if (!sbi->devs->extra_devices && !erofs_is_fscache_mode(sb)) |
217 | sbi->devs->flatdev = true; | |
218 | ||
dfeab2e9 GX |
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(&sbi->devs->rwsem); | |
ba73eadd JX |
222 | if (sbi->devs->extra_devices) { |
223 | idr_for_each_entry(&sbi->devs->tree, dif, id) { | |
224 | err = erofs_init_device(&buf, sb, dif, &pos); | |
955b478e JX |
225 | if (err) |
226 | break; | |
ba73eadd JX |
227 | } |
228 | } else { | |
229 | for (id = 0; id < ondisk_extradevs; id++) { | |
230 | dif = kzalloc(sizeof(*dif), GFP_KERNEL); | |
231 | if (!dif) { | |
232 | err = -ENOMEM; | |
93b856bb JX |
233 | break; |
234 | } | |
93b856bb | 235 | |
ba73eadd JX |
236 | err = idr_alloc(&sbi->devs->tree, dif, 0, 0, GFP_KERNEL); |
237 | if (err < 0) { | |
238 | kfree(dif); | |
239 | break; | |
240 | } | |
241 | ++sbi->devs->extra_devices; | |
242 | ||
243 | err = erofs_init_device(&buf, sb, dif, &pos); | |
244 | if (err) | |
245 | break; | |
246 | } | |
dfeab2e9 | 247 | } |
dfeab2e9 | 248 | up_read(&sbi->devs->rwsem); |
2b5379f7 | 249 | erofs_put_metabuf(&buf); |
dfeab2e9 GX |
250 | return err; |
251 | } | |
252 | ||
99634bf3 | 253 | static int erofs_read_superblock(struct super_block *sb) |
ba2b77a8 | 254 | { |
2c534624 | 255 | struct erofs_sb_info *sbi = EROFS_SB(sb); |
ed6e0401 | 256 | struct erofs_buf buf = __EROFS_BUF_INITIALIZER; |
0259f209 | 257 | struct erofs_super_block *dsb; |
fe7c2423 | 258 | void *data; |
ba2b77a8 GX |
259 | int ret; |
260 | ||
706e50e4 | 261 | data = erofs_read_metabuf(&buf, sb, 0, true); |
ed6e0401 | 262 | if (IS_ERR(data)) { |
4f761fa2 | 263 | erofs_err(sb, "cannot read erofs superblock"); |
ed6e0401 | 264 | return PTR_ERR(data); |
ba2b77a8 GX |
265 | } |
266 | ||
fe7c2423 | 267 | dsb = (struct erofs_super_block *)(data + EROFS_SUPER_OFFSET); |
ba2b77a8 | 268 | ret = -EINVAL; |
0259f209 | 269 | if (le32_to_cpu(dsb->magic) != EROFS_SUPER_MAGIC_V1) { |
4f761fa2 | 270 | erofs_err(sb, "cannot find valid erofs superblock"); |
ba2b77a8 GX |
271 | goto out; |
272 | } | |
273 | ||
2e1473d5 | 274 | sbi->blkszbits = dsb->blkszbits; |
d3c4bdcc JX |
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 | ||
b858a484 | 284 | sbi->feature_compat = le32_to_cpu(dsb->feature_compat); |
de06a6a3 | 285 | if (erofs_sb_has_sb_chksum(sbi)) { |
b858a484 PS |
286 | ret = erofs_superblock_csum_verify(sb, data); |
287 | if (ret) | |
288 | goto out; | |
289 | } | |
290 | ||
0508c1ad | 291 | ret = -EINVAL; |
2c534624 HL |
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); | |
5efe5137 | 296 | goto out; |
2c534624 | 297 | } |
5efe5137 | 298 | |
14373711 | 299 | sbi->sb_size = 128 + dsb->sb_extslots * EROFS_SB_EXTSLOT_SIZE; |
d3c4bdcc | 300 | if (sbi->sb_size > PAGE_SIZE - EROFS_SUPER_OFFSET) { |
14373711 GX |
301 | erofs_err(sb, "invalid sb_extslots %u (more than a fs block)", |
302 | sbi->sb_size); | |
303 | goto out; | |
304 | } | |
61ba89b5 | 305 | sbi->dif0.blocks = le32_to_cpu(dsb->blocks_lo); |
0259f209 | 306 | sbi->meta_blkaddr = le32_to_cpu(dsb->meta_blkaddr); |
b17500a0 | 307 | #ifdef CONFIG_EROFS_FS_XATTR |
0259f209 | 308 | sbi->xattr_blkaddr = le32_to_cpu(dsb->xattr_blkaddr); |
6a318ccd JX |
309 | sbi->xattr_prefix_start = le32_to_cpu(dsb->xattr_prefix_start); |
310 | sbi->xattr_prefix_count = dsb->xattr_prefix_count; | |
fd73a439 | 311 | sbi->xattr_filter_reserved = dsb->xattr_filter_reserved; |
b17500a0 | 312 | #endif |
8a765682 | 313 | sbi->islotbits = ilog2(sizeof(struct erofs_inode_compact)); |
2e1473d5 GX |
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 | } | |
cb9bce79 | 321 | sbi->packed_nid = le64_to_cpu(dsb->packed_nid); |
0259f209 | 322 | sbi->inos = le64_to_cpu(dsb->inos); |
ba2b77a8 | 323 | |
2e1473d5 GX |
324 | sbi->epoch = (s64)le64_to_cpu(dsb->epoch); |
325 | sbi->fixed_nsec = le32_to_cpu(dsb->fixed_nsec); | |
cc69a681 | 326 | super_set_uuid(sb, (void *)dsb->uuid, sizeof(dsb->uuid)); |
ba2b77a8 | 327 | |
5d50538f | 328 | /* parse on-disk compression configurations */ |
efb4fb02 | 329 | ret = z_erofs_parse_cfgs(sb, dsb); |
dfeab2e9 GX |
330 | if (ret < 0) |
331 | goto out; | |
332 | ||
333 | /* handle multiple devices */ | |
ba73eadd | 334 | ret = erofs_scan_devices(sb, dsb); |
ab92184f | 335 | |
0f24e3c0 GX |
336 | if (erofs_sb_has_48bit(sbi)) |
337 | erofs_info(sb, "EXPERIMENTAL 48-bit layout support in use. Use at your own risk!"); | |
9c0cc9c7 | 338 | if (erofs_is_fscache_mode(sb)) |
0d442ce0 | 339 | erofs_info(sb, "[deprecated] fscache-based on-demand read feature in use. Use at your own risk!"); |
ba2b77a8 | 340 | out: |
ed6e0401 | 341 | erofs_put_metabuf(&buf); |
ba2b77a8 GX |
342 | return ret; |
343 | } | |
344 | ||
07abe43a | 345 | static void erofs_default_options(struct erofs_sb_info *sbi) |
ba2b77a8 | 346 | { |
5fb76bb0 | 347 | #ifdef CONFIG_EROFS_FS_ZIP |
07abe43a BL |
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; | |
5fb76bb0 | 351 | #endif |
b17500a0 | 352 | #ifdef CONFIG_EROFS_FS_XATTR |
07abe43a | 353 | set_opt(&sbi->opt, XATTR_USER); |
b17500a0 | 354 | #endif |
b17500a0 | 355 | #ifdef CONFIG_EROFS_FS_POSIX_ACL |
07abe43a | 356 | set_opt(&sbi->opt, POSIX_ACL); |
b17500a0 | 357 | #endif |
ba2b77a8 GX |
358 | } |
359 | ||
360 | enum { | |
6422cde1 | 361 | Opt_user_xattr, Opt_acl, Opt_cache_strategy, Opt_dax, Opt_dax_enum, |
c36ec00d | 362 | Opt_device, Opt_fsid, Opt_domain_id, Opt_directio, Opt_fsoffset, |
ba2b77a8 GX |
363 | }; |
364 | ||
f57a3fe4 CY |
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 | {} | |
ba2b77a8 GX |
370 | }; |
371 | ||
06252e9c GX |
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 | ||
f57a3fe4 CY |
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), | |
06252e9c GX |
383 | fsparam_flag("dax", Opt_dax), |
384 | fsparam_enum("dax", Opt_dax_enum, erofs_dax_param_enums), | |
dfeab2e9 | 385 | fsparam_string("device", Opt_device), |
9c0cc9c7 | 386 | fsparam_string("fsid", Opt_fsid), |
2ef16441 | 387 | fsparam_string("domain_id", Opt_domain_id), |
6422cde1 | 388 | fsparam_flag_no("directio", Opt_directio), |
c36ec00d | 389 | fsparam_u64("fsoffset", Opt_fsoffset), |
f57a3fe4 CY |
390 | {} |
391 | }; | |
ba2b77a8 | 392 | |
06252e9c GX |
393 | static bool erofs_fc_set_dax_mode(struct fs_context *fc, unsigned int mode) |
394 | { | |
395 | #ifdef CONFIG_FS_DAX | |
07abe43a | 396 | struct erofs_sb_info *sbi = fc->s_fs_info; |
06252e9c GX |
397 | |
398 | switch (mode) { | |
399 | case EROFS_MOUNT_DAX_ALWAYS: | |
07abe43a BL |
400 | set_opt(&sbi->opt, DAX_ALWAYS); |
401 | clear_opt(&sbi->opt, DAX_NEVER); | |
06252e9c GX |
402 | return true; |
403 | case EROFS_MOUNT_DAX_NEVER: | |
07abe43a BL |
404 | set_opt(&sbi->opt, DAX_NEVER); |
405 | clear_opt(&sbi->opt, DAX_ALWAYS); | |
06252e9c GX |
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 | ||
f57a3fe4 CY |
417 | static int erofs_fc_parse_param(struct fs_context *fc, |
418 | struct fs_parameter *param) | |
419 | { | |
07abe43a | 420 | struct erofs_sb_info *sbi = fc->s_fs_info; |
f57a3fe4 | 421 | struct fs_parse_result result; |
dfeab2e9 GX |
422 | struct erofs_device_info *dif; |
423 | int opt, ret; | |
ba2b77a8 | 424 | |
f57a3fe4 CY |
425 | opt = fs_parse(fc, erofs_fs_parameters, param, &result); |
426 | if (opt < 0) | |
427 | return opt; | |
ba2b77a8 | 428 | |
f57a3fe4 CY |
429 | switch (opt) { |
430 | case Opt_user_xattr: | |
b17500a0 | 431 | #ifdef CONFIG_EROFS_FS_XATTR |
f57a3fe4 | 432 | if (result.boolean) |
07abe43a | 433 | set_opt(&sbi->opt, XATTR_USER); |
f57a3fe4 | 434 | else |
07abe43a | 435 | clear_opt(&sbi->opt, XATTR_USER); |
b17500a0 | 436 | #else |
f57a3fe4 | 437 | errorfc(fc, "{,no}user_xattr options not supported"); |
b17500a0 | 438 | #endif |
f57a3fe4 CY |
439 | break; |
440 | case Opt_acl: | |
b17500a0 | 441 | #ifdef CONFIG_EROFS_FS_POSIX_ACL |
f57a3fe4 | 442 | if (result.boolean) |
07abe43a | 443 | set_opt(&sbi->opt, POSIX_ACL); |
f57a3fe4 | 444 | else |
07abe43a | 445 | clear_opt(&sbi->opt, POSIX_ACL); |
b17500a0 | 446 | #else |
f57a3fe4 | 447 | errorfc(fc, "{,no}acl options not supported"); |
b17500a0 | 448 | #endif |
f57a3fe4 CY |
449 | break; |
450 | case Opt_cache_strategy: | |
451 | #ifdef CONFIG_EROFS_FS_ZIP | |
07abe43a | 452 | sbi->opt.cache_strategy = result.uint_32; |
f57a3fe4 CY |
453 | #else |
454 | errorfc(fc, "compression not supported, cache_strategy ignored"); | |
455 | #endif | |
456 | break; | |
06252e9c GX |
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, result.uint_32)) | |
463 | return -EINVAL; | |
464 | break; | |
dfeab2e9 GX |
465 | case Opt_device: |
466 | dif = kzalloc(sizeof(*dif), GFP_KERNEL); | |
467 | if (!dif) | |
468 | return -ENOMEM; | |
469 | dif->path = kstrdup(param->string, GFP_KERNEL); | |
470 | if (!dif->path) { | |
471 | kfree(dif); | |
472 | return -ENOMEM; | |
473 | } | |
07abe43a BL |
474 | down_write(&sbi->devs->rwsem); |
475 | ret = idr_alloc(&sbi->devs->tree, dif, 0, 0, GFP_KERNEL); | |
476 | up_write(&sbi->devs->rwsem); | |
dfeab2e9 GX |
477 | if (ret < 0) { |
478 | kfree(dif->path); | |
479 | kfree(dif); | |
480 | return ret; | |
481 | } | |
07abe43a | 482 | ++sbi->devs->extra_devices; |
dfeab2e9 | 483 | break; |
9c0cc9c7 | 484 | #ifdef CONFIG_EROFS_FS_ONDEMAND |
e02ac3e7 | 485 | case Opt_fsid: |
07abe43a BL |
486 | kfree(sbi->fsid); |
487 | sbi->fsid = kstrdup(param->string, GFP_KERNEL); | |
488 | if (!sbi->fsid) | |
9c0cc9c7 | 489 | return -ENOMEM; |
2ef16441 JZ |
490 | break; |
491 | case Opt_domain_id: | |
07abe43a BL |
492 | kfree(sbi->domain_id); |
493 | sbi->domain_id = kstrdup(param->string, GFP_KERNEL); | |
494 | if (!sbi->domain_id) | |
2ef16441 | 495 | return -ENOMEM; |
e02ac3e7 | 496 | break; |
2ef16441 | 497 | #else |
e02ac3e7 JX |
498 | case Opt_fsid: |
499 | case Opt_domain_id: | |
500 | errorfc(fc, "%s option not supported", erofs_fs_parameters[opt].name); | |
9c0cc9c7 | 501 | break; |
e02ac3e7 | 502 | #endif |
6422cde1 GX |
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; | |
c36ec00d SY |
513 | case Opt_fsoffset: |
514 | sbi->dif0.fsoff = result.uint_64; | |
515 | break; | |
ba2b77a8 GX |
516 | } |
517 | return 0; | |
518 | } | |
519 | ||
510de836 HL |
520 | static int erofs_encode_fh(struct inode *inode, u32 *fh, int *max_len, |
521 | struct inode *parent) | |
3e917cc3 | 522 | { |
510de836 HL |
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; | |
3e917cc3 HL |
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 | { | |
510de836 HL |
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 | ((u64)fid->raw[0] << 32) | fid->raw[1])); | |
3e917cc3 HL |
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 | { | |
510de836 HL |
561 | if (fh_type != FILEID_INO64_GEN_PARENT || fh_len < 6) |
562 | return NULL; | |
563 | ||
564 | return d_obtain_alias(erofs_iget(sb, | |
565 | ((u64)fid->raw[3] << 32) | fid->raw[4])); | |
3e917cc3 HL |
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(d_inode(child), &dotdot_name, &nid, &d_type); | |
575 | if (err) | |
576 | return ERR_PTR(err); | |
312fe643 | 577 | return d_obtain_alias(erofs_iget(child->d_sb, nid)); |
3e917cc3 HL |
578 | } |
579 | ||
580 | static const struct export_operations erofs_export_ops = { | |
510de836 | 581 | .encode_fh = erofs_encode_fh, |
3e917cc3 HL |
582 | .fh_to_dentry = erofs_fh_to_dentry, |
583 | .fh_to_parent = erofs_fh_to_parent, | |
584 | .get_parent = erofs_get_parent, | |
585 | }; | |
586 | ||
684b290a HX |
587 | static void erofs_set_sysfs_name(struct super_block *sb) |
588 | { | |
589 | struct erofs_sb_info *sbi = EROFS_SB(sb); | |
590 | ||
fb176750 GX |
591 | if (sbi->domain_id) |
592 | super_set_sysfs_name_generic(sb, "%s,%s", sbi->domain_id, | |
593 | sbi->fsid); | |
594 | else if (sbi->fsid) | |
595 | super_set_sysfs_name_generic(sb, "%s", sbi->fsid); | |
596 | else if (erofs_is_fileio_mode(sbi)) | |
597 | super_set_sysfs_name_generic(sb, "%s", | |
598 | bdi_dev_name(sb->s_bdi)); | |
599 | else | |
600 | super_set_sysfs_name_id(sb); | |
684b290a HX |
601 | } |
602 | ||
f57a3fe4 | 603 | static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc) |
ba2b77a8 GX |
604 | { |
605 | struct inode *inode; | |
07abe43a | 606 | struct erofs_sb_info *sbi = EROFS_SB(sb); |
8f7acdae | 607 | int err; |
ba2b77a8 | 608 | |
8f7acdae | 609 | sb->s_magic = EROFS_SUPER_MAGIC; |
37c90c5f JX |
610 | sb->s_flags |= SB_RDONLY | SB_NOATIME; |
611 | sb->s_maxbytes = MAX_LFS_FILESIZE; | |
612 | sb->s_op = &erofs_sops; | |
8f7acdae | 613 | |
3acea5fc | 614 | sbi->blkszbits = PAGE_SHIFT; |
fb176750 | 615 | if (!sb->s_bdev) { |
d3c4bdcc JX |
616 | sb->s_blocksize = PAGE_SIZE; |
617 | sb->s_blocksize_bits = PAGE_SHIFT; | |
c6be2bd0 | 618 | |
fb176750 GX |
619 | if (erofs_is_fscache_mode(sb)) { |
620 | err = erofs_fscache_register_fs(sb); | |
621 | if (err) | |
622 | return err; | |
623 | } | |
c665b394 JX |
624 | err = super_setup_bdi(sb); |
625 | if (err) | |
626 | return err; | |
93b856bb | 627 | } else { |
d3c4bdcc JX |
628 | if (!sb_set_blocksize(sb, PAGE_SIZE)) { |
629 | errorfc(fc, "failed to set initial blksize"); | |
93b856bb JX |
630 | return -EINVAL; |
631 | } | |
632 | ||
7b00af2c GX |
633 | sbi->dif0.dax_dev = fs_dax_get_by_bdev(sb->s_bdev, |
634 | &sbi->dif0.dax_part_off, NULL, NULL); | |
93b856bb JX |
635 | } |
636 | ||
99634bf3 | 637 | err = erofs_read_superblock(sb); |
ba2b77a8 | 638 | if (err) |
8f7acdae | 639 | return err; |
ba2b77a8 | 640 | |
d3c4bdcc JX |
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 | } | |
bae08541 HL |
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)) { | |
d3c4bdcc JX |
651 | errorfc(fc, "failed to set erofs blksize"); |
652 | return -EINVAL; | |
653 | } | |
654 | } | |
7b0800d0 | 655 | |
c36ec00d SY |
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 | ||
d3c4bdcc | 664 | if (test_opt(&sbi->opt, DAX_ALWAYS)) { |
7b00af2c | 665 | if (!sbi->dif0.dax_dev) { |
7b0800d0 CH |
666 | errorfc(fc, "DAX unsupported by block device. Turning off DAX."); |
667 | clear_opt(&sbi->opt, DAX_ALWAYS); | |
d3c4bdcc JX |
668 | } else if (sbi->blkszbits != PAGE_SHIFT) { |
669 | errorfc(fc, "unsupported blocksize for DAX"); | |
670 | clear_opt(&sbi->opt, DAX_ALWAYS); | |
7b0800d0 | 671 | } |
06252e9c | 672 | } |
ba2b77a8 | 673 | |
37c90c5f | 674 | sb->s_time_gran = 1; |
b17500a0 | 675 | sb->s_xattr = erofs_xattr_handlers; |
3e917cc3 | 676 | sb->s_export_op = &erofs_export_ops; |
e7cda1ee | 677 | |
e6242465 | 678 | if (test_opt(&sbi->opt, POSIX_ACL)) |
516c115c GX |
679 | sb->s_flags |= SB_POSIXACL; |
680 | else | |
681 | sb->s_flags &= ~SB_POSIXACL; | |
682 | ||
fe1e57d4 GX |
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, sbi->packed_nid); | |
689 | if (IS_ERR(inode)) | |
690 | return PTR_ERR(inode); | |
691 | sbi->packed_inode = inode; | |
692 | } | |
e7e9a307 | 693 | |
6b8a113c | 694 | inode = erofs_iget(sb, sbi->root_nid); |
8f7acdae GX |
695 | if (IS_ERR(inode)) |
696 | return PTR_ERR(inode); | |
ba2b77a8 | 697 | |
8d8a09b0 | 698 | if (!S_ISDIR(inode->i_mode)) { |
4f761fa2 | 699 | erofs_err(sb, "rootino(nid %llu) is not a directory(i_mode %o)", |
6b8a113c | 700 | sbi->root_nid, inode->i_mode); |
94832d93 | 701 | iput(inode); |
8f7acdae | 702 | return -EINVAL; |
ba2b77a8 | 703 | } |
ba2b77a8 | 704 | sb->s_root = d_make_root(inode); |
8d8a09b0 | 705 | if (!sb->s_root) |
8f7acdae | 706 | return -ENOMEM; |
ba2b77a8 | 707 | |
22fe04a7 | 708 | erofs_shrinker_register(sb); |
6a318ccd JX |
709 | err = erofs_xattr_prefixes_init(sb); |
710 | if (err) | |
711 | return err; | |
712 | ||
59aadaa7 | 713 | erofs_set_sysfs_name(sb); |
168e9a76 HJ |
714 | err = erofs_register_sysfs(sb); |
715 | if (err) | |
716 | return err; | |
717 | ||
6b8a113c | 718 | erofs_info(sb, "mounted with root inode @ nid %llu.", sbi->root_nid); |
f57a3fe4 CY |
719 | return 0; |
720 | } | |
721 | ||
722 | static int erofs_fc_get_tree(struct fs_context *fc) | |
723 | { | |
07abe43a | 724 | struct erofs_sb_info *sbi = fc->s_fs_info; |
fb176750 | 725 | int ret; |
9c0cc9c7 | 726 | |
07abe43a | 727 | if (IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && sbi->fsid) |
9c0cc9c7 JX |
728 | return get_tree_nodev(fc, erofs_fc_fill_super); |
729 | ||
14c2d972 GX |
730 | ret = get_tree_bdev_flags(fc, erofs_fc_fill_super, |
731 | IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE) ? | |
732 | GET_TREE_BDEV_QUIET_LOOKUP : 0); | |
fb176750 GX |
733 | #ifdef CONFIG_EROFS_FS_BACKED_BY_FILE |
734 | if (ret == -ENOTBLK) { | |
e2de3c1b GX |
735 | struct file *file; |
736 | ||
fb176750 GX |
737 | if (!fc->source) |
738 | return invalf(fc, "No source specified"); | |
e2de3c1b GX |
739 | file = filp_open(fc->source, O_RDONLY | O_LARGEFILE, 0); |
740 | if (IS_ERR(file)) | |
741 | return PTR_ERR(file); | |
7b00af2c | 742 | sbi->dif0.file = file; |
fb176750 | 743 | |
7b00af2c GX |
744 | if (S_ISREG(file_inode(sbi->dif0.file)->i_mode) && |
745 | sbi->dif0.file->f_mapping->a_ops->read_folio) | |
416a8b2c | 746 | return get_tree_nodev(fc, erofs_fc_fill_super); |
fb176750 GX |
747 | } |
748 | #endif | |
749 | return ret; | |
f57a3fe4 CY |
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); | |
07abe43a | 756 | struct erofs_sb_info *new_sbi = fc->s_fs_info; |
f57a3fe4 CY |
757 | |
758 | DBG_BUGON(!sb_rdonly(sb)); | |
759 | ||
07abe43a | 760 | if (new_sbi->fsid || new_sbi->domain_id) |
39bfcb81 JX |
761 | erofs_info(sb, "ignoring reconfiguration for fsid|domain_id."); |
762 | ||
07abe43a | 763 | if (test_opt(&new_sbi->opt, POSIX_ACL)) |
f57a3fe4 CY |
764 | fc->sb_flags |= SB_POSIXACL; |
765 | else | |
766 | fc->sb_flags &= ~SB_POSIXACL; | |
767 | ||
07abe43a | 768 | sbi->opt = new_sbi->opt; |
f57a3fe4 CY |
769 | |
770 | fc->sb_flags |= SB_RDONLY; | |
ba2b77a8 | 771 | return 0; |
8f7acdae GX |
772 | } |
773 | ||
dfeab2e9 GX |
774 | static int erofs_release_device_info(int id, void *ptr, void *data) |
775 | { | |
776 | struct erofs_device_info *dif = ptr; | |
777 | ||
8012b866 | 778 | fs_put_dax(dif->dax_dev, NULL); |
fb176750 GX |
779 | if (dif->file) |
780 | fput(dif->file); | |
e1de2da0 JZ |
781 | erofs_fscache_unregister_cookie(dif->fscache); |
782 | dif->fscache = NULL; | |
dfeab2e9 GX |
783 | kfree(dif->path); |
784 | kfree(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, &erofs_release_device_info, NULL); | |
793 | idr_destroy(&devs->tree); | |
794 | kfree(devs); | |
795 | } | |
796 | ||
e2de3c1b | 797 | static void erofs_sb_free(struct erofs_sb_info *sbi) |
f57a3fe4 | 798 | { |
07abe43a BL |
799 | erofs_free_dev_context(sbi->devs); |
800 | kfree(sbi->fsid); | |
801 | kfree(sbi->domain_id); | |
7b00af2c GX |
802 | if (sbi->dif0.file) |
803 | fput(sbi->dif0.file); | |
07abe43a | 804 | kfree(sbi); |
f57a3fe4 CY |
805 | } |
806 | ||
e2de3c1b GX |
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 | ||
f57a3fe4 CY |
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) | |
8f7acdae | 823 | { |
07abe43a | 824 | struct erofs_sb_info *sbi; |
a9849560 | 825 | |
07abe43a BL |
826 | sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); |
827 | if (!sbi) | |
dfeab2e9 | 828 | return -ENOMEM; |
07abe43a BL |
829 | |
830 | sbi->devs = kzalloc(sizeof(struct erofs_dev_context), GFP_KERNEL); | |
831 | if (!sbi->devs) { | |
832 | kfree(sbi); | |
dfeab2e9 GX |
833 | return -ENOMEM; |
834 | } | |
07abe43a | 835 | fc->s_fs_info = sbi; |
f57a3fe4 | 836 | |
07abe43a BL |
837 | idr_init(&sbi->devs->tree); |
838 | init_rwsem(&sbi->devs->rwsem); | |
839 | erofs_default_options(sbi); | |
f57a3fe4 | 840 | fc->ops = &erofs_context_ops; |
f57a3fe4 | 841 | return 0; |
ba2b77a8 GX |
842 | } |
843 | ||
fe1e57d4 GX |
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 | ||
8f7acdae | 854 | static void erofs_kill_sb(struct super_block *sb) |
ba2b77a8 | 855 | { |
7af2ae1b | 856 | struct erofs_sb_info *sbi = EROFS_SB(sb); |
8f7acdae | 857 | |
7b00af2c GX |
858 | if ((IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && sbi->fsid) || |
859 | sbi->dif0.file) | |
1015c101 | 860 | kill_anon_super(sb); |
9c0cc9c7 JX |
861 | else |
862 | kill_block_super(sb); | |
fe1e57d4 | 863 | erofs_drop_internal_inodes(sbi); |
7b00af2c | 864 | fs_put_dax(sbi->dif0.dax_dev, NULL); |
c6be2bd0 | 865 | erofs_fscache_unregister_fs(sb); |
e2de3c1b | 866 | erofs_sb_free(sbi); |
8f7acdae GX |
867 | sb->s_fs_info = NULL; |
868 | } | |
ba2b77a8 | 869 | |
8f7acdae GX |
870 | static void erofs_put_super(struct super_block *sb) |
871 | { | |
872 | struct erofs_sb_info *const sbi = EROFS_SB(sb); | |
ba2b77a8 | 873 | |
168e9a76 | 874 | erofs_unregister_sysfs(sb); |
22fe04a7 | 875 | erofs_shrinker_unregister(sb); |
6a318ccd | 876 | erofs_xattr_prefixes_cleanup(sb); |
fe1e57d4 | 877 | erofs_drop_internal_inodes(sbi); |
61fef989 JX |
878 | erofs_free_dev_context(sbi->devs); |
879 | sbi->devs = NULL; | |
e1de2da0 | 880 | erofs_fscache_unregister_fs(sb); |
ba2b77a8 GX |
881 | } |
882 | ||
0f28be64 | 883 | static struct file_system_type erofs_fs_type = { |
ba2b77a8 GX |
884 | .owner = THIS_MODULE, |
885 | .name = "erofs", | |
f57a3fe4 | 886 | .init_fs_context = erofs_init_fs_context, |
8f7acdae | 887 | .kill_sb = erofs_kill_sb, |
6c459b78 | 888 | .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP, |
ba2b77a8 GX |
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(); | |
ba2b77a8 | 897 | |
1c2dfbf9 | 898 | erofs_inode_cachep = kmem_cache_create("erofs_inode", |
8ec9a252 | 899 | sizeof(struct erofs_inode), 0, |
f88c3fb8 | 900 | SLAB_RECLAIM_ACCOUNT | SLAB_ACCOUNT, |
8ec9a252 | 901 | erofs_inode_init_once); |
19905955 YL |
902 | if (!erofs_inode_cachep) |
903 | return -ENOMEM; | |
ba2b77a8 | 904 | |
22fe04a7 | 905 | err = erofs_init_shrinker(); |
a1581312 GX |
906 | if (err) |
907 | goto shrinker_err; | |
908 | ||
5a7cce82 | 909 | err = z_erofs_init_subsystem(); |
3883a79a GX |
910 | if (err) |
911 | goto zip_err; | |
3883a79a | 912 | |
168e9a76 HJ |
913 | err = erofs_init_sysfs(); |
914 | if (err) | |
915 | goto sysfs_err; | |
916 | ||
ba2b77a8 GX |
917 | err = register_filesystem(&erofs_fs_type); |
918 | if (err) | |
919 | goto fs_err; | |
920 | ||
ba2b77a8 GX |
921 | return 0; |
922 | ||
923 | fs_err: | |
168e9a76 HJ |
924 | erofs_exit_sysfs(); |
925 | sysfs_err: | |
5a7cce82 | 926 | z_erofs_exit_subsystem(); |
3883a79a | 927 | zip_err: |
22fe04a7 | 928 | erofs_exit_shrinker(); |
a1581312 | 929 | shrinker_err: |
1c2dfbf9 | 930 | kmem_cache_destroy(erofs_inode_cachep); |
ba2b77a8 GX |
931 | return err; |
932 | } | |
933 | ||
934 | static void __exit erofs_module_exit(void) | |
935 | { | |
936 | unregister_filesystem(&erofs_fs_type); | |
1c2dfbf9 | 937 | |
622ceadd | 938 | /* Ensure all RCU free inodes / pclusters are safe to be destroyed. */ |
1c2dfbf9 | 939 | rcu_barrier(); |
622ceadd | 940 | |
168e9a76 | 941 | erofs_exit_sysfs(); |
5a7cce82 | 942 | z_erofs_exit_subsystem(); |
622ceadd | 943 | erofs_exit_shrinker(); |
1c2dfbf9 | 944 | kmem_cache_destroy(erofs_inode_cachep); |
ba2b77a8 GX |
945 | } |
946 | ||
ba2b77a8 GX |
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); | |
ba2b77a8 GX |
951 | |
952 | buf->f_type = sb->s_magic; | |
3acea5fc | 953 | buf->f_bsize = sb->s_blocksize; |
dfeab2e9 | 954 | buf->f_blocks = sbi->total_blocks; |
ba2b77a8 | 955 | buf->f_bfree = buf->f_bavail = 0; |
ba2b77a8 GX |
956 | buf->f_files = ULLONG_MAX; |
957 | buf->f_ffree = ULLONG_MAX - sbi->inos; | |
ba2b77a8 GX |
958 | buf->f_namelen = EROFS_NAME_LEN; |
959 | ||
1872df8d | 960 | if (uuid_is_null(&sb->s_uuid)) |
fb176750 | 961 | buf->f_fsid = u64_to_fsid(!sb->s_bdev ? 0 : |
1872df8d HL |
962 | huge_encode_dev(sb->s_bdev->bd_dev)); |
963 | else | |
964 | buf->f_fsid = uuid_to_fsid(sb->s_uuid.b); | |
ba2b77a8 GX |
965 | return 0; |
966 | } | |
967 | ||
968 | static int erofs_show_options(struct seq_file *seq, struct dentry *root) | |
969 | { | |
06252e9c | 970 | struct erofs_sb_info *sbi = EROFS_SB(root->d_sb); |
e6242465 | 971 | struct erofs_mount_opts *opt = &sbi->opt; |
b17500a0 | 972 | |
c34110e0 HL |
973 | if (IS_ENABLED(CONFIG_EROFS_FS_XATTR)) |
974 | seq_puts(seq, test_opt(opt, XATTR_USER) ? | |
975 | ",user_xattr" : ",nouser_xattr"); | |
976 | if (IS_ENABLED(CONFIG_EROFS_FS_POSIX_ACL)) | |
977 | seq_puts(seq, test_opt(opt, POSIX_ACL) ? ",acl" : ",noacl"); | |
978 | if (IS_ENABLED(CONFIG_EROFS_FS_ZIP)) | |
979 | seq_printf(seq, ",cache_strategy=%s", | |
980 | erofs_param_cache_strategy[opt->cache_strategy].name); | |
e6242465 | 981 | if (test_opt(opt, DAX_ALWAYS)) |
06252e9c | 982 | seq_puts(seq, ",dax=always"); |
e6242465 | 983 | if (test_opt(opt, DAX_NEVER)) |
06252e9c | 984 | seq_puts(seq, ",dax=never"); |
6422cde1 GX |
985 | if (erofs_is_fileio_mode(sbi) && test_opt(opt, DIRECT_IO)) |
986 | seq_puts(seq, ",directio"); | |
9c0cc9c7 | 987 | #ifdef CONFIG_EROFS_FS_ONDEMAND |
39bfcb81 JX |
988 | if (sbi->fsid) |
989 | seq_printf(seq, ",fsid=%s", sbi->fsid); | |
990 | if (sbi->domain_id) | |
991 | seq_printf(seq, ",domain_id=%s", sbi->domain_id); | |
9c0cc9c7 | 992 | #endif |
c36ec00d SY |
993 | if (sbi->dif0.fsoff) |
994 | seq_printf(seq, ",fsoffset=%llu", sbi->dif0.fsoff); | |
ba2b77a8 GX |
995 | return 0; |
996 | } | |
997 | ||
ba2b77a8 GX |
998 | const struct super_operations erofs_sops = { |
999 | .put_super = erofs_put_super, | |
99634bf3 GX |
1000 | .alloc_inode = erofs_alloc_inode, |
1001 | .free_inode = erofs_free_inode, | |
ba2b77a8 GX |
1002 | .statfs = erofs_statfs, |
1003 | .show_options = erofs_show_options, | |
ba2b77a8 GX |
1004 | }; |
1005 | ||
1006 | module_init(erofs_module_init); | |
1007 | module_exit(erofs_module_exit); | |
1008 | ||
1009 | MODULE_DESCRIPTION("Enhanced ROM File System"); | |
bc33d9f3 | 1010 | MODULE_AUTHOR("Gao Xiang, Chao Yu, Miao Xie, CONSUMER BG, HUAWEI Inc."); |
ba2b77a8 | 1011 | MODULE_LICENSE("GPL"); |