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:
Tom Rini 2020-09-07 21:00:47 -04:00
commit 2a9f9d633d
30 changed files with 8598 additions and 2564 deletions

View file

@ -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/

View file

@ -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

View file

@ -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;

View file

@ -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__ */

View file

@ -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__ */

View file

@ -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;
}

View 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);
}
}

View 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
View 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

View file

@ -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
View 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
View 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

View file

@ -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;
}

File diff suppressed because it is too large Load diff

View file

@ -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

File diff suppressed because it is too large Load diff

50
fs/btrfs/disk-io.h Normal file
View 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
View 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
View 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

View file

@ -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
View 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

View file

@ -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);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

47
fs/btrfs/root-tree.c Normal file
View 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;
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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

File diff suppressed because it is too large Load diff

204
fs/btrfs/volumes.h Normal file
View 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