diff options
Diffstat (limited to 'c_src')
-rw-r--r-- | c_src/cmd_format.c | 3 | ||||
-rw-r--r-- | c_src/cmd_fsck.c | 75 | ||||
-rw-r--r-- | c_src/cmd_fusemount.c | 49 | ||||
-rw-r--r-- | c_src/cmd_kill_btree_node.c | 75 | ||||
-rw-r--r-- | c_src/cmd_list_journal.c | 103 | ||||
-rw-r--r-- | c_src/posix_to_bcachefs.c | 8 | ||||
-rw-r--r-- | c_src/tools-util.c | 33 | ||||
-rw-r--r-- | c_src/tools-util.h | 4 |
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 */ |