diff --git a/include/crucible/fs.h b/include/crucible/fs.h index 26f123a..0cb5275 100644 --- a/include/crucible/fs.h +++ b/include/crucible/fs.h @@ -1,9 +1,9 @@ #ifndef CRUCIBLE_FS_H #define CRUCIBLE_FS_H +#include "crucible/bytevector.h" #include "crucible/endian.h" #include "crucible/error.h" -#include "crucible/spanner.h" // Terribly Linux-specific FS-wrangling functions @@ -63,7 +63,7 @@ namespace crucible { decltype(elem_cnt) get_elem_cnt() const; decltype(elem_missed) get_elem_missed() const; - vector m_data; + ByteVector m_data; }; struct BtrfsIoctlLogicalInoArgs : public btrfs_ioctl_logical_ino_args { @@ -170,8 +170,8 @@ namespace crucible { struct BtrfsIoctlSearchHeader : public btrfs_ioctl_search_header { BtrfsIoctlSearchHeader(); - Spanner m_data; - size_t set_data(const vector &v, size_t offset); + ByteVector m_data; + size_t set_data(const ByteVector &v, size_t offset); bool operator<(const BtrfsIoctlSearchHeader &that) const; }; @@ -185,7 +185,7 @@ namespace crucible { ostream & operator<<(ostream &os, const BtrfsIoctlSearchHeader &hdr); struct BtrfsIoctlSearchKey : public btrfs_ioctl_search_key { - BtrfsIoctlSearchKey(size_t buf_size = 4096); + BtrfsIoctlSearchKey(size_t buf_size = 1024); bool do_ioctl_nothrow(int fd); void do_ioctl(int fd); @@ -193,9 +193,7 @@ namespace crucible { void next_min(const BtrfsIoctlSearchHeader& ref); size_t m_buf_size; - vector m_ioctl_arg; set m_result; - }; ostream & operator<<(ostream &os, const btrfs_ioctl_search_key &key); diff --git a/lib/fs.cc b/lib/fs.cc index 48a9c36..c3ae00f 100644 --- a/lib/fs.cc +++ b/lib/fs.cc @@ -139,9 +139,9 @@ namespace crucible { BtrfsExtentSame::do_ioctl() { dest_count = m_info.size(); - vector ioctl_arg = vector_copy_struct(this); - ioctl_arg.resize(sizeof(btrfs_ioctl_same_args) + dest_count * sizeof(btrfs_ioctl_same_extent_info), 0); - btrfs_ioctl_same_args *ioctl_ptr = reinterpret_cast(ioctl_arg.data()); + const size_t buf_size = sizeof(btrfs_ioctl_same_args) + dest_count * sizeof(btrfs_ioctl_same_extent_info); + ByteVector ioctl_arg(static_cast(*this), buf_size); + btrfs_ioctl_same_args *const ioctl_ptr = ioctl_arg.get(); size_t count = 0; for (auto i = m_info.cbegin(); i != m_info.cend(); ++i) { ioctl_ptr->info[count] = static_cast(m_info[count]); @@ -195,9 +195,9 @@ namespace crucible { BtrfsDataContainer::prepare(size_t container_size) { if (m_data.size() < container_size) { - m_data.resize(container_size); + m_data = ByteVector(container_size); } - btrfs_data_container *p = reinterpret_cast(m_data.data()); + const auto p = m_data.get(); 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); @@ -328,7 +328,7 @@ namespace crucible { bool BtrfsIoctlLogicalInoArgs::do_ioctl_nothrow(int fd) { - btrfs_ioctl_logical_ino_args *p = static_cast(this); + btrfs_ioctl_logical_ino_args *const p = static_cast(this); inodes = reinterpret_cast(m_container.prepare(m_container_size)); size = m_container.get_size(); @@ -676,11 +676,9 @@ namespace crucible { THROW_CHECK1(out_of_range, m_min_count, m_min_count <= m_max_count); auto extent_count = m_min_count; - vector ioctl_arg = vector_copy_struct(this); + ByteVector ioctl_arg(static_cast(*this), sizeof(fiemap) + extent_count * sizeof(fiemap_extent)); - ioctl_arg.resize(sizeof(fiemap) + extent_count * sizeof(fiemap_extent), 0); - - fiemap *ioctl_ptr = reinterpret_cast(ioctl_arg.data()); + fiemap *const ioctl_ptr = ioctl_arg.get(); auto start = fm_start; auto end = fm_start + fm_length; @@ -736,7 +734,7 @@ namespace crucible { max_offset = numeric_limits::max(); max_transid = numeric_limits::max(); max_type = numeric_limits::max(); - nr_items = numeric_limits::max(); + nr_items = 1; } BtrfsIoctlSearchHeader::BtrfsIoctlSearchHeader() @@ -745,49 +743,68 @@ namespace crucible { } size_t - BtrfsIoctlSearchHeader::set_data(const vector &v, size_t offset) + BtrfsIoctlSearchHeader::set_data(const ByteVector &v, size_t offset) { THROW_CHECK2(invalid_argument, offset, v.size(), offset + sizeof(btrfs_ioctl_search_header) <= v.size()); memcpy(static_cast(this), &v[offset], sizeof(btrfs_ioctl_search_header)); offset += sizeof(btrfs_ioctl_search_header); THROW_CHECK2(invalid_argument, offset + len, v.size(), offset + len <= v.size()); - m_data = Spanner(&v[offset], &v[offset + len]); + m_data = ByteVector(v, offset, len); return offset + len; } bool BtrfsIoctlSearchKey::do_ioctl_nothrow(int fd) { - // Normally we like to be paranoid and fill empty bytes with zero, - // but these buffers can be huge. 80% of a 4GHz CPU huge. - - // Keep the ioctl buffer from one run to the next to save on malloc costs - size_t target_buf_size = sizeof(btrfs_ioctl_search_args_v2) + m_buf_size; - - m_ioctl_arg = vector_copy_struct(this); - m_ioctl_arg.resize(target_buf_size); + // It would be really nice if the kernel tells us whether our + // buffer overflowed or how big the overflowing object + // was; instead, we have to guess. m_result.clear(); + // Make sure there is space for at least the search key and one (empty) header + size_t buf_size = max(m_buf_size, sizeof(btrfs_ioctl_search_args_v2) + sizeof(btrfs_ioctl_search_header)); + ByteVector ioctl_arg; + btrfs_ioctl_search_args_v2 *ioctl_ptr; + do { + // ioctl buffer size does not include search key header or buffer size + ioctl_arg = ByteVector(buf_size + sizeof(btrfs_ioctl_search_args_v2)); + ioctl_ptr = ioctl_arg.get(); + ioctl_ptr->key = static_cast(*this); + ioctl_ptr->buf_size = buf_size; + // Don't bother supporting V1. Kernels that old have other problems. + int rv = ioctl(fd, BTRFS_IOC_TREE_SEARCH_V2, ioctl_arg.data()); + if (rv != 0 && errno != EOVERFLOW) { + return false; + } + if (rv == 0 && nr_items <= ioctl_ptr->key.nr_items) { + // got all the items we wanted, thanks + m_buf_size = max(m_buf_size, buf_size); + break; + } + // Didn't get all the items we wanted. Increase the buf size and try again. + // These sizes are very common on default-formatted btrfs, so use these + // instead of naive doubling. + if (buf_size < 4096) { + buf_size = 4096; + } else if (buf_size < 16384) { + buf_size = 16384; + } else if (buf_size < 65536) { + buf_size = 65536; + } else { + buf_size *= 2; + } + // don't automatically raise the buf size higher than 64K, the largest possible btrfs item + } while (buf_size < 65536); - btrfs_ioctl_search_args_v2 *ioctl_ptr = reinterpret_cast(m_ioctl_arg.data()); - - ioctl_ptr->buf_size = m_buf_size; - - // Don't bother supporting V1. Kernels that old have other problems. - int rv = ioctl(fd, BTRFS_IOC_TREE_SEARCH_V2, ioctl_ptr); - if (rv != 0) { - return false; - } - + // ioctl changes nr_items, this has to be copied back static_cast(*this) = ioctl_ptr->key; size_t offset = pointer_distance(ioctl_ptr->buf, ioctl_ptr); for (decltype(nr_items) i = 0; i < nr_items; ++i) { BtrfsIoctlSearchHeader item; - offset = item.set_data(m_ioctl_arg, offset); + offset = item.set_data(ioctl_arg, offset); m_result.insert(item); } - return true; } @@ -815,7 +832,7 @@ namespace crucible { ostream & hexdump(ostream &os, const V &v) { - os << "vector { size = " << v.size() << ", data:\n"; + os << "V { size = " << v.size() << ", data:\n"; for (size_t i = 0; i < v.size(); i += 8) { string hex, ascii; for (size_t j = i; j < i + 8; ++j) {