├── .gitignore ├── src ├── diskformat │ ├── macros │ │ ├── leaf_item_type_entries.rs │ │ ├── leaf_item_destructure.rs │ │ ├── mod.rs │ │ ├── tree_item_range_accessor.rs │ │ ├── leaf_item_composite_type_entries.rs │ │ ├── tree_item_accessor.rs │ │ ├── leaf_item_composite_type_iterator.rs │ │ └── leaf_item_composite_type_implementation.rs │ ├── item │ │ ├── extent_inline_ref_data.rs │ │ ├── inode_ref_data.rs │ │ ├── extent_data_ref_data.rs │ │ ├── tree_block_info_data.rs │ │ ├── extent_data_constants.rs │ │ ├── extent_item_data.rs │ │ ├── root_ref_data.rs │ │ ├── leaf_item_contents.rs │ │ ├── leaf_item_constants.rs │ │ ├── internal_item.rs │ │ ├── chunk_item_stripe_data.rs │ │ ├── extent_constants.rs │ │ ├── unknown_item.rs │ │ ├── dir_item.rs │ │ ├── extent_data_data.rs │ │ ├── invalid_item.rs │ │ ├── inode_ref.rs │ │ ├── dir_item_data.rs │ │ ├── chunk_item_data.rs │ │ ├── chunk_item.rs │ │ ├── mod.rs │ │ ├── extent_item.rs │ │ ├── leaf_item_header.rs │ │ ├── root_item.rs │ │ ├── inode_item_data.rs │ │ ├── inode_ref_entry.rs │ │ ├── inode_item.rs │ │ ├── root_ref.rs │ │ ├── root_backref.rs │ │ ├── dir_index.rs │ │ ├── dir_item_entry.rs │ │ ├── extent_data.rs │ │ ├── root_item_data.rs │ │ └── leaf_item.rs │ ├── node │ │ ├── mod.rs │ │ ├── leaf_node_items.rs │ │ ├── header.rs │ │ ├── leaf_node.rs │ │ ├── internal_node.rs │ │ └── node.rs │ ├── core │ │ ├── file_type.rs │ │ ├── dev_item.rs │ │ ├── dev_item_data.rs │ │ ├── superblock_reserved.rs │ │ ├── superblock_unused.rs │ │ ├── superblock_system_chunks_data.rs │ │ ├── superblock_chunk_item.rs │ │ ├── mod.rs │ │ ├── physical_address.rs │ │ ├── timestamp.rs │ │ ├── key.rs │ │ ├── mmap_device_set.rs │ │ ├── label.rs │ │ ├── uuid.rs │ │ ├── superblock_system_chunks.rs │ │ ├── checksum.rs │ │ ├── device.rs │ │ ├── root_backup.rs │ │ ├── device_set.rs │ │ ├── superblock_data.rs │ │ └── superblock.rs │ ├── mod.rs │ ├── tree │ │ ├── mod.rs │ │ ├── unknown_tree.rs │ │ ├── extent_tree.rs │ │ ├── filesystem_tree.rs │ │ ├── read_tree.rs │ │ ├── root_tree.rs │ │ ├── chunk_tree.rs │ │ ├── tree_id.rs │ │ └── tree.rs │ ├── misc-old.rs │ ├── prelude.rs │ ├── naked_string.rs │ └── compression.rs ├── compress │ ├── mod.rs │ ├── lzo.rs │ └── zlib.rs ├── linux │ ├── types │ │ ├── dedupe_range_status.rs │ │ ├── filesystem_info.rs │ │ ├── dedupe_range.rs │ │ ├── space_info.rs │ │ ├── device_info.rs │ │ ├── dedupe_range_dest_info.rs │ │ ├── compression_type.rs │ │ ├── group_type.rs │ │ ├── mod.rs │ │ ├── group_profile.rs │ │ └── file_descriptor.rs │ ├── ctypes │ │ ├── ioctl_space_args.rs │ │ ├── ioctl_space_info.rs │ │ ├── ioctl_fiemap.rs │ │ ├── ioctl_file_dedupe_range.rs │ │ ├── ioctl_file_dedupe_range_info.rs │ │ ├── ioctl_fiemap_extent.rs │ │ ├── ioctl_defrag_range_args.rs │ │ ├── ioctl_fs_info_args.rs │ │ ├── mod.rs │ │ ├── ioctl_device_path.rs │ │ ├── ioctl_dev_info_args.rs │ │ └── ioctl_constants.rs │ ├── mod.rs │ ├── operations │ │ ├── mod.rs │ │ ├── defragment.rs │ │ ├── filesystem_info.rs │ │ ├── space_info.rs │ │ ├── fiemap.rs │ │ └── deduplicate.rs │ ├── imports.rs │ └── ioctl_wrapper.rs └── lib.rs ├── Cargo.toml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /src/diskformat/macros/leaf_item_type_entries.rs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/compress/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod lzo; 2 | pub mod zlib; 3 | 4 | // ex: noet ts=4 filetype=rust; 5 | -------------------------------------------------------------------------------- /src/linux/types/dedupe_range_status.rs: -------------------------------------------------------------------------------- 1 | #[ derive (Debug, Eq, PartialEq) ] 2 | pub enum DedupeRangeStatus { 3 | Same, 4 | Differs, 5 | } 6 | 7 | // ex: noet ts=4 filetype=rust 8 | -------------------------------------------------------------------------------- /src/linux/ctypes/ioctl_space_args.rs: -------------------------------------------------------------------------------- 1 | #[ repr (C) ] 2 | #[ derive (Copy, Clone, Debug) ] 3 | pub struct IoctlSpaceArgs { 4 | pub space_slots: u64, 5 | pub total_spaces: u64, 6 | } 7 | 8 | // ex: noet ts=4 filetype=rust 9 | -------------------------------------------------------------------------------- /src/linux/mod.rs: -------------------------------------------------------------------------------- 1 | mod ctypes; 2 | mod imports; 3 | mod ioctl_wrapper; 4 | mod types; 5 | mod operations; 6 | 7 | pub use self::types::*; 8 | pub use self::operations::*; 9 | 10 | // ex: noet ts=4 filetype=rust 11 | -------------------------------------------------------------------------------- /src/linux/ctypes/ioctl_space_info.rs: -------------------------------------------------------------------------------- 1 | #[ repr (C) ] 2 | #[ derive (Copy, Clone, Debug) ] 3 | pub struct IoctlSpaceInfo { 4 | pub flags: u64, 5 | pub total_bytes: u64, 6 | pub used_bytes: u64, 7 | } 8 | 9 | // ex: noet ts=4 filetype=rust 10 | -------------------------------------------------------------------------------- /src/diskformat/item/extent_inline_ref_data.rs: -------------------------------------------------------------------------------- 1 | #[ repr (C, packed) ] 2 | #[ derive (Copy, Clone, Debug, Eq, Hash, PartialEq) ] 3 | pub struct BtrfsInlineRefData { 4 | inline_ref_type: u8, 5 | offset: u64, 6 | } 7 | 8 | // ex: noet ts=4 filetype=rust 9 | -------------------------------------------------------------------------------- /src/diskformat/item/inode_ref_data.rs: -------------------------------------------------------------------------------- 1 | #[ repr (C, packed) ] 2 | #[ derive (Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd) ] 3 | pub struct BtrfsInodeRefData { 4 | pub sequence: u64, 5 | pub name_length: u16, 6 | } 7 | 8 | // ex: noet ts=4 filetype=rust 9 | -------------------------------------------------------------------------------- /src/linux/types/filesystem_info.rs: -------------------------------------------------------------------------------- 1 | use linux::imports::*; 2 | 3 | #[ derive (Debug, Eq, PartialEq) ] 4 | pub struct FilesystemInfo { 5 | pub max_id: u64, 6 | pub num_devices: u64, 7 | pub filesystem_id: Uuid, 8 | } 9 | 10 | // ex: noet ts=4 filetype=rust 11 | -------------------------------------------------------------------------------- /src/linux/types/dedupe_range.rs: -------------------------------------------------------------------------------- 1 | use linux::imports::*; 2 | 3 | #[ derive (Debug, Eq, PartialEq) ] 4 | pub struct DedupeRange { 5 | pub src_offset: u64, 6 | pub src_length: u64, 7 | pub dest_infos: Vec , 8 | } 9 | 10 | // ex: noet ts=4 filetype=rust 11 | -------------------------------------------------------------------------------- /src/linux/types/space_info.rs: -------------------------------------------------------------------------------- 1 | use linux::imports::*; 2 | 3 | #[ derive (Debug, Eq, PartialEq) ] 4 | pub struct SpaceInfo { 5 | pub group_type: GroupType, 6 | pub group_profile: GroupProfile, 7 | pub total_bytes: u64, 8 | pub used_bytes: u64, 9 | } 10 | 11 | // ex: noet ts=4 filetype=rust 12 | -------------------------------------------------------------------------------- /src/diskformat/item/extent_data_ref_data.rs: -------------------------------------------------------------------------------- 1 | #[ repr (C, packed) ] 2 | #[ derive (Copy, Clone, Debug, Eq, Hash, PartialEq) ] 3 | pub struct BtrfsExtentDataRefData { 4 | tree_id: u64, 5 | object_id: u64, 6 | offset: u64, 7 | reference_count: u64, 8 | } 9 | 10 | // ex: noet ts=4 filetype=rust 11 | -------------------------------------------------------------------------------- /src/linux/types/device_info.rs: -------------------------------------------------------------------------------- 1 | use linux::imports::*; 2 | 3 | #[ derive (Debug, Eq, PartialEq) ] 4 | pub struct DeviceInfo { 5 | pub device_id: u64, 6 | pub uuid: Uuid, 7 | pub bytes_used: u64, 8 | pub total_bytes: u64, 9 | pub path: OsString, 10 | } 11 | 12 | // ex: noet ts=4 filetype=rust 13 | -------------------------------------------------------------------------------- /src/diskformat/item/tree_block_info_data.rs: -------------------------------------------------------------------------------- 1 | use super::super::prelude::*; 2 | 3 | #[ repr (C, packed) ] 4 | #[ derive (Copy, Clone, Debug, Eq, Hash, PartialEq) ] 5 | pub struct BtrfsTreeBlockInfoData { 6 | pub first_entry_key: BtrfsKey, 7 | pub level: u8, 8 | } 9 | 10 | // ex: noet ts=4 filetype=rust 11 | -------------------------------------------------------------------------------- /src/linux/ctypes/ioctl_fiemap.rs: -------------------------------------------------------------------------------- 1 | #[ repr (C) ] 2 | #[ derive (Copy, Clone, Debug) ] 3 | pub struct IoctlFiemap { 4 | pub start: u64, 5 | pub length: u64, 6 | pub flags: u32, 7 | pub mapped_extents: u32, 8 | pub extent_count: u32, 9 | pub reserved: u32, 10 | } 11 | 12 | // ex: noet ts=4 filetype=rust 13 | -------------------------------------------------------------------------------- /src/linux/ctypes/ioctl_file_dedupe_range.rs: -------------------------------------------------------------------------------- 1 | #[ repr (C) ] 2 | #[ derive (Copy, Clone, Debug) ] 3 | pub struct IoctlFileDedupeRange { 4 | pub src_offset: u64, 5 | pub src_length: u64, 6 | pub dest_count: u16, 7 | pub reserved1: u16, 8 | pub reserved2: u16, 9 | } 10 | 11 | // ex: noet ts=4 filetype=rust 12 | -------------------------------------------------------------------------------- /src/linux/types/dedupe_range_dest_info.rs: -------------------------------------------------------------------------------- 1 | use linux::imports::*; 2 | 3 | #[ derive (Debug, Eq, PartialEq) ] 4 | pub struct DedupeRangeDestInfo { 5 | pub dest_fd: i64, 6 | pub dest_offset: u64, 7 | pub bytes_deduped: u64, 8 | pub status: DedupeRangeStatus, 9 | } 10 | 11 | // ex: noet ts=4 filetype=rust 12 | -------------------------------------------------------------------------------- /src/linux/ctypes/ioctl_file_dedupe_range_info.rs: -------------------------------------------------------------------------------- 1 | #[ repr (C) ] 2 | #[ derive (Copy, Clone, Debug) ] 3 | pub struct IoctlFileDedupeRangeInfo { 4 | pub dest_fd: i64, 5 | pub dest_offset: u64, 6 | pub bytes_deduped: u64, 7 | pub status: i32, 8 | pub reserved: u32, 9 | } 10 | 11 | // ex: noet ts=4 filetype=rust 12 | -------------------------------------------------------------------------------- /src/diskformat/node/mod.rs: -------------------------------------------------------------------------------- 1 | mod header; 2 | mod internal_node; 3 | mod leaf_node; 4 | mod leaf_node_items; 5 | mod node; 6 | 7 | pub use self::header::*; 8 | pub use self::internal_node::*; 9 | pub use self::leaf_node::*; 10 | pub use self::leaf_node_items::*; 11 | pub use self::node::*; 12 | 13 | // ex: noet ts=4 filetype=rust 14 | -------------------------------------------------------------------------------- /src/linux/operations/mod.rs: -------------------------------------------------------------------------------- 1 | mod deduplicate; 2 | mod defragment; 3 | mod fiemap; 4 | mod filesystem_info; 5 | mod space_info; 6 | 7 | pub use self::deduplicate::*; 8 | pub use self::defragment::*; 9 | pub use self::fiemap::*; 10 | pub use self::filesystem_info::*; 11 | pub use self::space_info::*; 12 | 13 | // ex: noet ts=4 filetype=rust 14 | -------------------------------------------------------------------------------- /src/diskformat/macros/leaf_item_destructure.rs: -------------------------------------------------------------------------------- 1 | macro_rules! leaf_item_destructure { 2 | 3 | ( 4 | $ leaf_item : expr , 5 | $ enum_type : ident , 6 | ) => { 7 | 8 | match $ leaf_item { 9 | 10 | & BtrfsLeafItem::$ enum_type (ref item) => 11 | Some (item), 12 | 13 | _ => 14 | None, 15 | 16 | } 17 | 18 | } 19 | 20 | } 21 | 22 | // ex: noet ts=4 filetype=rust 23 | -------------------------------------------------------------------------------- /src/linux/ctypes/ioctl_fiemap_extent.rs: -------------------------------------------------------------------------------- 1 | #[ repr (C) ] 2 | #[ derive (Copy, Clone, Debug) ] 3 | pub struct IoctlFiemapExtent { 4 | pub logical: u64, 5 | pub physical: u64, 6 | pub length: u64, 7 | pub reserved0: u64, 8 | pub reserved1: u64, 9 | pub flags: u32, 10 | pub reserved2: u32, 11 | pub reserved3: u32, 12 | pub reserved4: u32, 13 | } 14 | 15 | // ex: noet ts=4 filetype=rust 16 | -------------------------------------------------------------------------------- /src/linux/ctypes/ioctl_defrag_range_args.rs: -------------------------------------------------------------------------------- 1 | #[ repr (C) ] 2 | #[ derive (Copy, Clone, Debug) ] 3 | pub struct IoctlDefragRangeArgs { 4 | pub start: u64, 5 | pub len: u64, 6 | pub flags: u64, 7 | pub extent_thresh: u32, 8 | pub compress_type: u32, 9 | pub unused_0: u32, 10 | pub unused_1: u32, 11 | pub unused_2: u32, 12 | pub unused_3: u32, 13 | } 14 | 15 | // ex: noet ts=4 filetype=rust 16 | -------------------------------------------------------------------------------- /src/diskformat/core/file_type.rs: -------------------------------------------------------------------------------- 1 | pub const BTRFS_FT_UNKNOWN: u8 = 0; 2 | pub const BTRFS_FT_REG_FILE: u8 = 1; 3 | pub const BTRFS_FT_DIR: u8 = 2; 4 | pub const BTRFS_FT_CHRDEV: u8 = 3; 5 | pub const BTRFS_FT_BLKDEV: u8 = 4; 6 | pub const BTRFS_FT_FIFO: u8 = 5; 7 | pub const BTRFS_FT_SOCK: u8 = 6; 8 | pub const BTRFS_FT_SYMLINK: u8 = 7; 9 | pub const BTRFS_FT_XATTR: u8 = 8; 10 | 11 | // ex: noet ts=4 filetype=rust 12 | -------------------------------------------------------------------------------- /src/diskformat/item/extent_data_constants.rs: -------------------------------------------------------------------------------- 1 | pub const BTRFS_EXTENT_DATA_INLINE_TYPE: u8 = 0; 2 | pub const BTRFS_EXTENT_DATA_REGULAR_TYPE: u8 = 1; 3 | pub const BTRFS_EXTENT_DATA_PREALLOC_TYPE: u8 = 2; 4 | 5 | pub const BTRFS_EXTENT_DATA_NO_COMPRESSION: u8 = 0; 6 | pub const BTRFS_EXTENT_DATA_ZLIB_COMPRESSION: u8 = 1; 7 | pub const BTRFS_EXTENT_DATA_LZO_COMPRESSION: u8 = 2; 8 | 9 | // ex: noet ts=4 filetype=rust 10 | -------------------------------------------------------------------------------- /src/diskformat/macros/mod.rs: -------------------------------------------------------------------------------- 1 | #[ macro_use] 2 | mod leaf_item_composite_type_entries; 3 | 4 | #[ macro_use] 5 | mod leaf_item_composite_type_implementation; 6 | 7 | #[ macro_use] 8 | mod leaf_item_composite_type_iterator; 9 | 10 | #[ macro_use] 11 | mod leaf_item_destructure; 12 | 13 | #[ macro_use] 14 | mod tree_item_accessor; 15 | 16 | #[ macro_use] 17 | mod tree_item_range_accessor; 18 | 19 | // ex: noet ts=4 filetype=rust 20 | -------------------------------------------------------------------------------- /src/diskformat/mod.rs: -------------------------------------------------------------------------------- 1 | #[ macro_use] 2 | mod macros; 3 | 4 | mod compression; 5 | mod core; 6 | mod filesystem; 7 | mod item; 8 | mod node; 9 | mod prelude; 10 | mod tree; 11 | mod naked_string; 12 | 13 | pub use self::compression::*; 14 | pub use self::core::*; 15 | pub use self::filesystem::*; 16 | pub use self::item::*; 17 | pub use self::naked_string::*; 18 | pub use self::node::*; 19 | pub use self::tree::*; 20 | 21 | // ex: noet ts=4 filetype=rust 22 | -------------------------------------------------------------------------------- /src/linux/types/compression_type.rs: -------------------------------------------------------------------------------- 1 | use linux::imports::*; 2 | 3 | #[ derive (Debug, Eq, PartialEq) ] 4 | pub enum CompressionType { 5 | None, 6 | Zlib, 7 | Lzo, 8 | } 9 | 10 | impl Into for CompressionType { 11 | 12 | fn into (self) -> u32 { 13 | 14 | use CompressionType::*; 15 | 16 | match self { 17 | None => COMPRESS_NONE, 18 | Zlib => COMPRESS_ZLIB, 19 | Lzo => COMPRESS_LZO, 20 | } 21 | 22 | } 23 | 24 | } 25 | 26 | // ex: noet ts=4 filetype=rust 27 | -------------------------------------------------------------------------------- /src/diskformat/tree/mod.rs: -------------------------------------------------------------------------------- 1 | mod chunk_tree; 2 | mod extent_tree; 3 | mod filesystem_tree; 4 | mod read_tree; 5 | mod root_tree; 6 | mod tree; 7 | mod tree_id; 8 | mod unknown_tree; 9 | 10 | pub use self::chunk_tree::*; 11 | pub use self::extent_tree::*; 12 | pub use self::filesystem_tree::*; 13 | pub use self::read_tree::*; 14 | pub use self::root_tree::*; 15 | pub use self::tree::*; 16 | pub use self::tree_id::*; 17 | pub use self::unknown_tree::*; 18 | 19 | // ex: noet ts=4 filetype=rust 20 | -------------------------------------------------------------------------------- /src/diskformat/item/extent_item_data.rs: -------------------------------------------------------------------------------- 1 | #[ repr (C, packed) ] 2 | #[ derive (Copy, Clone, Debug, Eq, Hash, PartialEq) ] 3 | pub struct BtrfsExtentItemData { 4 | pub reference_count: u64, 5 | pub generation: u64, 6 | pub flags: u64, 7 | } 8 | 9 | #[ cfg (test) ] 10 | mod tests { 11 | 12 | use std::mem; 13 | 14 | use super::*; 15 | 16 | #[ test ] 17 | fn test_size () { 18 | assert! (mem::size_of:: () == 0x18); 19 | } 20 | 21 | } 22 | 23 | // ex: noet ts=4 filetype=rust 24 | -------------------------------------------------------------------------------- /src/diskformat/item/root_ref_data.rs: -------------------------------------------------------------------------------- 1 | #[ repr (C, packed) ] 2 | #[ derive (Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd) ] 3 | pub struct BtrfsRootRefData { 4 | pub directory_id: u64, 5 | pub sequence: u64, 6 | pub name_length: u16, 7 | } 8 | 9 | #[ cfg (test) ] 10 | mod tests { 11 | 12 | use std::mem; 13 | 14 | use super::*; 15 | 16 | #[ test ] 17 | fn test_size () { 18 | assert! (mem::size_of:: () == 0x12); 19 | } 20 | 21 | } 22 | 23 | // ex: noet ts=4 filetype=rust 24 | -------------------------------------------------------------------------------- /src/diskformat/core/dev_item.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[ derive (Copy, Clone, Debug, Eq, Hash, PartialEq) ] 4 | pub struct BtrfsDevItem <'a> { 5 | data: & 'a BtrfsDevItemData, 6 | } 7 | 8 | impl <'a> BtrfsDevItem <'a> { 9 | 10 | pub fn new ( 11 | data: & 'a BtrfsDevItemData, 12 | ) -> BtrfsDevItem <'a> { 13 | 14 | BtrfsDevItem { 15 | data: data, 16 | } 17 | 18 | } 19 | 20 | pub fn device_id (& self) -> u64 { 21 | self.data.device_id 22 | } 23 | 24 | } 25 | 26 | // ex: noet ts=4 filetype=rust 27 | -------------------------------------------------------------------------------- /src/diskformat/item/leaf_item_contents.rs: -------------------------------------------------------------------------------- 1 | use super::super::*; 2 | 3 | pub trait BtrfsLeafItemContents <'a> { 4 | 5 | fn header (& self) -> & BtrfsLeafItemHeader; 6 | 7 | fn item_type (& self) -> u8 { 8 | self.header ().item_type () 9 | } 10 | 11 | fn key (& self) -> BtrfsKey { 12 | self.header ().key () 13 | } 14 | 15 | fn object_id (& self) -> u64 { 16 | self.header ().object_id () 17 | } 18 | 19 | fn offset (& self) -> u64 { 20 | self.header ().offset () 21 | } 22 | 23 | } 24 | 25 | // ex: noet ts=4 filetype=rust 26 | -------------------------------------------------------------------------------- /src/diskformat/item/leaf_item_constants.rs: -------------------------------------------------------------------------------- 1 | pub const BTRFS_INODE_ITEM_TYPE: u8 = 0x01; 2 | pub const BTRFS_INODE_REF_TYPE: u8 = 0x0c; 3 | pub const BTRFS_DIR_ITEM_TYPE: u8 = 0x54; 4 | pub const BTRFS_DIR_INDEX_TYPE: u8 = 0x60; 5 | pub const BTRFS_EXTENT_DATA_TYPE: u8 = 0x6c; 6 | pub const BTRFS_ROOT_ITEM_TYPE: u8 = 0x84; 7 | pub const BTRFS_EXTENT_ITEM_TYPE: u8 = 0xa8; 8 | pub const BTRFS_CHUNK_ITEM_TYPE: u8 = 0xe4; 9 | pub const BTRFS_ROOT_BACKREF_ITEM_TYPE: u8 = 0x90; 10 | pub const BTRFS_ROOT_REF_ITEM_TYPE: u8 = 0x9c; 11 | 12 | // ex: noet ts=4 filetype=rust 13 | -------------------------------------------------------------------------------- /src/diskformat/item/internal_item.rs: -------------------------------------------------------------------------------- 1 | use diskformat::*; 2 | 3 | #[ repr (C, packed) ] 4 | #[ derive (Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd) ] 5 | pub struct BtrfsInternalItem { 6 | key: BtrfsKey, 7 | block_number: u64, 8 | generation: u64, 9 | } 10 | 11 | impl BtrfsInternalItem { 12 | 13 | pub fn key (& self) -> BtrfsKey { 14 | self.key 15 | } 16 | 17 | pub fn block_number (& self) -> u64 { 18 | self.block_number 19 | } 20 | 21 | pub fn generation (& self) -> u64 { 22 | self.generation 23 | } 24 | 25 | } 26 | 27 | // ex: noet ts=4 filetype=rust 28 | -------------------------------------------------------------------------------- /src/diskformat/misc-old.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::slice; 3 | 4 | pub const BTRFS_CHILD_UNKNOWN_TYPE: u8 = 0; 5 | pub const BTRFS_CHILD_REGULAR_FILE_TYPE: u8 = 1; 6 | pub const BTRFS_CHILD_DIRECTORY_TYPE: u8 = 2; 7 | pub const BTRFS_CHILD_CHARACTER_DEVICE_TYPE: u8 = 3; 8 | pub const BTRFS_CHILD_BLOCK_DEVICE_TYPE: u8 = 4; 9 | pub const BTRFS_CHILD_FIFO_TYPE: u8 = 5; 10 | pub const BTRFS_CHILD_SOCKET_TYPE: u8 = 6; 11 | pub const BTRFS_CHILD_SYMBOLIC_LINK_TYPE: u8 = 7; 12 | pub const BTRFS_CHILD_EXTENDED_ATTRIBUTE_TYPE: u8 = 8; 13 | 14 | // ex: noet ts=4 filetype=rust 15 | -------------------------------------------------------------------------------- /src/diskformat/item/chunk_item_stripe_data.rs: -------------------------------------------------------------------------------- 1 | use super::super::*; 2 | 3 | #[ repr (C, packed) ] 4 | #[ derive (Copy, Clone, Debug) ] 5 | pub struct BtrfsChunkItemStripeData { 6 | pub device_id: u64, 7 | pub offset: u64, 8 | pub device_uuid: BtrfsUuid, 9 | } 10 | 11 | impl BtrfsChunkItemStripeData { 12 | 13 | pub fn device_id (& self) -> u64 { 14 | self.device_id 15 | } 16 | 17 | pub fn offset (& self) -> u64 { 18 | self.offset 19 | } 20 | 21 | pub fn device_uuid (& self) -> BtrfsUuid { 22 | self.device_uuid 23 | } 24 | 25 | } 26 | 27 | // ex: noet ts=4 filetype=rust 28 | -------------------------------------------------------------------------------- /src/linux/imports.rs: -------------------------------------------------------------------------------- 1 | pub use std::error::Error; 2 | pub use std::ffi::CString; 3 | pub use std::ffi::OsString; 4 | pub use std::fmt; 5 | pub use std::fs; 6 | pub use std::io; 7 | pub use std::iter; 8 | pub use std::iter::FromIterator; 9 | pub use std::mem; 10 | pub use std::os::unix::ffi::OsStringExt; 11 | pub use std::path::Path; 12 | pub use std::slice; 13 | 14 | pub mod libc { pub use libc::*; } 15 | 16 | pub use uuid::Uuid; 17 | 18 | pub use linux::ctypes::*; 19 | pub use linux::ioctl_wrapper::*; 20 | pub use linux::operations::*; 21 | pub use linux::types::*; 22 | 23 | // ex: et ts=4 filetype=rust; 24 | -------------------------------------------------------------------------------- /src/diskformat/core/dev_item_data.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[ repr (C, packed) ] 4 | #[ derive (Copy, Clone, Debug, Eq, Hash, PartialEq) ] 5 | pub struct BtrfsDevItemData { 6 | pub device_id: u64, 7 | pub num_bytes: u64, 8 | pub num_bytes_used: u64, 9 | pub optimal_io_align: u32, 10 | pub optimal_io_width: u32, 11 | pub minimal_io_size: u32, 12 | pub device_type: u64, 13 | pub generation: u64, 14 | pub start_offset: u64, 15 | pub dev_group: u32, 16 | pub seek_speed: u8, 17 | pub bandwidth: u8, 18 | pub device_uuid: BtrfsUuid, 19 | pub fs_uuid: BtrfsUuid, 20 | } 21 | 22 | // ex: noet ts=4 filetype=rust 23 | -------------------------------------------------------------------------------- /src/diskformat/item/extent_constants.rs: -------------------------------------------------------------------------------- 1 | pub const BTRFS_EXTENT_ITEM_INLINE_TYPE: u8 = 0; 2 | pub const BTRFS_EXTENT_ITEM_REGULAR_TYPE: u8 = 1; 3 | pub const BTRFS_EXTENT_ITEM_PREALLOC_TYPE: u8 = 2; 4 | 5 | pub const BTRFS_EXTENT_ITEM_NO_COMPRESSION: u8 = 0x00; 6 | pub const BTRFS_EXTENT_ITEM_ZLIB_COMPRESSION: u8 = 0x01; 7 | pub const BTRFS_EXTENT_ITEM_LZO_COMPRESSION: u8 = 0x02; 8 | 9 | pub const BTRFS_EXTENT_FLAG_DATA: u64 = 0x0000000000000001; 10 | pub const BTRFS_EXTENT_FLAG_TREE_BLOCK: u64 = 0x0000000000000002; 11 | pub const BTRFS_EXTENT_FLAG_FULL_BACKREF: u64 = 0x0000000000000080; 12 | 13 | // ex: noet ts=4 filetype=rust 14 | -------------------------------------------------------------------------------- /src/diskformat/tree/unknown_tree.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | use super::super::*; 4 | 5 | pub struct BtrfsUnknownTree <'a> { 6 | tree_items: BTreeMap >, 7 | } 8 | 9 | impl <'a> BtrfsTree <'a> for BtrfsUnknownTree <'a> { 10 | 11 | fn new ( 12 | tree_items: BTreeMap >, 13 | ) -> BtrfsUnknownTree { 14 | 15 | BtrfsUnknownTree { 16 | tree_items: tree_items, 17 | } 18 | 19 | } 20 | 21 | fn tree_items ( 22 | & 'a self, 23 | ) -> & 'a BTreeMap > { 24 | & self.tree_items 25 | } 26 | 27 | } 28 | 29 | // ex: noet ts=4 filetype=rust 30 | -------------------------------------------------------------------------------- /src/diskformat/tree/extent_tree.rs: -------------------------------------------------------------------------------- 1 | use super::super::prelude::*; 2 | 3 | pub struct BtrfsExtentTree <'a> { 4 | tree_items: BTreeMap >, 5 | } 6 | 7 | impl <'a> BtrfsExtentTree <'a> { 8 | 9 | } 10 | 11 | impl <'a> BtrfsTree <'a> for BtrfsExtentTree <'a> { 12 | 13 | fn new ( 14 | tree_items: BTreeMap >, 15 | ) -> BtrfsExtentTree { 16 | 17 | BtrfsExtentTree { 18 | tree_items: tree_items, 19 | } 20 | 21 | } 22 | 23 | fn tree_items ( 24 | & 'a self, 25 | ) -> & 'a BTreeMap > { 26 | & self.tree_items 27 | } 28 | 29 | } 30 | 31 | // ex: noet ts=4 filetype=rust 32 | -------------------------------------------------------------------------------- /src/diskformat/item/unknown_item.rs: -------------------------------------------------------------------------------- 1 | use diskformat::*; 2 | 3 | #[ derive (Clone, Debug) ] 4 | pub struct BtrfsUnknownItem <'a> { 5 | header: & 'a BtrfsLeafItemHeader, 6 | data_bytes: & 'a [u8], 7 | } 8 | 9 | impl <'a> BtrfsUnknownItem <'a> { 10 | 11 | pub fn new ( 12 | header: & 'a BtrfsLeafItemHeader, 13 | data_bytes: & 'a [u8], 14 | ) -> BtrfsUnknownItem <'a> { 15 | 16 | BtrfsUnknownItem { 17 | header: header, 18 | data_bytes: data_bytes, 19 | } 20 | 21 | } 22 | 23 | } 24 | 25 | impl <'a> BtrfsLeafItemContents <'a> for BtrfsUnknownItem <'a> { 26 | 27 | fn header (& self) -> & BtrfsLeafItemHeader { 28 | self.header 29 | } 30 | 31 | } 32 | 33 | // ex: noet ts=4 filetype=rust 34 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "btrfs" 4 | description = "Interface for BTRFS ioctls etc" 5 | license = "MIT" 6 | 7 | version = "1.2.2" 8 | 9 | authors = [ 10 | "James Pharaoh ", 11 | ] 12 | 13 | include = [ 14 | "src/**/*.rs", 15 | "Cargo.toml", 16 | "README.md", 17 | ] 18 | 19 | [dependencies] 20 | 21 | byteorder = "1.1" 22 | chrono = "0.4" 23 | crc = "1.4" 24 | flate2 = "0.2" 25 | itertools = "0.6" 26 | lazy_static = "0.2" 27 | libc = "0.2" 28 | memmap = "0.5" 29 | minilzo = "0.2" 30 | nix = "0.8" 31 | output = "0.6" 32 | uuid = "0.5" 33 | 34 | [replace] 35 | 36 | "output:0.6.2" = { path = "../rust-output" } 37 | 38 | # noet ts=4 filetype=toml 39 | -------------------------------------------------------------------------------- /src/diskformat/item/dir_item.rs: -------------------------------------------------------------------------------- 1 | use super::super::prelude::*; 2 | 3 | #[ derive (Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd) ] 4 | pub struct BtrfsDirItem <'a> { 5 | header: & 'a BtrfsLeafItemHeader, 6 | data_bytes: & 'a [u8], 7 | } 8 | 9 | leaf_item_composite_type_entries! ( 10 | BtrfsDirItem, 11 | BtrfsDirItemEntries, 12 | ); 13 | 14 | leaf_item_composite_type_implementation! ( 15 | BtrfsDirItem, 16 | BtrfsDirItemData, 17 | BtrfsDirItemEntries, 18 | BtrfsDirItemIterator, 19 | ); 20 | 21 | leaf_item_composite_type_iterator! ( 22 | BtrfsDirItem, 23 | BtrfsDirItemData, 24 | BtrfsDirItemEntry, 25 | BtrfsDirItemIterator, 26 | data_size, 27 | name_size, 28 | ); 29 | 30 | // ex: noet ts=4 filetype=rust 31 | -------------------------------------------------------------------------------- /src/diskformat/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use std::borrow::Cow; 2 | pub use std::collections::BTreeMap; 3 | pub use std::collections::HashMap; 4 | pub use std::collections::LinkedList; 5 | pub use std::ffi::OsStr; 6 | pub use std::fmt::Debug; 7 | pub use std::fmt::Error as FmtError; 8 | pub use std::fmt::Formatter; 9 | pub use std::mem; 10 | pub use std::os::unix::ffi::OsStrExt; 11 | pub use std::path::Path; 12 | pub use std::path::PathBuf; 13 | 14 | pub use byteorder::ByteOrder; 15 | pub use byteorder::LittleEndian; 16 | 17 | pub mod flate2 { 18 | pub use flate2::*; 19 | } 20 | 21 | pub mod minilzo { 22 | pub use minilzo::*; 23 | } 24 | 25 | pub use output::Output; 26 | 27 | pub use super::*; 28 | 29 | // ex: noet ts=4 filetype=rust 30 | -------------------------------------------------------------------------------- /src/diskformat/item/extent_data_data.rs: -------------------------------------------------------------------------------- 1 | #[ repr (C, packed) ] 2 | #[ derive (Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd) ] 3 | pub struct BtrfsExtentDataData { 4 | 5 | pub generation: u64, 6 | pub logical_data_size: u64, 7 | pub compression: u8, 8 | pub encryption: u8, 9 | pub other_encoding: u16, 10 | pub extent_type: u8, 11 | 12 | pub extent_logical_address: u64, 13 | pub extent_size: u64, 14 | pub extent_data_offset: u64, 15 | pub extent_data_size: u64, 16 | 17 | } 18 | 19 | #[ cfg (test) ] 20 | mod tests { 21 | 22 | use std::mem; 23 | 24 | use super::*; 25 | 26 | #[ test ] 27 | fn test_size () { 28 | assert! (mem::size_of:: () == 0x35); 29 | } 30 | 31 | } 32 | 33 | // ex: noet ts=4 filetype=rust 34 | -------------------------------------------------------------------------------- /src/diskformat/item/invalid_item.rs: -------------------------------------------------------------------------------- 1 | use diskformat::*; 2 | 3 | #[ derive (Clone, Debug) ] 4 | pub struct BtrfsInvalidItem <'a> { 5 | header: & 'a BtrfsLeafItemHeader, 6 | data_bytes: & 'a [u8], 7 | error: String, 8 | } 9 | 10 | impl <'a> BtrfsInvalidItem <'a> { 11 | 12 | pub fn new ( 13 | header: & 'a BtrfsLeafItemHeader, 14 | data_bytes: & 'a [u8], 15 | error: String, 16 | ) -> BtrfsInvalidItem <'a> { 17 | 18 | BtrfsInvalidItem { 19 | header: header, 20 | data_bytes: data_bytes, 21 | error: error, 22 | } 23 | 24 | } 25 | 26 | } 27 | 28 | impl <'a> BtrfsLeafItemContents <'a> for BtrfsInvalidItem <'a> { 29 | 30 | fn header (& self) -> & BtrfsLeafItemHeader { 31 | self.header 32 | } 33 | 34 | } 35 | 36 | // ex: noet ts=4 filetype=rust 37 | -------------------------------------------------------------------------------- /src/linux/ctypes/ioctl_fs_info_args.rs: -------------------------------------------------------------------------------- 1 | use linux::ctypes::ioctl_constants::*; 2 | 3 | #[ repr (C) ] 4 | #[ derive (Copy, Clone, Debug) ] 5 | pub struct IoctlFsInfoArgs { 6 | pub max_id: u64, 7 | pub num_devices: u64, 8 | pub filesystem_id: [u8; UUID_SIZE], 9 | pub reserved0: [u64; 32], 10 | pub reserved1: [u64; 32], 11 | pub reserved2: [u64; 32], 12 | pub reserved3: [u64; 28], 13 | } 14 | 15 | impl IoctlFsInfoArgs { 16 | 17 | pub fn new ( 18 | ) -> IoctlFsInfoArgs { 19 | 20 | IoctlFsInfoArgs { 21 | max_id: 0, 22 | num_devices: 0, 23 | filesystem_id: [0u8; 16], 24 | reserved0: [0u64; 32], 25 | reserved1: [0u64; 32], 26 | reserved2: [0u64; 32], 27 | reserved3: [0u64; 28], 28 | } 29 | 30 | } 31 | 32 | } 33 | 34 | // ex: noet ts=4 filetype=rust 35 | -------------------------------------------------------------------------------- /src/linux/ioctl_wrapper.rs: -------------------------------------------------------------------------------- 1 | use linux::imports::*; 2 | 3 | // ---------- btrfs 4 | 5 | pub const BTRFS_IOCTL_MAGIC: u64 = 0x94; 6 | 7 | ioctl! ( 8 | write ioctl_defrag_range with BTRFS_IOCTL_MAGIC, 16; 9 | IoctlDefragRangeArgs); 10 | 11 | ioctl! ( 12 | readwrite ioctl_dev_info with BTRFS_IOCTL_MAGIC, 30; 13 | IoctlDevInfoArgs); 14 | 15 | ioctl! ( 16 | readwrite ioctl_file_dedupe_range with BTRFS_IOCTL_MAGIC, 54; 17 | IoctlFileDedupeRange); 18 | 19 | ioctl! ( 20 | read ioctl_fs_info with BTRFS_IOCTL_MAGIC, 31; 21 | IoctlFsInfoArgs); 22 | 23 | ioctl! ( 24 | readwrite ioctl_space_info with BTRFS_IOCTL_MAGIC, 20; 25 | IoctlSpaceArgs); 26 | 27 | // ---------- other 28 | 29 | ioctl! ( 30 | readwrite ioctl_fiemap with 'f' as u64, 11; 31 | IoctlFiemap); 32 | 33 | // ex: noet ts=4 filetype=rust 34 | -------------------------------------------------------------------------------- /src/diskformat/item/inode_ref.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::fmt::Error as FmtError; 3 | use std::fmt::Formatter; 4 | use std::mem; 5 | 6 | use super::super::*; 7 | 8 | #[ derive (Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd) ] 9 | pub struct BtrfsInodeRef <'a> { 10 | header: & 'a BtrfsLeafItemHeader, 11 | data_bytes: & 'a [u8], 12 | } 13 | 14 | leaf_item_composite_type_entries! ( 15 | BtrfsInodeRef, 16 | BtrfsInodeRefEntries, 17 | ); 18 | 19 | leaf_item_composite_type_implementation! ( 20 | BtrfsInodeRef, 21 | BtrfsInodeRefData, 22 | BtrfsInodeRefEntries, 23 | BtrfsInodeRefIterator, 24 | ); 25 | 26 | leaf_item_composite_type_iterator! ( 27 | BtrfsInodeRef, 28 | BtrfsInodeRefData, 29 | BtrfsInodeRefEntry, 30 | BtrfsInodeRefIterator, 31 | name_length, 32 | ); 33 | 34 | // ex: noet ts=4 filetype=rust 35 | -------------------------------------------------------------------------------- /src/linux/ctypes/mod.rs: -------------------------------------------------------------------------------- 1 | mod ioctl_constants; 2 | mod ioctl_dev_info_args; 3 | mod ioctl_device_path; 4 | mod ioctl_defrag_range_args; 5 | mod ioctl_fiemap; 6 | mod ioctl_fiemap_extent; 7 | mod ioctl_file_dedupe_range; 8 | mod ioctl_file_dedupe_range_info; 9 | mod ioctl_fs_info_args; 10 | mod ioctl_space_args; 11 | mod ioctl_space_info; 12 | 13 | pub use self::ioctl_constants::*; 14 | pub use self::ioctl_dev_info_args::*; 15 | pub use self::ioctl_device_path::*; 16 | pub use self::ioctl_defrag_range_args::*; 17 | pub use self::ioctl_fiemap::*; 18 | pub use self::ioctl_fiemap_extent::*; 19 | pub use self::ioctl_file_dedupe_range::*; 20 | pub use self::ioctl_file_dedupe_range_info::*; 21 | pub use self::ioctl_fs_info_args::*; 22 | pub use self::ioctl_space_args::*; 23 | pub use self::ioctl_space_info::*; 24 | 25 | // ex: noet ts=4 filetype=rust 26 | -------------------------------------------------------------------------------- /src/diskformat/core/superblock_reserved.rs: -------------------------------------------------------------------------------- 1 | use std::hash::Hash; 2 | use std::hash::Hasher; 3 | 4 | #[ derive (Copy) ] 5 | pub struct BtrfsSuperblockReserved { 6 | data: [u8; 0xf0], 7 | } 8 | 9 | impl Clone for BtrfsSuperblockReserved { 10 | 11 | fn clone (& self) -> BtrfsSuperblockReserved { 12 | * self 13 | } 14 | 15 | } 16 | 17 | impl Eq for BtrfsSuperblockReserved { 18 | } 19 | 20 | impl Hash for BtrfsSuperblockReserved { 21 | 22 | fn hash ( 23 | & self, 24 | state: & mut State, 25 | ) { 26 | 27 | (& self.data [..]).hash ( 28 | state); 29 | 30 | } 31 | 32 | } 33 | 34 | impl PartialEq for BtrfsSuperblockReserved { 35 | 36 | fn eq ( 37 | & self, 38 | other: & Self, 39 | ) -> bool { 40 | 41 | & self.data [..] == & other.data [..] 42 | 43 | } 44 | 45 | } 46 | 47 | // ex: noet ts=4 filetype=rust 48 | -------------------------------------------------------------------------------- /src/diskformat/core/superblock_unused.rs: -------------------------------------------------------------------------------- 1 | use std::hash::Hash; 2 | use std::hash::Hasher; 3 | 4 | #[ derive (Copy) ] 5 | pub struct BtrfsSuperblockUnused { 6 | data: [u8; 0x235], 7 | } 8 | 9 | impl Clone for BtrfsSuperblockUnused { 10 | 11 | fn clone (& self) -> BtrfsSuperblockUnused { 12 | * self 13 | } 14 | 15 | } 16 | 17 | impl Eq for BtrfsSuperblockUnused { 18 | } 19 | 20 | impl Hash for BtrfsSuperblockUnused { 21 | 22 | fn hash ( 23 | & self, 24 | state: & mut State, 25 | ) { 26 | 27 | (& self.data [..]).hash ( 28 | state); 29 | 30 | } 31 | 32 | } 33 | 34 | impl PartialEq for BtrfsSuperblockUnused { 35 | 36 | fn eq ( 37 | & self, 38 | other: & BtrfsSuperblockUnused, 39 | ) -> bool { 40 | 41 | & self.data [..] == & other.data [..] 42 | 43 | } 44 | 45 | } 46 | 47 | // ex: noet ts=4 filetype=rust 48 | -------------------------------------------------------------------------------- /src/linux/types/group_type.rs: -------------------------------------------------------------------------------- 1 | use linux::imports::*; 2 | 3 | #[ derive (Debug, Eq, PartialEq) ] 4 | pub enum GroupType { 5 | Data, 6 | System, 7 | MetaData, 8 | DataAndMetaData, 9 | GlobalReserve, 10 | Unknown, 11 | } 12 | 13 | impl From for GroupType { 14 | 15 | fn from ( 16 | flags: u64, 17 | ) -> GroupType { 18 | 19 | match flags & BLOCK_GROUP_TYPE_AND_RESERVED_MASK { 20 | 21 | BLOCK_GROUP_DATA => 22 | GroupType::Data, 23 | 24 | BLOCK_GROUP_SYSTEM => 25 | GroupType::System, 26 | 27 | BLOCK_GROUP_METADATA => 28 | GroupType::MetaData, 29 | 30 | BLOCK_GROUP_DATA_AND_METADATA => 31 | GroupType::DataAndMetaData, 32 | 33 | BLOCK_GROUP_RESERVED => 34 | GroupType::GlobalReserve, 35 | 36 | _ => 37 | GroupType::Unknown, 38 | 39 | } 40 | 41 | } 42 | 43 | } 44 | 45 | // ex: noet ts=4 filetype=rust 46 | -------------------------------------------------------------------------------- /src/diskformat/macros/tree_item_range_accessor.rs: -------------------------------------------------------------------------------- 1 | macro_rules! tree_item_range_accessor { 2 | 3 | ( 4 | $ accessor_name : ident , 5 | $ item_type : ident , 6 | $ item_type_id : expr , 7 | $ leaf_item_type : ident , 8 | ) => { 9 | 10 | pub fn $ accessor_name ( 11 | & 'a self, 12 | object_id: u64, 13 | ) -> Vec <$ item_type <'a>> { 14 | 15 | assert_ne! ( 16 | $ item_type_id, 17 | 0xff); 18 | 19 | self.tree_items ().range ( 20 | BtrfsKey::new ( 21 | object_id, 22 | $ item_type_id, 23 | 0) 24 | .. 25 | BtrfsKey::new ( 26 | object_id, 27 | $ item_type_id + 1, 28 | 0) 29 | ).map ( 30 | |(_key, item)| 31 | 32 | leaf_item_destructure! ( 33 | item, 34 | $ leaf_item_type, 35 | ).unwrap ().clone () 36 | 37 | ).collect () 38 | 39 | } 40 | 41 | }; 42 | 43 | } 44 | 45 | // ex: noet ts=4 filetype=rust 46 | -------------------------------------------------------------------------------- /src/diskformat/core/superblock_system_chunks_data.rs: -------------------------------------------------------------------------------- 1 | use std::hash::Hash; 2 | use std::hash::Hasher; 3 | 4 | #[ derive (Copy) ] 5 | pub struct BtrfsSuperblockSystemChunksData { 6 | pub data: [u8; 0x800], 7 | } 8 | 9 | impl Clone for BtrfsSuperblockSystemChunksData { 10 | 11 | fn clone (& self) -> BtrfsSuperblockSystemChunksData { 12 | * self 13 | } 14 | 15 | } 16 | 17 | impl Eq for BtrfsSuperblockSystemChunksData { 18 | } 19 | 20 | impl Hash for BtrfsSuperblockSystemChunksData { 21 | 22 | fn hash ( 23 | & self, 24 | state: & mut State, 25 | ) { 26 | 27 | (& self.data [..]).hash ( 28 | state); 29 | 30 | } 31 | 32 | } 33 | 34 | impl PartialEq for BtrfsSuperblockSystemChunksData { 35 | 36 | fn eq ( 37 | & self, 38 | other: & BtrfsSuperblockSystemChunksData, 39 | ) -> bool { 40 | 41 | & self.data [..] == & other.data [..] 42 | 43 | } 44 | 45 | } 46 | 47 | // ex: noet ts=4 filetype=rust 48 | -------------------------------------------------------------------------------- /src/diskformat/macros/leaf_item_composite_type_entries.rs: -------------------------------------------------------------------------------- 1 | macro_rules! leaf_item_composite_type_entries { 2 | 3 | ( 4 | $ container : ident , 5 | $ entries : ident , 6 | ) => { 7 | 8 | pub struct $ entries <'a> { 9 | container: & 'a $ container <'a>, 10 | } 11 | 12 | impl <'a> $ entries <'a> { 13 | 14 | pub fn new ( 15 | container: & 'a $ container <'a>, 16 | ) -> $ entries <'a> { 17 | 18 | $ entries { 19 | container: container, 20 | } 21 | 22 | } 23 | 24 | } 25 | 26 | impl <'a> Debug for $ entries <'a> { 27 | 28 | fn fmt ( 29 | & self, 30 | formatter: & mut Formatter, 31 | ) -> Result <(), FmtError> { 32 | 33 | let mut debug_list = 34 | formatter.debug_list (); 35 | 36 | debug_list.entries ( 37 | self.container.entries ()); 38 | 39 | debug_list.finish () 40 | 41 | } 42 | 43 | } 44 | 45 | } 46 | 47 | } 48 | 49 | // ex: noet ts=4 filetype=rust 50 | -------------------------------------------------------------------------------- /src/linux/types/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module contains the public types used by this library. 2 | //! 3 | //! Many of these closely match the C types used internally when interfacing 4 | //! with the kernal, but they are desiggned to be idomatically Rust and far 5 | //! simpler to use. 6 | 7 | mod compression_type; 8 | mod dedupe_range; 9 | mod dedupe_range_dest_info; 10 | mod dedupe_range_status; 11 | mod device_info; 12 | mod file_descriptor; 13 | mod filesystem_info; 14 | mod group_profile; 15 | mod group_type; 16 | mod space_info; 17 | 18 | pub use self::compression_type::*; 19 | pub use self::dedupe_range::*; 20 | pub use self::dedupe_range_dest_info::*; 21 | pub use self::dedupe_range_status::*; 22 | pub use self::device_info::*; 23 | pub use self::file_descriptor::*; 24 | pub use self::filesystem_info::*; 25 | pub use self::group_profile::*; 26 | pub use self::group_type::*; 27 | pub use self::space_info::*; 28 | 29 | // ex: noet ts=4 filetype=rust 30 | -------------------------------------------------------------------------------- /src/diskformat/core/superblock_chunk_item.rs: -------------------------------------------------------------------------------- 1 | use super::super::*; 2 | 3 | pub struct BtrfsSuperblockChunkItem <'a> { 4 | key: & 'a BtrfsKey, 5 | data: & 'a BtrfsChunkItemData, 6 | } 7 | 8 | impl <'a> BtrfsSuperblockChunkItem <'a> { 9 | 10 | pub fn new ( 11 | key: & 'a BtrfsKey, 12 | data: & 'a BtrfsChunkItemData, 13 | ) -> Result , String> { 14 | 15 | if key.item_type () != BTRFS_CHUNK_ITEM_TYPE { 16 | 17 | return Err ( 18 | format! ( 19 | "Invalid key type for chunk item: 0x{:02x}", 20 | key.item_type ())); 21 | 22 | } 23 | 24 | Ok ( 25 | BtrfsSuperblockChunkItem { 26 | key: key, 27 | data: data, 28 | } 29 | ) 30 | 31 | } 32 | 33 | pub fn key (& self) -> & BtrfsKey { 34 | self.key 35 | } 36 | 37 | pub fn data (& self) -> & BtrfsChunkItemData { 38 | self.data 39 | } 40 | 41 | pub fn stripes (& self) -> & [BtrfsChunkItemStripeData] { 42 | self.data.stripes () 43 | } 44 | 45 | } 46 | 47 | // ex: noet ts=4 filetype=rust 48 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This library contains a partial implementation of a library for operating 2 | //! with BTRFS filesystems. It is based on the C implementations of the BTRFS 3 | //! utilities. 4 | //! 5 | //! It's home page is at [gitlab.wellbehavedsoftware.com] 6 | //! (https://gitlab.wellbehavedsoftware.com/well-behaved-software/rust-btrfs). 7 | 8 | #![ allow (unused_parens) ] 9 | 10 | #![ deny (non_camel_case_types) ] 11 | #![ deny (non_snake_case) ] 12 | #![ deny (non_upper_case_globals) ] 13 | #![ deny (unreachable_patterns) ] 14 | #![ deny (unused_comparisons) ] 15 | #![ deny (unused_must_use) ] 16 | 17 | #[ macro_use ] 18 | extern crate lazy_static; 19 | 20 | #[ macro_use ] 21 | extern crate nix; 22 | 23 | #[ macro_use ] 24 | extern crate output; 25 | 26 | extern crate byteorder; 27 | extern crate chrono; 28 | extern crate crc; 29 | extern crate itertools; 30 | extern crate libc; 31 | extern crate flate2; 32 | extern crate memmap; 33 | extern crate minilzo; 34 | extern crate uuid; 35 | 36 | pub mod compress; 37 | pub mod diskformat; 38 | pub mod linux; 39 | 40 | pub use linux::*; 41 | 42 | // ex: noet ts=4 filetype=rust 43 | -------------------------------------------------------------------------------- /src/diskformat/item/dir_item_data.rs: -------------------------------------------------------------------------------- 1 | use super::super::prelude::*; 2 | 3 | #[ repr (C, packed) ] 4 | #[ derive (Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd) ] 5 | pub struct BtrfsDirItemData { 6 | pub child_key: BtrfsKey, 7 | pub transaction_id: u64, 8 | pub data_size: u16, 9 | pub name_size: u16, 10 | pub child_type: u8, 11 | } 12 | 13 | impl Debug for BtrfsDirItemData { 14 | 15 | fn fmt ( 16 | & self, 17 | formatter: & mut Formatter, 18 | ) -> Result <(), FmtError> { 19 | 20 | let mut debug_struct = 21 | formatter.debug_struct ( 22 | "BtrfsDirItemData"); 23 | 24 | debug_struct.field ( 25 | "child_key", 26 | & NakedString::from ( 27 | self.child_key.to_string ())); 28 | 29 | debug_struct.field ( 30 | "transaction_id", 31 | & self.transaction_id); 32 | 33 | debug_struct.field ( 34 | "data_size", 35 | & self.data_size); 36 | 37 | debug_struct.field ( 38 | "name_size", 39 | & self.name_size); 40 | 41 | debug_struct.field ( 42 | "child_type", 43 | & self.child_type); 44 | 45 | debug_struct.finish () 46 | 47 | } 48 | 49 | } 50 | 51 | // ex: noet ts=4 filetype=rust 52 | -------------------------------------------------------------------------------- /src/diskformat/item/chunk_item_data.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::slice; 3 | 4 | use super::super::*; 5 | 6 | #[ repr (C, packed) ] 7 | #[ derive (Copy, Clone, Debug) ] 8 | pub struct BtrfsChunkItemData { 9 | pub chunk_size: u64, 10 | pub root_object_id: u64, 11 | pub stripe_length: u64, 12 | pub flags: u64, 13 | pub optimal_io_alignment: u32, 14 | pub optimal_io_width: u32, 15 | pub minimal_io_size: u32, 16 | pub num_stripes: u16, 17 | pub sub_stripes: u16, 18 | } 19 | 20 | impl BtrfsChunkItemData { 21 | 22 | pub fn chunk_size (& self) -> u64 { 23 | self.chunk_size 24 | } 25 | 26 | pub fn stripes (& self) -> & [BtrfsChunkItemStripeData] { 27 | 28 | unsafe { 29 | slice::from_raw_parts ( 30 | ( 31 | self 32 | as * const BtrfsChunkItemData 33 | as * const u8 34 | ).offset ( 35 | mem::size_of:: () as isize, 36 | ) as * const BtrfsChunkItemStripeData, 37 | self.num_stripes as usize, 38 | ) 39 | } 40 | 41 | } 42 | 43 | pub fn num_stripes (& self) -> u16 { 44 | self.num_stripes 45 | } 46 | 47 | pub fn sub_stripes (& self) -> u16 { 48 | self.sub_stripes 49 | } 50 | 51 | } 52 | 53 | // ex: noet ts=4 filetype=rust 54 | -------------------------------------------------------------------------------- /src/diskformat/core/mod.rs: -------------------------------------------------------------------------------- 1 | mod checksum; 2 | mod dev_item; 3 | mod dev_item_data; 4 | mod device; 5 | mod device_set; 6 | mod file_type; 7 | mod key; 8 | mod label; 9 | mod mmap_device_set; 10 | mod physical_address; 11 | mod root_backup; 12 | mod superblock; 13 | mod superblock_chunk_item; 14 | mod superblock_data; 15 | mod superblock_reserved; 16 | mod superblock_system_chunks; 17 | mod superblock_system_chunks_data; 18 | mod superblock_unused; 19 | mod timestamp; 20 | mod uuid; 21 | 22 | pub use self::checksum::*; 23 | pub use self::device::*; 24 | pub use self::device_set::*; 25 | pub use self::dev_item::*; 26 | pub use self::dev_item_data::*; 27 | pub use self::file_type::*; 28 | pub use self::key::*; 29 | pub use self::label::*; 30 | pub use self::mmap_device_set::*; 31 | pub use self::physical_address::*; 32 | pub use self::root_backup::*; 33 | pub use self::superblock::*; 34 | pub use self::superblock_chunk_item::*; 35 | pub use self::superblock_data::*; 36 | pub use self::superblock_reserved::*; 37 | pub use self::superblock_system_chunks::*; 38 | pub use self::superblock_system_chunks_data::*; 39 | pub use self::superblock_unused::*; 40 | pub use self::timestamp::*; 41 | pub use self::uuid::*; 42 | 43 | // ex: noet ts=4 filetype=rust 44 | -------------------------------------------------------------------------------- /src/diskformat/naked_string.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::fmt::Debug; 3 | use std::fmt::Display; 4 | use std::fmt::Error as FmtError; 5 | use std::fmt::Formatter; 6 | 7 | pub struct NakedString <'a> (Cow <'a, str>); 8 | 9 | impl <'a> From <& 'a str> for NakedString <'a> { 10 | 11 | fn from ( 12 | value: & str, 13 | ) -> NakedString { 14 | 15 | NakedString (Cow::Borrowed (value)) 16 | 17 | } 18 | 19 | } 20 | 21 | impl <'a> From for NakedString <'a> { 22 | 23 | fn from ( 24 | value: String, 25 | ) -> NakedString <'a> { 26 | 27 | NakedString (Cow::Owned (value)) 28 | 29 | } 30 | 31 | } 32 | 33 | impl <'a> Debug for NakedString <'a> { 34 | 35 | fn fmt ( 36 | & self, 37 | formatter: & mut Formatter, 38 | ) -> Result <(), FmtError> { 39 | 40 | let NakedString (ref value) = 41 | * self; 42 | 43 | formatter.write_str ( 44 | value.as_ref ()) 45 | 46 | } 47 | 48 | } 49 | 50 | impl <'a> Display for NakedString <'a> { 51 | 52 | fn fmt ( 53 | & self, 54 | formatter: & mut Formatter, 55 | ) -> Result <(), FmtError> { 56 | 57 | let NakedString (ref value) = 58 | * self; 59 | 60 | formatter.write_str ( 61 | value.as_ref ()) 62 | 63 | } 64 | 65 | } 66 | 67 | // ex: noet ts=4 filetype=rust 68 | -------------------------------------------------------------------------------- /src/linux/ctypes/ioctl_device_path.rs: -------------------------------------------------------------------------------- 1 | use linux::imports::*; 2 | 3 | #[ repr (C) ] 4 | #[ derive (Copy) ] 5 | pub struct IoctlDevicePath { 6 | pub path: [u8; DEVICE_PATH_NAME_MAX], 7 | } 8 | 9 | impl IoctlDevicePath { 10 | 11 | pub fn as_vec ( 12 | & self, 13 | ) -> Vec { 14 | 15 | self.path.iter ().cloned ().take_while ( 16 | |item| 17 | * item != 0 18 | ).collect () 19 | 20 | } 21 | 22 | pub fn as_os_string ( 23 | & self, 24 | ) -> OsString { 25 | 26 | OsString::from_vec ( 27 | self.as_vec ()) 28 | 29 | } 30 | 31 | } 32 | 33 | impl Clone for IoctlDevicePath { 34 | 35 | fn clone ( 36 | & self, 37 | ) -> Self { 38 | 39 | IoctlDevicePath { 40 | path: self.path, 41 | } 42 | 43 | } 44 | 45 | fn clone_from ( 46 | & mut self, 47 | source: & Self, 48 | ) { 49 | 50 | self.path.clone_from_slice ( 51 | & source.path) 52 | 53 | } 54 | 55 | } 56 | 57 | impl fmt::Debug for IoctlDevicePath { 58 | 59 | fn fmt ( 60 | & self, 61 | formatter: & mut fmt::Formatter, 62 | ) -> fmt::Result { 63 | 64 | let string_bytes = 65 | self.as_vec (); 66 | 67 | let rust_string = 68 | String::from_utf8_lossy ( 69 | & string_bytes); 70 | 71 | rust_string.fmt ( 72 | formatter) 73 | 74 | } 75 | 76 | } 77 | 78 | // ex: noet ts=4 filetype=rust 79 | -------------------------------------------------------------------------------- /src/diskformat/macros/tree_item_accessor.rs: -------------------------------------------------------------------------------- 1 | macro_rules! tree_item_accessor { 2 | 3 | ( 4 | $ accessor_name : ident , 5 | $ item_type : ident , 6 | $ item_type_id : expr , 7 | $ leaf_item_type : ident , 8 | ) => { 9 | 10 | pub fn $ accessor_name ( 11 | & 'a self, 12 | object_id: u64, 13 | offset: u64, 14 | ) -> Option <$ item_type <'a>> { 15 | 16 | self.tree_items ().get ( 17 | & BtrfsKey::new ( 18 | object_id, 19 | $ item_type_id, 20 | offset) 21 | ).map ( 22 | |item| 23 | 24 | leaf_item_destructure! ( 25 | item, 26 | $ leaf_item_type, 27 | ).unwrap ().clone () 28 | 29 | ) 30 | 31 | } 32 | 33 | }; 34 | 35 | ( 36 | $ accessor_name : ident , 37 | $ item_type : ident , 38 | $ item_type_id : expr , 39 | $ leaf_item_type : ident , 40 | $ offset : expr , 41 | ) => { 42 | 43 | pub fn $ accessor_name ( 44 | & 'a self, 45 | object_id: u64, 46 | ) -> Option <$ item_type <'a>> { 47 | 48 | self.tree_items ().get ( 49 | & BtrfsKey::new ( 50 | object_id, 51 | $ item_type_id, 52 | $ offset) 53 | ).map ( 54 | |item| 55 | 56 | leaf_item_destructure! ( 57 | item, 58 | $ leaf_item_type, 59 | ).unwrap ().clone () 60 | 61 | ) 62 | 63 | } 64 | 65 | }; 66 | 67 | } 68 | 69 | // ex: noet ts=4 filetype=rust 70 | -------------------------------------------------------------------------------- /src/linux/ctypes/ioctl_dev_info_args.rs: -------------------------------------------------------------------------------- 1 | use linux::ctypes::ioctl_constants::*; 2 | use linux::ctypes::ioctl_device_path::*; 3 | 4 | #[ repr (C) ] 5 | #[ derive (Copy, Clone, Debug) ] 6 | pub struct IoctlDevInfoArgs { 7 | pub devid: u64, 8 | pub uuid: [u8; UUID_SIZE], 9 | pub bytes_used: u64, 10 | pub total_bytes: u64, 11 | pub unused0: [u64; 32], 12 | pub unused1: [u64; 32], 13 | pub unused2: [u64; 32], 14 | pub unused3: [u64; 32], 15 | pub unused4: [u64; 32], 16 | pub unused5: [u64; 32], 17 | pub unused6: [u64; 32], 18 | pub unused7: [u64; 32], 19 | pub unused8: [u64; 32], 20 | pub unused9: [u64; 32], 21 | pub unused10: [u64; 32], 22 | pub unused11: [u64; 27], 23 | pub path: IoctlDevicePath, 24 | } 25 | 26 | impl IoctlDevInfoArgs { 27 | 28 | pub fn new ( 29 | ) -> IoctlDevInfoArgs { 30 | 31 | IoctlDevInfoArgs { 32 | devid: 0, 33 | uuid: [0; UUID_SIZE], 34 | bytes_used: 0, 35 | total_bytes: 0, 36 | unused0: [0; 32], 37 | unused1: [0; 32], 38 | unused2: [0; 32], 39 | unused3: [0; 32], 40 | unused4: [0; 32], 41 | unused5: [0; 32], 42 | unused6: [0; 32], 43 | unused7: [0; 32], 44 | unused8: [0; 32], 45 | unused9: [0; 32], 46 | unused10: [0; 32], 47 | unused11: [0; 27], 48 | path: IoctlDevicePath { 49 | path: [0; DEVICE_PATH_NAME_MAX], 50 | }, 51 | } 52 | 53 | } 54 | 55 | } 56 | 57 | // ex: noet ts=4 filetype=rust 58 | -------------------------------------------------------------------------------- /src/diskformat/item/chunk_item.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | 3 | use super::super::*; 4 | 5 | #[ derive (Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd) ] 6 | pub struct BtrfsChunkItem <'a> { 7 | header: & 'a BtrfsLeafItemHeader, 8 | data_bytes: & 'a [u8], 9 | } 10 | 11 | impl <'a> BtrfsChunkItem <'a> { 12 | 13 | pub fn from_bytes ( 14 | header: & 'a BtrfsLeafItemHeader, 15 | data_bytes: & 'a [u8], 16 | ) -> Result , String> { 17 | 18 | // sanity check 19 | 20 | if data_bytes.len () < mem::size_of:: () { 21 | 22 | return Err ( 23 | format! ( 24 | "Must be at least 0x{:x} bytes", 25 | mem::size_of:: ())); 26 | 27 | } 28 | 29 | // TODO check stripes 30 | 31 | // create chunk item 32 | 33 | Ok ( 34 | BtrfsChunkItem { 35 | header: header, 36 | data_bytes: data_bytes, 37 | } 38 | ) 39 | 40 | } 41 | 42 | pub fn data (& self) -> & BtrfsChunkItemData { 43 | 44 | unsafe { 45 | & * ( 46 | self.data_bytes.as_ptr () 47 | as * const BtrfsChunkItemData 48 | ) 49 | } 50 | 51 | } 52 | 53 | pub fn stripes (& self) -> & [BtrfsChunkItemStripeData] { 54 | self.data ().stripes () 55 | } 56 | 57 | } 58 | 59 | impl <'a> BtrfsLeafItemContents <'a> for BtrfsChunkItem <'a> { 60 | 61 | fn header (& self) -> & BtrfsLeafItemHeader { 62 | self.header 63 | } 64 | 65 | } 66 | 67 | // ex: noet ts=4 filetype=rust 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust BTRFS library 2 | 3 | Home page at [rust-btrfs.com](http://rust-btrfs.com) 4 | 5 | Git repository at [github.com](https://github.com/wellbehavedsoftware/rust-btrfs) 6 | 7 | Available from [crates.io](https://crates.io/crates/btrfs) 8 | 9 | Written by [James Pharaoh](mailto:james@pharaoh.uk) 10 | 11 | Documentation at [docs.rs](https://docs.rs/btrfs/) 12 | 13 | Open sourced under the permissive [MIT licence](https://opensource.org/licenses/MIT) 14 | 15 | ## Description 16 | 17 | This is a (somewhat incomplete) rust reimplementation of the rust userspace 18 | library. In fact, there isn't a C userspace library as such, but the userspace 19 | tools include lowlevel interfaces which this project is based on. 20 | 21 | This is mostly here to implement the things I need for now, but I'm open to any 22 | contributions to make this the standard BTRFS userspace library for rust! 23 | 24 | ## Supported features 25 | 26 | This library consists of a number of wrappers around the BTRFS ioctls. 27 | 28 | - Deduplication (not BTRFS specific) 29 | - Fiemap (file extent map, not BTRFS specific) 30 | - File system info 31 | - Space and device info 32 | 33 | ## Other links 34 | 35 | - [BTRFS wiki](https://btrfs.wiki.kernel.org/index.php/Main_Page) 36 | - BTRFS utilities (kdave) git://git.kernel.org/pub/scm/linux/kernel/git/kdave/btrfs-progs.git 37 | - BTRFS utilities (mason) git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-progs.git 38 | -------------------------------------------------------------------------------- /src/linux/types/group_profile.rs: -------------------------------------------------------------------------------- 1 | use linux::imports::*; 2 | 3 | #[ derive (Debug, Eq, PartialEq) ] 4 | pub enum GroupProfile { 5 | Single, 6 | Raid0, 7 | Raid1, 8 | Raid5, 9 | Raid6, 10 | Dup, 11 | Raid10, 12 | Unknown, 13 | } 14 | 15 | impl GroupProfile { 16 | 17 | pub fn from_string ( 18 | string_value: & str, 19 | ) -> Option { 20 | 21 | match string_value { 22 | 23 | "single" => Some (GroupProfile::Single), 24 | "raid0" => Some (GroupProfile::Raid0), 25 | "raid1" => Some (GroupProfile::Raid1), 26 | "raid5" => Some (GroupProfile::Raid5), 27 | "raid6" => Some (GroupProfile::Raid6), 28 | "dup" => Some (GroupProfile::Dup), 29 | "raid10" => Some (GroupProfile::Raid10), 30 | "unknown" => Some (GroupProfile::Unknown), 31 | 32 | _ => None, 33 | 34 | } 35 | 36 | } 37 | 38 | } 39 | 40 | impl From for GroupProfile { 41 | 42 | fn from ( 43 | flags: u64, 44 | ) -> GroupProfile { 45 | 46 | match flags & BLOCK_GROUP_PROFILE_MASK { 47 | 48 | 0 => 49 | GroupProfile::Single, 50 | 51 | BLOCK_GROUP_RAID0 => 52 | GroupProfile::Raid0, 53 | 54 | BLOCK_GROUP_RAID1 => 55 | GroupProfile::Raid1, 56 | 57 | BLOCK_GROUP_RAID5 => 58 | GroupProfile::Raid5, 59 | 60 | BLOCK_GROUP_RAID6 => 61 | GroupProfile::Raid6, 62 | 63 | BLOCK_GROUP_DUP => 64 | GroupProfile::Dup, 65 | 66 | BLOCK_GROUP_RAID10 => 67 | GroupProfile::Raid10, 68 | 69 | _ => 70 | GroupProfile::Unknown, 71 | 72 | } 73 | 74 | } 75 | 76 | } 77 | 78 | // ex: noet ts=4 filetype=rust 79 | -------------------------------------------------------------------------------- /src/diskformat/core/physical_address.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::fmt::Display; 3 | use std::fmt::Error as FmtError; 4 | use std::fmt::Formatter; 5 | 6 | #[ derive (Clone, Copy, Eq, Hash, PartialEq) ] 7 | pub struct BtrfsPhysicalAddress { 8 | device_id: u64, 9 | offset: u64, 10 | } 11 | 12 | impl BtrfsPhysicalAddress { 13 | 14 | pub fn new ( 15 | device_id: u64, 16 | offset: u64, 17 | ) -> BtrfsPhysicalAddress { 18 | 19 | BtrfsPhysicalAddress { 20 | device_id: device_id, 21 | offset: offset, 22 | } 23 | 24 | } 25 | 26 | pub fn device_id (& self) -> u64 { 27 | self.device_id 28 | } 29 | 30 | pub fn offset (& self) -> u64 { 31 | self.offset 32 | } 33 | 34 | } 35 | 36 | impl Debug for BtrfsPhysicalAddress { 37 | 38 | fn fmt ( 39 | & self, 40 | formatter: & mut Formatter, 41 | ) -> Result <(), FmtError> { 42 | 43 | let mut debug_struct = 44 | formatter.debug_struct ( 45 | "BtrfsPhysicalAddress"); 46 | 47 | debug_struct.field ( 48 | "device_id", 49 | & self.device_id ()); 50 | 51 | debug_struct.field ( 52 | "offset", 53 | & format! ( 54 | "0x{:x}", 55 | self.offset ())); 56 | 57 | debug_struct.finish () 58 | 59 | } 60 | 61 | } 62 | 63 | impl Display for BtrfsPhysicalAddress { 64 | 65 | fn fmt ( 66 | & self, 67 | formatter: & mut Formatter, 68 | ) -> Result <(), FmtError> { 69 | 70 | formatter.write_fmt ( 71 | format_args! ( 72 | "{}/0x{:x}", 73 | self.device_id (), 74 | self.offset ())) 75 | 76 | } 77 | 78 | } 79 | 80 | // ex: noet ts=4 filetype=rust 81 | -------------------------------------------------------------------------------- /src/diskformat/macros/leaf_item_composite_type_iterator.rs: -------------------------------------------------------------------------------- 1 | macro_rules! leaf_item_composite_type_iterator { 2 | 3 | ( 4 | $ container : ident , 5 | $ data : ident , 6 | $ entry : ident , 7 | $ iterator : ident , 8 | $ ( $ size_field : ident , ) * 9 | ) => { 10 | 11 | pub struct $ iterator <'a> { 12 | header: & 'a BtrfsLeafItemHeader, 13 | data: & 'a [u8], 14 | } 15 | 16 | impl <'a> $ iterator <'a> { 17 | 18 | pub fn new ( 19 | header: & 'a BtrfsLeafItemHeader, 20 | data: & 'a [u8], 21 | ) -> $ iterator <'a> { 22 | 23 | $ iterator { 24 | header: header, 25 | data: data, 26 | } 27 | 28 | } 29 | 30 | } 31 | 32 | impl <'a> Iterator for $ iterator <'a> { 33 | 34 | type Item = $ entry <'a>; 35 | 36 | fn next ( 37 | & mut self, 38 | ) -> Option <$ entry <'a>> { 39 | 40 | use std::mem; 41 | 42 | if ! self.data.is_empty () { 43 | 44 | let data: & 'a $ data = 45 | unsafe { 46 | 47 | mem::transmute ( 48 | & self.data [0]) 49 | 50 | }; 51 | 52 | let entry_size = vec! [ 53 | mem::size_of::<$ data> (), 54 | $ (data . $ size_field as usize , ) * 55 | ].iter ().sum (); 56 | 57 | let entry = 58 | $ entry ::from_bytes ( 59 | self.header, 60 | & self.data [0 .. entry_size], 61 | ).unwrap (); 62 | 63 | self.data = 64 | & self.data [entry_size .. ]; 65 | 66 | Some (entry) 67 | 68 | } else { 69 | 70 | None 71 | 72 | } 73 | 74 | } 75 | 76 | } 77 | 78 | } 79 | 80 | } 81 | 82 | // ex: noet ts=4 filetype=rust 83 | -------------------------------------------------------------------------------- /src/linux/ctypes/ioctl_constants.rs: -------------------------------------------------------------------------------- 1 | pub const UUID_SIZE: usize = 16; 2 | pub const DEVICE_PATH_NAME_MAX: usize = 1024; 3 | 4 | pub const AVAIL_ALLOC_BIT_SINGLE: u64 = 1 << 48; 5 | 6 | pub const BLOCK_GROUP_DATA: u64 = 1 << 0; 7 | pub const BLOCK_GROUP_SYSTEM: u64 = 1 << 1; 8 | pub const BLOCK_GROUP_METADATA: u64 = 1 << 2; 9 | 10 | pub const BLOCK_GROUP_RAID0: u64 = 1 << 3; 11 | pub const BLOCK_GROUP_RAID1: u64 = 1 << 4; 12 | pub const BLOCK_GROUP_DUP: u64 = 1 << 5; 13 | pub const BLOCK_GROUP_RAID10: u64 = 1 << 6; 14 | pub const BLOCK_GROUP_RAID5: u64 = 1 << 7; 15 | pub const BLOCK_GROUP_RAID6: u64 = 1 << 8; 16 | 17 | pub const BLOCK_GROUP_RESERVED: u64 = AVAIL_ALLOC_BIT_SINGLE; 18 | 19 | pub const BLOCK_GROUP_DATA_AND_METADATA: u64 = ( 20 | BLOCK_GROUP_DATA 21 | | BLOCK_GROUP_METADATA 22 | ); 23 | 24 | pub const BLOCK_GROUP_TYPE_MASK: u64 = ( 25 | BLOCK_GROUP_DATA 26 | | BLOCK_GROUP_SYSTEM 27 | | BLOCK_GROUP_METADATA 28 | ); 29 | 30 | pub const BLOCK_GROUP_TYPE_AND_RESERVED_MASK: u64 = ( 31 | BLOCK_GROUP_TYPE_MASK 32 | | BLOCK_GROUP_RESERVED 33 | ); 34 | 35 | pub const BLOCK_GROUP_PROFILE_MASK: u64 = ( 36 | BLOCK_GROUP_RAID0 37 | | BLOCK_GROUP_RAID1 38 | | BLOCK_GROUP_RAID5 39 | | BLOCK_GROUP_RAID6 40 | | BLOCK_GROUP_DUP 41 | | BLOCK_GROUP_RAID10 42 | ); 43 | 44 | pub const COMPRESS_NONE: u32 = 0; 45 | pub const COMPRESS_ZLIB: u32 = 1; 46 | pub const COMPRESS_LZO: u32 = 2; 47 | 48 | pub const DEFRAG_RANGE_COMPRESS: u64 = 1; 49 | pub const DEFRAG_RANGE_START_IO: u64 = 2; 50 | 51 | pub const FILE_DEDUPE_RANGE_SAME: i32 = 0; 52 | pub const FILE_DEDUPE_RANGE_DIFFERS: i32 = 1; 53 | 54 | // ex: noet ts=4 filetype=rust 55 | -------------------------------------------------------------------------------- /src/diskformat/node/leaf_node_items.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | 3 | use diskformat::*; 4 | 5 | pub struct BtrfsLeafNodeItems <'a> { 6 | bytes: & 'a [u8], 7 | index: u32, 8 | num_items: u32, 9 | } 10 | 11 | impl <'a> BtrfsLeafNodeItems <'a> { 12 | 13 | pub fn new ( 14 | bytes: & 'a [u8], 15 | num_items: u32, 16 | ) -> BtrfsLeafNodeItems <'a> { 17 | 18 | BtrfsLeafNodeItems { 19 | bytes: bytes, 20 | index: 0, 21 | num_items: num_items, 22 | } 23 | 24 | } 25 | 26 | } 27 | 28 | impl <'a> Iterator for BtrfsLeafNodeItems <'a> { 29 | 30 | type Item = BtrfsLeafItem <'a>; 31 | 32 | fn next ( 33 | & mut self, 34 | ) -> Option > { 35 | 36 | if self.index < self.num_items { 37 | 38 | // read header 39 | 40 | let header_start = 41 | self.index as usize 42 | * mem::size_of:: (); 43 | 44 | let header_end = 45 | self.index as usize 46 | * mem::size_of:: () 47 | + mem::size_of:: (); 48 | 49 | let item_header_bytes = 50 | & self.bytes [ 51 | header_start .. header_end]; 52 | 53 | let item_header = 54 | BtrfsLeafItemHeader::from_bytes ( 55 | item_header_bytes, 56 | ).unwrap (); 57 | 58 | // read data 59 | 60 | let item_data = 61 | & self.bytes [ 62 | item_header.data_offset () as usize 63 | .. 64 | item_header.data_offset () as usize 65 | + item_header.data_size () as usize 66 | ]; 67 | 68 | self.index += 1; 69 | 70 | Some ( 71 | BtrfsLeafItem::from_bytes ( 72 | item_header, 73 | item_data) 74 | ) 75 | 76 | } else { 77 | 78 | None 79 | 80 | } 81 | 82 | } 83 | 84 | } 85 | 86 | // ex: noet ts=4 filetype=rust 87 | -------------------------------------------------------------------------------- /src/diskformat/macros/leaf_item_composite_type_implementation.rs: -------------------------------------------------------------------------------- 1 | macro_rules! leaf_item_composite_type_implementation { 2 | 3 | ( 4 | $ container : ident, 5 | $ data : ident , 6 | $ entries : ident , 7 | $ iterator : ident , 8 | ) => { 9 | 10 | impl <'a> $ container <'a> { 11 | 12 | pub fn from_bytes ( 13 | header: & 'a BtrfsLeafItemHeader, 14 | data_bytes: & 'a [u8], 15 | ) -> Result <$ container <'a>, String> { 16 | 17 | // sanity check 18 | 19 | if data_bytes.len () < mem::size_of::<$ data> () { 20 | 21 | return Err ( 22 | format! ( 23 | "Must be at least 0x{:x} bytes", 24 | mem::size_of::<$ data> ())); 25 | 26 | } 27 | 28 | // return 29 | 30 | Ok ($ container { 31 | header, 32 | data_bytes, 33 | }) 34 | 35 | } 36 | 37 | pub fn entries (& self) -> $ iterator <'a> { 38 | 39 | $ iterator ::new ( 40 | self.header, 41 | self.data_bytes) 42 | 43 | } 44 | 45 | } 46 | 47 | impl <'a> BtrfsLeafItemContents <'a> for $ container <'a> { 48 | 49 | fn header (& self) -> & BtrfsLeafItemHeader { 50 | self.header 51 | } 52 | 53 | } 54 | 55 | impl <'a> Debug for $ container <'a> { 56 | 57 | fn fmt ( 58 | & self, 59 | formatter: & mut Formatter, 60 | ) -> Result <(), FmtError> { 61 | 62 | let mut debug_struct = 63 | formatter.debug_struct ( 64 | stringify! ($ container)); 65 | 66 | debug_struct.field ( 67 | "key", 68 | & NakedString::from ( 69 | self.key ().to_string_no_type_decimal ())); 70 | 71 | debug_struct.field ( 72 | "entries", 73 | & $ entries ::new ( 74 | self)); 75 | 76 | debug_struct.finish () 77 | 78 | } 79 | 80 | } 81 | 82 | } 83 | 84 | } 85 | 86 | // ex: noet ts=4 filetype=rust 87 | -------------------------------------------------------------------------------- /src/diskformat/core/timestamp.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::fmt::Display; 3 | use std::fmt::Error as FmtError; 4 | use std::fmt::Formatter; 5 | 6 | use chrono::NaiveDateTime; 7 | 8 | #[ repr (C, packed) ] 9 | #[ derive (Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd) ] 10 | pub struct BtrfsTimestamp { 11 | seconds: i64, 12 | nano_seconds: u32, 13 | } 14 | 15 | impl BtrfsTimestamp { 16 | 17 | pub fn new ( 18 | seconds: i64, 19 | nano_seconds: u32, 20 | ) -> BtrfsTimestamp { 21 | 22 | BtrfsTimestamp { 23 | seconds: seconds, 24 | nano_seconds: nano_seconds, 25 | } 26 | 27 | } 28 | 29 | pub fn seconds (& self) -> i64 { 30 | self.seconds 31 | } 32 | 33 | pub fn nano_seconds (& self) -> u32 { 34 | self.nano_seconds 35 | } 36 | 37 | pub fn to_naive_date_time (& self) -> NaiveDateTime { 38 | 39 | NaiveDateTime::from_timestamp ( 40 | self.seconds, 41 | self.nano_seconds) 42 | 43 | } 44 | 45 | pub fn to_string (& self) -> String { 46 | 47 | self.to_naive_date_time ().format ( 48 | "%Y-%m-%dT%H:%M:%S%.fZ", 49 | ).to_string () 50 | 51 | } 52 | 53 | } 54 | 55 | impl Debug for BtrfsTimestamp { 56 | 57 | fn fmt ( 58 | & self, 59 | formatter: & mut Formatter, 60 | ) -> Result <(), FmtError> { 61 | 62 | formatter.write_fmt ( 63 | format_args! ( 64 | "BtrfsTimestamp ({})", 65 | self.to_naive_date_time ().format ( 66 | "%Y-%m-%dT%H:%M:%S%.fZ", 67 | ))) 68 | 69 | } 70 | 71 | } 72 | 73 | impl Display for BtrfsTimestamp { 74 | 75 | fn fmt ( 76 | & self, 77 | formatter: & mut Formatter, 78 | ) -> Result <(), FmtError> { 79 | 80 | formatter.write_fmt ( 81 | format_args! ( 82 | "{}", 83 | self.to_naive_date_time ().format ( 84 | "%Y-%m-%dT%H:%M:%S%.fZ", 85 | ))) 86 | 87 | } 88 | 89 | } 90 | 91 | // ex: noet ts=4 filetype=rust 92 | -------------------------------------------------------------------------------- /src/linux/operations/defragment.rs: -------------------------------------------------------------------------------- 1 | use linux::imports::*; 2 | 3 | // --------- high level wrapper 4 | 5 | pub fn defragment_file < 6 | FilePathRef: AsRef 7 | > ( 8 | file_path: FilePathRef, 9 | extent_threshold: u32, 10 | compression_type: CompressionType, 11 | flush_to_disk: bool, 12 | ) -> Result <(), String> { 13 | 14 | let file_path = 15 | file_path.as_ref (); 16 | 17 | let file_descriptor = 18 | FileDescriptor::open ( 19 | file_path, 20 | libc::O_RDONLY, 21 | ).map_err ( 22 | |error| 23 | 24 | format! ( 25 | "Error opening file: {}", 26 | error) 27 | 28 | ) ?; 29 | 30 | defragment_range ( 31 | file_descriptor.get_value (), 32 | 0, 33 | -1_i64 as u64, 34 | extent_threshold, 35 | compression_type, 36 | flush_to_disk, 37 | ) 38 | 39 | } 40 | 41 | pub fn defragment_range ( 42 | file_descriptor: libc::c_int, 43 | start: u64, 44 | len: u64, 45 | extent_threshold: u32, 46 | compression_type: CompressionType, 47 | flush_to_disk: bool, 48 | ) -> Result <(), String> { 49 | 50 | let defrag_range_args = 51 | IoctlDefragRangeArgs { 52 | start: start, 53 | len: len, 54 | flags: ( 55 | if compression_type != CompressionType::None { 56 | DEFRAG_RANGE_COMPRESS 57 | } else { 0 } 58 | | 59 | if flush_to_disk { 60 | DEFRAG_RANGE_START_IO 61 | } else { 0 } 62 | ), 63 | extent_thresh: extent_threshold, 64 | compress_type: compression_type.into (), 65 | unused_0: 0, 66 | unused_1: 0, 67 | unused_2: 0, 68 | unused_3: 0, 69 | }; 70 | 71 | // call ioctl 72 | 73 | unsafe { 74 | 75 | ioctl_defrag_range ( 76 | file_descriptor, 77 | & defrag_range_args as * const IoctlDefragRangeArgs, 78 | ) 79 | 80 | }.map_err ( 81 | |error| 82 | 83 | format! ( 84 | "Defragment IOCTL returned {}", 85 | error) 86 | 87 | ) ?; 88 | 89 | // return ok 90 | 91 | Ok (()) 92 | 93 | } 94 | 95 | // ex: noet ts=4 filetype=rust 96 | -------------------------------------------------------------------------------- /src/diskformat/core/key.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::fmt::Error as FmtError; 3 | use std::fmt::Formatter; 4 | 5 | #[ repr (C, packed) ] 6 | #[ derive (Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd) ] 7 | pub struct BtrfsKey { 8 | object_id: u64, 9 | item_type: u8, 10 | offset: u64, 11 | } 12 | 13 | impl BtrfsKey { 14 | 15 | pub fn new ( 16 | object_id: u64, 17 | item_type: u8, 18 | offset: u64, 19 | ) -> BtrfsKey { 20 | 21 | BtrfsKey { 22 | object_id: object_id, 23 | item_type: item_type, 24 | offset: offset, 25 | } 26 | 27 | } 28 | 29 | pub fn object_id (& self) -> u64 { 30 | self.object_id 31 | } 32 | 33 | pub fn item_type (& self) -> u8 { 34 | self.item_type 35 | } 36 | 37 | pub fn offset (& self) -> u64 { 38 | self.offset 39 | } 40 | 41 | pub fn to_string (& self) -> String { 42 | 43 | format! ( 44 | "{}/{} @ 0x{:x}", 45 | self.object_id, 46 | self.item_type, 47 | self.offset) 48 | 49 | } 50 | 51 | pub fn to_string_decimal (& self) -> String { 52 | 53 | format! ( 54 | "{}/{} @ {}", 55 | self.object_id, 56 | self.item_type, 57 | self.offset) 58 | 59 | } 60 | 61 | pub fn to_string_no_type (& self) -> String { 62 | 63 | format! ( 64 | "{} @ 0x{:x}", 65 | self.object_id, 66 | self.offset) 67 | 68 | } 69 | 70 | pub fn to_string_no_type_decimal (& self) -> String { 71 | 72 | format! ( 73 | "{} @ {}", 74 | self.object_id, 75 | self.offset) 76 | 77 | } 78 | 79 | } 80 | 81 | impl Debug for BtrfsKey { 82 | 83 | fn fmt ( 84 | & self, 85 | formatter: & mut Formatter, 86 | ) -> Result <(), FmtError> { 87 | 88 | formatter.write_fmt ( 89 | format_args! ( 90 | "BtrfsKey ({}/{} @ 0x{:x})", 91 | self.object_id, 92 | self.item_type, 93 | self.offset)) 94 | 95 | } 96 | 97 | } 98 | 99 | // ex: noet ts=4 filetype=rust 100 | -------------------------------------------------------------------------------- /src/diskformat/core/mmap_device_set.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::fs::File; 3 | use std::io::Seek; 4 | use std::io::SeekFrom; 5 | use std::path::PathBuf; 6 | 7 | use memmap::Mmap; 8 | use memmap::Protection; 9 | 10 | use super::super::*; 11 | 12 | pub struct BtrfsMmapDeviceSet { 13 | mmaps: Vec , 14 | } 15 | 16 | impl BtrfsMmapDeviceSet { 17 | 18 | pub fn open ( 19 | device_paths: & Vec , 20 | ) -> Result { 21 | 22 | // open devices 23 | 24 | let mut mmaps: Vec = 25 | Vec::new (); 26 | 27 | for device_path in device_paths.iter () { 28 | 29 | let mut file = 30 | File::open ( 31 | device_path, 32 | ).map_err ( 33 | |error| 34 | 35 | format! ( 36 | "Error opening {}: {}", 37 | device_path.to_string_lossy (), 38 | error.description ()) 39 | 40 | ) ?; 41 | 42 | let file_size = 43 | file.seek ( 44 | SeekFrom::End (0), 45 | ).map_err ( 46 | |error| 47 | 48 | format! ( 49 | "Error finding file size for {}: {}", 50 | device_path.to_string_lossy (), 51 | error.description ()) 52 | 53 | ) ?; 54 | 55 | let mmap = 56 | Mmap::open_with_offset ( 57 | & file, 58 | Protection::Read, 59 | 0, 60 | file_size as usize, 61 | ).map_err ( 62 | |error| 63 | 64 | format! ( 65 | "Error mmaping {}: {}", 66 | device_path.to_string_lossy (), 67 | error.description ()) 68 | 69 | ) ?; 70 | 71 | mmaps.push ( 72 | mmap); 73 | 74 | } 75 | 76 | Ok (BtrfsMmapDeviceSet { 77 | mmaps: mmaps, 78 | }) 79 | 80 | } 81 | 82 | pub fn devices <'a> ( 83 | & 'a self, 84 | ) -> Result , String> { 85 | 86 | BtrfsDeviceSet::new ( 87 | & self.mmaps.iter ().map ( 88 | |mmap| unsafe { mmap.as_slice () }, 89 | ).collect::> () 90 | ) 91 | 92 | } 93 | 94 | } 95 | 96 | // ex: noet ts=4 filetype=rust 97 | -------------------------------------------------------------------------------- /src/linux/types/file_descriptor.rs: -------------------------------------------------------------------------------- 1 | use linux::imports::*; 2 | 3 | // ---------- file descriptor with destructor 4 | 5 | pub struct FileDescriptor { 6 | value: libc::c_int, 7 | } 8 | 9 | impl FileDescriptor { 10 | 11 | pub fn open > ( 12 | path: AsPath, 13 | flags: libc::c_int, 14 | ) -> Result { 15 | 16 | let path = 17 | path.as_ref (); 18 | 19 | // TODO should be able to do this cleanly on linux... 20 | 21 | let path_string = 22 | try! ( 23 | 24 | path.to_str ().ok_or ( 25 | 26 | format! ( 27 | "Invalid characters in path: {}", 28 | path.to_string_lossy ()) 29 | 30 | ) 31 | 32 | ).to_owned (); 33 | 34 | let path_c = 35 | try! ( 36 | 37 | CString::new ( 38 | path_string.into_bytes (), 39 | ).map_err ( 40 | |_| 41 | 42 | format! ( 43 | "Invalid characters in path: {}", 44 | path.to_string_lossy ()) 45 | 46 | ) 47 | 48 | ); 49 | 50 | let fd = unsafe { 51 | 52 | libc::open ( 53 | path_c.as_ptr (), 54 | flags) 55 | 56 | }; 57 | 58 | if fd >= 0 { 59 | 60 | Ok ( 61 | FileDescriptor { 62 | value: fd, 63 | } 64 | ) 65 | 66 | } else { 67 | 68 | Err ( 69 | format! ( 70 | "error opening {:?}", 71 | path) 72 | ) 73 | 74 | } 75 | 76 | } 77 | 78 | pub fn get_value ( 79 | & self, 80 | ) -> libc::c_int { 81 | self.value 82 | } 83 | 84 | } 85 | 86 | impl Drop for FileDescriptor { 87 | 88 | fn drop ( 89 | & mut self, 90 | ) { 91 | 92 | unsafe { 93 | 94 | libc::close ( 95 | self.value); 96 | 97 | } 98 | 99 | } 100 | 101 | } 102 | 103 | impl <'a> From <& 'a FileDescriptor> for libc::c_int { 104 | 105 | fn from ( 106 | file_descriptor: & 'a FileDescriptor, 107 | ) -> libc::c_int { 108 | 109 | file_descriptor.value 110 | 111 | } 112 | 113 | } 114 | 115 | // ex: noet ts=4 filetype=rust 116 | -------------------------------------------------------------------------------- /src/diskformat/core/label.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::fmt::Debug; 3 | use std::fmt::Error as FmtError; 4 | use std::fmt::Formatter; 5 | use std::hash::Hash; 6 | use std::hash::Hasher; 7 | use std::str; 8 | use std::str::Utf8Error; 9 | 10 | pub const BTRFS_LABEL_BYTES: usize = 0x100; 11 | 12 | #[ repr (C, packed) ] 13 | #[ derive (Copy) ] 14 | pub struct BtrfsLabel { 15 | data: [u8; BTRFS_LABEL_BYTES], 16 | } 17 | 18 | impl BtrfsLabel { 19 | 20 | pub fn len ( 21 | & self, 22 | ) -> usize { 23 | 24 | self.data.iter ().position ( 25 | |byte| * byte == 0 26 | ).unwrap_or (self.data.len ()) 27 | 28 | } 29 | 30 | pub fn data ( 31 | & self, 32 | ) -> & [u8] { 33 | 34 | & self.data [0 .. self.len ()] 35 | 36 | } 37 | 38 | pub fn as_str ( 39 | & self, 40 | ) -> Result <& str, Utf8Error> { 41 | 42 | str::from_utf8 ( 43 | self.data ()) 44 | 45 | } 46 | 47 | pub fn to_string_lossy ( 48 | & self, 49 | ) -> Cow { 50 | 51 | String::from_utf8_lossy ( 52 | self.data ()) 53 | 54 | } 55 | 56 | } 57 | 58 | impl Clone for BtrfsLabel { 59 | 60 | fn clone (& self) -> BtrfsLabel { 61 | * self 62 | } 63 | 64 | } 65 | 66 | impl Debug for BtrfsLabel { 67 | 68 | fn fmt ( 69 | & self, 70 | formatter: & mut Formatter, 71 | ) -> Result <(), FmtError> { 72 | 73 | formatter.write_str ( 74 | & self.to_string_lossy (), 75 | ) ?; 76 | 77 | Ok (()) 78 | 79 | } 80 | 81 | } 82 | 83 | impl Eq for BtrfsLabel { 84 | } 85 | 86 | impl Hash for BtrfsLabel { 87 | 88 | fn hash ( 89 | & self, 90 | state: & mut State, 91 | ) { 92 | 93 | (& self.data [..]).hash ( 94 | state); 95 | 96 | } 97 | 98 | } 99 | 100 | impl PartialEq for BtrfsLabel { 101 | 102 | fn eq ( 103 | & self, 104 | other: & BtrfsLabel, 105 | ) -> bool { 106 | 107 | & self.data [..] == & other.data [..] 108 | 109 | } 110 | 111 | } 112 | 113 | // ex: noet ts=4 filetype=rust 114 | -------------------------------------------------------------------------------- /src/diskformat/core/uuid.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::fmt::Display; 3 | use std::fmt::Error as FmtError; 4 | use std::fmt::Formatter; 5 | 6 | use itertools::Itertools; 7 | 8 | pub const BTRFS_UUID_BYTES: usize = 0x10; 9 | 10 | #[ repr (C, packed) ] 11 | #[ derive (Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd) ] 12 | pub struct BtrfsUuid { 13 | bytes: [u8; BTRFS_UUID_BYTES], 14 | } 15 | 16 | impl BtrfsUuid { 17 | 18 | pub fn bytes (& self) -> & [u8] { 19 | & self.bytes 20 | } 21 | 22 | pub fn to_string (& self) -> String { 23 | 24 | format! ( 25 | "{:02x}-{:02x}-{:02x}-{:02x}-{:02x}", 26 | self.bytes [0x00 .. 0x04].iter ().format (""), 27 | self.bytes [0x04 .. 0x06].iter ().format (""), 28 | self.bytes [0x06 .. 0x08].iter ().format (""), 29 | self.bytes [0x08 .. 0x0a].iter ().format (""), 30 | self.bytes [0x0a .. 0x10].iter ().format ("")) 31 | 32 | } 33 | 34 | } 35 | 36 | impl Debug for BtrfsUuid { 37 | 38 | fn fmt ( 39 | & self, 40 | formatter: & mut Formatter, 41 | ) -> Result <(), FmtError> { 42 | 43 | formatter.write_fmt ( 44 | format_args! ( 45 | "BtrfsUuid ({:02x}-{:02x}-{:02x}-{:02x}-{:02x})", 46 | self.bytes [0x00 .. 0x04].iter ().format (""), 47 | self.bytes [0x04 .. 0x06].iter ().format (""), 48 | self.bytes [0x06 .. 0x08].iter ().format (""), 49 | self.bytes [0x08 .. 0x0a].iter ().format (""), 50 | self.bytes [0x0a .. 0x10].iter ().format (""))) 51 | 52 | } 53 | 54 | } 55 | 56 | impl Display for BtrfsUuid { 57 | 58 | fn fmt ( 59 | & self, 60 | formatter: & mut Formatter, 61 | ) -> Result <(), FmtError> { 62 | 63 | formatter.write_fmt ( 64 | format_args! ( 65 | "{:02x}-{:02x}-{:02x}-{:02x}-{:02x}", 66 | self.bytes [0x00 .. 0x04].iter ().format (""), 67 | self.bytes [0x04 .. 0x06].iter ().format (""), 68 | self.bytes [0x06 .. 0x08].iter ().format (""), 69 | self.bytes [0x08 .. 0x0a].iter ().format (""), 70 | self.bytes [0x0a .. 0x10].iter ().format (""))) 71 | 72 | } 73 | 74 | } 75 | 76 | // ex: noet ts=4 filetype=rust 77 | -------------------------------------------------------------------------------- /src/diskformat/item/mod.rs: -------------------------------------------------------------------------------- 1 | mod chunk_item; 2 | mod chunk_item_stripe_data; 3 | mod chunk_item_data; 4 | mod dir_index; 5 | mod dir_item; 6 | mod dir_item_data; 7 | mod dir_item_entry; 8 | mod extent_constants; 9 | mod extent_data; 10 | mod extent_data_constants; 11 | mod extent_data_data; 12 | mod extent_data_ref_data; 13 | mod extent_inline_ref_data; 14 | mod extent_item; 15 | mod extent_item_data; 16 | mod inode_item; 17 | mod inode_item_data; 18 | mod inode_ref; 19 | mod inode_ref_data; 20 | mod inode_ref_entry; 21 | mod internal_item; 22 | mod invalid_item; 23 | mod leaf_item; 24 | mod leaf_item_constants; 25 | mod leaf_item_contents; 26 | mod leaf_item_header; 27 | mod root_backref; 28 | mod root_item; 29 | mod root_item_data; 30 | mod root_ref; 31 | mod root_ref_data; 32 | mod tree_block_info_data; 33 | mod unknown_item; 34 | 35 | pub use self::chunk_item::*; 36 | pub use self::chunk_item_data::*; 37 | pub use self::chunk_item_stripe_data::*; 38 | pub use self::dir_index::*; 39 | pub use self::dir_item::*; 40 | pub use self::dir_item_data::*; 41 | pub use self::dir_item_entry::*; 42 | pub use self::extent_constants::*; 43 | pub use self::extent_data::*; 44 | pub use self::extent_data_constants::*; 45 | pub use self::extent_data_data::*; 46 | pub use self::extent_data_ref_data::*; 47 | pub use self::extent_inline_ref_data::*; 48 | pub use self::extent_item::*; 49 | pub use self::extent_item_data::*; 50 | pub use self::inode_item::*; 51 | pub use self::inode_item_data::*; 52 | pub use self::inode_ref::*; 53 | pub use self::inode_ref_data::*; 54 | pub use self::inode_ref_entry::*; 55 | pub use self::internal_item::*; 56 | pub use self::invalid_item::*; 57 | pub use self::leaf_item::*; 58 | pub use self::leaf_item_constants::*; 59 | pub use self::leaf_item_contents::*; 60 | pub use self::leaf_item_header::*; 61 | pub use self::root_backref::*; 62 | pub use self::root_item::*; 63 | pub use self::root_item_data::*; 64 | pub use self::root_ref::*; 65 | pub use self::root_ref_data::*; 66 | pub use self::tree_block_info_data::*; 67 | pub use self::unknown_item::*; 68 | 69 | // ex: noet ts=4 filetype=rust 70 | -------------------------------------------------------------------------------- /src/diskformat/tree/filesystem_tree.rs: -------------------------------------------------------------------------------- 1 | use super::super::prelude::*; 2 | 3 | pub struct BtrfsFilesystemTree <'a> { 4 | tree_items: BTreeMap >, 5 | } 6 | 7 | impl <'a> BtrfsFilesystemTree <'a> { 8 | 9 | tree_item_accessor! ( 10 | dir_index, 11 | BtrfsDirIndex, 12 | BTRFS_DIR_INDEX_TYPE, 13 | DirIndex, 14 | ); 15 | 16 | tree_item_range_accessor! ( 17 | dir_indexes, 18 | BtrfsDirIndex, 19 | BTRFS_DIR_INDEX_TYPE, 20 | DirIndex, 21 | ); 22 | 23 | tree_item_accessor! ( 24 | dir_item, 25 | BtrfsDirItem, 26 | BTRFS_DIR_ITEM_TYPE, 27 | DirItem, 28 | ); 29 | 30 | pub fn dir_item_entry ( 31 | & 'a self, 32 | object_id: u64, 33 | name: & [u8], 34 | ) -> Option > { 35 | 36 | self.dir_item ( 37 | object_id, 38 | btrfs_crc32_linux (name) as u64, 39 | ).and_then ( 40 | |dir_item| 41 | 42 | dir_item.entries ().find ( 43 | |dir_item_entry| 44 | 45 | dir_item_entry.name () == name 46 | 47 | ) 48 | 49 | ) 50 | 51 | } 52 | 53 | tree_item_range_accessor! ( 54 | dir_items, 55 | BtrfsDirItem, 56 | BTRFS_DIR_ITEM_TYPE, 57 | DirItem, 58 | ); 59 | 60 | tree_item_range_accessor! ( 61 | extent_datas, 62 | BtrfsExtentData, 63 | BTRFS_EXTENT_DATA_TYPE, 64 | ExtentData, 65 | ); 66 | 67 | tree_item_accessor! ( 68 | inode_item, 69 | BtrfsInodeItem, 70 | BTRFS_INODE_ITEM_TYPE, 71 | InodeItem, 72 | 0, 73 | ); 74 | 75 | tree_item_range_accessor! ( 76 | inode_refs, 77 | BtrfsInodeRef, 78 | BTRFS_INODE_REF_TYPE, 79 | InodeRef, 80 | ); 81 | 82 | } 83 | 84 | impl <'a> BtrfsTree <'a> for BtrfsFilesystemTree <'a> { 85 | 86 | fn new ( 87 | tree_items: BTreeMap >, 88 | ) -> BtrfsFilesystemTree { 89 | 90 | BtrfsFilesystemTree { 91 | tree_items: tree_items, 92 | } 93 | 94 | } 95 | 96 | fn tree_items ( 97 | & 'a self, 98 | ) -> & 'a BTreeMap > { 99 | & self.tree_items 100 | } 101 | 102 | } 103 | 104 | // ex: noet ts=4 filetype=rust 105 | -------------------------------------------------------------------------------- /src/diskformat/node/header.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::fmt::DebugStruct; 3 | use std::fmt::Error as FmtError; 4 | use std::fmt::Formatter; 5 | 6 | use super::super::*; 7 | 8 | #[ repr (C, packed) ] 9 | #[ derive (Copy, Clone, Eq, Hash, PartialEq) ] 10 | pub struct BtrfsNodeHeader { 11 | pub checksum: BtrfsChecksum, 12 | pub fs_uuid: BtrfsUuid, 13 | pub logical_address: u64, 14 | pub flags_and_backref: u64, 15 | pub chunk_tree_uuid: BtrfsUuid, 16 | pub generation: u64, 17 | pub tree_id: BtrfsTreeId, 18 | pub num_items: u32, 19 | pub level: u8, 20 | } 21 | 22 | impl BtrfsNodeHeader { 23 | 24 | pub fn checksum (& self) -> BtrfsChecksum { 25 | self.checksum 26 | } 27 | 28 | pub fn fs_uuid (& self) -> BtrfsUuid { 29 | self.fs_uuid 30 | } 31 | 32 | pub fn generation (& self) -> u64 { 33 | self.generation 34 | } 35 | 36 | pub fn tree_id (& self) -> BtrfsTreeId { 37 | self.tree_id 38 | } 39 | 40 | pub fn num_items (& self) -> u32 { 41 | self.num_items 42 | } 43 | 44 | pub fn level (& self) -> u8 { 45 | self.level 46 | } 47 | 48 | pub fn debug_struct ( 49 | & self, 50 | debug_struct: & mut DebugStruct, 51 | ) { 52 | 53 | debug_struct.field ( 54 | "checksum", 55 | & NakedString::from ( 56 | self.checksum.to_string ())); 57 | 58 | debug_struct.field ( 59 | "fs_uuid", 60 | & NakedString::from ( 61 | self.fs_uuid.to_string ())); 62 | 63 | debug_struct.field ( 64 | "generation", 65 | & self.generation); 66 | 67 | debug_struct.field ( 68 | "tree_id", 69 | & NakedString::from ( 70 | self.tree_id.to_string ())); 71 | 72 | debug_struct.field ( 73 | "num_items", 74 | & self.num_items); 75 | 76 | debug_struct.field ( 77 | "level", 78 | & self.level); 79 | 80 | } 81 | 82 | } 83 | 84 | impl Debug for BtrfsNodeHeader { 85 | 86 | fn fmt ( 87 | & self, 88 | formatter: & mut Formatter, 89 | ) -> Result <(), FmtError> { 90 | 91 | let mut debug_struct = 92 | formatter.debug_struct ( 93 | "BtrfsNodeHeader"); 94 | 95 | self.debug_struct ( 96 | & mut debug_struct); 97 | 98 | debug_struct.finish () 99 | 100 | } 101 | 102 | } 103 | 104 | // ex: noet ts=4 filetype=rust 105 | -------------------------------------------------------------------------------- /src/diskformat/node/leaf_node.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::fmt::Error as FmtError; 3 | use std::fmt::Formatter; 4 | use std::mem; 5 | 6 | use diskformat::*; 7 | 8 | #[ derive (Clone, Copy, Eq, Hash, PartialEq) ] 9 | pub struct BtrfsLeafNode <'a> { 10 | physical_address: BtrfsPhysicalAddress, 11 | bytes: & 'a [u8], 12 | } 13 | 14 | impl <'a> BtrfsLeafNode <'a> { 15 | 16 | pub fn new ( 17 | physical_address: BtrfsPhysicalAddress, 18 | bytes: & 'a [u8], 19 | ) -> BtrfsLeafNode <'a> { 20 | 21 | BtrfsLeafNode { 22 | physical_address: physical_address, 23 | bytes: bytes, 24 | } 25 | 26 | } 27 | 28 | pub fn header ( 29 | & 'a self, 30 | ) -> & 'a BtrfsNodeHeader { 31 | 32 | unsafe { 33 | & * (self.bytes.as_ptr () as * const BtrfsNodeHeader) 34 | } 35 | 36 | } 37 | 38 | pub fn is_leaf ( 39 | & self, 40 | ) -> bool { 41 | self.header ().level () == 0 42 | } 43 | 44 | pub fn items ( 45 | self, 46 | ) -> BtrfsLeafNodeItems <'a> { 47 | 48 | BtrfsLeafNodeItems::new ( 49 | & self.bytes [mem::size_of:: () ..], 50 | self.num_items (), 51 | ) 52 | 53 | } 54 | 55 | pub fn physical_address (& self) -> BtrfsPhysicalAddress { 56 | self.physical_address 57 | } 58 | 59 | pub fn checksum (& self) -> BtrfsChecksum { 60 | self.header ().checksum () 61 | } 62 | 63 | pub fn fs_uuid (& self) -> BtrfsUuid { 64 | self.header ().fs_uuid () 65 | } 66 | 67 | pub fn tree_id (& self) -> BtrfsTreeId { 68 | self.header ().tree_id () 69 | } 70 | 71 | pub fn num_items (& self) -> u32 { 72 | self.header ().num_items () 73 | } 74 | 75 | pub fn level (& self) -> u8 { 76 | self.header ().level () 77 | } 78 | 79 | } 80 | 81 | impl <'a> Debug for BtrfsLeafNode <'a> { 82 | 83 | fn fmt ( 84 | & self, 85 | formatter: & mut Formatter, 86 | ) -> Result <(), FmtError> { 87 | 88 | let mut debug_struct = 89 | formatter.debug_struct ( 90 | "BtrfsLeafNode"); 91 | 92 | self.header ().debug_struct ( 93 | & mut debug_struct); 94 | 95 | debug_struct.field ( 96 | "items", 97 | & NakedString::from ( 98 | "...")); 99 | 100 | debug_struct.finish () 101 | 102 | } 103 | 104 | } 105 | 106 | // ex: noet ts=4 filetype=rust 107 | -------------------------------------------------------------------------------- /src/diskformat/core/superblock_system_chunks.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::mem; 3 | 4 | use super::super::*; 5 | 6 | pub struct BtrfsSuperblockSystemChunks <'a> { 7 | address: * const u8, 8 | end_address: * const u8, 9 | phantom: PhantomData <& 'a BtrfsSuperblockData>, 10 | } 11 | 12 | impl <'a> BtrfsSuperblockSystemChunks <'a> { 13 | 14 | pub fn new ( 15 | superblock_data: & 'a BtrfsSuperblockData, 16 | ) -> BtrfsSuperblockSystemChunks <'a> { 17 | 18 | let start_address = 19 | & superblock_data.system_chunks.data [0] as * const u8; 20 | 21 | let end_address = 22 | unsafe { 23 | 24 | start_address.offset ( 25 | superblock_data.system_chunks_size as isize) 26 | 27 | }; 28 | 29 | BtrfsSuperblockSystemChunks { 30 | address: start_address, 31 | end_address: end_address, 32 | phantom: PhantomData, 33 | } 34 | 35 | } 36 | 37 | } 38 | 39 | impl <'a> Iterator for BtrfsSuperblockSystemChunks <'a> { 40 | 41 | type Item = BtrfsSuperblockChunkItem <'a>; 42 | 43 | fn next ( 44 | & mut self, 45 | ) -> Option > { 46 | 47 | if self.address < self.end_address { 48 | 49 | // get key 50 | 51 | let chunk_item_key = unsafe { 52 | & * ( 53 | self.address 54 | as * const BtrfsKey 55 | ) 56 | }; 57 | 58 | self.address = unsafe { 59 | self.address.offset ( 60 | mem::size_of:: () as isize, 61 | ) 62 | }; 63 | 64 | // get data 65 | 66 | let chunk_item_data = unsafe { 67 | & * ( 68 | self.address 69 | as * const BtrfsChunkItemData 70 | ) 71 | }; 72 | 73 | self.address = unsafe { 74 | self.address.offset ( 75 | mem::size_of:: () as isize, 76 | ) 77 | }; 78 | 79 | // skip chunk item stripes 80 | 81 | self.address = unsafe { 82 | self.address.offset ( 83 | mem::size_of:: () as isize 84 | * chunk_item_data.num_stripes () as isize, 85 | ) 86 | }; 87 | 88 | // return 89 | 90 | Some ( 91 | BtrfsSuperblockChunkItem::new ( 92 | chunk_item_key, 93 | chunk_item_data, 94 | ).expect ("Invalid chunk item") 95 | ) 96 | 97 | } else { 98 | 99 | None 100 | 101 | } 102 | 103 | } 104 | 105 | } 106 | 107 | // ex: noet ts=4 filetype=rust 108 | -------------------------------------------------------------------------------- /src/diskformat/item/extent_item.rs: -------------------------------------------------------------------------------- 1 | use super::super::prelude::*; 2 | 3 | #[ derive (Copy, Clone, Debug, Eq, Hash, PartialEq) ] 4 | pub struct BtrfsExtentItem <'a> { 5 | header: & 'a BtrfsLeafItemHeader, 6 | data_bytes: & 'a [u8], 7 | } 8 | 9 | impl <'a> BtrfsExtentItem <'a> { 10 | 11 | pub fn from_bytes ( 12 | header: & 'a BtrfsLeafItemHeader, 13 | data_bytes: & 'a [u8], 14 | ) -> Result , String> { 15 | 16 | // create extent item 17 | 18 | let extent_item = 19 | BtrfsExtentItem { 20 | header: header, 21 | data_bytes: data_bytes, 22 | }; 23 | 24 | // sanity check 25 | 26 | if data_bytes.len () != mem::size_of:: () { 27 | 28 | return Err ( 29 | format! ( 30 | "Must be exactly 0x{:x} bytes", 31 | mem::size_of:: ())); 32 | 33 | } 34 | 35 | // return 36 | 37 | Ok (extent_item) 38 | 39 | } 40 | 41 | pub fn offset (& self) -> u64 { 42 | self.header.key ().offset () 43 | } 44 | 45 | pub fn data (& self) -> & BtrfsExtentItemData { 46 | 47 | unsafe { 48 | & * ( 49 | self.data_bytes.as_ptr () 50 | as * const BtrfsExtentItemData 51 | ) 52 | } 53 | 54 | } 55 | 56 | pub fn reference_count (& self) -> u64 { 57 | self.data ().reference_count 58 | } 59 | 60 | pub fn generation (& self) -> u64 { 61 | self.data ().generation 62 | } 63 | 64 | pub fn flags (& self) -> u64 { 65 | self.data ().flags 66 | } 67 | 68 | pub fn is_data (& self) -> bool { 69 | self.data ().flags & BTRFS_EXTENT_FLAG_DATA != 0 70 | } 71 | 72 | pub fn is_tree_block (& self) -> bool { 73 | self.data ().flags & BTRFS_EXTENT_FLAG_TREE_BLOCK != 0 74 | } 75 | 76 | pub fn is_full_backref (& self) -> bool { 77 | self.data ().flags & BTRFS_EXTENT_FLAG_FULL_BACKREF != 0 78 | } 79 | 80 | pub fn tree_block_info (& self) -> & BtrfsTreeBlockInfoData { 81 | 82 | if ! self.is_tree_block () { 83 | panic! (); 84 | } 85 | 86 | unsafe { 87 | mem::transmute ( 88 | & self.data_bytes [ 89 | mem::size_of:: ()]) 90 | } 91 | 92 | } 93 | 94 | } 95 | 96 | impl <'a> BtrfsLeafItemContents <'a> for BtrfsExtentItem <'a> { 97 | 98 | fn header (& self) -> & BtrfsLeafItemHeader { 99 | self.header 100 | } 101 | 102 | } 103 | 104 | // ex: noet ts=4 filetype=rust 105 | -------------------------------------------------------------------------------- /src/diskformat/item/leaf_item_header.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::fmt::DebugStruct; 3 | use std::fmt::Error as FmtError; 4 | use std::fmt::Formatter; 5 | use std::mem; 6 | 7 | use diskformat::*; 8 | 9 | #[ repr (C, packed) ] 10 | #[ derive (Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd) ] 11 | pub struct BtrfsLeafItemHeader { 12 | key: BtrfsKey, 13 | data_offset: u32, 14 | data_size: u32, 15 | } 16 | 17 | impl BtrfsLeafItemHeader { 18 | 19 | pub fn from_bytes ( 20 | bytes: & [u8], 21 | ) -> Result <& BtrfsLeafItemHeader, String> { 22 | 23 | // sanity check 24 | 25 | if bytes.len () != mem::size_of:: () { 26 | 27 | return Err ( 28 | format! ( 29 | "Must be exactly 0x{:x} bytes", 30 | mem::size_of:: ())); 31 | 32 | } 33 | 34 | // cast 35 | 36 | Ok ( 37 | unsafe { 38 | & * (bytes.as_ptr () as * const BtrfsLeafItemHeader) 39 | } 40 | ) 41 | 42 | } 43 | 44 | pub fn key (& self) -> BtrfsKey { 45 | self.key 46 | } 47 | 48 | pub fn object_id (& self) -> u64 { 49 | self.key.object_id () 50 | } 51 | 52 | pub fn item_type (& self) -> u8 { 53 | self.key.item_type () 54 | } 55 | 56 | pub fn offset (& self) -> u64 { 57 | self.key.offset () 58 | } 59 | 60 | pub fn data_offset (& self) -> u32 { 61 | self.data_offset 62 | } 63 | 64 | pub fn data_size (& self) -> u32 { 65 | self.data_size 66 | } 67 | 68 | pub fn debug_struct ( 69 | & self, 70 | debug_struct: & mut DebugStruct, 71 | ) { 72 | 73 | debug_struct.field ( 74 | "key", 75 | & NakedString::from ( 76 | self.key ().to_string ())); 77 | 78 | debug_struct.field ( 79 | "data_offset", 80 | & NakedString::from ( 81 | format! ( 82 | "0x{:x}", 83 | self.data_offset ()))); 84 | 85 | debug_struct.field ( 86 | "data_size", 87 | & self.data_size ()); 88 | 89 | } 90 | 91 | } 92 | 93 | impl Debug for BtrfsLeafItemHeader { 94 | 95 | fn fmt ( 96 | & self, 97 | formatter: & mut Formatter, 98 | ) -> Result <(), FmtError> { 99 | 100 | let mut debug_struct = 101 | formatter.debug_struct ( 102 | "BtrfsLeafItemHeader"); 103 | 104 | self.debug_struct ( 105 | & mut debug_struct); 106 | 107 | debug_struct.finish () 108 | 109 | } 110 | 111 | } 112 | 113 | // ex: noet ts=4 filetype=rust 114 | -------------------------------------------------------------------------------- /src/diskformat/node/internal_node.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::fmt::Error as FmtError; 3 | use std::fmt::Formatter; 4 | use std::mem; 5 | use std::slice; 6 | 7 | use super::super::*; 8 | 9 | #[ derive (Clone, Copy, Eq, Hash, PartialEq) ] 10 | pub struct BtrfsInternalNode <'a> { 11 | physical_address: BtrfsPhysicalAddress, 12 | bytes: & 'a [u8], 13 | } 14 | 15 | impl <'a> BtrfsInternalNode <'a> { 16 | 17 | pub fn new ( 18 | physical_address: BtrfsPhysicalAddress, 19 | bytes: & 'a [u8], 20 | ) -> Result , String> { 21 | 22 | Ok (BtrfsInternalNode { 23 | physical_address: physical_address, 24 | bytes: bytes, 25 | }) 26 | 27 | } 28 | 29 | pub fn header ( 30 | & 'a self, 31 | ) -> & 'a BtrfsNodeHeader { 32 | 33 | unsafe { 34 | & * (self.bytes.as_ptr () as * const BtrfsNodeHeader) 35 | } 36 | 37 | } 38 | 39 | pub fn is_leaf ( 40 | & self, 41 | ) -> bool { 42 | false 43 | } 44 | 45 | pub fn items ( 46 | & self, 47 | ) -> & 'a [BtrfsInternalItem] { 48 | 49 | let start_address = 50 | & self.bytes [mem::size_of:: ()] 51 | as * const u8 52 | as * const BtrfsInternalItem; 53 | 54 | unsafe { 55 | 56 | slice::from_raw_parts ( 57 | start_address, 58 | self.header ().num_items () as usize) 59 | 60 | } 61 | 62 | } 63 | 64 | pub fn physical_address (& self) -> BtrfsPhysicalAddress { 65 | self.physical_address 66 | } 67 | 68 | pub fn checksum (& self) -> BtrfsChecksum { 69 | self.header ().checksum () 70 | } 71 | 72 | pub fn fs_uuid (& self) -> BtrfsUuid { 73 | self.header ().fs_uuid () 74 | } 75 | 76 | pub fn tree_id (& self) -> BtrfsTreeId { 77 | self.header ().tree_id () 78 | } 79 | 80 | pub fn num_items (& self) -> u32 { 81 | self.header ().num_items () 82 | } 83 | 84 | pub fn level (& self) -> u8 { 85 | self.header ().level () 86 | } 87 | 88 | } 89 | 90 | impl <'a> Debug for BtrfsInternalNode <'a> { 91 | 92 | fn fmt ( 93 | & self, 94 | formatter: & mut Formatter, 95 | ) -> Result <(), FmtError> { 96 | 97 | let mut debug_struct = 98 | formatter.debug_struct ( 99 | "BtrfsInternalNode"); 100 | 101 | self.header ().debug_struct ( 102 | & mut debug_struct); 103 | 104 | debug_struct.field ( 105 | "items", 106 | & NakedString::from ( 107 | "...")); 108 | 109 | debug_struct.finish () 110 | 111 | } 112 | 113 | } 114 | 115 | // ex: noet ts=4 filetype=rust 116 | -------------------------------------------------------------------------------- /src/diskformat/core/checksum.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::fmt::Display; 3 | use std::fmt::Error as FmtError; 4 | use std::fmt::Formatter; 5 | 6 | use crc::crc32; 7 | 8 | use itertools::Itertools; 9 | 10 | pub const BTRFS_CHECKSUM_BYTES: usize = 0x20; 11 | 12 | #[ repr (C, packed) ] 13 | #[ derive (Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd) ] 14 | pub struct BtrfsChecksum { 15 | bytes: [u8; BTRFS_CHECKSUM_BYTES], 16 | } 17 | 18 | pub fn btrfs_crc32c ( 19 | bytes: & [u8], 20 | ) -> u32 { 21 | 22 | crc32::checksum_castagnoli ( 23 | bytes) 24 | 25 | } 26 | 27 | pub fn btrfs_crc32_linux ( 28 | bytes: & [u8], 29 | ) -> u32 { 30 | 31 | ! crc32::update ( 32 | 1, 33 | & crc32::CASTAGNOLI_TABLE, 34 | bytes) 35 | 36 | } 37 | 38 | impl BtrfsChecksum { 39 | 40 | pub fn bytes (& self) -> & [u8] { 41 | & self.bytes 42 | } 43 | 44 | pub fn bytes_truncated (& self) -> & [u8] { 45 | 46 | if self.bytes.iter ().skip (4).all ( 47 | |byte| * byte == 0 48 | ) { 49 | 50 | & self.bytes [0 .. 4] 51 | 52 | } else { 53 | 54 | & self.bytes 55 | 56 | } 57 | 58 | } 59 | 60 | pub fn to_string ( 61 | & self, 62 | ) -> String { 63 | 64 | format! ( 65 | "{:02x}", 66 | self.bytes_truncated ().iter ().format ("")) 67 | 68 | } 69 | 70 | } 71 | 72 | impl Debug for BtrfsChecksum { 73 | 74 | fn fmt ( 75 | & self, 76 | formatter: & mut Formatter, 77 | ) -> Result <(), FmtError> { 78 | 79 | formatter.write_fmt ( 80 | format_args! ( 81 | "BtrfsChecksum ({})", 82 | self.to_string ())) 83 | 84 | } 85 | 86 | } 87 | 88 | impl Display for BtrfsChecksum { 89 | 90 | fn fmt ( 91 | & self, 92 | formatter: & mut Formatter, 93 | ) -> Result <(), FmtError> { 94 | 95 | formatter.write_fmt ( 96 | format_args! ( 97 | "{}", 98 | self.to_string ())) 99 | 100 | } 101 | 102 | } 103 | 104 | impl From for BtrfsChecksum { 105 | 106 | fn from ( 107 | value: u32, 108 | ) -> BtrfsChecksum { 109 | 110 | BtrfsChecksum { 111 | bytes: [ 112 | ((value & 0x000000ff) >> 0) as u8, 113 | ((value & 0x0000ff00) >> 8) as u8, 114 | ((value & 0x00ff0000) >> 16) as u8, 115 | ((value & 0xff000000) >> 24) as u8, 116 | 0, 0, 0, 0, 117 | 0, 0, 0, 0, 118 | 0, 0, 0, 0, 119 | 0, 0, 0, 0, 120 | 0, 0, 0, 0, 121 | 0, 0, 0, 0, 122 | 0, 0, 0, 0, 123 | ] 124 | } 125 | 126 | } 127 | 128 | } 129 | 130 | // ex: noet ts=4 filetype=rust 131 | -------------------------------------------------------------------------------- /src/diskformat/node/node.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::fmt::Error as FmtError; 3 | use std::fmt::Formatter; 4 | 5 | use super::super::*; 6 | 7 | #[ derive (Clone, Eq, Hash, PartialEq) ] 8 | pub enum BtrfsNode <'a> { 9 | Internal (BtrfsInternalNode <'a>), 10 | Leaf (BtrfsLeafNode <'a>), 11 | } 12 | 13 | impl <'a> BtrfsNode <'a> { 14 | 15 | pub fn from_bytes ( 16 | physical_address: BtrfsPhysicalAddress, 17 | bytes: & 'a [u8], 18 | ) -> Result , String> { 19 | 20 | // verify checksum 21 | 22 | let calculated_checksum = 23 | BtrfsChecksum::from ( 24 | btrfs_crc32c ( 25 | & bytes [0x20 .. bytes.len ()])); 26 | 27 | let header = unsafe { 28 | & * (bytes.as_ptr () as * const BtrfsNodeHeader) 29 | }; 30 | 31 | if header.checksum () != calculated_checksum { 32 | 33 | return Err ( 34 | "Checksum mismatch".to_owned ()); 35 | 36 | } 37 | 38 | // construct 39 | 40 | if header.level () == 0 { 41 | 42 | Ok ( 43 | BtrfsNode::Leaf ( 44 | BtrfsLeafNode::new ( 45 | physical_address, 46 | bytes, 47 | ) 48 | ) 49 | ) 50 | 51 | } else { 52 | 53 | Ok ( 54 | BtrfsNode::Internal ( 55 | BtrfsInternalNode::new ( 56 | physical_address, 57 | bytes, 58 | ) ? 59 | ) 60 | ) 61 | 62 | } 63 | 64 | } 65 | 66 | pub fn physical_address ( 67 | & self, 68 | ) -> BtrfsPhysicalAddress { 69 | 70 | match self { 71 | 72 | & BtrfsNode::Internal (ref node) => 73 | node.physical_address (), 74 | 75 | & BtrfsNode::Leaf (ref node) => 76 | node.physical_address (), 77 | 78 | } 79 | 80 | } 81 | 82 | pub fn header ( 83 | & self, 84 | ) -> & BtrfsNodeHeader { 85 | 86 | match self { 87 | 88 | & BtrfsNode::Internal (ref node) => 89 | node.header (), 90 | 91 | & BtrfsNode::Leaf (ref node) => 92 | node.header (), 93 | 94 | } 95 | 96 | } 97 | 98 | pub fn tree_id (& self) -> BtrfsTreeId { 99 | self.header ().tree_id () 100 | } 101 | 102 | pub fn generation (& self) -> u64 { 103 | self.header ().generation () 104 | } 105 | 106 | } 107 | 108 | impl <'a> Debug for BtrfsNode <'a> { 109 | 110 | fn fmt ( 111 | & self, 112 | formatter: & mut Formatter, 113 | ) -> Result <(), FmtError> { 114 | 115 | match self { 116 | 117 | & BtrfsNode::Internal (internal_node) => 118 | formatter.write_fmt ( 119 | format_args! ( 120 | "{:?}", 121 | internal_node)), 122 | 123 | & BtrfsNode::Leaf (leaf_node) => 124 | formatter.write_fmt ( 125 | format_args! ( 126 | "{:?}", 127 | leaf_node)), 128 | 129 | } 130 | 131 | } 132 | 133 | } 134 | 135 | // ex: noet ts=4 filetype=rust 136 | -------------------------------------------------------------------------------- /src/diskformat/item/root_item.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::fmt::Error as FmtError; 3 | use std::fmt::Formatter; 4 | use std::mem; 5 | 6 | use super::super::*; 7 | 8 | #[ derive (Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd) ] 9 | pub struct BtrfsRootItem <'a> { 10 | header: & 'a BtrfsLeafItemHeader, 11 | data_bytes: & 'a [u8], 12 | } 13 | 14 | impl <'a> BtrfsRootItem <'a> { 15 | 16 | pub fn from_bytes ( 17 | header: & 'a BtrfsLeafItemHeader, 18 | data_bytes: & 'a [u8], 19 | ) -> Result , String> { 20 | 21 | // sanity check 22 | 23 | if data_bytes.len () != mem::size_of:: () { 24 | 25 | return Err ( 26 | format! ( 27 | "Must be at least 0x{:x} bytes", 28 | mem::size_of:: ())); 29 | 30 | } 31 | 32 | // create root item 33 | 34 | let root_item = BtrfsRootItem { 35 | header: header, 36 | data_bytes: data_bytes, 37 | }; 38 | 39 | // return 40 | 41 | Ok (root_item) 42 | 43 | } 44 | 45 | pub fn data (& self) -> & BtrfsRootItemData { 46 | 47 | unsafe { 48 | & * ( 49 | self.data_bytes.as_ptr () 50 | as * const BtrfsRootItemData 51 | ) 52 | } 53 | 54 | } 55 | 56 | pub fn inode_item (& self) -> & BtrfsInodeItemData { 57 | & self.data ().inode_item 58 | } 59 | 60 | pub fn expected_generation (& self) -> u64 { 61 | self.data ().expected_generation 62 | } 63 | 64 | pub fn root_object_id (& self) -> u64 { 65 | self.data ().root_object_id 66 | } 67 | 68 | pub fn root_node_block_number (& self) -> u64 { 69 | self.data ().root_node_block_number 70 | } 71 | 72 | pub fn byte_limit (& self) -> u64 { 73 | self.data ().byte_limit 74 | } 75 | 76 | pub fn bytes_used (& self) -> u64 { 77 | self.data ().bytes_used 78 | } 79 | 80 | pub fn last_snapshot_generation (& self) -> u64 { 81 | self.data ().last_snapshot_generation 82 | } 83 | 84 | pub fn flags (& self) -> u64 { 85 | self.data ().flags 86 | } 87 | 88 | pub fn num_references (& self) -> u32 { 89 | self.data ().num_references 90 | } 91 | 92 | pub fn drop_progress (& self) -> BtrfsKey { 93 | self.data ().drop_progress 94 | } 95 | 96 | pub fn drop_level (& self) -> u8 { 97 | self.data ().drop_level 98 | } 99 | 100 | pub fn tree_level (& self) -> u8 { 101 | self.data ().tree_level 102 | } 103 | 104 | } 105 | 106 | impl <'a> BtrfsLeafItemContents <'a> for BtrfsRootItem <'a> { 107 | 108 | fn header (& self) -> & BtrfsLeafItemHeader { 109 | self.header 110 | } 111 | 112 | } 113 | 114 | impl <'a> Debug for BtrfsRootItem <'a> { 115 | 116 | fn fmt ( 117 | & self, 118 | formatter: & mut Formatter, 119 | ) -> Result <(), FmtError> { 120 | 121 | let mut debug_struct = 122 | formatter.debug_struct ( 123 | "BtrfsRootItem"); 124 | 125 | self.header.debug_struct ( 126 | & mut debug_struct); 127 | 128 | self.data ().debug_struct ( 129 | & mut debug_struct); 130 | 131 | debug_struct.finish () 132 | 133 | } 134 | 135 | } 136 | 137 | // ex: noet ts=4 filetype=rust 138 | -------------------------------------------------------------------------------- /src/diskformat/item/inode_item_data.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::fmt::DebugStruct; 3 | use std::fmt::Error as FmtError; 4 | use std::fmt::Formatter; 5 | 6 | use super::super::*; 7 | 8 | #[ repr (C, packed) ] 9 | #[ derive (Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd) ] 10 | pub struct BtrfsInodeItemData { 11 | pub generation: u64, 12 | pub transaction_id: u64, 13 | pub st_size: u64, 14 | pub st_blocks: u64, 15 | pub block_group: u64, 16 | pub st_nlink: u32, 17 | pub st_uid: u32, 18 | pub st_gid: u32, 19 | pub st_mode: u32, 20 | pub st_rdev: u64, 21 | pub flags: u64, 22 | pub sequence: u64, 23 | pub reserved: [u8; 0x20], 24 | pub st_atime: BtrfsTimestamp, 25 | pub st_ctime: BtrfsTimestamp, 26 | pub st_mtime: BtrfsTimestamp, 27 | pub st_otime: BtrfsTimestamp, 28 | } 29 | 30 | impl BtrfsInodeItemData { 31 | 32 | pub fn debug_struct ( 33 | & self, 34 | debug_struct: & mut DebugStruct, 35 | ) { 36 | 37 | debug_struct.field ( 38 | "generation", 39 | & self.generation); 40 | 41 | debug_struct.field ( 42 | "transaction_id", 43 | & self.transaction_id); 44 | 45 | debug_struct.field ( 46 | "st_size", 47 | & self.st_size); 48 | 49 | debug_struct.field ( 50 | "st_blocks", 51 | & self.st_blocks); 52 | 53 | debug_struct.field ( 54 | "block_group", 55 | & self.block_group); 56 | 57 | debug_struct.field ( 58 | "st_nlink", 59 | & self.st_nlink); 60 | 61 | debug_struct.field ( 62 | "st_uid", 63 | & self.st_uid); 64 | 65 | debug_struct.field ( 66 | "st_gid", 67 | & self.st_gid); 68 | 69 | debug_struct.field ( 70 | "st_mode", 71 | & NakedString::from ( 72 | format! ( 73 | "0o{:5o}", 74 | self.st_mode))); 75 | 76 | debug_struct.field ( 77 | "st_rdev", 78 | & self.st_rdev); 79 | 80 | debug_struct.field ( 81 | "flags", 82 | & self.flags); 83 | 84 | debug_struct.field ( 85 | "sequence", 86 | & self.sequence); 87 | 88 | debug_struct.field ( 89 | "st_atime", 90 | & NakedString::from ( 91 | self.st_atime.to_string ())); 92 | 93 | debug_struct.field ( 94 | "st_ctime", 95 | & NakedString::from ( 96 | self.st_ctime.to_string ())); 97 | 98 | debug_struct.field ( 99 | "st_mtime", 100 | & NakedString::from ( 101 | self.st_mtime.to_string ())); 102 | 103 | debug_struct.field ( 104 | "st_otime", 105 | & NakedString::from ( 106 | self.st_otime.to_string ())); 107 | 108 | } 109 | 110 | } 111 | 112 | impl Debug for BtrfsInodeItemData { 113 | 114 | fn fmt ( 115 | & self, 116 | formatter: & mut Formatter, 117 | ) -> Result <(), FmtError> { 118 | 119 | let mut debug_struct = 120 | formatter.debug_struct ( 121 | "BtrfsInodeItemData"); 122 | 123 | self.debug_struct ( 124 | & mut debug_struct); 125 | 126 | debug_struct.finish () 127 | 128 | } 129 | 130 | } 131 | 132 | #[ cfg (test) ] 133 | mod tests { 134 | 135 | use std::mem; 136 | 137 | use super::*; 138 | 139 | #[ test ] 140 | fn test_size () { 141 | assert! (mem::size_of:: () == 0xa0); 142 | } 143 | 144 | } 145 | 146 | // ex: noet ts=4 filetype=rust 147 | -------------------------------------------------------------------------------- /src/diskformat/item/inode_ref_entry.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::fmt::Debug; 3 | use std::fmt::Error as FmtError; 4 | use std::fmt::Formatter; 5 | use std::mem; 6 | 7 | use diskformat::*; 8 | 9 | #[ derive (Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd) ] 10 | pub struct BtrfsInodeRefEntry <'a> { 11 | header: & 'a BtrfsLeafItemHeader, 12 | data_bytes: & 'a [u8], 13 | } 14 | 15 | impl <'a> BtrfsInodeRefEntry <'a> { 16 | 17 | pub fn from_bytes ( 18 | header: & 'a BtrfsLeafItemHeader, 19 | data_bytes: & 'a [u8], 20 | ) -> Result , String> { 21 | 22 | // sanity check 23 | 24 | if data_bytes.len () < mem::size_of:: () { 25 | 26 | return Err ( 27 | format! ( 28 | "Must be at least 0x{:x} bytes", 29 | mem::size_of:: ())); 30 | 31 | } 32 | 33 | // create inode ref 34 | 35 | let inode_ref = BtrfsInodeRefEntry { 36 | header: header, 37 | data_bytes: data_bytes, 38 | }; 39 | 40 | // sanity check 41 | 42 | if data_bytes.len () != ( 43 | mem::size_of:: () 44 | + inode_ref.name_length () as usize 45 | ) { 46 | 47 | return Err ( 48 | format! ( 49 | "Must be at exactly 0x{:x} bytes", 50 | mem::size_of:: () 51 | + inode_ref.name_length () as usize)); 52 | 53 | } 54 | 55 | // return 56 | 57 | Ok (inode_ref) 58 | 59 | } 60 | 61 | pub fn header (& self) -> & BtrfsLeafItemHeader { 62 | self.header 63 | } 64 | 65 | pub fn data (& self) -> & BtrfsInodeRefData { 66 | 67 | unsafe { 68 | & * ( 69 | self.data_bytes.as_ptr () 70 | as * const BtrfsInodeRefData 71 | ) 72 | } 73 | 74 | } 75 | 76 | pub fn sequence (& self) -> u64 { 77 | self.data ().sequence 78 | } 79 | 80 | pub fn name_length (& self) -> u16 { 81 | self.data ().name_length 82 | } 83 | 84 | pub fn name ( 85 | & 'a self, 86 | ) -> & 'a [u8] { 87 | 88 | & self.data_bytes [ 89 | mem::size_of:: () 90 | .. 91 | mem::size_of:: () 92 | + self.name_length () as usize 93 | ] 94 | 95 | } 96 | 97 | pub fn name_to_string_lossy ( 98 | & self, 99 | ) -> Cow { 100 | 101 | String::from_utf8_lossy ( 102 | self.name ()) 103 | 104 | } 105 | 106 | } 107 | 108 | impl <'a> BtrfsLeafItemContents <'a> for BtrfsInodeRefEntry <'a> { 109 | 110 | fn header (& self) -> & BtrfsLeafItemHeader { 111 | self.header 112 | } 113 | 114 | } 115 | 116 | impl <'a> Debug for BtrfsInodeRefEntry <'a> { 117 | 118 | fn fmt ( 119 | & self, 120 | formatter: & mut Formatter, 121 | ) -> Result <(), FmtError> { 122 | 123 | let mut debug_struct = 124 | formatter.debug_struct ( 125 | "BtrfsInodeRefEntry"); 126 | 127 | debug_struct.field ( 128 | "key", 129 | & NakedString::from ( 130 | self.key ().to_string_no_type_decimal ())); 131 | 132 | debug_struct.field ( 133 | "sequence", 134 | & self.sequence ()); 135 | 136 | debug_struct.field ( 137 | "name", 138 | & self.name_to_string_lossy ()); 139 | 140 | debug_struct.finish () 141 | 142 | } 143 | 144 | } 145 | 146 | // ex: noet ts=4 filetype=rust 147 | -------------------------------------------------------------------------------- /src/compress/lzo.rs: -------------------------------------------------------------------------------- 1 | use libc; 2 | 3 | use std::mem; 4 | use std::ptr; 5 | 6 | pub const LZO_E_OK: libc::c_int = 0; 7 | pub const LZO_E_ERROR: libc::c_int = -1; 8 | 9 | #[ link (name = "lzo2") ] 10 | extern { 11 | 12 | fn __lzo_init_v2 ( 13 | version: libc::c_uint, 14 | short_size: libc::c_int, 15 | int_size: libc::c_int, 16 | long_size: libc::c_int, 17 | uint32_size: libc::c_int, 18 | uint_size: libc::c_int, 19 | dict_size: libc::c_int, 20 | charp_size: libc::c_int, 21 | voidp_size: libc::c_int, 22 | callback_size: libc::c_int, 23 | ) -> libc::c_int; 24 | 25 | fn lzo2a_decompress ( 26 | src: * const u8, 27 | src_len: libc::c_uint, 28 | dst: * const u8, 29 | dst_len: * mut libc::c_uint, 30 | wrkmem: * const u8, 31 | ) -> libc::c_int; 32 | 33 | fn lzo2a_decompress_safe ( 34 | src: * const u8, 35 | src_len: libc::c_uint, 36 | dst: * const u8, 37 | dst_len: * mut libc::c_uint, 38 | wrkmem: * const u8, 39 | ) -> libc::c_int; 40 | 41 | } 42 | 43 | // initialisation 44 | 45 | static mut INITIALISED: bool = false; 46 | 47 | pub fn initialise ( 48 | ) -> Result <(), String> { 49 | 50 | if unsafe { INITIALISED } { 51 | return Ok (()); 52 | } 53 | 54 | let result = unsafe { 55 | 56 | __lzo_init_v2 ( 57 | 0x2080, 58 | mem::size_of:: () as libc::c_int, 59 | mem::size_of:: () as libc::c_int, 60 | mem::size_of:: () as libc::c_int, 61 | mem::size_of:: () as libc::c_int, 62 | mem::size_of:: () as libc::c_int, 63 | mem::size_of::<* const u8> () as libc::c_int, 64 | mem::size_of::<* const u8> () as libc::c_int, 65 | mem::size_of::<* const u8> () as libc::c_int, 66 | ( 67 | mem::size_of::<* const u8> () * 4 68 | + mem::size_of:: () * 2 69 | ) as libc::c_int) 70 | 71 | }; 72 | 73 | match result { 74 | 75 | LZO_E_OK => { 76 | 77 | unsafe { 78 | INITIALISED = true; 79 | } 80 | 81 | Ok (()) 82 | 83 | }, 84 | 85 | LZO_E_ERROR => 86 | Err ( 87 | "LZO initialisation failed".to_owned ()), 88 | 89 | _ => 90 | Err ( 91 | format! ( 92 | "LZO initialisation error {}", 93 | result)), 94 | 95 | } 96 | 97 | } 98 | 99 | // decompression 100 | 101 | pub fn decompress ( 102 | input: & [u8], 103 | output_size: usize, 104 | ) -> Result , String> { 105 | 106 | initialise ().unwrap_or_else ( 107 | |error| 108 | 109 | panic! ( 110 | error) 111 | 112 | ); 113 | 114 | let mut output = 115 | Vec::new (); 116 | 117 | output.resize ( 118 | output_size, 119 | 0u8); 120 | 121 | let mut output_len = 122 | output.len () as u32; 123 | 124 | let result = unsafe { 125 | 126 | lzo2a_decompress_safe ( 127 | input.as_ptr (), 128 | input.len () as u32, 129 | output.as_ptr (), 130 | & mut output_len as * mut u32, 131 | ptr::null ()) 132 | 133 | }; 134 | 135 | output.resize ( 136 | output_len as usize, 137 | 0u8); 138 | 139 | match result { 140 | 141 | LZO_E_OK => 142 | Ok (output), 143 | 144 | _ => 145 | Err ( 146 | format! ( 147 | "LZO error code {}", 148 | result)), 149 | 150 | } 151 | 152 | } 153 | 154 | // ex: noet ts=4 filetype=rust 155 | -------------------------------------------------------------------------------- /src/diskformat/item/inode_item.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::fmt::Error as FmtError; 3 | use std::fmt::Formatter; 4 | use std::mem; 5 | 6 | use super::super::*; 7 | 8 | #[ derive (Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd) ] 9 | pub struct BtrfsInodeItem <'a> { 10 | header: & 'a BtrfsLeafItemHeader, 11 | data_bytes: & 'a [u8], 12 | } 13 | 14 | impl <'a> BtrfsInodeItem <'a> { 15 | 16 | pub fn from_bytes ( 17 | header: & 'a BtrfsLeafItemHeader, 18 | data_bytes: & 'a [u8], 19 | ) -> Result , String> { 20 | 21 | // sanity check 22 | 23 | if data_bytes.len () != mem::size_of:: () { 24 | 25 | return Err ( 26 | format! ( 27 | "Must be at exactly 0x{:x} bytes", 28 | mem::size_of:: ())); 29 | 30 | } 31 | 32 | // create inode item 33 | 34 | Ok ( 35 | BtrfsInodeItem { 36 | header: header, 37 | data_bytes: data_bytes, 38 | } 39 | ) 40 | 41 | } 42 | 43 | pub fn data (& self) -> & BtrfsInodeItemData { 44 | 45 | unsafe { 46 | & * ( 47 | self.data_bytes.as_ptr () 48 | as * const BtrfsInodeItemData 49 | ) 50 | } 51 | 52 | } 53 | 54 | pub fn generation (& self) -> u64 { 55 | self.data ().generation 56 | } 57 | 58 | pub fn transaction_id (& self) -> u64 { 59 | self.data ().transaction_id 60 | } 61 | 62 | pub fn st_size (& self) -> u64 { 63 | self.data ().st_size 64 | } 65 | 66 | pub fn st_blocks (& self) -> u64 { 67 | self.data ().st_blocks 68 | } 69 | 70 | pub fn block_group (& self) -> u64 { 71 | self.data ().block_group 72 | } 73 | 74 | pub fn st_nlink (& self) -> u32 { 75 | self.data ().st_nlink 76 | } 77 | 78 | pub fn st_uid (& self) -> u32 { 79 | self.data ().st_uid 80 | } 81 | 82 | pub fn st_gid (& self) -> u32 { 83 | self.data ().st_gid 84 | } 85 | 86 | pub fn st_mode (& self) -> u32 { 87 | self.data ().st_mode 88 | } 89 | 90 | pub fn st_rdev (& self) -> u64 { 91 | self.data ().st_rdev 92 | } 93 | 94 | pub fn flags (& self) -> u64 { 95 | self.data ().flags 96 | } 97 | 98 | pub fn sequence (& self) -> u64 { 99 | self.data ().sequence 100 | } 101 | 102 | pub fn st_atime (& self) -> BtrfsTimestamp { 103 | self.data ().st_atime 104 | } 105 | 106 | pub fn st_ctime (& self) -> BtrfsTimestamp { 107 | self.data ().st_ctime 108 | } 109 | 110 | pub fn st_mtime (& self) -> BtrfsTimestamp { 111 | self.data ().st_mtime 112 | } 113 | 114 | pub fn st_otime (& self) -> BtrfsTimestamp { 115 | self.data ().st_otime 116 | } 117 | 118 | } 119 | 120 | impl <'a> BtrfsLeafItemContents <'a> for BtrfsInodeItem <'a> { 121 | 122 | fn header (& self) -> & BtrfsLeafItemHeader { 123 | self.header 124 | } 125 | 126 | } 127 | 128 | impl <'a> Debug for BtrfsInodeItem <'a> { 129 | 130 | fn fmt ( 131 | & self, 132 | formatter: & mut Formatter, 133 | ) -> Result <(), FmtError> { 134 | 135 | let mut debug_struct = 136 | formatter.debug_struct ( 137 | "BtrfsInodeItem"); 138 | 139 | self.header ().debug_struct ( 140 | & mut debug_struct); 141 | 142 | self.data ().debug_struct ( 143 | & mut debug_struct); 144 | 145 | debug_struct.finish () 146 | 147 | } 148 | 149 | } 150 | 151 | // ex: noet ts=4 filetype=rust 152 | -------------------------------------------------------------------------------- /src/diskformat/item/root_ref.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::fmt::Debug; 3 | use std::fmt::Error as FmtError; 4 | use std::fmt::Formatter; 5 | use std::mem; 6 | 7 | use diskformat::*; 8 | 9 | #[ derive (Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd) ] 10 | pub struct BtrfsRootRef <'a> { 11 | header: & 'a BtrfsLeafItemHeader, 12 | data_bytes: & 'a [u8], 13 | } 14 | 15 | impl <'a> BtrfsRootRef <'a> { 16 | 17 | pub fn from_bytes ( 18 | header: & 'a BtrfsLeafItemHeader, 19 | data_bytes: & 'a [u8], 20 | ) -> Result , String> { 21 | 22 | // sanity check 23 | 24 | if data_bytes.len () < mem::size_of:: () { 25 | 26 | return Err ( 27 | format! ( 28 | "Must be at least 0x{:x} bytes", 29 | mem::size_of:: ())); 30 | 31 | } 32 | 33 | // create dir item 34 | 35 | let root_ref = BtrfsRootRef { 36 | header: header, 37 | data_bytes: data_bytes, 38 | }; 39 | 40 | // sanity check 41 | 42 | if data_bytes.len () != ( 43 | mem::size_of:: () 44 | + root_ref.name_length () as usize 45 | ) { 46 | 47 | return Err ( 48 | format! ( 49 | "Must be at exactly 0x{:x} bytes", 50 | mem::size_of:: () 51 | + root_ref.name_length () as usize)); 52 | 53 | } 54 | 55 | // return 56 | 57 | Ok (root_ref) 58 | 59 | } 60 | 61 | pub fn header (& self) -> & BtrfsLeafItemHeader { 62 | self.header 63 | } 64 | 65 | pub fn data (& self) -> & BtrfsRootRefData { 66 | 67 | unsafe { 68 | & * ( 69 | self.data_bytes.as_ptr () 70 | as * const BtrfsRootRefData 71 | ) 72 | } 73 | 74 | } 75 | 76 | pub fn directory_id (& self) -> u64 { 77 | self.data ().directory_id 78 | } 79 | 80 | pub fn sequence (& self) -> u64 { 81 | self.data ().sequence 82 | } 83 | 84 | pub fn name_length (& self) -> u16 { 85 | self.data ().name_length 86 | } 87 | 88 | pub fn name ( 89 | & 'a self, 90 | ) -> & 'a [u8] { 91 | 92 | & self.data_bytes [ 93 | mem::size_of:: () 94 | .. 95 | mem::size_of:: () 96 | + self.name_length () as usize 97 | ] 98 | 99 | } 100 | 101 | pub fn name_to_string_lossy ( 102 | & self, 103 | ) -> Cow { 104 | 105 | String::from_utf8_lossy ( 106 | self.name ()) 107 | 108 | } 109 | 110 | } 111 | 112 | impl <'a> BtrfsLeafItemContents <'a> for BtrfsRootRef <'a> { 113 | 114 | fn header (& self) -> & BtrfsLeafItemHeader { 115 | self.header 116 | } 117 | 118 | } 119 | 120 | impl <'a> Debug for BtrfsRootRef <'a> { 121 | 122 | fn fmt ( 123 | & self, 124 | formatter: & mut Formatter, 125 | ) -> Result <(), FmtError> { 126 | 127 | let mut debug_struct = 128 | formatter.debug_struct ( 129 | "BtrfsRootRef"); 130 | 131 | debug_struct.field ( 132 | "key", 133 | & NakedString::from ( 134 | self.key ().to_string_decimal ())); 135 | 136 | debug_struct.field ( 137 | "directory_id", 138 | & self.directory_id ()); 139 | 140 | debug_struct.field ( 141 | "sequence", 142 | & self.sequence ()); 143 | 144 | debug_struct.field ( 145 | "name", 146 | & self.name_to_string_lossy ()); 147 | 148 | debug_struct.finish () 149 | 150 | } 151 | 152 | } 153 | 154 | // ex: noet ts=4 filetype=rust 155 | -------------------------------------------------------------------------------- /src/diskformat/item/root_backref.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::fmt::Debug; 3 | use std::fmt::Error as FmtError; 4 | use std::fmt::Formatter; 5 | use std::mem; 6 | 7 | use diskformat::*; 8 | 9 | #[ derive (Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd) ] 10 | pub struct BtrfsRootBackref <'a> { 11 | header: & 'a BtrfsLeafItemHeader, 12 | data_bytes: & 'a [u8], 13 | } 14 | 15 | impl <'a> BtrfsRootBackref <'a> { 16 | 17 | pub fn from_bytes ( 18 | header: & 'a BtrfsLeafItemHeader, 19 | data_bytes: & 'a [u8], 20 | ) -> Result , String> { 21 | 22 | // sanity check 23 | 24 | if data_bytes.len () < mem::size_of:: () { 25 | 26 | return Err ( 27 | format! ( 28 | "Must be at least 0x{:x} bytes", 29 | mem::size_of:: ())); 30 | 31 | } 32 | 33 | // create root backref 34 | 35 | let root_ref = BtrfsRootBackref { 36 | header: header, 37 | data_bytes: data_bytes, 38 | }; 39 | 40 | // sanity check 41 | 42 | if data_bytes.len () != ( 43 | mem::size_of:: () 44 | + root_ref.name_length () as usize 45 | ) { 46 | 47 | return Err ( 48 | format! ( 49 | "Must be at exactly 0x{:x} bytes", 50 | mem::size_of:: () 51 | + root_ref.name_length () as usize)); 52 | 53 | } 54 | 55 | // return 56 | 57 | Ok (root_ref) 58 | 59 | } 60 | 61 | pub fn header (& self) -> & BtrfsLeafItemHeader { 62 | self.header 63 | } 64 | 65 | pub fn data (& self) -> & BtrfsRootRefData { 66 | 67 | unsafe { 68 | & * ( 69 | self.data_bytes.as_ptr () 70 | as * const BtrfsRootRefData 71 | ) 72 | } 73 | 74 | } 75 | 76 | pub fn directory_id (& self) -> u64 { 77 | self.data ().directory_id 78 | } 79 | 80 | pub fn sequence (& self) -> u64 { 81 | self.data ().sequence 82 | } 83 | 84 | pub fn name_length (& self) -> u16 { 85 | self.data ().name_length 86 | } 87 | 88 | pub fn name ( 89 | & 'a self, 90 | ) -> & 'a [u8] { 91 | 92 | & self.data_bytes [ 93 | mem::size_of:: () 94 | .. 95 | mem::size_of:: () 96 | + self.name_length () as usize 97 | ] 98 | 99 | } 100 | 101 | pub fn name_to_string_lossy ( 102 | & self, 103 | ) -> Cow { 104 | 105 | String::from_utf8_lossy ( 106 | self.name ()) 107 | 108 | } 109 | 110 | } 111 | 112 | impl <'a> BtrfsLeafItemContents <'a> for BtrfsRootBackref <'a> { 113 | 114 | fn header (& self) -> & BtrfsLeafItemHeader { 115 | self.header 116 | } 117 | 118 | } 119 | 120 | impl <'a> Debug for BtrfsRootBackref <'a> { 121 | 122 | fn fmt ( 123 | & self, 124 | formatter: & mut Formatter, 125 | ) -> Result <(), FmtError> { 126 | 127 | let mut debug_struct = 128 | formatter.debug_struct ( 129 | "BtrfsRootBackref"); 130 | 131 | debug_struct.field ( 132 | "key", 133 | & NakedString::from ( 134 | self.key ().to_string_decimal ())); 135 | 136 | debug_struct.field ( 137 | "directory_id", 138 | & self.directory_id ()); 139 | 140 | debug_struct.field ( 141 | "sequence", 142 | & self.sequence ()); 143 | 144 | debug_struct.field ( 145 | "name", 146 | & self.name_to_string_lossy ()); 147 | 148 | debug_struct.finish () 149 | 150 | } 151 | 152 | } 153 | 154 | // ex: noet ts=4 filetype=rust 155 | -------------------------------------------------------------------------------- /src/diskformat/core/device.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | 3 | use super::super::*; 4 | 5 | #[ derive (Clone) ] 6 | pub struct BtrfsDevice <'a> { 7 | data: & 'a [u8], 8 | superblock_data: * const BtrfsSuperblockData, 9 | superblock_offset: usize, 10 | } 11 | 12 | impl <'a> BtrfsDevice <'a> { 13 | 14 | pub fn new ( 15 | data: & [u8], 16 | ) -> Result { 17 | 18 | // find superblock 19 | 20 | let (superblock_data, superblock_offset) = { 21 | 22 | let mut superblock_datas: Vec <(& BtrfsSuperblockData, usize)> = 23 | Vec::new (); 24 | 25 | for superblock_offset in vec! [ 26 | 0x1_0000, 27 | 0x400_0000, 28 | 0x40_0000_0000, 29 | 0x4_0000_0000_0000, 30 | ] { 31 | 32 | if superblock_offset 33 | + mem::size_of:: () 34 | >= data.len () { 35 | 36 | continue; 37 | 38 | } 39 | 40 | let superblock_data: & BtrfsSuperblockData = 41 | unsafe { 42 | mem::transmute ( 43 | & data [superblock_offset]) 44 | }; 45 | 46 | if superblock_data.magic != BTRFS_MAGIC { 47 | continue; 48 | } 49 | 50 | superblock_datas.push ( 51 | ( 52 | superblock_data, 53 | superblock_offset, 54 | ) 55 | ); 56 | 57 | } 58 | 59 | superblock_datas.into_iter ().max_by_key ( 60 | |& (superblock_data, _offset)| superblock_data.generation, 61 | ).ok_or ( 62 | "No superblocks found".to_string (), 63 | ) ? 64 | 65 | }; 66 | 67 | Ok (BtrfsDevice { 68 | data: data, 69 | superblock_data: superblock_data, 70 | superblock_offset: superblock_offset, 71 | }) 72 | 73 | } 74 | 75 | pub fn slice_at ( 76 | & self, 77 | offset: usize, 78 | len: usize, 79 | ) -> Option <& [u8]> { 80 | 81 | if offset + len <= self.data.len () { 82 | 83 | Some ( 84 | & self.data [offset .. offset + len] 85 | ) 86 | 87 | } else { 88 | 89 | None 90 | 91 | } 92 | 93 | } 94 | 95 | pub unsafe fn struct_at ( 96 | & self, 97 | offset: u64, 98 | ) -> & Type { 99 | 100 | mem::transmute ( 101 | & self.data [ 102 | offset as usize]) 103 | 104 | } 105 | 106 | pub fn node_at ( 107 | & self, 108 | offset: u64, 109 | ) -> Result { 110 | 111 | self.slice_at ( 112 | offset as usize, 113 | self.superblock ().node_size () as usize, 114 | ).ok_or ( 115 | 116 | format! ( 117 | "Node offset out of range for device {}: 0x{:x}", 118 | self.device_id (), 119 | offset) 120 | 121 | ).and_then ( 122 | |node_bytes| 123 | 124 | BtrfsNode::from_bytes ( 125 | BtrfsPhysicalAddress::new ( 126 | self.device_id (), 127 | offset as u64), 128 | node_bytes) 129 | 130 | ) 131 | 132 | } 133 | 134 | pub fn data (& self) -> & [u8] { 135 | self.data 136 | } 137 | 138 | pub fn len (& self) -> usize { 139 | self.data.len () 140 | } 141 | 142 | pub fn superblock ( 143 | & self, 144 | ) -> BtrfsSuperblock { 145 | 146 | BtrfsSuperblock::new ( 147 | unsafe { 148 | & * self.superblock_data 149 | } 150 | ) 151 | 152 | } 153 | 154 | pub fn superblock_offset (& self) -> usize { 155 | self.superblock_offset 156 | } 157 | 158 | pub fn device_id (& self) -> u64 { 159 | self.superblock ().device_id () 160 | } 161 | 162 | } 163 | 164 | // ex: noett ts=4 filetype=rust 165 | -------------------------------------------------------------------------------- /src/linux/operations/filesystem_info.rs: -------------------------------------------------------------------------------- 1 | //! This module contains an interface to the kernel's file system info 2 | //! functionality. 3 | //! 4 | //! This includes information about the various backing devices and their usage. 5 | //! This information is essential to detect when a BTRFS filesystem is reaching 6 | //! capacity, and a high level of usage may indicate that a balance operation 7 | //! is required. 8 | 9 | use linux::imports::*; 10 | use nix::Errno as NixErrno; 11 | use nix::Error as NixError; 12 | 13 | // ---------- get filesystem info 14 | 15 | pub fn get_filesystem_info ( 16 | file_descriptor: libc::c_int, 17 | ) -> Result { 18 | 19 | // get filesystem info 20 | 21 | let mut c_fs_info_args = 22 | IoctlFsInfoArgs::new (); 23 | 24 | unsafe { 25 | 26 | ioctl_fs_info ( 27 | file_descriptor, 28 | & mut c_fs_info_args as * mut IoctlFsInfoArgs) 29 | 30 | }.map_err ( 31 | |error| 32 | 33 | format! ( 34 | "Error getting btrfs filesystem information: {}", 35 | error) 36 | 37 | ) ?; 38 | 39 | // return 40 | 41 | Ok ( 42 | FilesystemInfo { 43 | 44 | max_id: 45 | c_fs_info_args.max_id, 46 | 47 | num_devices: 48 | c_fs_info_args.num_devices, 49 | 50 | filesystem_id: 51 | Uuid::from_bytes ( 52 | & c_fs_info_args.filesystem_id, 53 | ).unwrap (), 54 | 55 | } 56 | ) 57 | 58 | } 59 | 60 | pub fn get_device_info ( 61 | file_descriptor: libc::c_int, 62 | device_id: u64, 63 | ) -> Result