if (arg.flags || arg.pad)
return -EINVAL;
- return bch2_fs_start(c) ? -EIO : 0;
+ return bch2_fs_start(c);
}
static long bch2_ioctl_stop(struct bch_fs *c)
down_write(&sb->s_umount);
sb->s_flags |= SB_RDONLY;
- bch2_fs_emergency_read_only(c);
+ if (bch2_fs_emergency_read_only(c))
+ bch_err(c, "emergency read only due to ioctl");
up_write(&sb->s_umount);
return 0;
vinode = bch2_vfs_inode_get(c, BCACHEFS_ROOT_INO);
if (IS_ERR(vinode)) {
+ bch_err(c, "error mounting: error getting root inode %i",
+ (int) PTR_ERR(vinode));
ret = PTR_ERR(vinode);
goto err_put_super;
}
sb->s_root = d_make_root(vinode);
if (!sb->s_root) {
+ bch_err(c, "error mounting: error allocating root dentry");
ret = -ENOMEM;
goto err_put_super;
}
BTREE_INSERT_NOFAIL|
BTREE_INSERT_LAZY_RW);
if (ret) {
- bch_err(c, "error in fs gc: error %i "
- "updating inode", ret);
+ bch_err(c, "error in fsck: error %i updating inode", ret);
goto err;
}
link = genradix_ptr_alloc(links, inum - range_start, GFP_KERNEL);
if (!link) {
- bch_verbose(c, "allocation failed during fs gc - will need another pass");
+ bch_verbose(c, "allocation failed during fsck - will need another pass");
*range_end = inum;
return;
}
}
ret = bch2_trans_exit(&trans) ?: ret;
if (ret)
- bch_err(c, "error in fs gc: btree error %i while walking dirents", ret);
+ bch_err(c, "error in fsck: btree error %i while walking dirents", ret);
return ret;
}
ret = bch2_inode_rm(c, u.bi_inum);
if (ret)
- bch_err(c, "error in fs gc: error %i "
- "while deleting inode", ret);
+ bch_err(c, "error in fsck: error %i while deleting inode", ret);
return ret;
}
ret = bch2_inode_truncate(c, u.bi_inum, u.bi_size);
if (ret) {
- bch_err(c, "error in fs gc: error %i "
- "truncating inode", ret);
+ bch_err(c, "error in fsck: error %i truncating inode", ret);
return ret;
}
sectors = bch2_count_inode_sectors(trans, u.bi_inum);
if (sectors < 0) {
- bch_err(c, "error in fs gc: error %i "
- "recounting inode sectors",
+ bch_err(c, "error in fsck: error %i recounting inode sectors",
(int) sectors);
return sectors;
}
BTREE_INSERT_NOFAIL|
BTREE_INSERT_LAZY_RW);
if (ret && ret != -EINTR)
- bch_err(c, "error in fs gc: error %i "
+ bch_err(c, "error in fsck: error %i "
"updating inode", ret);
}
fsck_err:
bch2_trans_exit(&trans);
if (ret2)
- bch_err(c, "error in fs gc: btree error %i while walking inodes", ret2);
+ bch_err(c, "error in fsck: btree error %i while walking inodes", ret2);
return ret ?: ret2;
}
return ret;
}
-noinline_for_stack
-static int check_inodes_fast(struct bch_fs *c)
-{
- struct btree_trans trans;
- struct btree_iter *iter;
- struct bkey_s_c k;
- struct bkey_s_c_inode inode;
- int ret;
-
- bch2_trans_init(&trans, c);
- bch2_trans_preload_iters(&trans);
-
- for_each_btree_key(&trans, iter, BTREE_ID_INODES, POS_MIN, 0, k, ret) {
- if (k.k->type != KEY_TYPE_inode)
- continue;
-
- inode = bkey_s_c_to_inode(k);
-
- if (inode.v->bi_flags &
- (BCH_INODE_I_SIZE_DIRTY|
- BCH_INODE_I_SECTORS_DIRTY|
- BCH_INODE_UNLINKED)) {
- ret = check_inode(&trans, NULL, iter, inode, NULL);
- BUG_ON(ret == -EINTR);
- if (ret)
- break;
- }
- }
- BUG_ON(ret == -EINTR);
-
- return bch2_trans_exit(&trans) ?: ret;
-}
-
/*
* Checks for inconsistencies that shouldn't happen, unless we have a bug.
* Doesn't fix them yet, mainly because they haven't yet been observed:
*/
-static int bch2_fsck_full(struct bch_fs *c)
+int bch2_fsck_full(struct bch_fs *c)
{
struct bch_inode_unpacked root_inode, lostfound_inode;
- int ret;
- bch_verbose(c, "starting fsck:");
- ret = check_extents(c) ?:
+ return check_extents(c) ?:
check_dirents(c) ?:
check_xattrs(c) ?:
check_root(c, &root_inode) ?:
check_lostfound(c, &root_inode, &lostfound_inode) ?:
check_directory_structure(c, &lostfound_inode) ?:
check_inode_nlinks(c, &lostfound_inode);
-
- bch2_flush_fsck_errs(c);
- bch_verbose(c, "fsck done");
-
- return ret;
}
-static int bch2_fsck_inode_nlink(struct bch_fs *c)
+int bch2_fsck_inode_nlink(struct bch_fs *c)
{
struct bch_inode_unpacked root_inode, lostfound_inode;
- int ret;
- bch_verbose(c, "checking inode link counts:");
- ret = check_root(c, &root_inode) ?:
+ return check_root(c, &root_inode) ?:
check_lostfound(c, &root_inode, &lostfound_inode) ?:
check_inode_nlinks(c, &lostfound_inode);
-
- bch2_flush_fsck_errs(c);
- bch_verbose(c, "done");
-
- return ret;
}
-static int bch2_fsck_walk_inodes_only(struct bch_fs *c)
+int bch2_fsck_walk_inodes_only(struct bch_fs *c)
{
+ struct btree_trans trans;
+ struct btree_iter *iter;
+ struct bkey_s_c k;
+ struct bkey_s_c_inode inode;
int ret;
- bch_verbose(c, "walking inodes:");
- ret = check_inodes_fast(c);
-
- bch2_flush_fsck_errs(c);
- bch_verbose(c, "done");
+ bch2_trans_init(&trans, c);
+ bch2_trans_preload_iters(&trans);
- return ret;
-}
+ for_each_btree_key(&trans, iter, BTREE_ID_INODES, POS_MIN, 0, k, ret) {
+ if (k.k->type != KEY_TYPE_inode)
+ continue;
-int bch2_fsck(struct bch_fs *c)
-{
- if (c->opts.fsck)
- return bch2_fsck_full(c);
+ inode = bkey_s_c_to_inode(k);
- if (c->sb.clean)
- return 0;
+ if (inode.v->bi_flags &
+ (BCH_INODE_I_SIZE_DIRTY|
+ BCH_INODE_I_SECTORS_DIRTY|
+ BCH_INODE_UNLINKED)) {
+ ret = check_inode(&trans, NULL, iter, inode, NULL);
+ BUG_ON(ret == -EINTR);
+ if (ret)
+ break;
+ }
+ }
+ BUG_ON(ret == -EINTR);
- return c->sb.features & (1 << BCH_FEATURE_ATOMIC_NLINK)
- ? bch2_fsck_walk_inodes_only(c)
- : bch2_fsck_inode_nlink(c);
+ return bch2_trans_exit(&trans) ?: ret;
}
#ifndef _BCACHEFS_FSCK_H
#define _BCACHEFS_FSCK_H
-int bch2_fsck(struct bch_fs *);
+int bch2_fsck_full(struct bch_fs *);
+int bch2_fsck_inode_nlink(struct bch_fs *);
+int bch2_fsck_walk_inodes_only(struct bch_fs *);
#endif /* _BCACHEFS_FSCK_H */
NO_SB_OPT, false, \
NULL, "Super read only mode - no writes at all will be issued,\n"\
"even if we have to replay the journal") \
- x(noreplay, u8, \
- OPT_MOUNT, \
- OPT_BOOL(), \
- NO_SB_OPT, false, \
- NULL, "Don't replay the journal (only for internal tools)")\
x(norecovery, u8, \
OPT_MOUNT, \
OPT_BOOL(), \
NO_SB_OPT, false, \
- NULL, NULL) \
+ NULL, "Don't replay the journal") \
x(noexcl, u8, \
OPT_MOUNT, \
OPT_BOOL(), \
if (!c->sb.clean) {
ret = bch2_journal_seq_blacklist_add(c,
- journal_seq,
- journal_seq + 4);
+ journal_seq,
+ journal_seq + 4);
if (ret) {
bch_err(c, "error creating new journal seq blacklist entry");
goto err;
* journal; after an unclean shutdown we have to walk all
* pointers to metadata:
*/
- bch_verbose(c, "starting metadata mark and sweep:");
+ bch_info(c, "starting metadata mark and sweep");
err = "error in mark and sweep";
ret = bch2_gc(c, NULL, true, true);
if (ret)
if (c->opts.fsck ||
!(c->sb.compat & (1ULL << BCH_COMPAT_FEAT_ALLOC_INFO)) ||
test_bit(BCH_FS_REBUILD_REPLICAS, &c->flags)) {
- bch_verbose(c, "starting mark and sweep:");
+ bch_info(c, "starting mark and sweep");
err = "error in mark and sweep";
ret = bch2_gc(c, &journal_keys, true, false);
if (ret)
if (c->sb.encryption_type && !c->sb.clean)
atomic64_add(1 << 16, &c->key_version);
- if (c->opts.noreplay)
+ if (c->opts.norecovery)
goto out;
- bch_verbose(c, "starting journal replay:");
+ bch_verbose(c, "starting journal replay");
err = "journal replay failed";
ret = bch2_journal_replay(c, journal_keys);
if (ret)
goto err;
bch_verbose(c, "journal replay done");
- bch_verbose(c, "writing allocation info:");
- err = "error writing out alloc info";
- ret = bch2_stripes_write(c, BTREE_INSERT_LAZY_RW, &wrote) ?:
- bch2_alloc_write(c, BTREE_INSERT_LAZY_RW, &wrote);
- if (ret) {
- bch_err(c, "error writing alloc info");
- goto err;
+ if (!c->opts.nochanges) {
+ /*
+ * note that even when filesystem was clean there might be work
+ * to do here, if we ran gc (because of fsck) which recalculated
+ * oldest_gen:
+ */
+ bch_verbose(c, "writing allocation info");
+ err = "error writing out alloc info";
+ ret = bch2_stripes_write(c, BTREE_INSERT_LAZY_RW, &wrote) ?:
+ bch2_alloc_write(c, BTREE_INSERT_LAZY_RW, &wrote);
+ if (ret) {
+ bch_err(c, "error writing alloc info");
+ goto err;
+ }
+ bch_verbose(c, "alloc write done");
}
- bch_verbose(c, "alloc write done");
- if (c->opts.norecovery)
- goto out;
+ if (!c->sb.clean) {
+ if (!(c->sb.features & (1 << BCH_FEATURE_ATOMIC_NLINK))) {
+ bch_info(c, "checking inode link counts");
+ err = "error in recovery";
+ ret = bch2_fsck_inode_nlink(c);
+ if (ret)
+ goto err;
+ bch_verbose(c, "check inodes done");
- err = "error in fsck";
- ret = bch2_fsck(c);
- if (ret)
- goto err;
+ } else {
+ bch_verbose(c, "checking for deleted inodes");
+ err = "error in recovery";
+ ret = bch2_fsck_walk_inodes_only(c);
+ if (ret)
+ goto err;
+ bch_verbose(c, "check inodes done");
+ }
+ }
+
+ if (c->opts.fsck) {
+ bch_info(c, "starting fsck");
+ err = "error in fsck";
+ ret = bch2_fsck_full(c);
+ if (ret)
+ goto err;
+ bch_verbose(c, "fsck done");
+ }
if (enabled_qtypes(c)) {
- bch_verbose(c, "reading quotas:");
+ bch_verbose(c, "reading quotas");
ret = bch2_fs_quota_read(c);
if (ret)
goto err;
c->journal_seq_blacklist_table->nr > 128)
queue_work(system_long_wq, &c->journal_seq_blacklist_gc_work);
out:
+ ret = 0;
+err:
+fsck_err:
+ bch2_flush_fsck_errs(c);
journal_keys_free(&journal_keys);
journal_entries_free(&journal_entries);
kfree(clean);
+ if (ret)
+ bch_err(c, "Error in recovery: %s (%i)", err, ret);
+ else
+ bch_verbose(c, "ret %i", ret);
return ret;
-err:
-fsck_err:
- bch_err(c, "Error in recovery: %s (%i)", err, ret);
- goto out;
}
int bch2_fs_initialize(struct bch_fs *c)
!test_bit(BCH_FS_ERROR, &c->flags) &&
!test_bit(BCH_FS_EMERGENCY_RO, &c->flags) &&
test_bit(BCH_FS_STARTED, &c->flags) &&
- !c->opts.noreplay &&
!c->opts.norecovery)
bch2_fs_mark_clean(c);
if (test_bit(BCH_FS_RW, &c->flags))
return 0;
- if (c->opts.nochanges ||
- c->opts.noreplay)
- return -EINVAL;
+ /*
+ * nochanges is used for fsck -n mode - we have to allow going rw
+ * during recovery for that to work:
+ */
+ if (c->opts.norecovery ||
+ (c->opts.nochanges &&
+ (!early || c->opts.read_only)))
+ return -EROFS;
ret = bch2_fs_mark_dirty(c);
if (ret)
c->block_bits = ilog2(c->opts.block_size);
c->btree_foreground_merge_threshold = BTREE_FOREGROUND_MERGE_THRESHOLD(c);
- c->opts.nochanges |= c->opts.noreplay;
- c->opts.read_only |= c->opts.nochanges;
- c->opts.read_only |= c->opts.noreplay;
-
if (bch2_fs_init_fault("fs_alloc"))
goto err;
goto out;
}
-const char *bch2_fs_start(struct bch_fs *c)
+noinline_for_stack
+static void print_mount_opts(struct bch_fs *c)
+{
+ enum bch_opt_id i;
+ char buf[512];
+ struct printbuf p = PBUF(buf);
+ bool first = true;
+
+ strcpy(buf, "(null)");
+
+ if (c->opts.read_only) {
+ pr_buf(&p, "ro");
+ first = false;
+ }
+
+ for (i = 0; i < bch2_opts_nr; i++) {
+ const struct bch_option *opt = &bch2_opt_table[i];
+ u64 v = bch2_opt_get_by_id(&c->opts, i);
+
+ if (!(opt->mode & OPT_MOUNT))
+ continue;
+
+ if (v == bch2_opt_get_by_id(&bch2_opts_default, i))
+ continue;
+
+ if (!first)
+ pr_buf(&p, ",");
+ first = false;
+ bch2_opt_to_text(&p, c, opt, v, OPT_SHOW_MOUNT_STYLE);
+ }
+
+ bch_info(c, "mounted with opts: %s", buf);
+}
+
+int bch2_fs_start(struct bch_fs *c)
{
const char *err = "cannot allocate memory";
struct bch_sb_field_members *mi;
goto err;
err = "dynamic fault";
+ ret = -EINVAL;
if (bch2_fs_init_fault("fs_start"))
goto err;
- if (c->opts.read_only) {
+ if (c->opts.read_only || c->opts.nochanges) {
bch2_fs_read_only(c);
} else {
- if (!test_bit(BCH_FS_RW, &c->flags)
- ? bch2_fs_read_write(c)
- : bch2_fs_read_write_late(c)) {
- err = "error going read write";
+ err = "error going read write";
+ ret = !test_bit(BCH_FS_RW, &c->flags)
+ ? bch2_fs_read_write(c)
+ : bch2_fs_read_write_late(c);
+ if (ret)
goto err;
- }
}
set_bit(BCH_FS_STARTED, &c->flags);
-
- err = NULL;
+ print_mount_opts(c);
+ ret = 0;
out:
mutex_unlock(&c->state_lock);
- return err;
+ return ret;
err:
switch (ret) {
case BCH_FSCK_ERRORS_NOT_FIXED:
break;
}
- BUG_ON(!err);
+ BUG_ON(!ret);
goto out;
}
goto err_print;
if (!c->opts.nostart) {
- err = bch2_fs_start(c);
- if (err)
- goto err_print;
+ ret = bch2_fs_start(c);
+ if (ret)
+ goto err;
}
out:
kfree(sb);
const char *err;
struct bch_fs *c;
bool allocated_fs = false;
+ int ret;
err = bch2_sb_validate(sb);
if (err)
mutex_unlock(&c->sb_lock);
if (!c->opts.nostart && bch2_fs_may_start(c)) {
- err = bch2_fs_start(c);
- if (err)
+ err = "error starting filesystem";
+ ret = bch2_fs_start(c);
+ if (ret)
goto err;
}
void bch2_fs_stop(struct bch_fs *);
-const char *bch2_fs_start(struct bch_fs *);
+int bch2_fs_start(struct bch_fs *);
struct bch_fs *bch2_fs_open(char * const *, unsigned, struct bch_opts);
const char *bch2_fs_open_incremental(const char *path);