-void bch2_snapshot_to_text(struct printbuf *out, struct bch_fs *c,
- struct bkey_s_c k)
-{
- struct bkey_s_c_snapshot s = bkey_s_c_to_snapshot(k);
-
- prt_printf(out, "is_subvol %llu deleted %llu parent %10u children %10u %10u subvol %u",
- BCH_SNAPSHOT_SUBVOL(s.v),
- BCH_SNAPSHOT_DELETED(s.v),
- le32_to_cpu(s.v->parent),
- le32_to_cpu(s.v->children[0]),
- le32_to_cpu(s.v->children[1]),
- le32_to_cpu(s.v->subvol));
-}
-
-int bch2_snapshot_invalid(const struct bch_fs *c, struct bkey_s_c k,
- int rw, struct printbuf *err)
-{
- struct bkey_s_c_snapshot s;
- u32 i, id;
-
- if (bkey_cmp(k.k->p, POS(0, U32_MAX)) > 0 ||
- bkey_cmp(k.k->p, POS(0, 1)) < 0) {
- prt_printf(err, "bad pos");
- return -BCH_ERR_invalid_bkey;
- }
-
- if (bkey_val_bytes(k.k) != sizeof(struct bch_snapshot)) {
- prt_printf(err, "bad val size (%zu != %zu)",
- bkey_val_bytes(k.k), sizeof(struct bch_snapshot));
- return -BCH_ERR_invalid_bkey;
- }
-
- s = bkey_s_c_to_snapshot(k);
-
- id = le32_to_cpu(s.v->parent);
- if (id && id <= k.k->p.offset) {
- prt_printf(err, "bad parent node (%u <= %llu)",
- id, k.k->p.offset);
- return -BCH_ERR_invalid_bkey;
- }
-
- if (le32_to_cpu(s.v->children[0]) < le32_to_cpu(s.v->children[1])) {
- prt_printf(err, "children not normalized");
- return -BCH_ERR_invalid_bkey;
- }
-
- if (s.v->children[0] &&
- s.v->children[0] == s.v->children[1]) {
- prt_printf(err, "duplicate child nodes");
- return -BCH_ERR_invalid_bkey;
- }
-
- for (i = 0; i < 2; i++) {
- id = le32_to_cpu(s.v->children[i]);
-
- if (id >= k.k->p.offset) {
- prt_printf(err, "bad child node (%u >= %llu)",
- id, k.k->p.offset);
- return -BCH_ERR_invalid_bkey;
- }
- }
-
- return 0;
-}
-
-int bch2_mark_snapshot(struct btree_trans *trans,
- struct bkey_s_c old, struct bkey_s_c new,
- unsigned flags)
-{
- struct bch_fs *c = trans->c;
- struct snapshot_t *t;
-
- t = genradix_ptr_alloc(&c->snapshots,
- U32_MAX - new.k->p.offset,
- GFP_KERNEL);
- if (!t)
- return -ENOMEM;
-
- if (new.k->type == KEY_TYPE_snapshot) {
- struct bkey_s_c_snapshot s = bkey_s_c_to_snapshot(new);
-
- t->parent = le32_to_cpu(s.v->parent);
- t->children[0] = le32_to_cpu(s.v->children[0]);
- t->children[1] = le32_to_cpu(s.v->children[1]);
- t->subvol = BCH_SNAPSHOT_SUBVOL(s.v) ? le32_to_cpu(s.v->subvol) : 0;
- } else {
- t->parent = 0;
- t->children[0] = 0;
- t->children[1] = 0;
- t->subvol = 0;
- }
-
- return 0;
-}
-
-static int snapshot_lookup(struct btree_trans *trans, u32 id,
- struct bch_snapshot *s)
-{
- struct btree_iter iter;
- struct bkey_s_c k;
- int ret;
-
- bch2_trans_iter_init(trans, &iter, BTREE_ID_snapshots, POS(0, id),
- BTREE_ITER_WITH_UPDATES);
- k = bch2_btree_iter_peek_slot(&iter);
- ret = bkey_err(k) ?: k.k->type == KEY_TYPE_snapshot ? 0 : -ENOENT;
-
- if (!ret)
- *s = *bkey_s_c_to_snapshot(k).v;
-
- bch2_trans_iter_exit(trans, &iter);
- return ret;
-}
-
-static int snapshot_live(struct btree_trans *trans, u32 id)
-{
- struct bch_snapshot v;
- int ret;
-
- if (!id)
- return 0;
-
- ret = snapshot_lookup(trans, id, &v);
- if (ret == -ENOENT)
- bch_err(trans->c, "snapshot node %u not found", id);
- if (ret)
- return ret;
-
- return !BCH_SNAPSHOT_DELETED(&v);
-}
-
-static int bch2_snapshot_set_equiv(struct btree_trans *trans, struct bkey_s_c k)
-{
- struct bch_fs *c = trans->c;
- unsigned i, nr_live = 0, live_idx = 0;
- struct bkey_s_c_snapshot snap;
- u32 id = k.k->p.offset, child[2];
-
- if (k.k->type != KEY_TYPE_snapshot)
- return 0;
-
- snap = bkey_s_c_to_snapshot(k);
-
- child[0] = le32_to_cpu(snap.v->children[0]);
- child[1] = le32_to_cpu(snap.v->children[1]);
-
- for (i = 0; i < 2; i++) {
- int ret = snapshot_live(trans, child[i]);
-
- if (ret < 0)
- return ret;
-
- if (ret)
- live_idx = i;
- nr_live += ret;
- }
-
- snapshot_t(c, id)->equiv = nr_live == 1
- ? snapshot_t(c, child[live_idx])->equiv
- : id;
- return 0;
-}
-
-/* fsck: */
-static int check_snapshot(struct btree_trans *trans,
- struct btree_iter *iter,
- struct bkey_s_c k)
-{
- struct bch_fs *c = trans->c;
- struct bkey_s_c_snapshot s;
- struct bch_subvolume subvol;
- struct bch_snapshot v;
- struct printbuf buf = PRINTBUF;
- bool should_have_subvol;
- u32 i, id;
- int ret = 0;
-
- if (k.k->type != KEY_TYPE_snapshot)
- return 0;
-
- s = bkey_s_c_to_snapshot(k);
- id = le32_to_cpu(s.v->parent);
- if (id) {
- ret = snapshot_lookup(trans, id, &v);
- if (ret == -ENOENT)
- bch_err(c, "snapshot with nonexistent parent:\n %s",
- (bch2_bkey_val_to_text(&buf, c, s.s_c), buf.buf));
- if (ret)
- goto err;
-
- if (le32_to_cpu(v.children[0]) != s.k->p.offset &&
- le32_to_cpu(v.children[1]) != s.k->p.offset) {
- bch_err(c, "snapshot parent %u missing pointer to child %llu",
- id, s.k->p.offset);
- ret = -EINVAL;
- goto err;
- }
- }
-
- for (i = 0; i < 2 && s.v->children[i]; i++) {
- id = le32_to_cpu(s.v->children[i]);
-
- ret = snapshot_lookup(trans, id, &v);
- if (ret == -ENOENT)
- bch_err(c, "snapshot node %llu has nonexistent child %u",
- s.k->p.offset, id);
- if (ret)
- goto err;
-
- if (le32_to_cpu(v.parent) != s.k->p.offset) {
- bch_err(c, "snapshot child %u has wrong parent (got %u should be %llu)",
- id, le32_to_cpu(v.parent), s.k->p.offset);
- ret = -EINVAL;
- goto err;
- }
- }
-
- should_have_subvol = BCH_SNAPSHOT_SUBVOL(s.v) &&
- !BCH_SNAPSHOT_DELETED(s.v);
-
- if (should_have_subvol) {
- id = le32_to_cpu(s.v->subvol);
- ret = bch2_subvolume_get(trans, id, 0, false, &subvol);
- if (ret == -ENOENT)
- bch_err(c, "snapshot points to nonexistent subvolume:\n %s",
- (bch2_bkey_val_to_text(&buf, c, s.s_c), buf.buf));
- if (ret)
- goto err;
-
- if (BCH_SNAPSHOT_SUBVOL(s.v) != (le32_to_cpu(subvol.snapshot) == s.k->p.offset)) {
- bch_err(c, "snapshot node %llu has wrong BCH_SNAPSHOT_SUBVOL",
- s.k->p.offset);
- ret = -EINVAL;
- goto err;
- }
- } else {
- if (fsck_err_on(s.v->subvol, c, "snapshot should not point to subvol:\n %s",
- (bch2_bkey_val_to_text(&buf, c, s.s_c), buf.buf))) {
- struct bkey_i_snapshot *u = bch2_trans_kmalloc(trans, sizeof(*u));
-
- ret = PTR_ERR_OR_ZERO(u);
- if (ret)
- goto err;
-
- bkey_reassemble(&u->k_i, s.s_c);
- u->v.subvol = 0;
- ret = bch2_trans_update(trans, iter, &u->k_i, 0);
- if (ret)
- goto err;
- }
- }
-
- if (BCH_SNAPSHOT_DELETED(s.v))
- set_bit(BCH_FS_HAVE_DELETED_SNAPSHOTS, &c->flags);
-err:
-fsck_err:
- printbuf_exit(&buf);
- return ret;
-}
-
-int bch2_fs_check_snapshots(struct bch_fs *c)
-{
- struct btree_trans trans;
- struct btree_iter iter;
- struct bkey_s_c k;
- int ret;
-
- bch2_trans_init(&trans, c, 0, 0);
-
- ret = for_each_btree_key_commit(&trans, iter,
- BTREE_ID_snapshots, POS_MIN,
- BTREE_ITER_PREFETCH, k,
- NULL, NULL, BTREE_INSERT_LAZY_RW|BTREE_INSERT_NOFAIL,
- check_snapshot(&trans, &iter, k));
-
- if (ret)
- bch_err(c, "error %i checking snapshots", ret);
-
- bch2_trans_exit(&trans);
- return ret;
-}