diff --git a/include/crucible/btrfs.h b/include/crucible/btrfs.h index ee76ca1..21f6193 100644 --- a/include/crucible/btrfs.h +++ b/include/crucible/btrfs.h @@ -202,4 +202,9 @@ struct btrfs_ioctl_search_args_v2) #endif +#ifndef BTRFS_IOC_LOGICAL_INO_V2 +#define BTRFS_IOC_LOGICAL_INO_V2 _IOWR(BTRFS_IOCTL_MAGIC, 59, struct btrfs_ioctl_logical_ino_args) +#define BTRFS_LOGICAL_INO_ARGS_IGNORE_OFFSET (1ULL << 0) +#endif + #endif // CRUCIBLE_BTRFS_H diff --git a/include/crucible/fs.h b/include/crucible/fs.h index 502dc49..37b14e0 100644 --- a/include/crucible/fs.h +++ b/include/crucible/fs.h @@ -58,7 +58,7 @@ namespace crucible { struct BtrfsDataContainer : public btrfs_data_container { BtrfsDataContainer(size_t size = 64 * 1024); - void *prepare(); + void *prepare(size_t size); size_t get_size() const; decltype(bytes_left) get_bytes_left() const; @@ -70,12 +70,31 @@ namespace crucible { }; struct BtrfsIoctlLogicalInoArgs : public btrfs_ioctl_logical_ino_args { - BtrfsIoctlLogicalInoArgs(uint64_t logical, size_t buf_size = 64 * 1024); + 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); - BtrfsDataContainer m_container; - vector m_iors; + 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() const; + private: + iterator m_begin = nullptr; + iterator m_end = nullptr; + friend struct BtrfsIoctlLogicalInoArgs; + } m_iors; }; ostream & operator<<(ostream &os, const BtrfsIoctlLogicalInoArgs &p); @@ -85,7 +104,7 @@ namespace crucible { virtual void do_ioctl(int fd); virtual bool do_ioctl_nothrow(int fd); - BtrfsDataContainer m_container; + size_t m_container_size; vector m_paths; }; diff --git a/lib/fs.cc b/lib/fs.cc index d5f0b98..8a7a655 100644 --- a/lib/fs.cc +++ b/lib/fs.cc @@ -238,11 +238,13 @@ namespace crucible { } void * - BtrfsDataContainer::prepare() + BtrfsDataContainer::prepare(size_t container_size) { + if (m_data.size() < container_size) { + m_data.resize(container_size); + } btrfs_data_container *p = reinterpret_cast(m_data.data()); - size_t min_size = offsetof(btrfs_data_container, val); - size_t container_size = m_data.size(); + const size_t min_size = offsetof(btrfs_data_container, val); if (container_size < min_size) { THROW_ERROR(out_of_range, "container size " << container_size << " smaller than minimum " << min_size); } @@ -301,33 +303,123 @@ namespace crucible { } BtrfsIoctlLogicalInoArgs::BtrfsIoctlLogicalInoArgs(uint64_t new_logical, size_t new_size) : - m_container(new_size) + m_container_size(new_size) { memset_zero(this); logical = new_logical; } + size_t + BtrfsIoctlLogicalInoArgs::BtrfsInodeOffsetRootSpan::size() const + { + return m_end - m_begin; + } + + BtrfsIoctlLogicalInoArgs::BtrfsInodeOffsetRootSpan::const_iterator + BtrfsIoctlLogicalInoArgs::BtrfsInodeOffsetRootSpan::cbegin() const + { + return m_begin; + } + + BtrfsIoctlLogicalInoArgs::BtrfsInodeOffsetRootSpan::const_iterator + BtrfsIoctlLogicalInoArgs::BtrfsInodeOffsetRootSpan::cend() const + { + return m_end; + } + + BtrfsIoctlLogicalInoArgs::BtrfsInodeOffsetRootSpan::iterator + BtrfsIoctlLogicalInoArgs::BtrfsInodeOffsetRootSpan::begin() const + { + return m_begin; + } + + BtrfsIoctlLogicalInoArgs::BtrfsInodeOffsetRootSpan::iterator + BtrfsIoctlLogicalInoArgs::BtrfsInodeOffsetRootSpan::end() const + { + return m_end; + } + + BtrfsIoctlLogicalInoArgs::BtrfsInodeOffsetRootSpan::iterator + BtrfsIoctlLogicalInoArgs::BtrfsInodeOffsetRootSpan::data() const + { + return m_begin; + } + + BtrfsIoctlLogicalInoArgs::BtrfsInodeOffsetRootSpan::operator vector() const + { + return vector(m_begin, m_end); + } + + void + BtrfsIoctlLogicalInoArgs::BtrfsInodeOffsetRootSpan::clear() + { + m_end = m_begin = nullptr; + } + + void + BtrfsIoctlLogicalInoArgs::set_flags(uint64_t new_flags) + { + // We are still supporting building with old headers that don't have .flags yet + reserved[3] = new_flags; + } + + uint64_t + BtrfsIoctlLogicalInoArgs::get_flags() const + { + // We are still supporting building with old headers that don't have .flags yet + return reserved[3]; + } + bool BtrfsIoctlLogicalInoArgs::do_ioctl_nothrow(int fd) { btrfs_ioctl_logical_ino_args *p = static_cast(this); - inodes = reinterpret_cast(m_container.prepare()); - size = m_container.get_size(); + thread_local BtrfsDataContainer container; + inodes = reinterpret_cast(container.prepare(m_container_size)); + size = container.get_size(); m_iors.clear(); - if (ioctl(fd, BTRFS_IOC_LOGICAL_INO, p)) { - return false; + static unsigned long bili_version = 0; + + if (get_flags() == 0) { + // Could use either V1 or V2 + if (bili_version) { + // We tested both versions and came to a decision + if (ioctl(fd, bili_version, p)) { + return false; + } + } else { + // Try V2 + if (ioctl(fd, BTRFS_IOC_LOGICAL_INO_V2, p)) { + // V2 failed, try again with V1 + if (ioctl(fd, BTRFS_IOC_LOGICAL_INO, p)) { + // both V1 and V2 failed, doesn't tell us which one to choose + return false; + } + // V1 and V2 both tested with same arguments, V1 OK, and V2 failed + bili_version = BTRFS_IOC_LOGICAL_INO; + } else { + // V2 succeeded, don't use V1 any more + bili_version = BTRFS_IOC_LOGICAL_INO_V2; + } + } + } else { + // Flags/size require a V2 feature, no fallback to V1 possible + if (ioctl(fd, BTRFS_IOC_LOGICAL_INO_V2, p)) { + return false; + } + // V2 succeeded so we don't need to probe any more + bili_version = BTRFS_IOC_LOGICAL_INO_V2; } btrfs_data_container *bdc = reinterpret_cast(p->inodes); BtrfsInodeOffsetRoot *input_iter = reinterpret_cast(bdc->val); - m_iors.reserve(bdc->elem_cnt); - - for (auto count = bdc->elem_cnt; count > 2; count -= 3) { - m_iors.push_back(*input_iter++); - } + // elem_cnt counts uint64_t, but BtrfsInodeOffsetRoot is 3x uint64_t + THROW_CHECK1(runtime_error, bdc->elem_cnt, bdc->elem_cnt % 3 == 0); + m_iors.m_begin = input_iter; + m_iors.m_end = input_iter + bdc->elem_cnt / 3; return true; } @@ -350,7 +442,7 @@ namespace crucible { } BtrfsIoctlInoPathArgs::BtrfsIoctlInoPathArgs(uint64_t inode, size_t new_size) : - m_container(new_size) + m_container_size(new_size) { memset_zero(this); inum = inode; @@ -360,8 +452,9 @@ namespace crucible { BtrfsIoctlInoPathArgs::do_ioctl_nothrow(int fd) { btrfs_ioctl_ino_path_args *p = static_cast(this); - fspath = reinterpret_cast(m_container.prepare()); - size = m_container.get_size(); + thread_local BtrfsDataContainer container; + fspath = reinterpret_cast(container.prepare(m_container_size)); + size = m_container_size; m_paths.clear(); @@ -377,8 +470,8 @@ namespace crucible { for (auto count = bdc->elem_cnt; count > 0; --count) { const char *path = cp + *up++; - if (static_cast(path - cp) > m_container.get_size()) { - THROW_ERROR(out_of_range, "offset " << (path - cp) << " > size " << m_container.get_size() << " in " << __PRETTY_FUNCTION__); + if (static_cast(path - cp) > container.get_size()) { + THROW_ERROR(out_of_range, "offset " << (path - cp) << " > size " << container.get_size() << " in " << __PRETTY_FUNCTION__); } m_paths.push_back(string(path)); }