mirror of
https://github.com/Zygo/bees.git
synced 2025-05-17 21:35:45 +02:00
btrfs-tree: introduce lightweight classes for btrfs tree search operations
btrfs-tree provides classes for low-level access to btrfs tree objects. An item class is provided to decode polymorphic btrfs item fields. Several tree classes provide forward and backward iteration over raw object items at different tree levels. A csum tree class provides convenient access to csums by bytenr, supporting all current btrfs csum types. Wrapper classes for inode and subvol items provide direct access to btrfs metadata fields without clumsy stat() wrappers or ioctls. Signed-off-by: Zygo Blaxell <bees@furryterror.org>
This commit is contained in:
parent
24b904f002
commit
4d59939b07
199
include/crucible/btrfs-tree.h
Normal file
199
include/crucible/btrfs-tree.h
Normal file
@ -0,0 +1,199 @@
|
||||
#ifndef CRUCIBLE_BTRFS_TREE_H
|
||||
#define CRUCIBLE_BTRFS_TREE_H
|
||||
|
||||
#include "crucible/fd.h"
|
||||
#include "crucible/fs.h"
|
||||
#include "crucible/bytevector.h"
|
||||
|
||||
namespace crucible {
|
||||
using namespace std;
|
||||
|
||||
class BtrfsTreeItem {
|
||||
uint64_t m_objectid = 0;
|
||||
uint64_t m_offset = 0;
|
||||
uint64_t m_transid = 0;
|
||||
ByteVector m_data;
|
||||
uint8_t m_type = 0;
|
||||
public:
|
||||
uint64_t objectid() const { return m_objectid; }
|
||||
uint64_t offset() const { return m_offset; }
|
||||
uint64_t transid() const { return m_transid; }
|
||||
uint8_t type() const { return m_type; }
|
||||
const ByteVector data() const { return m_data; }
|
||||
BtrfsTreeItem() = default;
|
||||
BtrfsTreeItem(const BtrfsIoctlSearchHeader &bish);
|
||||
BtrfsTreeItem& operator=(const BtrfsIoctlSearchHeader &bish);
|
||||
bool operator!() const;
|
||||
|
||||
/// Member access methods. Invoking a method on the
|
||||
/// wrong type of item will throw an exception.
|
||||
|
||||
/// @{ Block group items
|
||||
uint64_t block_group_flags() const;
|
||||
uint64_t block_group_used() const;
|
||||
/// @}
|
||||
|
||||
/// @{ Dev extent items (physical byte ranges)
|
||||
uint64_t dev_extent_chunk_offset() const;
|
||||
uint64_t dev_extent_length() const;
|
||||
/// @}
|
||||
|
||||
/// @{ Dev items (devices)
|
||||
uint64_t dev_item_total_bytes() const;
|
||||
uint64_t dev_item_bytes_used() const;
|
||||
/// @}
|
||||
|
||||
/// @{ Inode items
|
||||
uint64_t inode_size() const;
|
||||
/// @}
|
||||
|
||||
/// @{ Extent refs (EXTENT_DATA)
|
||||
uint64_t file_extent_logical_bytes() const;
|
||||
uint64_t file_extent_generation() const;
|
||||
uint64_t file_extent_offset() const;
|
||||
uint64_t file_extent_bytenr() const;
|
||||
uint8_t file_extent_type() const;
|
||||
btrfs_compression_type file_extent_compression() const;
|
||||
/// @}
|
||||
|
||||
/// @{ Extent items (EXTENT_ITEM)
|
||||
uint64_t extent_begin() const;
|
||||
uint64_t extent_end() const;
|
||||
uint64_t extent_generation() const;
|
||||
/// @}
|
||||
|
||||
/// @{ Root items
|
||||
uint64_t root_flags() const;
|
||||
/// @}
|
||||
|
||||
/// @{ Root backref items.
|
||||
uint64_t root_ref_dirid() const;
|
||||
string root_ref_name() const;
|
||||
uint64_t root_ref_parent_rootid() const;
|
||||
/// @}
|
||||
};
|
||||
|
||||
ostream &operator<<(ostream &os, const BtrfsTreeItem &bti);
|
||||
|
||||
class BtrfsTreeFetcher {
|
||||
protected:
|
||||
Fd m_fd;
|
||||
BtrfsIoctlSearchKey m_sk;
|
||||
uint64_t m_tree = 0;
|
||||
uint64_t m_min_transid = 0;
|
||||
uint64_t m_max_transid = numeric_limits<uint64_t>::max();
|
||||
uint64_t m_block_size = 0;
|
||||
uint64_t m_lookbehind_size = 0;
|
||||
uint64_t m_scale_size = 0;
|
||||
uint8_t m_type = 0;
|
||||
|
||||
uint64_t scale_logical(uint64_t logical) const;
|
||||
uint64_t unscale_logical(uint64_t logical) const;
|
||||
const static uint64_t s_max_logical = numeric_limits<uint64_t>::max();
|
||||
uint64_t scaled_max_logical() const;
|
||||
|
||||
virtual void fill_sk(BtrfsIoctlSearchKey &key, uint64_t object);
|
||||
virtual void next_sk(BtrfsIoctlSearchKey &key, const BtrfsIoctlSearchHeader &hdr);
|
||||
virtual uint64_t hdr_logical(const BtrfsIoctlSearchHeader &hdr) = 0;
|
||||
virtual bool hdr_match(const BtrfsIoctlSearchHeader &hdr) = 0;
|
||||
virtual bool hdr_stop(const BtrfsIoctlSearchHeader &hdr) = 0;
|
||||
Fd fd() const;
|
||||
void fd(Fd fd);
|
||||
public:
|
||||
virtual ~BtrfsTreeFetcher() = default;
|
||||
BtrfsTreeFetcher(Fd new_fd);
|
||||
void type(uint8_t type);
|
||||
void tree(uint64_t tree);
|
||||
void transid(uint64_t min_transid, uint64_t max_transid = numeric_limits<uint64_t>::max());
|
||||
/// Block size (sectorsize) of filesystem
|
||||
uint64_t block_size() const;
|
||||
/// Fetch last object < logical, null if not found
|
||||
BtrfsTreeItem prev(uint64_t logical);
|
||||
/// Fetch first object > logical, null if not found
|
||||
BtrfsTreeItem next(uint64_t logical);
|
||||
/// Fetch object at exactly logical, null if not found
|
||||
BtrfsTreeItem at(uint64_t);
|
||||
/// Fetch first object >= logical
|
||||
BtrfsTreeItem lower_bound(uint64_t logical);
|
||||
/// Fetch last object <= logical
|
||||
BtrfsTreeItem rlower_bound(uint64_t logical);
|
||||
|
||||
/// Estimated distance between objects
|
||||
virtual uint64_t lookbehind_size() const;
|
||||
virtual void lookbehind_size(uint64_t);
|
||||
|
||||
/// Scale size (normally block size but must be set to 1 for fs trees)
|
||||
uint64_t scale_size() const;
|
||||
void scale_size(uint64_t);
|
||||
};
|
||||
|
||||
class BtrfsTreeObjectFetcher : public BtrfsTreeFetcher {
|
||||
protected:
|
||||
virtual void fill_sk(BtrfsIoctlSearchKey &key, uint64_t logical) override;
|
||||
virtual uint64_t hdr_logical(const BtrfsIoctlSearchHeader &hdr) override;
|
||||
virtual bool hdr_match(const BtrfsIoctlSearchHeader &hdr) override;
|
||||
virtual bool hdr_stop(const BtrfsIoctlSearchHeader &hdr) override;
|
||||
public:
|
||||
using BtrfsTreeFetcher::BtrfsTreeFetcher;
|
||||
};
|
||||
|
||||
class BtrfsTreeOffsetFetcher : public BtrfsTreeFetcher {
|
||||
protected:
|
||||
uint64_t m_objectid = 0;
|
||||
virtual void fill_sk(BtrfsIoctlSearchKey &key, uint64_t offset) override;
|
||||
virtual uint64_t hdr_logical(const BtrfsIoctlSearchHeader &hdr) override;
|
||||
virtual bool hdr_match(const BtrfsIoctlSearchHeader &hdr) override;
|
||||
virtual bool hdr_stop(const BtrfsIoctlSearchHeader &hdr) override;
|
||||
public:
|
||||
using BtrfsTreeFetcher::BtrfsTreeFetcher;
|
||||
void objectid(uint64_t objectid);
|
||||
uint64_t objectid() const;
|
||||
};
|
||||
|
||||
class BtrfsCsumTreeFetcher : public BtrfsTreeOffsetFetcher {
|
||||
public:
|
||||
const uint32_t BTRFS_CSUM_TYPE_UNKNOWN = uint32_t(1) << 16;
|
||||
private:
|
||||
size_t m_sum_size = 0;
|
||||
uint32_t m_sum_type = BTRFS_CSUM_TYPE_UNKNOWN;
|
||||
public:
|
||||
BtrfsCsumTreeFetcher(const Fd &fd);
|
||||
|
||||
uint32_t sum_type() const;
|
||||
size_t sum_size() const;
|
||||
void get_sums(uint64_t logical, size_t count, function<void(uint64_t logical, const uint8_t *buf, size_t count)> output);
|
||||
};
|
||||
|
||||
/// Fetch extent items from extent tree
|
||||
class BtrfsExtentItemFetcher : public BtrfsTreeObjectFetcher {
|
||||
public:
|
||||
BtrfsExtentItemFetcher(const Fd &fd);
|
||||
};
|
||||
|
||||
/// Fetch extent refs from an inode
|
||||
class BtrfsExtentDataFetcher : public BtrfsTreeOffsetFetcher {
|
||||
public:
|
||||
BtrfsExtentDataFetcher(const Fd &fd);
|
||||
};
|
||||
|
||||
/// Fetch inodes from a subvol
|
||||
class BtrfsFsTreeFetcher : public BtrfsTreeObjectFetcher {
|
||||
public:
|
||||
BtrfsFsTreeFetcher(const Fd &fd, uint64_t subvol);
|
||||
};
|
||||
|
||||
class BtrfsInodeFetcher : public BtrfsTreeObjectFetcher {
|
||||
public:
|
||||
BtrfsInodeFetcher(const Fd &fd);
|
||||
BtrfsTreeItem stat(uint64_t subvol, uint64_t inode);
|
||||
};
|
||||
|
||||
class BtrfsRootFetcher : public BtrfsTreeObjectFetcher {
|
||||
public:
|
||||
BtrfsRootFetcher(const Fd &fd);
|
||||
BtrfsTreeItem root(uint64_t subvol);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -5,6 +5,7 @@ default: libcrucible.a
|
||||
|
||||
CRUCIBLE_OBJS = \
|
||||
bytevector.o \
|
||||
btrfs-tree.o \
|
||||
chatter.o \
|
||||
city.o \
|
||||
cleanup.o \
|
||||
|
671
lib/btrfs-tree.cc
Normal file
671
lib/btrfs-tree.cc
Normal file
@ -0,0 +1,671 @@
|
||||
#include "crucible/btrfs-tree.h"
|
||||
#include "crucible/btrfs.h"
|
||||
#include "crucible/error.h"
|
||||
#include "crucible/fs.h"
|
||||
#include "crucible/hexdump.h"
|
||||
#include "crucible/seeker.h"
|
||||
|
||||
namespace crucible {
|
||||
using namespace std;
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeItem::extent_begin() const
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, btrfs_search_type_ntoa(m_type), m_type == BTRFS_EXTENT_ITEM_KEY);
|
||||
return m_objectid;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeItem::extent_end() const
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, btrfs_search_type_ntoa(m_type), m_type == BTRFS_EXTENT_ITEM_KEY);
|
||||
return m_objectid + m_offset;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeItem::extent_generation() const
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, btrfs_search_type_ntoa(m_type), m_type == BTRFS_EXTENT_ITEM_KEY);
|
||||
return btrfs_get_member(&btrfs_extent_item::generation, m_data);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeItem::root_ref_dirid() const
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, btrfs_search_type_ntoa(m_type), m_type == BTRFS_ROOT_BACKREF_KEY);
|
||||
return btrfs_get_member(&btrfs_root_ref::dirid, m_data);
|
||||
}
|
||||
|
||||
string
|
||||
BtrfsTreeItem::root_ref_name() const
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, btrfs_search_type_ntoa(m_type), m_type == BTRFS_ROOT_BACKREF_KEY);
|
||||
const auto name_len = btrfs_get_member(&btrfs_root_ref::name_len, m_data);
|
||||
const auto name_start = sizeof(struct btrfs_root_ref);
|
||||
const auto name_end = name_len + name_start;
|
||||
THROW_CHECK2(runtime_error, m_data.size(), name_end, m_data.size() >= name_end);
|
||||
return string(m_data.data() + name_start, m_data.data() + name_end);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeItem::root_ref_parent_rootid() const
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, btrfs_search_type_ntoa(m_type), m_type == BTRFS_ROOT_BACKREF_KEY);
|
||||
return offset();
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeItem::root_flags() const
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, btrfs_search_type_ntoa(m_type), m_type == BTRFS_ROOT_ITEM_KEY);
|
||||
return btrfs_get_member(&btrfs_root_item::flags, m_data);
|
||||
}
|
||||
|
||||
ostream &
|
||||
operator<<(ostream &os, const BtrfsTreeItem &bti)
|
||||
{
|
||||
os << "BtrfsTreeItem {"
|
||||
<< " objectid = " << to_hex(bti.objectid())
|
||||
<< ", type = " << btrfs_search_type_ntoa(bti.type())
|
||||
<< ", offset = " << to_hex(bti.offset())
|
||||
<< ", transid = " << bti.transid()
|
||||
<< ", data = ";
|
||||
hexdump(os, bti.data());
|
||||
return os;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeItem::block_group_flags() const
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, m_type, m_type == BTRFS_BLOCK_GROUP_ITEM_KEY);
|
||||
return btrfs_get_member(&btrfs_block_group_item::flags, m_data);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeItem::block_group_used() const
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, m_type, m_type == BTRFS_BLOCK_GROUP_ITEM_KEY);
|
||||
return btrfs_get_member(&btrfs_block_group_item::used, m_data);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeItem::dev_extent_chunk_offset() const
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, m_type, m_type == BTRFS_DEV_EXTENT_KEY);
|
||||
return btrfs_get_member(&btrfs_dev_extent::chunk_offset, m_data);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeItem::dev_extent_length() const
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, m_type, m_type == BTRFS_DEV_EXTENT_KEY);
|
||||
return btrfs_get_member(&btrfs_dev_extent::length, m_data);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeItem::dev_item_total_bytes() const
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, m_type, m_type == BTRFS_DEV_ITEM_KEY);
|
||||
return btrfs_get_member(&btrfs_dev_item::total_bytes, m_data);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeItem::dev_item_bytes_used() const
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, m_type, m_type == BTRFS_DEV_ITEM_KEY);
|
||||
return btrfs_get_member(&btrfs_dev_item::bytes_used, m_data);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
|
||||
BtrfsTreeItem::inode_size() const
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, m_type, m_type == BTRFS_INODE_ITEM_KEY);
|
||||
return btrfs_get_member(&btrfs_inode_item::size, m_data);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeItem::file_extent_logical_bytes() const
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, m_type, m_type == BTRFS_EXTENT_DATA_KEY);
|
||||
const auto file_extent_item_type = btrfs_get_member(&btrfs_file_extent_item::type, m_data);
|
||||
switch (file_extent_item_type) {
|
||||
case BTRFS_FILE_EXTENT_INLINE:
|
||||
return btrfs_get_member(&btrfs_file_extent_item::ram_bytes, m_data);
|
||||
case BTRFS_FILE_EXTENT_PREALLOC:
|
||||
case BTRFS_FILE_EXTENT_REG:
|
||||
return btrfs_get_member(&btrfs_file_extent_item::num_bytes, m_data);
|
||||
default:
|
||||
THROW_ERROR(runtime_error, "unknown btrfs_file_extent_item type " << file_extent_item_type);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeItem::file_extent_offset() const
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, m_type, m_type == BTRFS_EXTENT_DATA_KEY);
|
||||
const auto file_extent_item_type = btrfs_get_member(&btrfs_file_extent_item::type, m_data);
|
||||
switch (file_extent_item_type) {
|
||||
case BTRFS_FILE_EXTENT_INLINE:
|
||||
THROW_ERROR(invalid_argument, "extent is inline " << *this);
|
||||
case BTRFS_FILE_EXTENT_PREALLOC:
|
||||
case BTRFS_FILE_EXTENT_REG:
|
||||
return btrfs_get_member(&btrfs_file_extent_item::offset, m_data);
|
||||
default:
|
||||
THROW_ERROR(runtime_error, "unknown btrfs_file_extent_item type " << file_extent_item_type << " in " << *this);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeItem::file_extent_generation() const
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, m_type, m_type == BTRFS_EXTENT_DATA_KEY);
|
||||
return btrfs_get_member(&btrfs_file_extent_item::generation, m_data);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeItem::file_extent_bytenr() const
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, m_type, m_type == BTRFS_EXTENT_DATA_KEY);
|
||||
auto file_extent_item_type = btrfs_get_member(&btrfs_file_extent_item::type, m_data);
|
||||
switch (file_extent_item_type) {
|
||||
case BTRFS_FILE_EXTENT_INLINE:
|
||||
THROW_ERROR(invalid_argument, "extent is inline " << *this);
|
||||
case BTRFS_FILE_EXTENT_PREALLOC:
|
||||
case BTRFS_FILE_EXTENT_REG:
|
||||
return btrfs_get_member(&btrfs_file_extent_item::disk_bytenr, m_data);
|
||||
default:
|
||||
THROW_ERROR(runtime_error, "unknown btrfs_file_extent_item type " << file_extent_item_type << " in " << *this);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t
|
||||
BtrfsTreeItem::file_extent_type() const
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, m_type, m_type == BTRFS_EXTENT_DATA_KEY);
|
||||
return btrfs_get_member(&btrfs_file_extent_item::type, m_data);
|
||||
}
|
||||
|
||||
btrfs_compression_type
|
||||
BtrfsTreeItem::file_extent_compression() const
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, m_type, m_type == BTRFS_EXTENT_DATA_KEY);
|
||||
return static_cast<btrfs_compression_type>(btrfs_get_member(&btrfs_file_extent_item::compression, m_data));
|
||||
}
|
||||
|
||||
BtrfsTreeItem::BtrfsTreeItem(const BtrfsIoctlSearchHeader &bish) :
|
||||
m_objectid(bish.objectid),
|
||||
m_offset(bish.offset),
|
||||
m_transid(bish.transid),
|
||||
m_data(bish.m_data),
|
||||
m_type(bish.type)
|
||||
{
|
||||
}
|
||||
|
||||
BtrfsTreeItem &
|
||||
BtrfsTreeItem::operator=(const BtrfsIoctlSearchHeader &bish)
|
||||
{
|
||||
m_objectid = bish.objectid;
|
||||
m_offset = bish.offset;
|
||||
m_transid = bish.transid;
|
||||
m_data = bish.m_data;
|
||||
m_type = bish.type;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool
|
||||
BtrfsTreeItem::operator!() const
|
||||
{
|
||||
return m_transid == 0 && m_objectid == 0 && m_offset == 0 && m_type == 0;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeFetcher::block_size() const
|
||||
{
|
||||
return m_block_size;
|
||||
}
|
||||
|
||||
BtrfsTreeFetcher::BtrfsTreeFetcher(Fd new_fd) :
|
||||
m_fd(new_fd)
|
||||
{
|
||||
BtrfsIoctlFsInfoArgs bifia;
|
||||
bifia.do_ioctl(fd());
|
||||
m_block_size = bifia.sectorsize;
|
||||
THROW_CHECK1(runtime_error, m_block_size, m_block_size > 0);
|
||||
// We don't believe sector sizes that aren't multiples of 4K
|
||||
THROW_CHECK1(runtime_error, m_block_size, (m_block_size % 4096) == 0);
|
||||
m_lookbehind_size = 128 * 1024;
|
||||
m_scale_size = m_block_size;
|
||||
}
|
||||
|
||||
Fd
|
||||
BtrfsTreeFetcher::fd() const
|
||||
{
|
||||
return m_fd;
|
||||
}
|
||||
|
||||
void
|
||||
BtrfsTreeFetcher::fd(Fd fd)
|
||||
{
|
||||
m_fd = fd;
|
||||
}
|
||||
|
||||
void
|
||||
BtrfsTreeFetcher::type(uint8_t type)
|
||||
{
|
||||
m_type = type;
|
||||
}
|
||||
|
||||
void
|
||||
BtrfsTreeFetcher::tree(uint64_t tree)
|
||||
{
|
||||
m_tree = tree;
|
||||
}
|
||||
|
||||
void
|
||||
BtrfsTreeFetcher::transid(uint64_t min_transid, uint64_t max_transid)
|
||||
{
|
||||
m_min_transid = min_transid;
|
||||
m_max_transid = max_transid;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeFetcher::lookbehind_size() const
|
||||
{
|
||||
return m_lookbehind_size;
|
||||
}
|
||||
|
||||
void
|
||||
BtrfsTreeFetcher::lookbehind_size(uint64_t lookbehind_size)
|
||||
{
|
||||
m_lookbehind_size = lookbehind_size;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeFetcher::scale_size() const
|
||||
{
|
||||
return m_scale_size;
|
||||
}
|
||||
|
||||
void
|
||||
BtrfsTreeFetcher::scale_size(uint64_t scale_size)
|
||||
{
|
||||
m_scale_size = scale_size;
|
||||
}
|
||||
|
||||
void
|
||||
BtrfsTreeFetcher::fill_sk(BtrfsIoctlSearchKey &sk, uint64_t object)
|
||||
{
|
||||
(void)object;
|
||||
// btrfs allows tree ID 0 meaning the current tree, but we do not.
|
||||
THROW_CHECK0(invalid_argument, m_tree != 0);
|
||||
sk.tree_id = m_tree;
|
||||
sk.min_type = m_type;
|
||||
sk.max_type = m_type;
|
||||
sk.min_transid = m_min_transid;
|
||||
sk.max_transid = m_max_transid;
|
||||
sk.nr_items = 1;
|
||||
}
|
||||
|
||||
void
|
||||
BtrfsTreeFetcher::next_sk(BtrfsIoctlSearchKey &key, const BtrfsIoctlSearchHeader &hdr)
|
||||
{
|
||||
key.next_min(hdr, m_type);
|
||||
}
|
||||
|
||||
BtrfsTreeItem
|
||||
BtrfsTreeFetcher::at(uint64_t logical)
|
||||
{
|
||||
BtrfsIoctlSearchKey &sk = m_sk;
|
||||
fill_sk(sk, logical);
|
||||
// Exact match, should return 0 or 1 items
|
||||
sk.max_type = sk.min_type;
|
||||
sk.nr_items = 1;
|
||||
sk.do_ioctl(fd());
|
||||
THROW_CHECK1(runtime_error, sk.m_result.size(), sk.m_result.size() < 2);
|
||||
for (const auto &i : sk.m_result) {
|
||||
if (hdr_logical(i) == logical && hdr_match(i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return BtrfsTreeItem();
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeFetcher::scale_logical(const uint64_t logical) const
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, logical, (logical % m_scale_size) == 0 || logical == s_max_logical);
|
||||
return logical / m_scale_size;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeFetcher::scaled_max_logical() const
|
||||
{
|
||||
return scale_logical(s_max_logical);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeFetcher::unscale_logical(const uint64_t logical) const
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, logical, logical <= scaled_max_logical());
|
||||
if (logical == scaled_max_logical()) {
|
||||
return s_max_logical;
|
||||
}
|
||||
return logical * scale_size();
|
||||
}
|
||||
|
||||
BtrfsTreeItem
|
||||
BtrfsTreeFetcher::rlower_bound(uint64_t logical)
|
||||
{
|
||||
#if 0
|
||||
#define BTFRLB_DEBUG(x) do { cerr << x; } while (false)
|
||||
#else
|
||||
#define BTFRLB_DEBUG(x) do { } while (false)
|
||||
#endif
|
||||
BtrfsTreeItem closest_item;
|
||||
uint64_t closest_logical = 0;
|
||||
BtrfsIoctlSearchKey &sk = m_sk;
|
||||
size_t loops = 0;
|
||||
BTFRLB_DEBUG("rlower_bound: " << to_hex(logical) << endl);
|
||||
seek_backward(scale_logical(logical), [&](uint64_t lower_bound, uint64_t upper_bound) {
|
||||
++loops;
|
||||
fill_sk(sk, unscale_logical(min(scaled_max_logical(), lower_bound)));
|
||||
set<uint64_t> rv;
|
||||
do {
|
||||
sk.nr_items = 4;
|
||||
sk.do_ioctl(fd());
|
||||
BTFRLB_DEBUG("fetch: loop " << loops << " lower_bound..upper_bound " << to_hex(lower_bound) << ".." << to_hex(upper_bound));
|
||||
for (auto &i : sk.m_result) {
|
||||
next_sk(sk, i);
|
||||
const auto this_logical = hdr_logical(i);
|
||||
const auto scaled_hdr_logical = scale_logical(this_logical);
|
||||
BTFRLB_DEBUG(" " << to_hex(scaled_hdr_logical));
|
||||
if (hdr_match(i)) {
|
||||
if (this_logical <= logical && this_logical > closest_logical) {
|
||||
closest_logical = this_logical;
|
||||
closest_item = i;
|
||||
}
|
||||
BTFRLB_DEBUG("(match)");
|
||||
rv.insert(scaled_hdr_logical);
|
||||
}
|
||||
if (scaled_hdr_logical > upper_bound || hdr_stop(i)) {
|
||||
if (scaled_hdr_logical >= upper_bound) {
|
||||
BTFRLB_DEBUG("(" << to_hex(scaled_hdr_logical) << " >= " << to_hex(upper_bound) << ")");
|
||||
}
|
||||
if (hdr_stop(i)) {
|
||||
rv.insert(numeric_limits<uint64_t>::max());
|
||||
BTFRLB_DEBUG("(stop)");
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
BTFRLB_DEBUG("(cont'd)");
|
||||
}
|
||||
}
|
||||
BTFRLB_DEBUG(endl);
|
||||
// We might get a search result that contains only non-matching items.
|
||||
// Keep looping until we find any matching item or we run out of tree.
|
||||
} while (rv.empty() && !sk.m_result.empty());
|
||||
return rv;
|
||||
}, scale_logical(lookbehind_size()));
|
||||
return closest_item;
|
||||
#undef BTFRLB_DEBUG
|
||||
}
|
||||
|
||||
BtrfsTreeItem
|
||||
BtrfsTreeFetcher::lower_bound(uint64_t logical)
|
||||
{
|
||||
BtrfsIoctlSearchKey &sk = m_sk;
|
||||
fill_sk(sk, logical);
|
||||
do {
|
||||
assert(sk.max_offset == s_max_logical);
|
||||
sk.do_ioctl(fd());
|
||||
for (auto &i : sk.m_result) {
|
||||
if (hdr_match(i)) {
|
||||
return i;
|
||||
}
|
||||
if (hdr_stop(i)) {
|
||||
return BtrfsTreeItem();
|
||||
}
|
||||
next_sk(sk, i);
|
||||
}
|
||||
} while (!sk.m_result.empty());
|
||||
return BtrfsTreeItem();
|
||||
}
|
||||
|
||||
BtrfsTreeItem
|
||||
BtrfsTreeFetcher::next(uint64_t logical)
|
||||
{
|
||||
const auto scaled_logical = scale_logical(logical);
|
||||
if (scaled_logical + 1 > scaled_max_logical()) {
|
||||
return BtrfsTreeItem();
|
||||
}
|
||||
return lower_bound(unscale_logical(scaled_logical + 1));
|
||||
}
|
||||
|
||||
BtrfsTreeItem
|
||||
BtrfsTreeFetcher::prev(uint64_t logical)
|
||||
{
|
||||
const auto scaled_logical = scale_logical(logical);
|
||||
if (scaled_logical < 1) {
|
||||
return BtrfsTreeItem();
|
||||
}
|
||||
return rlower_bound(unscale_logical(scaled_logical - 1));
|
||||
}
|
||||
|
||||
void
|
||||
BtrfsTreeObjectFetcher::fill_sk(BtrfsIoctlSearchKey &sk, uint64_t object)
|
||||
{
|
||||
BtrfsTreeFetcher::fill_sk(sk, object);
|
||||
sk.min_offset = 0;
|
||||
sk.max_offset = numeric_limits<decltype(sk.max_offset)>::max();
|
||||
sk.min_objectid = object;
|
||||
sk.max_objectid = numeric_limits<decltype(sk.max_objectid)>::max();
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeObjectFetcher::hdr_logical(const BtrfsIoctlSearchHeader &hdr)
|
||||
{
|
||||
return hdr.objectid;
|
||||
}
|
||||
|
||||
bool
|
||||
BtrfsTreeObjectFetcher::hdr_match(const BtrfsIoctlSearchHeader &hdr)
|
||||
{
|
||||
// If you're calling this method without overriding it, you should have set type first
|
||||
assert(m_type);
|
||||
return hdr.type == m_type;
|
||||
}
|
||||
|
||||
bool
|
||||
BtrfsTreeObjectFetcher::hdr_stop(const BtrfsIoctlSearchHeader &hdr)
|
||||
{
|
||||
return false;
|
||||
(void)hdr;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeOffsetFetcher::hdr_logical(const BtrfsIoctlSearchHeader &hdr)
|
||||
{
|
||||
return hdr.offset;
|
||||
}
|
||||
|
||||
bool
|
||||
BtrfsTreeOffsetFetcher::hdr_match(const BtrfsIoctlSearchHeader &hdr)
|
||||
{
|
||||
assert(m_type);
|
||||
return hdr.type == m_type && hdr.objectid == m_objectid;
|
||||
}
|
||||
|
||||
bool
|
||||
BtrfsTreeOffsetFetcher::hdr_stop(const BtrfsIoctlSearchHeader &hdr)
|
||||
{
|
||||
assert(m_type);
|
||||
return hdr.objectid > m_objectid || hdr.type > m_type;
|
||||
}
|
||||
|
||||
void
|
||||
BtrfsTreeOffsetFetcher::objectid(uint64_t objectid)
|
||||
{
|
||||
m_objectid = objectid;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BtrfsTreeOffsetFetcher::objectid() const
|
||||
{
|
||||
return m_objectid;
|
||||
}
|
||||
|
||||
void
|
||||
BtrfsTreeOffsetFetcher::fill_sk(BtrfsIoctlSearchKey &sk, uint64_t offset)
|
||||
{
|
||||
BtrfsTreeFetcher::fill_sk(sk, offset);
|
||||
sk.min_offset = offset;
|
||||
sk.max_offset = numeric_limits<decltype(sk.max_offset)>::max();
|
||||
sk.min_objectid = m_objectid;
|
||||
sk.max_objectid = m_objectid;
|
||||
}
|
||||
|
||||
void
|
||||
BtrfsCsumTreeFetcher::get_sums(uint64_t const logical, size_t count, function<void(uint64_t logical, const uint8_t *buf, size_t bytes)> output)
|
||||
{
|
||||
#if 0
|
||||
#define BCTFGS_DEBUG(x) do { cerr << x; } while (false)
|
||||
#else
|
||||
#define BCTFGS_DEBUG(x) do { } while (false)
|
||||
#endif
|
||||
const uint64_t logical_end = logical + count * block_size();
|
||||
BtrfsTreeItem bti = rlower_bound(logical);
|
||||
size_t loops = 0;
|
||||
BCTFGS_DEBUG("get_sums " << to_hex(logical) << ".." << to_hex(logical_end) << endl);
|
||||
while (!!bti) {
|
||||
BCTFGS_DEBUG("get_sums[" << loops << "]: " << bti << endl);
|
||||
++loops;
|
||||
// Reject wrong type or objectid
|
||||
THROW_CHECK1(runtime_error, bti.type(), bti.type() == BTRFS_EXTENT_CSUM_KEY);
|
||||
THROW_CHECK1(runtime_error, bti.objectid(), bti.objectid() == BTRFS_EXTENT_CSUM_OBJECTID);
|
||||
// Is this object in range?
|
||||
const uint64_t data_logical = bti.offset();
|
||||
if (data_logical >= logical_end) {
|
||||
// csum object is past end of range, we are done
|
||||
return;
|
||||
}
|
||||
// Figure out how long this csum item is in various units
|
||||
const size_t csum_byte_count = bti.data().size();
|
||||
THROW_CHECK1(runtime_error, csum_byte_count, (csum_byte_count % m_sum_size) == 0);
|
||||
THROW_CHECK1(runtime_error, csum_byte_count, csum_byte_count > 0);
|
||||
const size_t csum_count = csum_byte_count / m_sum_size;
|
||||
const uint64_t data_byte_count = csum_count * block_size();
|
||||
const uint64_t data_logical_end = data_logical + data_byte_count;
|
||||
if (data_logical_end <= logical) {
|
||||
// too low, look at next item
|
||||
bti = lower_bound(logical);
|
||||
continue;
|
||||
}
|
||||
// There is some overlap?
|
||||
const uint64_t overlap_begin = max(logical, data_logical);
|
||||
const uint64_t overlap_end = min(logical_end, data_logical_end);
|
||||
THROW_CHECK2(runtime_error, overlap_begin, overlap_end, overlap_begin < overlap_end);
|
||||
const uint64_t overlap_offset = overlap_begin - data_logical;
|
||||
THROW_CHECK1(runtime_error, overlap_offset, (overlap_offset % block_size()) == 0);
|
||||
const uint64_t overlap_index = overlap_offset * m_sum_size / block_size();
|
||||
const uint64_t overlap_byte_count = overlap_end - overlap_begin;
|
||||
const uint64_t overlap_csum_byte_count = overlap_byte_count * m_sum_size / block_size();
|
||||
// Can't be bigger than a btrfs item
|
||||
THROW_CHECK1(runtime_error, overlap_index, overlap_index < 65536);
|
||||
THROW_CHECK1(runtime_error, overlap_csum_byte_count, overlap_csum_byte_count < 65536);
|
||||
// Yes, process the overlap
|
||||
output(overlap_begin, bti.data().data() + overlap_index, overlap_csum_byte_count);
|
||||
// Advance
|
||||
bti = lower_bound(overlap_end);
|
||||
}
|
||||
#undef BCTFGS_DEBUG
|
||||
}
|
||||
|
||||
uint32_t
|
||||
BtrfsCsumTreeFetcher::sum_type() const
|
||||
{
|
||||
return m_sum_type;
|
||||
}
|
||||
|
||||
size_t
|
||||
BtrfsCsumTreeFetcher::sum_size() const
|
||||
{
|
||||
return m_sum_size;
|
||||
}
|
||||
|
||||
BtrfsCsumTreeFetcher::BtrfsCsumTreeFetcher(const Fd &new_fd) :
|
||||
BtrfsTreeOffsetFetcher(new_fd)
|
||||
{
|
||||
type(BTRFS_EXTENT_CSUM_KEY);
|
||||
tree(BTRFS_CSUM_TREE_OBJECTID);
|
||||
objectid(BTRFS_EXTENT_CSUM_OBJECTID);
|
||||
BtrfsIoctlFsInfoArgs bifia;
|
||||
bifia.do_ioctl(fd());
|
||||
m_sum_type = static_cast<btrfs_compression_type>(bifia.csum_type());
|
||||
m_sum_size = bifia.csum_size();
|
||||
if (m_sum_type == BTRFS_CSUM_TYPE_CRC32 && m_sum_size == 0) {
|
||||
// Older kernel versions don't fill in this field
|
||||
m_sum_size = 4;
|
||||
}
|
||||
THROW_CHECK1(runtime_error, m_sum_size, m_sum_size > 0);
|
||||
}
|
||||
|
||||
BtrfsExtentItemFetcher::BtrfsExtentItemFetcher(const Fd &new_fd) :
|
||||
BtrfsTreeObjectFetcher(new_fd)
|
||||
{
|
||||
tree(BTRFS_EXTENT_TREE_OBJECTID);
|
||||
type(BTRFS_EXTENT_ITEM_KEY);
|
||||
}
|
||||
|
||||
BtrfsExtentDataFetcher::BtrfsExtentDataFetcher(const Fd &new_fd) :
|
||||
BtrfsTreeOffsetFetcher(new_fd)
|
||||
{
|
||||
type(BTRFS_EXTENT_DATA_KEY);
|
||||
}
|
||||
|
||||
BtrfsFsTreeFetcher::BtrfsFsTreeFetcher(const Fd &new_fd, uint64_t subvol) :
|
||||
BtrfsTreeObjectFetcher(new_fd)
|
||||
{
|
||||
tree(subvol);
|
||||
type(BTRFS_EXTENT_DATA_KEY);
|
||||
scale_size(1);
|
||||
}
|
||||
|
||||
BtrfsInodeFetcher::BtrfsInodeFetcher(const Fd &fd) :
|
||||
BtrfsTreeObjectFetcher(fd)
|
||||
{
|
||||
type(BTRFS_INODE_ITEM_KEY);
|
||||
scale_size(1);
|
||||
}
|
||||
|
||||
BtrfsTreeItem
|
||||
BtrfsInodeFetcher::stat(uint64_t subvol, uint64_t inode)
|
||||
{
|
||||
tree(subvol);
|
||||
const auto item = at(inode);
|
||||
if (!!item) {
|
||||
THROW_CHECK2(runtime_error, item.objectid(), inode, inode == item.objectid());
|
||||
THROW_CHECK2(runtime_error, item.type(), BTRFS_INODE_ITEM_KEY, item.type() == BTRFS_INODE_ITEM_KEY);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
BtrfsRootFetcher::BtrfsRootFetcher(const Fd &fd) :
|
||||
BtrfsTreeObjectFetcher(fd)
|
||||
{
|
||||
tree(BTRFS_ROOT_TREE_OBJECTID);
|
||||
type(BTRFS_ROOT_ITEM_KEY);
|
||||
scale_size(1);
|
||||
}
|
||||
|
||||
BtrfsTreeItem
|
||||
BtrfsRootFetcher::root(uint64_t subvol)
|
||||
{
|
||||
const auto item = at(subvol);
|
||||
if (!!item) {
|
||||
THROW_CHECK2(runtime_error, item.objectid(), subvol, subvol == item.objectid());
|
||||
THROW_CHECK2(runtime_error, item.type(), BTRFS_ROOT_ITEM_KEY, item.type() == BTRFS_ROOT_ITEM_KEY);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user