summaryrefslogtreecommitdiff
path: root/c_src
diff options
context:
space:
mode:
Diffstat (limited to 'c_src')
-rw-r--r--c_src/cmd_format.c3
-rw-r--r--c_src/cmd_fsck.c75
-rw-r--r--c_src/cmd_fusemount.c49
-rw-r--r--c_src/cmd_kill_btree_node.c75
-rw-r--r--c_src/cmd_list_journal.c103
-rw-r--r--c_src/posix_to_bcachefs.c8
-rw-r--r--c_src/tools-util.c33
-rw-r--r--c_src/tools-util.h4
8 files changed, 257 insertions, 93 deletions
diff --git a/c_src/cmd_format.c b/c_src/cmd_format.c
index d0c8e197..152f7f37 100644
--- a/c_src/cmd_format.c
+++ b/c_src/cmd_format.c
@@ -220,8 +220,7 @@ int cmd_format(int argc, char *argv[])
unconsumed_dev_option = true;
break;
case O_version:
- if (kstrtouint(optarg, 10, &opts.version))
- die("invalid version");
+ opts.version = version_parse(optarg);
break;
case O_no_initialize:
initialize = false;
diff --git a/c_src/cmd_fsck.c b/c_src/cmd_fsck.c
index b532de56..2ea51ff2 100644
--- a/c_src/cmd_fsck.c
+++ b/c_src/cmd_fsck.c
@@ -90,12 +90,14 @@ static int splice_fd_to_stdinout(int fd)
return close(fd);
}
-static int fsck_online(const char *dev_path)
+static int fsck_online(const char *dev_path, const char *opt_str)
{
int dev_idx;
struct bchfs_handle fs = bchu_fs_open_by_dev(dev_path, &dev_idx);
- struct bch_ioctl_fsck_online fsck = { 0 };
+ struct bch_ioctl_fsck_online fsck = {
+ .opts = (unsigned long) opt_str
+ };
int fsck_fd = ioctl(fs.ioctl_fd, BCH_IOCTL_FSCK_ONLINE, &fsck);
if (fsck_fd < 0)
@@ -160,6 +162,45 @@ static bool should_use_kernel_fsck(darray_str devs)
return ret;
}
+static bool is_blockdev(const char *path)
+{
+ struct stat s;
+ if (stat(path, &s))
+ return true;
+ return S_ISBLK(s.st_mode);
+}
+
+static void loopdev_free(const char *path)
+{
+ char *cmd = mprintf("losetup -d %s", path);
+ system(cmd);
+ free(cmd);
+}
+
+static char *loopdev_alloc(const char *path)
+{
+ char *cmd = mprintf("losetup --show -f %s", path);
+ FILE *f = popen(cmd, "r");
+ free(cmd);
+ if (!f) {
+ fprintf(stderr, "error executing losetup: %m\n");
+ return NULL;
+ }
+
+ char *line = NULL;
+ size_t n = 0;
+ getline(&line, &n, f);
+ int ret = pclose(f);
+ if (ret) {
+ fprintf(stderr, "error executing losetup: %i\n", ret);
+ free(line);
+ return NULL;
+ }
+
+ strim(line);
+ return line;
+}
+
int cmd_fsck(int argc, char *argv[])
{
static const struct option longopts[] = {
@@ -183,7 +224,7 @@ int cmd_fsck(int argc, char *argv[])
append_opt(&opts_str, "read_only");
while ((opt = getopt_long(argc, argv,
- "apynfo:rRkvh",
+ "apynfo:rRkKvh",
longopts, NULL)) != -1)
switch (opt) {
case 'a': /* outdated alias for -p */
@@ -232,7 +273,7 @@ int cmd_fsck(int argc, char *argv[])
darray_for_each(devs, i)
if (dev_mounted(*i)) {
printf("Running fsck online\n");
- return fsck_online(*i);
+ return fsck_online(*i, opts_str.buf);
}
int kernel_probed = kernel;
@@ -243,19 +284,35 @@ int cmd_fsck(int argc, char *argv[])
struct printbuf parse_later = PRINTBUF;
if (kernel_probed) {
+ darray_str loopdevs = {};
+ int fsck_fd = -1;
+
printf("Running in-kernel offline fsck\n");
- struct bch_ioctl_fsck_offline *fsck = calloc(sizeof(*fsck) +
- sizeof(u64) * devs.nr, 1);
+ struct bch_ioctl_fsck_offline *fsck = calloc(sizeof(*fsck) + sizeof(u64) * devs.nr, 1);
fsck->opts = (unsigned long)opts_str.buf;
- darray_for_each(devs, i)
- fsck->devs[i - devs.data] = (unsigned long) *i;
+ darray_for_each(devs, i) {
+ if (is_blockdev(*i)) {
+ fsck->devs[i - devs.data] = (unsigned long) *i;
+ } else {
+ char *l = loopdev_alloc(*i);
+ if (!l)
+ goto kernel_fsck_err;
+ darray_push(&loopdevs, l);
+ fsck->devs[i - devs.data] = (unsigned long) l;
+ }
+ }
fsck->nr_devs = devs.nr;
int ctl_fd = bcachectl_open();
- int fsck_fd = ioctl(ctl_fd, BCH_IOCTL_FSCK_OFFLINE, fsck);
+ fsck_fd = ioctl(ctl_fd, BCH_IOCTL_FSCK_OFFLINE, fsck);
+kernel_fsck_err:
free(fsck);
+ darray_for_each(loopdevs, i)
+ loopdev_free(*i);
+ darray_exit(&loopdevs);
+
if (fsck_fd < 0 && kernel < 0)
goto userland_fsck;
diff --git a/c_src/cmd_fusemount.c b/c_src/cmd_fusemount.c
index f43d29f5..e5674b42 100644
--- a/c_src/cmd_fusemount.c
+++ b/c_src/cmd_fusemount.c
@@ -242,7 +242,7 @@ static int do_create(struct bch_fs *c, subvol_inum dir,
bch2_inode_init_early(c, new_inode);
- return bch2_trans_do(c, NULL, NULL, 0,
+ return bch2_trans_commit_do(c, NULL, NULL, 0,
bch2_create_trans(trans,
dir, &dir_u,
new_inode, &qstr,
@@ -295,7 +295,7 @@ static void bcachefs_fuse_unlink(fuse_req_t req, fuse_ino_t dir_ino,
fuse_log(FUSE_LOG_DEBUG, "bcachefs_fuse_unlink(%llu, %s)\n", dir.inum, name);
- int ret = bch2_trans_do(c, NULL, NULL,
+ int ret = bch2_trans_commit_do(c, NULL, NULL,
BCH_TRANS_COMMIT_no_enospc,
bch2_unlink_trans(trans, dir, &dir_u,
&inode_u, &qstr, false));
@@ -330,7 +330,7 @@ static void bcachefs_fuse_rename(fuse_req_t req,
src_dir.inum, srcname, dst_dir.inum, dstname, flags);
/* XXX handle overwrites */
- ret = bch2_trans_do(c, NULL, NULL, 0,
+ ret = bch2_trans_commit_do(c, NULL, NULL, 0,
bch2_rename_trans(trans,
src_dir, &src_dir_u,
dst_dir, &dst_dir_u,
@@ -354,7 +354,7 @@ static void bcachefs_fuse_link(fuse_req_t req, fuse_ino_t ino,
fuse_log(FUSE_LOG_DEBUG, "bcachefs_fuse_link(%llu, %llu, %s)\n",
inum.inum, newparent.inum, newname);
- ret = bch2_trans_do(c, NULL, NULL, 0,
+ ret = bch2_trans_commit_do(c, NULL, NULL, 0,
bch2_link_trans(trans, newparent, &dir_u,
inum, &inode_u, &qstr));
@@ -1203,6 +1203,7 @@ int cmd_fusemount(int argc, char *argv[])
struct bch_opts bch_opts = bch2_opts_empty();
struct bf_context ctx = { 0 };
struct bch_fs *c = NULL;
+ struct fuse_session *se = NULL;
int ret = 0, i;
/* Parse arguments. */
@@ -1221,9 +1222,9 @@ int cmd_fusemount(int argc, char *argv[])
goto out;
}
if (fuse_opts.show_version) {
- /* TODO: Show bcachefs version. */
printf("FUSE library version %s\n", fuse_pkgversion());
fuse_lowlevel_version();
+ printf("bcachefs version: %s\n", VERSION_STRING);
ret = 0;
goto out;
}
@@ -1263,17 +1264,22 @@ int cmd_fusemount(int argc, char *argv[])
bch2_err_str(PTR_ERR(c)));
/* Fuse */
- struct fuse_session *se =
- fuse_session_new(&args, &bcachefs_fuse_ops,
- sizeof(bcachefs_fuse_ops), c);
- if (!se)
- die("fuse_lowlevel_new err: %m");
+ se = fuse_session_new(&args, &bcachefs_fuse_ops,
+ sizeof(bcachefs_fuse_ops), c);
+ if (!se) {
+ fprintf(stderr, "fuse_lowlevel_new err: %m\n");
+ goto err;
+ }
- if (fuse_set_signal_handlers(se) < 0)
- die("fuse_set_signal_handlers err: %m");
+ if (fuse_set_signal_handlers(se) < 0) {
+ fprintf(stderr, "fuse_set_signal_handlers err: %m\n");
+ goto err;
+ }
- if (fuse_session_mount(se, fuse_opts.mountpoint))
- die("fuse_mount err: %m");
+ if (fuse_session_mount(se, fuse_opts.mountpoint)) {
+ fprintf(stderr, "fuse_mount err: %m\n");
+ goto err;
+ }
/* This print statement is a trigger for tests. */
printf("Fuse mount initialized.\n");
@@ -1287,17 +1293,22 @@ int cmd_fusemount(int argc, char *argv[])
ret = fuse_session_loop(se);
- /* Cleanup */
- fuse_session_unmount(se);
- fuse_remove_signal_handlers(se);
- fuse_session_destroy(se);
-
out:
+ if (se) {
+ fuse_session_unmount(se);
+ fuse_remove_signal_handlers(se);
+ fuse_session_destroy(se);
+ }
+
free(fuse_opts.mountpoint);
fuse_opt_free_args(&args);
bf_context_free(&ctx);
return ret ? 1 : 0;
+
+err:
+ bch2_fs_stop(c);
+ goto out;
}
#endif /* BCACHEFS_FUSE */
diff --git a/c_src/cmd_kill_btree_node.c b/c_src/cmd_kill_btree_node.c
index fcb49f15..c8f43150 100644
--- a/c_src/cmd_kill_btree_node.c
+++ b/c_src/cmd_kill_btree_node.c
@@ -27,30 +27,44 @@ static void kill_btree_node_usage(void)
"Report bugs to <linux-bcachefs@vger.kernel.org>");
}
+struct kill_node {
+ unsigned btree;
+ unsigned level;
+ u64 idx;
+};
+
int cmd_kill_btree_node(int argc, char *argv[])
{
struct bch_opts opts = bch2_opts_empty();
- enum btree_id btree_id = 0;
- unsigned level = 0;
- u64 node_index = 0;
+ DARRAY(struct kill_node) kill_nodes = {};
int opt;
opt_set(opts, read_only, true);
- while ((opt = getopt(argc, argv, "b:l:i:h")) != -1)
+ while ((opt = getopt(argc, argv, "n:h")) != -1)
switch (opt) {
- case 'b':
- btree_id = read_string_list_or_die(optarg,
- __bch2_btree_ids, "btree id");
- break;
- case 'l':
- if (kstrtouint(optarg, 10, &level) || level >= BTREE_MAX_DEPTH)
+ case 'n': {
+ char *p = optarg;
+ const char *str_btree = strsep(&p, ":");
+ const char *str_level = strsep(&p, ":");
+ const char *str_idx = strsep(&p, ":");
+
+ struct kill_node n = {
+ .btree = read_string_list_or_die(str_btree,
+ __bch2_btree_ids, "btree id"),
+ };
+
+ if (str_level &&
+ (kstrtouint(str_level, 10, &n.level) || n.level >= BTREE_MAX_DEPTH))
die("invalid level");
+
+ if (str_idx &&
+ kstrtoull(str_idx, 10, &n.idx))
+ die("invalid index %s", str_idx);
+
+ darray_push(&kill_nodes, n);
break;
- case 'i':
- if (kstrtoull(optarg, 10, &node_index))
- die("invalid index %s", optarg);
- break;
+ }
case 'h':
kill_btree_node_usage();
exit(EXIT_SUCCESS);
@@ -71,16 +85,19 @@ int cmd_kill_btree_node(int argc, char *argv[])
if (ret)
die("error %s from posix_memalign", bch2_err_str(ret));
- ret = bch2_trans_run(c,
- __for_each_btree_node(trans, iter, btree_id, POS_MIN, 0, level, 0, b, ({
- if (b->c.level != level)
+ struct btree_trans *trans = bch2_trans_get(c);
+
+ darray_for_each(kill_nodes, i) {
+ ret = __for_each_btree_node(trans, iter, i->btree, POS_MIN, 0, i->level, 0, b, ({
+ if (b->c.level != i->level)
continue;
int ret2 = 0;
- if (!node_index) {
+ if (!i->idx) {
struct printbuf buf = PRINTBUF;
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&b->key));
- bch_info(c, "killing btree node %s", buf.buf);
+ bch_info(c, "killing btree node %s l=%u %s",
+ bch2_btree_id_str(i->btree), i->level, buf.buf);
printbuf_exit(&buf);
ret2 = 1;
@@ -102,16 +119,22 @@ int cmd_kill_btree_node(int argc, char *argv[])
}
}
- node_index--;
+ i->idx--;
ret2;
- })));
- if (ret < 0)
- bch_err(c, "error %i walking btree nodes", ret);
- else if (!ret) {
- bch_err(c, "node at specified index not found");
- ret = EXIT_FAILURE;
+ }));
+
+ if (ret < 0) {
+ bch_err(c, "error %i walking btree nodes", ret);
+ break;
+ } else if (!ret) {
+ bch_err(c, "node at specified index not found");
+ ret = EXIT_FAILURE;
+ break;
+ }
}
+ bch2_trans_put(trans);
bch2_fs_stop(c);
+ darray_exit(&kill_nodes);
return ret < 0 ? ret : 0;
}
diff --git a/c_src/cmd_list_journal.c b/c_src/cmd_list_journal.c
index c15655c0..fe7f9b05 100644
--- a/c_src/cmd_list_journal.c
+++ b/c_src/cmd_list_journal.c
@@ -80,15 +80,23 @@ static bool entry_matches_transaction_filter(struct jset_entry *entry,
}
static bool should_print_transaction(struct jset_entry *entry, struct jset_entry *end,
- d_bbpos_range filter)
+ darray_str msg_filter,
+ d_bbpos_range key_filter)
{
- if (!filter.nr)
+ struct jset_entry_log *l = container_of(entry, struct jset_entry_log, entry);
+ unsigned b = jset_entry_log_msg_bytes(l);
+
+ darray_for_each(msg_filter, i)
+ if (!strncmp(*i, l->d, b))
+ return false;
+
+ if (!key_filter.nr)
return true;
for (entry = vstruct_next(entry);
entry != end && !entry_is_transaction_start(entry);
entry = vstruct_next(entry))
- if (entry_matches_transaction_filter(entry, filter))
+ if (entry_matches_transaction_filter(entry, key_filter))
return true;
return false;
@@ -112,8 +120,40 @@ static bool should_print_entry(struct jset_entry *entry, d_btree_id filter)
return false;
}
+static void journal_entry_header_to_text(struct printbuf *out,
+ struct bch_fs *c,
+ struct journal_replay *p, bool blacklisted)
+{
+ if (blacklisted)
+ prt_str(out, "blacklisted ");
+
+ prt_printf(out,
+ "journal entry %llu\n"
+ " version %u\n"
+ " last seq %llu\n"
+ " flush %u\n"
+ " written at ",
+ le64_to_cpu(p->j.seq),
+ le32_to_cpu(p->j.version),
+ le64_to_cpu(p->j.last_seq),
+ !JSET_NO_FLUSH(&p->j));
+ bch2_journal_ptrs_to_text(out, c, p);
+
+ if (blacklisted)
+ star_start_of_lines(out->buf);
+}
+
+static void journal_entry_header_print(struct bch_fs *c, struct journal_replay *p, bool blacklisted)
+{
+ struct printbuf buf = PRINTBUF;
+ journal_entry_header_to_text(&buf, c, p, blacklisted);
+ printf("%s\n", buf.buf);
+ printbuf_exit(&buf);
+}
+
static void journal_entries_print(struct bch_fs *c, unsigned nr_entries,
- d_bbpos_range transaction_filter,
+ darray_str transaction_msg_filter,
+ d_bbpos_range transaction_key_filter,
d_btree_id key_filter)
{
struct journal_replay *p, **_p;
@@ -121,6 +161,8 @@ static void journal_entries_print(struct bch_fs *c, unsigned nr_entries,
struct printbuf buf = PRINTBUF;
genradix_for_each(&c->journal_entries, iter, _p) {
+ bool printed_header = false;
+
p = *_p;
if (!p)
continue;
@@ -132,28 +174,10 @@ static void journal_entries_print(struct bch_fs *c, unsigned nr_entries,
bch2_journal_seq_is_blacklisted(c,
le64_to_cpu(p->j.seq), false);
- if (!transaction_filter.nr) {
- if (blacklisted)
- printf("blacklisted ");
-
- printf("journal entry %llu\n", le64_to_cpu(p->j.seq));
-
- printbuf_reset(&buf);
-
- prt_printf(&buf,
- " version %u\n"
- " last seq %llu\n"
- " flush %u\n"
- " written at ",
- le32_to_cpu(p->j.version),
- le64_to_cpu(p->j.last_seq),
- !JSET_NO_FLUSH(&p->j));
- bch2_journal_ptrs_to_text(&buf, c, p);
-
- if (blacklisted)
- star_start_of_lines(buf.buf);
- printf("%s\n", buf.buf);
- printbuf_reset(&buf);
+ if (!transaction_msg_filter.nr &&
+ !transaction_key_filter.nr) {
+ journal_entry_header_print(c, p, blacklisted);
+ printed_header = true;
}
struct jset_entry *entry = p->j.start;
@@ -165,7 +189,9 @@ static void journal_entries_print(struct bch_fs *c, unsigned nr_entries,
* commit:
*/
if (entry_is_transaction_start(entry)) {
- if (!should_print_transaction(entry, end, transaction_filter)) {
+ if (!should_print_transaction(entry, end,
+ transaction_msg_filter,
+ transaction_key_filter)) {
do {
entry = vstruct_next(entry);
} while (entry != end && !entry_is_transaction_start(entry));
@@ -179,7 +205,11 @@ static void journal_entries_print(struct bch_fs *c, unsigned nr_entries,
if (!should_print_entry(entry, key_filter))
goto next;
- bool highlight = entry_matches_transaction_filter(entry, transaction_filter);
+ if (!printed_header)
+ journal_entry_header_print(c, p, blacklisted);
+ printed_header = true;
+
+ bool highlight = entry_matches_transaction_filter(entry, transaction_key_filter);
if (highlight)
fputs(RED, stdout);
@@ -213,8 +243,9 @@ int cmd_list_journal(int argc, char *argv[])
};
struct bch_opts opts = bch2_opts_empty();
u32 nr_entries = U32_MAX;
- d_bbpos_range transaction_filter = { 0 };
- d_btree_id key_filter = { 0 };
+ darray_str transaction_msg_filter = {};
+ d_bbpos_range transaction_key_filter = {};
+ d_btree_id key_filter = {};
int opt;
opt_set(opts, noexcl, true);
@@ -228,7 +259,7 @@ int cmd_list_journal(int argc, char *argv[])
opt_set(opts, retain_recovery_info ,true);
opt_set(opts, read_journal_only,true);
- while ((opt = getopt_long(argc, argv, "an:t:k:vh",
+ while ((opt = getopt_long(argc, argv, "an:m:t:k:vh",
longopts, NULL)) != -1)
switch (opt) {
case 'a':
@@ -239,8 +270,11 @@ int cmd_list_journal(int argc, char *argv[])
die("error parsing nr_entries");
opt_set(opts, read_entire_journal, true);
break;
+ case 'm':
+ darray_push(&transaction_msg_filter, strdup(optarg));
+ break;
case 't':
- darray_push(&transaction_filter, bbpos_range_parse(optarg));
+ darray_push(&transaction_key_filter, bbpos_range_parse(optarg));
break;
case 'k':
darray_push(&key_filter, read_string_list_or_die(optarg, __bch2_btree_ids, "btree id"));
@@ -263,7 +297,10 @@ int cmd_list_journal(int argc, char *argv[])
if (IS_ERR(c))
die("error opening %s: %s", argv[0], bch2_err_str(PTR_ERR(c)));
- journal_entries_print(c, nr_entries, transaction_filter, key_filter);
+ journal_entries_print(c, nr_entries,
+ transaction_msg_filter,
+ transaction_key_filter,
+ key_filter);
bch2_fs_stop(c);
return 0;
}
diff --git a/c_src/posix_to_bcachefs.c b/c_src/posix_to_bcachefs.c
index 19c53cc3..d4701263 100644
--- a/c_src/posix_to_bcachefs.c
+++ b/c_src/posix_to_bcachefs.c
@@ -32,7 +32,7 @@ void create_link(struct bch_fs *c,
struct bch_inode_unpacked parent_u;
struct bch_inode_unpacked inode;
- int ret = bch2_trans_do(c, NULL, NULL, 0,
+ int ret = bch2_trans_commit_do(c, NULL, NULL, 0,
bch2_link_trans(trans,
(subvol_inum) { 1, parent->bi_inum }, &parent_u,
(subvol_inum) { 1, inum }, &inode, &qstr));
@@ -51,7 +51,7 @@ struct bch_inode_unpacked create_file(struct bch_fs *c,
bch2_inode_init_early(c, &new_inode);
- int ret = bch2_trans_do(c, NULL, NULL, 0,
+ int ret = bch2_trans_commit_do(c, NULL, NULL, 0,
bch2_create_trans(trans,
(subvol_inum) { 1, parent->bi_inum }, parent,
&new_inode, &qstr,
@@ -71,7 +71,7 @@ struct bch_inode_unpacked create_file(struct bch_fs *c,
static const struct xattr_handler *xattr_resolve_name(char **name)
{
- const struct xattr_handler **handlers = bch2_xattr_handlers;
+ const struct xattr_handler * const *handlers = bch2_xattr_handlers;
const struct xattr_handler *handler;
for_each_xattr_handler(handlers, handler) {
@@ -125,7 +125,7 @@ void copy_xattrs(struct bch_fs *c, struct bch_inode_unpacked *dst,
if (IS_ERR(h))
continue;
- int ret = bch2_trans_do(c, NULL, NULL, 0,
+ int ret = bch2_trans_commit_do(c, NULL, NULL, 0,
bch2_xattr_set(trans,
(subvol_inum) { 1, dst->bi_inum },
dst, &hash_info, attr,
diff --git a/c_src/tools-util.c b/c_src/tools-util.c
index 43c9b789..3df2b004 100644
--- a/c_src/tools-util.c
+++ b/c_src/tools-util.c
@@ -185,6 +185,24 @@ unsigned get_blocksize(int fd)
/* Open a block device, do magic blkid stuff to probe for existing filesystems: */
int open_for_format(struct dev_opts *dev, bool force)
{
+ int blkid_version_code = blkid_get_library_version(NULL, NULL);
+ if (blkid_version_code < 2401) {
+ if (force) {
+ fprintf(
+ stderr,
+ "Continuing with out of date libblkid %s because --force was passed.\n",
+ BLKID_VERSION);
+ } else {
+ // Reference for picking 2.40.1:
+ // https://mirrors.edge.kernel.org/pub/linux/utils/util-linux/v2.40/v2.40.1-ReleaseNotes
+ // https://github.com/util-linux/util-linux/issues/3103
+ die(
+ "Refusing to format when using libblkid %s\n"
+ "libblkid >= 2.40.1 is required to check for existing filesystems\n"
+ "Earlier versions may not recognize some bcachefs filesystems.\n", BLKID_VERSION);
+ }
+ }
+
blkid_probe pr;
const char *fs_type = NULL, *fs_label = NULL;
size_t fs_type_len, fs_label_len;
@@ -708,6 +726,21 @@ struct bbpos_range bbpos_range_parse(char *buf)
return (struct bbpos_range) { .start = start, .end = end };
}
+unsigned version_parse(char *buf)
+{
+ char *s = buf;
+ char *major_str = strsep(&s, ".");
+ char *minor_str = strsep(&s, ".");
+
+ unsigned major, minor;
+
+ if (kstrtouint(major_str, 10, &major) ||
+ kstrtouint(minor_str, 10, &minor))
+ die("invalid version");
+
+ return BCH_VERSION(major, minor);
+}
+
darray_str get_or_split_cmdline_devs(int argc, char *argv[])
{
darray_str ret = {};
diff --git a/c_src/tools-util.h b/c_src/tools-util.h
index 269d589b..1694ad86 100644
--- a/c_src/tools-util.h
+++ b/c_src/tools-util.h
@@ -174,6 +174,8 @@ struct fiemap_extent fiemap_iter_next(struct fiemap_iter *);
char *strcmp_prefix(char *, const char *);
+/* Avoid conflicts with libblkid's crc32 function in static builds */
+#define crc32c bch_crc32c
u32 crc32c(u32, const void *, size_t);
char *dev_to_name(dev_t);
@@ -207,6 +209,8 @@ struct bbpos_range {
struct bbpos_range bbpos_range_parse(char *);
+unsigned version_parse(char *);
+
darray_str get_or_split_cmdline_devs(int argc, char *argv[]);
#endif /* _TOOLS_UTIL_H */