// SPDX-License-Identifier: GPL-2.0
#include "bcachefs.h"
#include "btree_key_cache.h"
#include "btree_update.h"
#include "errcode.h"
#include "error.h"
#include "fs.h"
#include "subvolume.h"
/* Snapshot tree: */
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,
unsigned flags, struct printbuf *err)
{
struct bkey_s_c_snapshot s;
u32 i, id;
if (bkey_gt(k.k->p, POS(0, U32_MAX)) ||
bkey_lt(k.k->p, POS(0, 1))) {
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,
enum btree_id btree, unsigned level,
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 -BCH_ERR_ENOMEM_mark_snapshot;
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;