mirror of
https://github.com/Zygo/bees.git
synced 2025-05-17 21:35:45 +02:00
The Linux kernel's btrfs headers are better than the libbtrfs-dev headers: - the libbtrfs-dev headers have C++ language compatibility issues - upstream version in Linux kernel is more accurate and up to date - macros in libbtrfs-dev's ctree.h hide information that would enable bees to perform runtime buffer length checking - enum types whose presence cannot be detected with #ifdef When accessing members of metadata items from the filesystem, we want to verify that the member we are accessing is within the boundaries of the item that was retrieved; otherwise, a memory access violation may occur or garbage may be returned to the caller. A simple C++ template, given a pointer to a structure member and a buffer, can determine that the buffer contains enough bytes to safely access a struct member. This was implemented back in 2016, but left unused due to ctree.h issues. Some btrfs metadata structures have variable length despite using a fixed-size in-memory structure. The members that appear earliest in the structure contain information about which following members of the structure are used. The item stored in the filesystem is truncated after the last used member, and all following members must not be accessed. 'btrfs_stack_*' accessor macros obscure the memory boundaries of the members they access, which makes it impossible for a C++ template to verify the memory access. If the template checks the length of the entire structure, it will find an access violation for variable-length metadata items because the item is rarely large enough for the entire structure. Get rid of all the libbtrfs-dev accessor macros and reimplement them with the necessary buffer length checks. Signed-off-by: Zygo Blaxell <bees@furryterror.org>
250 lines
7.3 KiB
C++
250 lines
7.3 KiB
C++
#ifndef CRUCIBLE_FS_H
|
|
#define CRUCIBLE_FS_H
|
|
|
|
#include "crucible/endian.h"
|
|
#include "crucible/error.h"
|
|
#include "crucible/spanner.h"
|
|
|
|
// Terribly Linux-specific FS-wrangling functions
|
|
|
|
// BTRFS
|
|
#include "crucible/btrfs.h"
|
|
|
|
// FIEMAP_* structs and flags
|
|
#include <linux/fiemap.h>
|
|
|
|
#include <cstdint>
|
|
#include <iosfwd>
|
|
#include <set>
|
|
#include <vector>
|
|
|
|
#include <fcntl.h>
|
|
#include <sys/statvfs.h>
|
|
|
|
namespace crucible {
|
|
using namespace std;
|
|
|
|
// wrapper around fallocate(...FALLOC_FL_PUNCH_HOLE...)
|
|
void punch_hole(int fd, off_t offset, off_t len);
|
|
|
|
struct BtrfsExtentInfo : public btrfs_ioctl_same_extent_info {
|
|
BtrfsExtentInfo(int dst_fd, off_t dst_offset);
|
|
};
|
|
|
|
struct BtrfsExtentSame : public btrfs_ioctl_same_args {
|
|
virtual ~BtrfsExtentSame();
|
|
BtrfsExtentSame(int src_fd, off_t src_offset, off_t src_length);
|
|
void add(int fd, off_t offset);
|
|
virtual void do_ioctl();
|
|
|
|
int m_fd;
|
|
vector<BtrfsExtentInfo> m_info;
|
|
};
|
|
|
|
ostream & operator<<(ostream &os, const btrfs_ioctl_same_extent_info *info);
|
|
ostream & operator<<(ostream &os, const btrfs_ioctl_same_args *info);
|
|
ostream & operator<<(ostream &os, const BtrfsExtentSame &bes);
|
|
|
|
struct BtrfsInodeOffsetRoot {
|
|
uint64_t m_inum;
|
|
uint64_t m_offset;
|
|
uint64_t m_root;
|
|
};
|
|
|
|
ostream & operator<<(ostream &os, const BtrfsInodeOffsetRoot &p);
|
|
|
|
struct BtrfsDataContainer : public btrfs_data_container {
|
|
BtrfsDataContainer(size_t size = 64 * 1024);
|
|
void *prepare(size_t size);
|
|
|
|
size_t get_size() const;
|
|
decltype(bytes_left) get_bytes_left() const;
|
|
decltype(bytes_missing) get_bytes_missing() const;
|
|
decltype(elem_cnt) get_elem_cnt() const;
|
|
decltype(elem_missed) get_elem_missed() const;
|
|
|
|
vector<uint8_t> m_data;
|
|
};
|
|
|
|
struct BtrfsIoctlLogicalInoArgs : public btrfs_ioctl_logical_ino_args {
|
|
BtrfsIoctlLogicalInoArgs(uint64_t logical, size_t buf_size = 16 * 1024 * 1024);
|
|
|
|
uint64_t get_flags() const;
|
|
void set_flags(uint64_t new_flags);
|
|
|
|
virtual void do_ioctl(int fd);
|
|
virtual bool do_ioctl_nothrow(int fd);
|
|
|
|
size_t m_container_size;
|
|
struct BtrfsInodeOffsetRootSpan {
|
|
using iterator = BtrfsInodeOffsetRoot*;
|
|
using const_iterator = const BtrfsInodeOffsetRoot*;
|
|
size_t size() const;
|
|
iterator begin() const;
|
|
iterator end() const;
|
|
const_iterator cbegin() const;
|
|
const_iterator cend() const;
|
|
iterator data() const;
|
|
void clear();
|
|
operator vector<BtrfsInodeOffsetRoot>() const;
|
|
private:
|
|
iterator m_begin = nullptr;
|
|
iterator m_end = nullptr;
|
|
friend struct BtrfsIoctlLogicalInoArgs;
|
|
} m_iors;
|
|
BtrfsDataContainer m_container;
|
|
};
|
|
|
|
ostream & operator<<(ostream &os, const BtrfsIoctlLogicalInoArgs &p);
|
|
|
|
struct BtrfsIoctlInoPathArgs : public btrfs_ioctl_ino_path_args {
|
|
BtrfsIoctlInoPathArgs(uint64_t inode, size_t buf_size = 64 * 1024);
|
|
virtual void do_ioctl(int fd);
|
|
virtual bool do_ioctl_nothrow(int fd);
|
|
|
|
size_t m_container_size;
|
|
vector<string> m_paths;
|
|
};
|
|
|
|
ostream & operator<<(ostream &os, const BtrfsIoctlInoPathArgs &p);
|
|
|
|
struct BtrfsIoctlInoLookupArgs : public btrfs_ioctl_ino_lookup_args {
|
|
BtrfsIoctlInoLookupArgs(uint64_t objectid);
|
|
virtual void do_ioctl(int fd);
|
|
virtual bool do_ioctl_nothrow(int fd);
|
|
// use objectid = BTRFS_FIRST_FREE_OBJECTID
|
|
// this->treeid is the rootid for the path (we get the path too)
|
|
};
|
|
|
|
struct BtrfsIoctlDefragRangeArgs : public btrfs_ioctl_defrag_range_args {
|
|
BtrfsIoctlDefragRangeArgs();
|
|
virtual void do_ioctl(int fd);
|
|
virtual bool do_ioctl_nothrow(int fd);
|
|
};
|
|
|
|
ostream & operator<<(ostream &os, const BtrfsIoctlDefragRangeArgs *p);
|
|
|
|
// in btrfs/ctree.h, but that's a nightmare to #include here
|
|
typedef enum {
|
|
BTRFS_COMPRESS_NONE = 0,
|
|
BTRFS_COMPRESS_ZLIB = 1,
|
|
BTRFS_COMPRESS_LZO = 2,
|
|
BTRFS_COMPRESS_ZSTD = 3,
|
|
BTRFS_COMPRESS_TYPES = 3
|
|
} btrfs_compression_type;
|
|
|
|
struct FiemapExtent : public fiemap_extent {
|
|
FiemapExtent();
|
|
FiemapExtent(const fiemap_extent &that);
|
|
operator bool() const;
|
|
off_t begin() const;
|
|
off_t end() const;
|
|
};
|
|
|
|
struct Fiemap : public fiemap {
|
|
|
|
// Get entire file
|
|
Fiemap(uint64_t start = 0, uint64_t length = FIEMAP_MAX_OFFSET);
|
|
|
|
void do_ioctl(int fd);
|
|
|
|
vector<FiemapExtent> m_extents;
|
|
uint64_t m_min_count = (4096 - sizeof(fiemap)) / sizeof(fiemap_extent);
|
|
uint64_t m_max_count = 16 * 1024 * 1024 / sizeof(fiemap_extent);
|
|
};
|
|
|
|
ostream & operator<<(ostream &os, const fiemap_extent *info);
|
|
ostream & operator<<(ostream &os, const FiemapExtent &info);
|
|
ostream & operator<<(ostream &os, const fiemap *info);
|
|
ostream & operator<<(ostream &os, const Fiemap &info);
|
|
|
|
string fiemap_extent_flags_ntoa(unsigned long flags);
|
|
|
|
// Helper functions
|
|
void btrfs_clone_range(int src_fd, off_t src_offset, off_t src_length, int dst_fd, off_t dst_offset);
|
|
bool btrfs_extent_same(int src_fd, off_t src_offset, off_t src_length, int dst_fd, off_t dst_offset);
|
|
|
|
struct BtrfsIoctlSearchHeader : public btrfs_ioctl_search_header {
|
|
BtrfsIoctlSearchHeader();
|
|
Spanner<const uint8_t> m_data;
|
|
size_t set_data(const vector<uint8_t> &v, size_t offset);
|
|
bool operator<(const BtrfsIoctlSearchHeader &that) const;
|
|
};
|
|
|
|
// Perf blames this function for a few percent overhead; move it here so it can be inline
|
|
inline bool BtrfsIoctlSearchHeader::operator<(const BtrfsIoctlSearchHeader &that) const
|
|
{
|
|
return tie(objectid, type, offset, len, transid) < tie(that.objectid, that.type, that.offset, that.len, that.transid);
|
|
}
|
|
|
|
ostream & operator<<(ostream &os, const btrfs_ioctl_search_header &hdr);
|
|
ostream & operator<<(ostream &os, const BtrfsIoctlSearchHeader &hdr);
|
|
|
|
struct BtrfsIoctlSearchKey : public btrfs_ioctl_search_key {
|
|
BtrfsIoctlSearchKey(size_t buf_size = 4096);
|
|
virtual bool do_ioctl_nothrow(int fd);
|
|
virtual void do_ioctl(int fd);
|
|
|
|
// Copy objectid/type/offset so we move forward
|
|
void next_min(const BtrfsIoctlSearchHeader& ref);
|
|
|
|
size_t m_buf_size;
|
|
vector<uint8_t> m_ioctl_arg;
|
|
set<BtrfsIoctlSearchHeader> m_result;
|
|
|
|
};
|
|
|
|
ostream & operator<<(ostream &os, const btrfs_ioctl_search_key &key);
|
|
ostream & operator<<(ostream &os, const BtrfsIoctlSearchKey &key);
|
|
|
|
string btrfs_search_type_ntoa(unsigned type);
|
|
string btrfs_search_objectid_ntoa(uint64_t objectid);
|
|
|
|
uint64_t btrfs_get_root_id(int fd);
|
|
uint64_t btrfs_get_root_transid(int fd);
|
|
|
|
template<class T, class V>
|
|
const T*
|
|
get_struct_ptr(const V &v, size_t offset = 0)
|
|
{
|
|
THROW_CHECK2(out_of_range, v.size(), offset + sizeof(T), offset + sizeof(T) <= v.size());
|
|
const uint8_t *const data_ptr = v.data();
|
|
return reinterpret_cast<const T*>(data_ptr + offset);
|
|
}
|
|
|
|
template<class S, class T, class V>
|
|
T
|
|
btrfs_get_member(T S::* member, V &v, size_t offset = 0)
|
|
{
|
|
const S *const sp = nullptr;
|
|
const T *const spm = &(sp->*member);
|
|
const auto member_offset = reinterpret_cast<const uint8_t *>(spm) - reinterpret_cast<const uint8_t *>(sp);
|
|
const void *struct_ptr = get_struct_ptr<T>(v, offset + member_offset);
|
|
const T unaligned_t = get_unaligned<T>(struct_ptr);
|
|
return le_to_cpu(unaligned_t);
|
|
}
|
|
|
|
struct Statvfs : public statvfs {
|
|
Statvfs();
|
|
Statvfs(string path);
|
|
Statvfs(int fd);
|
|
unsigned long size() const;
|
|
unsigned long free() const;
|
|
unsigned long available() const;
|
|
};
|
|
|
|
template<class V> ostream &hexdump(ostream &os, const V &v);
|
|
|
|
struct BtrfsIoctlFsInfoArgs : public btrfs_ioctl_fs_info_args_v2 {
|
|
BtrfsIoctlFsInfoArgs();
|
|
void do_ioctl(int fd);
|
|
string uuid() const;
|
|
uint16_t csum_type() const;
|
|
uint16_t csum_size() const;
|
|
};
|
|
|
|
ostream & operator<<(ostream &os, const BtrfsIoctlFsInfoArgs &a);
|
|
};
|
|
|
|
#endif // CRUCIBLE_FS_H
|