]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - libbcachefs/opts.c
Update bcachefs sources to da7fefde29 bcachefs: shim for userspace raid library
[bcachefs-tools-debian] / libbcachefs / opts.c
index 7c4cf80481563e1fc2a4e7c9ed59a50f7d26e6cf..449cd5bfcfc7317adc615e72a5f4ad385b6cc811 100644 (file)
@@ -1,7 +1,11 @@
 
 #include <linux/kernel.h>
 
+#include "bcachefs.h"
+#include "compress.h"
+#include "disk_groups.h"
 #include "opts.h"
+#include "super-io.h"
 #include "util.h"
 
 const char * const bch2_error_actions[] = {
@@ -22,6 +26,7 @@ const char * const bch2_compression_types[] = {
        "none",
        "lz4",
        "gzip",
+       "zstd",
        NULL
 };
 
@@ -32,6 +37,16 @@ const char * const bch2_str_hash_types[] = {
        NULL
 };
 
+const char * const bch2_data_types[] = {
+       "none",
+       "sb",
+       "journal",
+       "btree",
+       "data",
+       "cached",
+       NULL
+};
+
 const char * const bch2_cache_replacement_policies[] = {
        "lru",
        "fifo",
@@ -57,60 +72,51 @@ const char * const bch2_dev_state[] = {
        NULL
 };
 
-const struct bch_option bch2_opt_table[] = {
-#define OPT_BOOL()             .type = BCH_OPT_BOOL
-#define OPT_UINT(_min, _max)   .type = BCH_OPT_UINT, .min = _min, .max = _max
-#define OPT_STR(_choices)      .type = BCH_OPT_STR, .choices = _choices
+void bch2_opts_apply(struct bch_opts *dst, struct bch_opts src)
+{
+#define BCH_OPT(_name, ...)                                            \
+       if (opt_defined(src, _name))                                    \
+               opt_set(*dst, _name, src._name);
 
-#define BCH_OPT(_name, _mode, _sb_opt, _bits, _type)                   \
-       [Opt_##_name] = {                                               \
-               .name   = #_name,                                       \
-               .set_sb = SET_##_sb_opt,                                \
-               _type                                                   \
-       },
-       BCH_VISIBLE_OPTS()
+       BCH_OPTS()
 #undef BCH_OPT
-};
+}
 
-static enum bch_opt_id bch2_opt_lookup(const char *name)
+bool bch2_opt_defined_by_id(const struct bch_opts *opts, enum bch_opt_id id)
 {
-       const struct bch_option *i;
-
-       for (i = bch2_opt_table;
-            i < bch2_opt_table + ARRAY_SIZE(bch2_opt_table);
-            i++)
-               if (!strcmp(name, i->name))
-                       return i - bch2_opt_table;
-
-       return -1;
+       switch (id) {
+#define BCH_OPT(_name, ...)                                            \
+       case Opt_##_name:                                               \
+               return opt_defined(*opts, _name);
+       BCH_OPTS()
+#undef BCH_OPT
+       default:
+               BUG();
+       }
 }
 
-static u64 bch2_opt_get(struct bch_opts *opts, enum bch_opt_id id)
+u64 bch2_opt_get_by_id(const struct bch_opts *opts, enum bch_opt_id id)
 {
        switch (id) {
 #define BCH_OPT(_name, ...)                                            \
        case Opt_##_name:                                               \
-               return opts->_name;                                     \
-
-       BCH_VISIBLE_OPTS()
+               return opts->_name;
+       BCH_OPTS()
 #undef BCH_OPT
-
        default:
                BUG();
        }
 }
 
-void bch2_opt_set(struct bch_opts *opts, enum bch_opt_id id, u64 v)
+void bch2_opt_set_by_id(struct bch_opts *opts, enum bch_opt_id id, u64 v)
 {
        switch (id) {
 #define BCH_OPT(_name, ...)                                            \
        case Opt_##_name:                                               \
-               opts->_name = v;                                        \
+               opt_set(*opts, _name, v);                               \
                break;
-
-       BCH_VISIBLE_OPTS()
+       BCH_OPTS()
 #undef BCH_OPT
-
        default:
                BUG();
        }
@@ -120,23 +126,79 @@ void bch2_opt_set(struct bch_opts *opts, enum bch_opt_id id, u64 v)
  * Initial options from superblock - here we don't want any options undefined,
  * any options the superblock doesn't specify are set to 0:
  */
-struct bch_opts bch2_sb_opts(struct bch_sb *sb)
+struct bch_opts bch2_opts_from_sb(struct bch_sb *sb)
 {
        struct bch_opts opts = bch2_opts_empty();
 
-#define BCH_OPT(_name, _mode, _sb_opt, ...)                            \
+#define BCH_OPT(_name, _bits, _mode, _type, _sb_opt, _default)         \
        if (_sb_opt != NO_SB_OPT)                                       \
-               opts._name = _sb_opt(sb);
-
+               opt_set(opts, _name, _sb_opt(sb));
        BCH_OPTS()
 #undef BCH_OPT
 
        return opts;
 }
 
-static int parse_one_opt(enum bch_opt_id id, const char *val, u64 *res)
+const struct bch_option bch2_opt_table[] = {
+#define OPT_BOOL()             .type = BCH_OPT_BOOL
+#define OPT_UINT(_min, _max)   .type = BCH_OPT_UINT, .min = _min, .max = _max
+#define OPT_STR(_choices)      .type = BCH_OPT_STR, .choices = _choices
+#define OPT_FN(_fn)            .type = BCH_OPT_FN,                     \
+                               .parse = _fn##_parse,                   \
+                               .to_text = _fn##_to_text
+
+#define BCH_OPT(_name, _bits, _mode, _type, _sb_opt, _default)         \
+       [Opt_##_name] = {                                               \
+               .attr   = {                                             \
+                       .name   = #_name,                               \
+                       .mode = _mode == OPT_RUNTIME ? 0644 : 0444,     \
+               },                                                      \
+               .mode   = _mode,                                        \
+               .set_sb = SET_##_sb_opt,                                \
+               _type                                                   \
+       },
+
+       BCH_OPTS()
+#undef BCH_OPT
+};
+
+int bch2_opt_lookup(const char *name)
+{
+       const struct bch_option *i;
+
+       for (i = bch2_opt_table;
+            i < bch2_opt_table + ARRAY_SIZE(bch2_opt_table);
+            i++)
+               if (!strcmp(name, i->attr.name))
+                       return i - bch2_opt_table;
+
+       return -1;
+}
+
+struct synonym {
+       const char      *s1, *s2;
+};
+
+static const struct synonym bch_opt_synonyms[] = {
+       { "quota",      "usrquota" },
+};
+
+static int bch2_mount_opt_lookup(const char *name)
+{
+       const struct synonym *i;
+
+       for (i = bch_opt_synonyms;
+            i < bch_opt_synonyms + ARRAY_SIZE(bch_opt_synonyms);
+            i++)
+               if (!strcmp(name, i->s1))
+                       name = i->s2;
+
+       return bch2_opt_lookup(name);
+}
+
+int bch2_opt_parse(struct bch_fs *c, const struct bch_option *opt,
+                  const char *val, u64 *res)
 {
-       const struct bch_option *opt = &bch2_opt_table[id];
        ssize_t ret;
 
        switch (opt->type) {
@@ -157,12 +219,91 @@ static int parse_one_opt(enum bch_opt_id id, const char *val, u64 *res)
                        return -ERANGE;
                break;
        case BCH_OPT_STR:
-               ret = bch2_read_string_list(val, opt->choices);
+               ret = match_string(opt->choices, -1, val);
                if (ret < 0)
                        return ret;
 
                *res = ret;
                break;
+       case BCH_OPT_FN:
+               if (!c)
+                       return -EINVAL;
+
+               return opt->parse(c, val, res);
+       }
+
+       return 0;
+}
+
+void bch2_opt_to_text(struct printbuf *out, struct bch_fs *c,
+                     const struct bch_option *opt, u64 v,
+                     unsigned flags)
+{
+       if (flags & OPT_SHOW_MOUNT_STYLE) {
+               if (opt->type == BCH_OPT_BOOL) {
+                       pr_buf(out, "%s%s",
+                              v ? "" : "no",
+                              opt->attr.name);
+                       return;
+               }
+
+               pr_buf(out, "%s=", opt->attr.name);
+       }
+
+       switch (opt->type) {
+       case BCH_OPT_BOOL:
+       case BCH_OPT_UINT:
+               pr_buf(out, "%lli", v);
+               break;
+       case BCH_OPT_STR:
+               if (flags & OPT_SHOW_FULL_LIST)
+                       bch2_string_opt_to_text(out, opt->choices, v);
+               else
+                       pr_buf(out, opt->choices[v]);
+               break;
+       case BCH_OPT_FN:
+               opt->to_text(out, c, v);
+               break;
+       default:
+               BUG();
+       }
+}
+
+int bch2_opt_check_may_set(struct bch_fs *c, int id, u64 v)
+{
+       int ret = 0;
+
+       switch (id) {
+       case Opt_compression:
+       case Opt_background_compression:
+               ret = bch2_check_set_has_compressed_data(c, v);
+               break;
+       case Opt_erasure_code:
+               if (v &&
+                   !(c->sb.features & (1ULL << BCH_FEATURE_EC))) {
+                       mutex_lock(&c->sb_lock);
+                       c->disk_sb.sb->features[0] |=
+                               cpu_to_le64(1ULL << BCH_FEATURE_EC);
+
+                       bch2_write_super(c);
+                       mutex_unlock(&c->sb_lock);
+               }
+               break;
+       }
+
+       return ret;
+}
+
+int bch2_opts_check_may_set(struct bch_fs *c)
+{
+       unsigned i;
+       int ret;
+
+       for (i = 0; i < bch2_opts_nr; i++) {
+               ret = bch2_opt_check_may_set(c, i,
+                               bch2_opt_get_by_id(&c->opts, i));
+               if (ret)
+                       return ret;
        }
 
        return 0;
@@ -179,63 +320,102 @@ int bch2_parse_mount_opts(struct bch_opts *opts, char *options)
                val     = opt;
 
                if (val) {
-                       id = bch2_opt_lookup(name);
+                       id = bch2_mount_opt_lookup(name);
                        if (id < 0)
-                               return -EINVAL;
+                               goto bad_opt;
 
-                       ret = parse_one_opt(id, val, &v);
+                       ret = bch2_opt_parse(NULL, &bch2_opt_table[id], val, &v);
                        if (ret < 0)
-                               return ret;
+                               goto bad_val;
                } else {
-                       id = bch2_opt_lookup(name);
+                       id = bch2_mount_opt_lookup(name);
                        v = 1;
 
                        if (id < 0 &&
                            !strncmp("no", name, 2)) {
-                               id = bch2_opt_lookup(name + 2);
+                               id = bch2_mount_opt_lookup(name + 2);
                                v = 0;
                        }
 
+                       if (id < 0)
+                               goto bad_opt;
+
                        if (bch2_opt_table[id].type != BCH_OPT_BOOL)
-                               return -EINVAL;
+                               goto no_val;
                }
 
-               bch2_opt_set(opts, id, v);
+               if (bch2_opt_table[id].mode < OPT_MOUNT)
+                       goto bad_opt;
+
+               if (id == Opt_acl &&
+                   !IS_ENABLED(CONFIG_BCACHEFS_POSIX_ACL))
+                       goto bad_opt;
+
+               if ((id == Opt_usrquota ||
+                    id == Opt_grpquota) &&
+                   !IS_ENABLED(CONFIG_BCACHEFS_QUOTA))
+                       goto bad_opt;
+
+               bch2_opt_set_by_id(opts, id, v);
        }
 
        return 0;
+bad_opt:
+       pr_err("Bad mount option %s", name);
+       return -1;
+bad_val:
+       pr_err("Invalid value %s for mount option %s", val, name);
+       return -1;
+no_val:
+       pr_err("Mount option %s requires a value", name);
+       return -1;
 }
 
-enum bch_opt_id bch2_parse_sysfs_opt(const char *name, const char *val,
-                                   u64 *res)
-{
-       enum bch_opt_id id = bch2_opt_lookup(name);
-       int ret;
-
-       if (id < 0)
-               return -EINVAL;
+/* io opts: */
 
-       ret = parse_one_opt(id, val, res);
-       if (ret < 0)
-               return ret;
-
-       return id;
+struct bch_io_opts bch2_opts_to_inode_opts(struct bch_opts src)
+{
+       struct bch_io_opts ret = { 0 };
+#define BCH_INODE_OPT(_name, _bits)                                    \
+       if (opt_defined(src, _name))                                    \
+               opt_set(ret, _name, src._name);
+       BCH_INODE_OPTS()
+#undef BCH_INODE_OPT
+       return ret;
 }
 
-ssize_t bch2_opt_show(struct bch_opts *opts, const char *name,
-                    char *buf, size_t size)
+struct bch_opts bch2_inode_opts_to_opts(struct bch_io_opts src)
 {
-       enum bch_opt_id id = bch2_opt_lookup(name);
-       const struct bch_option *opt;
-       u64 v;
-
-       if (id < 0)
-               return -EINVAL;
+       struct bch_opts ret = { 0 };
+#define BCH_INODE_OPT(_name, _bits)                                    \
+       if (opt_defined(src, _name))                                    \
+               opt_set(ret, _name, src._name);
+       BCH_INODE_OPTS()
+#undef BCH_INODE_OPT
+       return ret;
+}
 
-       v = bch2_opt_get(opts, id);
-       opt = &bch2_opt_table[id];
+void bch2_io_opts_apply(struct bch_io_opts *dst, struct bch_io_opts src)
+{
+#define BCH_INODE_OPT(_name, _bits)                                    \
+       if (opt_defined(src, _name))                                    \
+               opt_set(*dst, _name, src._name);
+       BCH_INODE_OPTS()
+#undef BCH_INODE_OPT
+}
 
-       return opt->type == BCH_OPT_STR
-               ? bch2_snprint_string_list(buf, size, opt->choices, v)
-               : snprintf(buf, size, "%lli\n", v);
+bool bch2_opt_is_inode_opt(enum bch_opt_id id)
+{
+       static const enum bch_opt_id inode_opt_list[] = {
+#define BCH_INODE_OPT(_name, _bits)    Opt_##_name,
+       BCH_INODE_OPTS()
+#undef BCH_INODE_OPT
+       };
+       unsigned i;
+
+       for (i = 0; i < ARRAY_SIZE(inode_opt_list); i++)
+               if (inode_opt_list[i] == id)
+                       return true;
+
+       return false;
 }