]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - libbcachefs.c
pytest: remove arbitrary assertion
[bcachefs-tools-debian] / libbcachefs.c
index ea52ce4887e277f31051a97b9ddecee31dff8a58..34246dc9106d5ffd83712f4b6571b4e91e042abe 100644 (file)
@@ -1,4 +1,5 @@
 #include <ctype.h>
+#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <stdbool.h>
@@ -20,6 +21,7 @@
 #include "libbcachefs/btree_cache.h"
 #include "libbcachefs/checksum.h"
 #include "libbcachefs/disk_groups.h"
+#include "libbcachefs/journal_seq_blacklist.h"
 #include "libbcachefs/opts.h"
 #include "libbcachefs/replicas.h"
 #include "libbcachefs/super-io.h"
@@ -33,52 +35,35 @@ static u64 min_size(unsigned bucket_size)
        return BCH_MIN_NR_NBUCKETS * bucket_size;
 }
 
-static void init_layout(struct bch_sb_layout *l, unsigned block_size,
-                       u64 start, u64 end)
+static void init_layout(struct bch_sb_layout *l,
+                       unsigned block_size,
+                       unsigned sb_size,
+                       u64 sb_start, u64 sb_end)
 {
-       unsigned sb_size;
-       u64 backup; /* offset of 2nd sb */
+       unsigned i;
 
        memset(l, 0, sizeof(*l));
 
-       if (start != BCH_SB_SECTOR)
-               start = round_up(start, block_size);
-       end = round_down(end, block_size);
-
-       if (start >= end)
-               die("insufficient space for superblocks");
-
-       /*
-        * Create two superblocks in the allowed range: reserve a maximum of 64k
-        */
-       sb_size = min_t(u64, 128, end - start / 2);
-
-       backup = start + sb_size;
-       backup = round_up(backup, block_size);
-
-       backup = min(backup, end);
-
-       sb_size = min(end - backup, backup- start);
-       sb_size = rounddown_pow_of_two(sb_size);
-
-       if (sb_size < 8)
-               die("insufficient space for superblocks");
-
        l->magic                = BCACHE_MAGIC;
        l->layout_type          = 0;
        l->nr_superblocks       = 2;
        l->sb_max_size_bits     = ilog2(sb_size);
-       l->sb_offset[0]         = cpu_to_le64(start);
-       l->sb_offset[1]         = cpu_to_le64(backup);
+
+       /* Create two superblocks in the allowed range: */
+       for (i = 0; i < l->nr_superblocks; i++) {
+               if (sb_start != BCH_SB_SECTOR)
+                       sb_start = round_up(sb_start, block_size);
+
+               l->sb_offset[i] = cpu_to_le64(sb_start);
+               sb_start += sb_size;
+       }
+
+       if (sb_start >= sb_end)
+               die("insufficient space for superblocks");
 }
 
 void bch2_pick_bucket_size(struct bch_opts opts, struct dev_opts *dev)
 {
-       if (!dev->sb_offset) {
-               dev->sb_offset  = BCH_SB_SECTOR;
-               dev->sb_end     = BCH_SB_SECTOR + 256;
-       }
-
        if (!dev->size)
                dev->size = get_size(dev->path, dev->fd) >> 9;
 
@@ -200,13 +185,16 @@ struct bch_sb *bch2_format(struct bch_opt_strs    fs_opt_strs,
        if (bch2_sb_realloc(&sb, 0))
                die("insufficient memory");
 
-       sb.sb->version          = le16_to_cpu(bcachefs_metadata_version_current);
-       sb.sb->version_min      = le16_to_cpu(bcachefs_metadata_version_current);
+       sb.sb->version          = le16_to_cpu(opts.version);
+       sb.sb->version_min      = le16_to_cpu(opts.version);
        sb.sb->magic            = BCACHE_MAGIC;
        sb.sb->block_size       = cpu_to_le16(fs_opts.block_size);
        sb.sb->user_uuid        = opts.uuid;
        sb.sb->nr_devices       = nr_devs;
 
+       if (opts.version == bcachefs_metadata_version_current)
+               sb.sb->features[0] |= BCH_SB_FEATURES_ALL;
+
        uuid_generate(sb.sb->uuid.b);
 
        if (opts.label)
@@ -253,7 +241,7 @@ struct bch_sb *bch2_format(struct bch_opt_strs      fs_opt_strs,
                m->first_bucket = 0;
                m->bucket_size  = cpu_to_le16(i->bucket_size);
 
-               SET_BCH_MEMBER_REPLACEMENT(m,   CACHE_REPLACEMENT_LRU);
+               SET_BCH_MEMBER_REPLACEMENT(m,   BCH_CACHE_REPLACEMENT_lru);
                SET_BCH_MEMBER_DISCARD(m,       i->discard);
                SET_BCH_MEMBER_DATA_ALLOWED(m,  i->data_allowed);
                SET_BCH_MEMBER_DURABILITY(m,    i->durability + 1);
@@ -280,6 +268,8 @@ struct bch_sb *bch2_format(struct bch_opt_strs      fs_opt_strs,
                parse_target(&sb, devs, nr_devs, fs_opt_strs.background_target));
        SET_BCH_SB_PROMOTE_TARGET(sb.sb,
                parse_target(&sb, devs, nr_devs, fs_opt_strs.promote_target));
+       SET_BCH_SB_METADATA_TARGET(sb.sb,
+               parse_target(&sb, devs, nr_devs, fs_opt_strs.metadata_target));
 
        /* Crypt: */
        if (opts.encrypted) {
@@ -293,9 +283,30 @@ struct bch_sb *bch2_format(struct bch_opt_strs     fs_opt_strs,
        for (i = devs; i < devs + nr_devs; i++) {
                sb.sb->dev_idx = i - devs;
 
+               if (!i->sb_offset) {
+                       i->sb_offset    = BCH_SB_SECTOR;
+                       i->sb_end       = i->size;
+               }
+
                init_layout(&sb.sb->layout, fs_opts.block_size,
+                           opts.superblock_size,
                            i->sb_offset, i->sb_end);
 
+               /*
+                * Also create a backup superblock at the end of the disk:
+                *
+                * If we're not creating a superblock at the default offset, it
+                * means we're being run from the migrate tool and we could be
+                * overwriting existing data if we write to the end of the disk:
+                */
+               if (i->sb_offset == BCH_SB_SECTOR) {
+                       struct bch_sb_layout *l = &sb.sb->layout;
+                       u64 backup_sb = i->size - (1 << l->sb_max_size_bits);
+
+                       backup_sb = rounddown(backup_sb, i->bucket_size);
+                       l->sb_offset[l->nr_superblocks++] = cpu_to_le64(backup_sb);
+               }
+
                if (i->sb_offset == BCH_SB_SECTOR) {
                        /* Zero start of disk */
                        static const char zeroes[BCH_SB_SECTOR << 9];
@@ -531,14 +542,14 @@ static void bch2_sb_print_members(struct bch_sb *sb, struct bch_sb_field *f,
                       time_str,
 
                       BCH_MEMBER_STATE(m) < BCH_MEMBER_STATE_NR
-                      ? bch2_dev_state[BCH_MEMBER_STATE(m)]
+                      ? bch2_member_states[BCH_MEMBER_STATE(m)]
                       : "unknown",
 
                       group,
                       data_allowed_str,
                       data_has_str,
 
-                      BCH_MEMBER_REPLACEMENT(m) < CACHE_REPLACEMENT_NR
+                      BCH_MEMBER_REPLACEMENT(m) < BCH_CACHE_REPLACEMENT_NR
                       ? bch2_cache_replacement_policies[BCH_MEMBER_REPLACEMENT(m)]
                       : "unknown",
 
@@ -617,6 +628,27 @@ static void bch2_sb_print_disk_groups(struct bch_sb *sb, struct bch_sb_field *f,
 static void bch2_sb_print_clean(struct bch_sb *sb, struct bch_sb_field *f,
                                enum units units)
 {
+       struct bch_sb_field_clean *clean = field_to_type(f, clean);
+
+
+       printf("  flags:       %x", le32_to_cpu(clean->flags));
+       printf("  journal seq: %llx", le64_to_cpu(clean->journal_seq));
+}
+
+static void bch2_sb_print_journal_seq_blacklist(struct bch_sb *sb, struct bch_sb_field *f,
+                               enum units units)
+{
+       struct bch_sb_field_journal_seq_blacklist *bl = field_to_type(f, journal_seq_blacklist);
+       unsigned i, nr = blacklist_nr_entries(bl);
+
+       for (i = 0; i < nr; i++) {
+               struct journal_seq_blacklist_entry *e =
+                       bl->start + i;
+
+               printf("  %llu-%llu\n",
+                      le64_to_cpu(e->start),
+                      le64_to_cpu(e->end));
+       }
 }
 
 typedef void (*sb_field_print_fn)(struct bch_sb *, struct bch_sb_field *, enum units);
@@ -651,12 +683,15 @@ void bch2_sb_print(struct bch_sb *sb, bool print_layout,
 {
        struct bch_sb_field_members *mi;
        char user_uuid_str[40], internal_uuid_str[40];
+       char features_str[500];
+       char compat_features_str[500];
        char fields_have_str[200];
        char label[BCH_SB_LABEL_SIZE + 1];
        char time_str[64];
        char foreground_str[64];
        char background_str[64];
        char promote_str[64];
+       char metadata_str[64];
        struct bch_sb_field *f;
        u64 fields_have = 0;
        unsigned nr_devices = 0;
@@ -696,6 +731,17 @@ void bch2_sb_print(struct bch_sb *sb, bool print_layout,
        bch2_sb_get_target(sb, promote_str, sizeof(promote_str),
                BCH_SB_PROMOTE_TARGET(sb));
 
+       bch2_sb_get_target(sb, metadata_str, sizeof(metadata_str),
+               BCH_SB_METADATA_TARGET(sb));
+
+       bch2_flags_to_text(&PBUF(features_str),
+                          bch2_sb_features,
+                          le64_to_cpu(sb->features[0]));
+
+       bch2_flags_to_text(&PBUF(compat_features_str),
+                          bch2_sb_compat,
+                          le64_to_cpu(sb->compat[0]));
+
        vstruct_for_each(sb, f)
                fields_have |= 1 << le32_to_cpu(f->type);
        bch2_flags_to_text(&PBUF(fields_have_str),
@@ -703,13 +749,18 @@ void bch2_sb_print(struct bch_sb *sb, bool print_layout,
 
        printf("External UUID:                  %s\n"
               "Internal UUID:                  %s\n"
+              "Device index:                   %u\n"
               "Label:                          %s\n"
-              "Version:                        %llu\n"
+              "Version:                        %u\n"
+              "Oldest version on disk:         %u\n"
               "Created:                        %s\n"
+              "Squence number:                 %llu\n"
               "Block_size:                     %s\n"
               "Btree node size:                %s\n"
               "Error action:                   %s\n"
               "Clean:                          %llu\n"
+              "Features:                       %s\n"
+              "Compat features:                %s\n"
 
               "Metadata replicas:              %llu\n"
               "Data replicas:                  %llu\n"
@@ -721,6 +772,7 @@ void bch2_sb_print(struct bch_sb *sb, bool print_layout,
               "Foreground write target:        %s\n"
               "Background write target:        %s\n"
               "Promote target:                 %s\n"
+               "Metadata target:                %s\n"
 
               "String hash type:               %s (%llu)\n"
               "32 bit inodes:                  %llu\n"
@@ -732,39 +784,45 @@ void bch2_sb_print(struct bch_sb *sb, bool print_layout,
               "Superblock size:                %llu\n",
               user_uuid_str,
               internal_uuid_str,
+              sb->dev_idx,
               label,
-              le64_to_cpu(sb->version),
+              le16_to_cpu(sb->version),
+              le16_to_cpu(sb->version_min),
               time_str,
+              le64_to_cpu(sb->seq),
               pr_units(le16_to_cpu(sb->block_size), units),
               pr_units(BCH_SB_BTREE_NODE_SIZE(sb), units),
 
-              BCH_SB_ERROR_ACTION(sb) < BCH_NR_ERROR_ACTIONS
+              BCH_SB_ERROR_ACTION(sb) < BCH_ON_ERROR_NR
               ? bch2_error_actions[BCH_SB_ERROR_ACTION(sb)]
               : "unknown",
 
               BCH_SB_CLEAN(sb),
+              features_str,
+              compat_features_str,
 
               BCH_SB_META_REPLICAS_WANT(sb),
               BCH_SB_DATA_REPLICAS_WANT(sb),
 
               BCH_SB_META_CSUM_TYPE(sb) < BCH_CSUM_OPT_NR
-              ? bch2_csum_types[BCH_SB_META_CSUM_TYPE(sb)]
+              ? bch2_csum_opts[BCH_SB_META_CSUM_TYPE(sb)]
               : "unknown",
               BCH_SB_META_CSUM_TYPE(sb),
 
               BCH_SB_DATA_CSUM_TYPE(sb) < BCH_CSUM_OPT_NR
-              ? bch2_csum_types[BCH_SB_DATA_CSUM_TYPE(sb)]
+              ? bch2_csum_opts[BCH_SB_DATA_CSUM_TYPE(sb)]
               : "unknown",
               BCH_SB_DATA_CSUM_TYPE(sb),
 
               BCH_SB_COMPRESSION_TYPE(sb) < BCH_COMPRESSION_OPT_NR
-              ? bch2_compression_types[BCH_SB_COMPRESSION_TYPE(sb)]
+              ? bch2_compression_opts[BCH_SB_COMPRESSION_TYPE(sb)]
               : "unknown",
               BCH_SB_COMPRESSION_TYPE(sb),
 
               foreground_str,
               background_str,
               promote_str,
+               metadata_str,
 
               BCH_SB_STR_HASH_TYPE(sb) < BCH_STR_HASH_NR
               ? bch2_str_hash_types[BCH_SB_STR_HASH_TYPE(sb)]
@@ -864,7 +922,7 @@ struct bchfs_handle bcache_fs_open(const char *path)
  * Given a path to a block device, open the filesystem it belongs to; also
  * return the device's idx:
  */
-struct bchfs_handle bchu_fs_open_by_dev(const char *path, unsigned *idx)
+struct bchfs_handle bchu_fs_open_by_dev(const char *path, int *idx)
 {
        char buf[1024], *uuid_str;
 
@@ -908,6 +966,17 @@ struct bchfs_handle bchu_fs_open_by_dev(const char *path, unsigned *idx)
        return bcache_fs_open(uuid_str);
 }
 
+int bchu_dev_path_to_idx(struct bchfs_handle fs, const char *dev_path)
+{
+       int idx;
+       struct bchfs_handle fs2 = bchu_fs_open_by_dev(dev_path, &idx);
+
+       if (memcmp(&fs.uuid, &fs2.uuid, sizeof(fs.uuid)))
+               idx = -1;
+       bcache_fs_close(fs2);
+       return idx;
+}
+
 int bchu_data(struct bchfs_handle fs, struct bch_ioctl_data cmd)
 {
        int progress_fd = xioctl(fs.ioctl_fd, BCH_IOCTL_DATA, &cmd);
@@ -927,18 +996,21 @@ int bchu_data(struct bchfs_handle fs, struct bch_ioctl_data cmd)
                printf("\33[2K\r");
 
                printf("%llu%% complete: current position %s",
-                      e.p.sectors_done * 100 / e.p.sectors_total,
+                      e.p.sectors_total
+                      ? e.p.sectors_done * 100 / e.p.sectors_total
+                      : 0,
                       bch2_data_types[e.p.data_type]);
 
                switch (e.p.data_type) {
-               case BCH_DATA_BTREE:
-               case BCH_DATA_USER:
+               case BCH_DATA_btree:
+               case BCH_DATA_user:
                        printf(" %s:%llu:%llu",
                               bch2_btree_ids[e.p.btree_id],
                               e.p.pos.inode,
                               e.p.pos.offset);
                }
 
+               fflush(stdout);
                sleep(1);
        }
        printf("\nDone\n");
@@ -949,6 +1021,16 @@ int bchu_data(struct bchfs_handle fs, struct bch_ioctl_data cmd)
 
 /* option parsing */
 
+void bch2_opt_strs_free(struct bch_opt_strs *opts)
+{
+       unsigned i;
+
+       for (i = 0; i < bch2_opts_nr; i++) {
+               free(opts->by_id[i]);
+               opts->by_id[i] = NULL;
+       }
+}
+
 struct bch_opt_strs bch2_cmdline_opts_get(int *argc, char *argv[],
                                          unsigned opt_types)
 {
@@ -981,9 +1063,8 @@ struct bch_opt_strs bch2_cmdline_opts_get(int *argc, char *argv[],
                optid = bch2_opt_lookup(optstr);
                if (optid < 0 ||
                    !(bch2_opt_table[optid].mode & opt_types)) {
-                       free(optstr);
                        i++;
-                       continue;
+                       goto next;
                }
 
                if (!valstr &&
@@ -995,13 +1076,15 @@ struct bch_opt_strs bch2_cmdline_opts_get(int *argc, char *argv[],
                if (!valstr)
                        valstr = "1";
 
-               opts.by_id[optid] = valstr;
+               opts.by_id[optid] = strdup(valstr);
 
                *argc -= nr_args;
                memmove(&argv[i],
                        &argv[i + nr_args],
                        sizeof(char *) * (*argc - i));
                argv[*argc] = NULL;
+next:
+               free(optstr);
        }
 
        return opts;
@@ -1022,7 +1105,9 @@ struct bch_opts bch2_parse_opts(struct bch_opt_strs strs)
                ret = bch2_opt_parse(NULL, &bch2_opt_table[i],
                                     strs.by_id[i], &v);
                if (ret < 0)
-                       die("Invalid %s: %s", strs.by_id[i], strerror(-ret));
+                       die("Invalid %s: %s",
+                           bch2_opt_table[i].attr.name,
+                           strerror(-ret));
 
                bch2_opt_set_by_id(&opts, i, v);
        }
@@ -1030,22 +1115,17 @@ struct bch_opts bch2_parse_opts(struct bch_opt_strs strs)
        return opts;
 }
 
+#define newline(c)             \
+       do {                    \
+               printf("\n");   \
+               c = 0;          \
+       } while(0)
 void bch2_opts_usage(unsigned opt_types)
 {
        const struct bch_option *opt;
        unsigned i, c = 0, helpcol = 30;
 
-       void tabalign() {
-               while (c < helpcol) {
-                       putchar(' ');
-                       c++;
-               }
-       }
 
-       void newline() {
-               printf("\n");
-               c = 0;
-       }
 
        for (opt = bch2_opt_table;
             opt < bch2_opt_table + bch2_opts_nr;
@@ -1076,21 +1156,62 @@ void bch2_opts_usage(unsigned opt_types)
                        const char *l = opt->help;
 
                        if (c >= helpcol)
-                               newline();
+                               newline(c);
 
                        while (1) {
                                const char *n = strchrnul(l, '\n');
 
-                               tabalign();
+                               while (c < helpcol) {
+                                       putchar(' ');
+                                       c++;
+                               }
                                printf("%.*s", (int) (n - l), l);
-                               newline();
+                               newline(c);
 
                                if (!*n)
                                        break;
                                l = n + 1;
                        }
                } else {
-                       newline();
+                       newline(c);
                }
        }
 }
+
+dev_names bchu_fs_get_devices(struct bchfs_handle fs)
+{
+       DIR *dir = fdopendir(fs.sysfs_fd);
+       struct dirent *d;
+       dev_names devs;
+
+       darray_init(devs);
+
+       while ((errno = 0), (d = readdir(dir))) {
+               struct dev_name n = { 0, NULL, NULL };
+
+               if (sscanf(d->d_name, "dev-%u", &n.idx) != 1)
+                       continue;
+
+               char *block_attr = mprintf("dev-%u/block", n.idx);
+
+               char sysfs_block_buf[4096];
+               ssize_t r = readlinkat(fs.sysfs_fd, block_attr,
+                                      sysfs_block_buf, sizeof(sysfs_block_buf));
+               if (r > 0) {
+                       sysfs_block_buf[r] = '\0';
+                       n.dev = strdup(basename(sysfs_block_buf));
+               }
+
+               free(block_attr);
+
+               char *label_attr = mprintf("dev-%u/label", n.idx);
+               n.label = read_file_str(fs.sysfs_fd, label_attr);
+               free(label_attr);
+
+               darray_append(devs, n);
+       }
+
+       closedir(dir);
+
+       return devs;
+}