├── .gitignore ├── Cargo.toml ├── README.md └── src ├── compress ├── lzo.rs ├── mod.rs └── zlib.rs ├── diskformat ├── compression.rs ├── core │ ├── checksum.rs │ ├── dev_item.rs │ ├── dev_item_data.rs │ ├── device.rs │ ├── device_set.rs │ ├── file_type.rs │ ├── key.rs │ ├── label.rs │ ├── mmap_device_set.rs │ ├── mod.rs │ ├── physical_address.rs │ ├── root_backup.rs │ ├── superblock.rs │ ├── superblock_chunk_item.rs │ ├── superblock_data.rs │ ├── superblock_reserved.rs │ ├── superblock_system_chunks.rs │ ├── superblock_system_chunks_data.rs │ ├── superblock_unused.rs │ ├── timestamp.rs │ └── uuid.rs ├── filesystem.rs ├── item │ ├── chunk_item.rs │ ├── chunk_item_data.rs │ ├── chunk_item_stripe_data.rs │ ├── dir_index.rs │ ├── dir_item.rs │ ├── dir_item_data.rs │ ├── dir_item_entry.rs │ ├── extent_constants.rs │ ├── extent_data.rs │ ├── extent_data_constants.rs │ ├── extent_data_data.rs │ ├── extent_data_ref_data.rs │ ├── extent_inline_ref_data.rs │ ├── extent_item.rs │ ├── extent_item_data.rs │ ├── inode_item.rs │ ├── inode_item_data.rs │ ├── inode_ref.rs │ ├── inode_ref_data.rs │ ├── inode_ref_entry.rs │ ├── internal_item.rs │ ├── invalid_item.rs │ ├── leaf_item.rs │ ├── leaf_item_constants.rs │ ├── leaf_item_contents.rs │ ├── leaf_item_header.rs │ ├── mod.rs │ ├── root_backref.rs │ ├── root_item.rs │ ├── root_item_data.rs │ ├── root_ref.rs │ ├── root_ref_data.rs │ ├── tree_block_info_data.rs │ └── unknown_item.rs ├── macros │ ├── leaf_item_composite_type_entries.rs │ ├── leaf_item_composite_type_implementation.rs │ ├── leaf_item_composite_type_iterator.rs │ ├── leaf_item_destructure.rs │ ├── leaf_item_type_entries.rs │ ├── mod.rs │ ├── tree_item_accessor.rs │ └── tree_item_range_accessor.rs ├── misc-old.rs ├── mod.rs ├── naked_string.rs ├── node │ ├── header.rs │ ├── internal_node.rs │ ├── leaf_node.rs │ ├── leaf_node_items.rs │ ├── mod.rs │ └── node.rs ├── prelude.rs └── tree │ ├── chunk_tree.rs │ ├── extent_tree.rs │ ├── filesystem_tree.rs │ ├── mod.rs │ ├── read_tree.rs │ ├── root_tree.rs │ ├── tree.rs │ ├── tree_id.rs │ └── unknown_tree.rs ├── lib.rs └── linux ├── ctypes ├── ioctl_constants.rs ├── ioctl_defrag_range_args.rs ├── ioctl_dev_info_args.rs ├── ioctl_device_path.rs ├── ioctl_fiemap.rs ├── ioctl_fiemap_extent.rs ├── ioctl_file_dedupe_range.rs ├── ioctl_file_dedupe_range_info.rs ├── ioctl_fs_info_args.rs ├── ioctl_space_args.rs ├── ioctl_space_info.rs └── mod.rs ├── imports.rs ├── ioctl_wrapper.rs ├── mod.rs ├── operations ├── deduplicate.rs ├── defragment.rs ├── fiemap.rs ├── filesystem_info.rs ├── mod.rs └── space_info.rs └── types ├── compression_type.rs ├── dedupe_range.rs ├── dedupe_range_dest_info.rs ├── dedupe_range_status.rs ├── device_info.rs ├── file_descriptor.rs ├── filesystem_info.rs ├── group_profile.rs ├── group_type.rs ├── mod.rs └── space_info.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/compress/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod lzo; 2 | pub mod zlib; 3 | 4 | // ex: noet ts=4 filetype=rust; 5 | -------------------------------------------------------------------------------- /src/compress/zlib.rs: -------------------------------------------------------------------------------- 1 | use libc::c_int; 2 | use libc::c_void; 3 | 4 | use std::mem; 5 | use std::ptr; 6 | 7 | #[ repr (C) ] 8 | struct ZlibStream { 9 | 10 | next_in: * const u8, 11 | avail_in: u32, 12 | total_in: u64, 13 | 14 | next_out: * mut u8, 15 | avail_out: u32, 16 | total_out: u64, 17 | 18 | msg: * const u8, 19 | internal_state: * const c_void, 20 | 21 | alloc_func: * const c_void, 22 | free_func: * const c_void, 23 | opaque: * const c_void, 24 | 25 | data_type: i32, 26 | adler: u64, 27 | reserved: u64, 28 | 29 | } 30 | 31 | #[ link (name = "z") ] 32 | extern { 33 | 34 | fn deflateInit2_ ( 35 | strm: * mut ZlibStream, 36 | level: c_int, 37 | method: c_int, 38 | windowBits: c_int, 39 | memLevel: c_int, 40 | strategy: c_int, 41 | version: * const u8, 42 | stream_size: c_int, 43 | ) -> c_int; 44 | 45 | fn deflate ( 46 | strm: * mut ZlibStream, 47 | flush: c_int, 48 | ) -> c_int; 49 | 50 | fn inflateInit2_ ( 51 | strm: * mut ZlibStream, 52 | windowBits: c_int, 53 | version: * const u8, 54 | stream_size: c_int, 55 | ) -> c_int; 56 | 57 | fn inflate ( 58 | strm: * mut ZlibStream, 59 | flush: c_int, 60 | ) -> c_int; 61 | 62 | } 63 | 64 | // flush values 65 | 66 | const Z_NO_FLUSH: c_int = 0; 67 | //const Z_PARTIAL_FLUSH: c_int = 1; 68 | //const Z_SYNC_FLUSH: c_int = 2; 69 | //const Z_FULL_FLUSH: c_int = 3; 70 | const Z_FINISH: c_int = 4; 71 | //const Z_BLOCK: c_int = 5; 72 | //const Z_TREES: c_int = 6; 73 | 74 | // return codes 75 | 76 | const Z_OK: c_int = 0; 77 | const Z_STREAM_END: c_int = 1; 78 | //const Z_NEED_DICT: c_int = 2; 79 | //const Z_MEM_ERROR: c_int = -4; 80 | //const Z_BUF_ERROR: c_int = -5; 81 | //const Z_VERSION_ERROR: c_int = -6; 82 | 83 | // compression level 84 | 85 | const Z_DEFAULT_COMPRESSION: c_int = -1; 86 | 87 | // compression method 88 | 89 | const Z_DEFLATED: c_int = 8; 90 | 91 | // compression strategy 92 | 93 | //const Z_FILTERED: c_int = 1; 94 | //const Z_HUFFMAN_ONLY: c_int = 2; 95 | //const Z_RLE: c_int = 3; 96 | //const Z_FIXED: c_int = 4; 97 | const Z_DEFAULT_STRATEGY: c_int = 0; 98 | 99 | // data type 100 | 101 | //const Z_BINARY: c_int = 0; 102 | //const Z_TEXT: c_int = 1; 103 | //const Z_UNKNOWN: c_int = 2; 104 | 105 | // compression 106 | 107 | pub fn compress ( 108 | input: & [u8], 109 | ) -> Result , String> { 110 | 111 | // create structure 112 | 113 | let mut zlib_stream = ZlibStream { 114 | 115 | next_in: & input [0], 116 | avail_in: input.len () as u32, 117 | total_in: 0, 118 | 119 | next_out: ptr::null_mut (), 120 | avail_out: 0, 121 | total_out: 0, 122 | 123 | msg: ptr::null (), 124 | internal_state: ptr::null (), 125 | 126 | alloc_func: ptr::null (), 127 | free_func: ptr::null (), 128 | opaque: ptr::null (), 129 | 130 | data_type: 0, 131 | adler: 0, 132 | reserved: 0, 133 | 134 | }; 135 | 136 | // init 137 | 138 | let init_result = unsafe { 139 | 140 | deflateInit2_ ( 141 | & mut zlib_stream, 142 | Z_DEFAULT_COMPRESSION, 143 | Z_DEFLATED, 144 | -15, 145 | 8, 146 | Z_DEFAULT_STRATEGY, 147 | & b"1.2.8\0" [0], 148 | mem::size_of:: () as i32) 149 | 150 | }; 151 | 152 | if init_result != Z_OK { 153 | 154 | return Err ( 155 | format! ( 156 | "Deflate init returned {}", 157 | init_result)); 158 | 159 | } 160 | 161 | // main loop 162 | 163 | let mut result: Vec = 164 | vec! (); 165 | 166 | loop { 167 | 168 | result.reserve ( 169 | 1024 * 1024); 170 | 171 | let temp_len = 172 | result.len (); 173 | 174 | zlib_stream.next_out = unsafe { 175 | 176 | result.get_unchecked_mut ( 177 | temp_len) 178 | 179 | }; 180 | 181 | zlib_stream.avail_out = 182 | 1024 * 1024; 183 | 184 | let deflate_mode = 185 | if zlib_stream.total_in == input.len () as u64 { 186 | Z_FINISH 187 | } else { 188 | Z_NO_FLUSH 189 | }; 190 | 191 | let loop_result = unsafe { 192 | 193 | deflate ( 194 | & mut zlib_stream, 195 | deflate_mode) 196 | 197 | }; 198 | 199 | unsafe { 200 | 201 | result.set_len ( 202 | zlib_stream.total_out as usize); 203 | 204 | } 205 | 206 | if loop_result == Z_STREAM_END { 207 | break; 208 | } 209 | 210 | if loop_result != Z_OK { 211 | 212 | // TODO clean up! 213 | 214 | return Err ( 215 | format! ( 216 | "Deflate returned {}", 217 | loop_result)); 218 | 219 | } 220 | 221 | } 222 | 223 | Ok (result) 224 | 225 | } 226 | 227 | pub fn decompress ( 228 | input: & [u8], 229 | ) -> Result , String> { 230 | 231 | // create structure 232 | 233 | let mut zlib_stream = ZlibStream { 234 | 235 | next_in: & input [0], 236 | avail_in: input.len () as u32, 237 | total_in: 0, 238 | 239 | next_out: ptr::null_mut (), 240 | avail_out: 0, 241 | total_out: 0, 242 | 243 | msg: ptr::null (), 244 | internal_state: ptr::null (), 245 | 246 | alloc_func: ptr::null (), 247 | free_func: ptr::null (), 248 | opaque: ptr::null (), 249 | 250 | data_type: 0, 251 | adler: 0, 252 | reserved: 0, 253 | 254 | }; 255 | 256 | // init 257 | 258 | let init_result = unsafe { 259 | 260 | inflateInit2_ ( 261 | & mut zlib_stream, 262 | -15, 263 | & b"1.2.8\0" [0], 264 | mem::size_of:: () as i32) 265 | 266 | }; 267 | 268 | if init_result != Z_OK { 269 | 270 | return Err ( 271 | format! ( 272 | "Inflate init returned {}", 273 | init_result)); 274 | 275 | } 276 | 277 | // read data 278 | 279 | let mut result: Vec = 280 | vec! (); 281 | 282 | loop { 283 | 284 | result.reserve ( 285 | 1024 * 1024); 286 | 287 | let temp_len = 288 | result.len (); 289 | 290 | zlib_stream.next_out = unsafe { 291 | 292 | result.get_unchecked_mut ( 293 | temp_len) 294 | 295 | }; 296 | 297 | zlib_stream.avail_out = 298 | 1024 * 1024; 299 | 300 | let loop_result = unsafe { 301 | 302 | inflate ( 303 | & mut zlib_stream, 304 | Z_NO_FLUSH) 305 | 306 | }; 307 | 308 | unsafe { 309 | 310 | result.set_len ( 311 | zlib_stream.total_out as usize); 312 | 313 | } 314 | 315 | if loop_result == Z_STREAM_END { 316 | break; 317 | } 318 | 319 | if loop_result != Z_OK { 320 | 321 | // TODO clean up! 322 | 323 | return Err ( 324 | format! ( 325 | "Deflate returned {}", 326 | loop_result)); 327 | 328 | } 329 | 330 | } 331 | 332 | result.shrink_to_fit (); 333 | 334 | Ok (result) 335 | 336 | } 337 | -------------------------------------------------------------------------------- /src/diskformat/compression.rs: -------------------------------------------------------------------------------- 1 | use super::prelude::*; 2 | 3 | pub fn btrfs_decompress_pages ( 4 | compression_type: u8, 5 | raw_data: & [u8], 6 | logical_size: u64, 7 | ) -> Result , String> { 8 | 9 | match compression_type { 10 | 11 | BTRFS_EXTENT_DATA_NO_COMPRESSION => 12 | Ok (Cow::Borrowed ( 13 | raw_data, 14 | )), 15 | 16 | BTRFS_EXTENT_DATA_LZO_COMPRESSION => { 17 | 18 | let mut buffer: Vec = 19 | Vec::new (); 20 | 21 | let mut position: usize = 0; 22 | 23 | if raw_data.len () < 4 { 24 | 25 | return Err ( 26 | format! ( 27 | "LZO paging corruption (0)")); 28 | 29 | } 30 | 31 | let total_compressed_size = 32 | LittleEndian::read_u32 ( 33 | & raw_data [position .. position + 4], 34 | ) as usize; 35 | 36 | if raw_data.len () < total_compressed_size { 37 | 38 | return Err ( 39 | format! ( 40 | "LZO paging corruption (1) @ {} / {}", 41 | position, 42 | total_compressed_size)); 43 | 44 | } 45 | 46 | position += 4; 47 | 48 | while position < total_compressed_size { 49 | 50 | // skip to next memory page if page compressed size won't fit 51 | 52 | let bytes_left = 53 | 0x1000 - (position % 0x1000); 54 | 55 | if bytes_left < 4 { 56 | 57 | if position + bytes_left > total_compressed_size { 58 | 59 | return Err ( 60 | format! ( 61 | "LZO paging corruption (2) @ {} / {}", 62 | position, 63 | total_compressed_size)); 64 | 65 | } 66 | 67 | position += bytes_left; 68 | 69 | } 70 | 71 | if position == total_compressed_size { 72 | break; 73 | } 74 | 75 | // read page compressed size 76 | 77 | if position + 4 > total_compressed_size { 78 | 79 | return Err ( 80 | format! ( 81 | "LZO paging corruption (3) @ {} / {}", 82 | position, 83 | total_compressed_size)); 84 | 85 | } 86 | 87 | let page_compressed_size = 88 | LittleEndian::read_u32 ( 89 | & raw_data [position .. position + 4], 90 | ) as usize; 91 | 92 | position += 4; 93 | 94 | // read page 95 | 96 | if position + page_compressed_size > total_compressed_size { 97 | 98 | return Err ( 99 | format! ( 100 | "LZO paging corruption (4) @ {} / {}", 101 | position, 102 | total_compressed_size)); 103 | 104 | } 105 | 106 | buffer.extend_from_slice ( 107 | & minilzo::decompress ( 108 | & raw_data [ 109 | position 110 | .. 111 | position + page_compressed_size 112 | ], 113 | logical_size as usize, 114 | ).map_err ( 115 | |error| 116 | 117 | format! ( 118 | "LZO decompression failed: {:?}", 119 | error) 120 | 121 | ) ?, 122 | ); 123 | 124 | position += page_compressed_size; 125 | 126 | } 127 | 128 | Ok (Cow::Owned (buffer)) 129 | 130 | }, 131 | 132 | BTRFS_EXTENT_DATA_ZLIB_COMPRESSION => 133 | btrfs_decompress ( 134 | compression_type, 135 | & raw_data [8 .. ], 136 | logical_size), 137 | 138 | _ => 139 | panic! (), 140 | 141 | } 142 | 143 | } 144 | 145 | pub fn btrfs_decompress ( 146 | compression_type: u8, 147 | raw_data: & [u8], 148 | logical_size: u64, 149 | ) -> Result , String> { 150 | 151 | match compression_type { 152 | 153 | BTRFS_EXTENT_DATA_NO_COMPRESSION => 154 | Ok (Cow::Borrowed ( 155 | raw_data, 156 | )), 157 | 158 | BTRFS_EXTENT_DATA_LZO_COMPRESSION => try! ( 159 | minilzo::decompress ( 160 | raw_data, 161 | logical_size as usize, 162 | ).map ( 163 | |uncompressed_data| 164 | 165 | Ok (Cow::Owned ( 166 | uncompressed_data, 167 | )) 168 | 169 | ).or_else ( 170 | |error| 171 | 172 | Err ( 173 | format! ( 174 | "LZO decompression failed: {:?}", 175 | error) 176 | ) 177 | 178 | ) 179 | ), 180 | 181 | BTRFS_EXTENT_DATA_ZLIB_COMPRESSION => { 182 | 183 | let mut uncompressed_data = 184 | Vec::with_capacity ( 185 | logical_size as usize); 186 | 187 | uncompressed_data.resize ( 188 | logical_size as usize, 189 | 0u8); 190 | 191 | let mut decompress = 192 | flate2::Decompress::new ( 193 | false); 194 | 195 | match ( 196 | decompress.decompress ( 197 | raw_data, 198 | & mut uncompressed_data, 199 | flate2::Flush::Finish, 200 | ).unwrap_or_else ( 201 | |error| 202 | 203 | panic! ( 204 | "ZLIB decompression failed: {:?}", 205 | error) 206 | 207 | ) 208 | ) { 209 | 210 | flate2::Status::Ok => 211 | (), 212 | 213 | _ => 214 | panic! ( 215 | "ZLIB decompression failed"), 216 | 217 | } 218 | 219 | if decompress.total_out () as usize 220 | != uncompressed_data.len () { 221 | 222 | panic! ( 223 | "ZLIB decompressed size {} does not match {}", 224 | decompress.total_out (), 225 | uncompressed_data.len ()); 226 | 227 | } 228 | 229 | Ok (Cow::Owned ( 230 | uncompressed_data 231 | )) 232 | 233 | }, 234 | 235 | _ => 236 | panic! ( 237 | format! ( 238 | "Unrecognised inline extent data compression {}", 239 | compression_type)), 240 | 241 | } 242 | 243 | } 244 | 245 | // ex: noet ts=4 filetype=rust 246 | -------------------------------------------------------------------------------- /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/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/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/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/diskformat/core/device_set.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::mem; 3 | 4 | use super::super::*; 5 | 6 | pub struct BtrfsDeviceSet <'a> { 7 | devices: HashMap >, 8 | superblock: BtrfsSuperblock <'a>, 9 | } 10 | 11 | impl <'a> BtrfsDeviceSet <'a> { 12 | 13 | pub fn new ( 14 | datas: & [& 'a [u8]], 15 | ) -> Result , String> { 16 | 17 | if datas.len () == 0 { 18 | 19 | return Err ( 20 | format! ( 21 | "No device data supplied")); 22 | 23 | } 24 | 25 | let mut devices = 26 | HashMap::new (); 27 | 28 | let mut superblocks: Vec > = 29 | Vec::new (); 30 | 31 | for data in datas { 32 | 33 | let btrfs_device = 34 | BtrfsDevice::new ( 35 | data, 36 | ) ?; 37 | 38 | if devices.contains_key ( 39 | & btrfs_device.device_id ()) { 40 | 41 | return Err ( 42 | format! ( 43 | "Multiple devices with id {}", 44 | btrfs_device.device_id ())); 45 | 46 | } 47 | 48 | superblocks.push ( 49 | BtrfsSuperblock::for_bytes ( 50 | & data [ 51 | btrfs_device.superblock_offset () 52 | .. 53 | btrfs_device.superblock_offset () 54 | + mem::size_of:: () 55 | ], 56 | ), 57 | ); 58 | 59 | devices.insert ( 60 | btrfs_device.device_id (), 61 | btrfs_device); 62 | 63 | } 64 | 65 | let superblock = 66 | superblocks.into_iter ().max_by_key ( 67 | |superblock| superblock.generation () 68 | ).unwrap (); 69 | 70 | Ok (BtrfsDeviceSet { 71 | devices: devices, 72 | superblock: superblock, 73 | }) 74 | 75 | } 76 | 77 | pub fn device ( 78 | & 'a self, 79 | device_id: u64, 80 | ) -> Option <& 'a BtrfsDevice <'a>> { 81 | 82 | self.devices.get ( 83 | & device_id, 84 | ) 85 | 86 | } 87 | 88 | pub fn system_logical_to_physical ( 89 | & self, 90 | logical_address: u64, 91 | ) -> Option { 92 | 93 | self.superblock.system_logical_to_physical ( 94 | logical_address, 95 | ) 96 | 97 | } 98 | 99 | pub fn system_slice_at_logical_address ( 100 | & self, 101 | logical_address: u64, 102 | size: usize, 103 | ) -> Result <& [u8], String> { 104 | 105 | let physical_address = 106 | self.system_logical_to_physical ( 107 | logical_address, 108 | ).ok_or ( 109 | 110 | format! ( 111 | "Logical address not valid: 0x{:x}", 112 | logical_address) 113 | 114 | ) ?; 115 | 116 | self.slice_at_physical_address ( 117 | physical_address, 118 | size, 119 | ) 120 | 121 | } 122 | 123 | pub fn slice_at_physical_address ( 124 | & self, 125 | physical_address: BtrfsPhysicalAddress, 126 | size: usize, 127 | ) -> Result <& [u8], String> { 128 | 129 | let device = 130 | self.devices.get ( 131 | & physical_address.device_id (), 132 | ).ok_or ( 133 | 134 | format! ( 135 | "Device not found: {}", 136 | physical_address.device_id ()) 137 | 138 | ) ?; 139 | 140 | device.slice_at ( 141 | physical_address.offset () as usize, 142 | size, 143 | ).ok_or ( 144 | 145 | format! ( 146 | "Physical address invalid for device {}: 0x{:x}", 147 | physical_address.device_id (), 148 | physical_address.offset ()) 149 | 150 | ) 151 | 152 | } 153 | 154 | pub fn node_at_physical_address ( 155 | & self, 156 | physical_address: BtrfsPhysicalAddress, 157 | ) -> Result { 158 | 159 | let device = 160 | self.devices.get ( 161 | & physical_address.device_id (), 162 | ).ok_or ( 163 | 164 | format! ( 165 | "Device not found: {}", 166 | physical_address.device_id ()) 167 | 168 | ) ?; 169 | 170 | device.node_at ( 171 | physical_address.offset (), 172 | ) 173 | 174 | } 175 | 176 | pub fn superblock (& 'a self) -> BtrfsSuperblock <'a> { 177 | self.superblock 178 | } 179 | 180 | } 181 | 182 | // ex: noet ts=4 filetype=rust 183 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/core/root_backup.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 | #[ repr (C, packed) ] 8 | #[ derive (Clone, Copy, Eq, Hash, PartialEq) ] 9 | pub struct BtrfsRootBackup { 10 | pub tree_root: u64, 11 | pub tree_root_gen: u64, 12 | pub chunk_root: u64, 13 | pub chunk_root_gen: u64, 14 | pub extent_root: u64, 15 | pub extent_root_gen: u64, 16 | pub fs_root: u64, 17 | pub fs_root_gen: u64, 18 | pub dev_root: u64, 19 | pub dev_root_gen: u64, 20 | pub csum_root: u64, 21 | pub csum_root_gen: u64, 22 | pub total_bytes: u64, 23 | pub bytes_used: u64, 24 | pub num_devices: u64, 25 | pub unused_0: [u64; 4], 26 | pub tree_root_level: u8, 27 | pub chunk_root_level: u8, 28 | pub extent_root_level: u8, 29 | pub fs_root_level: u8, 30 | pub dev_root_level: u8, 31 | pub csum_root_level: u8, 32 | pub unused_1: [u8; 10], 33 | } 34 | 35 | impl Debug for BtrfsRootBackup { 36 | 37 | fn fmt ( 38 | & self, 39 | formatter: & mut Formatter, 40 | ) -> Result <(), FmtError> { 41 | 42 | let mut debug_struct = 43 | formatter.debug_struct ( 44 | "BtrfsRootBackup"); 45 | 46 | debug_struct.field ( 47 | "tree_root", 48 | & NakedString::from ( 49 | format! ( 50 | "0x{:x}", 51 | self.tree_root))); 52 | 53 | debug_struct.field ( 54 | "tree_root_gen", 55 | & self.tree_root_gen); 56 | 57 | debug_struct.field ( 58 | "chunk_root", 59 | & NakedString::from ( 60 | format! ( 61 | "0x{:x}", 62 | self.chunk_root))); 63 | 64 | debug_struct.field ( 65 | "chunk_root_gen", 66 | & self.chunk_root_gen); 67 | 68 | debug_struct.field ( 69 | "extent_root", 70 | & NakedString::from ( 71 | format! ( 72 | "0x{:x}", 73 | self.extent_root))); 74 | 75 | debug_struct.field ( 76 | "extent_root_gen", 77 | & self.extent_root_gen); 78 | 79 | debug_struct.field ( 80 | "fs_root", 81 | & NakedString::from ( 82 | format! ( 83 | "0x{:x}", 84 | self.fs_root))); 85 | 86 | debug_struct.field ( 87 | "fs_root_gen", 88 | & self.fs_root_gen); 89 | 90 | debug_struct.field ( 91 | "dev_root", 92 | & NakedString::from ( 93 | format! ( 94 | "0x{:x}", 95 | self.dev_root))); 96 | 97 | debug_struct.field ( 98 | "dev_root_gen", 99 | & self.dev_root_gen); 100 | 101 | debug_struct.field ( 102 | "csum_root", 103 | & NakedString::from ( 104 | format! ( 105 | "0x{:x}", 106 | self.csum_root))); 107 | 108 | debug_struct.field ( 109 | "csum_root_gen", 110 | & self.csum_root_gen); 111 | 112 | debug_struct.field ( 113 | "total_bytes", 114 | & self.total_bytes); 115 | 116 | debug_struct.field ( 117 | "bytes_used", 118 | & self.bytes_used); 119 | 120 | debug_struct.field ( 121 | "num_devices", 122 | & self.num_devices); 123 | 124 | debug_struct.field ( 125 | "num_devices", 126 | & self.num_devices); 127 | 128 | debug_struct.field ( 129 | "tree_root_level", 130 | & self.tree_root_level); 131 | 132 | debug_struct.field ( 133 | "chunk_root_level", 134 | & self.chunk_root_level); 135 | 136 | debug_struct.field ( 137 | "extent_root_level", 138 | & self.extent_root_level); 139 | 140 | debug_struct.field ( 141 | "fs_root_level", 142 | & self.fs_root_level); 143 | 144 | debug_struct.field ( 145 | "dev_root_level", 146 | & self.dev_root_level); 147 | 148 | debug_struct.field ( 149 | "csum_root_level", 150 | & self.csum_root_level); 151 | 152 | debug_struct.finish () 153 | 154 | } 155 | 156 | } 157 | 158 | #[ cfg (test) ] 159 | mod tests { 160 | 161 | use std::mem; 162 | 163 | use super::*; 164 | 165 | #[ test ] 166 | fn test_size () { 167 | assert! (mem::size_of:: () == 0xa8); 168 | } 169 | 170 | } 171 | 172 | // ex: noet ts=4 filetype=rust 173 | -------------------------------------------------------------------------------- /src/diskformat/core/superblock.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 | pub const BTRFS_MAGIC: [u8; 0x8] = * b"_BHRfS_M"; 8 | 9 | #[ derive (Copy, Clone, Eq, Hash, PartialEq) ] 10 | pub struct BtrfsSuperblock <'a> { 11 | data: & 'a BtrfsSuperblockData, 12 | } 13 | 14 | impl <'a> BtrfsSuperblock <'a> { 15 | 16 | pub fn new ( 17 | data: & 'a BtrfsSuperblockData, 18 | ) -> BtrfsSuperblock <'a> { 19 | 20 | BtrfsSuperblock { 21 | data: data, 22 | } 23 | 24 | } 25 | 26 | pub fn for_bytes ( 27 | bytes: & 'a [u8], 28 | ) -> BtrfsSuperblock <'a> { 29 | 30 | Self::new ( 31 | BtrfsSuperblockData::for_bytes ( 32 | bytes)) 33 | 34 | } 35 | 36 | pub fn system_chunks ( 37 | self, 38 | ) -> BtrfsSuperblockSystemChunks <'a> { 39 | 40 | BtrfsSuperblockSystemChunks::new ( 41 | & self.data, 42 | ) 43 | 44 | } 45 | 46 | pub fn system_logical_to_physical ( 47 | & self, 48 | logical_address: u64, 49 | ) -> Option { 50 | 51 | for system_chunk in self.system_chunks () { 52 | 53 | if logical_address < system_chunk.key ().offset () 54 | || logical_address >= ( 55 | system_chunk.key ().offset () 56 | + system_chunk.data ().chunk_size () 57 | ) { 58 | continue; 59 | } 60 | 61 | let system_chunk_stripe = 62 | system_chunk.stripes () [0]; 63 | 64 | return Some ( 65 | BtrfsPhysicalAddress::new ( 66 | system_chunk_stripe.device_id (), 67 | logical_address 68 | - system_chunk.key ().offset () 69 | + system_chunk_stripe.offset ())); 70 | 71 | } 72 | 73 | None 74 | 75 | } 76 | 77 | pub fn checksum (& self) -> BtrfsChecksum { 78 | self.data.checksum 79 | } 80 | 81 | pub fn fs_uuid (& self) -> BtrfsUuid { 82 | self.data.fs_uuid 83 | } 84 | 85 | pub fn physical_address (& self) -> u64 { 86 | self.data.physical_address 87 | } 88 | 89 | pub fn flags (& self) -> u64 { 90 | self.data.flags 91 | } 92 | 93 | pub fn magic (& self) -> [u8; 0x8] { 94 | self.data.magic 95 | } 96 | 97 | pub fn generation (& self) -> u64 { 98 | self.data.generation 99 | } 100 | 101 | pub fn root_tree_logical_address (& self) -> u64 { 102 | self.data.root_tree_logical_address 103 | } 104 | 105 | pub fn chunk_tree_logical_address (& self) -> u64 { 106 | self.data.chunk_tree_logical_address 107 | } 108 | 109 | pub fn log_tree_logical_address (& self) -> u64 { 110 | self.data.log_tree_logical_address 111 | } 112 | 113 | pub fn log_root_transid (& self) -> u64 { 114 | self.data.log_root_transid 115 | } 116 | 117 | pub fn total_bytes (& self) -> u64 { 118 | self.data.total_bytes 119 | } 120 | 121 | pub fn bytes_used (& self) -> u64 { 122 | self.data.bytes_used 123 | } 124 | 125 | pub fn root_dir_objectid (& self) -> u64 { 126 | self.data.root_dir_objectid 127 | } 128 | 129 | pub fn num_devices (& self) -> u64 { 130 | self.data.num_devices 131 | } 132 | 133 | pub fn sector_size (& self) -> u32 { 134 | self.data.sector_size 135 | } 136 | 137 | pub fn node_size (& self) -> u32 { 138 | self.data.node_size 139 | } 140 | 141 | pub fn leaf_size (& self) -> u32 { 142 | self.data.leaf_size 143 | } 144 | 145 | pub fn stripe_size (& self) -> u32 { 146 | self.data.stripe_size 147 | } 148 | 149 | pub fn system_chunks_size (& self) -> u32 { 150 | self.data.system_chunks_size 151 | } 152 | 153 | pub fn chunk_root_generation (& self) -> u64 { 154 | self.data.chunk_root_generation 155 | } 156 | 157 | pub fn compat_flags (& self) -> u64 { 158 | self.data.compat_flags 159 | } 160 | 161 | pub fn compat_ro_flags (& self) -> u64 { 162 | self.data.compat_ro_flags 163 | } 164 | 165 | pub fn incompat_flags (& self) -> u64 { 166 | self.data.incompat_flags 167 | } 168 | 169 | pub fn csum_type (& self) -> u16 { 170 | self.data.csum_type 171 | } 172 | 173 | pub fn root_level (& self) -> u8 { 174 | self.data.root_level 175 | } 176 | 177 | pub fn chunk_root_level (& self) -> u8 { 178 | self.data.chunk_root_level 179 | } 180 | 181 | pub fn log_root_level (& self) -> u8 { 182 | self.data.log_root_level 183 | } 184 | 185 | pub fn dev_item (& self) -> BtrfsDevItem <'a> { 186 | BtrfsDevItem::new (& self.data.dev_item) 187 | } 188 | 189 | pub fn label (& self) -> & BtrfsLabel { 190 | & self.data.label 191 | } 192 | 193 | pub fn cache_generation (& self) -> u64 { 194 | self.data.cache_generation 195 | } 196 | 197 | pub fn uuid_tree_generation (& self) -> u64 { 198 | self.data.uuid_tree_generation 199 | } 200 | 201 | pub fn root_backups (& self) -> & [BtrfsRootBackup] { 202 | & self.data.root_backups 203 | } 204 | 205 | pub fn device_id (& self) -> u64 { 206 | self.dev_item ().device_id () 207 | } 208 | 209 | } 210 | 211 | impl <'a> Debug for BtrfsSuperblock <'a> { 212 | 213 | fn fmt ( 214 | & self, 215 | formatter: & mut Formatter, 216 | ) -> Result <(), FmtError> { 217 | 218 | let mut debug_struct = 219 | formatter.debug_struct ( 220 | "BtrfsSuperblock"); 221 | 222 | debug_struct.field ( 223 | "checksum", 224 | & self.checksum ()); 225 | 226 | debug_struct.field ( 227 | "fs_uuid", 228 | & self.fs_uuid ()); 229 | 230 | debug_struct.field ( 231 | "physical_address", 232 | & self.physical_address ()); 233 | 234 | debug_struct.field ( 235 | "flags", 236 | & self.flags ()); 237 | 238 | debug_struct.field ( 239 | "magic", 240 | & self.magic ()); 241 | 242 | debug_struct.field ( 243 | "generation", 244 | & self.generation ()); 245 | 246 | debug_struct.field ( 247 | "root_tree_logical_address", 248 | & self.root_tree_logical_address ()); 249 | 250 | debug_struct.field ( 251 | "chunk_tree_logical_address", 252 | & self.chunk_tree_logical_address ()); 253 | 254 | debug_struct.field ( 255 | "log_tree_logical_address", 256 | & self.log_tree_logical_address ()); 257 | 258 | debug_struct.field ( 259 | "log_root_transid", 260 | & self.log_root_transid ()); 261 | 262 | debug_struct.field ( 263 | "total_bytes", 264 | & self.total_bytes ()); 265 | 266 | debug_struct.field ( 267 | "bytes_used", 268 | & self.bytes_used ()); 269 | 270 | debug_struct.field ( 271 | "root_dir_objectid", 272 | & self.root_dir_objectid ()); 273 | 274 | debug_struct.field ( 275 | "num_devices", 276 | & self.num_devices ()); 277 | 278 | debug_struct.field ( 279 | "sector_size", 280 | & self.sector_size ()); 281 | 282 | debug_struct.field ( 283 | "node_size", 284 | & self.node_size ()); 285 | 286 | debug_struct.field ( 287 | "leaf_size", 288 | & self.leaf_size ()); 289 | 290 | debug_struct.field ( 291 | "stipe_size", 292 | & self.stripe_size ()); 293 | 294 | debug_struct.field ( 295 | "system_chunks_size", 296 | & self.system_chunks_size ()); 297 | 298 | debug_struct.field ( 299 | "chunk_root_generation", 300 | & self.chunk_root_generation ()); 301 | 302 | debug_struct.field ( 303 | "compat_flags", 304 | & self.compat_flags ()); 305 | 306 | debug_struct.field ( 307 | "compat_ro_flags", 308 | & self.compat_ro_flags ()); 309 | 310 | debug_struct.field ( 311 | "incompat_flags", 312 | & self.incompat_flags ()); 313 | 314 | debug_struct.field ( 315 | "csum_type", 316 | & self.csum_type ()); 317 | 318 | debug_struct.field ( 319 | "root_level", 320 | & self.root_level ()); 321 | 322 | debug_struct.field ( 323 | "chunk_root_level", 324 | & self.chunk_root_level ()); 325 | 326 | debug_struct.field ( 327 | "log_root_level", 328 | & self.log_root_level ()); 329 | 330 | debug_struct.field ( 331 | "dev_item", 332 | & self.dev_item ()); 333 | 334 | debug_struct.field ( 335 | "label", 336 | & self.label ()); 337 | 338 | debug_struct.field ( 339 | "reserved", 340 | & "TODO".to_string ()); 341 | 342 | debug_struct.field ( 343 | "system_chunks", 344 | & "TODO".to_string ()); 345 | 346 | debug_struct.field ( 347 | "unused", 348 | & "...".to_string ()); 349 | 350 | debug_struct.finish () ?; 351 | 352 | Ok (()) 353 | 354 | } 355 | 356 | } 357 | 358 | // ex: noet ts=4 filetype=rust 359 | -------------------------------------------------------------------------------- /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/diskformat/core/superblock_data.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 | #[ repr (C, packed) ] 9 | #[ derive (Copy, Eq, Hash, PartialEq) ] 10 | pub struct BtrfsSuperblockData { 11 | pub checksum: BtrfsChecksum, 12 | pub fs_uuid: BtrfsUuid, 13 | pub physical_address: u64, 14 | pub flags: u64, 15 | pub magic: [u8; 0x8], 16 | pub generation: u64, 17 | pub root_tree_logical_address: u64, 18 | pub chunk_tree_logical_address: u64, 19 | pub log_tree_logical_address: u64, 20 | pub log_root_transid: u64, 21 | pub total_bytes: u64, 22 | pub bytes_used: u64, 23 | pub root_dir_objectid: u64, 24 | pub num_devices: u64, 25 | pub sector_size: u32, 26 | pub node_size: u32, 27 | pub leaf_size: u32, 28 | pub stripe_size: u32, 29 | pub system_chunks_size: u32, 30 | pub chunk_root_generation: u64, 31 | pub compat_flags: u64, 32 | pub compat_ro_flags: u64, 33 | pub incompat_flags: u64, 34 | pub csum_type: u16, 35 | pub root_level: u8, 36 | pub chunk_root_level: u8, 37 | pub log_root_level: u8, 38 | pub dev_item: BtrfsDevItemData, 39 | pub label: BtrfsLabel, 40 | pub cache_generation: u64, 41 | pub uuid_tree_generation: u64, 42 | pub reserved: BtrfsSuperblockReserved, 43 | pub system_chunks: BtrfsSuperblockSystemChunksData, 44 | pub root_backups: [BtrfsRootBackup; 4], 45 | pub unused: BtrfsSuperblockUnused, 46 | } 47 | 48 | impl BtrfsSuperblockData { 49 | 50 | pub fn for_bytes ( 51 | bytes: & [u8], 52 | ) -> & BtrfsSuperblockData { 53 | 54 | assert! ( 55 | bytes.len () == mem::size_of:: ()); 56 | 57 | let superblock_data: & BtrfsSuperblockData = 58 | unsafe { 59 | mem::transmute ( 60 | & bytes [0]) 61 | }; 62 | 63 | // TODO verify stuff 64 | 65 | superblock_data 66 | 67 | } 68 | 69 | } 70 | 71 | impl Clone for BtrfsSuperblockData { 72 | 73 | fn clone (& self) -> BtrfsSuperblockData { 74 | * self 75 | } 76 | 77 | } 78 | 79 | impl Debug for BtrfsSuperblockData { 80 | 81 | fn fmt ( 82 | & self, 83 | formatter: & mut Formatter, 84 | ) -> Result <(), FmtError> { 85 | 86 | let mut debug_struct = 87 | formatter.debug_struct ( 88 | "BtrfsSuperblockData"); 89 | 90 | debug_struct.field ( 91 | "checksum", 92 | & self.checksum); 93 | 94 | debug_struct.field ( 95 | "fs_uuid", 96 | & self.fs_uuid); 97 | 98 | debug_struct.field ( 99 | "physical_address", 100 | & self.physical_address); 101 | 102 | debug_struct.field ( 103 | "flags", 104 | & self.flags); 105 | 106 | debug_struct.field ( 107 | "magic", 108 | & self.magic); 109 | 110 | debug_struct.field ( 111 | "generation", 112 | & self.generation); 113 | 114 | debug_struct.field ( 115 | "root_tree_logical_address", 116 | & self.root_tree_logical_address); 117 | 118 | debug_struct.field ( 119 | "chunk_tree_logical_address", 120 | & self.chunk_tree_logical_address); 121 | 122 | debug_struct.field ( 123 | "log_tree_logical_address", 124 | & self.log_tree_logical_address); 125 | 126 | debug_struct.field ( 127 | "log_root_transid", 128 | & self.log_root_transid); 129 | 130 | debug_struct.field ( 131 | "total_bytes", 132 | & self.total_bytes); 133 | 134 | debug_struct.field ( 135 | "bytes_used", 136 | & self.bytes_used); 137 | 138 | debug_struct.field ( 139 | "root_dir_objectid", 140 | & self.root_dir_objectid); 141 | 142 | debug_struct.field ( 143 | "num_devices", 144 | & self.num_devices); 145 | 146 | debug_struct.field ( 147 | "sector_size", 148 | & self.sector_size); 149 | 150 | debug_struct.field ( 151 | "node_size", 152 | & self.node_size); 153 | 154 | debug_struct.field ( 155 | "leaf_size", 156 | & self.leaf_size); 157 | 158 | debug_struct.field ( 159 | "stipe_size", 160 | & self.stripe_size); 161 | 162 | debug_struct.field ( 163 | "system_chunks_size", 164 | & self.system_chunks_size); 165 | 166 | debug_struct.field ( 167 | "chunk_root_generation", 168 | & self.chunk_root_generation); 169 | 170 | debug_struct.field ( 171 | "compat_flags", 172 | & self.compat_flags); 173 | 174 | debug_struct.field ( 175 | "compat_ro_flags", 176 | & self.compat_ro_flags); 177 | 178 | debug_struct.field ( 179 | "incompat_flags", 180 | & self.incompat_flags); 181 | 182 | debug_struct.field ( 183 | "csum_type", 184 | & self.csum_type); 185 | 186 | debug_struct.field ( 187 | "root_level", 188 | & self.root_level); 189 | 190 | debug_struct.field ( 191 | "chunk_root_level", 192 | & self.chunk_root_level); 193 | 194 | debug_struct.field ( 195 | "log_root_level", 196 | & self.log_root_level); 197 | 198 | debug_struct.field ( 199 | "dev_item", 200 | & self.dev_item); 201 | 202 | debug_struct.field ( 203 | "label", 204 | & self.label); 205 | 206 | debug_struct.field ( 207 | "reserved", 208 | & "TODO".to_string ()); 209 | 210 | debug_struct.field ( 211 | "system_chunks", 212 | & "TODO".to_string ()); 213 | 214 | debug_struct.field ( 215 | "unused", 216 | & "...".to_string ()); 217 | 218 | debug_struct.finish () ?; 219 | 220 | Ok (()) 221 | 222 | } 223 | 224 | } 225 | 226 | #[ test ] 227 | fn test_size () { 228 | assert! (mem::size_of:: () == 0x1000); 229 | } 230 | 231 | // ex: noet ts=4 filetype=rust 232 | -------------------------------------------------------------------------------- /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_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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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/diskformat/item/dir_index.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, PartialEq) ] 10 | pub struct BtrfsDirIndex <'a> { 11 | header: & 'a BtrfsLeafItemHeader, 12 | data_bytes: & 'a [u8], 13 | } 14 | 15 | impl <'a> BtrfsDirIndex <'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 dir_item = BtrfsDirIndex { 36 | header: header, 37 | data_bytes: data_bytes, 38 | }; 39 | 40 | // sanity check 41 | 42 | if data_bytes.len () != ( 43 | mem::size_of:: () 44 | + dir_item.data_size () as usize 45 | + dir_item.name_size () as usize 46 | ) { 47 | 48 | return Err ( 49 | format! ( 50 | "Must be at least 0x{:x} bytes", 51 | mem::size_of:: () 52 | + dir_item.data_size () as usize 53 | + dir_item.name_size () as usize)); 54 | 55 | } 56 | 57 | // return 58 | 59 | Ok (dir_item) 60 | 61 | } 62 | 63 | pub fn data (& self) -> & BtrfsDirItemData { 64 | 65 | unsafe { 66 | & * ( 67 | self.data_bytes.as_ptr () 68 | as * const BtrfsDirItemData 69 | ) 70 | } 71 | 72 | } 73 | 74 | pub fn child_key (& self) -> BtrfsKey { 75 | self.data ().child_key 76 | } 77 | 78 | pub fn child_object_id (& self) -> u64 { 79 | self.child_key ().object_id () 80 | } 81 | 82 | pub fn transaction_id (& self) -> u64 { 83 | self.data ().transaction_id 84 | } 85 | 86 | pub fn name_size (& self) -> u16 { 87 | self.data ().name_size 88 | } 89 | 90 | pub fn data_size (& self) -> u16 { 91 | self.data ().data_size 92 | } 93 | 94 | pub fn child_type (& self) -> u8 { 95 | self.data ().child_type 96 | } 97 | 98 | pub fn name ( 99 | & 'a self, 100 | ) -> & 'a [u8] { 101 | 102 | & self.data_bytes [ 103 | mem::size_of:: () 104 | .. 105 | mem::size_of:: () 106 | + self.name_size () as usize 107 | ] 108 | 109 | } 110 | 111 | pub fn name_to_string_lossy ( 112 | & self, 113 | ) -> Cow { 114 | 115 | String::from_utf8_lossy ( 116 | self.name ()) 117 | 118 | } 119 | 120 | } 121 | 122 | impl <'a> BtrfsLeafItemContents <'a> for BtrfsDirIndex <'a> { 123 | 124 | fn header (& self) -> & BtrfsLeafItemHeader { 125 | self.header 126 | } 127 | 128 | } 129 | 130 | impl <'a> Debug for BtrfsDirIndex <'a> { 131 | 132 | fn fmt ( 133 | & self, 134 | formatter: & mut Formatter, 135 | ) -> Result <(), FmtError> { 136 | 137 | let mut debug_struct = 138 | formatter.debug_struct ( 139 | "BtrfsDirIndex"); 140 | 141 | debug_struct.field ( 142 | "key", 143 | & NakedString::from ( 144 | self.key ().to_string_decimal ())); 145 | 146 | debug_struct.field ( 147 | "child_key", 148 | & NakedString::from ( 149 | self.child_key ().to_string ())); 150 | 151 | debug_struct.field ( 152 | "transaction_id", 153 | & self.transaction_id ()); 154 | 155 | debug_struct.field ( 156 | "data_size", 157 | & self.data_size ()); 158 | 159 | debug_struct.field ( 160 | "name", 161 | & self.name_to_string_lossy ()); 162 | 163 | debug_struct.field ( 164 | "child_type", 165 | & self.child_type ()); 166 | 167 | debug_struct.finish () 168 | 169 | } 170 | 171 | } 172 | 173 | // ex: noet ts=4 filetype=rust 174 | -------------------------------------------------------------------------------- /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/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/dir_item_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 BtrfsDirItemEntry <'a> { 11 | header: & 'a BtrfsLeafItemHeader, 12 | data_bytes: & 'a [u8], 13 | } 14 | 15 | impl <'a> BtrfsDirItemEntry <'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 dir_item = BtrfsDirItemEntry { 36 | header: header, 37 | data_bytes: data_bytes, 38 | }; 39 | 40 | // sanity check 41 | 42 | if data_bytes.len () != ( 43 | mem::size_of:: () 44 | + dir_item.data_size () as usize 45 | + dir_item.name_size () as usize 46 | ) { 47 | 48 | return Err ( 49 | format! ( 50 | "Must be at least 0x{:x} bytes", 51 | mem::size_of:: () 52 | + dir_item.data_size () as usize 53 | + dir_item.name_size () as usize)); 54 | 55 | } 56 | 57 | // return 58 | 59 | Ok (dir_item) 60 | 61 | } 62 | 63 | pub fn header (& self) -> & BtrfsLeafItemHeader { 64 | self.header 65 | } 66 | 67 | pub fn data (& self) -> & BtrfsDirItemData { 68 | 69 | unsafe { 70 | & * ( 71 | self.data_bytes.as_ptr () 72 | as * const BtrfsDirItemData 73 | ) 74 | } 75 | 76 | } 77 | 78 | pub fn child_key (& self) -> BtrfsKey { 79 | self.data ().child_key 80 | } 81 | 82 | pub fn child_object_id (& self) -> u64 { 83 | self.data ().child_key.object_id () 84 | } 85 | 86 | pub fn transaction_id (& self) -> u64 { 87 | self.data ().transaction_id 88 | } 89 | 90 | pub fn name_size (& self) -> u16 { 91 | self.data ().name_size 92 | } 93 | 94 | pub fn data_size (& self) -> u16 { 95 | self.data ().data_size 96 | } 97 | 98 | pub fn child_type (& self) -> u8 { 99 | self.data ().child_type 100 | } 101 | 102 | pub fn name ( 103 | & 'a self, 104 | ) -> & 'a [u8] { 105 | 106 | & self.data_bytes [ 107 | mem::size_of:: () 108 | .. 109 | mem::size_of:: () 110 | + self.name_size () as usize 111 | ] 112 | 113 | } 114 | 115 | pub fn name_to_string_lossy ( 116 | & self, 117 | ) -> Cow { 118 | 119 | String::from_utf8_lossy ( 120 | self.name ()) 121 | 122 | } 123 | 124 | } 125 | 126 | impl <'a> BtrfsLeafItemContents <'a> for BtrfsDirItemEntry <'a> { 127 | 128 | fn header (& self) -> & BtrfsLeafItemHeader { 129 | self.header 130 | } 131 | 132 | } 133 | 134 | impl <'a> Debug for BtrfsDirItemEntry <'a> { 135 | 136 | fn fmt ( 137 | & self, 138 | formatter: & mut Formatter, 139 | ) -> Result <(), FmtError> { 140 | 141 | let mut debug_struct = 142 | formatter.debug_struct ( 143 | "BtrfsDirItemEntry"); 144 | 145 | self.header ().debug_struct ( 146 | & mut debug_struct); 147 | 148 | debug_struct.field ( 149 | "child_key", 150 | & NakedString::from ( 151 | self.child_key ().to_string ())); 152 | 153 | debug_struct.field ( 154 | "transaction_id", 155 | & self.transaction_id ()); 156 | 157 | debug_struct.field ( 158 | "data_size", 159 | & self.data_size ()); 160 | 161 | debug_struct.field ( 162 | "name", 163 | & self.name_to_string_lossy ()); 164 | 165 | debug_struct.field ( 166 | "child_type", 167 | & self.child_type ()); 168 | 169 | debug_struct.field ( 170 | "data", 171 | & NakedString::from ( 172 | "...")); 173 | 174 | debug_struct.finish () 175 | 176 | } 177 | 178 | } 179 | 180 | // ex: noet ts=4 filetype=rust 181 | -------------------------------------------------------------------------------- /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/item/extent_data.rs: -------------------------------------------------------------------------------- 1 | use super::super::prelude::*; 2 | 3 | #[ derive (Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd) ] 4 | pub struct BtrfsExtentData <'a> { 5 | header: & 'a BtrfsLeafItemHeader, 6 | data_bytes: & 'a [u8], 7 | } 8 | 9 | impl <'a> BtrfsLeafItemContents <'a> for BtrfsExtentData <'a> { 10 | 11 | fn header (& self) -> & BtrfsLeafItemHeader { 12 | self.header 13 | } 14 | 15 | } 16 | 17 | impl <'a> BtrfsExtentData <'a> { 18 | 19 | pub fn from_bytes ( 20 | header: & 'a BtrfsLeafItemHeader, 21 | data_bytes: & 'a [u8], 22 | ) -> Result , String> { 23 | 24 | // create extent data 25 | 26 | let extent_data = 27 | BtrfsExtentData { 28 | header: header, 29 | data_bytes: data_bytes, 30 | }; 31 | 32 | // sanity check 33 | 34 | if extent_data.inline () { 35 | 36 | if data_bytes.len () 37 | < mem::size_of:: () 38 | - mem::size_of:: () * 4 { 39 | 40 | return Err ( 41 | format! ( 42 | "Must be at least 0x{:x} bytes", 43 | mem::size_of:: () 44 | - mem::size_of:: () * 4)); 45 | 46 | } 47 | 48 | } else { 49 | 50 | if data_bytes.len () 51 | != mem::size_of:: () { 52 | 53 | return Err ( 54 | format! ( 55 | "Must be exactly 0x{:x} bytes", 56 | mem::size_of:: ())); 57 | 58 | } 59 | 60 | } 61 | 62 | // return 63 | 64 | Ok (extent_data) 65 | 66 | } 67 | 68 | pub fn offset (& self) -> u64 { 69 | self.header.offset () 70 | } 71 | 72 | pub fn data (& self) -> & BtrfsExtentDataData { 73 | 74 | unsafe { 75 | & * ( 76 | self.data_bytes.as_ptr () 77 | as * const BtrfsExtentDataData 78 | ) 79 | } 80 | 81 | } 82 | 83 | pub fn generation (& self) -> u64 { 84 | self.data ().generation 85 | } 86 | 87 | pub fn logical_data_size (& self) -> u64 { 88 | self.data ().logical_data_size 89 | } 90 | 91 | pub fn compression (& self) -> u8 { 92 | self.data ().compression 93 | } 94 | 95 | pub fn encryption (& self) -> u8 { 96 | self.data ().encryption 97 | } 98 | 99 | pub fn other_encoding (& self) -> u16 { 100 | self.data ().other_encoding 101 | } 102 | 103 | pub fn extent_type (& self) -> u8 { 104 | self.data ().extent_type 105 | } 106 | 107 | pub fn extent_logical_address (& self) -> u64 { 108 | 109 | if self.inline () { 110 | panic! (); 111 | } 112 | 113 | self.data ().extent_logical_address 114 | 115 | } 116 | 117 | pub fn extent_size (& self) -> u64 { 118 | 119 | if self.inline () { 120 | panic! (); 121 | } 122 | 123 | self.data ().extent_size 124 | 125 | } 126 | 127 | pub fn extent_data_offset (& self) -> u64 { 128 | 129 | if self.inline () { 130 | panic! (); 131 | } 132 | 133 | self.data ().extent_data_offset 134 | 135 | } 136 | 137 | pub fn extent_data_size (& self) -> u64 { 138 | 139 | if self.inline () { 140 | panic! (); 141 | } 142 | 143 | self.data ().extent_data_size 144 | 145 | } 146 | 147 | pub fn inline (& self) -> bool { 148 | self.data ().extent_type == BTRFS_EXTENT_DATA_INLINE_TYPE 149 | } 150 | 151 | pub fn regular (& self) -> bool { 152 | self.data ().extent_type == BTRFS_EXTENT_DATA_REGULAR_TYPE 153 | } 154 | 155 | pub fn prealloc (& self) -> bool { 156 | self.data ().extent_type == BTRFS_EXTENT_DATA_PREALLOC_TYPE 157 | } 158 | 159 | pub fn inline_data ( 160 | & self, 161 | ) -> Result