le32_to_cpu(s.v->subvol));
}
-const char *bch2_snapshot_invalid(const struct bch_fs *c, struct bkey_s_c k)
+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)
- return "bad pos";
+ bkey_cmp(k.k->p, POS(0, 1)) < 0) {
+ pr_buf(err, "bad pos");
+ return -EINVAL;
+ }
- if (bkey_val_bytes(k.k) != sizeof(struct bch_snapshot))
- return "bad val size";
+ if (bkey_val_bytes(k.k) != sizeof(struct bch_snapshot)) {
+ pr_buf(err, "bad val size (%zu != %zu)",
+ bkey_val_bytes(k.k), sizeof(struct bch_snapshot));
+ return -EINVAL;
+ }
s = bkey_s_c_to_snapshot(k);
id = le32_to_cpu(s.v->parent);
- if (id && id <= k.k->p.offset)
- return "bad parent node";
+ if (id && id <= k.k->p.offset) {
+ pr_buf(err, "bad parent node (%u <= %llu)",
+ id, k.k->p.offset);
+ return -EINVAL;
+ }
- if (le32_to_cpu(s.v->children[0]) < le32_to_cpu(s.v->children[1]))
- return "children not normalized";
+ if (le32_to_cpu(s.v->children[0]) < le32_to_cpu(s.v->children[1])) {
+ pr_buf(err, "children not normalized");
+ return -EINVAL;
+ }
if (s.v->children[0] &&
- s.v->children[0] == s.v->children[1])
- return "duplicate child nodes";
+ s.v->children[0] == s.v->children[1]) {
+ pr_buf(err, "duplicate child nodes");
+ return -EINVAL;
+ }
for (i = 0; i < 2; i++) {
id = le32_to_cpu(s.v->children[i]);
- if (id >= k.k->p.offset)
- return "bad child node";
+ if (id >= k.k->p.offset) {
+ pr_buf(err, "bad child node (%u >= %llu)",
+ id, k.k->p.offset);
+ return -EINVAL;
+ }
}
- return NULL;
+ return 0;
}
int bch2_mark_snapshot(struct btree_trans *trans,
for_each_btree_key(trans, iter, BTREE_ID_snapshots,
POS_MIN, 0, k, ret) {
u32 id = k.k->p.offset, child[2];
- unsigned nr_live = 0, live_idx;
+ unsigned nr_live = 0, live_idx = 0;
if (k.k->type != KEY_TYPE_snapshot)
continue;
for (i = 0; i < 2; i++) {
ret = snapshot_live(trans, child[i]);
if (ret < 0)
- break;
+ goto err;
if (ret)
live_idx = i;
? snapshot_t(c, child[live_idx])->equiv
: id;
}
+err:
bch2_trans_iter_exit(trans, &iter);
if (ret)
return ret;
}
-static int bch2_snapshot_node_create(struct btree_trans *trans, u32 parent,
- u32 *new_snapids,
- u32 *snapshot_subvols,
- unsigned nr_snapids)
+int bch2_snapshot_node_create(struct btree_trans *trans, u32 parent,
+ u32 *new_snapids,
+ u32 *snapshot_subvols,
+ unsigned nr_snapids)
{
struct btree_iter iter;
struct bkey_i_snapshot *n;
n = bch2_trans_kmalloc(trans, sizeof(*n));
ret = PTR_ERR_OR_ZERO(n);
if (ret)
- return ret;
+ goto err;
bkey_snapshot_init(&n->k_i);
n->k.p = iter.pos;
n->v.pad = 0;
SET_BCH_SNAPSHOT_SUBVOL(&n->v, true);
- bch2_trans_update(trans, &iter, &n->k_i, 0);
-
- ret = bch2_mark_snapshot(trans, bkey_s_c_null, bkey_i_to_s_c(&n->k_i), 0);
+ ret = bch2_trans_update(trans, &iter, &n->k_i, 0) ?:
+ bch2_mark_snapshot(trans, bkey_s_c_null, bkey_i_to_s_c(&n->k_i), 0);
if (ret)
- break;
+ goto err;
new_snapids[i] = iter.pos.offset;
}
n = bch2_trans_kmalloc(trans, sizeof(*n));
ret = PTR_ERR_OR_ZERO(n);
if (ret)
- return ret;
+ goto err;
bkey_reassemble(&n->k_i, k);
n->v.children[0] = cpu_to_le32(new_snapids[0]);
n->v.children[1] = cpu_to_le32(new_snapids[1]);
SET_BCH_SNAPSHOT_SUBVOL(&n->v, false);
- bch2_trans_update(trans, &iter, &n->k_i, 0);
+ ret = bch2_trans_update(trans, &iter, &n->k_i, 0);
+ if (ret)
+ goto err;
}
err:
bch2_trans_iter_exit(trans, &iter);
return ret;
}
-static int snapshot_id_add(struct snapshot_id_list *s, u32 id)
+static int snapshot_id_add(snapshot_id_list *s, u32 id)
{
BUG_ON(snapshot_list_has_id(s, id));
- if (s->nr == s->size) {
- size_t new_size = max(8U, s->size * 2);
- void *n = krealloc(s->d,
- new_size * sizeof(s->d[0]),
- GFP_KERNEL);
- if (!n) {
- pr_err("error allocating snapshot ID list");
- return -ENOMEM;
- }
-
- s->d = n;
- s->size = new_size;
- };
-
- s->d[s->nr++] = id;
- return 0;
+ return darray_push(*s, id);
}
static int bch2_snapshot_delete_keys_btree(struct btree_trans *trans,
- struct snapshot_id_list *deleted,
+ snapshot_id_list *deleted,
enum btree_id btree_id)
{
struct bch_fs *c = trans->c;
struct btree_iter iter;
struct bkey_s_c k;
- struct snapshot_id_list equiv_seen = { 0 };
+ snapshot_id_list equiv_seen = { 0 };
struct bpos last_pos = POS_MIN;
int ret = 0;
}
bch2_trans_iter_exit(trans, &iter);
- kfree(equiv_seen.d);
+ darray_exit(equiv_seen);
return ret;
}
struct btree_iter iter;
struct bkey_s_c k;
struct bkey_s_c_snapshot snap;
- struct snapshot_id_list deleted = { 0 };
+ snapshot_id_list deleted = { 0 };
u32 i, id, children[2];
int ret = 0;
for (i = 0; i < deleted.nr; i++) {
ret = __bch2_trans_do(&trans, NULL, NULL, 0,
- bch2_snapshot_node_delete(&trans, deleted.d[i]));
+ bch2_snapshot_node_delete(&trans, deleted.data[i]));
if (ret) {
bch_err(c, "error deleting snapshot %u: %i",
- deleted.d[i], ret);
+ deleted.data[i], ret);
goto err;
}
}
err:
- kfree(deleted.d);
+ darray_exit(deleted);
bch2_trans_exit(&trans);
percpu_ref_put(&c->writes);
}
/* Subvolumes: */
-const char *bch2_subvolume_invalid(const struct bch_fs *c, struct bkey_s_c k)
+int bch2_subvolume_invalid(const struct bch_fs *c, struct bkey_s_c k,
+ int rw, struct printbuf *err)
{
- if (bkey_cmp(k.k->p, SUBVOL_POS_MIN) < 0)
- return "invalid pos";
-
- if (bkey_cmp(k.k->p, SUBVOL_POS_MAX) > 0)
- return "invalid pos";
+ if (bkey_cmp(k.k->p, SUBVOL_POS_MIN) < 0 ||
+ bkey_cmp(k.k->p, SUBVOL_POS_MAX) > 0) {
+ pr_buf(err, "invalid pos");
+ return -EINVAL;
+ }
- if (bkey_val_bytes(k.k) != sizeof(struct bch_subvolume))
- return "bad val size";
+ if (bkey_val_bytes(k.k) != sizeof(struct bch_subvolume)) {
+ pr_buf(err, "incorrect value size (%zu != %zu)",
+ bkey_val_bytes(k.k), sizeof(struct bch_subvolume));
+ return -EINVAL;
+ }
- return NULL;
+ return 0;
}
void bch2_subvolume_to_text(struct printbuf *out, struct bch_fs *c,
return ret;
}
+int bch2_snapshot_get_subvol(struct btree_trans *trans, u32 snapshot,
+ struct bch_subvolume *subvol)
+{
+ struct bch_snapshot snap;
+
+ return snapshot_lookup(trans, snapshot, &snap) ?:
+ bch2_subvolume_get(trans, le32_to_cpu(snap.subvol), true, 0, subvol);
+}
+
int bch2_subvolume_get_snapshot(struct btree_trans *trans, u32 subvol,
u32 *snapid)
{
{
struct bch_fs *c = container_of(work, struct bch_fs,
snapshot_wait_for_pagecache_and_delete_work);
- struct snapshot_id_list s;
+ snapshot_id_list s;
u32 *id;
int ret = 0;
while (!ret) {
mutex_lock(&c->snapshots_unlinked_lock);
s = c->snapshots_unlinked;
- memset(&c->snapshots_unlinked, 0, sizeof(c->snapshots_unlinked));
+ darray_init(c->snapshots_unlinked);
mutex_unlock(&c->snapshots_unlinked_lock);
if (!s.nr)
bch2_evict_subvolume_inodes(c, &s);
- for (id = s.d; id < s.d + s.nr; id++) {
+ for (id = s.data; id < s.data + s.nr; id++) {
ret = bch2_trans_do(c, NULL, NULL, BTREE_INSERT_NOFAIL,
bch2_subvolume_delete(&trans, *id));
if (ret) {
}
}
- kfree(s.d);
+ darray_exit(s);
}
percpu_ref_put(&c->writes);
if (src_subvolid) {
src_subvol->v.snapshot = cpu_to_le32(new_nodes[1]);
- bch2_trans_update(trans, &src_iter, &src_subvol->k_i, 0);
+ ret = bch2_trans_update(trans, &src_iter, &src_subvol->k_i, 0);
+ if (ret)
+ goto err;
}
new_subvol = bch2_trans_kmalloc(trans, sizeof(*new_subvol));
SET_BCH_SUBVOLUME_RO(&new_subvol->v, ro);
SET_BCH_SUBVOLUME_SNAP(&new_subvol->v, src_subvolid != 0);
new_subvol->k.p = dst_iter.pos;
- bch2_trans_update(trans, &dst_iter, &new_subvol->k_i, 0);
+ ret = bch2_trans_update(trans, &dst_iter, &new_subvol->k_i, 0);
+ if (ret)
+ goto err;
*new_subvolid = new_subvol->k.p.offset;
*new_snapshotid = new_nodes[0];