1 | // SPDX-License-Identifier: GPL-2.0 |
2 | |
3 | #include <linux/kernel.h> |
4 | |
5 | #include "bcachefs.h" |
6 | #include "compress.h" |
7 | #include "disk_groups.h" |
8 | #include "error.h" |
9 | #include "opts.h" |
10 | #include "recovery_passes.h" |
11 | #include "super-io.h" |
12 | #include "util.h" |
13 | |
14 | #define x(t, n, ...) [n] = #t, |
15 | |
16 | const char * const bch2_error_actions[] = { |
17 | BCH_ERROR_ACTIONS() |
18 | NULL |
19 | }; |
20 | |
21 | const char * const bch2_fsck_fix_opts[] = { |
22 | BCH_FIX_ERRORS_OPTS() |
23 | NULL |
24 | }; |
25 | |
26 | const char * const bch2_version_upgrade_opts[] = { |
27 | BCH_VERSION_UPGRADE_OPTS() |
28 | NULL |
29 | }; |
30 | |
31 | const char * const bch2_sb_features[] = { |
32 | BCH_SB_FEATURES() |
33 | NULL |
34 | }; |
35 | |
36 | const char * const bch2_sb_compat[] = { |
37 | BCH_SB_COMPAT() |
38 | NULL |
39 | }; |
40 | |
41 | const char * const __bch2_btree_ids[] = { |
42 | BCH_BTREE_IDS() |
43 | NULL |
44 | }; |
45 | |
46 | static const char * const __bch2_csum_types[] = { |
47 | BCH_CSUM_TYPES() |
48 | NULL |
49 | }; |
50 | |
51 | const char * const bch2_csum_opts[] = { |
52 | BCH_CSUM_OPTS() |
53 | NULL |
54 | }; |
55 | |
56 | static const char * const __bch2_compression_types[] = { |
57 | BCH_COMPRESSION_TYPES() |
58 | NULL |
59 | }; |
60 | |
61 | const char * const bch2_compression_opts[] = { |
62 | BCH_COMPRESSION_OPTS() |
63 | NULL |
64 | }; |
65 | |
66 | const char * const bch2_str_hash_types[] = { |
67 | BCH_STR_HASH_TYPES() |
68 | NULL |
69 | }; |
70 | |
71 | const char * const bch2_str_hash_opts[] = { |
72 | BCH_STR_HASH_OPTS() |
73 | NULL |
74 | }; |
75 | |
76 | const char * const __bch2_data_types[] = { |
77 | BCH_DATA_TYPES() |
78 | NULL |
79 | }; |
80 | |
81 | const char * const bch2_member_states[] = { |
82 | BCH_MEMBER_STATES() |
83 | NULL |
84 | }; |
85 | |
86 | static const char * const __bch2_jset_entry_types[] = { |
87 | BCH_JSET_ENTRY_TYPES() |
88 | NULL |
89 | }; |
90 | |
91 | static const char * const __bch2_fs_usage_types[] = { |
92 | BCH_FS_USAGE_TYPES() |
93 | NULL |
94 | }; |
95 | |
96 | #undef x |
97 | |
98 | static void prt_str_opt_boundscheck(struct printbuf *out, const char * const opts[], |
99 | unsigned nr, const char *type, unsigned idx) |
100 | { |
101 | if (idx < nr) |
102 | prt_str(out, str: opts[idx]); |
103 | else |
104 | prt_printf(out, "(unknown %s %u)" , type, idx); |
105 | } |
106 | |
107 | #define PRT_STR_OPT_BOUNDSCHECKED(name, type) \ |
108 | void bch2_prt_##name(struct printbuf *out, type t) \ |
109 | { \ |
110 | prt_str_opt_boundscheck(out, __bch2_##name##s, ARRAY_SIZE(__bch2_##name##s) - 1, #name, t);\ |
111 | } |
112 | |
113 | PRT_STR_OPT_BOUNDSCHECKED(jset_entry_type, enum bch_jset_entry_type); |
114 | PRT_STR_OPT_BOUNDSCHECKED(fs_usage_type, enum bch_fs_usage_type); |
115 | PRT_STR_OPT_BOUNDSCHECKED(data_type, enum bch_data_type); |
116 | PRT_STR_OPT_BOUNDSCHECKED(csum_type, enum bch_csum_type); |
117 | PRT_STR_OPT_BOUNDSCHECKED(compression_type, enum bch_compression_type); |
118 | |
119 | static int bch2_opt_fix_errors_parse(struct bch_fs *c, const char *val, u64 *res, |
120 | struct printbuf *err) |
121 | { |
122 | if (!val) { |
123 | *res = FSCK_FIX_yes; |
124 | } else { |
125 | int ret = match_string(array: bch2_fsck_fix_opts, n: -1, string: val); |
126 | |
127 | if (ret < 0 && err) |
128 | prt_str(out: err, str: "fix_errors: invalid selection" ); |
129 | if (ret < 0) |
130 | return ret; |
131 | *res = ret; |
132 | } |
133 | |
134 | return 0; |
135 | } |
136 | |
137 | static void bch2_opt_fix_errors_to_text(struct printbuf *out, |
138 | struct bch_fs *c, |
139 | struct bch_sb *sb, |
140 | u64 v) |
141 | { |
142 | prt_str(out, str: bch2_fsck_fix_opts[v]); |
143 | } |
144 | |
145 | #define bch2_opt_fix_errors (struct bch_opt_fn) { \ |
146 | .parse = bch2_opt_fix_errors_parse, \ |
147 | .to_text = bch2_opt_fix_errors_to_text, \ |
148 | } |
149 | |
150 | const char * const bch2_d_types[BCH_DT_MAX] = { |
151 | [DT_UNKNOWN] = "unknown" , |
152 | [DT_FIFO] = "fifo" , |
153 | [DT_CHR] = "chr" , |
154 | [DT_DIR] = "dir" , |
155 | [DT_BLK] = "blk" , |
156 | [DT_REG] = "reg" , |
157 | [DT_LNK] = "lnk" , |
158 | [DT_SOCK] = "sock" , |
159 | [DT_WHT] = "whiteout" , |
160 | [DT_SUBVOL] = "subvol" , |
161 | }; |
162 | |
163 | u64 BCH2_NO_SB_OPT(const struct bch_sb *sb) |
164 | { |
165 | BUG(); |
166 | } |
167 | |
168 | void SET_BCH2_NO_SB_OPT(struct bch_sb *sb, u64 v) |
169 | { |
170 | BUG(); |
171 | } |
172 | |
173 | void bch2_opts_apply(struct bch_opts *dst, struct bch_opts src) |
174 | { |
175 | #define x(_name, ...) \ |
176 | if (opt_defined(src, _name)) \ |
177 | opt_set(*dst, _name, src._name); |
178 | |
179 | BCH_OPTS() |
180 | #undef x |
181 | } |
182 | |
183 | bool bch2_opt_defined_by_id(const struct bch_opts *opts, enum bch_opt_id id) |
184 | { |
185 | switch (id) { |
186 | #define x(_name, ...) \ |
187 | case Opt_##_name: \ |
188 | return opt_defined(*opts, _name); |
189 | BCH_OPTS() |
190 | #undef x |
191 | default: |
192 | BUG(); |
193 | } |
194 | } |
195 | |
196 | u64 bch2_opt_get_by_id(const struct bch_opts *opts, enum bch_opt_id id) |
197 | { |
198 | switch (id) { |
199 | #define x(_name, ...) \ |
200 | case Opt_##_name: \ |
201 | return opts->_name; |
202 | BCH_OPTS() |
203 | #undef x |
204 | default: |
205 | BUG(); |
206 | } |
207 | } |
208 | |
209 | void bch2_opt_set_by_id(struct bch_opts *opts, enum bch_opt_id id, u64 v) |
210 | { |
211 | switch (id) { |
212 | #define x(_name, ...) \ |
213 | case Opt_##_name: \ |
214 | opt_set(*opts, _name, v); \ |
215 | break; |
216 | BCH_OPTS() |
217 | #undef x |
218 | default: |
219 | BUG(); |
220 | } |
221 | } |
222 | |
223 | const struct bch_option bch2_opt_table[] = { |
224 | #define OPT_BOOL() .type = BCH_OPT_BOOL, .min = 0, .max = 2 |
225 | #define OPT_UINT(_min, _max) .type = BCH_OPT_UINT, \ |
226 | .min = _min, .max = _max |
227 | #define OPT_STR(_choices) .type = BCH_OPT_STR, \ |
228 | .min = 0, .max = ARRAY_SIZE(_choices), \ |
229 | .choices = _choices |
230 | #define OPT_STR_NOLIMIT(_choices) .type = BCH_OPT_STR, \ |
231 | .min = 0, .max = U64_MAX, \ |
232 | .choices = _choices |
233 | #define OPT_FN(_fn) .type = BCH_OPT_FN, .fn = _fn |
234 | |
235 | #define x(_name, _bits, _flags, _type, _sb_opt, _default, _hint, _help) \ |
236 | [Opt_##_name] = { \ |
237 | .attr = { \ |
238 | .name = #_name, \ |
239 | .mode = (_flags) & OPT_RUNTIME ? 0644 : 0444, \ |
240 | }, \ |
241 | .flags = _flags, \ |
242 | .hint = _hint, \ |
243 | .help = _help, \ |
244 | .get_sb = _sb_opt, \ |
245 | .set_sb = SET_##_sb_opt, \ |
246 | _type \ |
247 | }, |
248 | |
249 | BCH_OPTS() |
250 | #undef x |
251 | }; |
252 | |
253 | int bch2_opt_lookup(const char *name) |
254 | { |
255 | const struct bch_option *i; |
256 | |
257 | for (i = bch2_opt_table; |
258 | i < bch2_opt_table + ARRAY_SIZE(bch2_opt_table); |
259 | i++) |
260 | if (!strcmp(name, i->attr.name)) |
261 | return i - bch2_opt_table; |
262 | |
263 | return -1; |
264 | } |
265 | |
266 | struct synonym { |
267 | const char *s1, *s2; |
268 | }; |
269 | |
270 | static const struct synonym bch_opt_synonyms[] = { |
271 | { "quota" , "usrquota" }, |
272 | }; |
273 | |
274 | static int bch2_mount_opt_lookup(const char *name) |
275 | { |
276 | const struct synonym *i; |
277 | |
278 | for (i = bch_opt_synonyms; |
279 | i < bch_opt_synonyms + ARRAY_SIZE(bch_opt_synonyms); |
280 | i++) |
281 | if (!strcmp(name, i->s1)) |
282 | name = i->s2; |
283 | |
284 | return bch2_opt_lookup(name); |
285 | } |
286 | |
287 | int bch2_opt_validate(const struct bch_option *opt, u64 v, struct printbuf *err) |
288 | { |
289 | if (v < opt->min) { |
290 | if (err) |
291 | prt_printf(err, "%s: too small (min %llu)" , |
292 | opt->attr.name, opt->min); |
293 | return -BCH_ERR_ERANGE_option_too_small; |
294 | } |
295 | |
296 | if (opt->max && v >= opt->max) { |
297 | if (err) |
298 | prt_printf(err, "%s: too big (max %llu)" , |
299 | opt->attr.name, opt->max); |
300 | return -BCH_ERR_ERANGE_option_too_big; |
301 | } |
302 | |
303 | if ((opt->flags & OPT_SB_FIELD_SECTORS) && (v & 511)) { |
304 | if (err) |
305 | prt_printf(err, "%s: not a multiple of 512" , |
306 | opt->attr.name); |
307 | return -BCH_ERR_opt_parse_error; |
308 | } |
309 | |
310 | if ((opt->flags & OPT_MUST_BE_POW_2) && !is_power_of_2(n: v)) { |
311 | if (err) |
312 | prt_printf(err, "%s: must be a power of two" , |
313 | opt->attr.name); |
314 | return -BCH_ERR_opt_parse_error; |
315 | } |
316 | |
317 | if (opt->fn.validate) |
318 | return opt->fn.validate(v, err); |
319 | |
320 | return 0; |
321 | } |
322 | |
323 | int bch2_opt_parse(struct bch_fs *c, |
324 | const struct bch_option *opt, |
325 | const char *val, u64 *res, |
326 | struct printbuf *err) |
327 | { |
328 | ssize_t ret; |
329 | |
330 | switch (opt->type) { |
331 | case BCH_OPT_BOOL: |
332 | if (val) { |
333 | ret = kstrtou64(s: val, base: 10, res); |
334 | } else { |
335 | ret = 0; |
336 | *res = 1; |
337 | } |
338 | |
339 | if (ret < 0 || (*res != 0 && *res != 1)) { |
340 | if (err) |
341 | prt_printf(err, "%s: must be bool" , opt->attr.name); |
342 | return ret < 0 ? ret : -BCH_ERR_option_not_bool; |
343 | } |
344 | break; |
345 | case BCH_OPT_UINT: |
346 | if (!val) { |
347 | prt_printf(err, "%s: required value" , |
348 | opt->attr.name); |
349 | return -EINVAL; |
350 | } |
351 | |
352 | ret = opt->flags & OPT_HUMAN_READABLE |
353 | ? bch2_strtou64_h(val, res) |
354 | : kstrtou64(s: val, base: 10, res); |
355 | if (ret < 0) { |
356 | if (err) |
357 | prt_printf(err, "%s: must be a number" , |
358 | opt->attr.name); |
359 | return ret; |
360 | } |
361 | break; |
362 | case BCH_OPT_STR: |
363 | if (!val) { |
364 | prt_printf(err, "%s: required value" , |
365 | opt->attr.name); |
366 | return -EINVAL; |
367 | } |
368 | |
369 | ret = match_string(array: opt->choices, n: -1, string: val); |
370 | if (ret < 0) { |
371 | if (err) |
372 | prt_printf(err, "%s: invalid selection" , |
373 | opt->attr.name); |
374 | return ret; |
375 | } |
376 | |
377 | *res = ret; |
378 | break; |
379 | case BCH_OPT_FN: |
380 | ret = opt->fn.parse(c, val, res, err); |
381 | if (ret < 0) { |
382 | if (err) |
383 | prt_printf(err, "%s: parse error" , |
384 | opt->attr.name); |
385 | return ret; |
386 | } |
387 | } |
388 | |
389 | return bch2_opt_validate(opt, v: *res, err); |
390 | } |
391 | |
392 | void bch2_opt_to_text(struct printbuf *out, |
393 | struct bch_fs *c, struct bch_sb *sb, |
394 | const struct bch_option *opt, u64 v, |
395 | unsigned flags) |
396 | { |
397 | if (flags & OPT_SHOW_MOUNT_STYLE) { |
398 | if (opt->type == BCH_OPT_BOOL) { |
399 | prt_printf(out, "%s%s" , |
400 | v ? "" : "no" , |
401 | opt->attr.name); |
402 | return; |
403 | } |
404 | |
405 | prt_printf(out, "%s=" , opt->attr.name); |
406 | } |
407 | |
408 | switch (opt->type) { |
409 | case BCH_OPT_BOOL: |
410 | case BCH_OPT_UINT: |
411 | if (opt->flags & OPT_HUMAN_READABLE) |
412 | prt_human_readable_u64(out, v); |
413 | else |
414 | prt_printf(out, "%lli" , v); |
415 | break; |
416 | case BCH_OPT_STR: |
417 | if (flags & OPT_SHOW_FULL_LIST) |
418 | prt_string_option(out, opt->choices, v); |
419 | else |
420 | prt_str(out, str: opt->choices[v]); |
421 | break; |
422 | case BCH_OPT_FN: |
423 | opt->fn.to_text(out, c, sb, v); |
424 | break; |
425 | default: |
426 | BUG(); |
427 | } |
428 | } |
429 | |
430 | int bch2_opt_check_may_set(struct bch_fs *c, int id, u64 v) |
431 | { |
432 | int ret = 0; |
433 | |
434 | switch (id) { |
435 | case Opt_compression: |
436 | case Opt_background_compression: |
437 | ret = bch2_check_set_has_compressed_data(c, v); |
438 | break; |
439 | case Opt_erasure_code: |
440 | if (v) |
441 | bch2_check_set_feature(c, feat: BCH_FEATURE_ec); |
442 | break; |
443 | } |
444 | |
445 | return ret; |
446 | } |
447 | |
448 | int bch2_opts_check_may_set(struct bch_fs *c) |
449 | { |
450 | unsigned i; |
451 | int ret; |
452 | |
453 | for (i = 0; i < bch2_opts_nr; i++) { |
454 | ret = bch2_opt_check_may_set(c, id: i, |
455 | v: bch2_opt_get_by_id(opts: &c->opts, id: i)); |
456 | if (ret) |
457 | return ret; |
458 | } |
459 | |
460 | return 0; |
461 | } |
462 | |
463 | int bch2_parse_mount_opts(struct bch_fs *c, struct bch_opts *opts, |
464 | char *options) |
465 | { |
466 | char *copied_opts, *copied_opts_start; |
467 | char *opt, *name, *val; |
468 | int ret, id; |
469 | struct printbuf err = PRINTBUF; |
470 | u64 v; |
471 | |
472 | if (!options) |
473 | return 0; |
474 | |
475 | /* |
476 | * sys_fsconfig() is now occasionally providing us with option lists |
477 | * starting with a comma - weird. |
478 | */ |
479 | if (*options == ',') |
480 | options++; |
481 | |
482 | copied_opts = kstrdup(s: options, GFP_KERNEL); |
483 | if (!copied_opts) |
484 | return -ENOMEM; |
485 | copied_opts_start = copied_opts; |
486 | |
487 | while ((opt = strsep(&copied_opts, "," )) != NULL) { |
488 | name = strsep(&opt, "=" ); |
489 | val = opt; |
490 | |
491 | id = bch2_mount_opt_lookup(name); |
492 | |
493 | /* Check for the form "noopt", negation of a boolean opt: */ |
494 | if (id < 0 && |
495 | !val && |
496 | !strncmp("no" , name, 2)) { |
497 | id = bch2_mount_opt_lookup(name: name + 2); |
498 | val = "0" ; |
499 | } |
500 | |
501 | /* Unknown options are ignored: */ |
502 | if (id < 0) |
503 | continue; |
504 | |
505 | if (!(bch2_opt_table[id].flags & OPT_MOUNT)) |
506 | goto bad_opt; |
507 | |
508 | if (id == Opt_acl && |
509 | !IS_ENABLED(CONFIG_BCACHEFS_POSIX_ACL)) |
510 | goto bad_opt; |
511 | |
512 | if ((id == Opt_usrquota || |
513 | id == Opt_grpquota) && |
514 | !IS_ENABLED(CONFIG_BCACHEFS_QUOTA)) |
515 | goto bad_opt; |
516 | |
517 | ret = bch2_opt_parse(c, opt: &bch2_opt_table[id], val, res: &v, err: &err); |
518 | if (ret < 0) |
519 | goto bad_val; |
520 | |
521 | bch2_opt_set_by_id(opts, id, v); |
522 | } |
523 | |
524 | ret = 0; |
525 | goto out; |
526 | |
527 | bad_opt: |
528 | pr_err("Bad mount option %s" , name); |
529 | ret = -BCH_ERR_option_name; |
530 | goto out; |
531 | bad_val: |
532 | pr_err("Invalid mount option %s" , err.buf); |
533 | ret = -BCH_ERR_option_value; |
534 | goto out; |
535 | out: |
536 | kfree(objp: copied_opts_start); |
537 | printbuf_exit(&err); |
538 | return ret; |
539 | } |
540 | |
541 | u64 bch2_opt_from_sb(struct bch_sb *sb, enum bch_opt_id id) |
542 | { |
543 | const struct bch_option *opt = bch2_opt_table + id; |
544 | u64 v; |
545 | |
546 | v = opt->get_sb(sb); |
547 | |
548 | if (opt->flags & OPT_SB_FIELD_ILOG2) |
549 | v = 1ULL << v; |
550 | |
551 | if (opt->flags & OPT_SB_FIELD_SECTORS) |
552 | v <<= 9; |
553 | |
554 | return v; |
555 | } |
556 | |
557 | /* |
558 | * Initial options from superblock - here we don't want any options undefined, |
559 | * any options the superblock doesn't specify are set to 0: |
560 | */ |
561 | int bch2_opts_from_sb(struct bch_opts *opts, struct bch_sb *sb) |
562 | { |
563 | unsigned id; |
564 | |
565 | for (id = 0; id < bch2_opts_nr; id++) { |
566 | const struct bch_option *opt = bch2_opt_table + id; |
567 | |
568 | if (opt->get_sb == BCH2_NO_SB_OPT) |
569 | continue; |
570 | |
571 | bch2_opt_set_by_id(opts, id, v: bch2_opt_from_sb(sb, id)); |
572 | } |
573 | |
574 | return 0; |
575 | } |
576 | |
577 | void __bch2_opt_set_sb(struct bch_sb *sb, const struct bch_option *opt, u64 v) |
578 | { |
579 | if (opt->set_sb == SET_BCH2_NO_SB_OPT) |
580 | return; |
581 | |
582 | if (opt->flags & OPT_SB_FIELD_SECTORS) |
583 | v >>= 9; |
584 | |
585 | if (opt->flags & OPT_SB_FIELD_ILOG2) |
586 | v = ilog2(v); |
587 | |
588 | opt->set_sb(sb, v); |
589 | } |
590 | |
591 | void bch2_opt_set_sb(struct bch_fs *c, const struct bch_option *opt, u64 v) |
592 | { |
593 | if (opt->set_sb == SET_BCH2_NO_SB_OPT) |
594 | return; |
595 | |
596 | mutex_lock(&c->sb_lock); |
597 | __bch2_opt_set_sb(sb: c->disk_sb.sb, opt, v); |
598 | bch2_write_super(c); |
599 | mutex_unlock(lock: &c->sb_lock); |
600 | } |
601 | |
602 | /* io opts: */ |
603 | |
604 | struct bch_io_opts bch2_opts_to_inode_opts(struct bch_opts src) |
605 | { |
606 | return (struct bch_io_opts) { |
607 | #define x(_name, _bits) ._name = src._name, |
608 | BCH_INODE_OPTS() |
609 | #undef x |
610 | }; |
611 | } |
612 | |
613 | bool bch2_opt_is_inode_opt(enum bch_opt_id id) |
614 | { |
615 | static const enum bch_opt_id inode_opt_list[] = { |
616 | #define x(_name, _bits) Opt_##_name, |
617 | BCH_INODE_OPTS() |
618 | #undef x |
619 | }; |
620 | unsigned i; |
621 | |
622 | for (i = 0; i < ARRAY_SIZE(inode_opt_list); i++) |
623 | if (inode_opt_list[i] == id) |
624 | return true; |
625 | |
626 | return false; |
627 | } |
628 | |