mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-15 01:17:39 +00:00
Merge branch '2020-09-14-btrfs-rewrite' into next
- Bring in the update to btrfs support that rewrites it based on btrfs-progs.
This commit is contained in:
commit
2a9f9d633d
30 changed files with 8598 additions and 2564 deletions
|
@ -599,6 +599,8 @@ F: tools/binman/
|
|||
|
||||
BTRFS
|
||||
M: Marek Behun <marek.behun@nic.cz>
|
||||
R: Qu Wenruo <wqu@suse.com>
|
||||
L: linux-btrfs@vger.kernel.org
|
||||
S: Maintained
|
||||
F: cmd/btrfs.c
|
||||
F: fs/btrfs/
|
||||
|
|
|
@ -2,5 +2,6 @@
|
|||
#
|
||||
# 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
|
||||
|
||||
obj-y := btrfs.o chunk-map.o compression.o ctree.o dev.o dir-item.o \
|
||||
extent-io.o hash.o inode.o root.o subvolume.o super.o
|
||||
obj-y := btrfs.o compression.o ctree.o dev.o dir-item.o \
|
||||
extent-io.o inode.o subvolume.o crypto/hash.o disk-io.o \
|
||||
common/rbtree-utils.o extent-cache.o volumes.o root-tree.o
|
||||
|
|
319
fs/btrfs/btrfs.c
319
fs/btrfs/btrfs.c
|
@ -5,219 +5,280 @@
|
|||
* 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
|
||||
*/
|
||||
|
||||
#include "btrfs.h"
|
||||
#include <config.h>
|
||||
#include <malloc.h>
|
||||
#include <uuid.h>
|
||||
#include <linux/time.h>
|
||||
#include "btrfs.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "disk-io.h"
|
||||
|
||||
struct btrfs_info btrfs_info;
|
||||
struct btrfs_fs_info *current_fs_info;
|
||||
|
||||
static int readdir_callback(const struct btrfs_root *root,
|
||||
struct btrfs_dir_item *item)
|
||||
static int show_dir(struct btrfs_root *root, struct extent_buffer *eb,
|
||||
struct btrfs_dir_item *di)
|
||||
{
|
||||
static const char typestr[BTRFS_FT_MAX][4] = {
|
||||
[BTRFS_FT_UNKNOWN] = " ? ",
|
||||
[BTRFS_FT_REG_FILE] = " ",
|
||||
[BTRFS_FT_DIR] = "DIR",
|
||||
[BTRFS_FT_CHRDEV] = "CHR",
|
||||
[BTRFS_FT_BLKDEV] = "BLK",
|
||||
[BTRFS_FT_FIFO] = "FIF",
|
||||
[BTRFS_FT_SOCK] = "SCK",
|
||||
[BTRFS_FT_SYMLINK] = "SYM",
|
||||
[BTRFS_FT_XATTR] = " ? ",
|
||||
struct btrfs_fs_info *fs_info = root->fs_info;
|
||||
struct btrfs_inode_item ii;
|
||||
struct btrfs_key key;
|
||||
static const char* dir_item_str[] = {
|
||||
[BTRFS_FT_REG_FILE] = "FILE",
|
||||
[BTRFS_FT_DIR] = "DIR",
|
||||
[BTRFS_FT_CHRDEV] = "CHRDEV",
|
||||
[BTRFS_FT_BLKDEV] = "BLKDEV",
|
||||
[BTRFS_FT_FIFO] = "FIFO",
|
||||
[BTRFS_FT_SOCK] = "SOCK",
|
||||
[BTRFS_FT_SYMLINK] = "SYMLINK",
|
||||
[BTRFS_FT_XATTR] = "XATTR"
|
||||
};
|
||||
struct btrfs_inode_item inode;
|
||||
const char *name = (const char *) (item + 1);
|
||||
char filetime[32], *target = NULL;
|
||||
u8 type = btrfs_dir_type(eb, di);
|
||||
char namebuf[BTRFS_NAME_LEN];
|
||||
char *target = NULL;
|
||||
char filetime[32];
|
||||
time_t mtime;
|
||||
int ret;
|
||||
|
||||
if (btrfs_lookup_inode(root, &item->location, &inode, NULL)) {
|
||||
printf("%s: Cannot find inode item for directory entry %.*s!\n",
|
||||
__func__, item->name_len, name);
|
||||
return 0;
|
||||
btrfs_dir_item_key_to_cpu(eb, di, &key);
|
||||
|
||||
if (key.type == BTRFS_ROOT_ITEM_KEY) {
|
||||
struct btrfs_root *subvol;
|
||||
|
||||
/* It's a subvolume, get its mtime from root item */
|
||||
subvol = btrfs_read_fs_root(fs_info, &key);
|
||||
if (IS_ERR(subvol)) {
|
||||
ret = PTR_ERR(subvol);
|
||||
error("Can't find root %llu", key.objectid);
|
||||
return ret;
|
||||
}
|
||||
mtime = btrfs_stack_timespec_sec(&subvol->root_item.otime);
|
||||
} else {
|
||||
struct btrfs_path path;
|
||||
|
||||
/* It's regular inode, get its mtime from inode item */
|
||||
btrfs_init_path(&path);
|
||||
ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
|
||||
if (ret > 0)
|
||||
ret = -ENOENT;
|
||||
if (ret < 0) {
|
||||
error("Can't find inode %llu", key.objectid);
|
||||
btrfs_release_path(&path);
|
||||
return ret;
|
||||
}
|
||||
read_extent_buffer(path.nodes[0], &ii,
|
||||
btrfs_item_ptr_offset(path.nodes[0], path.slots[0]),
|
||||
sizeof(ii));
|
||||
btrfs_release_path(&path);
|
||||
mtime = btrfs_stack_timespec_sec(&ii.mtime);
|
||||
}
|
||||
|
||||
mtime = inode.mtime.sec;
|
||||
ctime_r(&mtime, filetime);
|
||||
|
||||
if (item->type == BTRFS_FT_SYMLINK) {
|
||||
target = malloc(min(inode.size + 1,
|
||||
(u64) btrfs_info.sb.sectorsize));
|
||||
|
||||
if (target && btrfs_readlink(root, item->location.objectid,
|
||||
target)) {
|
||||
free(target);
|
||||
target = NULL;
|
||||
if (type == BTRFS_FT_SYMLINK) {
|
||||
target = malloc(fs_info->sectorsize);
|
||||
if (!target) {
|
||||
error("Can't alloc memory for symlink %llu",
|
||||
key.objectid);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!target)
|
||||
printf("%s: Cannot read symlink target!\n", __func__);
|
||||
ret = btrfs_readlink(root, key.objectid, target);
|
||||
if (ret < 0) {
|
||||
error("Failed to read symlink %llu", key.objectid);
|
||||
goto out;
|
||||
}
|
||||
target[ret] = '\0';
|
||||
}
|
||||
|
||||
printf("<%s> ", typestr[item->type]);
|
||||
if (item->type == BTRFS_FT_CHRDEV || item->type == BTRFS_FT_BLKDEV)
|
||||
printf("%4u,%5u ", (unsigned int) (inode.rdev >> 20),
|
||||
(unsigned int) (inode.rdev & 0xfffff));
|
||||
if (type < ARRAY_SIZE(dir_item_str) && dir_item_str[type])
|
||||
printf("<%s> ", dir_item_str[type]);
|
||||
else
|
||||
printf("%10llu ", inode.size);
|
||||
|
||||
printf("%24.24s %.*s", filetime, item->name_len, name);
|
||||
|
||||
if (item->type == BTRFS_FT_SYMLINK) {
|
||||
printf(" -> %s", target ? target : "?");
|
||||
if (target)
|
||||
free(target);
|
||||
printf("DIR_ITEM.%u", type);
|
||||
if (type == BTRFS_FT_CHRDEV || type == BTRFS_FT_BLKDEV) {
|
||||
ASSERT(key.type == BTRFS_INODE_ITEM_KEY);
|
||||
printf("%4llu,%5llu ", btrfs_stack_inode_rdev(&ii) >> 20,
|
||||
btrfs_stack_inode_rdev(&ii) & 0xfffff);
|
||||
} else {
|
||||
if (key.type == BTRFS_INODE_ITEM_KEY)
|
||||
printf("%10llu ", btrfs_stack_inode_size(&ii));
|
||||
else
|
||||
printf("%10llu ", 0ULL);
|
||||
}
|
||||
|
||||
read_extent_buffer(eb, namebuf, (unsigned long)(di + 1),
|
||||
btrfs_dir_name_len(eb, di));
|
||||
printf("%24.24s %.*s", filetime, btrfs_dir_name_len(eb, di), namebuf);
|
||||
if (type == BTRFS_FT_SYMLINK)
|
||||
printf(" -> %s", target ? target : "?");
|
||||
printf("\n");
|
||||
|
||||
return 0;
|
||||
out:
|
||||
free(target);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btrfs_probe(struct blk_desc *fs_dev_desc,
|
||||
struct disk_partition *fs_partition)
|
||||
{
|
||||
btrfs_blk_desc = fs_dev_desc;
|
||||
btrfs_part_info = fs_partition;
|
||||
|
||||
memset(&btrfs_info, 0, sizeof(btrfs_info));
|
||||
struct btrfs_fs_info *fs_info;
|
||||
int ret = -1;
|
||||
|
||||
btrfs_hash_init();
|
||||
if (btrfs_read_superblock())
|
||||
return -1;
|
||||
|
||||
if (btrfs_chunk_map_init()) {
|
||||
printf("%s: failed to init chunk map\n", __func__);
|
||||
return -1;
|
||||
fs_info = open_ctree_fs_info(fs_dev_desc, fs_partition);
|
||||
if (fs_info) {
|
||||
current_fs_info = fs_info;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
btrfs_info.tree_root.objectid = 0;
|
||||
btrfs_info.tree_root.bytenr = btrfs_info.sb.root;
|
||||
btrfs_info.chunk_root.objectid = 0;
|
||||
btrfs_info.chunk_root.bytenr = btrfs_info.sb.chunk_root;
|
||||
|
||||
if (btrfs_read_chunk_tree()) {
|
||||
printf("%s: failed to read chunk tree\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (btrfs_find_root(btrfs_get_default_subvol_objectid(),
|
||||
&btrfs_info.fs_root, NULL)) {
|
||||
printf("%s: failed to find default subvolume\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btrfs_ls(const char *path)
|
||||
{
|
||||
struct btrfs_root root = btrfs_info.fs_root;
|
||||
u64 inr;
|
||||
struct btrfs_fs_info *fs_info = current_fs_info;
|
||||
struct btrfs_root *root = fs_info->fs_root;
|
||||
u64 ino = BTRFS_FIRST_FREE_OBJECTID;
|
||||
u8 type;
|
||||
int ret;
|
||||
|
||||
inr = btrfs_lookup_path(&root, root.root_dirid, path, &type, NULL, 40);
|
||||
|
||||
if (inr == -1ULL) {
|
||||
ASSERT(fs_info);
|
||||
ret = btrfs_lookup_path(fs_info->fs_root, BTRFS_FIRST_FREE_OBJECTID,
|
||||
path, &root, &ino, &type, 40);
|
||||
if (ret < 0) {
|
||||
printf("Cannot lookup path %s\n", path);
|
||||
return -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (type != BTRFS_FT_DIR) {
|
||||
printf("Not a directory: %s\n", path);
|
||||
return -1;
|
||||
error("Not a directory: %s", path);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (btrfs_readdir(&root, inr, readdir_callback)) {
|
||||
printf("An error occured while listing directory %s\n", path);
|
||||
return -1;
|
||||
ret = btrfs_iter_dir(root, ino, show_dir);
|
||||
if (ret < 0) {
|
||||
error("An error occured while listing directory %s", path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int btrfs_exists(const char *file)
|
||||
{
|
||||
struct btrfs_root root = btrfs_info.fs_root;
|
||||
u64 inr;
|
||||
struct btrfs_fs_info *fs_info = current_fs_info;
|
||||
struct btrfs_root *root;
|
||||
u64 ino;
|
||||
u8 type;
|
||||
int ret;
|
||||
|
||||
inr = btrfs_lookup_path(&root, root.root_dirid, file, &type, NULL, 40);
|
||||
ASSERT(fs_info);
|
||||
|
||||
return (inr != -1ULL && type == BTRFS_FT_REG_FILE);
|
||||
ret = btrfs_lookup_path(fs_info->fs_root, BTRFS_FIRST_FREE_OBJECTID,
|
||||
file, &root, &ino, &type, 40);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
if (type == BTRFS_FT_REG_FILE)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int btrfs_size(const char *file, loff_t *size)
|
||||
{
|
||||
struct btrfs_root root = btrfs_info.fs_root;
|
||||
struct btrfs_inode_item inode;
|
||||
u64 inr;
|
||||
struct btrfs_fs_info *fs_info = current_fs_info;
|
||||
struct btrfs_inode_item *ii;
|
||||
struct btrfs_root *root;
|
||||
struct btrfs_path path;
|
||||
struct btrfs_key key;
|
||||
u64 ino;
|
||||
u8 type;
|
||||
int ret;
|
||||
|
||||
inr = btrfs_lookup_path(&root, root.root_dirid, file, &type, &inode,
|
||||
40);
|
||||
|
||||
if (inr == -1ULL) {
|
||||
ret = btrfs_lookup_path(fs_info->fs_root, BTRFS_FIRST_FREE_OBJECTID,
|
||||
file, &root, &ino, &type, 40);
|
||||
if (ret < 0) {
|
||||
printf("Cannot lookup file %s\n", file);
|
||||
return -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (type != BTRFS_FT_REG_FILE) {
|
||||
printf("Not a regular file: %s\n", file);
|
||||
return -1;
|
||||
return -ENOENT;
|
||||
}
|
||||
btrfs_init_path(&path);
|
||||
key.objectid = ino;
|
||||
key.type = BTRFS_INODE_ITEM_KEY;
|
||||
key.offset = 0;
|
||||
|
||||
*size = inode.size;
|
||||
return 0;
|
||||
ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
|
||||
if (ret < 0) {
|
||||
printf("Cannot lookup ino %llu\n", ino);
|
||||
return ret;
|
||||
}
|
||||
if (ret > 0) {
|
||||
printf("Ino %llu does not exist\n", ino);
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
ii = btrfs_item_ptr(path.nodes[0], path.slots[0],
|
||||
struct btrfs_inode_item);
|
||||
*size = btrfs_inode_size(path.nodes[0], ii);
|
||||
out:
|
||||
btrfs_release_path(&path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btrfs_read(const char *file, void *buf, loff_t offset, loff_t len,
|
||||
loff_t *actread)
|
||||
{
|
||||
struct btrfs_root root = btrfs_info.fs_root;
|
||||
struct btrfs_inode_item inode;
|
||||
u64 inr, rd;
|
||||
struct btrfs_fs_info *fs_info = current_fs_info;
|
||||
struct btrfs_root *root;
|
||||
loff_t real_size = 0;
|
||||
u64 ino;
|
||||
u8 type;
|
||||
int ret;
|
||||
|
||||
inr = btrfs_lookup_path(&root, root.root_dirid, file, &type, &inode,
|
||||
40);
|
||||
|
||||
if (inr == -1ULL) {
|
||||
printf("Cannot lookup file %s\n", file);
|
||||
return -1;
|
||||
ASSERT(fs_info);
|
||||
ret = btrfs_lookup_path(fs_info->fs_root, BTRFS_FIRST_FREE_OBJECTID,
|
||||
file, &root, &ino, &type, 40);
|
||||
if (ret < 0) {
|
||||
error("Cannot lookup file %s", file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (type != BTRFS_FT_REG_FILE) {
|
||||
printf("Not a regular file: %s\n", file);
|
||||
return -1;
|
||||
error("Not a regular file: %s", file);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!len)
|
||||
len = inode.size;
|
||||
|
||||
if (len > inode.size - offset)
|
||||
len = inode.size - offset;
|
||||
|
||||
rd = btrfs_file_read(&root, inr, offset, len, buf);
|
||||
if (rd == -1ULL) {
|
||||
printf("An error occured while reading file %s\n", file);
|
||||
return -1;
|
||||
if (!len) {
|
||||
ret = btrfs_size(file, &real_size);
|
||||
if (ret < 0) {
|
||||
error("Failed to get inode size: %s", file);
|
||||
return ret;
|
||||
}
|
||||
len = real_size;
|
||||
}
|
||||
|
||||
*actread = rd;
|
||||
if (len > real_size - offset)
|
||||
len = real_size - offset;
|
||||
|
||||
ret = btrfs_file_read(root, ino, offset, len, buf);
|
||||
if (ret < 0) {
|
||||
error("An error occured while reading file %s", file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*actread = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void btrfs_close(void)
|
||||
{
|
||||
btrfs_chunk_map_exit();
|
||||
if (current_fs_info) {
|
||||
close_ctree_fs_info(current_fs_info);
|
||||
current_fs_info = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int btrfs_uuid(char *uuid_str)
|
||||
{
|
||||
#ifdef CONFIG_LIB_UUID
|
||||
uuid_bin_to_str(btrfs_info.sb.fsid, uuid_str, UUID_STR_FORMAT_STD);
|
||||
if (current_fs_info)
|
||||
uuid_bin_to_str(current_fs_info->super_copy->fsid, uuid_str,
|
||||
UUID_STR_FORMAT_STD);
|
||||
return 0;
|
||||
#endif
|
||||
return -ENOSYS;
|
||||
|
|
|
@ -11,77 +11,18 @@
|
|||
#include <linux/rbtree.h>
|
||||
#include "conv-funcs.h"
|
||||
|
||||
struct btrfs_info {
|
||||
struct btrfs_super_block sb;
|
||||
|
||||
struct btrfs_root tree_root;
|
||||
struct btrfs_root fs_root;
|
||||
struct btrfs_root chunk_root;
|
||||
|
||||
struct rb_root chunks_root;
|
||||
};
|
||||
|
||||
extern struct btrfs_info btrfs_info;
|
||||
|
||||
/* hash.c */
|
||||
void btrfs_hash_init(void);
|
||||
u32 btrfs_crc32c(u32, const void *, size_t);
|
||||
u32 btrfs_csum_data(char *, u32, size_t);
|
||||
void btrfs_csum_final(u32, void *);
|
||||
|
||||
static inline u64 btrfs_name_hash(const char *name, int len)
|
||||
{
|
||||
return btrfs_crc32c((u32) ~1, name, len);
|
||||
}
|
||||
|
||||
/* dev.c */
|
||||
extern struct blk_desc *btrfs_blk_desc;
|
||||
extern struct disk_partition *btrfs_part_info;
|
||||
|
||||
int btrfs_devread(u64, int, void *);
|
||||
|
||||
/* chunk-map.c */
|
||||
u64 btrfs_map_logical_to_physical(u64);
|
||||
int btrfs_chunk_map_init(void);
|
||||
void btrfs_chunk_map_exit(void);
|
||||
int btrfs_read_chunk_tree(void);
|
||||
extern struct btrfs_fs_info *current_fs_info;
|
||||
|
||||
/* compression.c */
|
||||
u32 btrfs_decompress(u8 type, const char *, u32, char *, u32);
|
||||
|
||||
/* super.c */
|
||||
int btrfs_read_superblock(void);
|
||||
|
||||
/* dir-item.c */
|
||||
typedef int (*btrfs_readdir_callback_t)(const struct btrfs_root *,
|
||||
struct btrfs_dir_item *);
|
||||
|
||||
int btrfs_lookup_dir_item(const struct btrfs_root *, u64, const char *, int,
|
||||
struct btrfs_dir_item *);
|
||||
int btrfs_readdir(const struct btrfs_root *, u64, btrfs_readdir_callback_t);
|
||||
|
||||
/* root.c */
|
||||
int btrfs_find_root(u64, struct btrfs_root *, struct btrfs_root_item *);
|
||||
u64 btrfs_lookup_root_ref(u64, struct btrfs_root_ref *, char *);
|
||||
|
||||
/* inode.c */
|
||||
u64 btrfs_lookup_inode_ref(struct btrfs_root *, u64, struct btrfs_inode_ref *,
|
||||
char *);
|
||||
int btrfs_lookup_inode(const struct btrfs_root *, struct btrfs_key *,
|
||||
struct btrfs_inode_item *, struct btrfs_root *);
|
||||
int btrfs_readlink(const struct btrfs_root *, u64, char *);
|
||||
u64 btrfs_lookup_path(struct btrfs_root *, u64, const char *, u8 *,
|
||||
struct btrfs_inode_item *, int);
|
||||
u64 btrfs_file_read(const struct btrfs_root *, u64, u64, u64, char *);
|
||||
int btrfs_readlink(struct btrfs_root *root, u64 ino, char *target);
|
||||
int btrfs_file_read(struct btrfs_root *root, u64 ino, u64 file_offset, u64 len,
|
||||
char *dest);
|
||||
|
||||
/* subvolume.c */
|
||||
u64 btrfs_get_default_subvol_objectid(void);
|
||||
|
||||
/* extent-io.c */
|
||||
u64 btrfs_read_extent_inline(struct btrfs_path *,
|
||||
struct btrfs_file_extent_item *, u64, u64,
|
||||
char *);
|
||||
u64 btrfs_read_extent_reg(struct btrfs_path *, struct btrfs_file_extent_item *,
|
||||
u64, u64, char *);
|
||||
|
||||
#endif /* !__BTRFS_BTRFS_H__ */
|
||||
|
|
|
@ -1,766 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* From linux/include/uapi/linux/btrfs_tree.h
|
||||
*/
|
||||
|
||||
#ifndef __BTRFS_BTRFS_TREE_H__
|
||||
#define __BTRFS_BTRFS_TREE_H__
|
||||
|
||||
#include <common.h>
|
||||
|
||||
#define BTRFS_VOL_NAME_MAX 255
|
||||
#define BTRFS_NAME_MAX 255
|
||||
#define BTRFS_LABEL_SIZE 256
|
||||
#define BTRFS_FSID_SIZE 16
|
||||
#define BTRFS_UUID_SIZE 16
|
||||
|
||||
/*
|
||||
* This header contains the structure definitions and constants used
|
||||
* by file system objects that can be retrieved using
|
||||
* the BTRFS_IOC_SEARCH_TREE ioctl. That means basically anything that
|
||||
* is needed to describe a leaf node's key or item contents.
|
||||
*/
|
||||
|
||||
/* holds pointers to all of the tree roots */
|
||||
#define BTRFS_ROOT_TREE_OBJECTID 1ULL
|
||||
|
||||
/* stores information about which extents are in use, and reference counts */
|
||||
#define BTRFS_EXTENT_TREE_OBJECTID 2ULL
|
||||
|
||||
/*
|
||||
* chunk tree stores translations from logical -> physical block numbering
|
||||
* the super block points to the chunk tree
|
||||
*/
|
||||
#define BTRFS_CHUNK_TREE_OBJECTID 3ULL
|
||||
|
||||
/*
|
||||
* stores information about which areas of a given device are in use.
|
||||
* one per device. The tree of tree roots points to the device tree
|
||||
*/
|
||||
#define BTRFS_DEV_TREE_OBJECTID 4ULL
|
||||
|
||||
/* one per subvolume, storing files and directories */
|
||||
#define BTRFS_FS_TREE_OBJECTID 5ULL
|
||||
|
||||
/* directory objectid inside the root tree */
|
||||
#define BTRFS_ROOT_TREE_DIR_OBJECTID 6ULL
|
||||
|
||||
/* holds checksums of all the data extents */
|
||||
#define BTRFS_CSUM_TREE_OBJECTID 7ULL
|
||||
|
||||
/* holds quota configuration and tracking */
|
||||
#define BTRFS_QUOTA_TREE_OBJECTID 8ULL
|
||||
|
||||
/* for storing items that use the BTRFS_UUID_KEY* types */
|
||||
#define BTRFS_UUID_TREE_OBJECTID 9ULL
|
||||
|
||||
/* tracks free space in block groups. */
|
||||
#define BTRFS_FREE_SPACE_TREE_OBJECTID 10ULL
|
||||
|
||||
/* device stats in the device tree */
|
||||
#define BTRFS_DEV_STATS_OBJECTID 0ULL
|
||||
|
||||
/* for storing balance parameters in the root tree */
|
||||
#define BTRFS_BALANCE_OBJECTID -4ULL
|
||||
|
||||
/* orhpan objectid for tracking unlinked/truncated files */
|
||||
#define BTRFS_ORPHAN_OBJECTID -5ULL
|
||||
|
||||
/* does write ahead logging to speed up fsyncs */
|
||||
#define BTRFS_TREE_LOG_OBJECTID -6ULL
|
||||
#define BTRFS_TREE_LOG_FIXUP_OBJECTID -7ULL
|
||||
|
||||
/* for space balancing */
|
||||
#define BTRFS_TREE_RELOC_OBJECTID -8ULL
|
||||
#define BTRFS_DATA_RELOC_TREE_OBJECTID -9ULL
|
||||
|
||||
/*
|
||||
* extent checksums all have this objectid
|
||||
* this allows them to share the logging tree
|
||||
* for fsyncs
|
||||
*/
|
||||
#define BTRFS_EXTENT_CSUM_OBJECTID -10ULL
|
||||
|
||||
/* For storing free space cache */
|
||||
#define BTRFS_FREE_SPACE_OBJECTID -11ULL
|
||||
|
||||
/*
|
||||
* The inode number assigned to the special inode for storing
|
||||
* free ino cache
|
||||
*/
|
||||
#define BTRFS_FREE_INO_OBJECTID -12ULL
|
||||
|
||||
/* dummy objectid represents multiple objectids */
|
||||
#define BTRFS_MULTIPLE_OBJECTIDS -255ULL
|
||||
|
||||
/*
|
||||
* All files have objectids in this range.
|
||||
*/
|
||||
#define BTRFS_FIRST_FREE_OBJECTID 256ULL
|
||||
#define BTRFS_LAST_FREE_OBJECTID -256ULL
|
||||
#define BTRFS_FIRST_CHUNK_TREE_OBJECTID 256ULL
|
||||
|
||||
|
||||
/*
|
||||
* the device items go into the chunk tree. The key is in the form
|
||||
* [ 1 BTRFS_DEV_ITEM_KEY device_id ]
|
||||
*/
|
||||
#define BTRFS_DEV_ITEMS_OBJECTID 1ULL
|
||||
|
||||
#define BTRFS_BTREE_INODE_OBJECTID 1
|
||||
|
||||
#define BTRFS_EMPTY_SUBVOL_DIR_OBJECTID 2
|
||||
|
||||
#define BTRFS_DEV_REPLACE_DEVID 0ULL
|
||||
|
||||
/*
|
||||
* inode items have the data typically returned from stat and store other
|
||||
* info about object characteristics. There is one for every file and dir in
|
||||
* the FS
|
||||
*/
|
||||
#define BTRFS_INODE_ITEM_KEY 1
|
||||
#define BTRFS_INODE_REF_KEY 12
|
||||
#define BTRFS_INODE_EXTREF_KEY 13
|
||||
#define BTRFS_XATTR_ITEM_KEY 24
|
||||
#define BTRFS_ORPHAN_ITEM_KEY 48
|
||||
/* reserve 2-15 close to the inode for later flexibility */
|
||||
|
||||
/*
|
||||
* dir items are the name -> inode pointers in a directory. There is one
|
||||
* for every name in a directory.
|
||||
*/
|
||||
#define BTRFS_DIR_LOG_ITEM_KEY 60
|
||||
#define BTRFS_DIR_LOG_INDEX_KEY 72
|
||||
#define BTRFS_DIR_ITEM_KEY 84
|
||||
#define BTRFS_DIR_INDEX_KEY 96
|
||||
/*
|
||||
* extent data is for file data
|
||||
*/
|
||||
#define BTRFS_EXTENT_DATA_KEY 108
|
||||
|
||||
/*
|
||||
* extent csums are stored in a separate tree and hold csums for
|
||||
* an entire extent on disk.
|
||||
*/
|
||||
#define BTRFS_EXTENT_CSUM_KEY 128
|
||||
|
||||
/*
|
||||
* root items point to tree roots. They are typically in the root
|
||||
* tree used by the super block to find all the other trees
|
||||
*/
|
||||
#define BTRFS_ROOT_ITEM_KEY 132
|
||||
|
||||
/*
|
||||
* root backrefs tie subvols and snapshots to the directory entries that
|
||||
* reference them
|
||||
*/
|
||||
#define BTRFS_ROOT_BACKREF_KEY 144
|
||||
|
||||
/*
|
||||
* root refs make a fast index for listing all of the snapshots and
|
||||
* subvolumes referenced by a given root. They point directly to the
|
||||
* directory item in the root that references the subvol
|
||||
*/
|
||||
#define BTRFS_ROOT_REF_KEY 156
|
||||
|
||||
/*
|
||||
* extent items are in the extent map tree. These record which blocks
|
||||
* are used, and how many references there are to each block
|
||||
*/
|
||||
#define BTRFS_EXTENT_ITEM_KEY 168
|
||||
|
||||
/*
|
||||
* The same as the BTRFS_EXTENT_ITEM_KEY, except it's metadata we already know
|
||||
* the length, so we save the level in key->offset instead of the length.
|
||||
*/
|
||||
#define BTRFS_METADATA_ITEM_KEY 169
|
||||
|
||||
#define BTRFS_TREE_BLOCK_REF_KEY 176
|
||||
|
||||
#define BTRFS_EXTENT_DATA_REF_KEY 178
|
||||
|
||||
#define BTRFS_EXTENT_REF_V0_KEY 180
|
||||
|
||||
#define BTRFS_SHARED_BLOCK_REF_KEY 182
|
||||
|
||||
#define BTRFS_SHARED_DATA_REF_KEY 184
|
||||
|
||||
/*
|
||||
* block groups give us hints into the extent allocation trees. Which
|
||||
* blocks are free etc etc
|
||||
*/
|
||||
#define BTRFS_BLOCK_GROUP_ITEM_KEY 192
|
||||
|
||||
/*
|
||||
* Every block group is represented in the free space tree by a free space info
|
||||
* item, which stores some accounting information. It is keyed on
|
||||
* (block_group_start, FREE_SPACE_INFO, block_group_length).
|
||||
*/
|
||||
#define BTRFS_FREE_SPACE_INFO_KEY 198
|
||||
|
||||
/*
|
||||
* A free space extent tracks an extent of space that is free in a block group.
|
||||
* It is keyed on (start, FREE_SPACE_EXTENT, length).
|
||||
*/
|
||||
#define BTRFS_FREE_SPACE_EXTENT_KEY 199
|
||||
|
||||
/*
|
||||
* When a block group becomes very fragmented, we convert it to use bitmaps
|
||||
* instead of extents. A free space bitmap is keyed on
|
||||
* (start, FREE_SPACE_BITMAP, length); the corresponding item is a bitmap with
|
||||
* (length / sectorsize) bits.
|
||||
*/
|
||||
#define BTRFS_FREE_SPACE_BITMAP_KEY 200
|
||||
|
||||
#define BTRFS_DEV_EXTENT_KEY 204
|
||||
#define BTRFS_DEV_ITEM_KEY 216
|
||||
#define BTRFS_CHUNK_ITEM_KEY 228
|
||||
|
||||
/*
|
||||
* Records the overall state of the qgroups.
|
||||
* There's only one instance of this key present,
|
||||
* (0, BTRFS_QGROUP_STATUS_KEY, 0)
|
||||
*/
|
||||
#define BTRFS_QGROUP_STATUS_KEY 240
|
||||
/*
|
||||
* Records the currently used space of the qgroup.
|
||||
* One key per qgroup, (0, BTRFS_QGROUP_INFO_KEY, qgroupid).
|
||||
*/
|
||||
#define BTRFS_QGROUP_INFO_KEY 242
|
||||
/*
|
||||
* Contains the user configured limits for the qgroup.
|
||||
* One key per qgroup, (0, BTRFS_QGROUP_LIMIT_KEY, qgroupid).
|
||||
*/
|
||||
#define BTRFS_QGROUP_LIMIT_KEY 244
|
||||
/*
|
||||
* Records the child-parent relationship of qgroups. For
|
||||
* each relation, 2 keys are present:
|
||||
* (childid, BTRFS_QGROUP_RELATION_KEY, parentid)
|
||||
* (parentid, BTRFS_QGROUP_RELATION_KEY, childid)
|
||||
*/
|
||||
#define BTRFS_QGROUP_RELATION_KEY 246
|
||||
|
||||
/*
|
||||
* Obsolete name, see BTRFS_TEMPORARY_ITEM_KEY.
|
||||
*/
|
||||
#define BTRFS_BALANCE_ITEM_KEY 248
|
||||
|
||||
/*
|
||||
* The key type for tree items that are stored persistently, but do not need to
|
||||
* exist for extended period of time. The items can exist in any tree.
|
||||
*
|
||||
* [subtype, BTRFS_TEMPORARY_ITEM_KEY, data]
|
||||
*
|
||||
* Existing items:
|
||||
*
|
||||
* - balance status item
|
||||
* (BTRFS_BALANCE_OBJECTID, BTRFS_TEMPORARY_ITEM_KEY, 0)
|
||||
*/
|
||||
#define BTRFS_TEMPORARY_ITEM_KEY 248
|
||||
|
||||
/*
|
||||
* Obsolete name, see BTRFS_PERSISTENT_ITEM_KEY
|
||||
*/
|
||||
#define BTRFS_DEV_STATS_KEY 249
|
||||
|
||||
/*
|
||||
* The key type for tree items that are stored persistently and usually exist
|
||||
* for a long period, eg. filesystem lifetime. The item kinds can be status
|
||||
* information, stats or preference values. The item can exist in any tree.
|
||||
*
|
||||
* [subtype, BTRFS_PERSISTENT_ITEM_KEY, data]
|
||||
*
|
||||
* Existing items:
|
||||
*
|
||||
* - device statistics, store IO stats in the device tree, one key for all
|
||||
* stats
|
||||
* (BTRFS_DEV_STATS_OBJECTID, BTRFS_DEV_STATS_KEY, 0)
|
||||
*/
|
||||
#define BTRFS_PERSISTENT_ITEM_KEY 249
|
||||
|
||||
/*
|
||||
* Persistantly stores the device replace state in the device tree.
|
||||
* The key is built like this: (0, BTRFS_DEV_REPLACE_KEY, 0).
|
||||
*/
|
||||
#define BTRFS_DEV_REPLACE_KEY 250
|
||||
|
||||
/*
|
||||
* Stores items that allow to quickly map UUIDs to something else.
|
||||
* These items are part of the filesystem UUID tree.
|
||||
* The key is built like this:
|
||||
* (UUID_upper_64_bits, BTRFS_UUID_KEY*, UUID_lower_64_bits).
|
||||
*/
|
||||
#if BTRFS_UUID_SIZE != 16
|
||||
#error "UUID items require BTRFS_UUID_SIZE == 16!"
|
||||
#endif
|
||||
#define BTRFS_UUID_KEY_SUBVOL 251 /* for UUIDs assigned to subvols */
|
||||
#define BTRFS_UUID_KEY_RECEIVED_SUBVOL 252 /* for UUIDs assigned to
|
||||
* received subvols */
|
||||
|
||||
/*
|
||||
* string items are for debugging. They just store a short string of
|
||||
* data in the FS
|
||||
*/
|
||||
#define BTRFS_STRING_ITEM_KEY 253
|
||||
|
||||
|
||||
|
||||
/* 32 bytes in various csum fields */
|
||||
#define BTRFS_CSUM_SIZE 32
|
||||
|
||||
/* csum types */
|
||||
#define BTRFS_CSUM_TYPE_CRC32 0
|
||||
|
||||
/*
|
||||
* flags definitions for directory entry item type
|
||||
*
|
||||
* Used by:
|
||||
* struct btrfs_dir_item.type
|
||||
*/
|
||||
#define BTRFS_FT_UNKNOWN 0
|
||||
#define BTRFS_FT_REG_FILE 1
|
||||
#define BTRFS_FT_DIR 2
|
||||
#define BTRFS_FT_CHRDEV 3
|
||||
#define BTRFS_FT_BLKDEV 4
|
||||
#define BTRFS_FT_FIFO 5
|
||||
#define BTRFS_FT_SOCK 6
|
||||
#define BTRFS_FT_SYMLINK 7
|
||||
#define BTRFS_FT_XATTR 8
|
||||
#define BTRFS_FT_MAX 9
|
||||
|
||||
/*
|
||||
* The key defines the order in the tree, and so it also defines (optimal)
|
||||
* block layout.
|
||||
*
|
||||
* objectid corresponds to the inode number.
|
||||
*
|
||||
* type tells us things about the object, and is a kind of stream selector.
|
||||
* so for a given inode, keys with type of 1 might refer to the inode data,
|
||||
* type of 2 may point to file data in the btree and type == 3 may point to
|
||||
* extents.
|
||||
*
|
||||
* offset is the starting byte offset for this key in the stream.
|
||||
*/
|
||||
|
||||
struct btrfs_key {
|
||||
__u64 objectid;
|
||||
__u8 type;
|
||||
__u64 offset;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct btrfs_dev_item {
|
||||
/* the internal btrfs device id */
|
||||
__u64 devid;
|
||||
|
||||
/* size of the device */
|
||||
__u64 total_bytes;
|
||||
|
||||
/* bytes used */
|
||||
__u64 bytes_used;
|
||||
|
||||
/* optimal io alignment for this device */
|
||||
__u32 io_align;
|
||||
|
||||
/* optimal io width for this device */
|
||||
__u32 io_width;
|
||||
|
||||
/* minimal io size for this device */
|
||||
__u32 sector_size;
|
||||
|
||||
/* type and info about this device */
|
||||
__u64 type;
|
||||
|
||||
/* expected generation for this device */
|
||||
__u64 generation;
|
||||
|
||||
/*
|
||||
* starting byte of this partition on the device,
|
||||
* to allow for stripe alignment in the future
|
||||
*/
|
||||
__u64 start_offset;
|
||||
|
||||
/* grouping information for allocation decisions */
|
||||
__u32 dev_group;
|
||||
|
||||
/* seek speed 0-100 where 100 is fastest */
|
||||
__u8 seek_speed;
|
||||
|
||||
/* bandwidth 0-100 where 100 is fastest */
|
||||
__u8 bandwidth;
|
||||
|
||||
/* btrfs generated uuid for this device */
|
||||
__u8 uuid[BTRFS_UUID_SIZE];
|
||||
|
||||
/* uuid of FS who owns this device */
|
||||
__u8 fsid[BTRFS_UUID_SIZE];
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct btrfs_stripe {
|
||||
__u64 devid;
|
||||
__u64 offset;
|
||||
__u8 dev_uuid[BTRFS_UUID_SIZE];
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct btrfs_chunk {
|
||||
/* size of this chunk in bytes */
|
||||
__u64 length;
|
||||
|
||||
/* objectid of the root referencing this chunk */
|
||||
__u64 owner;
|
||||
|
||||
__u64 stripe_len;
|
||||
__u64 type;
|
||||
|
||||
/* optimal io alignment for this chunk */
|
||||
__u32 io_align;
|
||||
|
||||
/* optimal io width for this chunk */
|
||||
__u32 io_width;
|
||||
|
||||
/* minimal io size for this chunk */
|
||||
__u32 sector_size;
|
||||
|
||||
/* 2^16 stripes is quite a lot, a second limit is the size of a single
|
||||
* item in the btree
|
||||
*/
|
||||
__u16 num_stripes;
|
||||
|
||||
/* sub stripes only matter for raid10 */
|
||||
__u16 sub_stripes;
|
||||
struct btrfs_stripe stripe;
|
||||
/* additional stripes go here */
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
#define BTRFS_FREE_SPACE_EXTENT 1
|
||||
#define BTRFS_FREE_SPACE_BITMAP 2
|
||||
|
||||
struct btrfs_free_space_entry {
|
||||
__u64 offset;
|
||||
__u64 bytes;
|
||||
__u8 type;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct btrfs_free_space_header {
|
||||
struct btrfs_key location;
|
||||
__u64 generation;
|
||||
__u64 num_entries;
|
||||
__u64 num_bitmaps;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
#define BTRFS_HEADER_FLAG_WRITTEN (1ULL << 0)
|
||||
#define BTRFS_HEADER_FLAG_RELOC (1ULL << 1)
|
||||
|
||||
/* Super block flags */
|
||||
/* Errors detected */
|
||||
#define BTRFS_SUPER_FLAG_ERROR (1ULL << 2)
|
||||
|
||||
#define BTRFS_SUPER_FLAG_SEEDING (1ULL << 32)
|
||||
#define BTRFS_SUPER_FLAG_METADUMP (1ULL << 33)
|
||||
|
||||
|
||||
/*
|
||||
* items in the extent btree are used to record the objectid of the
|
||||
* owner of the block and the number of references
|
||||
*/
|
||||
|
||||
struct btrfs_extent_item {
|
||||
__u64 refs;
|
||||
__u64 generation;
|
||||
__u64 flags;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
|
||||
#define BTRFS_EXTENT_FLAG_DATA (1ULL << 0)
|
||||
#define BTRFS_EXTENT_FLAG_TREE_BLOCK (1ULL << 1)
|
||||
|
||||
/* following flags only apply to tree blocks */
|
||||
|
||||
/* use full backrefs for extent pointers in the block */
|
||||
#define BTRFS_BLOCK_FLAG_FULL_BACKREF (1ULL << 8)
|
||||
|
||||
/*
|
||||
* this flag is only used internally by scrub and may be changed at any time
|
||||
* it is only declared here to avoid collisions
|
||||
*/
|
||||
#define BTRFS_EXTENT_FLAG_SUPER (1ULL << 48)
|
||||
|
||||
struct btrfs_tree_block_info {
|
||||
struct btrfs_key key;
|
||||
__u8 level;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct btrfs_extent_data_ref {
|
||||
__u64 root;
|
||||
__u64 objectid;
|
||||
__u64 offset;
|
||||
__u32 count;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct btrfs_shared_data_ref {
|
||||
__u32 count;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct btrfs_extent_inline_ref {
|
||||
__u8 type;
|
||||
__u64 offset;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
/* dev extents record free space on individual devices. The owner
|
||||
* field points back to the chunk allocation mapping tree that allocated
|
||||
* the extent. The chunk tree uuid field is a way to double check the owner
|
||||
*/
|
||||
struct btrfs_dev_extent {
|
||||
__u64 chunk_tree;
|
||||
__u64 chunk_objectid;
|
||||
__u64 chunk_offset;
|
||||
__u64 length;
|
||||
__u8 chunk_tree_uuid[BTRFS_UUID_SIZE];
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct btrfs_inode_ref {
|
||||
__u64 index;
|
||||
__u16 name_len;
|
||||
/* name goes here */
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct btrfs_inode_extref {
|
||||
__u64 parent_objectid;
|
||||
__u64 index;
|
||||
__u16 name_len;
|
||||
__u8 name[0];
|
||||
/* name goes here */
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct btrfs_timespec {
|
||||
__u64 sec;
|
||||
__u32 nsec;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct btrfs_inode_item {
|
||||
/* nfs style generation number */
|
||||
__u64 generation;
|
||||
/* transid that last touched this inode */
|
||||
__u64 transid;
|
||||
__u64 size;
|
||||
__u64 nbytes;
|
||||
__u64 block_group;
|
||||
__u32 nlink;
|
||||
__u32 uid;
|
||||
__u32 gid;
|
||||
__u32 mode;
|
||||
__u64 rdev;
|
||||
__u64 flags;
|
||||
|
||||
/* modification sequence number for NFS */
|
||||
__u64 sequence;
|
||||
|
||||
/*
|
||||
* a little future expansion, for more than this we can
|
||||
* just grow the inode item and version it
|
||||
*/
|
||||
__u64 reserved[4];
|
||||
struct btrfs_timespec atime;
|
||||
struct btrfs_timespec ctime;
|
||||
struct btrfs_timespec mtime;
|
||||
struct btrfs_timespec otime;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct btrfs_dir_log_item {
|
||||
__u64 end;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct btrfs_dir_item {
|
||||
struct btrfs_key location;
|
||||
__u64 transid;
|
||||
__u16 data_len;
|
||||
__u16 name_len;
|
||||
__u8 type;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
#define BTRFS_ROOT_SUBVOL_RDONLY (1ULL << 0)
|
||||
|
||||
/*
|
||||
* Internal in-memory flag that a subvolume has been marked for deletion but
|
||||
* still visible as a directory
|
||||
*/
|
||||
#define BTRFS_ROOT_SUBVOL_DEAD (1ULL << 48)
|
||||
|
||||
struct btrfs_root_item {
|
||||
struct btrfs_inode_item inode;
|
||||
__u64 generation;
|
||||
__u64 root_dirid;
|
||||
__u64 bytenr;
|
||||
__u64 byte_limit;
|
||||
__u64 bytes_used;
|
||||
__u64 last_snapshot;
|
||||
__u64 flags;
|
||||
__u32 refs;
|
||||
struct btrfs_key drop_progress;
|
||||
__u8 drop_level;
|
||||
__u8 level;
|
||||
|
||||
/*
|
||||
* The following fields appear after subvol_uuids+subvol_times
|
||||
* were introduced.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This generation number is used to test if the new fields are valid
|
||||
* and up to date while reading the root item. Every time the root item
|
||||
* is written out, the "generation" field is copied into this field. If
|
||||
* anyone ever mounted the fs with an older kernel, we will have
|
||||
* mismatching generation values here and thus must invalidate the
|
||||
* new fields. See btrfs_update_root and btrfs_find_last_root for
|
||||
* details.
|
||||
* the offset of generation_v2 is also used as the start for the memset
|
||||
* when invalidating the fields.
|
||||
*/
|
||||
__u64 generation_v2;
|
||||
__u8 uuid[BTRFS_UUID_SIZE];
|
||||
__u8 parent_uuid[BTRFS_UUID_SIZE];
|
||||
__u8 received_uuid[BTRFS_UUID_SIZE];
|
||||
__u64 ctransid; /* updated when an inode changes */
|
||||
__u64 otransid; /* trans when created */
|
||||
__u64 stransid; /* trans when sent. non-zero for received subvol */
|
||||
__u64 rtransid; /* trans when received. non-zero for received subvol */
|
||||
struct btrfs_timespec ctime;
|
||||
struct btrfs_timespec otime;
|
||||
struct btrfs_timespec stime;
|
||||
struct btrfs_timespec rtime;
|
||||
__u64 reserved[8]; /* for future */
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
/*
|
||||
* this is used for both forward and backward root refs
|
||||
*/
|
||||
struct btrfs_root_ref {
|
||||
__u64 dirid;
|
||||
__u64 sequence;
|
||||
__u16 name_len;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
#define BTRFS_FILE_EXTENT_INLINE 0
|
||||
#define BTRFS_FILE_EXTENT_REG 1
|
||||
#define BTRFS_FILE_EXTENT_PREALLOC 2
|
||||
|
||||
enum btrfs_compression_type {
|
||||
BTRFS_COMPRESS_NONE = 0,
|
||||
BTRFS_COMPRESS_ZLIB = 1,
|
||||
BTRFS_COMPRESS_LZO = 2,
|
||||
BTRFS_COMPRESS_ZSTD = 3,
|
||||
BTRFS_COMPRESS_TYPES = 3,
|
||||
BTRFS_COMPRESS_LAST = 4,
|
||||
};
|
||||
|
||||
struct btrfs_file_extent_item {
|
||||
/*
|
||||
* transaction id that created this extent
|
||||
*/
|
||||
__u64 generation;
|
||||
/*
|
||||
* max number of bytes to hold this extent in ram
|
||||
* when we split a compressed extent we can't know how big
|
||||
* each of the resulting pieces will be. So, this is
|
||||
* an upper limit on the size of the extent in ram instead of
|
||||
* an exact limit.
|
||||
*/
|
||||
__u64 ram_bytes;
|
||||
|
||||
/*
|
||||
* 32 bits for the various ways we might encode the data,
|
||||
* including compression and encryption. If any of these
|
||||
* are set to something a given disk format doesn't understand
|
||||
* it is treated like an incompat flag for reading and writing,
|
||||
* but not for stat.
|
||||
*/
|
||||
__u8 compression;
|
||||
__u8 encryption;
|
||||
__u16 other_encoding; /* spare for later use */
|
||||
|
||||
/* are we inline data or a real extent? */
|
||||
__u8 type;
|
||||
|
||||
/*
|
||||
* disk space consumed by the extent, checksum blocks are included
|
||||
* in these numbers
|
||||
*
|
||||
* At this offset in the structure, the inline extent data start.
|
||||
*/
|
||||
__u64 disk_bytenr;
|
||||
__u64 disk_num_bytes;
|
||||
/*
|
||||
* the logical offset in file blocks (no csums)
|
||||
* this extent record is for. This allows a file extent to point
|
||||
* into the middle of an existing extent on disk, sharing it
|
||||
* between two snapshots (useful if some bytes in the middle of the
|
||||
* extent have changed
|
||||
*/
|
||||
__u64 offset;
|
||||
/*
|
||||
* the logical number of file blocks (no csums included). This
|
||||
* always reflects the size uncompressed and without encoding.
|
||||
*/
|
||||
__u64 num_bytes;
|
||||
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct btrfs_csum_item {
|
||||
__u8 csum;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
/* different types of block groups (and chunks) */
|
||||
#define BTRFS_BLOCK_GROUP_DATA (1ULL << 0)
|
||||
#define BTRFS_BLOCK_GROUP_SYSTEM (1ULL << 1)
|
||||
#define BTRFS_BLOCK_GROUP_METADATA (1ULL << 2)
|
||||
#define BTRFS_BLOCK_GROUP_RAID0 (1ULL << 3)
|
||||
#define BTRFS_BLOCK_GROUP_RAID1 (1ULL << 4)
|
||||
#define BTRFS_BLOCK_GROUP_DUP (1ULL << 5)
|
||||
#define BTRFS_BLOCK_GROUP_RAID10 (1ULL << 6)
|
||||
#define BTRFS_BLOCK_GROUP_RAID5 (1ULL << 7)
|
||||
#define BTRFS_BLOCK_GROUP_RAID6 (1ULL << 8)
|
||||
#define BTRFS_BLOCK_GROUP_RESERVED (BTRFS_AVAIL_ALLOC_BIT_SINGLE | \
|
||||
BTRFS_SPACE_INFO_GLOBAL_RSV)
|
||||
|
||||
enum btrfs_raid_types {
|
||||
BTRFS_RAID_RAID10,
|
||||
BTRFS_RAID_RAID1,
|
||||
BTRFS_RAID_DUP,
|
||||
BTRFS_RAID_RAID0,
|
||||
BTRFS_RAID_SINGLE,
|
||||
BTRFS_RAID_RAID5,
|
||||
BTRFS_RAID_RAID6,
|
||||
BTRFS_NR_RAID_TYPES
|
||||
};
|
||||
|
||||
#define BTRFS_BLOCK_GROUP_TYPE_MASK (BTRFS_BLOCK_GROUP_DATA | \
|
||||
BTRFS_BLOCK_GROUP_SYSTEM | \
|
||||
BTRFS_BLOCK_GROUP_METADATA)
|
||||
|
||||
#define BTRFS_BLOCK_GROUP_PROFILE_MASK (BTRFS_BLOCK_GROUP_RAID0 | \
|
||||
BTRFS_BLOCK_GROUP_RAID1 | \
|
||||
BTRFS_BLOCK_GROUP_RAID5 | \
|
||||
BTRFS_BLOCK_GROUP_RAID6 | \
|
||||
BTRFS_BLOCK_GROUP_DUP | \
|
||||
BTRFS_BLOCK_GROUP_RAID10)
|
||||
#define BTRFS_BLOCK_GROUP_RAID56_MASK (BTRFS_BLOCK_GROUP_RAID5 | \
|
||||
BTRFS_BLOCK_GROUP_RAID6)
|
||||
|
||||
/*
|
||||
* We need a bit for restriper to be able to tell when chunks of type
|
||||
* SINGLE are available. This "extended" profile format is used in
|
||||
* fs_info->avail_*_alloc_bits (in-memory) and balance item fields
|
||||
* (on-disk). The corresponding on-disk bit in chunk.type is reserved
|
||||
* to avoid remappings between two formats in future.
|
||||
*/
|
||||
#define BTRFS_AVAIL_ALLOC_BIT_SINGLE (1ULL << 48)
|
||||
|
||||
/*
|
||||
* A fake block group type that is used to communicate global block reserve
|
||||
* size to userspace via the SPACE_INFO ioctl.
|
||||
*/
|
||||
#define BTRFS_SPACE_INFO_GLOBAL_RSV (1ULL << 49)
|
||||
|
||||
#define BTRFS_EXTENDED_PROFILE_MASK (BTRFS_BLOCK_GROUP_PROFILE_MASK | \
|
||||
BTRFS_AVAIL_ALLOC_BIT_SINGLE)
|
||||
|
||||
#endif /* __BTRFS_BTRFS_TREE_H__ */
|
|
@ -1,178 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* BTRFS filesystem implementation for U-Boot
|
||||
*
|
||||
* 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
|
||||
*/
|
||||
|
||||
#include "btrfs.h"
|
||||
#include <log.h>
|
||||
#include <malloc.h>
|
||||
|
||||
struct chunk_map_item {
|
||||
struct rb_node node;
|
||||
u64 logical;
|
||||
u64 length;
|
||||
u64 physical;
|
||||
};
|
||||
|
||||
static int add_chunk_mapping(struct btrfs_key *key, struct btrfs_chunk *chunk)
|
||||
{
|
||||
struct btrfs_stripe *stripe;
|
||||
u64 block_profile = chunk->type & BTRFS_BLOCK_GROUP_PROFILE_MASK;
|
||||
struct rb_node **new = &(btrfs_info.chunks_root.rb_node), *prnt = NULL;
|
||||
struct chunk_map_item *map_item;
|
||||
|
||||
if (block_profile && block_profile != BTRFS_BLOCK_GROUP_DUP) {
|
||||
printf("%s: unsupported chunk profile %llu\n", __func__,
|
||||
block_profile);
|
||||
return -1;
|
||||
} else if (!chunk->length) {
|
||||
printf("%s: zero length chunk\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
stripe = &chunk->stripe;
|
||||
btrfs_stripe_to_cpu(stripe);
|
||||
|
||||
while (*new) {
|
||||
struct chunk_map_item *this;
|
||||
|
||||
this = rb_entry(*new, struct chunk_map_item, node);
|
||||
|
||||
prnt = *new;
|
||||
if (key->offset < this->logical) {
|
||||
new = &((*new)->rb_left);
|
||||
} else if (key->offset > this->logical) {
|
||||
new = &((*new)->rb_right);
|
||||
} else {
|
||||
debug("%s: Logical address %llu already in map!\n",
|
||||
__func__, key->offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
map_item = malloc(sizeof(struct chunk_map_item));
|
||||
if (!map_item)
|
||||
return -1;
|
||||
|
||||
map_item->logical = key->offset;
|
||||
map_item->length = chunk->length;
|
||||
map_item->physical = le64_to_cpu(chunk->stripe.offset);
|
||||
rb_link_node(&map_item->node, prnt, new);
|
||||
rb_insert_color(&map_item->node, &btrfs_info.chunks_root);
|
||||
|
||||
debug("%s: Mapping %llu to %llu\n", __func__, map_item->logical,
|
||||
map_item->physical);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 btrfs_map_logical_to_physical(u64 logical)
|
||||
{
|
||||
struct rb_node *node = btrfs_info.chunks_root.rb_node;
|
||||
|
||||
while (node) {
|
||||
struct chunk_map_item *item;
|
||||
|
||||
item = rb_entry(node, struct chunk_map_item, node);
|
||||
|
||||
if (item->logical > logical)
|
||||
node = node->rb_left;
|
||||
else if (logical >= item->logical + item->length)
|
||||
node = node->rb_right;
|
||||
else
|
||||
return item->physical + logical - item->logical;
|
||||
}
|
||||
|
||||
printf("%s: Cannot map logical address %llu to physical\n", __func__,
|
||||
logical);
|
||||
|
||||
return -1ULL;
|
||||
}
|
||||
|
||||
void btrfs_chunk_map_exit(void)
|
||||
{
|
||||
struct rb_node *now, *next;
|
||||
struct chunk_map_item *item;
|
||||
|
||||
for (now = rb_first_postorder(&btrfs_info.chunks_root); now; now = next)
|
||||
{
|
||||
item = rb_entry(now, struct chunk_map_item, node);
|
||||
next = rb_next_postorder(now);
|
||||
free(item);
|
||||
}
|
||||
}
|
||||
|
||||
int btrfs_chunk_map_init(void)
|
||||
{
|
||||
u8 sys_chunk_array_copy[sizeof(btrfs_info.sb.sys_chunk_array)];
|
||||
u8 * const start = sys_chunk_array_copy;
|
||||
u8 * const end = start + btrfs_info.sb.sys_chunk_array_size;
|
||||
u8 *cur;
|
||||
struct btrfs_key *key;
|
||||
struct btrfs_chunk *chunk;
|
||||
|
||||
btrfs_info.chunks_root = RB_ROOT;
|
||||
|
||||
memcpy(sys_chunk_array_copy, btrfs_info.sb.sys_chunk_array,
|
||||
sizeof(sys_chunk_array_copy));
|
||||
|
||||
for (cur = start; cur < end;) {
|
||||
key = (struct btrfs_key *) cur;
|
||||
cur += sizeof(struct btrfs_key);
|
||||
chunk = (struct btrfs_chunk *) cur;
|
||||
|
||||
btrfs_key_to_cpu(key);
|
||||
btrfs_chunk_to_cpu(chunk);
|
||||
|
||||
if (key->type != BTRFS_CHUNK_ITEM_KEY) {
|
||||
printf("%s: invalid key type %u\n", __func__,
|
||||
key->type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (add_chunk_mapping(key, chunk))
|
||||
return -1;
|
||||
|
||||
cur += sizeof(struct btrfs_chunk);
|
||||
cur += sizeof(struct btrfs_stripe) * (chunk->num_stripes - 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int btrfs_read_chunk_tree(void)
|
||||
{
|
||||
struct btrfs_path path;
|
||||
struct btrfs_key key, *found_key;
|
||||
struct btrfs_chunk *chunk;
|
||||
int res = 0;
|
||||
|
||||
key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
|
||||
key.type = BTRFS_CHUNK_ITEM_KEY;
|
||||
key.offset = 0;
|
||||
|
||||
if (btrfs_search_tree(&btrfs_info.chunk_root, &key, &path))
|
||||
return -1;
|
||||
|
||||
do {
|
||||
found_key = btrfs_path_leaf_key(&path);
|
||||
if (btrfs_comp_keys_type(&key, found_key))
|
||||
continue;
|
||||
|
||||
chunk = btrfs_path_item_ptr(&path, struct btrfs_chunk);
|
||||
btrfs_chunk_to_cpu(chunk);
|
||||
if (add_chunk_mapping(found_key, chunk)) {
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
} while (!(res = btrfs_next_slot(&path)));
|
||||
|
||||
btrfs_free_path(&path);
|
||||
|
||||
if (res < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
83
fs/btrfs/common/rbtree-utils.c
Normal file
83
fs/btrfs/common/rbtree-utils.c
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Facebook. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License v2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 021110-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include "rbtree-utils.h"
|
||||
|
||||
int rb_insert(struct rb_root *root, struct rb_node *node,
|
||||
rb_compare_nodes comp)
|
||||
{
|
||||
struct rb_node **p = &root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
int ret;
|
||||
|
||||
while(*p) {
|
||||
parent = *p;
|
||||
|
||||
ret = comp(parent, node);
|
||||
if (ret < 0)
|
||||
p = &(*p)->rb_left;
|
||||
else if (ret > 0)
|
||||
p = &(*p)->rb_right;
|
||||
else
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
rb_link_node(node, parent, p);
|
||||
rb_insert_color(node, root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct rb_node *rb_search(struct rb_root *root, void *key, rb_compare_keys comp,
|
||||
struct rb_node **next_ret)
|
||||
{
|
||||
struct rb_node *n = root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
int ret = 0;
|
||||
|
||||
while(n) {
|
||||
parent = n;
|
||||
|
||||
ret = comp(n, key);
|
||||
if (ret < 0)
|
||||
n = n->rb_left;
|
||||
else if (ret > 0)
|
||||
n = n->rb_right;
|
||||
else
|
||||
return n;
|
||||
}
|
||||
|
||||
if (!next_ret)
|
||||
return NULL;
|
||||
|
||||
if (parent && ret > 0)
|
||||
parent = rb_next(parent);
|
||||
|
||||
*next_ret = parent;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void rb_free_nodes(struct rb_root *root, rb_free_node free_node)
|
||||
{
|
||||
struct rb_node *node;
|
||||
|
||||
while ((node = rb_first(root))) {
|
||||
rb_erase(node, root);
|
||||
free_node(node);
|
||||
}
|
||||
}
|
53
fs/btrfs/common/rbtree-utils.h
Normal file
53
fs/btrfs/common/rbtree-utils.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Facebook. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License v2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 021110-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __RBTREE_UTILS__
|
||||
#define __RBTREE_UTILS__
|
||||
|
||||
#include <linux/rbtree.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* The common insert/search/free functions */
|
||||
typedef int (*rb_compare_nodes)(struct rb_node *node1, struct rb_node *node2);
|
||||
typedef int (*rb_compare_keys)(struct rb_node *node, void *key);
|
||||
typedef void (*rb_free_node)(struct rb_node *node);
|
||||
|
||||
int rb_insert(struct rb_root *root, struct rb_node *node,
|
||||
rb_compare_nodes comp);
|
||||
/*
|
||||
* In some cases, we need return the next node if we don't find the node we
|
||||
* specify. At this time, we can use next_ret.
|
||||
*/
|
||||
struct rb_node *rb_search(struct rb_root *root, void *key, rb_compare_keys comp,
|
||||
struct rb_node **next_ret);
|
||||
void rb_free_nodes(struct rb_root *root, rb_free_node free_node);
|
||||
|
||||
#define FREE_RB_BASED_TREE(name, free_func) \
|
||||
static void free_##name##_tree(struct rb_root *root) \
|
||||
{ \
|
||||
rb_free_nodes(root, free_func); \
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
82
fs/btrfs/compat.h
Normal file
82
fs/btrfs/compat.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
#ifndef __BTRFS_COMPAT_H__
|
||||
#define __BTRFS_COMPAT_H__
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <fs_internal.h>
|
||||
#include <uuid.h>
|
||||
|
||||
/* Provide a compatibility layer to make code syncing easier */
|
||||
|
||||
/* A simple wraper to for error() used in btrfs-progs */
|
||||
#define error(fmt, ...) pr_err("BTRFS: " fmt "\n", ##__VA_ARGS__)
|
||||
|
||||
#define ASSERT(c) assert(c)
|
||||
|
||||
#define BTRFS_UUID_UNPARSED_SIZE 37
|
||||
|
||||
/* No <linux/limits.h> so have to define it here */
|
||||
#define XATTR_NAME_MAX 255
|
||||
#define PATH_MAX 4096
|
||||
|
||||
/*
|
||||
* Macros to generate set/get funcs for the struct fields
|
||||
* assume there is a lefoo_to_cpu for every type, so lets make a simple
|
||||
* one for u8:
|
||||
*/
|
||||
#define le8_to_cpu(v) (v)
|
||||
#define cpu_to_le8(v) (v)
|
||||
#define __le8 u8
|
||||
|
||||
/*
|
||||
* Macros to generate set/get funcs for the struct fields
|
||||
* assume there is a lefoo_to_cpu for every type, so lets make a simple
|
||||
* one for u8:
|
||||
*/
|
||||
#define le8_to_cpu(v) (v)
|
||||
#define cpu_to_le8(v) (v)
|
||||
#define __le8 u8
|
||||
|
||||
#define get_unaligned_le8(p) (*((u8 *)(p)))
|
||||
#define get_unaligned_8(p) (*((u8 *)(p)))
|
||||
#define put_unaligned_le8(val,p) ((*((u8 *)(p))) = (val))
|
||||
#define put_unaligned_8(val,p) ((*((u8 *)(p))) = (val))
|
||||
|
||||
/*
|
||||
* Read data from device specified by @desc and @part
|
||||
*
|
||||
* U-boot equivalent of pread().
|
||||
*
|
||||
* Return the bytes of data read.
|
||||
* Return <0 for error.
|
||||
*/
|
||||
static inline int __btrfs_devread(struct blk_desc *desc,
|
||||
struct disk_partition *part,
|
||||
void *buf, size_t size, u64 offset)
|
||||
{
|
||||
lbaint_t sector;
|
||||
int byte_offset;
|
||||
int ret;
|
||||
|
||||
sector = offset >> desc->log2blksz;
|
||||
byte_offset = offset % desc->blksz;
|
||||
|
||||
/* fs_devread() return 0 for error, >0 for success */
|
||||
ret = fs_devread(desc, part, sector, byte_offset, size, buf);
|
||||
if (!ret)
|
||||
return -EIO;
|
||||
return size;
|
||||
}
|
||||
|
||||
static inline void uuid_unparse(const u8 *uuid, char *out)
|
||||
{
|
||||
return uuid_bin_to_str((unsigned char *)uuid, out, 0);
|
||||
}
|
||||
|
||||
static inline int is_power_of_2(unsigned long n)
|
||||
{
|
||||
return (n != 0 && ((n & (n - 1)) == 0));
|
||||
}
|
||||
|
||||
#endif
|
|
@ -115,7 +115,7 @@ static u32 decompress_zlib(const u8 *_cbuf, u32 clen, u8 *dbuf, u32 dlen)
|
|||
while (stream.total_in < clen) {
|
||||
stream.next_in = cbuf + stream.total_in;
|
||||
stream.avail_in = min((u32) (clen - stream.total_in),
|
||||
(u32) btrfs_info.sb.sectorsize);
|
||||
current_fs_info->sectorsize);
|
||||
|
||||
ret = inflate(&stream, Z_NO_FLUSH);
|
||||
if (ret != Z_OK)
|
||||
|
|
55
fs/btrfs/crypto/hash.c
Normal file
55
fs/btrfs/crypto/hash.c
Normal file
|
@ -0,0 +1,55 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
#include <linux/xxhash.h>
|
||||
#include <linux/unaligned/access_ok.h>
|
||||
#include <linux/types.h>
|
||||
#include <u-boot/sha256.h>
|
||||
#include <u-boot/crc.h>
|
||||
|
||||
static u32 btrfs_crc32c_table[256];
|
||||
|
||||
void btrfs_hash_init(void)
|
||||
{
|
||||
static int inited = 0;
|
||||
|
||||
if (!inited) {
|
||||
crc32c_init(btrfs_crc32c_table, 0x82F63B78);
|
||||
inited = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int hash_sha256(const u8 *buf, size_t length, u8 *out)
|
||||
{
|
||||
sha256_context ctx;
|
||||
|
||||
sha256_starts(&ctx);
|
||||
sha256_update(&ctx, buf, length);
|
||||
sha256_finish(&ctx, out);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hash_xxhash(const u8 *buf, size_t length, u8 *out)
|
||||
{
|
||||
u64 hash;
|
||||
|
||||
hash = xxh64(buf, length, 0);
|
||||
put_unaligned_le64(hash, out);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hash_crc32c(const u8 *buf, size_t length, u8 *out)
|
||||
{
|
||||
u32 crc;
|
||||
|
||||
crc = crc32c_cal((u32)~0, (char *)buf, length, btrfs_crc32c_table);
|
||||
put_unaligned_le32(~crc, out);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 crc32c(u32 seed, const void * data, size_t len)
|
||||
{
|
||||
return crc32c_cal(seed, data, len, btrfs_crc32c_table);
|
||||
}
|
17
fs/btrfs/crypto/hash.h
Normal file
17
fs/btrfs/crypto/hash.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#ifndef CRYPTO_HASH_H
|
||||
#define CRYPTO_HASH_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define CRYPTO_HASH_SIZE_MAX 32
|
||||
|
||||
void btrfs_hash_init(void);
|
||||
int hash_crc32c(const u8 *buf, size_t length, u8 *out);
|
||||
int hash_xxhash(const u8 *buf, size_t length, u8 *out);
|
||||
int hash_sha256(const u8 *buf, size_t length, u8 *out);
|
||||
|
||||
u32 crc32c(u32 seed, const void * data, size_t len);
|
||||
|
||||
/* Blake2B is not yet supported due to lack of library */
|
||||
|
||||
#endif
|
909
fs/btrfs/ctree.c
909
fs/btrfs/ctree.c
|
@ -5,287 +5,738 @@
|
|||
* 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
|
||||
*/
|
||||
|
||||
#include "btrfs.h"
|
||||
#include <linux/kernel.h>
|
||||
#include <log.h>
|
||||
#include <malloc.h>
|
||||
#include <memalign.h>
|
||||
#include "btrfs.h"
|
||||
#include "disk-io.h"
|
||||
|
||||
int btrfs_comp_keys(struct btrfs_key *a, struct btrfs_key *b)
|
||||
static const struct btrfs_csum {
|
||||
u16 size;
|
||||
const char name[14];
|
||||
} btrfs_csums[] = {
|
||||
[BTRFS_CSUM_TYPE_CRC32] = { 4, "crc32c" },
|
||||
[BTRFS_CSUM_TYPE_XXHASH] = { 8, "xxhash64" },
|
||||
[BTRFS_CSUM_TYPE_SHA256] = { 32, "sha256" },
|
||||
[BTRFS_CSUM_TYPE_BLAKE2] = { 32, "blake2" },
|
||||
};
|
||||
|
||||
u16 btrfs_super_csum_size(const struct btrfs_super_block *sb)
|
||||
{
|
||||
if (a->objectid > b->objectid)
|
||||
return 1;
|
||||
if (a->objectid < b->objectid)
|
||||
return -1;
|
||||
if (a->type > b->type)
|
||||
return 1;
|
||||
if (a->type < b->type)
|
||||
return -1;
|
||||
if (a->offset > b->offset)
|
||||
return 1;
|
||||
if (a->offset < b->offset)
|
||||
return -1;
|
||||
return 0;
|
||||
const u16 csum_type = btrfs_super_csum_type(sb);
|
||||
|
||||
return btrfs_csums[csum_type].size;
|
||||
}
|
||||
|
||||
int btrfs_comp_keys_type(struct btrfs_key *a, struct btrfs_key *b)
|
||||
const char *btrfs_super_csum_name(u16 csum_type)
|
||||
{
|
||||
if (a->objectid > b->objectid)
|
||||
return 1;
|
||||
if (a->objectid < b->objectid)
|
||||
return -1;
|
||||
if (a->type > b->type)
|
||||
return 1;
|
||||
if (a->type < b->type)
|
||||
return -1;
|
||||
return 0;
|
||||
return btrfs_csums[csum_type].name;
|
||||
}
|
||||
|
||||
static int generic_bin_search(void *addr, int item_size, struct btrfs_key *key,
|
||||
int max, int *slot)
|
||||
size_t btrfs_super_num_csums(void)
|
||||
{
|
||||
int low = 0, high = max, mid, ret;
|
||||
struct btrfs_key *tmp;
|
||||
|
||||
while (low < high) {
|
||||
mid = (low + high) / 2;
|
||||
|
||||
tmp = (struct btrfs_key *) ((u8 *) addr + mid*item_size);
|
||||
ret = btrfs_comp_keys(tmp, key);
|
||||
|
||||
if (ret < 0) {
|
||||
low = mid + 1;
|
||||
} else if (ret > 0) {
|
||||
high = mid;
|
||||
} else {
|
||||
*slot = mid;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
*slot = low;
|
||||
return 1;
|
||||
return ARRAY_SIZE(btrfs_csums);
|
||||
}
|
||||
|
||||
int btrfs_bin_search(union btrfs_tree_node *p, struct btrfs_key *key,
|
||||
int *slot)
|
||||
u16 btrfs_csum_type_size(u16 csum_type)
|
||||
{
|
||||
void *addr;
|
||||
unsigned long size;
|
||||
|
||||
if (p->header.level) {
|
||||
addr = p->node.ptrs;
|
||||
size = sizeof(struct btrfs_key_ptr);
|
||||
} else {
|
||||
addr = p->leaf.items;
|
||||
size = sizeof(struct btrfs_item);
|
||||
}
|
||||
|
||||
return generic_bin_search(addr, size, key, p->header.nritems, slot);
|
||||
return btrfs_csums[csum_type].size;
|
||||
}
|
||||
|
||||
static void clear_path(struct btrfs_path *p)
|
||||
struct btrfs_path *btrfs_alloc_path(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < BTRFS_MAX_LEVEL; ++i) {
|
||||
p->nodes[i] = NULL;
|
||||
p->slots[i] = 0;
|
||||
}
|
||||
struct btrfs_path *path;
|
||||
path = kzalloc(sizeof(struct btrfs_path), GFP_NOFS);
|
||||
return path;
|
||||
}
|
||||
|
||||
void btrfs_free_path(struct btrfs_path *p)
|
||||
{
|
||||
if (!p)
|
||||
return;
|
||||
btrfs_release_path(p);
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
void btrfs_release_path(struct btrfs_path *p)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < BTRFS_MAX_LEVEL; ++i) {
|
||||
if (p->nodes[i])
|
||||
free(p->nodes[i]);
|
||||
for (i = 0; i < BTRFS_MAX_LEVEL; i++) {
|
||||
if (!p->nodes[i])
|
||||
continue;
|
||||
free_extent_buffer(p->nodes[i]);
|
||||
}
|
||||
|
||||
clear_path(p);
|
||||
memset(p, 0, sizeof(*p));
|
||||
}
|
||||
|
||||
static int read_tree_node(u64 physical, union btrfs_tree_node **buf)
|
||||
int btrfs_comp_cpu_keys(const struct btrfs_key *k1, const struct btrfs_key *k2)
|
||||
{
|
||||
ALLOC_CACHE_ALIGN_BUFFER(struct btrfs_header, hdr,
|
||||
sizeof(struct btrfs_header));
|
||||
unsigned long size, offset = sizeof(*hdr);
|
||||
union btrfs_tree_node *res;
|
||||
u32 i;
|
||||
|
||||
if (!btrfs_devread(physical, sizeof(*hdr), hdr))
|
||||
if (k1->objectid > k2->objectid)
|
||||
return 1;
|
||||
if (k1->objectid < k2->objectid)
|
||||
return -1;
|
||||
|
||||
btrfs_header_to_cpu(hdr);
|
||||
|
||||
if (hdr->level)
|
||||
size = sizeof(struct btrfs_node)
|
||||
+ hdr->nritems * sizeof(struct btrfs_key_ptr);
|
||||
else
|
||||
size = btrfs_info.sb.nodesize;
|
||||
|
||||
res = malloc_cache_aligned(size);
|
||||
if (!res) {
|
||||
debug("%s: malloc failed\n", __func__);
|
||||
if (k1->type > k2->type)
|
||||
return 1;
|
||||
if (k1->type < k2->type)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!btrfs_devread(physical + offset, size - offset,
|
||||
((u8 *) res) + offset)) {
|
||||
free(res);
|
||||
if (k1->offset > k2->offset)
|
||||
return 1;
|
||||
if (k1->offset < k2->offset)
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(&res->header, hdr, sizeof(*hdr));
|
||||
if (hdr->level)
|
||||
for (i = 0; i < hdr->nritems; ++i)
|
||||
btrfs_key_ptr_to_cpu(&res->node.ptrs[i]);
|
||||
else
|
||||
for (i = 0; i < hdr->nritems; ++i)
|
||||
btrfs_item_to_cpu(&res->leaf.items[i]);
|
||||
|
||||
*buf = res;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int btrfs_search_tree(const struct btrfs_root *root, struct btrfs_key *key,
|
||||
struct btrfs_path *p)
|
||||
static int btrfs_comp_keys(struct btrfs_disk_key *disk,
|
||||
const struct btrfs_key *k2)
|
||||
{
|
||||
u8 lvl, prev_lvl;
|
||||
int i, slot, ret;
|
||||
u64 logical, physical;
|
||||
union btrfs_tree_node *buf;
|
||||
struct btrfs_key k1;
|
||||
|
||||
clear_path(p);
|
||||
btrfs_disk_key_to_cpu(&k1, disk);
|
||||
return btrfs_comp_cpu_keys(&k1, k2);
|
||||
}
|
||||
|
||||
logical = root->bytenr;
|
||||
enum btrfs_tree_block_status
|
||||
btrfs_check_node(struct btrfs_fs_info *fs_info,
|
||||
struct btrfs_disk_key *parent_key, struct extent_buffer *buf)
|
||||
{
|
||||
int i;
|
||||
struct btrfs_key cpukey;
|
||||
struct btrfs_disk_key key;
|
||||
u32 nritems = btrfs_header_nritems(buf);
|
||||
enum btrfs_tree_block_status ret = BTRFS_TREE_BLOCK_INVALID_NRITEMS;
|
||||
|
||||
for (i = 0; i < BTRFS_MAX_LEVEL; ++i) {
|
||||
physical = btrfs_map_logical_to_physical(logical);
|
||||
if (physical == -1ULL)
|
||||
goto err;
|
||||
if (nritems == 0 || nritems > BTRFS_NODEPTRS_PER_BLOCK(fs_info))
|
||||
goto fail;
|
||||
|
||||
if (read_tree_node(physical, &buf))
|
||||
goto err;
|
||||
ret = BTRFS_TREE_BLOCK_INVALID_PARENT_KEY;
|
||||
if (parent_key && parent_key->type) {
|
||||
btrfs_node_key(buf, &key, 0);
|
||||
if (memcmp(parent_key, &key, sizeof(key)))
|
||||
goto fail;
|
||||
}
|
||||
ret = BTRFS_TREE_BLOCK_BAD_KEY_ORDER;
|
||||
for (i = 0; nritems > 1 && i < nritems - 2; i++) {
|
||||
btrfs_node_key(buf, &key, i);
|
||||
btrfs_node_key_to_cpu(buf, &cpukey, i + 1);
|
||||
if (btrfs_comp_keys(&key, &cpukey) >= 0)
|
||||
goto fail;
|
||||
}
|
||||
return BTRFS_TREE_BLOCK_CLEAN;
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
lvl = buf->header.level;
|
||||
if (i && prev_lvl != lvl + 1) {
|
||||
printf("%s: invalid level in header at %llu\n",
|
||||
__func__, logical);
|
||||
goto err;
|
||||
enum btrfs_tree_block_status
|
||||
btrfs_check_leaf(struct btrfs_fs_info *fs_info,
|
||||
struct btrfs_disk_key *parent_key, struct extent_buffer *buf)
|
||||
{
|
||||
int i;
|
||||
struct btrfs_key cpukey;
|
||||
struct btrfs_disk_key key;
|
||||
u32 nritems = btrfs_header_nritems(buf);
|
||||
enum btrfs_tree_block_status ret = BTRFS_TREE_BLOCK_INVALID_NRITEMS;
|
||||
|
||||
if (nritems * sizeof(struct btrfs_item) > buf->len) {
|
||||
fprintf(stderr, "invalid number of items %llu\n",
|
||||
(unsigned long long)buf->start);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (btrfs_header_level(buf) != 0) {
|
||||
ret = BTRFS_TREE_BLOCK_INVALID_LEVEL;
|
||||
fprintf(stderr, "leaf is not a leaf %llu\n",
|
||||
(unsigned long long)btrfs_header_bytenr(buf));
|
||||
goto fail;
|
||||
}
|
||||
if (btrfs_leaf_free_space(buf) < 0) {
|
||||
ret = BTRFS_TREE_BLOCK_INVALID_FREE_SPACE;
|
||||
fprintf(stderr, "leaf free space incorrect %llu %d\n",
|
||||
(unsigned long long)btrfs_header_bytenr(buf),
|
||||
btrfs_leaf_free_space(buf));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (nritems == 0)
|
||||
return BTRFS_TREE_BLOCK_CLEAN;
|
||||
|
||||
btrfs_item_key(buf, &key, 0);
|
||||
if (parent_key && parent_key->type &&
|
||||
memcmp(parent_key, &key, sizeof(key))) {
|
||||
ret = BTRFS_TREE_BLOCK_INVALID_PARENT_KEY;
|
||||
fprintf(stderr, "leaf parent key incorrect %llu\n",
|
||||
(unsigned long long)btrfs_header_bytenr(buf));
|
||||
goto fail;
|
||||
}
|
||||
for (i = 0; nritems > 1 && i < nritems - 1; i++) {
|
||||
btrfs_item_key(buf, &key, i);
|
||||
btrfs_item_key_to_cpu(buf, &cpukey, i + 1);
|
||||
if (btrfs_comp_keys(&key, &cpukey) >= 0) {
|
||||
ret = BTRFS_TREE_BLOCK_BAD_KEY_ORDER;
|
||||
fprintf(stderr, "bad key ordering %d %d\n", i, i+1);
|
||||
goto fail;
|
||||
}
|
||||
prev_lvl = lvl;
|
||||
if (btrfs_item_offset_nr(buf, i) !=
|
||||
btrfs_item_end_nr(buf, i + 1)) {
|
||||
ret = BTRFS_TREE_BLOCK_INVALID_OFFSETS;
|
||||
fprintf(stderr, "incorrect offsets %u %u\n",
|
||||
btrfs_item_offset_nr(buf, i),
|
||||
btrfs_item_end_nr(buf, i + 1));
|
||||
goto fail;
|
||||
}
|
||||
if (i == 0 && btrfs_item_end_nr(buf, i) !=
|
||||
BTRFS_LEAF_DATA_SIZE(fs_info)) {
|
||||
ret = BTRFS_TREE_BLOCK_INVALID_OFFSETS;
|
||||
fprintf(stderr, "bad item end %u wanted %u\n",
|
||||
btrfs_item_end_nr(buf, i),
|
||||
(unsigned)BTRFS_LEAF_DATA_SIZE(fs_info));
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < nritems; i++) {
|
||||
if (btrfs_item_end_nr(buf, i) >
|
||||
BTRFS_LEAF_DATA_SIZE(fs_info)) {
|
||||
btrfs_item_key(buf, &key, 0);
|
||||
ret = BTRFS_TREE_BLOCK_INVALID_OFFSETS;
|
||||
fprintf(stderr, "slot end outside of leaf %llu > %llu\n",
|
||||
(unsigned long long)btrfs_item_end_nr(buf, i),
|
||||
(unsigned long long)BTRFS_LEAF_DATA_SIZE(
|
||||
fs_info));
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
return BTRFS_TREE_BLOCK_CLEAN;
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int noinline check_block(struct btrfs_fs_info *fs_info,
|
||||
struct btrfs_path *path, int level)
|
||||
{
|
||||
struct btrfs_disk_key key;
|
||||
struct btrfs_disk_key *key_ptr = NULL;
|
||||
struct extent_buffer *parent;
|
||||
enum btrfs_tree_block_status ret;
|
||||
|
||||
if (path->nodes[level + 1]) {
|
||||
parent = path->nodes[level + 1];
|
||||
btrfs_node_key(parent, &key, path->slots[level + 1]);
|
||||
key_ptr = &key;
|
||||
}
|
||||
if (level == 0)
|
||||
ret = btrfs_check_leaf(fs_info, key_ptr, path->nodes[0]);
|
||||
else
|
||||
ret = btrfs_check_node(fs_info, key_ptr, path->nodes[level]);
|
||||
if (ret == BTRFS_TREE_BLOCK_CLEAN)
|
||||
return 0;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* search for key in the extent_buffer. The items start at offset p,
|
||||
* and they are item_size apart. There are 'max' items in p.
|
||||
*
|
||||
* the slot in the array is returned via slot, and it points to
|
||||
* the place where you would insert key if it is not found in
|
||||
* the array.
|
||||
*
|
||||
* slot may point to max if the key is bigger than all of the keys
|
||||
*/
|
||||
static int generic_bin_search(struct extent_buffer *eb, unsigned long p,
|
||||
int item_size, const struct btrfs_key *key,
|
||||
int max, int *slot)
|
||||
{
|
||||
int low = 0;
|
||||
int high = max;
|
||||
int mid;
|
||||
int ret;
|
||||
unsigned long offset;
|
||||
struct btrfs_disk_key *tmp;
|
||||
|
||||
while(low < high) {
|
||||
mid = (low + high) / 2;
|
||||
offset = p + mid * item_size;
|
||||
|
||||
tmp = (struct btrfs_disk_key *)(eb->data + offset);
|
||||
ret = btrfs_comp_keys(tmp, key);
|
||||
|
||||
ret = btrfs_bin_search(buf, key, &slot);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
if (ret && slot > 0 && lvl)
|
||||
slot -= 1;
|
||||
|
||||
p->slots[lvl] = slot;
|
||||
p->nodes[lvl] = buf;
|
||||
|
||||
if (lvl) {
|
||||
logical = buf->node.ptrs[slot].blockptr;
|
||||
} else {
|
||||
/*
|
||||
* The path might be invalid if:
|
||||
* cur leaf max < searched value < next leaf min
|
||||
*
|
||||
* Jump to the next valid element if it exists.
|
||||
*/
|
||||
if (slot >= buf->header.nritems)
|
||||
if (btrfs_next_slot(p) < 0)
|
||||
goto err;
|
||||
break;
|
||||
low = mid + 1;
|
||||
else if (ret > 0)
|
||||
high = mid;
|
||||
else {
|
||||
*slot = mid;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
btrfs_free_path(p);
|
||||
return -1;
|
||||
*slot = low;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int jump_leaf(struct btrfs_path *path, int dir)
|
||||
/*
|
||||
* simple bin_search frontend that does the right thing for
|
||||
* leaves vs nodes
|
||||
*/
|
||||
int btrfs_bin_search(struct extent_buffer *eb, const struct btrfs_key *key,
|
||||
int *slot)
|
||||
{
|
||||
struct btrfs_path p;
|
||||
u32 slot;
|
||||
int level = 1, from_level, i;
|
||||
if (btrfs_header_level(eb) == 0)
|
||||
return generic_bin_search(eb,
|
||||
offsetof(struct btrfs_leaf, items),
|
||||
sizeof(struct btrfs_item),
|
||||
key, btrfs_header_nritems(eb),
|
||||
slot);
|
||||
else
|
||||
return generic_bin_search(eb,
|
||||
offsetof(struct btrfs_node, ptrs),
|
||||
sizeof(struct btrfs_key_ptr),
|
||||
key, btrfs_header_nritems(eb),
|
||||
slot);
|
||||
}
|
||||
|
||||
dir = dir >= 0 ? 1 : -1;
|
||||
struct extent_buffer *read_node_slot(struct btrfs_fs_info *fs_info,
|
||||
struct extent_buffer *parent, int slot)
|
||||
{
|
||||
struct extent_buffer *ret;
|
||||
int level = btrfs_header_level(parent);
|
||||
|
||||
p = *path;
|
||||
if (slot < 0)
|
||||
return NULL;
|
||||
if (slot >= btrfs_header_nritems(parent))
|
||||
return NULL;
|
||||
|
||||
while (level < BTRFS_MAX_LEVEL) {
|
||||
if (!p.nodes[level])
|
||||
if (level == 0)
|
||||
return NULL;
|
||||
|
||||
ret = read_tree_block(fs_info, btrfs_node_blockptr(parent, slot),
|
||||
btrfs_node_ptr_generation(parent, slot));
|
||||
if (!extent_buffer_uptodate(ret))
|
||||
return ERR_PTR(-EIO);
|
||||
|
||||
if (btrfs_header_level(ret) != level - 1) {
|
||||
error("child eb corrupted: parent bytenr=%llu item=%d parent level=%d child level=%d",
|
||||
btrfs_header_bytenr(parent), slot,
|
||||
btrfs_header_level(parent), btrfs_header_level(ret));
|
||||
free_extent_buffer(ret);
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btrfs_find_item(struct btrfs_root *fs_root, struct btrfs_path *found_path,
|
||||
u64 iobjectid, u64 ioff, u8 key_type,
|
||||
struct btrfs_key *found_key)
|
||||
{
|
||||
int ret;
|
||||
struct btrfs_key key;
|
||||
struct extent_buffer *eb;
|
||||
struct btrfs_path *path;
|
||||
|
||||
key.type = key_type;
|
||||
key.objectid = iobjectid;
|
||||
key.offset = ioff;
|
||||
|
||||
if (found_path == NULL) {
|
||||
path = btrfs_alloc_path();
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
} else
|
||||
path = found_path;
|
||||
|
||||
ret = btrfs_search_slot(NULL, fs_root, &key, path, 0, 0);
|
||||
if ((ret < 0) || (found_key == NULL))
|
||||
goto out;
|
||||
|
||||
eb = path->nodes[0];
|
||||
if (ret && path->slots[0] >= btrfs_header_nritems(eb)) {
|
||||
ret = btrfs_next_leaf(fs_root, path);
|
||||
if (ret)
|
||||
goto out;
|
||||
eb = path->nodes[0];
|
||||
}
|
||||
|
||||
btrfs_item_key_to_cpu(eb, found_key, path->slots[0]);
|
||||
if (found_key->type != key.type ||
|
||||
found_key->objectid != key.objectid) {
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (path != found_path)
|
||||
btrfs_free_path(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* look for key in the tree. path is filled in with nodes along the way
|
||||
* if key is found, we return zero and you can find the item in the leaf
|
||||
* level of the path (level 0)
|
||||
*
|
||||
* If the key isn't found, the path points to the slot where it should
|
||||
* be inserted, and 1 is returned. If there are other errors during the
|
||||
* search a negative error number is returned.
|
||||
*
|
||||
* if ins_len > 0, nodes and leaves will be split as we walk down the
|
||||
* tree. if ins_len < 0, nodes will be merged as we walk down the tree (if
|
||||
* possible)
|
||||
*
|
||||
* NOTE: This version has no COW ability, thus we expect trans == NULL,
|
||||
* ins_len == 0 and cow == 0.
|
||||
*/
|
||||
int btrfs_search_slot(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, const struct btrfs_key *key,
|
||||
struct btrfs_path *p, int ins_len, int cow)
|
||||
{
|
||||
struct extent_buffer *b;
|
||||
int slot;
|
||||
int ret;
|
||||
int level;
|
||||
struct btrfs_fs_info *fs_info = root->fs_info;
|
||||
u8 lowest_level = 0;
|
||||
|
||||
assert(trans == NULL && ins_len == 0 && cow == 0);
|
||||
lowest_level = p->lowest_level;
|
||||
WARN_ON(lowest_level && ins_len > 0);
|
||||
WARN_ON(p->nodes[0] != NULL);
|
||||
|
||||
b = root->node;
|
||||
extent_buffer_get(b);
|
||||
while (b) {
|
||||
level = btrfs_header_level(b);
|
||||
/*
|
||||
if (cow) {
|
||||
int wret;
|
||||
wret = btrfs_cow_block(trans, root, b,
|
||||
p->nodes[level + 1],
|
||||
p->slots[level + 1],
|
||||
&b);
|
||||
if (wret) {
|
||||
free_extent_buffer(b);
|
||||
return wret;
|
||||
}
|
||||
}
|
||||
*/
|
||||
BUG_ON(!cow && ins_len);
|
||||
if (level != btrfs_header_level(b))
|
||||
WARN_ON(1);
|
||||
level = btrfs_header_level(b);
|
||||
p->nodes[level] = b;
|
||||
ret = check_block(fs_info, p, level);
|
||||
if (ret)
|
||||
return -1;
|
||||
ret = btrfs_bin_search(b, key, &slot);
|
||||
if (level != 0) {
|
||||
if (ret && slot > 0)
|
||||
slot -= 1;
|
||||
p->slots[level] = slot;
|
||||
/*
|
||||
if ((p->search_for_split || ins_len > 0) &&
|
||||
btrfs_header_nritems(b) >=
|
||||
BTRFS_NODEPTRS_PER_BLOCK(fs_info) - 3) {
|
||||
int sret = split_node(trans, root, p, level);
|
||||
BUG_ON(sret > 0);
|
||||
if (sret)
|
||||
return sret;
|
||||
b = p->nodes[level];
|
||||
slot = p->slots[level];
|
||||
} else if (ins_len < 0) {
|
||||
int sret = balance_level(trans, root, p,
|
||||
level);
|
||||
if (sret)
|
||||
return sret;
|
||||
b = p->nodes[level];
|
||||
if (!b) {
|
||||
btrfs_release_path(p);
|
||||
goto again;
|
||||
}
|
||||
slot = p->slots[level];
|
||||
BUG_ON(btrfs_header_nritems(b) == 1);
|
||||
}
|
||||
*/
|
||||
/* this is only true while dropping a snapshot */
|
||||
if (level == lowest_level)
|
||||
break;
|
||||
|
||||
b = read_node_slot(fs_info, b, slot);
|
||||
if (!extent_buffer_uptodate(b))
|
||||
return -EIO;
|
||||
} else {
|
||||
p->slots[level] = slot;
|
||||
/*
|
||||
if (ins_len > 0 &&
|
||||
ins_len > btrfs_leaf_free_space(b)) {
|
||||
int sret = split_leaf(trans, root, key,
|
||||
p, ins_len, ret == 0);
|
||||
BUG_ON(sret > 0);
|
||||
if (sret)
|
||||
return sret;
|
||||
}
|
||||
*/
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper to use instead of search slot if no exact match is needed but
|
||||
* instead the next or previous item should be returned.
|
||||
* When find_higher is true, the next higher item is returned, the next lower
|
||||
* otherwise.
|
||||
* When return_any and find_higher are both true, and no higher item is found,
|
||||
* return the next lower instead.
|
||||
* When return_any is true and find_higher is false, and no lower item is found,
|
||||
* return the next higher instead.
|
||||
* It returns 0 if any item is found, 1 if none is found (tree empty), and
|
||||
* < 0 on error
|
||||
*/
|
||||
int btrfs_search_slot_for_read(struct btrfs_root *root,
|
||||
const struct btrfs_key *key,
|
||||
struct btrfs_path *p, int find_higher,
|
||||
int return_any)
|
||||
{
|
||||
int ret;
|
||||
struct extent_buffer *leaf;
|
||||
|
||||
again:
|
||||
ret = btrfs_search_slot(NULL, root, key, p, 0, 0);
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
/*
|
||||
* A return value of 1 means the path is at the position where the item
|
||||
* should be inserted. Normally this is the next bigger item, but in
|
||||
* case the previous item is the last in a leaf, path points to the
|
||||
* first free slot in the previous leaf, i.e. at an invalid item.
|
||||
*/
|
||||
leaf = p->nodes[0];
|
||||
|
||||
if (find_higher) {
|
||||
if (p->slots[0] >= btrfs_header_nritems(leaf)) {
|
||||
ret = btrfs_next_leaf(root, p);
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
if (!return_any)
|
||||
return 1;
|
||||
/*
|
||||
* No higher item found, return the next lower instead
|
||||
*/
|
||||
return_any = 0;
|
||||
find_higher = 0;
|
||||
btrfs_release_path(p);
|
||||
goto again;
|
||||
}
|
||||
} else {
|
||||
if (p->slots[0] == 0) {
|
||||
ret = btrfs_prev_leaf(root, p);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (!ret) {
|
||||
leaf = p->nodes[0];
|
||||
if (p->slots[0] == btrfs_header_nritems(leaf))
|
||||
p->slots[0]--;
|
||||
return 0;
|
||||
}
|
||||
if (!return_any)
|
||||
return 1;
|
||||
/*
|
||||
* No lower item found, return the next higher instead
|
||||
*/
|
||||
return_any = 0;
|
||||
find_higher = 1;
|
||||
btrfs_release_path(p);
|
||||
goto again;
|
||||
} else {
|
||||
--p->slots[0];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* how many bytes are required to store the items in a leaf. start
|
||||
* and nr indicate which items in the leaf to check. This totals up the
|
||||
* space used both by the item structs and the item data
|
||||
*/
|
||||
static int leaf_space_used(struct extent_buffer *l, int start, int nr)
|
||||
{
|
||||
int data_len;
|
||||
int nritems = btrfs_header_nritems(l);
|
||||
int end = min(nritems, start + nr) - 1;
|
||||
|
||||
if (!nr)
|
||||
return 0;
|
||||
data_len = btrfs_item_end_nr(l, start);
|
||||
data_len = data_len - btrfs_item_offset_nr(l, end);
|
||||
data_len += sizeof(struct btrfs_item) * nr;
|
||||
WARN_ON(data_len < 0);
|
||||
return data_len;
|
||||
}
|
||||
|
||||
/*
|
||||
* The space between the end of the leaf items and
|
||||
* the start of the leaf data. IOW, how much room
|
||||
* the leaf has left for both items and data
|
||||
*/
|
||||
int btrfs_leaf_free_space(struct extent_buffer *leaf)
|
||||
{
|
||||
int nritems = btrfs_header_nritems(leaf);
|
||||
u32 leaf_data_size;
|
||||
int ret;
|
||||
|
||||
BUG_ON(leaf->fs_info && leaf->fs_info->nodesize != leaf->len);
|
||||
leaf_data_size = __BTRFS_LEAF_DATA_SIZE(leaf->len);
|
||||
ret = leaf_data_size - leaf_space_used(leaf, 0 ,nritems);
|
||||
if (ret < 0) {
|
||||
printk("leaf free space ret %d, leaf data size %u, used %d nritems %d\n",
|
||||
ret, leaf_data_size, leaf_space_used(leaf, 0, nritems),
|
||||
nritems);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* walk up the tree as far as required to find the previous leaf.
|
||||
* returns 0 if it found something or 1 if there are no lesser leaves.
|
||||
* returns < 0 on io errors.
|
||||
*/
|
||||
int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path)
|
||||
{
|
||||
int slot;
|
||||
int level = 1;
|
||||
struct extent_buffer *c;
|
||||
struct extent_buffer *next = NULL;
|
||||
struct btrfs_fs_info *fs_info = root->fs_info;
|
||||
|
||||
while(level < BTRFS_MAX_LEVEL) {
|
||||
if (!path->nodes[level])
|
||||
return 1;
|
||||
|
||||
slot = p.slots[level];
|
||||
if ((dir > 0 && slot + dir >= p.nodes[level]->header.nritems)
|
||||
|| (dir < 0 && !slot))
|
||||
slot = path->slots[level];
|
||||
c = path->nodes[level];
|
||||
if (slot == 0) {
|
||||
level++;
|
||||
else
|
||||
if (level == BTRFS_MAX_LEVEL)
|
||||
return 1;
|
||||
continue;
|
||||
}
|
||||
slot--;
|
||||
|
||||
next = read_node_slot(fs_info, c, slot);
|
||||
if (!extent_buffer_uptodate(next)) {
|
||||
if (IS_ERR(next))
|
||||
return PTR_ERR(next);
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
}
|
||||
path->slots[level] = slot;
|
||||
while(1) {
|
||||
level--;
|
||||
c = path->nodes[level];
|
||||
free_extent_buffer(c);
|
||||
slot = btrfs_header_nritems(next);
|
||||
if (slot != 0)
|
||||
slot--;
|
||||
path->nodes[level] = next;
|
||||
path->slots[level] = slot;
|
||||
if (!level)
|
||||
break;
|
||||
next = read_node_slot(fs_info, next, slot);
|
||||
if (!extent_buffer_uptodate(next)) {
|
||||
if (IS_ERR(next))
|
||||
return PTR_ERR(next);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Walk up the tree as far as necessary to find the next sibling tree block.
|
||||
* More generic version of btrfs_next_leaf(), as it could find sibling nodes
|
||||
* if @path->lowest_level is not 0.
|
||||
*
|
||||
* returns 0 if it found something or 1 if there are no greater leaves.
|
||||
* returns < 0 on io errors.
|
||||
*/
|
||||
int btrfs_next_sibling_tree_block(struct btrfs_fs_info *fs_info,
|
||||
struct btrfs_path *path)
|
||||
{
|
||||
int slot;
|
||||
int level = path->lowest_level + 1;
|
||||
struct extent_buffer *c;
|
||||
struct extent_buffer *next = NULL;
|
||||
|
||||
BUG_ON(path->lowest_level + 1 >= BTRFS_MAX_LEVEL);
|
||||
do {
|
||||
if (!path->nodes[level])
|
||||
return 1;
|
||||
|
||||
slot = path->slots[level] + 1;
|
||||
c = path->nodes[level];
|
||||
if (slot >= btrfs_header_nritems(c)) {
|
||||
level++;
|
||||
if (level == BTRFS_MAX_LEVEL)
|
||||
return 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
next = read_node_slot(fs_info, c, slot);
|
||||
if (!extent_buffer_uptodate(next))
|
||||
return -EIO;
|
||||
break;
|
||||
} while (level < BTRFS_MAX_LEVEL);
|
||||
path->slots[level] = slot;
|
||||
while(1) {
|
||||
level--;
|
||||
c = path->nodes[level];
|
||||
free_extent_buffer(c);
|
||||
path->nodes[level] = next;
|
||||
path->slots[level] = 0;
|
||||
if (level == path->lowest_level)
|
||||
break;
|
||||
next = read_node_slot(fs_info, next, 0);
|
||||
if (!extent_buffer_uptodate(next))
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int btrfs_previous_item(struct btrfs_root *root,
|
||||
struct btrfs_path *path, u64 min_objectid,
|
||||
int type)
|
||||
{
|
||||
struct btrfs_key found_key;
|
||||
struct extent_buffer *leaf;
|
||||
u32 nritems;
|
||||
int ret;
|
||||
|
||||
while(1) {
|
||||
if (path->slots[0] == 0) {
|
||||
ret = btrfs_prev_leaf(root, path);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
} else {
|
||||
path->slots[0]--;
|
||||
}
|
||||
leaf = path->nodes[0];
|
||||
nritems = btrfs_header_nritems(leaf);
|
||||
if (nritems == 0)
|
||||
return 1;
|
||||
if (path->slots[0] == nritems)
|
||||
path->slots[0]--;
|
||||
|
||||
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
|
||||
if (found_key.objectid < min_objectid)
|
||||
break;
|
||||
if (found_key.type == type)
|
||||
return 0;
|
||||
if (found_key.objectid == min_objectid &&
|
||||
found_key.type < type)
|
||||
break;
|
||||
}
|
||||
|
||||
if (level == BTRFS_MAX_LEVEL)
|
||||
return 1;
|
||||
|
||||
p.slots[level] = slot + dir;
|
||||
level--;
|
||||
from_level = level;
|
||||
|
||||
while (level >= 0) {
|
||||
u64 logical, physical;
|
||||
|
||||
slot = p.slots[level + 1];
|
||||
logical = p.nodes[level + 1]->node.ptrs[slot].blockptr;
|
||||
physical = btrfs_map_logical_to_physical(logical);
|
||||
if (physical == -1ULL)
|
||||
goto err;
|
||||
|
||||
if (read_tree_node(physical, &p.nodes[level]))
|
||||
goto err;
|
||||
|
||||
if (dir > 0)
|
||||
p.slots[level] = 0;
|
||||
else
|
||||
p.slots[level] = p.nodes[level]->header.nritems - 1;
|
||||
level--;
|
||||
}
|
||||
|
||||
/* Free rewritten nodes in path */
|
||||
for (i = 0; i <= from_level; ++i)
|
||||
free(path->nodes[i]);
|
||||
|
||||
*path = p;
|
||||
return 0;
|
||||
|
||||
err:
|
||||
/* Free rewritten nodes in p */
|
||||
for (i = level + 1; i <= from_level; ++i)
|
||||
free(p.nodes[i]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int btrfs_prev_slot(struct btrfs_path *p)
|
||||
{
|
||||
if (!p->slots[0])
|
||||
return jump_leaf(p, -1);
|
||||
|
||||
p->slots[0]--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int btrfs_next_slot(struct btrfs_path *p)
|
||||
{
|
||||
struct btrfs_leaf *leaf = &p->nodes[0]->leaf;
|
||||
|
||||
if (p->slots[0] + 1 >= leaf->header.nritems)
|
||||
return jump_leaf(p, 1);
|
||||
|
||||
p->slots[0]++;
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
|
1491
fs/btrfs/ctree.h
1491
fs/btrfs/ctree.h
File diff suppressed because it is too large
Load diff
|
@ -6,119 +6,163 @@
|
|||
*/
|
||||
|
||||
#include "btrfs.h"
|
||||
#include "disk-io.h"
|
||||
|
||||
static int verify_dir_item(struct btrfs_dir_item *item, u32 start, u32 total)
|
||||
static int verify_dir_item(struct btrfs_root *root,
|
||||
struct extent_buffer *leaf,
|
||||
struct btrfs_dir_item *dir_item)
|
||||
{
|
||||
u16 max_len = BTRFS_NAME_LEN;
|
||||
u32 end;
|
||||
u16 namelen = BTRFS_NAME_LEN;
|
||||
u8 type = btrfs_dir_type(leaf, dir_item);
|
||||
|
||||
if (item->type >= BTRFS_FT_MAX) {
|
||||
printf("%s: invalid dir item type: %i\n", __func__, item->type);
|
||||
if (type == BTRFS_FT_XATTR)
|
||||
namelen = XATTR_NAME_MAX;
|
||||
|
||||
if (btrfs_dir_name_len(leaf, dir_item) > namelen) {
|
||||
fprintf(stderr, "invalid dir item name len: %u\n",
|
||||
(unsigned)btrfs_dir_data_len(leaf, dir_item));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (item->type == BTRFS_FT_XATTR)
|
||||
max_len = 255; /* XATTR_NAME_MAX */
|
||||
|
||||
end = start + sizeof(*item) + item->name_len;
|
||||
if (item->name_len > max_len || end > total) {
|
||||
printf("%s: invalid dir item name len: %u\n", __func__,
|
||||
item->name_len);
|
||||
/* BTRFS_MAX_XATTR_SIZE is the same for all dir items */
|
||||
if ((btrfs_dir_data_len(leaf, dir_item) +
|
||||
btrfs_dir_name_len(leaf, dir_item)) >
|
||||
BTRFS_MAX_XATTR_SIZE(root->fs_info)) {
|
||||
fprintf(stderr, "invalid dir item name + data len: %u + %u\n",
|
||||
(unsigned)btrfs_dir_name_len(leaf, dir_item),
|
||||
(unsigned)btrfs_dir_data_len(leaf, dir_item));
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct btrfs_dir_item *
|
||||
btrfs_match_dir_item_name(struct btrfs_path *path, const char *name,
|
||||
int name_len)
|
||||
struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_root *root,
|
||||
struct btrfs_path *path,
|
||||
const char *name, int name_len)
|
||||
{
|
||||
struct btrfs_dir_item *item;
|
||||
u32 total_len, cur = 0, this_len;
|
||||
const char *name_ptr;
|
||||
struct btrfs_dir_item *dir_item;
|
||||
unsigned long name_ptr;
|
||||
u32 total_len;
|
||||
u32 cur = 0;
|
||||
u32 this_len;
|
||||
struct extent_buffer *leaf;
|
||||
|
||||
item = btrfs_path_item_ptr(path, struct btrfs_dir_item);
|
||||
leaf = path->nodes[0];
|
||||
dir_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item);
|
||||
total_len = btrfs_item_size_nr(leaf, path->slots[0]);
|
||||
if (verify_dir_item(root, leaf, dir_item))
|
||||
return NULL;
|
||||
|
||||
total_len = btrfs_path_item_size(path);
|
||||
|
||||
while (cur < total_len) {
|
||||
btrfs_dir_item_to_cpu(item);
|
||||
this_len = sizeof(*item) + item->name_len + item->data_len;
|
||||
name_ptr = (const char *) (item + 1);
|
||||
|
||||
if (verify_dir_item(item, cur, total_len))
|
||||
while(cur < total_len) {
|
||||
this_len = sizeof(*dir_item) +
|
||||
btrfs_dir_name_len(leaf, dir_item) +
|
||||
btrfs_dir_data_len(leaf, dir_item);
|
||||
if (this_len > (total_len - cur)) {
|
||||
fprintf(stderr, "invalid dir item size\n");
|
||||
return NULL;
|
||||
if (item->name_len == name_len && !memcmp(name_ptr, name,
|
||||
name_len))
|
||||
return item;
|
||||
}
|
||||
|
||||
name_ptr = (unsigned long)(dir_item + 1);
|
||||
|
||||
if (btrfs_dir_name_len(leaf, dir_item) == name_len &&
|
||||
memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0)
|
||||
return dir_item;
|
||||
|
||||
cur += this_len;
|
||||
item = (struct btrfs_dir_item *) ((u8 *) item + this_len);
|
||||
dir_item = (struct btrfs_dir_item *)((char *)dir_item +
|
||||
this_len);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int btrfs_lookup_dir_item(const struct btrfs_root *root, u64 dir,
|
||||
const char *name, int name_len,
|
||||
struct btrfs_dir_item *item)
|
||||
struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_path *path, u64 dir,
|
||||
const char *name, int name_len,
|
||||
int mod)
|
||||
{
|
||||
struct btrfs_path path;
|
||||
int ret;
|
||||
struct btrfs_key key;
|
||||
struct btrfs_dir_item *res = NULL;
|
||||
int ins_len = mod < 0 ? -1 : 0;
|
||||
int cow = mod != 0;
|
||||
struct btrfs_key found_key;
|
||||
struct extent_buffer *leaf;
|
||||
|
||||
key.objectid = dir;
|
||||
key.type = BTRFS_DIR_ITEM_KEY;
|
||||
|
||||
key.offset = btrfs_name_hash(name, name_len);
|
||||
|
||||
if (btrfs_search_tree(root, &key, &path))
|
||||
return -1;
|
||||
ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
if (ret > 0) {
|
||||
if (path->slots[0] == 0)
|
||||
return NULL;
|
||||
path->slots[0]--;
|
||||
}
|
||||
|
||||
if (btrfs_comp_keys_type(&key, btrfs_path_leaf_key(&path)))
|
||||
goto out;
|
||||
leaf = path->nodes[0];
|
||||
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
|
||||
|
||||
res = btrfs_match_dir_item_name(&path, name, name_len);
|
||||
if (res)
|
||||
*item = *res;
|
||||
out:
|
||||
btrfs_free_path(&path);
|
||||
return res ? 0 : -1;
|
||||
if (found_key.objectid != dir ||
|
||||
found_key.type != BTRFS_DIR_ITEM_KEY ||
|
||||
found_key.offset != key.offset)
|
||||
return NULL;
|
||||
|
||||
return btrfs_match_dir_item_name(root, path, name, name_len);
|
||||
}
|
||||
|
||||
int btrfs_readdir(const struct btrfs_root *root, u64 dir,
|
||||
btrfs_readdir_callback_t callback)
|
||||
int btrfs_iter_dir(struct btrfs_root *root, u64 ino,
|
||||
btrfs_iter_dir_callback_t callback)
|
||||
{
|
||||
struct btrfs_path path;
|
||||
struct btrfs_key key, *found_key;
|
||||
struct btrfs_dir_item *item;
|
||||
int res = 0;
|
||||
struct btrfs_key key;
|
||||
int ret;
|
||||
|
||||
key.objectid = dir;
|
||||
btrfs_init_path(&path);
|
||||
key.objectid = ino;
|
||||
key.type = BTRFS_DIR_INDEX_KEY;
|
||||
key.offset = 0;
|
||||
|
||||
if (btrfs_search_tree(root, &key, &path))
|
||||
return -1;
|
||||
|
||||
ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* Should not happen */
|
||||
if (ret == 0) {
|
||||
ret = -EUCLEAN;
|
||||
goto out;
|
||||
}
|
||||
if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
|
||||
ret = btrfs_next_leaf(root, &path);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (ret > 0) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
do {
|
||||
found_key = btrfs_path_leaf_key(&path);
|
||||
if (btrfs_comp_keys_type(&key, found_key))
|
||||
struct btrfs_dir_item *di;
|
||||
|
||||
btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
|
||||
if (key.objectid != ino || key.type != BTRFS_DIR_INDEX_KEY)
|
||||
break;
|
||||
di = btrfs_item_ptr(path.nodes[0], path.slots[0],
|
||||
struct btrfs_dir_item);
|
||||
if (verify_dir_item(root, path.nodes[0], di)) {
|
||||
ret = -EUCLEAN;
|
||||
goto out;
|
||||
}
|
||||
ret = callback(root, path.nodes[0], di);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
} while (!(ret = btrfs_next_item(root, &path)));
|
||||
|
||||
item = btrfs_path_item_ptr(&path, struct btrfs_dir_item);
|
||||
btrfs_dir_item_to_cpu(item);
|
||||
|
||||
if (verify_dir_item(item, 0, sizeof(*item) + item->name_len))
|
||||
continue;
|
||||
if (item->type == BTRFS_FT_XATTR)
|
||||
continue;
|
||||
|
||||
if (callback(root, item))
|
||||
break;
|
||||
} while (!(res = btrfs_next_slot(&path)));
|
||||
|
||||
btrfs_free_path(&path);
|
||||
|
||||
return res < 0 ? -1 : 0;
|
||||
if (ret > 0)
|
||||
ret = 0;
|
||||
out:
|
||||
btrfs_release_path(&path);
|
||||
return ret;
|
||||
}
|
||||
|
|
1062
fs/btrfs/disk-io.c
Normal file
1062
fs/btrfs/disk-io.c
Normal file
File diff suppressed because it is too large
Load diff
50
fs/btrfs/disk-io.h
Normal file
50
fs/btrfs/disk-io.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
#ifndef __BTRFS_DISK_IO_H__
|
||||
#define __BTRFS_DISK_IO_H__
|
||||
|
||||
#include <linux/sizes.h>
|
||||
#include <fs_internal.h>
|
||||
#include "ctree.h"
|
||||
#include "disk-io.h"
|
||||
|
||||
#define BTRFS_SUPER_INFO_OFFSET SZ_64K
|
||||
#define BTRFS_SUPER_INFO_SIZE SZ_4K
|
||||
|
||||
/* From btrfs-progs */
|
||||
int read_whole_eb(struct btrfs_fs_info *info, struct extent_buffer *eb, int mirror);
|
||||
struct extent_buffer* read_tree_block(struct btrfs_fs_info *fs_info, u64 bytenr,
|
||||
u64 parent_transid);
|
||||
|
||||
int read_extent_data(struct btrfs_fs_info *fs_info, char *data, u64 logical,
|
||||
u64 *len, int mirror);
|
||||
struct extent_buffer* btrfs_find_create_tree_block(
|
||||
struct btrfs_fs_info *fs_info, u64 bytenr);
|
||||
struct extent_buffer *btrfs_find_tree_block(struct btrfs_fs_info *fs_info,
|
||||
u64 bytenr, u32 blocksize);
|
||||
struct btrfs_root *btrfs_read_fs_root_no_cache(struct btrfs_fs_info *fs_info,
|
||||
struct btrfs_key *location);
|
||||
struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
|
||||
struct btrfs_key *location);
|
||||
|
||||
void btrfs_setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info,
|
||||
u64 objectid);
|
||||
|
||||
void btrfs_free_fs_info(struct btrfs_fs_info *fs_info);
|
||||
struct btrfs_fs_info *btrfs_new_fs_info(void);
|
||||
int btrfs_check_fs_compatibility(struct btrfs_super_block *sb);
|
||||
int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info);
|
||||
void btrfs_release_all_roots(struct btrfs_fs_info *fs_info);
|
||||
void btrfs_cleanup_all_caches(struct btrfs_fs_info *fs_info);
|
||||
|
||||
struct btrfs_fs_info *open_ctree_fs_info(struct blk_desc *desc,
|
||||
struct disk_partition *part);
|
||||
int close_ctree_fs_info(struct btrfs_fs_info *fs_info);
|
||||
|
||||
int btrfs_read_dev_super(struct blk_desc *desc, struct disk_partition *part,
|
||||
struct btrfs_super_block *sb);
|
||||
int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid);
|
||||
int btrfs_set_buffer_uptodate(struct extent_buffer *buf);
|
||||
int btrfs_csum_data(u16 csum_type, const u8 *data, u8 *out, size_t len);
|
||||
int csum_tree_block_size(struct extent_buffer *buf, u16 csum_sectorsize,
|
||||
int verify, u16 csum_type);
|
||||
#endif
|
318
fs/btrfs/extent-cache.c
Normal file
318
fs/btrfs/extent-cache.c
Normal file
|
@ -0,0 +1,318 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
/*
|
||||
* Crossported from the same named file of btrfs-progs.
|
||||
*
|
||||
* Minor modification to include headers.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/bug.h>
|
||||
#include <stdlib.h>
|
||||
#include "extent-cache.h"
|
||||
#include "common/rbtree-utils.h"
|
||||
|
||||
struct cache_extent_search_range {
|
||||
u64 objectid;
|
||||
u64 start;
|
||||
u64 size;
|
||||
};
|
||||
|
||||
static int cache_tree_comp_range(struct rb_node *node, void *data)
|
||||
{
|
||||
struct cache_extent *entry;
|
||||
struct cache_extent_search_range *range;
|
||||
|
||||
range = (struct cache_extent_search_range *)data;
|
||||
entry = rb_entry(node, struct cache_extent, rb_node);
|
||||
|
||||
if (entry->start + entry->size <= range->start)
|
||||
return 1;
|
||||
else if (range->start + range->size <= entry->start)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cache_tree_comp_nodes(struct rb_node *node1, struct rb_node *node2)
|
||||
{
|
||||
struct cache_extent *entry;
|
||||
struct cache_extent_search_range range;
|
||||
|
||||
entry = rb_entry(node2, struct cache_extent, rb_node);
|
||||
range.start = entry->start;
|
||||
range.size = entry->size;
|
||||
|
||||
return cache_tree_comp_range(node1, (void *)&range);
|
||||
}
|
||||
|
||||
static int cache_tree_comp_range2(struct rb_node *node, void *data)
|
||||
{
|
||||
struct cache_extent *entry;
|
||||
struct cache_extent_search_range *range;
|
||||
|
||||
range = (struct cache_extent_search_range *)data;
|
||||
entry = rb_entry(node, struct cache_extent, rb_node);
|
||||
|
||||
if (entry->objectid < range->objectid)
|
||||
return 1;
|
||||
else if (entry->objectid > range->objectid)
|
||||
return -1;
|
||||
else if (entry->start + entry->size <= range->start)
|
||||
return 1;
|
||||
else if (range->start + range->size <= entry->start)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cache_tree_comp_nodes2(struct rb_node *node1, struct rb_node *node2)
|
||||
{
|
||||
struct cache_extent *entry;
|
||||
struct cache_extent_search_range range;
|
||||
|
||||
entry = rb_entry(node2, struct cache_extent, rb_node);
|
||||
range.objectid = entry->objectid;
|
||||
range.start = entry->start;
|
||||
range.size = entry->size;
|
||||
|
||||
return cache_tree_comp_range2(node1, (void *)&range);
|
||||
}
|
||||
|
||||
void cache_tree_init(struct cache_tree *tree)
|
||||
{
|
||||
tree->root = RB_ROOT;
|
||||
}
|
||||
|
||||
static struct cache_extent *alloc_cache_extent(u64 start, u64 size)
|
||||
{
|
||||
struct cache_extent *pe = malloc(sizeof(*pe));
|
||||
|
||||
if (!pe)
|
||||
return pe;
|
||||
|
||||
pe->objectid = 0;
|
||||
pe->start = start;
|
||||
pe->size = size;
|
||||
return pe;
|
||||
}
|
||||
|
||||
int add_cache_extent(struct cache_tree *tree, u64 start, u64 size)
|
||||
{
|
||||
struct cache_extent *pe = alloc_cache_extent(start, size);
|
||||
int ret;
|
||||
|
||||
if (!pe)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = insert_cache_extent(tree, pe);
|
||||
if (ret)
|
||||
free(pe);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int insert_cache_extent(struct cache_tree *tree, struct cache_extent *pe)
|
||||
{
|
||||
return rb_insert(&tree->root, &pe->rb_node, cache_tree_comp_nodes);
|
||||
}
|
||||
|
||||
int insert_cache_extent2(struct cache_tree *tree, struct cache_extent *pe)
|
||||
{
|
||||
return rb_insert(&tree->root, &pe->rb_node, cache_tree_comp_nodes2);
|
||||
}
|
||||
|
||||
struct cache_extent *lookup_cache_extent(struct cache_tree *tree,
|
||||
u64 start, u64 size)
|
||||
{
|
||||
struct rb_node *node;
|
||||
struct cache_extent *entry;
|
||||
struct cache_extent_search_range range;
|
||||
|
||||
range.start = start;
|
||||
range.size = size;
|
||||
node = rb_search(&tree->root, &range, cache_tree_comp_range, NULL);
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
entry = rb_entry(node, struct cache_extent, rb_node);
|
||||
return entry;
|
||||
}
|
||||
|
||||
struct cache_extent *lookup_cache_extent2(struct cache_tree *tree,
|
||||
u64 objectid, u64 start, u64 size)
|
||||
{
|
||||
struct rb_node *node;
|
||||
struct cache_extent *entry;
|
||||
struct cache_extent_search_range range;
|
||||
|
||||
range.objectid = objectid;
|
||||
range.start = start;
|
||||
range.size = size;
|
||||
node = rb_search(&tree->root, &range, cache_tree_comp_range2, NULL);
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
entry = rb_entry(node, struct cache_extent, rb_node);
|
||||
return entry;
|
||||
}
|
||||
|
||||
struct cache_extent *search_cache_extent(struct cache_tree *tree, u64 start)
|
||||
{
|
||||
struct rb_node *next;
|
||||
struct rb_node *node;
|
||||
struct cache_extent *entry;
|
||||
struct cache_extent_search_range range;
|
||||
|
||||
range.start = start;
|
||||
range.size = 1;
|
||||
node = rb_search(&tree->root, &range, cache_tree_comp_range, &next);
|
||||
if (!node)
|
||||
node = next;
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
entry = rb_entry(node, struct cache_extent, rb_node);
|
||||
return entry;
|
||||
}
|
||||
|
||||
struct cache_extent *search_cache_extent2(struct cache_tree *tree,
|
||||
u64 objectid, u64 start)
|
||||
{
|
||||
struct rb_node *next;
|
||||
struct rb_node *node;
|
||||
struct cache_extent *entry;
|
||||
struct cache_extent_search_range range;
|
||||
|
||||
range.objectid = objectid;
|
||||
range.start = start;
|
||||
range.size = 1;
|
||||
node = rb_search(&tree->root, &range, cache_tree_comp_range2, &next);
|
||||
if (!node)
|
||||
node = next;
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
entry = rb_entry(node, struct cache_extent, rb_node);
|
||||
return entry;
|
||||
}
|
||||
|
||||
struct cache_extent *first_cache_extent(struct cache_tree *tree)
|
||||
{
|
||||
struct rb_node *node = rb_first(&tree->root);
|
||||
|
||||
if (!node)
|
||||
return NULL;
|
||||
return rb_entry(node, struct cache_extent, rb_node);
|
||||
}
|
||||
|
||||
struct cache_extent *last_cache_extent(struct cache_tree *tree)
|
||||
{
|
||||
struct rb_node *node = rb_last(&tree->root);
|
||||
|
||||
if (!node)
|
||||
return NULL;
|
||||
return rb_entry(node, struct cache_extent, rb_node);
|
||||
}
|
||||
|
||||
struct cache_extent *prev_cache_extent(struct cache_extent *pe)
|
||||
{
|
||||
struct rb_node *node = rb_prev(&pe->rb_node);
|
||||
|
||||
if (!node)
|
||||
return NULL;
|
||||
return rb_entry(node, struct cache_extent, rb_node);
|
||||
}
|
||||
|
||||
struct cache_extent *next_cache_extent(struct cache_extent *pe)
|
||||
{
|
||||
struct rb_node *node = rb_next(&pe->rb_node);
|
||||
|
||||
if (!node)
|
||||
return NULL;
|
||||
return rb_entry(node, struct cache_extent, rb_node);
|
||||
}
|
||||
|
||||
void remove_cache_extent(struct cache_tree *tree, struct cache_extent *pe)
|
||||
{
|
||||
rb_erase(&pe->rb_node, &tree->root);
|
||||
}
|
||||
|
||||
void cache_tree_free_extents(struct cache_tree *tree,
|
||||
free_cache_extent free_func)
|
||||
{
|
||||
struct cache_extent *ce;
|
||||
|
||||
while ((ce = first_cache_extent(tree))) {
|
||||
remove_cache_extent(tree, ce);
|
||||
free_func(ce);
|
||||
}
|
||||
}
|
||||
|
||||
static void free_extent_cache(struct cache_extent *pe)
|
||||
{
|
||||
free(pe);
|
||||
}
|
||||
|
||||
void free_extent_cache_tree(struct cache_tree *tree)
|
||||
{
|
||||
cache_tree_free_extents(tree, free_extent_cache);
|
||||
}
|
||||
|
||||
int add_merge_cache_extent(struct cache_tree *tree, u64 start, u64 size)
|
||||
{
|
||||
struct cache_extent *cache;
|
||||
struct cache_extent *next = NULL;
|
||||
struct cache_extent *prev = NULL;
|
||||
int next_merged = 0;
|
||||
int prev_merged = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (cache_tree_empty(tree))
|
||||
goto insert;
|
||||
|
||||
cache = search_cache_extent(tree, start);
|
||||
if (!cache) {
|
||||
/*
|
||||
* Either the tree is completely empty, or the no range after
|
||||
* start.
|
||||
* Either way, the last cache_extent should be prev.
|
||||
*/
|
||||
prev = last_cache_extent(tree);
|
||||
} else if (start <= cache->start) {
|
||||
next = cache;
|
||||
prev = prev_cache_extent(cache);
|
||||
} else {
|
||||
prev = cache;
|
||||
next = next_cache_extent(cache);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure the range to be inserted won't cover with existings
|
||||
* Or we will need extra loop to do merge
|
||||
*/
|
||||
BUG_ON(next && start + size > next->start);
|
||||
BUG_ON(prev && prev->start + prev->size > start);
|
||||
|
||||
if (next && start + size == next->start) {
|
||||
next_merged = 1;
|
||||
next->size = next->start + next->size - start;
|
||||
next->start = start;
|
||||
}
|
||||
if (prev && prev->start + prev->size == start) {
|
||||
prev_merged = 1;
|
||||
if (next_merged) {
|
||||
next->size = next->start + next->size - prev->start;
|
||||
next->start = prev->start;
|
||||
remove_cache_extent(tree, prev);
|
||||
free(prev);
|
||||
} else {
|
||||
prev->size = start + size - prev->start;
|
||||
}
|
||||
}
|
||||
insert:
|
||||
if (!prev_merged && !next_merged)
|
||||
ret = add_cache_extent(tree, start, size);
|
||||
return ret;
|
||||
}
|
104
fs/btrfs/extent-cache.h
Normal file
104
fs/btrfs/extent-cache.h
Normal file
|
@ -0,0 +1,104 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
/*
|
||||
* Crossported from the same named file of btrfs-progs.
|
||||
*
|
||||
* Minor modification to include headers.
|
||||
*/
|
||||
#ifndef __BTRFS_EXTENT_CACHE_H__
|
||||
#define __BTRFS_EXTENT_CACHE_H__
|
||||
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct cache_tree {
|
||||
struct rb_root root;
|
||||
};
|
||||
|
||||
struct cache_extent {
|
||||
struct rb_node rb_node;
|
||||
u64 objectid;
|
||||
u64 start;
|
||||
u64 size;
|
||||
};
|
||||
|
||||
void cache_tree_init(struct cache_tree *tree);
|
||||
|
||||
struct cache_extent *first_cache_extent(struct cache_tree *tree);
|
||||
struct cache_extent *last_cache_extent(struct cache_tree *tree);
|
||||
struct cache_extent *prev_cache_extent(struct cache_extent *pe);
|
||||
struct cache_extent *next_cache_extent(struct cache_extent *pe);
|
||||
|
||||
/*
|
||||
* Find a cache_extent which covers start.
|
||||
*
|
||||
* If not found, return next cache_extent if possible.
|
||||
*/
|
||||
struct cache_extent *search_cache_extent(struct cache_tree *tree, u64 start);
|
||||
|
||||
/*
|
||||
* Find a cache_extent which restrictly covers start.
|
||||
*
|
||||
* If not found, return NULL.
|
||||
*/
|
||||
struct cache_extent *lookup_cache_extent(struct cache_tree *tree,
|
||||
u64 start, u64 size);
|
||||
|
||||
/*
|
||||
* Add an non-overlap extent into cache tree
|
||||
*
|
||||
* If [start, start+size) overlap with existing one, it will return -EEXIST.
|
||||
*/
|
||||
int add_cache_extent(struct cache_tree *tree, u64 start, u64 size);
|
||||
|
||||
/*
|
||||
* Same with add_cache_extent, but with cache_extent strcut.
|
||||
*/
|
||||
int insert_cache_extent(struct cache_tree *tree, struct cache_extent *pe);
|
||||
void remove_cache_extent(struct cache_tree *tree, struct cache_extent *pe);
|
||||
|
||||
static inline int cache_tree_empty(struct cache_tree *tree)
|
||||
{
|
||||
return RB_EMPTY_ROOT(&tree->root);
|
||||
}
|
||||
|
||||
typedef void (*free_cache_extent)(struct cache_extent *pe);
|
||||
|
||||
void cache_tree_free_extents(struct cache_tree *tree,
|
||||
free_cache_extent free_func);
|
||||
|
||||
#define FREE_EXTENT_CACHE_BASED_TREE(name, free_func) \
|
||||
static void free_##name##_tree(struct cache_tree *tree) \
|
||||
{ \
|
||||
cache_tree_free_extents(tree, free_func); \
|
||||
}
|
||||
|
||||
void free_extent_cache_tree(struct cache_tree *tree);
|
||||
|
||||
/*
|
||||
* Search a cache_extent with same objectid, and covers start.
|
||||
*
|
||||
* If not found, return next if possible.
|
||||
*/
|
||||
struct cache_extent *search_cache_extent2(struct cache_tree *tree,
|
||||
u64 objectid, u64 start);
|
||||
/*
|
||||
* Search a cache_extent with same objectid, and covers the range
|
||||
* [start, start + size)
|
||||
*
|
||||
* If not found, return next cache_extent if possible.
|
||||
*/
|
||||
struct cache_extent *lookup_cache_extent2(struct cache_tree *tree,
|
||||
u64 objectid, u64 start, u64 size);
|
||||
int insert_cache_extent2(struct cache_tree *tree, struct cache_extent *pe);
|
||||
|
||||
/*
|
||||
* Insert a cache_extent range [start, start + size).
|
||||
*
|
||||
* This function may merge with existing cache_extent.
|
||||
* NOTE: caller must ensure the inserted range won't cover with any existing
|
||||
* range.
|
||||
*/
|
||||
int add_merge_cache_extent(struct cache_tree *tree, u64 start, u64 size);
|
||||
|
||||
#endif
|
|
@ -5,122 +5,805 @@
|
|||
* 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
|
||||
*/
|
||||
|
||||
#include "btrfs.h"
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/bug.h>
|
||||
#include <malloc.h>
|
||||
#include <memalign.h>
|
||||
#include "btrfs.h"
|
||||
#include "ctree.h"
|
||||
#include "extent-io.h"
|
||||
#include "disk-io.h"
|
||||
|
||||
u64 btrfs_read_extent_inline(struct btrfs_path *path,
|
||||
struct btrfs_file_extent_item *extent, u64 offset,
|
||||
u64 size, char *out)
|
||||
void extent_io_tree_init(struct extent_io_tree *tree)
|
||||
{
|
||||
u32 clen, dlen, orig_size = size, res;
|
||||
const char *cbuf;
|
||||
char *dbuf;
|
||||
const int data_off = offsetof(struct btrfs_file_extent_item,
|
||||
disk_bytenr);
|
||||
cache_tree_init(&tree->state);
|
||||
cache_tree_init(&tree->cache);
|
||||
tree->cache_size = 0;
|
||||
}
|
||||
|
||||
clen = btrfs_path_item_size(path) - data_off;
|
||||
cbuf = (const char *) extent + data_off;
|
||||
dlen = extent->ram_bytes;
|
||||
static struct extent_state *alloc_extent_state(void)
|
||||
{
|
||||
struct extent_state *state;
|
||||
|
||||
if (offset > dlen)
|
||||
return -1ULL;
|
||||
state = malloc(sizeof(*state));
|
||||
if (!state)
|
||||
return NULL;
|
||||
state->cache_node.objectid = 0;
|
||||
state->refs = 1;
|
||||
state->state = 0;
|
||||
state->xprivate = 0;
|
||||
return state;
|
||||
}
|
||||
|
||||
if (size > dlen - offset)
|
||||
size = dlen - offset;
|
||||
static void btrfs_free_extent_state(struct extent_state *state)
|
||||
{
|
||||
state->refs--;
|
||||
BUG_ON(state->refs < 0);
|
||||
if (state->refs == 0)
|
||||
free(state);
|
||||
}
|
||||
|
||||
if (extent->compression == BTRFS_COMPRESS_NONE) {
|
||||
memcpy(out, cbuf + offset, size);
|
||||
return size;
|
||||
static void free_extent_state_func(struct cache_extent *cache)
|
||||
{
|
||||
struct extent_state *es;
|
||||
|
||||
es = container_of(cache, struct extent_state, cache_node);
|
||||
btrfs_free_extent_state(es);
|
||||
}
|
||||
|
||||
static void free_extent_buffer_final(struct extent_buffer *eb);
|
||||
void extent_io_tree_cleanup(struct extent_io_tree *tree)
|
||||
{
|
||||
cache_tree_free_extents(&tree->state, free_extent_state_func);
|
||||
}
|
||||
|
||||
static inline void update_extent_state(struct extent_state *state)
|
||||
{
|
||||
state->cache_node.start = state->start;
|
||||
state->cache_node.size = state->end + 1 - state->start;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility function to look for merge candidates inside a given range.
|
||||
* Any extents with matching state are merged together into a single
|
||||
* extent in the tree. Extents with EXTENT_IO in their state field are
|
||||
* not merged
|
||||
*/
|
||||
static int merge_state(struct extent_io_tree *tree,
|
||||
struct extent_state *state)
|
||||
{
|
||||
struct extent_state *other;
|
||||
struct cache_extent *other_node;
|
||||
|
||||
if (state->state & EXTENT_IOBITS)
|
||||
return 0;
|
||||
|
||||
other_node = prev_cache_extent(&state->cache_node);
|
||||
if (other_node) {
|
||||
other = container_of(other_node, struct extent_state,
|
||||
cache_node);
|
||||
if (other->end == state->start - 1 &&
|
||||
other->state == state->state) {
|
||||
state->start = other->start;
|
||||
update_extent_state(state);
|
||||
remove_cache_extent(&tree->state, &other->cache_node);
|
||||
btrfs_free_extent_state(other);
|
||||
}
|
||||
}
|
||||
other_node = next_cache_extent(&state->cache_node);
|
||||
if (other_node) {
|
||||
other = container_of(other_node, struct extent_state,
|
||||
cache_node);
|
||||
if (other->start == state->end + 1 &&
|
||||
other->state == state->state) {
|
||||
other->start = state->start;
|
||||
update_extent_state(other);
|
||||
remove_cache_extent(&tree->state, &state->cache_node);
|
||||
btrfs_free_extent_state(state);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dlen > orig_size) {
|
||||
dbuf = malloc(dlen);
|
||||
if (!dbuf)
|
||||
return -1ULL;
|
||||
/*
|
||||
* insert an extent_state struct into the tree. 'bits' are set on the
|
||||
* struct before it is inserted.
|
||||
*/
|
||||
static int insert_state(struct extent_io_tree *tree,
|
||||
struct extent_state *state, u64 start, u64 end,
|
||||
int bits)
|
||||
{
|
||||
int ret;
|
||||
|
||||
BUG_ON(end < start);
|
||||
state->state |= bits;
|
||||
state->start = start;
|
||||
state->end = end;
|
||||
update_extent_state(state);
|
||||
ret = insert_cache_extent(&tree->state, &state->cache_node);
|
||||
BUG_ON(ret);
|
||||
merge_state(tree, state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* split a given extent state struct in two, inserting the preallocated
|
||||
* struct 'prealloc' as the newly created second half. 'split' indicates an
|
||||
* offset inside 'orig' where it should be split.
|
||||
*/
|
||||
static int split_state(struct extent_io_tree *tree, struct extent_state *orig,
|
||||
struct extent_state *prealloc, u64 split)
|
||||
{
|
||||
int ret;
|
||||
prealloc->start = orig->start;
|
||||
prealloc->end = split - 1;
|
||||
prealloc->state = orig->state;
|
||||
update_extent_state(prealloc);
|
||||
orig->start = split;
|
||||
update_extent_state(orig);
|
||||
ret = insert_cache_extent(&tree->state, &prealloc->cache_node);
|
||||
BUG_ON(ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* clear some bits on a range in the tree.
|
||||
*/
|
||||
static int clear_state_bit(struct extent_io_tree *tree,
|
||||
struct extent_state *state, int bits)
|
||||
{
|
||||
int ret = state->state & bits;
|
||||
|
||||
state->state &= ~bits;
|
||||
if (state->state == 0) {
|
||||
remove_cache_extent(&tree->state, &state->cache_node);
|
||||
btrfs_free_extent_state(state);
|
||||
} else {
|
||||
dbuf = out;
|
||||
merge_state(tree, state);
|
||||
}
|
||||
|
||||
res = btrfs_decompress(extent->compression, cbuf, clen, dbuf, dlen);
|
||||
if (res == -1 || res != dlen)
|
||||
goto err;
|
||||
|
||||
if (dlen > orig_size) {
|
||||
memcpy(out, dbuf + offset, size);
|
||||
free(dbuf);
|
||||
} else if (offset) {
|
||||
memmove(out, dbuf + offset, size);
|
||||
}
|
||||
|
||||
return size;
|
||||
|
||||
err:
|
||||
if (dlen > orig_size)
|
||||
free(dbuf);
|
||||
return -1ULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
u64 btrfs_read_extent_reg(struct btrfs_path *path,
|
||||
struct btrfs_file_extent_item *extent, u64 offset,
|
||||
u64 size, char *out)
|
||||
/*
|
||||
* extent_buffer_bitmap_set - set an area of a bitmap
|
||||
* @eb: the extent buffer
|
||||
* @start: offset of the bitmap item in the extent buffer
|
||||
* @pos: bit number of the first bit
|
||||
* @len: number of bits to set
|
||||
*/
|
||||
void extent_buffer_bitmap_set(struct extent_buffer *eb, unsigned long start,
|
||||
unsigned long pos, unsigned long len)
|
||||
{
|
||||
u64 physical, clen, dlen, orig_size = size;
|
||||
u32 res;
|
||||
char *cbuf, *dbuf;
|
||||
u8 *p = (u8 *)eb->data + start + BIT_BYTE(pos);
|
||||
const unsigned int size = pos + len;
|
||||
int bits_to_set = BITS_PER_BYTE - (pos % BITS_PER_BYTE);
|
||||
u8 mask_to_set = BITMAP_FIRST_BYTE_MASK(pos);
|
||||
|
||||
clen = extent->disk_num_bytes;
|
||||
dlen = extent->num_bytes;
|
||||
|
||||
if (offset > dlen)
|
||||
return -1ULL;
|
||||
|
||||
if (size > dlen - offset)
|
||||
size = dlen - offset;
|
||||
|
||||
/* sparse extent */
|
||||
if (extent->disk_bytenr == 0) {
|
||||
memset(out, 0, size);
|
||||
return size;
|
||||
while (len >= bits_to_set) {
|
||||
*p |= mask_to_set;
|
||||
len -= bits_to_set;
|
||||
bits_to_set = BITS_PER_BYTE;
|
||||
mask_to_set = ~0;
|
||||
p++;
|
||||
}
|
||||
|
||||
physical = btrfs_map_logical_to_physical(extent->disk_bytenr);
|
||||
if (physical == -1ULL)
|
||||
return -1ULL;
|
||||
|
||||
if (extent->compression == BTRFS_COMPRESS_NONE) {
|
||||
physical += extent->offset + offset;
|
||||
if (!btrfs_devread(physical, size, out))
|
||||
return -1ULL;
|
||||
|
||||
return size;
|
||||
if (len) {
|
||||
mask_to_set &= BITMAP_LAST_BYTE_MASK(size);
|
||||
*p |= mask_to_set;
|
||||
}
|
||||
|
||||
cbuf = malloc_cache_aligned(dlen > size ? clen + dlen : clen);
|
||||
if (!cbuf)
|
||||
return -1ULL;
|
||||
|
||||
if (dlen > orig_size)
|
||||
dbuf = cbuf + clen;
|
||||
else
|
||||
dbuf = out;
|
||||
|
||||
if (!btrfs_devread(physical, clen, cbuf))
|
||||
goto err;
|
||||
|
||||
res = btrfs_decompress(extent->compression, cbuf, clen, dbuf, dlen);
|
||||
if (res == -1)
|
||||
goto err;
|
||||
|
||||
if (dlen > orig_size)
|
||||
memcpy(out, dbuf + offset, size);
|
||||
else
|
||||
memmove(out, dbuf + offset, size);
|
||||
|
||||
free(cbuf);
|
||||
return res;
|
||||
|
||||
err:
|
||||
free(cbuf);
|
||||
return -1ULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* extent_buffer_bitmap_clear - clear an area of a bitmap
|
||||
* @eb: the extent buffer
|
||||
* @start: offset of the bitmap item in the extent buffer
|
||||
* @pos: bit number of the first bit
|
||||
* @len: number of bits to clear
|
||||
*/
|
||||
void extent_buffer_bitmap_clear(struct extent_buffer *eb, unsigned long start,
|
||||
unsigned long pos, unsigned long len)
|
||||
{
|
||||
u8 *p = (u8 *)eb->data + start + BIT_BYTE(pos);
|
||||
const unsigned int size = pos + len;
|
||||
int bits_to_clear = BITS_PER_BYTE - (pos % BITS_PER_BYTE);
|
||||
u8 mask_to_clear = BITMAP_FIRST_BYTE_MASK(pos);
|
||||
|
||||
while (len >= bits_to_clear) {
|
||||
*p &= ~mask_to_clear;
|
||||
len -= bits_to_clear;
|
||||
bits_to_clear = BITS_PER_BYTE;
|
||||
mask_to_clear = ~0;
|
||||
p++;
|
||||
}
|
||||
if (len) {
|
||||
mask_to_clear &= BITMAP_LAST_BYTE_MASK(size);
|
||||
*p &= ~mask_to_clear;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* clear some bits on a range in the tree.
|
||||
*/
|
||||
int clear_extent_bits(struct extent_io_tree *tree, u64 start, u64 end, int bits)
|
||||
{
|
||||
struct extent_state *state;
|
||||
struct extent_state *prealloc = NULL;
|
||||
struct cache_extent *node;
|
||||
u64 last_end;
|
||||
int err;
|
||||
int set = 0;
|
||||
|
||||
again:
|
||||
if (!prealloc) {
|
||||
prealloc = alloc_extent_state();
|
||||
if (!prealloc)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* this search will find the extents that end after
|
||||
* our range starts
|
||||
*/
|
||||
node = search_cache_extent(&tree->state, start);
|
||||
if (!node)
|
||||
goto out;
|
||||
state = container_of(node, struct extent_state, cache_node);
|
||||
if (state->start > end)
|
||||
goto out;
|
||||
last_end = state->end;
|
||||
|
||||
/*
|
||||
* | ---- desired range ---- |
|
||||
* | state | or
|
||||
* | ------------- state -------------- |
|
||||
*
|
||||
* We need to split the extent we found, and may flip
|
||||
* bits on second half.
|
||||
*
|
||||
* If the extent we found extends past our range, we
|
||||
* just split and search again. It'll get split again
|
||||
* the next time though.
|
||||
*
|
||||
* If the extent we found is inside our range, we clear
|
||||
* the desired bit on it.
|
||||
*/
|
||||
if (state->start < start) {
|
||||
err = split_state(tree, state, prealloc, start);
|
||||
BUG_ON(err == -EEXIST);
|
||||
prealloc = NULL;
|
||||
if (err)
|
||||
goto out;
|
||||
if (state->end <= end) {
|
||||
set |= clear_state_bit(tree, state, bits);
|
||||
if (last_end == (u64)-1)
|
||||
goto out;
|
||||
start = last_end + 1;
|
||||
} else {
|
||||
start = state->start;
|
||||
}
|
||||
goto search_again;
|
||||
}
|
||||
/*
|
||||
* | ---- desired range ---- |
|
||||
* | state |
|
||||
* We need to split the extent, and clear the bit
|
||||
* on the first half
|
||||
*/
|
||||
if (state->start <= end && state->end > end) {
|
||||
err = split_state(tree, state, prealloc, end + 1);
|
||||
BUG_ON(err == -EEXIST);
|
||||
|
||||
set |= clear_state_bit(tree, prealloc, bits);
|
||||
prealloc = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
start = state->end + 1;
|
||||
set |= clear_state_bit(tree, state, bits);
|
||||
if (last_end == (u64)-1)
|
||||
goto out;
|
||||
start = last_end + 1;
|
||||
goto search_again;
|
||||
out:
|
||||
if (prealloc)
|
||||
btrfs_free_extent_state(prealloc);
|
||||
return set;
|
||||
|
||||
search_again:
|
||||
if (start > end)
|
||||
goto out;
|
||||
goto again;
|
||||
}
|
||||
|
||||
/*
|
||||
* set some bits on a range in the tree.
|
||||
*/
|
||||
int set_extent_bits(struct extent_io_tree *tree, u64 start, u64 end, int bits)
|
||||
{
|
||||
struct extent_state *state;
|
||||
struct extent_state *prealloc = NULL;
|
||||
struct cache_extent *node;
|
||||
int err = 0;
|
||||
u64 last_start;
|
||||
u64 last_end;
|
||||
again:
|
||||
if (!prealloc) {
|
||||
prealloc = alloc_extent_state();
|
||||
if (!prealloc)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* this search will find the extents that end after
|
||||
* our range starts
|
||||
*/
|
||||
node = search_cache_extent(&tree->state, start);
|
||||
if (!node) {
|
||||
err = insert_state(tree, prealloc, start, end, bits);
|
||||
BUG_ON(err == -EEXIST);
|
||||
prealloc = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
state = container_of(node, struct extent_state, cache_node);
|
||||
last_start = state->start;
|
||||
last_end = state->end;
|
||||
|
||||
/*
|
||||
* | ---- desired range ---- |
|
||||
* | state |
|
||||
*
|
||||
* Just lock what we found and keep going
|
||||
*/
|
||||
if (state->start == start && state->end <= end) {
|
||||
state->state |= bits;
|
||||
merge_state(tree, state);
|
||||
if (last_end == (u64)-1)
|
||||
goto out;
|
||||
start = last_end + 1;
|
||||
goto search_again;
|
||||
}
|
||||
/*
|
||||
* | ---- desired range ---- |
|
||||
* | state |
|
||||
* or
|
||||
* | ------------- state -------------- |
|
||||
*
|
||||
* We need to split the extent we found, and may flip bits on
|
||||
* second half.
|
||||
*
|
||||
* If the extent we found extends past our
|
||||
* range, we just split and search again. It'll get split
|
||||
* again the next time though.
|
||||
*
|
||||
* If the extent we found is inside our range, we set the
|
||||
* desired bit on it.
|
||||
*/
|
||||
if (state->start < start) {
|
||||
err = split_state(tree, state, prealloc, start);
|
||||
BUG_ON(err == -EEXIST);
|
||||
prealloc = NULL;
|
||||
if (err)
|
||||
goto out;
|
||||
if (state->end <= end) {
|
||||
state->state |= bits;
|
||||
start = state->end + 1;
|
||||
merge_state(tree, state);
|
||||
if (last_end == (u64)-1)
|
||||
goto out;
|
||||
start = last_end + 1;
|
||||
} else {
|
||||
start = state->start;
|
||||
}
|
||||
goto search_again;
|
||||
}
|
||||
/*
|
||||
* | ---- desired range ---- |
|
||||
* | state | or | state |
|
||||
*
|
||||
* There's a hole, we need to insert something in it and
|
||||
* ignore the extent we found.
|
||||
*/
|
||||
if (state->start > start) {
|
||||
u64 this_end;
|
||||
if (end < last_start)
|
||||
this_end = end;
|
||||
else
|
||||
this_end = last_start -1;
|
||||
err = insert_state(tree, prealloc, start, this_end,
|
||||
bits);
|
||||
BUG_ON(err == -EEXIST);
|
||||
prealloc = NULL;
|
||||
if (err)
|
||||
goto out;
|
||||
start = this_end + 1;
|
||||
goto search_again;
|
||||
}
|
||||
/*
|
||||
* | ---- desired range ---- |
|
||||
* | ---------- state ---------- |
|
||||
* We need to split the extent, and set the bit
|
||||
* on the first half
|
||||
*/
|
||||
err = split_state(tree, state, prealloc, end + 1);
|
||||
BUG_ON(err == -EEXIST);
|
||||
|
||||
state->state |= bits;
|
||||
merge_state(tree, prealloc);
|
||||
prealloc = NULL;
|
||||
out:
|
||||
if (prealloc)
|
||||
btrfs_free_extent_state(prealloc);
|
||||
return err;
|
||||
search_again:
|
||||
if (start > end)
|
||||
goto out;
|
||||
goto again;
|
||||
}
|
||||
|
||||
int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end)
|
||||
{
|
||||
return set_extent_bits(tree, start, end, EXTENT_DIRTY);
|
||||
}
|
||||
|
||||
int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end)
|
||||
{
|
||||
return clear_extent_bits(tree, start, end, EXTENT_DIRTY);
|
||||
}
|
||||
|
||||
int find_first_extent_bit(struct extent_io_tree *tree, u64 start,
|
||||
u64 *start_ret, u64 *end_ret, int bits)
|
||||
{
|
||||
struct cache_extent *node;
|
||||
struct extent_state *state;
|
||||
int ret = 1;
|
||||
|
||||
/*
|
||||
* this search will find all the extents that end after
|
||||
* our range starts.
|
||||
*/
|
||||
node = search_cache_extent(&tree->state, start);
|
||||
if (!node)
|
||||
goto out;
|
||||
|
||||
while(1) {
|
||||
state = container_of(node, struct extent_state, cache_node);
|
||||
if (state->end >= start && (state->state & bits)) {
|
||||
*start_ret = state->start;
|
||||
*end_ret = state->end;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
node = next_cache_extent(node);
|
||||
if (!node)
|
||||
break;
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int test_range_bit(struct extent_io_tree *tree, u64 start, u64 end,
|
||||
int bits, int filled)
|
||||
{
|
||||
struct extent_state *state = NULL;
|
||||
struct cache_extent *node;
|
||||
int bitset = 0;
|
||||
|
||||
node = search_cache_extent(&tree->state, start);
|
||||
while (node && start <= end) {
|
||||
state = container_of(node, struct extent_state, cache_node);
|
||||
|
||||
if (filled && state->start > start) {
|
||||
bitset = 0;
|
||||
break;
|
||||
}
|
||||
if (state->start > end)
|
||||
break;
|
||||
if (state->state & bits) {
|
||||
bitset = 1;
|
||||
if (!filled)
|
||||
break;
|
||||
} else if (filled) {
|
||||
bitset = 0;
|
||||
break;
|
||||
}
|
||||
start = state->end + 1;
|
||||
if (start > end)
|
||||
break;
|
||||
node = next_cache_extent(node);
|
||||
if (!node) {
|
||||
if (filled)
|
||||
bitset = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return bitset;
|
||||
}
|
||||
|
||||
int set_state_private(struct extent_io_tree *tree, u64 start, u64 private)
|
||||
{
|
||||
struct cache_extent *node;
|
||||
struct extent_state *state;
|
||||
int ret = 0;
|
||||
|
||||
node = search_cache_extent(&tree->state, start);
|
||||
if (!node) {
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
state = container_of(node, struct extent_state, cache_node);
|
||||
if (state->start != start) {
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
state->xprivate = private;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int get_state_private(struct extent_io_tree *tree, u64 start, u64 *private)
|
||||
{
|
||||
struct cache_extent *node;
|
||||
struct extent_state *state;
|
||||
int ret = 0;
|
||||
|
||||
node = search_cache_extent(&tree->state, start);
|
||||
if (!node) {
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
state = container_of(node, struct extent_state, cache_node);
|
||||
if (state->start != start) {
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
*private = state->xprivate;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct extent_buffer *__alloc_extent_buffer(struct btrfs_fs_info *info,
|
||||
u64 bytenr, u32 blocksize)
|
||||
{
|
||||
struct extent_buffer *eb;
|
||||
|
||||
eb = calloc(1, sizeof(struct extent_buffer));
|
||||
if (!eb)
|
||||
return NULL;
|
||||
eb->data = malloc_cache_aligned(blocksize);
|
||||
if (!eb->data) {
|
||||
free(eb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
eb->start = bytenr;
|
||||
eb->len = blocksize;
|
||||
eb->refs = 1;
|
||||
eb->flags = 0;
|
||||
eb->cache_node.start = bytenr;
|
||||
eb->cache_node.size = blocksize;
|
||||
eb->fs_info = info;
|
||||
memset_extent_buffer(eb, 0, 0, blocksize);
|
||||
|
||||
return eb;
|
||||
}
|
||||
|
||||
struct extent_buffer *btrfs_clone_extent_buffer(struct extent_buffer *src)
|
||||
{
|
||||
struct extent_buffer *new;
|
||||
|
||||
new = __alloc_extent_buffer(src->fs_info, src->start, src->len);
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
copy_extent_buffer(new, src, 0, 0, src->len);
|
||||
new->flags |= EXTENT_BUFFER_DUMMY;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
static void free_extent_buffer_final(struct extent_buffer *eb)
|
||||
{
|
||||
BUG_ON(eb->refs);
|
||||
if (!(eb->flags & EXTENT_BUFFER_DUMMY)) {
|
||||
struct extent_io_tree *tree = &eb->fs_info->extent_cache;
|
||||
|
||||
remove_cache_extent(&tree->cache, &eb->cache_node);
|
||||
BUG_ON(tree->cache_size < eb->len);
|
||||
tree->cache_size -= eb->len;
|
||||
}
|
||||
free(eb->data);
|
||||
free(eb);
|
||||
}
|
||||
|
||||
static void free_extent_buffer_internal(struct extent_buffer *eb, bool free_now)
|
||||
{
|
||||
if (!eb || IS_ERR(eb))
|
||||
return;
|
||||
|
||||
eb->refs--;
|
||||
BUG_ON(eb->refs < 0);
|
||||
if (eb->refs == 0) {
|
||||
if (eb->flags & EXTENT_DIRTY) {
|
||||
error(
|
||||
"dirty eb leak (aborted trans): start %llu len %u",
|
||||
eb->start, eb->len);
|
||||
}
|
||||
if (eb->flags & EXTENT_BUFFER_DUMMY || free_now)
|
||||
free_extent_buffer_final(eb);
|
||||
}
|
||||
}
|
||||
|
||||
void free_extent_buffer(struct extent_buffer *eb)
|
||||
{
|
||||
free_extent_buffer_internal(eb, 1);
|
||||
}
|
||||
|
||||
struct extent_buffer *find_extent_buffer(struct extent_io_tree *tree,
|
||||
u64 bytenr, u32 blocksize)
|
||||
{
|
||||
struct extent_buffer *eb = NULL;
|
||||
struct cache_extent *cache;
|
||||
|
||||
cache = lookup_cache_extent(&tree->cache, bytenr, blocksize);
|
||||
if (cache && cache->start == bytenr &&
|
||||
cache->size == blocksize) {
|
||||
eb = container_of(cache, struct extent_buffer, cache_node);
|
||||
eb->refs++;
|
||||
}
|
||||
return eb;
|
||||
}
|
||||
|
||||
struct extent_buffer *find_first_extent_buffer(struct extent_io_tree *tree,
|
||||
u64 start)
|
||||
{
|
||||
struct extent_buffer *eb = NULL;
|
||||
struct cache_extent *cache;
|
||||
|
||||
cache = search_cache_extent(&tree->cache, start);
|
||||
if (cache) {
|
||||
eb = container_of(cache, struct extent_buffer, cache_node);
|
||||
eb->refs++;
|
||||
}
|
||||
return eb;
|
||||
}
|
||||
|
||||
struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info,
|
||||
u64 bytenr, u32 blocksize)
|
||||
{
|
||||
struct extent_buffer *eb;
|
||||
struct extent_io_tree *tree = &fs_info->extent_cache;
|
||||
struct cache_extent *cache;
|
||||
|
||||
cache = lookup_cache_extent(&tree->cache, bytenr, blocksize);
|
||||
if (cache && cache->start == bytenr &&
|
||||
cache->size == blocksize) {
|
||||
eb = container_of(cache, struct extent_buffer, cache_node);
|
||||
eb->refs++;
|
||||
} else {
|
||||
int ret;
|
||||
|
||||
if (cache) {
|
||||
eb = container_of(cache, struct extent_buffer,
|
||||
cache_node);
|
||||
free_extent_buffer(eb);
|
||||
}
|
||||
eb = __alloc_extent_buffer(fs_info, bytenr, blocksize);
|
||||
if (!eb)
|
||||
return NULL;
|
||||
ret = insert_cache_extent(&tree->cache, &eb->cache_node);
|
||||
if (ret) {
|
||||
free(eb);
|
||||
return NULL;
|
||||
}
|
||||
tree->cache_size += blocksize;
|
||||
}
|
||||
return eb;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a dummy extent buffer which won't be inserted into extent buffer
|
||||
* cache.
|
||||
*
|
||||
* This mostly allows super block read write using existing eb infrastructure
|
||||
* without pulluting the eb cache.
|
||||
*
|
||||
* This is especially important to avoid injecting eb->start == SZ_64K, as
|
||||
* fuzzed image could have invalid tree bytenr covers super block range,
|
||||
* and cause ref count underflow.
|
||||
*/
|
||||
struct extent_buffer *alloc_dummy_extent_buffer(struct btrfs_fs_info *fs_info,
|
||||
u64 bytenr, u32 blocksize)
|
||||
{
|
||||
struct extent_buffer *ret;
|
||||
|
||||
ret = __alloc_extent_buffer(fs_info, bytenr, blocksize);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
ret->flags |= EXTENT_BUFFER_DUMMY;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int read_extent_from_disk(struct blk_desc *desc, struct disk_partition *part,
|
||||
u64 physical, struct extent_buffer *eb,
|
||||
unsigned long offset, unsigned long len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = __btrfs_devread(desc, part, eb->data + offset, len, physical);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (ret != len) {
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int memcmp_extent_buffer(const struct extent_buffer *eb, const void *ptrv,
|
||||
unsigned long start, unsigned long len)
|
||||
{
|
||||
return memcmp(eb->data + start, ptrv, len);
|
||||
}
|
||||
|
||||
void read_extent_buffer(const struct extent_buffer *eb, void *dst,
|
||||
unsigned long start, unsigned long len)
|
||||
{
|
||||
memcpy(dst, eb->data + start, len);
|
||||
}
|
||||
|
||||
void write_extent_buffer(struct extent_buffer *eb, const void *src,
|
||||
unsigned long start, unsigned long len)
|
||||
{
|
||||
memcpy(eb->data + start, src, len);
|
||||
}
|
||||
|
||||
void copy_extent_buffer(struct extent_buffer *dst, struct extent_buffer *src,
|
||||
unsigned long dst_offset, unsigned long src_offset,
|
||||
unsigned long len)
|
||||
{
|
||||
memcpy(dst->data + dst_offset, src->data + src_offset, len);
|
||||
}
|
||||
|
||||
void memmove_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset,
|
||||
unsigned long src_offset, unsigned long len)
|
||||
{
|
||||
memmove(dst->data + dst_offset, dst->data + src_offset, len);
|
||||
}
|
||||
|
||||
void memset_extent_buffer(struct extent_buffer *eb, char c,
|
||||
unsigned long start, unsigned long len)
|
||||
{
|
||||
memset(eb->data + start, c, len);
|
||||
}
|
||||
|
||||
int extent_buffer_test_bit(struct extent_buffer *eb, unsigned long start,
|
||||
unsigned long nr)
|
||||
{
|
||||
return le_test_bit(nr, (u8 *)eb->data + start);
|
||||
}
|
||||
|
||||
int set_extent_buffer_dirty(struct extent_buffer *eb)
|
||||
{
|
||||
struct extent_io_tree *tree = &eb->fs_info->extent_cache;
|
||||
if (!(eb->flags & EXTENT_DIRTY)) {
|
||||
eb->flags |= EXTENT_DIRTY;
|
||||
set_extent_dirty(tree, eb->start, eb->start + eb->len - 1);
|
||||
extent_buffer_get(eb);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int clear_extent_buffer_dirty(struct extent_buffer *eb)
|
||||
{
|
||||
struct extent_io_tree *tree = &eb->fs_info->extent_cache;
|
||||
if (eb->flags & EXTENT_DIRTY) {
|
||||
eb->flags &= ~EXTENT_DIRTY;
|
||||
clear_extent_dirty(tree, eb->start, eb->start + eb->len - 1);
|
||||
free_extent_buffer(eb);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
164
fs/btrfs/extent-io.h
Normal file
164
fs/btrfs/extent-io.h
Normal file
|
@ -0,0 +1,164 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
/*
|
||||
* Crossported from btrfs-progs/extent_io.h
|
||||
*
|
||||
* Modification includes:
|
||||
* - extent_buffer:data
|
||||
* Use pointer to provide better alignment.
|
||||
* - Remove max_cache_size related interfaces
|
||||
* Includes free_extent_buffer_nocache()
|
||||
* As we don't cache eb in U-boot.
|
||||
* - Include headers
|
||||
*
|
||||
* Write related functions are kept as we still need to modify dummy extent
|
||||
* buffers even in RO environment.
|
||||
*/
|
||||
#ifndef __BTRFS_EXTENT_IO_H__
|
||||
#define __BTRFS_EXTENT_IO_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <fs_internal.h>
|
||||
#include "extent-cache.h"
|
||||
|
||||
#define EXTENT_DIRTY (1U << 0)
|
||||
#define EXTENT_WRITEBACK (1U << 1)
|
||||
#define EXTENT_UPTODATE (1U << 2)
|
||||
#define EXTENT_LOCKED (1U << 3)
|
||||
#define EXTENT_NEW (1U << 4)
|
||||
#define EXTENT_DELALLOC (1U << 5)
|
||||
#define EXTENT_DEFRAG (1U << 6)
|
||||
#define EXTENT_DEFRAG_DONE (1U << 7)
|
||||
#define EXTENT_BUFFER_FILLED (1U << 8)
|
||||
#define EXTENT_CSUM (1U << 9)
|
||||
#define EXTENT_BAD_TRANSID (1U << 10)
|
||||
#define EXTENT_BUFFER_DUMMY (1U << 11)
|
||||
#define EXTENT_IOBITS (EXTENT_LOCKED | EXTENT_WRITEBACK)
|
||||
|
||||
#define BLOCK_GROUP_DATA (1U << 1)
|
||||
#define BLOCK_GROUP_METADATA (1U << 2)
|
||||
#define BLOCK_GROUP_SYSTEM (1U << 4)
|
||||
|
||||
/*
|
||||
* The extent buffer bitmap operations are done with byte granularity instead of
|
||||
* word granularity for two reasons:
|
||||
* 1. The bitmaps must be little-endian on disk.
|
||||
* 2. Bitmap items are not guaranteed to be aligned to a word and therefore a
|
||||
* single word in a bitmap may straddle two pages in the extent buffer.
|
||||
*/
|
||||
#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE)
|
||||
#define BYTE_MASK ((1 << BITS_PER_BYTE) - 1)
|
||||
#define BITMAP_FIRST_BYTE_MASK(start) \
|
||||
((BYTE_MASK << ((start) & (BITS_PER_BYTE - 1))) & BYTE_MASK)
|
||||
#define BITMAP_LAST_BYTE_MASK(nbits) \
|
||||
(BYTE_MASK >> (-(nbits) & (BITS_PER_BYTE - 1)))
|
||||
|
||||
static inline int le_test_bit(int nr, const u8 *addr)
|
||||
{
|
||||
return 1U & (addr[BIT_BYTE(nr)] >> (nr & (BITS_PER_BYTE-1)));
|
||||
}
|
||||
|
||||
struct btrfs_fs_info;
|
||||
|
||||
struct extent_io_tree {
|
||||
struct cache_tree state;
|
||||
struct cache_tree cache;
|
||||
u64 cache_size;
|
||||
};
|
||||
|
||||
struct extent_state {
|
||||
struct cache_extent cache_node;
|
||||
u64 start;
|
||||
u64 end;
|
||||
int refs;
|
||||
unsigned long state;
|
||||
u64 xprivate;
|
||||
};
|
||||
|
||||
struct extent_buffer {
|
||||
struct cache_extent cache_node;
|
||||
u64 start;
|
||||
u32 len;
|
||||
int refs;
|
||||
u32 flags;
|
||||
struct btrfs_fs_info *fs_info;
|
||||
char *data;
|
||||
};
|
||||
|
||||
static inline void extent_buffer_get(struct extent_buffer *eb)
|
||||
{
|
||||
eb->refs++;
|
||||
}
|
||||
|
||||
void extent_io_tree_init(struct extent_io_tree *tree);
|
||||
void extent_io_tree_cleanup(struct extent_io_tree *tree);
|
||||
int set_extent_bits(struct extent_io_tree *tree, u64 start, u64 end, int bits);
|
||||
int clear_extent_bits(struct extent_io_tree *tree, u64 start, u64 end, int bits);
|
||||
int find_first_extent_bit(struct extent_io_tree *tree, u64 start,
|
||||
u64 *start_ret, u64 *end_ret, int bits);
|
||||
int test_range_bit(struct extent_io_tree *tree, u64 start, u64 end,
|
||||
int bits, int filled);
|
||||
int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end);
|
||||
int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end);
|
||||
static inline int set_extent_buffer_uptodate(struct extent_buffer *eb)
|
||||
{
|
||||
eb->flags |= EXTENT_UPTODATE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int clear_extent_buffer_uptodate(struct extent_buffer *eb)
|
||||
{
|
||||
eb->flags &= ~EXTENT_UPTODATE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int extent_buffer_uptodate(struct extent_buffer *eb)
|
||||
{
|
||||
if (!eb || IS_ERR(eb))
|
||||
return 0;
|
||||
if (eb->flags & EXTENT_UPTODATE)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int set_state_private(struct extent_io_tree *tree, u64 start, u64 xprivate);
|
||||
int get_state_private(struct extent_io_tree *tree, u64 start, u64 *xprivate);
|
||||
struct extent_buffer *find_extent_buffer(struct extent_io_tree *tree,
|
||||
u64 bytenr, u32 blocksize);
|
||||
struct extent_buffer *find_first_extent_buffer(struct extent_io_tree *tree,
|
||||
u64 start);
|
||||
struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info,
|
||||
u64 bytenr, u32 blocksize);
|
||||
struct extent_buffer *btrfs_clone_extent_buffer(struct extent_buffer *src);
|
||||
struct extent_buffer *alloc_dummy_extent_buffer(struct btrfs_fs_info *fs_info,
|
||||
u64 bytenr, u32 blocksize);
|
||||
void free_extent_buffer(struct extent_buffer *eb);
|
||||
int read_extent_from_disk(struct blk_desc *desc, struct disk_partition *part,
|
||||
u64 physical, struct extent_buffer *eb,
|
||||
unsigned long offset, unsigned long len);
|
||||
int memcmp_extent_buffer(const struct extent_buffer *eb, const void *ptrv,
|
||||
unsigned long start, unsigned long len);
|
||||
void read_extent_buffer(const struct extent_buffer *eb, void *dst,
|
||||
unsigned long start, unsigned long len);
|
||||
void write_extent_buffer(struct extent_buffer *eb, const void *src,
|
||||
unsigned long start, unsigned long len);
|
||||
void copy_extent_buffer(struct extent_buffer *dst, struct extent_buffer *src,
|
||||
unsigned long dst_offset, unsigned long src_offset,
|
||||
unsigned long len);
|
||||
void memmove_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset,
|
||||
unsigned long src_offset, unsigned long len);
|
||||
void memset_extent_buffer(struct extent_buffer *eb, char c,
|
||||
unsigned long start, unsigned long len);
|
||||
int extent_buffer_test_bit(struct extent_buffer *eb, unsigned long start,
|
||||
unsigned long nr);
|
||||
int set_extent_buffer_dirty(struct extent_buffer *eb);
|
||||
int clear_extent_buffer_dirty(struct extent_buffer *eb);
|
||||
void extent_buffer_bitmap_clear(struct extent_buffer *eb, unsigned long start,
|
||||
unsigned long pos, unsigned long len);
|
||||
void extent_buffer_bitmap_set(struct extent_buffer *eb, unsigned long start,
|
||||
unsigned long pos, unsigned long len);
|
||||
|
||||
#endif
|
|
@ -1,38 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* BTRFS filesystem implementation for U-Boot
|
||||
*
|
||||
* 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
|
||||
*/
|
||||
|
||||
#include "btrfs.h"
|
||||
#include <u-boot/crc.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
static u32 btrfs_crc32c_table[256];
|
||||
|
||||
void btrfs_hash_init(void)
|
||||
{
|
||||
static int inited = 0;
|
||||
|
||||
if (!inited) {
|
||||
crc32c_init(btrfs_crc32c_table, 0x82F63B78);
|
||||
inited = 1;
|
||||
}
|
||||
}
|
||||
|
||||
u32 btrfs_crc32c(u32 crc, const void *data, size_t length)
|
||||
{
|
||||
return crc32c_cal(crc, (const char *) data, length,
|
||||
btrfs_crc32c_table);
|
||||
}
|
||||
|
||||
u32 btrfs_csum_data(char *data, u32 seed, size_t len)
|
||||
{
|
||||
return btrfs_crc32c(seed, data, len);
|
||||
}
|
||||
|
||||
void btrfs_csum_final(u32 crc, void *result)
|
||||
{
|
||||
put_unaligned(cpu_to_le32(~crc), (u32 *)result);
|
||||
}
|
912
fs/btrfs/inode.c
912
fs/btrfs/inode.c
File diff suppressed because it is too large
Load diff
1333
fs/btrfs/kernel-shared/btrfs_tree.h
Normal file
1333
fs/btrfs/kernel-shared/btrfs_tree.h
Normal file
File diff suppressed because it is too large
Load diff
47
fs/btrfs/root-tree.c
Normal file
47
fs/btrfs/root-tree.c
Normal file
|
@ -0,0 +1,47 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
#include "ctree.h"
|
||||
|
||||
int btrfs_find_last_root(struct btrfs_root *root, u64 objectid,
|
||||
struct btrfs_root_item *item, struct btrfs_key *key)
|
||||
{
|
||||
struct btrfs_path *path;
|
||||
struct btrfs_key search_key;
|
||||
struct btrfs_key found_key;
|
||||
struct extent_buffer *l;
|
||||
int ret;
|
||||
int slot;
|
||||
|
||||
path = btrfs_alloc_path();
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
search_key.objectid = objectid;
|
||||
search_key.type = BTRFS_ROOT_ITEM_KEY;
|
||||
search_key.offset = (u64)-1;
|
||||
|
||||
ret = btrfs_search_slot(NULL, root, &search_key, path, 0, 0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (path->slots[0] == 0) {
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
BUG_ON(ret == 0);
|
||||
l = path->nodes[0];
|
||||
slot = path->slots[0] - 1;
|
||||
btrfs_item_key_to_cpu(l, &found_key, slot);
|
||||
if (found_key.type != BTRFS_ROOT_ITEM_KEY ||
|
||||
found_key.objectid != objectid) {
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
read_extent_buffer(l, item, btrfs_item_ptr_offset(l, slot),
|
||||
sizeof(*item));
|
||||
memcpy(key, &found_key, sizeof(found_key));
|
||||
ret = 0;
|
||||
out:
|
||||
btrfs_free_path(path);
|
||||
return ret;
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* BTRFS filesystem implementation for U-Boot
|
||||
*
|
||||
* 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
|
||||
*/
|
||||
|
||||
#include "btrfs.h"
|
||||
|
||||
static void read_root_item(struct btrfs_path *p, struct btrfs_root_item *item)
|
||||
{
|
||||
u32 len;
|
||||
int reset = 0;
|
||||
|
||||
len = btrfs_path_item_size(p);
|
||||
memcpy(item, btrfs_path_item_ptr(p, struct btrfs_root_item), len);
|
||||
btrfs_root_item_to_cpu(item);
|
||||
|
||||
if (len < sizeof(*item))
|
||||
reset = 1;
|
||||
if (!reset && item->generation != item->generation_v2) {
|
||||
if (item->generation_v2 != 0)
|
||||
printf("%s: generation != generation_v2 in root item",
|
||||
__func__);
|
||||
reset = 1;
|
||||
}
|
||||
if (reset) {
|
||||
memset(&item->generation_v2, 0,
|
||||
sizeof(*item) - offsetof(struct btrfs_root_item,
|
||||
generation_v2));
|
||||
}
|
||||
}
|
||||
|
||||
int btrfs_find_root(u64 objectid, struct btrfs_root *root,
|
||||
struct btrfs_root_item *root_item)
|
||||
{
|
||||
struct btrfs_path path;
|
||||
struct btrfs_root_item my_root_item;
|
||||
|
||||
if (!btrfs_search_tree_key_type(&btrfs_info.tree_root, objectid,
|
||||
BTRFS_ROOT_ITEM_KEY, &path))
|
||||
return -1;
|
||||
|
||||
if (!root_item)
|
||||
root_item = &my_root_item;
|
||||
read_root_item(&path, root_item);
|
||||
|
||||
if (root) {
|
||||
root->objectid = objectid;
|
||||
root->bytenr = root_item->bytenr;
|
||||
root->root_dirid = root_item->root_dirid;
|
||||
}
|
||||
|
||||
btrfs_free_path(&path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 btrfs_lookup_root_ref(u64 subvolid, struct btrfs_root_ref *refp, char *name)
|
||||
{
|
||||
struct btrfs_path path;
|
||||
struct btrfs_key *key;
|
||||
struct btrfs_root_ref *ref;
|
||||
u64 res = -1ULL;
|
||||
|
||||
key = btrfs_search_tree_key_type(&btrfs_info.tree_root, subvolid,
|
||||
BTRFS_ROOT_BACKREF_KEY, &path);
|
||||
|
||||
if (!key)
|
||||
return -1ULL;
|
||||
|
||||
ref = btrfs_path_item_ptr(&path, struct btrfs_root_ref);
|
||||
btrfs_root_ref_to_cpu(ref);
|
||||
|
||||
if (refp)
|
||||
*refp = *ref;
|
||||
|
||||
if (name) {
|
||||
if (ref->name_len > BTRFS_VOL_NAME_MAX) {
|
||||
printf("%s: volume name too long: %u\n", __func__,
|
||||
ref->name_len);
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(name, ref + 1, ref->name_len);
|
||||
}
|
||||
|
||||
res = key->offset;
|
||||
out:
|
||||
btrfs_free_path(&path);
|
||||
return res;
|
||||
}
|
||||
|
|
@ -5,126 +5,230 @@
|
|||
* 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
|
||||
*/
|
||||
|
||||
#include "btrfs.h"
|
||||
#include <malloc.h>
|
||||
#include "ctree.h"
|
||||
#include "btrfs.h"
|
||||
#include "disk-io.h"
|
||||
|
||||
static int get_subvol_name(u64 subvolid, char *name, int max_len)
|
||||
/*
|
||||
* Resolve the path of ino inside subvolume @root into @path_ret.
|
||||
*
|
||||
* @path_ret must be at least PATH_MAX size.
|
||||
*/
|
||||
static int get_path_in_subvol(struct btrfs_root *root, u64 ino, char *path_ret)
|
||||
{
|
||||
struct btrfs_root_ref rref;
|
||||
struct btrfs_inode_ref iref;
|
||||
struct btrfs_root root;
|
||||
u64 dir;
|
||||
char tmp[max(BTRFS_VOL_NAME_MAX, BTRFS_NAME_MAX)];
|
||||
char *ptr;
|
||||
struct btrfs_path path;
|
||||
struct btrfs_key key;
|
||||
char *tmp;
|
||||
u64 cur = ino;
|
||||
int ret = 0;
|
||||
|
||||
ptr = name + max_len - 1;
|
||||
*ptr = '\0';
|
||||
tmp = malloc(PATH_MAX);
|
||||
if (!tmp)
|
||||
return -ENOMEM;
|
||||
tmp[0] = '\0';
|
||||
|
||||
while (subvolid != BTRFS_FS_TREE_OBJECTID) {
|
||||
subvolid = btrfs_lookup_root_ref(subvolid, &rref, tmp);
|
||||
btrfs_init_path(&path);
|
||||
while (cur != BTRFS_FIRST_FREE_OBJECTID) {
|
||||
struct btrfs_inode_ref *iref;
|
||||
int name_len;
|
||||
|
||||
if (subvolid == -1ULL)
|
||||
return -1;
|
||||
btrfs_release_path(&path);
|
||||
key.objectid = cur;
|
||||
key.type = BTRFS_INODE_REF_KEY;
|
||||
key.offset = (u64)-1;
|
||||
|
||||
ptr -= rref.name_len + 1;
|
||||
if (ptr < name)
|
||||
goto too_long;
|
||||
ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
|
||||
/* Impossible */
|
||||
if (ret == 0)
|
||||
ret = -EUCLEAN;
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
ret = btrfs_previous_item(root, &path, cur,
|
||||
BTRFS_INODE_REF_KEY);
|
||||
if (ret > 0)
|
||||
ret = -ENOENT;
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
memcpy(ptr + 1, tmp, rref.name_len);
|
||||
*ptr = '/';
|
||||
strncpy(tmp, path_ret, PATH_MAX);
|
||||
iref = btrfs_item_ptr(path.nodes[0], path.slots[0],
|
||||
struct btrfs_inode_ref);
|
||||
name_len = btrfs_inode_ref_name_len(path.nodes[0],
|
||||
iref);
|
||||
if (name_len > BTRFS_NAME_LEN) {
|
||||
ret = -ENAMETOOLONG;
|
||||
goto out;
|
||||
}
|
||||
read_extent_buffer(path.nodes[0], path_ret,
|
||||
(unsigned long)(iref + 1), name_len);
|
||||
path_ret[name_len] = '/';
|
||||
path_ret[name_len + 1] = '\0';
|
||||
strncat(path_ret, tmp, PATH_MAX);
|
||||
|
||||
if (btrfs_find_root(subvolid, &root, NULL))
|
||||
return -1;
|
||||
btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
|
||||
cur = key.offset;
|
||||
}
|
||||
out:
|
||||
btrfs_release_path(&path);
|
||||
free(tmp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dir = rref.dirid;
|
||||
static int list_one_subvol(struct btrfs_root *root, char *path_ret)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = root->fs_info;
|
||||
struct btrfs_root *tree_root = fs_info->tree_root;
|
||||
struct btrfs_path path;
|
||||
struct btrfs_key key;
|
||||
char *tmp;
|
||||
u64 cur = root->root_key.objectid;
|
||||
int ret = 0;
|
||||
|
||||
while (dir != BTRFS_FIRST_FREE_OBJECTID) {
|
||||
dir = btrfs_lookup_inode_ref(&root, dir, &iref, tmp);
|
||||
tmp = malloc(PATH_MAX);
|
||||
if (!tmp)
|
||||
return -ENOMEM;
|
||||
tmp[0] = '\0';
|
||||
path_ret[0] = '\0';
|
||||
btrfs_init_path(&path);
|
||||
while (cur != BTRFS_FS_TREE_OBJECTID) {
|
||||
struct btrfs_root_ref *rr;
|
||||
struct btrfs_key location;
|
||||
int name_len;
|
||||
u64 ino;
|
||||
|
||||
if (dir == -1ULL)
|
||||
return -1;
|
||||
key.objectid = cur;
|
||||
key.type = BTRFS_ROOT_BACKREF_KEY;
|
||||
key.offset = (u64)-1;
|
||||
btrfs_release_path(&path);
|
||||
|
||||
ptr -= iref.name_len + 1;
|
||||
if (ptr < name)
|
||||
goto too_long;
|
||||
ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0);
|
||||
if (ret == 0)
|
||||
ret = -EUCLEAN;
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
ret = btrfs_previous_item(tree_root, &path, cur,
|
||||
BTRFS_ROOT_BACKREF_KEY);
|
||||
if (ret > 0)
|
||||
ret = -ENOENT;
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
memcpy(ptr + 1, tmp, iref.name_len);
|
||||
*ptr = '/';
|
||||
/* Get the subvolume name */
|
||||
rr = btrfs_item_ptr(path.nodes[0], path.slots[0],
|
||||
struct btrfs_root_ref);
|
||||
strncpy(tmp, path_ret, PATH_MAX);
|
||||
name_len = btrfs_root_ref_name_len(path.nodes[0], rr);
|
||||
if (name_len > BTRFS_NAME_LEN) {
|
||||
ret = -ENAMETOOLONG;
|
||||
goto out;
|
||||
}
|
||||
ino = btrfs_root_ref_dirid(path.nodes[0], rr);
|
||||
read_extent_buffer(path.nodes[0], path_ret,
|
||||
(unsigned long)(rr + 1), name_len);
|
||||
path_ret[name_len] = '/';
|
||||
path_ret[name_len + 1] = '\0';
|
||||
strncat(path_ret, tmp, PATH_MAX);
|
||||
|
||||
/* Get the path inside the parent subvolume */
|
||||
btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
|
||||
location.objectid = key.offset;
|
||||
location.type = BTRFS_ROOT_ITEM_KEY;
|
||||
location.offset = (u64)-1;
|
||||
root = btrfs_read_fs_root(fs_info, &location);
|
||||
if (IS_ERR(root)) {
|
||||
ret = PTR_ERR(root);
|
||||
goto out;
|
||||
}
|
||||
ret = get_path_in_subvol(root, ino, path_ret);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
cur = key.offset;
|
||||
}
|
||||
/* Add the leading '/' */
|
||||
strncpy(tmp, path_ret, PATH_MAX);
|
||||
strncpy(path_ret, "/", PATH_MAX);
|
||||
strncat(path_ret, tmp, PATH_MAX);
|
||||
out:
|
||||
btrfs_release_path(&path);
|
||||
free(tmp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int list_subvolums(struct btrfs_fs_info *fs_info)
|
||||
{
|
||||
struct btrfs_root *tree_root = fs_info->tree_root;
|
||||
struct btrfs_root *root;
|
||||
struct btrfs_path path;
|
||||
struct btrfs_key key;
|
||||
char *result;
|
||||
int ret = 0;
|
||||
|
||||
result = malloc(PATH_MAX);
|
||||
if (!result)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = list_one_subvol(fs_info->fs_root, result);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
root = fs_info->fs_root;
|
||||
printf("ID %llu gen %llu path %.*s\n",
|
||||
root->root_key.objectid, btrfs_root_generation(&root->root_item),
|
||||
PATH_MAX, result);
|
||||
|
||||
key.objectid = BTRFS_FIRST_FREE_OBJECTID;
|
||||
key.type = BTRFS_ROOT_ITEM_KEY;
|
||||
key.offset = 0;
|
||||
btrfs_init_path(&path);
|
||||
ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
while (1) {
|
||||
if (path.slots[0] >= btrfs_header_nritems(path.nodes[0]))
|
||||
goto next;
|
||||
|
||||
btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
|
||||
if (key.objectid > BTRFS_LAST_FREE_OBJECTID)
|
||||
break;
|
||||
if (key.objectid < BTRFS_FIRST_FREE_OBJECTID ||
|
||||
key.type != BTRFS_ROOT_ITEM_KEY)
|
||||
goto next;
|
||||
key.offset = (u64)-1;
|
||||
root = btrfs_read_fs_root(fs_info, &key);
|
||||
if (IS_ERR(root)) {
|
||||
ret = PTR_ERR(root);
|
||||
if (ret == -ENOENT)
|
||||
goto next;
|
||||
}
|
||||
ret = list_one_subvol(root, result);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
printf("ID %llu gen %llu path %.*s\n",
|
||||
root->root_key.objectid,
|
||||
btrfs_root_generation(&root->root_item),
|
||||
PATH_MAX, result);
|
||||
next:
|
||||
ret = btrfs_next_item(tree_root, &path);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (ret > 0) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ptr == name + max_len - 1) {
|
||||
name[0] = '/';
|
||||
name[1] = '\0';
|
||||
} else {
|
||||
memmove(name, ptr, name + max_len - ptr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
too_long:
|
||||
printf("%s: subvolume name too long\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
u64 btrfs_get_default_subvol_objectid(void)
|
||||
{
|
||||
struct btrfs_dir_item item;
|
||||
|
||||
if (btrfs_lookup_dir_item(&btrfs_info.tree_root,
|
||||
btrfs_info.sb.root_dir_objectid, "default", 7,
|
||||
&item))
|
||||
return BTRFS_FS_TREE_OBJECTID;
|
||||
return item.location.objectid;
|
||||
}
|
||||
|
||||
static void list_subvols(u64 tree, char *nameptr, int max_name_len, int level)
|
||||
{
|
||||
struct btrfs_key key, *found_key;
|
||||
struct btrfs_path path;
|
||||
struct btrfs_root_ref *ref;
|
||||
int res;
|
||||
|
||||
key.objectid = tree;
|
||||
key.type = BTRFS_ROOT_REF_KEY;
|
||||
key.offset = 0;
|
||||
|
||||
if (btrfs_search_tree(&btrfs_info.tree_root, &key, &path))
|
||||
return;
|
||||
|
||||
do {
|
||||
found_key = btrfs_path_leaf_key(&path);
|
||||
if (btrfs_comp_keys_type(&key, found_key))
|
||||
break;
|
||||
|
||||
ref = btrfs_path_item_ptr(&path, struct btrfs_root_ref);
|
||||
btrfs_root_ref_to_cpu(ref);
|
||||
|
||||
printf("ID %llu parent %llu name ", found_key->offset, tree);
|
||||
if (nameptr && !get_subvol_name(found_key->offset, nameptr,
|
||||
max_name_len))
|
||||
printf("%s\n", nameptr);
|
||||
else
|
||||
printf("%.*s\n", (int) ref->name_len,
|
||||
(const char *) (ref + 1));
|
||||
|
||||
if (level > 0)
|
||||
list_subvols(found_key->offset, nameptr, max_name_len,
|
||||
level - 1);
|
||||
else
|
||||
printf("%s: Too much recursion, maybe skipping some "
|
||||
"subvolumes\n", __func__);
|
||||
} while (!(res = btrfs_next_slot(&path)));
|
||||
|
||||
btrfs_free_path(&path);
|
||||
out:
|
||||
free(result);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void btrfs_list_subvols(void)
|
||||
{
|
||||
char *nameptr = malloc(4096);
|
||||
struct btrfs_fs_info *fs_info = current_fs_info;
|
||||
int ret;
|
||||
|
||||
list_subvols(BTRFS_FS_TREE_OBJECTID, nameptr, nameptr ? 4096 : 0, 40);
|
||||
|
||||
if (nameptr)
|
||||
free(nameptr);
|
||||
if (!fs_info)
|
||||
return;
|
||||
ret = list_subvolums(fs_info);
|
||||
if (ret < 0)
|
||||
error("failed to list subvolume: %d", ret);
|
||||
}
|
||||
|
|
257
fs/btrfs/super.c
257
fs/btrfs/super.c
|
@ -1,257 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* BTRFS filesystem implementation for U-Boot
|
||||
*
|
||||
* 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <log.h>
|
||||
#include <memalign.h>
|
||||
#include <part.h>
|
||||
#include <linux/compat.h>
|
||||
#include "btrfs.h"
|
||||
|
||||
#define BTRFS_SUPER_FLAG_SUPP (BTRFS_HEADER_FLAG_WRITTEN \
|
||||
| BTRFS_HEADER_FLAG_RELOC \
|
||||
| BTRFS_SUPER_FLAG_ERROR \
|
||||
| BTRFS_SUPER_FLAG_SEEDING \
|
||||
| BTRFS_SUPER_FLAG_METADUMP)
|
||||
|
||||
#define BTRFS_SUPER_INFO_SIZE 4096
|
||||
|
||||
/*
|
||||
* checks if a valid root backup is present.
|
||||
* considers the case when all root backups empty valid.
|
||||
* returns -1 in case of invalid root backup and 0 for valid.
|
||||
*/
|
||||
static int btrfs_check_super_roots(struct btrfs_super_block *sb)
|
||||
{
|
||||
struct btrfs_root_backup *root_backup;
|
||||
int i, newest = -1;
|
||||
int num_empty = 0;
|
||||
|
||||
for (i = 0; i < BTRFS_NUM_BACKUP_ROOTS; ++i) {
|
||||
root_backup = sb->super_roots + i;
|
||||
|
||||
if (root_backup->tree_root == 0 && root_backup->tree_root_gen == 0)
|
||||
num_empty++;
|
||||
|
||||
if (root_backup->tree_root_gen == sb->generation)
|
||||
newest = i;
|
||||
}
|
||||
|
||||
if (num_empty == BTRFS_NUM_BACKUP_ROOTS) {
|
||||
return 0;
|
||||
} else if (newest >= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int is_power_of_2(u64 x)
|
||||
{
|
||||
return !(x & (x - 1));
|
||||
}
|
||||
|
||||
static int btrfs_check_super_csum(char *raw_disk_sb)
|
||||
{
|
||||
struct btrfs_super_block *disk_sb =
|
||||
(struct btrfs_super_block *) raw_disk_sb;
|
||||
u16 csum_type = le16_to_cpu(disk_sb->csum_type);
|
||||
|
||||
if (csum_type == BTRFS_CSUM_TYPE_CRC32) {
|
||||
u32 crc = ~(u32) 0;
|
||||
const int csum_size = sizeof(crc);
|
||||
char result[csum_size];
|
||||
|
||||
crc = btrfs_csum_data(raw_disk_sb + BTRFS_CSUM_SIZE, crc,
|
||||
BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
|
||||
btrfs_csum_final(crc, result);
|
||||
|
||||
if (memcmp(raw_disk_sb, result, csum_size))
|
||||
return -1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btrfs_check_super(struct btrfs_super_block *sb)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (sb->flags & ~BTRFS_SUPER_FLAG_SUPP) {
|
||||
printf("%s: Unsupported flags: %llu\n", __func__,
|
||||
sb->flags & ~BTRFS_SUPER_FLAG_SUPP);
|
||||
}
|
||||
|
||||
if (sb->root_level > BTRFS_MAX_LEVEL) {
|
||||
printf("%s: tree_root level too big: %d >= %d\n", __func__,
|
||||
sb->root_level, BTRFS_MAX_LEVEL);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (sb->chunk_root_level > BTRFS_MAX_LEVEL) {
|
||||
printf("%s: chunk_root level too big: %d >= %d\n", __func__,
|
||||
sb->chunk_root_level, BTRFS_MAX_LEVEL);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (sb->log_root_level > BTRFS_MAX_LEVEL) {
|
||||
printf("%s: log_root level too big: %d >= %d\n", __func__,
|
||||
sb->log_root_level, BTRFS_MAX_LEVEL);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (!is_power_of_2(sb->sectorsize) || sb->sectorsize < 4096 ||
|
||||
sb->sectorsize > BTRFS_MAX_METADATA_BLOCKSIZE) {
|
||||
printf("%s: invalid sectorsize %u\n", __func__,
|
||||
sb->sectorsize);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (!is_power_of_2(sb->nodesize) || sb->nodesize < sb->sectorsize ||
|
||||
sb->nodesize > BTRFS_MAX_METADATA_BLOCKSIZE) {
|
||||
printf("%s: invalid nodesize %u\n", __func__, sb->nodesize);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (sb->nodesize != sb->__unused_leafsize) {
|
||||
printf("%s: invalid leafsize %u, should be %u\n", __func__,
|
||||
sb->__unused_leafsize, sb->nodesize);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (!IS_ALIGNED(sb->root, sb->sectorsize)) {
|
||||
printf("%s: tree_root block unaligned: %llu\n", __func__,
|
||||
sb->root);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (!IS_ALIGNED(sb->chunk_root, sb->sectorsize)) {
|
||||
printf("%s: chunk_root block unaligned: %llu\n", __func__,
|
||||
sb->chunk_root);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (!IS_ALIGNED(sb->log_root, sb->sectorsize)) {
|
||||
printf("%s: log_root block unaligned: %llu\n", __func__,
|
||||
sb->log_root);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (memcmp(sb->fsid, sb->dev_item.fsid, BTRFS_UUID_SIZE) != 0) {
|
||||
printf("%s: dev_item UUID does not match fsid\n", __func__);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (sb->bytes_used < 6*sb->nodesize) {
|
||||
printf("%s: bytes_used is too small %llu\n", __func__,
|
||||
sb->bytes_used);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (!is_power_of_2(sb->stripesize)) {
|
||||
printf("%s: invalid stripesize %u\n", __func__, sb->stripesize);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (sb->sys_chunk_array_size > BTRFS_SYSTEM_CHUNK_ARRAY_SIZE) {
|
||||
printf("%s: system chunk array too big %u > %u\n", __func__,
|
||||
sb->sys_chunk_array_size, BTRFS_SYSTEM_CHUNK_ARRAY_SIZE);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (sb->sys_chunk_array_size < sizeof(struct btrfs_key) +
|
||||
sizeof(struct btrfs_chunk)) {
|
||||
printf("%s: system chunk array too small %u < %zu\n", __func__,
|
||||
sb->sys_chunk_array_size, sizeof(struct btrfs_key)
|
||||
+ sizeof(struct btrfs_chunk));
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btrfs_read_superblock(void)
|
||||
{
|
||||
const u64 superblock_offsets[4] = {
|
||||
0x10000ull,
|
||||
0x4000000ull,
|
||||
0x4000000000ull,
|
||||
0x4000000000000ull
|
||||
};
|
||||
ALLOC_CACHE_ALIGN_BUFFER(char, raw_sb, BTRFS_SUPER_INFO_SIZE);
|
||||
struct btrfs_super_block *sb = (struct btrfs_super_block *) raw_sb;
|
||||
u64 dev_total_bytes;
|
||||
int i;
|
||||
|
||||
dev_total_bytes = (u64) btrfs_part_info->size * btrfs_part_info->blksz;
|
||||
|
||||
btrfs_info.sb.generation = 0;
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
if (superblock_offsets[i] + sizeof(sb) > dev_total_bytes)
|
||||
break;
|
||||
|
||||
if (!btrfs_devread(superblock_offsets[i], BTRFS_SUPER_INFO_SIZE,
|
||||
raw_sb))
|
||||
break;
|
||||
|
||||
if (btrfs_check_super_csum(raw_sb)) {
|
||||
debug("%s: invalid checksum at superblock mirror %i\n",
|
||||
__func__, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
btrfs_super_block_to_cpu(sb);
|
||||
|
||||
if (sb->magic != BTRFS_MAGIC) {
|
||||
debug("%s: invalid BTRFS magic 0x%016llX at "
|
||||
"superblock mirror %i\n", __func__, sb->magic, i);
|
||||
} else if (sb->bytenr != superblock_offsets[i]) {
|
||||
printf("%s: invalid bytenr 0x%016llX (expected "
|
||||
"0x%016llX) at superblock mirror %i\n",
|
||||
__func__, sb->bytenr, superblock_offsets[i], i);
|
||||
} else if (btrfs_check_super(sb)) {
|
||||
printf("%s: Checking superblock mirror %i failed\n",
|
||||
__func__, i);
|
||||
} else if (sb->generation > btrfs_info.sb.generation) {
|
||||
memcpy(&btrfs_info.sb, sb, sizeof(*sb));
|
||||
} else {
|
||||
/* Nothing */
|
||||
}
|
||||
}
|
||||
|
||||
if (!btrfs_info.sb.generation) {
|
||||
debug("%s: No valid BTRFS superblock found!\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (btrfs_check_super_roots(&btrfs_info.sb)) {
|
||||
printf("%s: No valid root_backup found!\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sb->sectorsize != PAGE_SIZE) {
|
||||
printf(
|
||||
"%s: Unsupported sector size (%u), only supports %u as sector size\n",
|
||||
__func__, sb->sectorsize, PAGE_SIZE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (btrfs_info.sb.num_devices != 1) {
|
||||
printf("%s: Unsupported number of devices (%lli). This driver "
|
||||
"only supports filesystem on one device.\n", __func__,
|
||||
btrfs_info.sb.num_devices);
|
||||
return -1;
|
||||
}
|
||||
|
||||
debug("Chosen superblock with generation = %llu\n",
|
||||
btrfs_info.sb.generation);
|
||||
|
||||
return 0;
|
||||
}
|
1173
fs/btrfs/volumes.c
Normal file
1173
fs/btrfs/volumes.c
Normal file
File diff suppressed because it is too large
Load diff
204
fs/btrfs/volumes.h
Normal file
204
fs/btrfs/volumes.h
Normal file
|
@ -0,0 +1,204 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
#ifndef __BTRFS_VOLUMES_H__
|
||||
#define __BTRFS_VOLUMES_H__
|
||||
|
||||
#include <fs_internal.h>
|
||||
#include "ctree.h"
|
||||
|
||||
#define BTRFS_STRIPE_LEN SZ_64K
|
||||
|
||||
struct btrfs_device {
|
||||
struct list_head dev_list;
|
||||
struct btrfs_root *dev_root;
|
||||
struct btrfs_fs_devices *fs_devices;
|
||||
|
||||
struct blk_desc *desc;
|
||||
struct disk_partition *part;
|
||||
|
||||
u64 total_devs;
|
||||
u64 super_bytes_used;
|
||||
|
||||
u64 generation;
|
||||
|
||||
/* the internal btrfs device id */
|
||||
u64 devid;
|
||||
|
||||
/* size of the device */
|
||||
u64 total_bytes;
|
||||
|
||||
/* bytes used */
|
||||
u64 bytes_used;
|
||||
|
||||
/* optimal io alignment for this device */
|
||||
u32 io_align;
|
||||
|
||||
/* optimal io width for this device */
|
||||
u32 io_width;
|
||||
|
||||
/* minimal io size for this device */
|
||||
u32 sector_size;
|
||||
|
||||
/* type and info about this device */
|
||||
u64 type;
|
||||
|
||||
/* physical drive uuid (or lvm uuid) */
|
||||
u8 uuid[BTRFS_UUID_SIZE];
|
||||
};
|
||||
|
||||
struct btrfs_fs_devices {
|
||||
u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */
|
||||
u8 metadata_uuid[BTRFS_FSID_SIZE]; /* FS specific uuid */
|
||||
|
||||
u64 latest_devid;
|
||||
u64 lowest_devid;
|
||||
u64 latest_trans;
|
||||
|
||||
u64 total_rw_bytes;
|
||||
|
||||
struct list_head devices;
|
||||
struct list_head list;
|
||||
|
||||
int seeding;
|
||||
struct btrfs_fs_devices *seed;
|
||||
};
|
||||
|
||||
struct btrfs_bio_stripe {
|
||||
struct btrfs_device *dev;
|
||||
u64 physical;
|
||||
};
|
||||
|
||||
struct btrfs_multi_bio {
|
||||
int error;
|
||||
int num_stripes;
|
||||
struct btrfs_bio_stripe stripes[];
|
||||
};
|
||||
|
||||
struct map_lookup {
|
||||
struct cache_extent ce;
|
||||
u64 type;
|
||||
int io_align;
|
||||
int io_width;
|
||||
int stripe_len;
|
||||
int sector_size;
|
||||
int num_stripes;
|
||||
int sub_stripes;
|
||||
struct btrfs_bio_stripe stripes[];
|
||||
};
|
||||
|
||||
struct btrfs_raid_attr {
|
||||
int sub_stripes; /* sub_stripes info for map */
|
||||
int dev_stripes; /* stripes per dev */
|
||||
int devs_max; /* max devs to use */
|
||||
int devs_min; /* min devs needed */
|
||||
int tolerated_failures; /* max tolerated fail devs */
|
||||
int devs_increment; /* ndevs has to be a multiple of this */
|
||||
int ncopies; /* how many copies to data has */
|
||||
int nparity; /* number of stripes worth of bytes to store
|
||||
* parity information */
|
||||
const char raid_name[8]; /* name of the raid */
|
||||
u64 bg_flag; /* block group flag of the raid */
|
||||
};
|
||||
|
||||
extern const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES];
|
||||
|
||||
static inline enum btrfs_raid_types btrfs_bg_flags_to_raid_index(u64 flags)
|
||||
{
|
||||
if (flags & BTRFS_BLOCK_GROUP_RAID10)
|
||||
return BTRFS_RAID_RAID10;
|
||||
else if (flags & BTRFS_BLOCK_GROUP_RAID1)
|
||||
return BTRFS_RAID_RAID1;
|
||||
else if (flags & BTRFS_BLOCK_GROUP_RAID1C3)
|
||||
return BTRFS_RAID_RAID1C3;
|
||||
else if (flags & BTRFS_BLOCK_GROUP_RAID1C4)
|
||||
return BTRFS_RAID_RAID1C4;
|
||||
else if (flags & BTRFS_BLOCK_GROUP_DUP)
|
||||
return BTRFS_RAID_DUP;
|
||||
else if (flags & BTRFS_BLOCK_GROUP_RAID0)
|
||||
return BTRFS_RAID_RAID0;
|
||||
else if (flags & BTRFS_BLOCK_GROUP_RAID5)
|
||||
return BTRFS_RAID_RAID5;
|
||||
else if (flags & BTRFS_BLOCK_GROUP_RAID6)
|
||||
return BTRFS_RAID_RAID6;
|
||||
|
||||
return BTRFS_RAID_SINGLE; /* BTRFS_BLOCK_GROUP_SINGLE */
|
||||
}
|
||||
|
||||
#define btrfs_multi_bio_size(n) (sizeof(struct btrfs_multi_bio) + \
|
||||
(sizeof(struct btrfs_bio_stripe) * (n)))
|
||||
#define btrfs_map_lookup_size(n) (sizeof(struct map_lookup) + \
|
||||
(sizeof(struct btrfs_bio_stripe) * (n)))
|
||||
|
||||
#define BTRFS_RAID5_P_STRIPE ((u64)-2)
|
||||
#define BTRFS_RAID6_Q_STRIPE ((u64)-1)
|
||||
|
||||
static inline u64 calc_stripe_length(u64 type, u64 length, int num_stripes)
|
||||
{
|
||||
u64 stripe_size;
|
||||
|
||||
if (type & BTRFS_BLOCK_GROUP_RAID0) {
|
||||
stripe_size = length;
|
||||
stripe_size /= num_stripes;
|
||||
} else if (type & BTRFS_BLOCK_GROUP_RAID10) {
|
||||
stripe_size = length * 2;
|
||||
stripe_size /= num_stripes;
|
||||
} else if (type & BTRFS_BLOCK_GROUP_RAID5) {
|
||||
stripe_size = length;
|
||||
stripe_size /= (num_stripes - 1);
|
||||
} else if (type & BTRFS_BLOCK_GROUP_RAID6) {
|
||||
stripe_size = length;
|
||||
stripe_size /= (num_stripes - 2);
|
||||
} else {
|
||||
stripe_size = length;
|
||||
}
|
||||
return stripe_size;
|
||||
}
|
||||
|
||||
#ifndef READ
|
||||
#define READ 0
|
||||
#define WRITE 1
|
||||
#define READA 2
|
||||
#endif
|
||||
|
||||
int __btrfs_map_block(struct btrfs_fs_info *fs_info, int rw,
|
||||
u64 logical, u64 *length, u64 *type,
|
||||
struct btrfs_multi_bio **multi_ret, int mirror_num,
|
||||
u64 **raid_map);
|
||||
int btrfs_map_block(struct btrfs_fs_info *fs_info, int rw,
|
||||
u64 logical, u64 *length,
|
||||
struct btrfs_multi_bio **multi_ret, int mirror_num,
|
||||
u64 **raid_map_ret);
|
||||
int btrfs_next_bg(struct btrfs_fs_info *map_tree, u64 *logical,
|
||||
u64 *size, u64 type);
|
||||
static inline int btrfs_next_bg_metadata(struct btrfs_fs_info *fs_info,
|
||||
u64 *logical, u64 *size)
|
||||
{
|
||||
return btrfs_next_bg(fs_info, logical, size,
|
||||
BTRFS_BLOCK_GROUP_METADATA);
|
||||
}
|
||||
static inline int btrfs_next_bg_system(struct btrfs_fs_info *fs_info,
|
||||
u64 *logical, u64 *size)
|
||||
{
|
||||
return btrfs_next_bg(fs_info, logical, size,
|
||||
BTRFS_BLOCK_GROUP_SYSTEM);
|
||||
}
|
||||
int btrfs_read_sys_array(struct btrfs_fs_info *fs_info);
|
||||
int btrfs_read_chunk_tree(struct btrfs_fs_info *fs_info);
|
||||
int btrfs_open_devices(struct btrfs_fs_devices *fs_devices);
|
||||
int btrfs_close_devices(struct btrfs_fs_devices *fs_devices);
|
||||
void btrfs_close_all_devices(void);
|
||||
int btrfs_num_copies(struct btrfs_fs_info *fs_info, u64 logical, u64 len);
|
||||
int btrfs_scan_one_device(struct blk_desc *desc, struct disk_partition *part,
|
||||
struct btrfs_fs_devices **fs_devices_ret,
|
||||
u64 *total_devs);
|
||||
struct list_head *btrfs_scanned_uuids(void);
|
||||
struct btrfs_device *btrfs_find_device(struct btrfs_fs_info *fs_info, u64 devid,
|
||||
u8 *uuid, u8 *fsid);
|
||||
int btrfs_check_chunk_valid(struct btrfs_fs_info *fs_info,
|
||||
struct extent_buffer *leaf,
|
||||
struct btrfs_chunk *chunk,
|
||||
int slot, u64 logical);
|
||||
u64 btrfs_stripe_length(struct btrfs_fs_info *fs_info,
|
||||
struct extent_buffer *leaf,
|
||||
struct btrfs_chunk *chunk);
|
||||
#endif
|
Loading…
Reference in a new issue