#include <linux/crc32.h>
#include "messages.h"
#include "fs.h"
#include "accessors.h"
#include "volumes.h"
static const struct btrfs_csums {
u16 size;
const char name[10];
} btrfs_csums[] = {
[BTRFS_CSUM_TYPE_CRC32] = { .size = 4, .name = "crc32c" },
[BTRFS_CSUM_TYPE_XXHASH] = { .size = 8, .name = "xxhash64" },
[BTRFS_CSUM_TYPE_SHA256] = { .size = 32, .name = "sha256" },
[BTRFS_CSUM_TYPE_BLAKE2] = { .size = 32, .name = "blake2b" },
};
u16 btrfs_csum_type_size(u16 type)
{
return btrfs_csums[type].size;
}
int btrfs_super_csum_size(const struct btrfs_super_block *s)
{
u16 t = btrfs_super_csum_type(s);
return btrfs_csum_type_size(t);
}
const char *btrfs_super_csum_name(u16 csum_type)
{
return btrfs_csums[csum_type].name;
}
size_t __attribute_const__ btrfs_get_num_csums(void)
{
return ARRAY_SIZE(btrfs_csums);
}
void btrfs_csum(u16 csum_type, const u8 *data, size_t len, u8 *out)
{
switch (csum_type) {
case BTRFS_CSUM_TYPE_CRC32:
put_unaligned_le32(~crc32c(~0, data, len), out);
break;
case BTRFS_CSUM_TYPE_XXHASH:
put_unaligned_le64(xxh64(data, len, 0), out);
break;
case BTRFS_CSUM_TYPE_SHA256:
sha256(data, len, out);
break;
case BTRFS_CSUM_TYPE_BLAKE2:
blake2b(NULL, 0, data, len, out, 32);
break;
default:
BUG();
}
}
void btrfs_csum_init(struct btrfs_csum_ctx *ctx, u16 csum_type)
{
ctx->csum_type = csum_type;
switch (ctx->csum_type) {
case BTRFS_CSUM_TYPE_CRC32:
ctx->crc32 = ~0;
break;
case BTRFS_CSUM_TYPE_XXHASH:
xxh64_reset(&ctx->xxh64, 0);
break;
case BTRFS_CSUM_TYPE_SHA256:
sha256_init(&ctx->sha256);
break;
case BTRFS_CSUM_TYPE_BLAKE2:
blake2b_init(&ctx->blake2b, 32);
break;
default:
BUG();
}
}
void btrfs_csum_update(struct btrfs_csum_ctx *ctx, const u8 *data, size_t len)
{
switch (ctx->csum_type) {
case BTRFS_CSUM_TYPE_CRC32:
ctx->crc32 = crc32c(ctx->crc32, data, len);
break;
case BTRFS_CSUM_TYPE_XXHASH:
xxh64_update(&ctx->xxh64, data, len);
break;
case BTRFS_CSUM_TYPE_SHA256:
sha256_update(&ctx->sha256, data, len);
break;
case BTRFS_CSUM_TYPE_BLAKE2:
blake2b_update(&ctx->blake2b, data, len);
break;
default:
BUG();
}
}
void btrfs_csum_final(struct btrfs_csum_ctx *ctx, u8 *out)
{
switch (ctx->csum_type) {
case BTRFS_CSUM_TYPE_CRC32:
put_unaligned_le32(~ctx->crc32, out);
break;
case BTRFS_CSUM_TYPE_XXHASH:
put_unaligned_le64(xxh64_digest(&ctx->xxh64), out);
break;
case BTRFS_CSUM_TYPE_SHA256:
sha256_final(&ctx->sha256, out);
break;
case BTRFS_CSUM_TYPE_BLAKE2:
blake2b_final(&ctx->blake2b, out);
break;
default:
BUG();
}
}
bool __attribute_const__ btrfs_supported_blocksize(u32 blocksize)
{
ASSERT(is_power_of_2(blocksize) && blocksize >= BTRFS_MIN_BLOCKSIZE &&
blocksize <= BTRFS_MAX_BLOCKSIZE);
if (blocksize == PAGE_SIZE || blocksize == SZ_4K || blocksize == BTRFS_MIN_BLOCKSIZE)
return true;
#ifdef CONFIG_BTRFS_EXPERIMENTAL
if (IS_ENABLED(CONFIG_HIGHMEM) && blocksize > PAGE_SIZE)
return false;
return true;
#endif
return false;
}
bool btrfs_exclop_start(struct btrfs_fs_info *fs_info,
enum btrfs_exclusive_operation type)
{
bool ret = false;
spin_lock(&fs_info->super_lock);
if (fs_info->exclusive_operation == BTRFS_EXCLOP_NONE) {
fs_info->exclusive_operation = type;
ret = true;
}
spin_unlock(&fs_info->super_lock);
return ret;
}
bool btrfs_exclop_start_try_lock(struct btrfs_fs_info *fs_info,
enum btrfs_exclusive_operation type)
{
spin_lock(&fs_info->super_lock);
if (fs_info->exclusive_operation == type ||
(fs_info->exclusive_operation == BTRFS_EXCLOP_BALANCE_PAUSED &&
type == BTRFS_EXCLOP_DEV_ADD))
return true;
spin_unlock(&fs_info->super_lock);
return false;
}
void btrfs_exclop_start_unlock(struct btrfs_fs_info *fs_info)
{
spin_unlock(&fs_info->super_lock);
}
void btrfs_exclop_finish(struct btrfs_fs_info *fs_info)
{
spin_lock(&fs_info->super_lock);
WRITE_ONCE(fs_info->exclusive_operation, BTRFS_EXCLOP_NONE);
spin_unlock(&fs_info->super_lock);
sysfs_notify(&fs_info->fs_devices->fsid_kobj, NULL, "exclusive_operation");
}
void btrfs_exclop_balance(struct btrfs_fs_info *fs_info,
enum btrfs_exclusive_operation op)
{
switch (op) {
case BTRFS_EXCLOP_BALANCE_PAUSED:
spin_lock(&fs_info->super_lock);
ASSERT(fs_info->exclusive_operation == BTRFS_EXCLOP_BALANCE ||
fs_info->exclusive_operation == BTRFS_EXCLOP_DEV_ADD ||
fs_info->exclusive_operation == BTRFS_EXCLOP_NONE ||
fs_info->exclusive_operation == BTRFS_EXCLOP_BALANCE_PAUSED);
fs_info->exclusive_operation = BTRFS_EXCLOP_BALANCE_PAUSED;
spin_unlock(&fs_info->super_lock);
break;
case BTRFS_EXCLOP_BALANCE:
spin_lock(&fs_info->super_lock);
ASSERT(fs_info->exclusive_operation == BTRFS_EXCLOP_BALANCE_PAUSED);
fs_info->exclusive_operation = BTRFS_EXCLOP_BALANCE;
spin_unlock(&fs_info->super_lock);
break;
default:
btrfs_warn(fs_info,
"invalid exclop balance operation %d requested", op);
}
}
void __btrfs_set_fs_incompat(struct btrfs_fs_info *fs_info, u64 flag,
const char *name)
{
struct btrfs_super_block *disk_super;
u64 features;
disk_super = fs_info->super_copy;
features = btrfs_super_incompat_flags(disk_super);
if (!(features & flag)) {
spin_lock(&fs_info->super_lock);
features = btrfs_super_incompat_flags(disk_super);
if (!(features & flag)) {
features |= flag;
btrfs_set_super_incompat_flags(disk_super, features);
btrfs_info(fs_info,
"setting incompat feature flag for %s (0x%llx)",
name, flag);
}
spin_unlock(&fs_info->super_lock);
set_bit(BTRFS_FS_FEATURE_CHANGED, &fs_info->flags);
}
}
void __btrfs_clear_fs_incompat(struct btrfs_fs_info *fs_info, u64 flag,
const char *name)
{
struct btrfs_super_block *disk_super;
u64 features;
disk_super = fs_info->super_copy;
features = btrfs_super_incompat_flags(disk_super);
if (features & flag) {
spin_lock(&fs_info->super_lock);
features = btrfs_super_incompat_flags(disk_super);
if (features & flag) {
features &= ~flag;
btrfs_set_super_incompat_flags(disk_super, features);
btrfs_info(fs_info,
"clearing incompat feature flag for %s (0x%llx)",
name, flag);
}
spin_unlock(&fs_info->super_lock);
set_bit(BTRFS_FS_FEATURE_CHANGED, &fs_info->flags);
}
}
void __btrfs_set_fs_compat_ro(struct btrfs_fs_info *fs_info, u64 flag,
const char *name)
{
struct btrfs_super_block *disk_super;
u64 features;
disk_super = fs_info->super_copy;
features = btrfs_super_compat_ro_flags(disk_super);
if (!(features & flag)) {
spin_lock(&fs_info->super_lock);
features = btrfs_super_compat_ro_flags(disk_super);
if (!(features & flag)) {
features |= flag;
btrfs_set_super_compat_ro_flags(disk_super, features);
btrfs_info(fs_info,
"setting compat-ro feature flag for %s (0x%llx)",
name, flag);
}
spin_unlock(&fs_info->super_lock);
set_bit(BTRFS_FS_FEATURE_CHANGED, &fs_info->flags);
}
}
void __btrfs_clear_fs_compat_ro(struct btrfs_fs_info *fs_info, u64 flag,
const char *name)
{
struct btrfs_super_block *disk_super;
u64 features;
disk_super = fs_info->super_copy;
features = btrfs_super_compat_ro_flags(disk_super);
if (features & flag) {
spin_lock(&fs_info->super_lock);
features = btrfs_super_compat_ro_flags(disk_super);
if (features & flag) {
features &= ~flag;
btrfs_set_super_compat_ro_flags(disk_super, features);
btrfs_info(fs_info,
"clearing compat-ro feature flag for %s (0x%llx)",
name, flag);
}
spin_unlock(&fs_info->super_lock);
set_bit(BTRFS_FS_FEATURE_CHANGED, &fs_info->flags);
}
}