Encryption support

This commit is contained in:
Kent Overstreet 2016-08-17 13:23:03 -08:00
parent dda0923eeb
commit b0f08fc1e3
12 changed files with 830 additions and 446 deletions

View File

@ -29,9 +29,9 @@ util.o: CFLAGS += `pkg-config --cflags blkid uuid`
bcache.o: CFLAGS += `pkg-config --cflags libnih`
bcache-objs = bcache.o bcache-assemble.o bcache-device.o bcache-format.o\
bcache-fs.o bcache-run.o
bcache-fs.o bcache-run.o bcache-key.o libbcache.o crypto.o
bcache: LDLIBS += `pkg-config --libs uuid blkid libnih`
bcache: LDLIBS += `pkg-config --libs uuid blkid libnih` -lscrypt -lsodium -lkeyutils
bcache: $(bcache-objs) util.o libccan.a
bcache-test: LDLIBS += `pkg-config --libs openssl`

View File

@ -20,81 +20,51 @@
#include <nih/command.h>
#include <nih/option.h>
#include "ccan/ilog/ilog.h"
#include "ccan/darray/darray.h"
#include "bcache.h"
#include "libbcache.h"
#include "bcache-format.h"
struct cache_opts {
int fd;
const char *dev;
unsigned bucket_size;
unsigned tier;
unsigned replacement_policy;
unsigned replication_set;
u64 size; /* 512 byte sectors */
u64 first_bucket;
u64 nbuckets;
};
struct backingdev_opts {
int fd;
const char *dev;
const char *label;
};
static darray(struct cache_opts) cache_devices;
static darray(struct backingdev_opts) backing_devices;
static char *label = NULL;
#include "crypto.h"
/* All in units of 512 byte sectors */
static unsigned block_size, bucket_size, btree_node_size;
static u64 filesystem_size;
static unsigned tier, replacement_policy;
static uuid_le set_uuid, user_uuid;
static darray(struct dev_opts) cache_devices;
static unsigned block_size, btree_node_size;
static unsigned meta_csum_type = BCH_CSUM_CRC32C;
static unsigned data_csum_type = BCH_CSUM_CRC32C;
static unsigned compression_type = BCH_COMPRESSION_NONE;
static unsigned replication_set, meta_replicas = 1, data_replicas = 1;
static int encrypted;
static unsigned meta_replicas = 1, data_replicas = 1;
static unsigned on_error_action;
static int discard;
static unsigned version = 1;
static char *label = NULL;
static uuid_le uuid;
static u64 data_offset = BDEV_DATA_START_DEFAULT;
static unsigned cache_mode = CACHE_MODE_WRITEBACK;
/* Device specific options: */
static u64 filesystem_size;
static unsigned bucket_size;
static unsigned tier;
static unsigned replacement_policy;
static int discard;
static int set_cache(NihOption *option, const char *arg)
{
darray_append(cache_devices, (struct cache_opts) {
darray_append(cache_devices, (struct dev_opts) {
.fd = dev_open(arg),
.dev = strdup(arg),
.size = filesystem_size,
.bucket_size = bucket_size,
.tier = tier,
.replacement_policy = replacement_policy,
.replication_set = replication_set,
.size = filesystem_size,
.discard = discard,
});
return 0;
}
static int set_bdev(NihOption *option, const char *arg)
static int set_uuid(NihOption *option, const char *arg)
{
darray_append(backing_devices, (struct backingdev_opts) {
.fd = dev_open(arg),
.dev = strdup(arg),
.label = label ? strdup(label) : NULL,
});
return 0;
}
static int set_cache_set_uuid(NihOption *option, const char *arg)
{
if (uuid_parse(arg, user_uuid.b))
if (uuid_parse(arg, uuid.b))
die("Bad uuid");
return 0;
}
@ -158,13 +128,6 @@ static int set_tier(NihOption *option, const char *arg)
return 0;
}
static int set_replication_set(NihOption *option, const char *arg)
{
replication_set = strtoul_or_die(arg, CACHE_REPLICATION_SET_MAX,
"replication set");
return 0;
}
static int set_meta_replicas(NihOption *option, const char *arg)
{
meta_replicas = strtoul_or_die(arg, CACHE_SET_META_REPLICAS_WANT_MAX,
@ -179,359 +142,97 @@ static int set_data_replicas(NihOption *option, const char *arg)
return 0;
}
static int set_cache_mode(NihOption *option, const char *arg)
{
cache_mode = read_string_list_or_die(arg, bdev_cache_mode,
"cache mode");
return 0;
}
static int set_version(NihOption *option, const char *arg)
{
version = strtoul_or_die(arg, 2, "version");
return 0;
}
NihOption opts_format[] = {
// { int shortoption, char *longoption, char *help, NihOptionGroup, char *argname, void *value, NihOptionSetter}
{ 'C', "cache", N_("Format a cache device"),
NULL, "dev", NULL, set_cache },
{ 'B', "bdev", N_("Format a backing device"),
NULL, "dev", NULL, set_bdev },
{ 'l', "label", N_("label"),
NULL, "label", &label, NULL},
{ 0, "cset_uuid", N_("UUID for the cache set"),
NULL, "uuid", NULL, set_cache_set_uuid },
{ 'w', "block", N_("block size (hard sector size of SSD, often 2k"),
{ 'w', "block", N_("block size"),
NULL, "size", NULL, set_block_size },
{ 'b', "bucket", N_("bucket size"),
NULL, "size", NULL, set_bucket_sizes },
{ 'n', "btree_node", N_("Btree node size, default 256k"),
NULL, "size", NULL, set_btree_node_size },
{ 0, "fs_size", N_("Size of filesystem on device" ),
NULL, "size", NULL, set_filesystem_size },
{ 'p', "cache_replacement_policy", NULL,
NULL, "(lru|fifo|random)", NULL, set_replacement_policy },
{ 0, "metadata_csum_type", N_("Checksum type"),
NULL, "(none|crc32c|crc64)", &meta_csum_type, set_csum_type },
{ 0, "data_csum_type", N_("Checksum type"),
NULL, "(none|crc32c|crc64)", &data_csum_type, set_csum_type },
{ 0, "compression_type", N_("Compression type"),
NULL, "(none|gzip)", NULL, set_compression_type },
{ 0, "encrypted", N_("enable encryption"),
NULL, NULL, &encrypted, NULL },
{ 0, "meta_replicas", N_("number of metadata replicas"),
NULL, "#", NULL, set_meta_replicas },
{ 0, "data_replicas", N_("number of data replicas"),
NULL, "#", NULL, set_data_replicas },
{ 0, "error_action", N_("Action to take on filesystem error"),
NULL, "(continue|readonly|panic)", NULL, set_on_error_action },
{ 0, "discard", N_("Enable discards"),
NULL, NULL, &discard, NULL },
{ 'l', "label", N_("label"),
NULL, "label", &label, NULL},
{ 0, "uuid", N_("filesystem UUID"),
NULL, "uuid", NULL, set_uuid },
/* Device specific options: */
{ 0, "fs_size", N_("Size of filesystem on device" ),
NULL, "size", NULL, set_filesystem_size },
{ 'b', "bucket", N_("bucket size"),
NULL, "size", NULL, set_bucket_sizes },
{ 't', "tier", N_("tier of subsequent devices"),
NULL, "#", NULL, set_tier },
{ 0, "replication_set", N_("replication set of subsequent devices"),
NULL, "#", NULL, set_replication_set },
{ 0, "meta_replicas", N_("number of metadata replicas"),
NULL, "#", NULL, set_meta_replicas },
{ 0, "data_replicas", N_("number of data replicas"),
NULL, "#", NULL, set_data_replicas },
{ 0, "cache_mode", N_("Cache mode (for backing devices)"),
NULL, "(writethrough|writeback|writearound", NULL, set_cache_mode },
{ 'o', "data_offset", N_("data offset in sectors"),
NULL, "offset", &data_offset, NULL},
{ 'v', "version", N_("superblock version"),
NULL, "#", NULL, set_version},
{ 'p', "cache_replacement_policy", NULL,
NULL, "(lru|fifo|random)", NULL, set_replacement_policy },
{ 0, "discard", N_("Enable discards"),
NULL, NULL, &discard, NULL },
NIH_OPTION_LAST
};
void __do_write_sb(int fd, void *sb, size_t bytes)
{
char zeroes[SB_SECTOR << 9] = {0};
/* Zero start of disk */
if (pwrite(fd, zeroes, SB_SECTOR << 9, 0) != SB_SECTOR << 9) {
perror("write error trying to zero start of disk\n");
exit(EXIT_FAILURE);
}
/* Write superblock */
if (pwrite(fd, sb, bytes, SB_SECTOR << 9) != bytes) {
perror("write error trying to write superblock\n");
exit(EXIT_FAILURE);
}
fsync(fd);
close(fd);
}
#define do_write_sb(_fd, _sb) \
__do_write_sb(_fd, _sb, ((void *) __bset_bkey_last(_sb)) - (void *) _sb);
void write_backingdev_sb(int fd, unsigned block_size, unsigned mode,
u64 data_offset, const char *label,
uuid_le set_uuid)
{
char uuid_str[40];
struct backingdev_sb sb;
memset(&sb, 0, sizeof(struct cache_sb));
sb.offset = SB_SECTOR;
sb.version = BCACHE_SB_VERSION_BDEV;
sb.magic = BCACHE_MAGIC;
uuid_generate(sb.disk_uuid.b);
sb.set_uuid = set_uuid;
sb.block_size = block_size;
uuid_unparse(sb.disk_uuid.b, uuid_str);
if (label)
memcpy(sb.label, label, SB_LABEL_SIZE);
SET_BDEV_CACHE_MODE(&sb, mode);
if (data_offset != BDEV_DATA_START_DEFAULT) {
sb.version = BCACHE_SB_VERSION_BDEV_WITH_OFFSET;
sb.data_offset = data_offset;
}
sb.csum = csum_set(&sb, BCH_CSUM_CRC64);
printf("UUID: %s\n"
"version: %u\n"
"block_size: %u\n"
"data_offset: %llu\n",
uuid_str, (unsigned) sb.version,
sb.block_size, data_offset);
do_write_sb(fd, &sb);
}
static void format_v0(void)
{
struct cache_opts *i;
set_uuid = user_uuid;
darray_foreach(i, cache_devices)
bucket_size = min(bucket_size, i->bucket_size);
struct cache_sb_v0 *sb = calloc(1, sizeof(*sb));
sb->offset = SB_SECTOR;
sb->version = BCACHE_SB_VERSION_CDEV_WITH_UUID;
sb->magic = BCACHE_MAGIC;
sb->block_size = block_size;
sb->bucket_size = bucket_size;
sb->set_uuid = set_uuid;
sb->nr_in_set = darray_size(cache_devices);
if (label)
memcpy(sb->label, label, sizeof(sb->label));
darray_foreach(i, cache_devices) {
char uuid_str[40], set_uuid_str[40];
uuid_generate(sb->uuid.b);
sb->nbuckets = i->nbuckets;
sb->first_bucket = i->first_bucket;
sb->nr_this_dev = i - cache_devices.item;
sb->csum = csum_set(sb, BCH_CSUM_CRC64);
uuid_unparse(sb->uuid.b, uuid_str);
uuid_unparse(sb->set_uuid.b, set_uuid_str);
printf("UUID: %s\n"
"Set UUID: %s\n"
"version: %u\n"
"nbuckets: %llu\n"
"block_size: %u\n"
"bucket_size: %u\n"
"nr_in_set: %u\n"
"nr_this_dev: %u\n"
"first_bucket: %u\n",
uuid_str, set_uuid_str,
(unsigned) sb->version,
sb->nbuckets,
sb->block_size,
sb->bucket_size,
sb->nr_in_set,
sb->nr_this_dev,
sb->first_bucket);
do_write_sb(i->fd, sb);
}
}
static void format_v1(void)
{
struct cache_sb *sb;
struct cache_opts *i;
sb = calloc(1, sizeof(*sb) + sizeof(struct cache_member) *
darray_size(cache_devices));
sb->offset = __cpu_to_le64(SB_SECTOR);
sb->version = __cpu_to_le64(BCACHE_SB_VERSION_CDEV_V3);
sb->magic = BCACHE_MAGIC;
sb->block_size = __cpu_to_le16(block_size);
sb->set_uuid = set_uuid;
sb->user_uuid = user_uuid;
sb->nr_in_set = darray_size(cache_devices);
if (label)
memcpy(sb->label, label, sizeof(sb->label));
/*
* don't have a userspace crc32c implementation handy, just always use
* crc64
*/
SET_CACHE_SB_CSUM_TYPE(sb, BCH_CSUM_CRC64);
SET_CACHE_META_PREFERRED_CSUM_TYPE(sb, meta_csum_type);
SET_CACHE_DATA_PREFERRED_CSUM_TYPE(sb, data_csum_type);
SET_CACHE_COMPRESSION_TYPE(sb, compression_type);
SET_CACHE_BTREE_NODE_SIZE(sb, btree_node_size);
SET_CACHE_SET_META_REPLICAS_WANT(sb, meta_replicas);
SET_CACHE_SET_META_REPLICAS_HAVE(sb, meta_replicas);
SET_CACHE_SET_DATA_REPLICAS_WANT(sb, data_replicas);
SET_CACHE_SET_DATA_REPLICAS_HAVE(sb, data_replicas);
SET_CACHE_ERROR_ACTION(sb, on_error_action);
darray_foreach(i, cache_devices) {
struct cache_member *m = sb->members +
(i - cache_devices.item);
uuid_generate(m->uuid.b);
m->nbuckets = __cpu_to_le64(i->nbuckets);
m->first_bucket = __cpu_to_le16(i->first_bucket);
m->bucket_size = __cpu_to_le16(i->bucket_size);
if (__le64_to_cpu(m->nbuckets < 1 << 7))
die("Not enough buckets: %llu, need %u",
__le64_to_cpu(m->nbuckets), 1 << 7);
SET_CACHE_TIER(m, i->tier);
SET_CACHE_REPLICATION_SET(m, i->replication_set);
SET_CACHE_REPLACEMENT(m, i->replacement_policy);
SET_CACHE_DISCARD(m, discard);
}
sb->u64s = __cpu_to_le16(bch_journal_buckets_offset(sb));
darray_foreach(i, cache_devices) {
char uuid_str[40], set_uuid_str[40];
struct cache_member *m = sb->members +
(i - cache_devices.item);
sb->disk_uuid = m->uuid;
sb->nr_this_dev = i - cache_devices.item;
sb->csum = __cpu_to_le64(__csum_set(sb, __le16_to_cpu(sb->u64s),
CACHE_SB_CSUM_TYPE(sb)));
uuid_unparse(sb->disk_uuid.b, uuid_str);
uuid_unparse(sb->user_uuid.b, set_uuid_str);
printf("UUID: %s\n"
"Set UUID: %s\n"
"version: %u\n"
"nbuckets: %llu\n"
"block_size: %u\n"
"bucket_size: %u\n"
"nr_in_set: %u\n"
"nr_this_dev: %u\n"
"first_bucket: %u\n",
uuid_str, set_uuid_str,
(unsigned) sb->version,
__le64_to_cpu(m->nbuckets),
__le16_to_cpu(sb->block_size),
__le16_to_cpu(m->bucket_size),
sb->nr_in_set,
sb->nr_this_dev,
__le16_to_cpu(m->first_bucket));
do_write_sb(i->fd, sb);
}
}
int cmd_format(NihCommand *command, char * const *args)
{
struct cache_opts *i;
struct backingdev_opts *ib;
char *passphrase = NULL;
if (!darray_size(cache_devices) &&
!darray_size(backing_devices))
if (!darray_size(cache_devices))
die("Please supply a device");
if (uuid_is_null(user_uuid.b))
uuid_generate(user_uuid.b);
if (uuid_is_null(uuid.b))
uuid_generate(uuid.b);
uuid_generate(set_uuid.b);
if (encrypted) {
char *pass2;
if (!block_size) {
darray_foreach(i, cache_devices)
block_size = max(block_size,
get_blocksize(i->dev, i->fd));
passphrase = read_passphrase("Enter passphrase: ");
pass2 = read_passphrase("Enter same passphrase again: ");
darray_foreach(ib, backing_devices)
block_size = max(block_size,
get_blocksize(ib->dev, ib->fd));
}
darray_foreach(i, cache_devices) {
if (!i->size)
i->size = get_size(i->dev, i->fd);
if (!i->bucket_size) {
u64 bytes = i->size << 9;
if (bytes < 1 << 20) /* 1M device - 256 4k buckets*/
i->bucket_size = rounddown_pow_of_two(bytes >> 17);
else
/* Max 1M bucket at around 256G */
i->bucket_size = 8 << min((ilog2(bytes >> 20) / 2), 9U);
if (strcmp(passphrase, pass2)) {
memzero_explicit(passphrase, strlen(passphrase));
memzero_explicit(pass2, strlen(pass2));
die("Passphrases do not match");
}
if (i->bucket_size < block_size)
die("Bucket size cannot be smaller than block size");
i->nbuckets = i->size / i->bucket_size;
i->first_bucket = (23 / i->bucket_size) + 3;
if (i->nbuckets < 1 << 7)
die("Not enough buckets: %llu, need %u",
i->nbuckets, 1 << 7);
memzero_explicit(pass2, strlen(pass2));
free(pass2);
}
if (!btree_node_size) {
/* 256k default btree node size */
btree_node_size = 512;
bcache_format(cache_devices.item, darray_size(cache_devices),
block_size,
btree_node_size,
meta_csum_type,
data_csum_type,
compression_type,
passphrase,
meta_replicas,
data_replicas,
on_error_action,
label,
uuid);
darray_foreach(i, cache_devices)
btree_node_size = min(btree_node_size, i->bucket_size);
if (passphrase) {
memzero_explicit(passphrase, strlen(passphrase));
free(passphrase);
}
switch (version) {
case 0:
format_v0();
break;
case 1:
format_v1();
break;
}
darray_foreach(ib, backing_devices)
write_backingdev_sb(ib->fd, block_size, cache_mode,
data_offset, ib->label,
set_uuid);
return 0;
}

58
bcache-key.c Normal file
View File

@ -0,0 +1,58 @@
#include <errno.h>
#include <unistd.h>
#include <keyutils.h>
#include <uuid/uuid.h>
#include <nih/command.h>
#include <nih/option.h>
#include "bcache.h"
#include "libbcache.h"
#include "crypto.h"
NihOption opts_unlock[] = {
NIH_OPTION_LAST
};
int cmd_unlock(NihCommand *command, char * const *args)
{
struct bcache_disk_key disk_key;
struct bcache_key key;
struct cache_sb sb;
char *passphrase;
char uuid[40];
char description[60];
if (!args[0] || args[1])
die("please supply a single device");
bcache_super_read(args[0], &sb);
if (!CACHE_SET_ENCRYPTION_KEY(&sb))
die("filesystem is not encrypted");
memcpy(&disk_key, sb.encryption_key, sizeof(disk_key));
if (!memcmp(&disk_key, bch_key_header, sizeof(bch_key_header)))
die("filesystem does not have encryption key");
passphrase = read_passphrase("Enter passphrase: ");
derive_passphrase(&key, passphrase);
disk_key_encrypt(&disk_key, &key);
if (memcmp(&disk_key, bch_key_header, sizeof(bch_key_header)))
die("incorrect passphrase");
uuid_unparse_lower(sb.user_uuid.b, uuid);
sprintf(description, "bcache:%s", uuid);
if (add_key("logon", description, &key, sizeof(key),
KEY_SPEC_USER_KEYRING) < 0)
die("add_key error: %s", strerror(errno));
memzero_explicit(&disk_key, sizeof(disk_key));
memzero_explicit(&key, sizeof(key));
memzero_explicit(passphrase, strlen(passphrase));
free(passphrase);
return 0;
}

7
bcache-key.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef _BCACHE_KEY_H
#define _BCACHE_KEY_H
extern NihOption opts_unlock[];
int cmd_unlock(NihCommand *, char * const *);
#endif /* _BCACHE_KEY_H */

View File

@ -77,7 +77,7 @@ struct bpos {
#else
#error edit for your odd byteorder.
#endif
} __attribute__((packed)) __attribute__((aligned(4)));
} __attribute__((packed, aligned(4)));
#define KEY_INODE_MAX ((__u64)~0ULL)
#define KEY_OFFSET_MAX ((__u64)~0ULL)
@ -102,6 +102,16 @@ struct bch_val {
__u64 __nothing[0];
};
struct bversion {
#if defined(__LITTLE_ENDIAN)
__u64 low;
__u32 high;
#elif defined(__BIG_ENDIAN)
__u32 high;
__u64 low;
#endif
} __attribute__((packed, aligned(4)));
struct bkey {
__u64 _data[0];
@ -117,17 +127,17 @@ struct bkey {
#if defined(__LITTLE_ENDIAN)
__u8 pad[1];
__u32 version;
struct bversion version;
__u32 size; /* extent size, in sectors */
struct bpos p;
#elif defined(__BIG_ENDIAN)
struct bpos p;
__u32 size; /* extent size, in sectors */
__u32 version;
struct bversion version;
__u8 pad[1];
#endif
} __attribute__((packed)) __attribute__((aligned(8)));
} __attribute__((packed, aligned(8)));
struct bkey_packed {
__u64 _data[0];
@ -149,7 +159,7 @@ struct bkey_packed {
* to the same size as struct bkey should hopefully be safest.
*/
__u8 pad[sizeof(struct bkey) - 3];
} __attribute__((packed)) __attribute__((aligned(8)));
} __attribute__((packed, aligned(8)));
#define BKEY_U64s (sizeof(struct bkey) / sizeof(__u64))
#define KEY_PACKED_BITS_START 24
@ -164,7 +174,8 @@ enum bch_bkey_fields {
BKEY_FIELD_OFFSET,
BKEY_FIELD_SNAPSHOT,
BKEY_FIELD_SIZE,
BKEY_FIELD_VERSION,
BKEY_FIELD_VERSION_HIGH,
BKEY_FIELD_VERSION_LOW,
BKEY_NR_FIELDS,
};
@ -180,7 +191,8 @@ enum bch_bkey_fields {
bkey_format_field(OFFSET, p.offset), \
bkey_format_field(SNAPSHOT, p.snapshot), \
bkey_format_field(SIZE, size), \
bkey_format_field(VERSION, version), \
bkey_format_field(VERSION_HIGH, version.high), \
bkey_format_field(VERSION_LOW, version.low), \
}, \
})
@ -358,39 +370,46 @@ struct bch_extent_crc32 {
uncompressed_size:8,
csum_type:4,
compression_type:4;
__u32 csum;
#elif defined (__BIG_ENDIAN_BITFIELD)
__u32 csum_type:4,
compression_type:4,
__u32 csum;
__u32 compression_type:4,
csum_type:4,
uncompressed_size:8,
compressed_size:8,
offset:7,
type:1;
#endif
__u32 csum;
} __attribute__((packed)) __attribute__((aligned(8)));
} __attribute__((packed, aligned(8)));
#define CRC32_EXTENT_SIZE_MAX (1U << 7)
/* 64k */
#define BCH_COMPRESSED_EXTENT_MAX 128
struct bch_extent_crc64 {
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u64 type:3,
compressed_size:18,
uncompressed_size:18,
offset:17,
compressed_size:10,
uncompressed_size:10,
offset:10,
nonce:23,
csum_type:4,
compression_type:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
__u64 csum_type:4,
compression_type:4,
offset:17,
uncompressed_size:18,
compressed_size:18,
__u64 compression_type:4,
csum_type:4,
nonce:23,
offset:10,
uncompressed_size:10,
compressed_size:10,
type:3;
#endif
__u64 csum;
} __attribute__((packed)) __attribute__((aligned(8)));
} __attribute__((packed, aligned(8)));
#define CRC64_EXTENT_SIZE_MAX (1U << 17)
#define CRC64_EXTENT_SIZE_MAX (1U << 10) /* inclusive */
#define CRC64_NONCE_MAX (1U << 23) /* exclusive */
/*
* @reservation - pointer hasn't been written to, just reserved
@ -411,10 +430,17 @@ struct bch_extent_ptr {
erasure_coded:1,
type:2;
#endif
} __attribute__((packed)) __attribute__((aligned(8)));
} __attribute__((packed, aligned(8)));
union bch_extent_entry {
__u8 type;
#if defined(__LITTLE_ENDIAN__) || BITS_PER_LONG == 64
unsigned long type;
#elif BITS_PER_LONG == 32
struct {
unsigned long pad;
unsigned long type;
};
#endif
struct bch_extent_crc32 crc32;
struct bch_extent_crc64 crc64;
struct bch_extent_ptr ptr;
@ -441,9 +467,29 @@ struct bch_extent {
union bch_extent_entry start[0];
__u64 _data[0];
} __attribute__((packed)) __attribute__((aligned(8)));
} __attribute__((packed, aligned(8)));
BKEY_VAL_TYPE(extent, BCH_EXTENT);
/* Maximum size (in u64s) a single pointer could be: */
#define BKEY_EXTENT_PTR_U64s_MAX\
((sizeof(struct bch_extent_crc64) + \
sizeof(struct bch_extent_ptr)) / sizeof(u64))
/* Maximum possible size of an entire extent value: */
#if 0
/* There's a hack in the keylist code that needs to be fixed.. */
#define BKEY_EXTENT_VAL_U64s_MAX \
(BKEY_EXTENT_PTR_U64s_MAX * BCH_REPLICAS_MAX)
#else
#define BKEY_EXTENT_VAL_U64s_MAX 8
#endif
/* * Maximum possible size of an entire extent, key + value: */
#define BKEY_EXTENT_U64s_MAX (BKEY_U64s + BKEY_EXTENT_VAL_U64s_MAX)
#define BKEY_BTREE_PTR_VAL_U64s_MAX BCH_REPLICAS_MAX
#define BKEY_BTREE_PTR_U64s_MAX (BKEY_U64s + BCH_REPLICAS_MAX)
/* Inodes */
#define BLOCKDEV_INODE_MAX 4096
@ -453,18 +499,8 @@ BKEY_VAL_TYPE(extent, BCH_EXTENT);
enum bch_inode_types {
BCH_INODE_FS = 128,
BCH_INODE_BLOCKDEV = 129,
BCH_INODE_CACHED_DEV = 130,
};
enum {
BCH_FS_PRIVATE_START = 16,
__BCH_INODE_I_SIZE_DIRTY = 16,
};
#define BCH_FL_USER_FLAGS ((1U << BCH_FS_PRIVATE_START) - 1)
#define BCH_INODE_I_SIZE_DIRTY (1 << __BCH_INODE_I_SIZE_DIRTY)
struct bch_inode {
struct bch_val v;
@ -478,24 +514,64 @@ struct bch_inode {
__le64 i_mtime;
__le64 i_size;
__le64 i_sectors;
__le32 i_uid;
__le32 i_gid;
__le32 i_nlink;
__le32 i_dev;
__le64 i_hash_seed;
} __attribute__((packed));
BKEY_VAL_TYPE(inode, BCH_INODE_FS);
enum {
/*
* User flags (get/settable with FS_IOC_*FLAGS, correspond to FS_*_FL
* flags)
*/
__BCH_INODE_SYNC = 0,
__BCH_INODE_IMMUTABLE = 1,
__BCH_INODE_APPEND = 2,
__BCH_INODE_NODUMP = 3,
__BCH_INODE_NOATIME = 4,
__BCH_INODE_I_SIZE_DIRTY= 5,
__BCH_INODE_I_SECTORS_DIRTY= 6,
/* not implemented yet: */
__BCH_INODE_HAS_XATTRS = 7, /* has xattrs in xattr btree */
};
LE32_BITMASK(INODE_STR_HASH_TYPE, struct bch_inode, i_flags, 28, 32);
#define BCH_INODE_SYNC (1 << __BCH_INODE_SYNC)
#define BCH_INODE_IMMUTABLE (1 << __BCH_INODE_IMMUTABLE)
#define BCH_INODE_APPEND (1 << __BCH_INODE_APPEND)
#define BCH_INODE_NODUMP (1 << __BCH_INODE_NODUMP)
#define BCH_INODE_NOATIME (1 << __BCH_INODE_NOATIME)
#define BCH_INODE_I_SIZE_DIRTY (1 << __BCH_INODE_I_SIZE_DIRTY)
#define BCH_INODE_I_SECTORS_DIRTY (1 << __BCH_INODE_I_SECTORS_DIRTY)
#define BCH_INODE_HAS_XATTRS (1 << __BCH_INODE_HAS_XATTRS)
struct bch_inode_blockdev {
struct bch_val v;
struct bch_inode i_inode;
__le64 i_size;
__le64 i_flags;
/* Seconds: */
__le64 i_ctime;
__le64 i_mtime;
uuid_le i_uuid;
__u8 i_label[32];
} __attribute__((packed));
} __attribute__((packed, aligned(8)));
BKEY_VAL_TYPE(inode_blockdev, BCH_INODE_BLOCKDEV);
/* Thin provisioned volume, or cache for another block device? */
LE64_BITMASK(CACHED_DEV, struct bch_inode_blockdev, i_flags, 0, 1)
/* Dirents */
/*
@ -644,7 +720,9 @@ struct cache_sb {
* to change:
*/
uuid_le user_uuid;
__le64 pad1[6];
__le64 flags2;
__le64 encryption_key[5];
/* Number of cache_member entries: */
__u8 nr_in_set;
@ -671,9 +749,11 @@ struct cache_sb {
};
};
LE64_BITMASK(CACHE_SYNC, struct cache_sb, flags, 0, 1);
/* XXX: rename CACHE_SET -> BCH_FS or something? */
LE64_BITMASK(CACHE_ERROR_ACTION, struct cache_sb, flags, 1, 4);
LE64_BITMASK(CACHE_SET_SYNC, struct cache_sb, flags, 0, 1);
LE64_BITMASK(CACHE_SET_ERROR_ACTION, struct cache_sb, flags, 1, 4);
#define BCH_ON_ERROR_CONTINUE 0U
#define BCH_ON_ERROR_RO 1U
#define BCH_ON_ERROR_PANIC 2U
@ -686,35 +766,144 @@ LE64_BITMASK(CACHE_SET_DATA_REPLICAS_WANT,struct cache_sb, flags, 8, 12);
LE64_BITMASK(CACHE_SB_CSUM_TYPE, struct cache_sb, flags, 12, 16);
LE64_BITMASK(CACHE_META_PREFERRED_CSUM_TYPE,struct cache_sb, flags, 16, 20);
LE64_BITMASK(CACHE_SET_META_CSUM_TYPE,struct cache_sb, flags, 16, 20);
#define BCH_CSUM_NONE 0U
#define BCH_CSUM_CRC32C 1U
#define BCH_CSUM_CRC64 2U
#define BCH_CSUM_NR 3U
#define BCH_CSUM_CHACHA20_POLY1305 3U
#define BCH_CSUM_NR 4U
LE64_BITMASK(CACHE_BTREE_NODE_SIZE, struct cache_sb, flags, 20, 36);
static inline _Bool bch_csum_type_is_encryption(unsigned type)
{
switch (type) {
case BCH_CSUM_CHACHA20_POLY1305:
return 1;
default:
return 0;
}
}
LE64_BITMASK(CACHE_SET_BTREE_NODE_SIZE, struct cache_sb, flags, 20, 36);
LE64_BITMASK(CACHE_SET_META_REPLICAS_HAVE,struct cache_sb, flags, 36, 40);
LE64_BITMASK(CACHE_SET_DATA_REPLICAS_HAVE,struct cache_sb, flags, 40, 44);
LE64_BITMASK(CACHE_SET_DIRENT_CSUM_TYPE,struct cache_sb, flags, 44, 48);
enum {
BCH_DIRENT_CSUM_CRC32C = 0,
BCH_DIRENT_CSUM_CRC64 = 1,
BCH_DIRENT_CSUM_SIPHASH = 2,
BCH_DIRENT_CSUM_SHA1 = 3,
LE64_BITMASK(CACHE_SET_STR_HASH_TYPE,struct cache_sb, flags, 44, 48);
enum bch_str_hash_type {
BCH_STR_HASH_CRC32C = 0,
BCH_STR_HASH_CRC64 = 1,
BCH_STR_HASH_SIPHASH = 2,
BCH_STR_HASH_SHA1 = 3,
};
LE64_BITMASK(CACHE_DATA_PREFERRED_CSUM_TYPE, struct cache_sb, flags, 48, 52);
#define BCH_STR_HASH_NR 4
LE64_BITMASK(CACHE_COMPRESSION_TYPE, struct cache_sb, flags, 52, 56);
LE64_BITMASK(CACHE_SET_DATA_CSUM_TYPE, struct cache_sb, flags, 48, 52);
LE64_BITMASK(CACHE_SET_COMPRESSION_TYPE, struct cache_sb, flags, 52, 56);
enum {
BCH_COMPRESSION_NONE = 0,
BCH_COMPRESSION_LZO1X = 1,
BCH_COMPRESSION_LZ4 = 1,
BCH_COMPRESSION_GZIP = 2,
BCH_COMPRESSION_XZ = 3,
};
#define BCH_COMPRESSION_NR 3U
/* Limit inode numbers to 32 bits: */
LE64_BITMASK(CACHE_INODE_32BIT, struct cache_sb, flags, 56, 57);
LE64_BITMASK(CACHE_SET_GC_RESERVE, struct cache_sb, flags, 57, 63);
LE64_BITMASK(CACHE_SET_ROOT_RESERVE, struct cache_sb, flags2, 0, 6);
/*
* If nonzero, encryption is enabled; overrides DATA/META_CSUM_TYPE. Also
* indicates encryption algorithm in use, if/when we get more than one:
*
*/
LE64_BITMASK(CACHE_SET_ENCRYPTION_TYPE, struct cache_sb, flags2, 6, 10);
/*
* If nonzero, we have an encryption key in the superblock, which is the key
* used to encrypt all other data/metadata. The key will normally be encrypted
* with the key userspace provides, but if encryption has been turned off we'll
* just store the master key unencrypted in the superblock so we can access the
* previously encrypted data.
*/
LE64_BITMASK(CACHE_SET_ENCRYPTION_KEY, struct cache_sb, flags2, 10, 11);
/* options: */
/**
* CACHE_SET_OPT(name, choices, min, max, sb_option, sysfs_writeable)
*
* @name - name of mount option, sysfs attribute, and struct cache_set_opts
* member
*
* @choices - array of strings that the user can select from - option is by
* array index
*
* Booleans are special cased; if @choices is bch_bool_opt the mount
* options name and noname will work as expected.
*
* @min, @max
*
* @sb_option - name of corresponding superblock option
*
* @sysfs_writeable - if true, option will be modifiable at runtime via sysfs
*/
#define CACHE_SET_SB_OPTS() \
CACHE_SET_OPT(errors, \
bch_error_actions, \
0, BCH_NR_ERROR_ACTIONS, \
CACHE_SET_ERROR_ACTION, \
true) \
CACHE_SET_OPT(metadata_replicas, \
bch_uint_opt, \
0, BCH_REPLICAS_MAX, \
CACHE_SET_META_REPLICAS_WANT, \
false) \
CACHE_SET_OPT(data_replicas, \
bch_uint_opt, \
0, BCH_REPLICAS_MAX, \
CACHE_SET_DATA_REPLICAS_WANT, \
false) \
CACHE_SET_OPT(metadata_checksum, \
bch_csum_types, \
0, BCH_CSUM_NR, \
CACHE_SET_META_CSUM_TYPE, \
true) \
CACHE_SET_OPT(data_checksum, \
bch_csum_types, \
0, BCH_CSUM_NR, \
CACHE_SET_DATA_CSUM_TYPE, \
true) \
CACHE_SET_OPT(compression, \
bch_compression_types, \
0, BCH_COMPRESSION_NR, \
CACHE_SET_COMPRESSION_TYPE, \
true) \
CACHE_SET_OPT(str_hash, \
bch_str_hash_types, \
0, BCH_STR_HASH_NR, \
CACHE_SET_STR_HASH_TYPE, \
true) \
CACHE_SET_OPT(inodes_32bit, \
bch_bool_opt, 0, 2, \
CACHE_INODE_32BIT, \
true) \
CACHE_SET_OPT(gc_reserve_percent, \
bch_uint_opt, \
5, 21, \
CACHE_SET_GC_RESERVE, \
false) \
CACHE_SET_OPT(root_reserve_percent, \
bch_uint_opt, \
0, 21, \
CACHE_SET_ROOT_RESERVE, \
false)
/* backing device specific stuff: */
struct backingdev_sb {
@ -828,18 +1017,13 @@ static inline __u64 bset_magic(struct cache_sb *sb)
return __le64_to_cpu(sb->set_magic) ^ BSET_MAGIC;
}
/*
* Journal
*
* On disk format for a journal entry:
* seq is monotonically increasing; every journal entry has its own unique
* sequence number.
*
* last_seq is the oldest journal entry that still has keys the btree hasn't
* flushed to disk yet.
*
* version is for on disk format changes.
*/
/* 128 bits, sufficient for cryptographic MACs: */
struct bch_csum {
__le64 lo;
__le64 hi;
};
/* Journal */
#define BCACHE_JSET_VERSION_UUIDv1 1
#define BCACHE_JSET_VERSION_UUID 1 /* Always latest UUID format */
@ -860,12 +1044,11 @@ struct jset_entry {
#define JSET_KEYS_U64s (sizeof(struct jset_entry) / sizeof(__u64))
LE32_BITMASK(JKEYS_TYPE, struct jset_entry, flags, 0, 8);
LE32_BITMASK(JOURNAL_ENTRY_TYPE, struct jset_entry, flags, 0, 8);
enum {
JKEYS_BTREE_KEYS = 0,
JKEYS_BTREE_ROOT = 1,
JKEYS_PRIO_PTRS = 2,
JOURNAL_ENTRY_BTREE_KEYS = 0,
JOURNAL_ENTRY_BTREE_ROOT = 1,
JOURNAL_ENTRY_PRIO_PTRS = 2,
/*
* Journal sequence numbers can be blacklisted: bsets record the max
@ -877,11 +1060,22 @@ enum {
* and then record that we skipped it so that the next time we crash and
* recover we don't think there was a missing journal entry.
*/
JKEYS_JOURNAL_SEQ_BLACKLISTED = 3,
JOURNAL_ENTRY_JOURNAL_SEQ_BLACKLISTED = 3,
};
/*
* On disk format for a journal entry:
* seq is monotonically increasing; every journal entry has its own unique
* sequence number.
*
* last_seq is the oldest journal entry that still has keys the btree hasn't
* flushed to disk yet.
*
* version is for on disk format changes.
*/
struct jset {
__le64 csum;
struct bch_csum csum;
__le64 magic;
__le32 version;
__le32 flags;
@ -901,11 +1095,15 @@ struct jset {
};
LE32_BITMASK(JSET_CSUM_TYPE, struct jset, flags, 0, 4);
LE32_BITMASK(JSET_BIG_ENDIAN, struct jset, flags, 4, 5);
#define BCH_JOURNAL_BUCKETS_MIN 20
/* Bucket prios/gens */
struct prio_set {
__le64 csum;
struct bch_csum csum;
__le64 magic;
__le32 version;
__le32 flags;
@ -985,7 +1183,7 @@ LE32_BITMASK(BSET_BTREE_LEVEL, struct bset, flags, 4, 8);
LE32_BITMASK(BSET_BIG_ENDIAN, struct bset, flags, 8, 9);
struct btree_node {
__le64 csum;
struct bch_csum csum;
__le64 magic;
/* Closed interval: */
@ -997,10 +1195,22 @@ struct btree_node {
} __attribute__((packed));
struct btree_node_entry {
__le64 csum;
struct bch_csum csum;
struct bset keys;
} __attribute__((packed));
/* Crypto: */
struct nonce {
__le32 d[4];
};
#define BCACHE_MASTER_KEY_HEADER "bch**key"
#define BCACHE_MASTER_KEY_NONCE ((struct nonce) \
{{ __cpu_to_le32(1), __cpu_to_le32(2), \
__cpu_to_le32(3), __cpu_to_le32(4) }})
/* OBSOLETE */
#define BITMASK(name, type, field, offset, end) \

View File

@ -31,6 +31,7 @@
#include "bcache-format.h"
#include "bcache-fs.h"
#include "bcache-run.h"
#include "bcache-key.h"
#define PACKAGE_NAME "bcache"
#define PACKAGE_VERSION "1.0"
@ -130,6 +131,11 @@ static NihCommand commands[] = {
CMD(device_remove, N_("<volume> <devices>"),
"Removes a device from its volume"),
/* Crypto */
CMD(unlock, N_("<device>"),
"Unlock an encrypted filesystem"),
#if 0
CMD(modify, N_("<options>"),
"Modifies attributes related to the volume",

130
crypto.c Normal file
View File

@ -0,0 +1,130 @@
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <linux/random.h>
#include <libscrypt.h>
#include <sodium/crypto_stream_chacha20.h>
#include "crypto.h"
char *read_passphrase(const char *prompt)
{
struct termios old, new;
char *buf = NULL;
size_t buflen = 0;
ssize_t ret;
fprintf(stderr, "%s", prompt);
fflush(stderr);
if (tcgetattr(fileno(stdin), &old))
die("error getting terminal attrs");
new = old;
new.c_lflag &= ~ECHO;
if (tcsetattr(fileno(stdin), TCSAFLUSH, &new))
die("error setting terminal attrs");
ret = getline(&buf, &buflen, stdin);
if (ret <= 0)
die("error reading passphrase");
tcsetattr(fileno(stdin), TCSAFLUSH, &old);
fprintf(stderr, "\n");
return buf;
}
void derive_passphrase(struct bcache_key *key, const char *passphrase)
{
const unsigned char salt[] = "bcache";
int ret;
ret = libscrypt_scrypt((void *) passphrase, strlen(passphrase),
salt, sizeof(salt),
SCRYPT_N, SCRYPT_r, SCRYPT_p,
(void *) key, sizeof(*key));
if (ret)
die("scrypt error: %i", ret);
}
void disk_key_encrypt(struct bcache_disk_key *disk_key,
struct bcache_key *key)
{
int ret;
ret = crypto_stream_chacha20_xor((void *) disk_key,
(void *) disk_key, sizeof(*disk_key),
(void *) &bch_master_key_nonce,
(void *) key);
if (ret)
die("chacha20 error: %i", ret);
}
void disk_key_init(struct bcache_disk_key *disk_key)
{
ssize_t ret;
memcpy(&disk_key->header, bch_key_header, sizeof(bch_key_header));
#if 0
ret = getrandom(disk_key->key, sizeof(disk_key->key), GRND_RANDOM);
if (ret != sizeof(disk_key->key))
die("error getting random bytes for key");
#else
int fd = open("/dev/random", O_RDONLY|O_NONBLOCK);
if (fd < 0)
die("error opening /dev/random");
size_t n = 0;
struct timespec start;
bool printed = false;
clock_gettime(CLOCK_MONOTONIC, &start);
while (n < sizeof(disk_key->key)) {
struct timeval timeout = { 1, 0 };
fd_set set;
FD_ZERO(&set);
FD_SET(fd, &set);
if (select(fd + 1, &set, NULL, NULL, &timeout) < 0)
die("select error");
ret = read(fd,
(void *) disk_key->key + n,
sizeof(disk_key->key) - n);
if (ret == -1 && errno != EINTR && errno != EAGAIN)
die("error reading from /dev/random");
if (ret > 0)
n += ret;
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
now.tv_sec -= start.tv_sec;
now.tv_nsec -= start.tv_nsec;
while (now.tv_nsec < 0) {
long nsec_per_sec = 1000 * 1000 * 1000;
long sec = now.tv_nsec / nsec_per_sec - 1;
now.tv_nsec -= sec * nsec_per_sec;
now.tv_sec += sec;
}
if (!printed && now.tv_sec >= 3) {
printf("Reading from /dev/random is taking a long time...\n)");
printed = true;
}
}
close(fd);
#endif
}

23
crypto.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef _CRYPTO_H
#define _CRYPTO_H
#include "util.h"
struct bcache_key {
u64 key[4];
};
struct bcache_disk_key {
u64 header;
u64 key[4];
};
static const char bch_key_header[8] = BCACHE_MASTER_KEY_HEADER;
static const struct nonce bch_master_key_nonce = BCACHE_MASTER_KEY_NONCE;
char *read_passphrase(const char *);
void derive_passphrase(struct bcache_key *, const char *);
void disk_key_encrypt(struct bcache_disk_key *, struct bcache_key *);
void disk_key_init(struct bcache_disk_key *);
#endif /* _CRYPTO_H */

206
libbcache.c Normal file
View File

@ -0,0 +1,206 @@
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <uuid/uuid.h>
#include "ccan/ilog/ilog.h"
#include "bcache-ondisk.h"
#include "libbcache.h"
#include "crypto.h"
void __do_write_sb(int fd, void *sb, size_t bytes)
{
char zeroes[SB_SECTOR << 9] = {0};
/* Zero start of disk */
if (pwrite(fd, zeroes, SB_SECTOR << 9, 0) != SB_SECTOR << 9) {
perror("write error trying to zero start of disk\n");
exit(EXIT_FAILURE);
}
/* Write superblock */
if (pwrite(fd, sb, bytes, SB_SECTOR << 9) != bytes) {
perror("write error trying to write superblock\n");
exit(EXIT_FAILURE);
}
fsync(fd);
close(fd);
}
#define do_write_sb(_fd, _sb) \
__do_write_sb(_fd, _sb, ((void *) __bset_bkey_last(_sb)) - (void *) _sb);
void bcache_format(struct dev_opts *devs, size_t nr_devs,
unsigned block_size,
unsigned btree_node_size,
unsigned meta_csum_type,
unsigned data_csum_type,
unsigned compression_type,
const char *passphrase,
unsigned meta_replicas,
unsigned data_replicas,
unsigned on_error_action,
char *label,
uuid_le uuid)
{
struct cache_sb *sb;
struct dev_opts *i;
/* calculate block size: */
if (!block_size)
for (i = devs; i < devs + nr_devs; i++)
block_size = max(block_size,
get_blocksize(i->dev, i->fd));
/* calculate bucket sizes: */
for (i = devs; i < devs + nr_devs; i++) {
if (!i->size)
i->size = get_size(i->dev, i->fd);
if (!i->bucket_size) {
u64 bytes = i->size << 9;
if (bytes < 1 << 20) /* 1M device - 256 4k buckets*/
i->bucket_size = rounddown_pow_of_two(bytes >> 17);
else
/* Max 1M bucket at around 256G */
i->bucket_size = 8 << min((ilog2(bytes >> 20) / 2), 9U);
}
if (i->bucket_size < block_size)
die("Bucket size cannot be smaller than block size");
i->nbuckets = i->size / i->bucket_size;
i->first_bucket = (23 / i->bucket_size) + 3;
if (i->nbuckets < 1 << 7)
die("Not enough buckets: %llu, need %u",
i->nbuckets, 1 << 7);
}
/* calculate btree node size: */
if (!btree_node_size) {
/* 256k default btree node size */
btree_node_size = 512;
for (i = devs; i < devs + nr_devs; i++)
btree_node_size = min(btree_node_size, i->bucket_size);
}
sb = calloc(1, sizeof(*sb) + sizeof(struct cache_member) * nr_devs);
sb->offset = __cpu_to_le64(SB_SECTOR);
sb->version = __cpu_to_le64(BCACHE_SB_VERSION_CDEV_V3);
sb->magic = BCACHE_MAGIC;
sb->block_size = __cpu_to_le16(block_size);
sb->user_uuid = uuid;
sb->nr_in_set = nr_devs;
uuid_generate(sb->set_uuid.b);
if (label)
strncpy((char *) sb->label, label, sizeof(sb->label));
/*
* don't have a userspace crc32c implementation handy, just always use
* crc64
*/
SET_CACHE_SB_CSUM_TYPE(sb, BCH_CSUM_CRC64);
SET_CACHE_SET_META_CSUM_TYPE(sb, meta_csum_type);
SET_CACHE_SET_DATA_CSUM_TYPE(sb, data_csum_type);
SET_CACHE_SET_COMPRESSION_TYPE(sb, compression_type);
SET_CACHE_SET_BTREE_NODE_SIZE(sb, btree_node_size);
SET_CACHE_SET_META_REPLICAS_WANT(sb, meta_replicas);
SET_CACHE_SET_META_REPLICAS_HAVE(sb, meta_replicas);
SET_CACHE_SET_DATA_REPLICAS_WANT(sb, data_replicas);
SET_CACHE_SET_DATA_REPLICAS_HAVE(sb, data_replicas);
SET_CACHE_SET_ERROR_ACTION(sb, on_error_action);
if (passphrase) {
struct bcache_key key;
struct bcache_disk_key disk_key;
derive_passphrase(&key, passphrase);
disk_key_init(&disk_key);
disk_key_encrypt(&disk_key, &key);
memcpy(sb->encryption_key, &disk_key, sizeof(disk_key));
SET_CACHE_SET_ENCRYPTION_TYPE(sb, 1);
SET_CACHE_SET_ENCRYPTION_KEY(sb, 1);
memzero_explicit(&disk_key, sizeof(disk_key));
memzero_explicit(&key, sizeof(key));
}
for (i = devs; i < devs + nr_devs; i++) {
struct cache_member *m = sb->members + (i - devs);
uuid_generate(m->uuid.b);
m->nbuckets = __cpu_to_le64(i->nbuckets);
m->first_bucket = __cpu_to_le16(i->first_bucket);
m->bucket_size = __cpu_to_le16(i->bucket_size);
SET_CACHE_TIER(m, i->tier);
SET_CACHE_REPLACEMENT(m, i->replacement_policy);
SET_CACHE_DISCARD(m, i->discard);
}
sb->u64s = __cpu_to_le16(bch_journal_buckets_offset(sb));
for (i = devs; i < devs + nr_devs; i++) {
struct cache_member *m = sb->members + (i - devs);
char uuid_str[40], set_uuid_str[40];
sb->disk_uuid = m->uuid;
sb->nr_this_dev = i - devs;
sb->csum = __cpu_to_le64(__csum_set(sb, __le16_to_cpu(sb->u64s),
CACHE_SB_CSUM_TYPE(sb)));
uuid_unparse(sb->disk_uuid.b, uuid_str);
uuid_unparse(sb->user_uuid.b, set_uuid_str);
printf("UUID: %s\n"
"Set UUID: %s\n"
"version: %u\n"
"nbuckets: %llu\n"
"block_size: %u\n"
"bucket_size: %u\n"
"nr_in_set: %u\n"
"nr_this_dev: %u\n"
"first_bucket: %u\n",
uuid_str, set_uuid_str,
(unsigned) sb->version,
__le64_to_cpu(m->nbuckets),
__le16_to_cpu(sb->block_size),
__le16_to_cpu(m->bucket_size),
sb->nr_in_set,
sb->nr_this_dev,
__le16_to_cpu(m->first_bucket));
do_write_sb(i->fd, sb);
}
free(sb);
}
void bcache_super_read(const char *path, struct cache_sb *sb)
{
int fd = open(path, O_RDONLY);
if (fd < 0)
die("couldn't open %s", path);
if (pread(fd, sb, sizeof(*sb), SB_SECTOR << 9) != sizeof(*sb))
die("error reading superblock");
if (memcmp(&sb->magic, &BCACHE_MAGIC, sizeof(sb->magic)))
die("not a bcache superblock");
}

35
libbcache.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef _LIBBCACHE_H
#define _LIBBCACHE_H
#include "util.h"
#include "stdbool.h"
struct dev_opts {
int fd;
const char *dev;
u64 size; /* 512 byte sectors */
unsigned bucket_size;
unsigned tier;
unsigned replacement_policy;
bool discard;
u64 first_bucket;
u64 nbuckets;
};
void bcache_format(struct dev_opts *devs, size_t nr_devs,
unsigned block_size,
unsigned btree_node_size,
unsigned meta_csum_type,
unsigned data_csum_type,
unsigned compression_type,
const char *passphrase,
unsigned meta_replicas,
unsigned data_replicas,
unsigned on_error_action,
char *label,
uuid_le uuid);
void bcache_super_read(const char *, struct cache_sb *);
#endif /* _LIBBCACHE_H */

6
util.c
View File

@ -487,3 +487,9 @@ struct bcache_handle bcache_fs_open(const char *path)
return ret;
}
void memzero_explicit(void *buf, size_t len)
{
void *(* volatile memset_s)(void *s, int c, size_t n) = memset;
memset_s(buf, 0, len);
}

4
util.h
View File

@ -74,7 +74,7 @@ u64 bch_checksum(unsigned, const void *, size_t);
#define __csum_set(i, u64s, type) \
({ \
const void *start = ((const void *) (i)) + sizeof(u64); \
const void *start = ((const void *) (i)) + sizeof(i->csum); \
const void *end = __bkey_idx(i, u64s); \
\
bch_checksum(type, start, end - start); \
@ -93,4 +93,6 @@ struct bcache_handle {
struct bcache_handle bcache_fs_open(const char *);
void memzero_explicit(void *, size_t);
#endif /* _UTIL_H */