#include "quota.h"
#include "super-io.h"
#include "super.h"
+#include "trace.h"
#include "vstructs.h"
#include "counters.h"
#include <linux/backing-dev.h>
-#include <linux/pretty-printers.h>
#include <linux/sort.h>
-#include <trace/events/bcachefs.h>
-
const char * const bch2_sb_fields[] = {
#define x(name, nr) #name,
BCH_SB_FIELDS()
void bch2_free_super(struct bch_sb_handle *sb)
{
- if (sb->bio)
- kfree(sb->bio);
+ kfree(sb->bio);
if (!IS_ERR_OR_NULL(sb->bdev))
blkdev_put(sb->bdev, sb->mode);
u64 max_bytes = 512 << sb->sb->layout.sb_max_size_bits;
if (new_bytes > max_bytes) {
- char buf[BDEVNAME_SIZE];
-
- pr_err("%s: superblock too big: want %zu but have %llu",
- bdevname(sb->bdev, buf), new_bytes, max_bytes);
- return -ENOSPC;
+ pr_err("%pg: superblock too big: want %zu but have %llu",
+ sb->bdev, new_bytes, max_bytes);
+ return -BCH_ERR_ENOSPC_sb;
}
}
return 0;
if (dynamic_fault("bcachefs:add:super_realloc"))
- return -ENOMEM;
+ return -BCH_ERR_ENOMEM_sb_realloc_injected;
if (sb->have_bio) {
unsigned nr_bvecs = DIV_ROUND_UP(new_buffer_size, PAGE_SIZE);
bio = bio_kmalloc(nr_bvecs, GFP_KERNEL);
if (!bio)
- return -ENOMEM;
+ return -BCH_ERR_ENOMEM_sb_bio_realloc;
bio_init(bio, NULL, bio->bi_inline_vecs, nr_bvecs, 0);
- if (sb->bio)
- kfree(sb->bio);
+ kfree(sb->bio);
sb->bio = bio;
}
new_sb = krealloc(sb->sb, new_buffer_size, GFP_NOFS|__GFP_ZERO);
if (!new_sb)
- return -ENOMEM;
+ return -BCH_ERR_ENOMEM_sb_buf_realloc;
sb->sb = new_sb;
sb->buffer_size = new_buffer_size;
u64 offset, prev_offset, max_sectors;
unsigned i;
- if (uuid_le_cmp(layout->magic, BCACHE_MAGIC)) {
+ if (uuid_le_cmp(layout->magic, BCACHE_MAGIC) &&
+ uuid_le_cmp(layout->magic, BCHFS_MAGIC)) {
prt_printf(out, "Not a bcachefs superblock layout");
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_layout;
}
if (layout->layout_type != 0) {
prt_printf(out, "Invalid superblock layout type %u",
layout->layout_type);
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_layout_type;
}
if (!layout->nr_superblocks) {
prt_printf(out, "Invalid superblock layout: no superblocks");
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_layout_nr_superblocks;
}
if (layout->nr_superblocks > ARRAY_SIZE(layout->sb_offset)) {
prt_printf(out, "Invalid superblock layout: too many superblocks");
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_layout_nr_superblocks;
}
max_sectors = 1 << layout->sb_max_size_bits;
prt_printf(out, "Invalid superblock layout: superblocks overlap\n"
" (sb %u ends at %llu next starts at %llu",
i - 1, prev_offset + max_sectors, offset);
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_layout_superblocks_overlap;
}
prev_offset = offset;
}
if (version >= bcachefs_metadata_version_max) {
prt_printf(out, "Unsupported superblock version %u (min %u, max %u)",
version, bcachefs_metadata_version_min, bcachefs_metadata_version_max);
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_version;
}
if (version_min < bcachefs_metadata_version_min) {
prt_printf(out, "Unsupported superblock version %u (min %u, max %u)",
version_min, bcachefs_metadata_version_min, bcachefs_metadata_version_max);
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_version;
}
if (version_min > version) {
prt_printf(out, "Bad minimum version %u, greater than version field %u",
version_min, version);
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_version;
}
if (sb->features[1] ||
(le64_to_cpu(sb->features[0]) & (~0ULL << BCH_FEATURE_NR))) {
prt_printf(out, "Filesystem has incompatible features");
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_features;
}
block_size = le16_to_cpu(sb->block_size);
if (block_size > PAGE_SECTORS) {
prt_printf(out, "Block size too big (got %u, max %u)",
block_size, PAGE_SECTORS);
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_block_size;
}
if (bch2_is_zero(sb->user_uuid.b, sizeof(uuid_le))) {
prt_printf(out, "Bad user UUID (got zeroes)");
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_uuid;
}
if (bch2_is_zero(sb->uuid.b, sizeof(uuid_le))) {
prt_printf(out, "Bad intenal UUID (got zeroes)");
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_uuid;
}
if (!sb->nr_devices ||
sb->nr_devices > BCH_SB_MEMBERS_MAX) {
prt_printf(out, "Bad number of member devices %u (max %u)",
sb->nr_devices, BCH_SB_MEMBERS_MAX);
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_too_many_members;
}
if (sb->dev_idx >= sb->nr_devices) {
prt_printf(out, "Bad dev_idx (got %u, nr_devices %u)",
sb->dev_idx, sb->nr_devices);
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_dev_idx;
}
if (!sb->time_precision ||
le32_to_cpu(sb->time_precision) > NSEC_PER_SEC) {
prt_printf(out, "Invalid time precision: %u (min 1, max %lu)",
le32_to_cpu(sb->time_precision), NSEC_PER_SEC);
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_time_precision;
}
if (rw == READ) {
vstruct_for_each(sb, f) {
if (!f->u64s) {
- prt_printf(out, "Invalid superblock: optional with size 0 (type %u)",
+ prt_printf(out, "Invalid superblock: optional field with size 0 (type %u)",
le32_to_cpu(f->type));
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_field_size;
}
if (vstruct_next(f) > vstruct_last(sb)) {
prt_printf(out, "Invalid superblock: optional field extends past end of superblock (type %u)",
le32_to_cpu(f->type));
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_field_size;
}
}
mi = bch2_sb_get_members(sb);
if (!mi) {
prt_printf(out, "Invalid superblock: member info area missing");
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_members_missing;
}
ret = bch2_sb_field_validate(sb, &mi->field, out);
ca->mi = bch2_mi_to_cpu(mi->members + i);
}
-static void __copy_super(struct bch_sb_handle *dst_handle, struct bch_sb *src)
+static int __copy_super(struct bch_sb_handle *dst_handle, struct bch_sb *src)
{
struct bch_sb_field *src_f, *dst_f;
struct bch_sb *dst = dst_handle->sb;
memcpy(dst->compat, src->compat, sizeof(dst->compat));
for (i = 0; i < BCH_SB_FIELD_NR; i++) {
+ int d;
+
if ((1U << i) & BCH_SINGLE_DEVICE_SB_FIELDS)
continue;
src_f = bch2_sb_field_get(src, i);
dst_f = bch2_sb_field_get(dst, i);
+
+ d = (src_f ? le32_to_cpu(src_f->u64s) : 0) -
+ (dst_f ? le32_to_cpu(dst_f->u64s) : 0);
+ if (d > 0) {
+ int ret = bch2_sb_realloc(dst_handle, le32_to_cpu(dst_handle->sb->u64s) + d);
+ if (ret)
+ return ret;
+
+ dst = dst_handle->sb;
+ dst_f = bch2_sb_field_get(dst, i);
+ }
+
dst_f = __bch2_sb_field_resize(dst_handle, dst_f,
src_f ? le32_to_cpu(src_f->u64s) : 0);
if (src_f)
memcpy(dst_f, src_f, vstruct_bytes(src_f));
}
+
+ return 0;
}
int bch2_sb_to_fs(struct bch_fs *c, struct bch_sb *src)
{
- struct bch_sb_field_journal *journal_buckets =
- bch2_sb_get_journal(src);
- unsigned journal_u64s = journal_buckets
- ? le32_to_cpu(journal_buckets->field.u64s)
- : 0;
int ret;
lockdep_assert_held(&c->sb_lock);
- ret = bch2_sb_realloc(&c->disk_sb,
- le32_to_cpu(src->u64s) - journal_u64s);
- if (ret)
- return ret;
-
- __copy_super(&c->disk_sb, src);
-
- ret = bch2_sb_replicas_to_cpu_replicas(c);
- if (ret)
- return ret;
-
- ret = bch2_sb_disk_groups_to_cpu(c);
+ ret = bch2_sb_realloc(&c->disk_sb, 0) ?:
+ __copy_super(&c->disk_sb, src) ?:
+ bch2_sb_replicas_to_cpu_replicas(c) ?:
+ bch2_sb_disk_groups_to_cpu(c);
if (ret)
return ret;
int bch2_sb_from_fs(struct bch_fs *c, struct bch_dev *ca)
{
- struct bch_sb *src = c->disk_sb.sb, *dst = ca->disk_sb.sb;
- struct bch_sb_field_journal *journal_buckets =
- bch2_sb_get_journal(dst);
- unsigned journal_u64s = journal_buckets
- ? le32_to_cpu(journal_buckets->field.u64s)
- : 0;
- unsigned u64s = le32_to_cpu(src->u64s) + journal_u64s;
- int ret;
-
- ret = bch2_sb_realloc(&ca->disk_sb, u64s);
- if (ret)
- return ret;
-
- __copy_super(&ca->disk_sb, src);
- return 0;
+ return __copy_super(&ca->disk_sb, c->disk_sb.sb);
}
/* read superblock: */
return ret;
}
- if (uuid_le_cmp(sb->sb->magic, BCACHE_MAGIC)) {
+ if (uuid_le_cmp(sb->sb->magic, BCACHE_MAGIC) &&
+ uuid_le_cmp(sb->sb->magic, BCHFS_MAGIC)) {
prt_printf(err, "Not a bcachefs superblock");
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_magic;
}
version = le16_to_cpu(sb->sb->version);
if (version >= bcachefs_metadata_version_max) {
prt_printf(err, "Unsupported superblock version %u (min %u, max %u)",
version, bcachefs_metadata_version_min, bcachefs_metadata_version_max);
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_version;
}
if (version_min < bcachefs_metadata_version_min) {
prt_printf(err, "Unsupported superblock version %u (min %u, max %u)",
version_min, bcachefs_metadata_version_min, bcachefs_metadata_version_max);
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_version;
}
bytes = vstruct_bytes(sb->sb);
if (bytes > 512 << sb->sb->layout.sb_max_size_bits) {
prt_printf(err, "Invalid superblock: too big (got %zu bytes, layout max %lu)",
bytes, 512UL << sb->sb->layout.sb_max_size_bits);
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_too_big;
}
if (bytes > sb->buffer_size) {
- if (bch2_sb_realloc(sb, le32_to_cpu(sb->sb->u64s)))
- return -ENOMEM;
+ ret = bch2_sb_realloc(sb, le32_to_cpu(sb->sb->u64s));
+ if (ret)
+ return ret;
goto reread;
}
if (BCH_SB_CSUM_TYPE(sb->sb) >= BCH_CSUM_NR) {
prt_printf(err, "unknown checksum type %llu", BCH_SB_CSUM_TYPE(sb->sb));
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_csum_type;
}
/* XXX: verify MACs */
if (bch2_crc_cmp(csum, sb->sb->csum)) {
prt_printf(err, "bad checksum");
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_csum;
}
sb->seq = le64_to_cpu(sb->sb->seq);
prt_printf(&err, "block size (%u) smaller than device block size (%u)",
le16_to_cpu(sb->sb->block_size) << 9,
bdev_logical_block_size(sb->bdev));
- ret = -EINVAL;
+ ret = -BCH_ERR_block_size_too_small;
goto err;
}
closure_init_stack(cl);
memset(&sb_written, 0, sizeof(sb_written));
+ if (c->opts.version_upgrade) {
+ c->disk_sb.sb->magic = BCHFS_MAGIC;
+ c->disk_sb.sb->layout.magic = BCHFS_MAGIC;
+ }
+
le64_add_cpu(&c->disk_sb.sb->seq, 1);
if (test_bit(BCH_FS_ERROR, &c->flags))
le64_to_cpu(ca->sb_read_scratch->seq),
ca->disk_sb.seq);
percpu_ref_put(&ca->io_ref);
- ret = -EROFS;
+ ret = -BCH_ERR_erofs_sb_err;
goto out;
}
le64_to_cpu(ca->sb_read_scratch->seq),
ca->disk_sb.seq);
percpu_ref_put(&ca->io_ref);
- ret = -EROFS;
+ ret = -BCH_ERR_erofs_sb_err;
goto out;
}
}
if ((void *) (mi->members + sb->nr_devices) >
vstruct_end(&mi->field)) {
prt_printf(err, "too many devices for section size");
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_members;
}
for (i = 0; i < sb->nr_devices; i++) {
if (le64_to_cpu(m->nbuckets) > LONG_MAX) {
prt_printf(err, "device %u: too many buckets (got %llu, max %lu)",
i, le64_to_cpu(m->nbuckets), LONG_MAX);
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_members;
}
if (le64_to_cpu(m->nbuckets) -
le16_to_cpu(m->first_bucket) < BCH_MIN_NR_NBUCKETS) {
prt_printf(err, "device %u: not enough buckets (got %llu, max %u)",
i, le64_to_cpu(m->nbuckets), BCH_MIN_NR_NBUCKETS);
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_members;
}
if (le16_to_cpu(m->bucket_size) <
le16_to_cpu(sb->block_size)) {
prt_printf(err, "device %u: bucket size %u smaller than block size %u",
i, le16_to_cpu(m->bucket_size), le16_to_cpu(sb->block_size));
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_members;
}
if (le16_to_cpu(m->bucket_size) <
BCH_SB_BTREE_NODE_SIZE(sb)) {
prt_printf(err, "device %u: bucket size %u smaller than btree node size %llu",
i, le16_to_cpu(m->bucket_size), BCH_SB_BTREE_NODE_SIZE(sb));
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_members;
}
}
if (vstruct_bytes(&crypt->field) < sizeof(*crypt)) {
prt_printf(err, "wrong size (got %zu should be %zu)",
vstruct_bytes(&crypt->field), sizeof(*crypt));
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_crypt;
}
if (BCH_CRYPT_KDF_TYPE(crypt)) {
prt_printf(err, "bad kdf type %llu", BCH_CRYPT_KDF_TYPE(crypt));
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_crypt;
}
return 0;
for (entry = clean->start;
entry < (struct jset_entry *) vstruct_end(&clean->field);
entry = vstruct_next(entry)) {
- ret = bch2_journal_entry_validate(c, "superblock", entry,
+ ret = bch2_journal_entry_validate(c, NULL, entry,
le16_to_cpu(c->disk_sb.sb->version),
BCH_SB_BIG_ENDIAN(c->disk_sb.sb),
write);
u->entry.type = BCH_JSET_ENTRY_data_usage;
u->v = cpu_to_le64(c->usage_base->replicas[i]);
- memcpy(&u->r, e, replicas_entry_bytes(e));
+ unsafe_memcpy(&u->r, e, replicas_entry_bytes(e),
+ "embedded variable length struct");
}
for_each_member_device(ca, c, dev) {
if (vstruct_bytes(&clean->field) < sizeof(*clean)) {
prt_printf(err, "wrong size (got %zu should be %zu)",
vstruct_bytes(&clean->field), sizeof(*clean));
- return -EINVAL;
+ return -BCH_ERR_invalid_sb_clean;
}
return 0;
unsigned nr_devices = 0;
if (!out->nr_tabstops)
- printbuf_tabstop_push(out, 32);
+ printbuf_tabstop_push(out, 44);
mi = bch2_sb_get_members(sb);
if (mi) {