├── .gitignore ├── Cargo.toml ├── LICENSE.md ├── README.md ├── ext2.img ├── rustfmt.toml └── src ├── error.rs ├── fs ├── mod.rs └── sync.rs ├── lib.rs ├── sector.rs ├── sys ├── block_group.rs ├── inode.rs ├── mod.rs └── superblock.rs └── volume ├── mod.rs └── size.rs /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | **/*.rs.bk 4 | Cargo.lock 5 | .*.sw? 6 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ext2" 3 | version = "0.1.0" 4 | authors = ["Szymon Walter "] 5 | 6 | [dependencies] 7 | bitflags = "1.0" 8 | rlibc = { version = "1.0", optional = true } 9 | spin = "0.4" 10 | genfs = "^0.1.4" 11 | 12 | [features] 13 | default = ["no_std"] 14 | no_std = ["rlibc"] 15 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # ext2-rs 2 | ## an ext2 implementation 3 | 4 | Copyright © 2018, Szymon Walter 5 | 6 | This software is provided 'as-is', without any express or implied warranty. 7 | In no event will the authors be held liable for any damages arising from 8 | the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this software must not be misrepresented; you must not 15 | claim that you wrote the original software. If you use this software 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 2. Altered source versions must be plainly marked as such, and must not 19 | be misrepresented as being the original software. 20 | 3. This notice may not be removed or altered from any source distribution. 21 | 22 | #### walter.szymon.98@gmail.com 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ext2-rs 2 | 3 | An OS and architecture independent implementation of ext2 in pure Rust. 4 | -------------------------------------------------------------------------------- /ext2.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pi-pi3/ext2-rs/1fe6dd820b800c35f917b531acc2ca64bf0791d1/ext2.img -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 2 | wrap_comments = true 3 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, Display}; 2 | use alloc::String; 3 | 4 | #[cfg(any(test, not(feature = "no_std")))] 5 | use std::io; 6 | 7 | /// The set of all possible errors 8 | #[derive(Debug)] 9 | pub enum Error { 10 | Other(String), 11 | BadMagic { 12 | magic: u16, 13 | }, 14 | OutOfBounds { 15 | index: usize, 16 | }, 17 | AddressOutOfBounds { 18 | sector: u32, 19 | offset: u32, 20 | size: usize, 21 | }, 22 | BadBlockGroupCount { 23 | by_blocks: u32, 24 | by_inodes: u32, 25 | }, 26 | InodeNotFound { 27 | inode: u32, 28 | }, 29 | NotADirectory { 30 | inode: u32, 31 | name: String, 32 | }, 33 | NotAbsolute { 34 | name: String, 35 | }, 36 | NotFound { 37 | name: String, 38 | }, 39 | #[cfg(any(test, not(feature = "no_std")))] 40 | Io { 41 | inner: io::Error, 42 | }, 43 | } 44 | 45 | impl Display for Error { 46 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 47 | match *self { 48 | Error::Other(ref msg) => write!(f, "{}", msg), 49 | Error::BadMagic { 50 | magic, 51 | } => write!(f, "invalid magic value: {}", magic), 52 | Error::OutOfBounds { 53 | index, 54 | } => write!(f, "index ouf of bounds: {}", index), 55 | Error::AddressOutOfBounds { 56 | sector, 57 | offset, 58 | size, 59 | } => write!(f, "address ouf of bounds: {}:{} with a block size of: {}", 60 | sector, offset, size), 61 | Error::BadBlockGroupCount { 62 | by_blocks, 63 | by_inodes, 64 | } => write!(f, "conflicting block group count data; by blocks: {}, by inodes: {}", by_blocks, by_inodes), 65 | Error::InodeNotFound { 66 | inode, 67 | } => write!(f, "couldn't find inode no. {}", &inode), 68 | Error::NotADirectory { 69 | inode, 70 | ref name, 71 | } => write!(f, "inode no. {} at: {} is not a directory", inode, &name), 72 | Error::NotAbsolute { 73 | ref name, 74 | } => write!(f, "{} is not an absolute path", &name), 75 | Error::NotFound { 76 | ref name, 77 | } => write!(f, "couldn't find {}", &name), 78 | #[cfg(any(test, not(feature = "no_std")))] 79 | Error::Io { 80 | ref inner, 81 | } => write!(f, "io error: {}", inner), 82 | } 83 | } 84 | } 85 | 86 | impl From for Error { 87 | fn from(_: Infallible) -> Error { 88 | unreachable!() 89 | } 90 | } 91 | 92 | #[cfg(any(test, not(feature = "no_std")))] 93 | impl From for Error { 94 | fn from(inner: io::Error) -> Error { 95 | Error::Io { inner } 96 | } 97 | } 98 | 99 | pub enum Infallible {} 100 | -------------------------------------------------------------------------------- /src/fs/mod.rs: -------------------------------------------------------------------------------- 1 | use core::mem; 2 | 3 | use alloc::Vec; 4 | 5 | use error::Error; 6 | use sector::{Address, SectorSize}; 7 | use volume::Volume; 8 | use sys::superblock::Superblock; 9 | use sys::block_group::BlockGroupDescriptor; 10 | use sys::inode::Inode as RawInode; 11 | 12 | pub mod sync; 13 | 14 | pub(crate) struct Struct { 15 | pub inner: T, 16 | pub offset: Address, 17 | } 18 | 19 | impl From<(T, Address)> for Struct { 20 | #[inline] 21 | fn from((inner, offset): (T, Address)) -> Struct { 22 | Struct { inner, offset } 23 | } 24 | } 25 | 26 | /// Safe wrapper for raw sys structs 27 | pub struct Ext2> { 28 | // TODO: should this have some different vis? 29 | pub(crate) volume: V, 30 | pub(crate) superblock: Struct, 31 | pub(crate) block_groups: Struct, S>, 32 | } 33 | 34 | impl> Ext2 { 35 | pub fn new(volume: V) -> Result, Error> { 36 | let superblock = unsafe { Struct::from(Superblock::find(&volume)?) }; 37 | let block_groups_offset = Address::with_block_size( 38 | superblock.inner.first_data_block + 1, 39 | 0, 40 | superblock.inner.log_block_size + 10, 41 | ); 42 | let block_groups_count = superblock 43 | .inner 44 | .block_group_count() 45 | .map(|count| count as usize) 46 | .map_err(|(a, b)| Error::BadBlockGroupCount { 47 | by_blocks: a, 48 | by_inodes: b, 49 | })?; 50 | let block_groups = unsafe { 51 | BlockGroupDescriptor::find_descriptor_table( 52 | &volume, 53 | block_groups_offset, 54 | block_groups_count, 55 | )? 56 | }; 57 | let block_groups = Struct::from(block_groups); 58 | Ok(Ext2 { 59 | volume, 60 | superblock, 61 | block_groups, 62 | }) 63 | } 64 | 65 | pub fn version(&self) -> (u32, u16) { 66 | ( 67 | self.superblock.inner.rev_major, 68 | self.superblock.inner.rev_minor, 69 | ) 70 | } 71 | 72 | pub fn inode_size(&self) -> usize { 73 | if self.version().0 == 0 { 74 | mem::size_of::() 75 | } else { 76 | // note: inodes bigger than 128 are not supported 77 | self.superblock.inner.inode_size as usize 78 | } 79 | } 80 | 81 | pub fn inodes_count(&self) -> usize { 82 | self.superblock.inner.inodes_per_group as _ 83 | } 84 | 85 | pub fn total_inodes_count(&self) -> usize { 86 | self.superblock.inner.inodes_count as _ 87 | } 88 | 89 | pub fn block_group_count(&self) -> Result { 90 | self.superblock 91 | .inner 92 | .block_group_count() 93 | .map(|count| count as usize) 94 | .map_err(|(a, b)| Error::BadBlockGroupCount { 95 | by_blocks: a, 96 | by_inodes: b, 97 | }) 98 | } 99 | 100 | pub fn total_block_count(&self) -> usize { 101 | self.superblock.inner.blocks_count as _ 102 | } 103 | 104 | pub fn free_block_count(&self) -> usize { 105 | self.superblock.inner.free_blocks_count as _ 106 | } 107 | 108 | pub fn block_size(&self) -> usize { 109 | self.superblock.inner.block_size() 110 | } 111 | 112 | pub fn log_block_size(&self) -> u32 { 113 | self.superblock.inner.log_block_size + 10 114 | } 115 | 116 | pub fn sector_size(&self) -> usize { 117 | S::SIZE 118 | } 119 | 120 | pub fn log_sector_size(&self) -> u32 { 121 | S::LOG_SIZE 122 | } 123 | } 124 | 125 | #[cfg(test)] 126 | mod tests { 127 | use std::fs::File; 128 | use std::cell::RefCell; 129 | 130 | use sector::{Address, Size512}; 131 | use volume::Volume; 132 | 133 | use super::Ext2; 134 | 135 | #[test] 136 | fn file_len() { 137 | let file = RefCell::new(File::open("ext2.img").unwrap()); 138 | assert_eq!( 139 | Address::::from(2048_u64) 140 | - Address::::from(1024_u64), 141 | Address::::new(2, 0) 142 | ); 143 | assert_eq!( 144 | unsafe { 145 | file.slice_unchecked( 146 | Address::::from(1024_u64) 147 | ..Address::::from(2048_u64), 148 | ).len() 149 | }, 150 | 1024 151 | ); 152 | } 153 | 154 | #[test] 155 | fn file() { 156 | let file = RefCell::new(File::open("ext2.img").unwrap()); 157 | let fs = Ext2::::new(file); 158 | 159 | assert!( 160 | fs.is_ok(), 161 | "Err({:?})", 162 | fs.err().unwrap_or_else(|| unreachable!()), 163 | ); 164 | 165 | let fs = fs.unwrap(); 166 | 167 | let vers = fs.version(); 168 | println!("version: {}.{}", vers.0, vers.1); 169 | assert_eq!(128, fs.inode_size()); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/fs/sync.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, Debug}; 2 | use core::nonzero::NonZero; 3 | use core::iter::Iterator; 4 | 5 | use alloc::{String, Vec}; 6 | use alloc::arc::Arc; 7 | 8 | use spin::{Mutex, MutexGuard}; 9 | use genfs::*; 10 | 11 | use error::Error; 12 | use sector::{Address, SectorSize}; 13 | use volume::Volume; 14 | use sys::inode::Inode as RawInode; 15 | 16 | use super::Ext2; 17 | 18 | pub struct Synced { 19 | inner: Arc>, 20 | } 21 | 22 | impl Synced { 23 | pub fn with_inner(inner: T) -> Synced { 24 | Synced { 25 | inner: Arc::new(Mutex::new(inner)), 26 | } 27 | } 28 | 29 | pub fn inner<'a>(&'a self) -> MutexGuard<'a, T> { 30 | self.inner.lock() 31 | } 32 | } 33 | 34 | impl Clone for Synced { 35 | fn clone(&self) -> Self { 36 | Synced { 37 | inner: self.inner.clone(), 38 | } 39 | } 40 | } 41 | 42 | impl> Synced> { 43 | pub fn new(volume: V) -> Result>, Error> { 44 | Ext2::new(volume).map(|inner| Synced::with_inner(inner)) 45 | } 46 | 47 | pub fn root_inode(&self) -> Inode { 48 | self.inode_nth(2).unwrap() 49 | } 50 | 51 | pub fn inode_nth(&self, index: usize) -> Option> { 52 | self.inodes_nth(index).next() 53 | } 54 | 55 | pub fn inodes(&self) -> Inodes { 56 | self.inodes_nth(1) 57 | } 58 | 59 | pub fn inodes_nth(&self, index: usize) -> Inodes { 60 | assert!(index > 0, "inodes are 1-indexed"); 61 | let inner = self.inner(); 62 | Inodes { 63 | fs: self.clone(), 64 | log_block_size: inner.log_block_size(), 65 | inode_size: inner.inode_size(), 66 | inodes_per_group: inner.inodes_count(), 67 | inodes_count: inner.total_inodes_count(), 68 | index, 69 | } 70 | } 71 | 72 | pub fn sector_size(&self) -> usize { 73 | S::SIZE 74 | } 75 | 76 | pub fn log_sector_size(&self) -> u32 { 77 | S::LOG_SIZE 78 | } 79 | } 80 | 81 | impl> Fs for Synced> { 82 | type Path = [u8]; 83 | type PathOwned = Vec; 84 | type File = Inode; 85 | type Dir = Directory; 86 | type DirEntry = DirectoryEntry; 87 | type Metadata = (); // TODO 88 | type Permissions = (); // TODO 89 | type Error = Error; 90 | 91 | fn open( 92 | &self, 93 | abs_path: &Self::Path, 94 | _options: &OpenOptions, 95 | ) -> Result { 96 | fn inner<'a, S, V, I>( 97 | fs: &Synced>, 98 | inode: Inode, 99 | mut path: I, 100 | abs_path: &[u8], 101 | ) -> Result, Error> 102 | where 103 | S: SectorSize, 104 | V: Volume, 105 | I: Iterator, 106 | { 107 | let name = match path.next() { 108 | Some(name) => name, 109 | None => return Ok(inode), 110 | }; 111 | 112 | let mut dir = 113 | inode.directory().ok_or_else(|| Error::NotADirectory { 114 | inode: inode.num, 115 | name: String::from_utf8_lossy(abs_path).into_owned(), 116 | })?; 117 | 118 | let entry = dir.find(|entry| { 119 | entry.is_err() || entry.as_ref().unwrap().name == name 120 | }).ok_or_else(|| Error::NotFound { 121 | name: String::from_utf8_lossy(abs_path).into_owned(), 122 | })??; 123 | 124 | let inode = fs.inode_nth(entry.inode) 125 | .ok_or(Error::InodeNotFound { inode: inode.num })?; 126 | 127 | inner(fs, inode, path, abs_path) 128 | } 129 | 130 | if abs_path.len() == 0 || abs_path[0] != b'/' { 131 | return Err(Error::NotAbsolute { 132 | name: String::from_utf8_lossy(abs_path).into_owned(), 133 | }); 134 | } 135 | 136 | if abs_path == b"/" { 137 | return Ok(self.root_inode()); 138 | } 139 | 140 | let mut path = abs_path.split(|byte| *byte == b'/'); 141 | path.next(); 142 | let root = self.root_inode(); 143 | 144 | inner(self, root, path, abs_path) 145 | } 146 | 147 | fn remove_file(&mut self, _path: &Self::Path) -> Result<(), Self::Error> { 148 | unimplemented!() 149 | } 150 | 151 | fn metadata( 152 | &self, 153 | _path: &Self::Path, 154 | ) -> Result { 155 | unimplemented!() 156 | } 157 | 158 | fn symlink_metadata( 159 | &self, 160 | _path: &Self::Path, 161 | ) -> Result { 162 | unimplemented!() 163 | } 164 | 165 | fn rename( 166 | &mut self, 167 | _from: &Self::Path, 168 | _to: &Self::Path, 169 | ) -> Result<(), Self::Error> { 170 | unimplemented!() 171 | } 172 | 173 | fn copy( 174 | &mut self, 175 | _from: &Self::Path, 176 | _to: &Self::Path, 177 | ) -> Result { 178 | unimplemented!() 179 | } 180 | 181 | fn hard_link( 182 | &mut self, 183 | _src: &Self::Path, 184 | _dst: &Self::Path, 185 | ) -> Result<(), Self::Error> { 186 | unimplemented!() 187 | } 188 | 189 | fn symlink( 190 | &mut self, 191 | _src: &Self::Path, 192 | _dst: &Self::Path, 193 | ) -> Result<(), Self::Error> { 194 | unimplemented!() 195 | } 196 | 197 | fn read_link( 198 | &self, 199 | _path: &Self::Path, 200 | ) -> Result { 201 | unimplemented!() 202 | } 203 | 204 | fn canonicalize( 205 | &self, 206 | _path: &Self::Path, 207 | ) -> Result { 208 | unimplemented!() 209 | } 210 | 211 | fn create_dir( 212 | &mut self, 213 | _path: &Self::Path, 214 | _options: &DirOptions, 215 | ) -> Result<(), Self::Error> { 216 | unimplemented!() 217 | } 218 | 219 | fn remove_dir(&mut self, _path: &Self::Path) -> Result<(), Self::Error> { 220 | unimplemented!() 221 | } 222 | 223 | fn remove_dir_all( 224 | &mut self, 225 | _path: &Self::Path, 226 | ) -> Result<(), Self::Error> { 227 | unimplemented!() 228 | } 229 | 230 | fn read_dir(&self, path: &Self::Path) -> Result { 231 | let inode = self.open(path, OpenOptions::new().read(true))?; 232 | inode.directory().ok_or(Error::NotADirectory { 233 | inode: inode.num, 234 | name: String::from_utf8_lossy(path).into_owned(), 235 | }) 236 | } 237 | 238 | fn set_permissions( 239 | &mut self, 240 | _path: &Self::Path, 241 | _perm: Self::Permissions, 242 | ) -> Result<(), Self::Error> { 243 | unimplemented!() 244 | } 245 | } 246 | 247 | impl> Debug for Synced> { 248 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 249 | write!(f, "Synced>", S::SIZE) 250 | } 251 | } 252 | 253 | #[derive(Debug, Clone)] 254 | pub struct Inodes> { 255 | fs: Synced>, 256 | log_block_size: u32, 257 | inode_size: usize, 258 | inodes_per_group: usize, 259 | inodes_count: usize, 260 | index: usize, 261 | } 262 | 263 | impl> Iterator for Inodes { 264 | type Item = Inode; 265 | 266 | fn next(&mut self) -> Option { 267 | if self.index < self.inodes_count { 268 | let block_group = (self.index - 1) / self.inodes_per_group; 269 | let index = (self.index - 1) % self.inodes_per_group; 270 | self.index += 1; 271 | 272 | let fs = self.fs.inner(); 273 | 274 | let inodes_block = 275 | fs.block_groups.inner[block_group].inode_table_block; 276 | 277 | let offset = Address::with_block_size( 278 | inodes_block, 279 | (index * self.inode_size) as i32, 280 | self.log_block_size, 281 | ); 282 | let raw = unsafe { 283 | RawInode::find_inode(&fs.volume, offset, self.inode_size).ok() 284 | }; 285 | raw.map(|(raw, offset)| { 286 | Inode::new( 287 | self.fs.clone(), 288 | raw, 289 | offset, 290 | (self.index - 1) as u32, 291 | ) 292 | }) 293 | } else { 294 | None 295 | } 296 | } 297 | } 298 | 299 | #[derive(Debug)] 300 | pub struct Inode> { 301 | fs: Synced>, 302 | inner: RawInode, 303 | addr: Address, 304 | num: u32, 305 | } 306 | 307 | impl> Clone for Inode { 308 | fn clone(&self) -> Self { 309 | Inode { 310 | fs: self.fs.clone(), 311 | inner: self.inner, 312 | addr: self.addr, 313 | num: self.num, 314 | } 315 | } 316 | } 317 | 318 | impl> Inode { 319 | pub fn new( 320 | fs: Synced>, 321 | inner: RawInode, 322 | addr: Address, 323 | num: u32, 324 | ) -> Inode { 325 | Inode { 326 | fs, 327 | inner, 328 | addr, 329 | num, 330 | } 331 | } 332 | 333 | pub fn read_to_end(&self, buf: &mut Vec) -> Result { 334 | let total_size = self.size(); 335 | let capacity = buf.capacity(); 336 | if capacity < total_size { 337 | buf.reserve_exact(total_size - capacity); 338 | } 339 | unsafe { 340 | buf.set_len(total_size); 341 | } 342 | let size = self.read(&mut buf[..]); 343 | size.and_then(|size| { 344 | unsafe { 345 | buf.set_len(size); 346 | } 347 | Ok(size) 348 | }).or_else(|err| { 349 | unsafe { 350 | buf.set_len(0); 351 | } 352 | Err(err) 353 | }) 354 | } 355 | 356 | pub fn blocks(&self) -> InodeBlocks { 357 | InodeBlocks { 358 | inode: self.clone(), 359 | index: 0, 360 | } 361 | } 362 | 363 | pub fn directory(&self) -> Option> { 364 | if self.is_dir() { 365 | Some(Directory { 366 | blocks: self.blocks(), 367 | offset: 0, 368 | buffer: None, 369 | block_size: { 370 | let fs = self.fs.inner(); 371 | fs.block_size() 372 | }, 373 | }) 374 | } else { 375 | None 376 | } 377 | } 378 | 379 | pub fn is_dir(&self) -> bool { 380 | use sys::inode::TypePerm; 381 | unsafe { self.inner.type_perm.contains(TypePerm::DIRECTORY) } 382 | } 383 | 384 | pub fn block(&self, index: usize) -> Option> { 385 | self.try_block(index).ok().and_then(|block| block) 386 | } 387 | 388 | pub fn try_block( 389 | &self, 390 | mut index: usize, 391 | ) -> Result>, Error> { 392 | // number of blocks in direct table: 12 393 | // number of blocks in indirect table: block_size/4 394 | // why? 395 | // - a block is n bytes long 396 | // - a block address occupies 32 bits, or 4 bytes 397 | // - thus, n/4 398 | // number of blocks in doubly table: (block_size/4)^2 399 | // why? 400 | // - every entry in the doubly table points to another block 401 | // - that's n/4 blocks, where n is the block size 402 | // - every block contains n/4 block pointers 403 | // - that's n/4 blocks with n/4 pointers each = (n/4)^2 404 | // number of blocks in triply table: (block_size/4)^3 405 | 406 | fn block_index>( 407 | volume: &V, 408 | block: u32, 409 | index: usize, 410 | log_block_size: u32, 411 | ) -> Result>, Error> { 412 | let offset = (index * 4) as i32; 413 | let end = offset + 4; 414 | let addr = Address::with_block_size(block, offset, log_block_size); 415 | let end = Address::with_block_size(block, end, log_block_size); 416 | let block = volume.slice(addr..end); 417 | match block { 418 | Ok(block) => unsafe { 419 | Ok(NonZero::new(block.dynamic_cast::().0)) 420 | }, 421 | Err(err) => Err(err.into()), 422 | } 423 | } 424 | 425 | let fs = self.fs.inner(); 426 | 427 | let bs4 = fs.block_size() / 4; 428 | let log_block_size = fs.log_block_size(); 429 | 430 | if index < 12 { 431 | return Ok(NonZero::new(self.inner.direct_pointer[index])); 432 | } 433 | 434 | index -= 12; 435 | 436 | if index < bs4 { 437 | let block = self.inner.indirect_pointer; 438 | return block_index(&fs.volume, block, index, log_block_size); 439 | } 440 | 441 | index -= bs4; 442 | 443 | if index < bs4 * bs4 { 444 | let indirect_index = index >> (log_block_size + 2); 445 | let block = match block_index( 446 | &fs.volume, 447 | self.inner.doubly_indirect, 448 | indirect_index, 449 | log_block_size, 450 | ) { 451 | Ok(Some(block)) => block.get(), 452 | Ok(None) => return Ok(None), 453 | Err(err) => return Err(err), 454 | }; 455 | return block_index( 456 | &fs.volume, 457 | block, 458 | index & (bs4 - 1), 459 | log_block_size, 460 | ); 461 | } 462 | 463 | index -= bs4 * bs4; 464 | 465 | if index < bs4 * bs4 * bs4 { 466 | let doubly_index = index >> (2 * log_block_size + 4); 467 | let indirect = match block_index( 468 | &fs.volume, 469 | self.inner.triply_indirect, 470 | doubly_index, 471 | log_block_size, 472 | ) { 473 | Ok(Some(block)) => block.get(), 474 | Ok(None) => return Ok(None), 475 | Err(err) => return Err(err), 476 | }; 477 | let indirect_index = (index >> (log_block_size + 2)) & (bs4 - 1); 478 | let block = match block_index( 479 | &fs.volume, 480 | indirect as u32, 481 | indirect_index, 482 | log_block_size, 483 | ) { 484 | Ok(Some(block)) => block.get(), 485 | Ok(None) => return Ok(None), 486 | Err(err) => return Err(err), 487 | }; 488 | return block_index( 489 | &fs.volume, 490 | block, 491 | index & (bs4 - 1), 492 | log_block_size, 493 | ); 494 | } 495 | 496 | Ok(None) 497 | } 498 | 499 | pub fn in_use(&self) -> bool { 500 | self.inner.hard_links > 0 501 | } 502 | 503 | pub fn uid(&self) -> u16 { 504 | self.inner.uid 505 | } 506 | 507 | pub fn sectors(&self) -> usize { 508 | self.inner.sectors_count as usize 509 | } 510 | 511 | pub fn size32(&self) -> u32 { 512 | self.inner.size_low 513 | } 514 | 515 | pub fn size64(&self) -> u64 { 516 | self.inner.size_low as u64 | (self.inner.size_high as u64) << 32 517 | } 518 | 519 | #[cfg(target_pointer_width = "64")] 520 | #[inline] 521 | pub fn size(&self) -> usize { 522 | self.size64() as usize 523 | } 524 | 525 | #[cfg(target_pointer_width = "32")] 526 | #[inline] 527 | pub fn size(&self) -> usize { 528 | self.size32() as usize 529 | } 530 | } 531 | 532 | impl> File for Inode { 533 | type Error = Error; 534 | 535 | fn read(&self, buf: &mut [u8]) -> Result { 536 | let total_size = self.size(); 537 | let block_size = { 538 | let fs = self.fs.inner(); 539 | fs.block_size() 540 | }; 541 | let mut offset = 0; 542 | 543 | for block in self.blocks() { 544 | match block { 545 | Ok((data, _)) => { 546 | let data_size = block_size 547 | .min(total_size - offset) 548 | .min(buf.len() - offset); 549 | let end = offset + data_size; 550 | buf[offset..end].copy_from_slice(&data[..data_size]); 551 | offset += data_size; 552 | } 553 | Err(err) => return Err(err.into()), 554 | } 555 | } 556 | 557 | Ok(offset) 558 | } 559 | 560 | fn write(&mut self, _buf: &[u8]) -> Result { 561 | unimplemented!() 562 | } 563 | 564 | fn flush(&mut self) -> Result<(), Self::Error> { 565 | unimplemented!() 566 | } 567 | 568 | fn seek(&mut self, _pos: SeekFrom) -> Result { 569 | unimplemented!() 570 | } 571 | } 572 | 573 | #[derive(Debug, Clone)] 574 | pub struct InodeBlocks> { 575 | inode: Inode, 576 | index: usize, 577 | } 578 | 579 | impl> Iterator for InodeBlocks { 580 | type Item = Result<(Vec, Address), Error>; 581 | 582 | fn next(&mut self) -> Option { 583 | let block = self.inode.try_block(self.index); 584 | let block = match block { 585 | Ok(Some(ok)) => ok, 586 | Ok(None) => return None, 587 | Err(err) => return Some(Err(err)), 588 | }; 589 | 590 | self.index += 1; 591 | let fs = self.inode.fs.inner(); 592 | 593 | let block = block.get(); 594 | let log_block_size = fs.log_block_size(); 595 | let offset = Address::with_block_size(block, 0, log_block_size); 596 | let end = Address::with_block_size(block + 1, 0, log_block_size); 597 | 598 | let slice = fs.volume 599 | .slice(offset..end) 600 | .map(|slice| (slice.to_vec(), offset)) 601 | .map_err(|err| err.into()); 602 | Some(slice) 603 | } 604 | } 605 | 606 | #[derive(Debug, Clone)] 607 | pub struct Directory> { 608 | blocks: InodeBlocks, 609 | offset: usize, 610 | buffer: Option>, 611 | block_size: usize, 612 | } 613 | 614 | impl> Dir 615 | for Directory 616 | { 617 | } 618 | 619 | impl> Iterator for Directory { 620 | type Item = Result; 621 | 622 | fn next(&mut self) -> Option { 623 | if self.buffer.is_none() || self.offset >= self.block_size { 624 | self.buffer = match self.blocks.next() { 625 | None => return None, 626 | Some(Ok((block, _))) => Some(block), 627 | Some(Err(err)) => return Some(Err(err)), 628 | }; 629 | 630 | self.offset = 0; 631 | } 632 | 633 | let buffer = &self.buffer.as_ref().unwrap()[self.offset..]; 634 | 635 | let inode = buffer[0] as u32 | (buffer[1] as u32) << 8 636 | | (buffer[2] as u32) << 16 637 | | (buffer[3] as u32) << 24; 638 | if inode == 0 { 639 | return None; 640 | } 641 | 642 | let size = buffer[4] as u16 | (buffer[5] as u16) << 8; 643 | let len = buffer[6]; 644 | let ty = buffer[7]; 645 | 646 | let name = buffer[8..8 + len as usize].to_vec(); 647 | 648 | self.offset += size as usize; 649 | 650 | Some(Ok(DirectoryEntry { 651 | name: name, 652 | inode: inode as usize, 653 | ty: ty, 654 | })) 655 | } 656 | } 657 | 658 | #[derive(Clone)] 659 | pub struct DirectoryEntry { 660 | pub name: Vec, 661 | pub inode: usize, 662 | pub ty: u8, 663 | } 664 | 665 | impl DirEntry for DirectoryEntry { 666 | type Path = [u8]; 667 | type PathOwned = Vec; 668 | type Metadata = (); // TODO 669 | type FileType = u8; // TODO: enum FileType 670 | type Error = Error; 671 | 672 | fn path(&self) -> Self::PathOwned { 673 | unimplemented!() 674 | } 675 | 676 | fn metadata(&self) -> Result { 677 | unimplemented!() 678 | } 679 | 680 | fn file_type(&self) -> Result { 681 | Ok(self.ty) 682 | } 683 | 684 | fn file_name(&self) -> &Self::Path { 685 | &self.name 686 | } 687 | } 688 | 689 | #[cfg(test)] 690 | mod tests { 691 | use std::fs::File; 692 | use std::cell::RefCell; 693 | 694 | use genfs::{File as GenFile, Fs, OpenOptions}; 695 | 696 | use sector::{SectorSize, Size512}; 697 | use volume::Volume; 698 | 699 | use super::{Ext2, Inode, Synced}; 700 | 701 | #[test] 702 | fn file() { 703 | let file = RefCell::new(File::open("ext2.img").unwrap()); 704 | let fs = Synced::>::new(file); 705 | 706 | assert!( 707 | fs.is_ok(), 708 | "Err({:?})", 709 | fs.err().unwrap_or_else(|| unreachable!()), 710 | ); 711 | 712 | let fs = fs.unwrap(); 713 | let inner = fs.inner(); 714 | 715 | let vers = inner.version(); 716 | println!("version: {}.{}", vers.0, vers.1); 717 | assert_eq!(128, inner.inode_size()); 718 | } 719 | 720 | #[test] 721 | fn inodes() { 722 | let file = RefCell::new(File::open("ext2.img").unwrap()); 723 | let fs = Synced::>::new(file); 724 | 725 | assert!( 726 | fs.is_ok(), 727 | "Err({:?})", 728 | fs.err().unwrap_or_else(|| unreachable!()), 729 | ); 730 | 731 | let fs = fs.unwrap(); 732 | 733 | let inodes = fs.inodes().filter(|inode| inode.in_use()); 734 | for inode in inodes { 735 | println!("{:?}", inode); 736 | } 737 | } 738 | 739 | #[test] 740 | fn inode_blocks() { 741 | use std::str; 742 | let file = RefCell::new(File::open("ext2.img").unwrap()); 743 | let fs = Synced::>::new(file).unwrap(); 744 | 745 | let inodes = fs.inodes().filter(|inode| { 746 | inode.in_use() && inode.uid() == 1000 && inode.size() < 1024 747 | }); 748 | for inode in inodes { 749 | println!("{:?}", inode); 750 | let size = inode.size(); 751 | for block in inode.blocks() { 752 | let (data, _) = block.unwrap(); 753 | assert_eq!(data.len(), { 754 | let fs = fs.inner(); 755 | fs.block_size() 756 | }); 757 | println!("{:?}", &data[..size]); 758 | let _ = str::from_utf8(&data[..size]) 759 | .map(|string| println!("{}", string)); 760 | } 761 | } 762 | } 763 | 764 | #[test] 765 | fn read_inode() { 766 | let file = RefCell::new(File::open("ext2.img").unwrap()); 767 | let fs = Synced::>::new(file).unwrap(); 768 | 769 | let inodes = fs.inodes().filter(|inode| { 770 | inode.in_use() && inode.uid() == 1000 && inode.size() < 1024 771 | }); 772 | for inode in inodes { 773 | let mut buf = Vec::with_capacity(inode.size()); 774 | unsafe { 775 | buf.set_len(inode.size()); 776 | } 777 | let size = inode.read(&mut buf[..]); 778 | assert!(size.is_ok()); 779 | let size = size.unwrap(); 780 | assert_eq!(size, inode.size()); 781 | unsafe { 782 | buf.set_len(size); 783 | } 784 | } 785 | } 786 | 787 | #[test] 788 | fn read_big() { 789 | let file = RefCell::new(File::open("ext2.img").unwrap()); 790 | let fs = Synced::>::new(file).unwrap(); 791 | 792 | let inodes = fs.inodes().filter(|inode| { 793 | inode.in_use() && inode.uid() == 1000 && inode.size() == 537600 794 | }); 795 | for inode in inodes { 796 | let mut buf = Vec::with_capacity(inode.size()); 797 | unsafe { 798 | buf.set_len(inode.size()); 799 | } 800 | let size = inode.read(&mut buf[..]); 801 | assert!(size.is_ok()); 802 | let size = size.unwrap(); 803 | assert_eq!(size, inode.size()); 804 | unsafe { 805 | buf.set_len(size); 806 | } 807 | 808 | for (i, &x) in buf.iter().enumerate() { 809 | if i & 1 == 0 { 810 | assert_eq!(x, b'u', "{}", i); 811 | } else { 812 | assert_eq!(x, b'\n', "{}", i); 813 | } 814 | } 815 | } 816 | } 817 | 818 | #[test] 819 | fn walkdir() { 820 | use std::str; 821 | 822 | fn walk<'vol, S: SectorSize, V: Volume>( 823 | fs: &'vol Synced>, 824 | inode: Inode, 825 | name: String, 826 | ) { 827 | inode.directory().map(|dir| { 828 | for entry in dir { 829 | assert!(entry.is_ok()); 830 | let entry = entry.unwrap(); 831 | let entry_name = str::from_utf8(&entry.name).unwrap_or("?"); 832 | println!("{}/{} => {}", name, entry_name, entry.inode,); 833 | if entry_name != "." && entry_name != ".." { 834 | walk( 835 | fs, 836 | fs.inode_nth(entry.inode).unwrap(), 837 | format!("{}/{}", name, entry_name), 838 | ); 839 | } 840 | } 841 | }); 842 | } 843 | 844 | let file = RefCell::new(File::open("ext2.img").unwrap()); 845 | let fs = Synced::>::new(file).unwrap(); 846 | 847 | let root = fs.root_inode(); 848 | walk(&fs, root, String::new()); 849 | } 850 | 851 | #[test] 852 | fn find() { 853 | use std::str; 854 | let file = RefCell::new(File::open("ext2.img").unwrap()); 855 | let fs = Synced::>::new(file).unwrap(); 856 | 857 | let found = fs.open(b"/home/funky/README.md", &OpenOptions::new()); 858 | 859 | assert!(found.is_ok()); 860 | let inode = found.unwrap(); 861 | let mut vec = Vec::new(); 862 | assert!(inode.read_to_end(&mut vec).is_ok()); 863 | println!("{}", str::from_utf8(&vec).unwrap()); 864 | } 865 | } 866 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(alloc)] 2 | #![feature(specialization)] 3 | #![feature(swap_with_slice)] 4 | #![feature(macro_lifetime_matcher)] 5 | #![feature(const_fn)] 6 | #![feature(step_trait)] 7 | #![feature(nonzero)] 8 | #![feature(associated_type_defaults)] 9 | #![cfg_attr(all(not(test), feature = "no_std"), no_std)] 10 | 11 | #[macro_use] 12 | extern crate alloc; 13 | #[macro_use] 14 | extern crate bitflags; 15 | extern crate genfs; 16 | extern crate spin; 17 | 18 | #[cfg(any(test, not(feature = "no_std")))] 19 | extern crate core; 20 | 21 | pub mod error; 22 | pub mod sys; 23 | pub mod sector; 24 | pub mod volume; 25 | pub mod fs; 26 | 27 | #[cfg(test)] 28 | mod tests { 29 | use sys::superblock::*; 30 | use sys::block_group::*; 31 | use sys::inode::*; 32 | 33 | #[test] 34 | fn sizes() { 35 | use std::mem::size_of; 36 | assert_eq!(size_of::(), 1024); 37 | assert_eq!(size_of::(), 32); 38 | assert_eq!(size_of::(), 128); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/sector.rs: -------------------------------------------------------------------------------- 1 | use core::mem; 2 | use core::marker::PhantomData; 3 | use core::ops::{Add, Sub}; 4 | use core::fmt::{self, Debug, Display, LowerHex}; 5 | use core::iter::Step; 6 | 7 | pub trait SectorSize: Clone + Copy + PartialEq + PartialOrd + 'static { 8 | // log_sector_size = log_2(sector_size) 9 | const LOG_SIZE: u32; 10 | const SIZE: usize = 1 << Self::LOG_SIZE; 11 | const OFFSET_MASK: u32 = (Self::SIZE - 1) as u32; 12 | } 13 | 14 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] 15 | pub struct Size512; 16 | impl SectorSize for Size512 { 17 | const LOG_SIZE: u32 = 9; 18 | } 19 | 20 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] 21 | pub struct Size1024; 22 | impl SectorSize for Size1024 { 23 | const LOG_SIZE: u32 = 10; 24 | } 25 | 26 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] 27 | pub struct Size2048; 28 | impl SectorSize for Size2048 { 29 | const LOG_SIZE: u32 = 11; 30 | } 31 | 32 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] 33 | pub struct Size4096; 34 | impl SectorSize for Size4096 { 35 | const LOG_SIZE: u32 = 12; 36 | } 37 | 38 | /// Address in a physical sector 39 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] 40 | pub struct Address { 41 | sector: u32, 42 | offset: u32, 43 | _phantom: PhantomData, 44 | } 45 | 46 | impl Address { 47 | pub unsafe fn new_unchecked(sector: u32, offset: u32) -> Address { 48 | assert!((offset as usize) < S::SIZE, "offset out of sector bounds"); 49 | let _phantom = PhantomData; 50 | Address { 51 | sector, 52 | offset, 53 | _phantom, 54 | } 55 | } 56 | 57 | pub fn new(sector: u32, offset: i32) -> Address { 58 | let sector = (sector as i32 + (offset >> S::LOG_SIZE)) as u32; 59 | let offset = offset.abs() as u32 & S::OFFSET_MASK; 60 | unsafe { Address::new_unchecked(sector, offset) } 61 | } 62 | 63 | pub fn with_block_size( 64 | block: u32, 65 | offset: i32, 66 | log_block_size: u32, 67 | ) -> Address { 68 | let block = (block as i32 + (offset >> log_block_size)) as u32; 69 | let offset = offset.abs() as u32 & ((1 << log_block_size) - 1); 70 | 71 | let log_diff = log_block_size as i32 - S::LOG_SIZE as i32; 72 | let top_offset = offset >> S::LOG_SIZE; 73 | let offset = offset & ((1 << S::LOG_SIZE) - 1); 74 | let sector = block << log_diff | top_offset; 75 | unsafe { Address::new_unchecked(sector, offset) } 76 | } 77 | 78 | pub fn into_index(&self) -> u64 { 79 | ((self.sector as u64) << S::LOG_SIZE) + self.offset as u64 80 | } 81 | 82 | pub const fn sector_size(&self) -> usize { 83 | S::SIZE 84 | } 85 | 86 | pub const fn log_sector_size(&self) -> u32 { 87 | S::LOG_SIZE 88 | } 89 | 90 | pub fn sector(&self) -> u32 { 91 | self.sector 92 | } 93 | 94 | pub fn offset(&self) -> u32 { 95 | self.offset 96 | } 97 | } 98 | 99 | impl Step for Address { 100 | fn steps_between(start: &Self, end: &Self) -> Option { 101 | if end.sector >= start.sector { 102 | Some(end.sector as usize - start.sector as usize) 103 | } else { 104 | None 105 | } 106 | } 107 | 108 | fn replace_one(&mut self) -> Self { 109 | mem::replace(self, Address::new(1, 0)) 110 | } 111 | 112 | fn replace_zero(&mut self) -> Self { 113 | mem::replace(self, Address::new(0, 0)) 114 | } 115 | 116 | fn add_one(&self) -> Self { 117 | Address::new(self.sector + 1, 0) 118 | } 119 | 120 | fn sub_one(&self) -> Self { 121 | Address::new(self.sector - 1, 0) 122 | } 123 | 124 | fn add_usize(&self, n: usize) -> Option { 125 | self.sector 126 | .checked_add(n as u32) 127 | .map(|sector| Address::new(sector, 0)) 128 | } 129 | } 130 | 131 | impl Debug for Address { 132 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 133 | let name = format!("Address<{}>", S::SIZE); 134 | f.debug_struct(&name) 135 | .field("sector", &self.sector) 136 | .field("offset", &self.offset) 137 | .finish() 138 | } 139 | } 140 | 141 | impl Display for Address { 142 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 143 | write!(f, "{}:{}", self.sector, self.offset) 144 | } 145 | } 146 | 147 | impl LowerHex for Address { 148 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 149 | write!(f, "{:x}:{:x}", self.sector, self.offset) 150 | } 151 | } 152 | 153 | impl From for Address { 154 | fn from(idx: u64) -> Address { 155 | let sector = idx >> S::LOG_SIZE; 156 | let offset = idx & S::OFFSET_MASK as u64; 157 | Address::new(sector as u32, offset as i32) 158 | } 159 | } 160 | 161 | impl From for Address { 162 | fn from(idx: usize) -> Address { 163 | let sector = idx >> S::LOG_SIZE; 164 | let offset = idx & S::OFFSET_MASK as usize; 165 | Address::new(sector as u32, offset as i32) 166 | } 167 | } 168 | 169 | impl Add for Address { 170 | type Output = Address; 171 | fn add(self, rhs: Address) -> Address { 172 | Address::new( 173 | self.sector + rhs.sector, 174 | (self.offset + rhs.offset) as i32, 175 | ) 176 | } 177 | } 178 | 179 | impl Sub for Address { 180 | type Output = Address; 181 | fn sub(self, rhs: Address) -> Address { 182 | Address::new( 183 | self.sector - rhs.sector, 184 | self.offset as i32 - rhs.offset as i32, 185 | ) 186 | } 187 | } 188 | 189 | #[cfg(test)] 190 | mod tests { 191 | use super::*; 192 | 193 | #[test] 194 | fn conv() { 195 | assert_eq!(Address::::new(0, 1024).into_index(), 1024); 196 | assert_eq!(Address::::from(1024_u64).into_index(), 1024); 197 | assert_eq!( 198 | Address::::with_block_size(1, 256, 10).into_index(), 199 | 1024 + 256 200 | ); 201 | assert_eq!( 202 | Address::::with_block_size(2, 0, 10).into_index(), 203 | 2048 204 | ); 205 | assert_eq!( 206 | Address::::with_block_size(0, 1792, 10).into_index(), 207 | 1792 208 | ); 209 | } 210 | 211 | #[test] 212 | fn arithmetic() { 213 | assert_eq!( 214 | Address::::new(0, 512), 215 | Address::::new(1, 0), 216 | ); 217 | 218 | assert_eq!( 219 | Address::::new(2, -256), 220 | Address::::new(1, 256), 221 | ); 222 | 223 | let a = Address::::new(0, 1024); 224 | let b = Address::::new(0, 1024); 225 | assert_eq!(a + b, Address::::new(1, 0)); 226 | assert_eq!((a + b).into_index(), 2048); 227 | 228 | let a = Address::::new(0, 2048); 229 | let b = Address::::new(0, 256); 230 | assert_eq!(a - b, Address::::new(3, 256)); 231 | assert_eq!((a - b).into_index(), 1792); 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/sys/block_group.rs: -------------------------------------------------------------------------------- 1 | use core::mem; 2 | use core::fmt::{self, Debug}; 3 | 4 | use alloc::Vec; 5 | 6 | use error::Error; 7 | use sector::{Address, SectorSize}; 8 | use volume::Volume; 9 | 10 | /// The Block Group Descriptor Table contains a descriptor for each block group 11 | /// within the file system. The number of block groups within the file system, 12 | /// and correspondingly, the number of entries in the Block Group Descriptor 13 | /// Table, is described above. Each descriptor contains information regarding 14 | /// where important data structures for that group are located. 15 | /// 16 | /// The (`BlockGroupDescriptor`) table is located in the block immediately 17 | /// following the Superblock. So if the block size (determined from a field in 18 | /// the superblock) is 1024 bytes per block, the Block Group Descriptor Table 19 | /// will begin at block 2. For any other block size, it will begin at block 1. 20 | /// Remember that blocks are numbered starting at 0, and that block numbers 21 | /// don't usually correspond to physical block addresses. 22 | #[repr(C, packed)] 23 | #[derive(Clone, Copy)] 24 | pub struct BlockGroupDescriptor { 25 | /// Block address of block usage bitmap 26 | pub block_usage_addr: u32, 27 | /// Block address of inode usage bitmap 28 | pub inode_usage_addr: u32, 29 | /// Starting block address of inode table 30 | pub inode_table_block: u32, 31 | /// Number of unallocated blocks in group 32 | pub free_blocks_count: u16, 33 | /// Number of unallocated inodes in group 34 | pub free_inodes_count: u16, 35 | /// Number of directories in group 36 | pub dirs_count: u16, 37 | #[doc(hidden)] 38 | _reserved: [u8; 14], 39 | } 40 | 41 | impl Debug for BlockGroupDescriptor { 42 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 43 | f.debug_struct("BlockGroupDescriptor") 44 | .field("block_usage_addr", unsafe { &self.block_usage_addr }) 45 | .field("inode_usage_addr", unsafe { &self.inode_usage_addr }) 46 | .field("inode_table_block", unsafe { &self.inode_table_block }) 47 | .field("free_blocks_count", unsafe { &self.free_blocks_count }) 48 | .field("free_inodes_count", unsafe { &self.free_inodes_count }) 49 | .field("dirs_count", unsafe { &self.dirs_count }) 50 | .finish() 51 | } 52 | } 53 | 54 | impl BlockGroupDescriptor { 55 | pub unsafe fn find_descriptor>( 56 | haystack: &V, 57 | offset: Address, 58 | ) -> Result<(BlockGroupDescriptor, Address), Error> { 59 | let end = 60 | offset + Address::from(mem::size_of::()); 61 | if haystack.size() < end { 62 | return Err(Error::AddressOutOfBounds { 63 | sector: end.sector(), 64 | offset: end.offset(), 65 | size: end.sector_size(), 66 | }); 67 | } 68 | 69 | let descr = haystack 70 | .slice_unchecked(offset..end) 71 | .dynamic_cast::(); 72 | 73 | Ok(descr) 74 | } 75 | 76 | pub unsafe fn find_descriptor_table>( 77 | haystack: &V, 78 | offset: Address, 79 | count: usize, 80 | ) -> Result<(Vec, Address), Error> { 81 | let end = offset 82 | + Address::from(count * mem::size_of::()); 83 | if haystack.size() < end { 84 | return Err(Error::AddressOutOfBounds { 85 | sector: end.sector(), 86 | offset: end.offset(), 87 | size: end.sector_size(), 88 | }); 89 | } 90 | 91 | let mut vec = Vec::with_capacity(count); 92 | for i in 0..count { 93 | let offset = offset 94 | + Address::from(i * mem::size_of::()); 95 | vec.push({ 96 | BlockGroupDescriptor::find_descriptor(haystack, offset)?.0 97 | }); 98 | } 99 | 100 | Ok((vec, offset)) 101 | } 102 | } 103 | 104 | #[cfg(test)] 105 | mod tests { 106 | use sector::{Address, Size512}; 107 | use super::*; 108 | 109 | #[test] 110 | fn find() { 111 | let volume = vec![0_u8; 4096]; 112 | let table = unsafe { 113 | BlockGroupDescriptor::find_descriptor_table( 114 | &volume, 115 | Address::::new(4, 0), 116 | 8, 117 | ) 118 | }; 119 | assert!( 120 | table.is_ok(), 121 | "Err({:?})", 122 | table.err().unwrap_or_else(|| unreachable!()), 123 | ); 124 | let table = table.unwrap_or_else(|_| unreachable!()); 125 | assert_eq!(table.0.len(), 8); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/sys/inode.rs: -------------------------------------------------------------------------------- 1 | use core::mem; 2 | use core::fmt::{self, Debug}; 3 | 4 | use error::Error; 5 | use sector::{Address, SectorSize}; 6 | use volume::Volume; 7 | 8 | /// An inode is a structure on the disk that represents a file, directory, 9 | /// symbolic link, etc. Inodes do not contain the data of the file / directory / 10 | /// etc. that they represent. Instead, they link to the blocks that actually 11 | /// contain the data. This lets the inodes themselves have a well-defined size 12 | /// which lets them be placed in easily indexed arrays. Each block group has an 13 | /// array of inodes it is responsible for, and conversely every inode within a 14 | /// file system belongs to one of such tables (and one of such block groups). 15 | #[repr(C, packed)] 16 | #[derive(Clone, Copy)] 17 | pub struct Inode { 18 | /// Type and Permissions (see below) 19 | pub type_perm: TypePerm, 20 | /// User ID 21 | pub uid: u16, 22 | /// Lower 32 bits of size in bytes 23 | pub size_low: u32, 24 | /// Last Access Time (in POSIX time) 25 | pub atime: u32, 26 | /// Creation Time (in POSIX time) 27 | pub ctime: u32, 28 | /// Last Modification time (in POSIX time) 29 | pub mtime: u32, 30 | /// Deletion time (in POSIX time) 31 | pub dtime: u32, 32 | /// Group ID 33 | pub gid: u16, 34 | /// Count of hard links (directory entries) to this inode. When this 35 | /// reaches 0, the data blocks are marked as unallocated. 36 | pub hard_links: u16, 37 | /// Count of disk sectors (not Ext2 blocks) in use by this inode, not 38 | /// counting the actual inode structure nor directory entries linking 39 | /// to the inode. 40 | pub sectors_count: u32, 41 | /// Flags 42 | pub flags: Flags, 43 | /// Operating System Specific value #1 44 | pub _os_specific_1: [u8; 4], 45 | /// Direct block pointers 46 | pub direct_pointer: [u32; 12], 47 | /// Singly Indirect Block Pointer (Points to a block that is a list of 48 | /// block pointers to data) 49 | pub indirect_pointer: u32, 50 | /// Doubly Indirect Block Pointer (Points to a block that is a list of 51 | /// block pointers to Singly Indirect Blocks) 52 | pub doubly_indirect: u32, 53 | /// Triply Indirect Block Pointer (Points to a block that is a list of 54 | /// block pointers to Doubly Indirect Blocks) 55 | pub triply_indirect: u32, 56 | /// Generation number (Primarily used for NFS) 57 | pub gen_number: u32, 58 | /// In Ext2 version 0, this field is reserved. In version >= 1, 59 | /// Extended attribute block (File ACL). 60 | pub ext_attribute_block: u32, 61 | /// In Ext2 version 0, this field is reserved. In version >= 1, Upper 62 | /// 32 bits of file size (if feature bit set) if it's a file, 63 | /// Directory ACL if it's a directory 64 | pub size_high: u32, 65 | /// Block address of fragment 66 | pub frag_block_addr: u32, 67 | /// Operating System Specific Value #2 68 | pub _os_specific_2: [u8; 12], 69 | } 70 | 71 | impl Debug for Inode { 72 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 73 | f.debug_struct("Inode") 74 | .field("type_perm", unsafe { &self.type_perm }) 75 | .field("uid", unsafe { &self.uid }) 76 | .field("size_low", unsafe { &self.size_low }) 77 | .field("atime", unsafe { &self.atime }) 78 | .field("ctime", unsafe { &self.ctime }) 79 | .field("mtime", unsafe { &self.mtime }) 80 | .field("dtime", unsafe { &self.dtime }) 81 | .field("gid", unsafe { &self.gid }) 82 | .field("hard_links", unsafe { &self.hard_links }) 83 | .field("sectors_count", unsafe { &self.sectors_count }) 84 | .field("flags", unsafe { &self.flags }) 85 | .field("os_specific_1", &self._os_specific_1) 86 | .field("direct_pointer", unsafe { &self.direct_pointer }) 87 | .field("indirect_pointer", unsafe { &self.indirect_pointer }) 88 | .field("doubly_indirect", unsafe { &self.doubly_indirect }) 89 | .field("triply_indirect", unsafe { &self.triply_indirect }) 90 | .field("gen_number", unsafe { &self.gen_number }) 91 | .field("ext_attribute_block", unsafe { &self.ext_attribute_block }) 92 | .field("size_high", unsafe { &self.size_high }) 93 | .field("frag_block_addr", unsafe { &self.frag_block_addr }) 94 | .field("os_specific_2", &self._os_specific_2) 95 | .finish() 96 | } 97 | } 98 | 99 | impl Inode { 100 | pub unsafe fn find_inode>( 101 | haystack: &V, 102 | offset: Address, 103 | size: usize, 104 | ) -> Result<(Inode, Address), Error> { 105 | if size != mem::size_of::() { 106 | unimplemented!("inodes with a size != 128"); 107 | } 108 | 109 | let end = offset + Address::from(size); 110 | if haystack.size() < end { 111 | return Err(Error::AddressOutOfBounds { 112 | sector: end.sector(), 113 | offset: end.offset(), 114 | size: end.sector_size(), 115 | }); 116 | } 117 | 118 | let inode = haystack 119 | .slice_unchecked(offset..end) 120 | .dynamic_cast::(); 121 | 122 | Ok(inode) 123 | } 124 | } 125 | 126 | bitflags! { 127 | pub struct TypePerm: u16 { 128 | /// FIFO 129 | const FIFO = 0x1000; 130 | /// Character device 131 | const CHAR_DEVICE = 0x2000; 132 | /// Directory 133 | const DIRECTORY = 0x4000; 134 | /// Block device 135 | const BLOCK_DEVICE = 0x6000; 136 | /// Regular file 137 | const FILE = 0x8000; 138 | /// Symbolic link 139 | const SYMLINK = 0xA000; 140 | /// Unix socket 141 | const SOCKET = 0xC000; 142 | /// Other—execute permission 143 | const O_EXEC = 0x001; 144 | /// Other—write permission 145 | const O_WRITE = 0x002; 146 | /// Other—read permission 147 | const O_READ = 0x004; 148 | /// Group—execute permission 149 | const G_EXEC = 0x008; 150 | /// Group—write permission 151 | const G_WRITE = 0x010; 152 | /// Group—read permission 153 | const G_READ = 0x020; 154 | /// User—execute permission 155 | const U_EXEC = 0x040; 156 | /// User—write permission 157 | const U_WRITE = 0x080; 158 | /// User—read permission 159 | const U_READ = 0x100; 160 | /// Sticky Bit 161 | const STICKY = 0x200; 162 | /// Set group ID 163 | const SET_GID = 0x400; 164 | /// Set user ID 165 | const SET_UID = 0x800; 166 | } 167 | } 168 | 169 | bitflags! { 170 | pub struct Flags: u32 { 171 | /// Secure deletion (not used) 172 | const SECURE_DEL = 0x00000001; 173 | /// Keep a copy of data when deleted (not used) 174 | const KEEP_COPY = 0x00000002; 175 | /// File compression (not used) 176 | const COMPRESSION = 0x00000004; 177 | /// Synchronous updates—new data is written immediately to disk 178 | const SYNC_UPDATE = 0x00000008; 179 | /// Immutable file (content cannot be changed) 180 | const IMMUTABLE = 0x00000010; 181 | /// Append only 182 | const APPEND_ONLY = 0x00000020; 183 | /// File is not included in 'dump' command 184 | const NODUMP = 0x00000040; 185 | /// Last accessed time should not updated 186 | const DONT_ATIME = 0x00000080; 187 | /// Hash indexed directory 188 | const HASH_DIR = 0x00010000; 189 | /// AFS directory 190 | const AFS_DIR = 0x00020000; 191 | /// Journal file data 192 | const JOURNAL_DATA = 0x00040000; 193 | } 194 | } 195 | 196 | /// Unknown entry type 197 | pub const UNKNOWN: u8 = 0; 198 | /// FIFO entry type 199 | pub const FIFO: u8 = 1; 200 | /// Character device entry type 201 | pub const CHAR_DEVICE: u8 = 2; 202 | /// Directory entry type 203 | pub const DIRECTORY: u8 = 3; 204 | /// Block device entry type 205 | pub const BLOCK_DEVICE: u8 = 4; 206 | /// Regular file entry type 207 | pub const FILE: u8 = 5; 208 | /// Symbolic link entry type 209 | pub const SYMLINK: u8 = 6; 210 | /// Unix socket entry type 211 | pub const SOCKET: u8 = 7; 212 | -------------------------------------------------------------------------------- /src/sys/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod superblock; 2 | pub mod block_group; 3 | pub mod inode; 4 | -------------------------------------------------------------------------------- /src/sys/superblock.rs: -------------------------------------------------------------------------------- 1 | use core::mem; 2 | use core::fmt::{self, Debug}; 3 | 4 | use error::Error; 5 | use sector::{Address, SectorSize}; 6 | use volume::Volume; 7 | 8 | /// Ext2 signature (0xef53), used to help confirm the presence of Ext2 on a 9 | /// volume 10 | pub const EXT2_MAGIC: u16 = 0xef53; 11 | 12 | /// Filesystem is free of errors 13 | pub const FS_CLEAN: u16 = 1; 14 | /// Filesystem has errors 15 | pub const FS_ERR: u16 = 2; 16 | 17 | /// Ignore errors 18 | pub const ERR_IGNORE: u16 = 1; 19 | /// Remount as read-only on error 20 | pub const ERR_RONLY: u16 = 2; 21 | /// Panic on error 22 | pub const ERR_PANIC: u16 = 3; 23 | 24 | /// Creator OS is Linux 25 | pub const OS_LINUX: u32 = 0; 26 | /// Creator OS is Hurd 27 | pub const OS_HURD: u32 = 1; 28 | /// Creator OS is Masix 29 | pub const OS_MASIX: u32 = 2; 30 | /// Creator OS is FreeBSD 31 | pub const OS_FREEBSD: u32 = 3; 32 | /// Creator OS is a BSD4.4-Lite derivative 33 | pub const OS_LITE: u32 = 4; 34 | 35 | /// The Superblock contains all information about the layout of the file system 36 | /// and possibly contains other important information like what optional 37 | /// features were used to create the file system. 38 | /// 39 | /// The Superblock is always located at byte 1024 from the beginning of the 40 | /// volume and is exactly 1024 bytes in length. For example, if the disk uses 41 | /// 512 byte sectors, the Superblock will begin at LBA 2 and will occupy all of 42 | /// sector 2 and 3. 43 | #[repr(C, packed)] 44 | #[derive(Clone, Copy)] 45 | pub struct Superblock { 46 | // taken from https://wiki.osdev.org/Ext2 47 | /// Total number of inodes in file system 48 | pub inodes_count: u32, 49 | /// Total number of blocks in file system 50 | pub blocks_count: u32, 51 | /// Number of blocks reserved for superuser (see offset 80) 52 | pub r_blocks_count: u32, 53 | /// Total number of unallocated blocks 54 | pub free_blocks_count: u32, 55 | /// Total number of unallocated inodes 56 | pub free_inodes_count: u32, 57 | /// Block number of the block containing the superblock 58 | pub first_data_block: u32, 59 | /// log2 (block size) - 10. (In other words, the number to shift 1,024 60 | /// to the left by to obtain the block size) 61 | pub log_block_size: u32, 62 | /// log2 (fragment size) - 10. (In other words, the number to shift 63 | /// 1,024 to the left by to obtain the fragment size) 64 | pub log_frag_size: i32, 65 | /// Number of blocks in each block group 66 | pub blocks_per_group: u32, 67 | /// Number of fragments in each block group 68 | pub frags_per_group: u32, 69 | /// Number of inodes in each block group 70 | pub inodes_per_group: u32, 71 | /// Last mount time (in POSIX time) 72 | pub mtime: u32, 73 | /// Last written time (in POSIX time) 74 | pub wtime: u32, 75 | /// Number of times the volume has been mounted since its last 76 | /// consistency check (fsck) 77 | pub mnt_count: u16, 78 | /// Number of mounts allowed before a consistency check (fsck) must be 79 | /// done 80 | pub max_mnt_count: i16, 81 | /// Ext2 signature (0xef53), used to help confirm the presence of Ext2 82 | /// on a volume 83 | pub magic: u16, 84 | /// File system state (see `FS_CLEAN` and `FS_ERR`) 85 | pub state: u16, 86 | /// What to do when an error is detected (see `ERR_IGNORE`, `ERR_RONLY` and 87 | /// `ERR_PANIC`) 88 | pub errors: u16, 89 | /// Minor portion of version (combine with Major portion below to 90 | /// construct full version field) 91 | pub rev_minor: u16, 92 | /// POSIX time of last consistency check (fsck) 93 | pub lastcheck: u32, 94 | /// Interval (in POSIX time) between forced consistency checks (fsck) 95 | pub checkinterval: u32, 96 | /// Operating system ID from which the filesystem on this volume was 97 | /// created 98 | pub creator_os: u32, 99 | /// Major portion of version (combine with Minor portion above to 100 | /// construct full version field) 101 | pub rev_major: u32, 102 | /// User ID that can use reserved blocks 103 | pub block_uid: u16, 104 | /// Group ID that can use reserved blocks 105 | pub block_gid: u16, 106 | 107 | /// First non-reserved inode in file system. 108 | pub first_inode: u32, 109 | /// SectorSize of each inode structure in bytes. 110 | pub inode_size: u16, 111 | /// Block group that this superblock is part of (if backup copy) 112 | pub block_group: u16, 113 | /// Optional features present (features that are not required to read 114 | /// or write, but usually result in a performance increase) 115 | pub features_opt: FeaturesOptional, 116 | /// Required features present (features that are required to be 117 | /// supported to read or write) 118 | pub features_req: FeaturesRequired, 119 | /// Features that if not supported, the volume must be mounted 120 | /// read-only) 121 | pub features_ronly: FeaturesROnly, 122 | /// File system ID (what is output by blkid) 123 | pub fs_id: [u8; 16], 124 | /// Volume name (C-style string: characters terminated by a 0 byte) 125 | pub volume_name: [u8; 16], 126 | /// Path volume was last mounted to (C-style string: characters 127 | /// terminated by a 0 byte) 128 | pub last_mnt_path: [u8; 64], 129 | /// Compression algorithms used (see Required features above) 130 | pub compression: u32, 131 | /// Number of blocks to preallocate for files 132 | pub prealloc_blocks_files: u8, 133 | /// Number of blocks to preallocate for directories 134 | pub prealloc_blocks_dirs: u8, 135 | #[doc(hidden)] 136 | _unused: [u8; 2], 137 | /// Journal ID (same style as the File system ID above) 138 | pub journal_id: [u8; 16], 139 | /// Journal inode 140 | pub journal_inode: u32, 141 | /// Journal device 142 | pub journal_dev: u32, 143 | /// Head of orphan inode list 144 | pub journal_orphan_head: u32, 145 | #[doc(hidden)] 146 | _reserved: [u8; 788], 147 | } 148 | 149 | impl Debug for Superblock { 150 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 151 | f.debug_struct("Superblock") 152 | .field("inodes_count", unsafe { &self.inodes_count }) 153 | .field("blocks_count", unsafe { &self.blocks_count }) 154 | .field("r_blocks_count", unsafe { &self.r_blocks_count }) 155 | .field("free_blocks_count", unsafe { &self.free_blocks_count }) 156 | .field("free_inodes_count", unsafe { &self.free_inodes_count }) 157 | .field("first_data_block", unsafe { &self.first_data_block }) 158 | .field("log_block_size", unsafe { &self.log_block_size }) 159 | .field("log_frag_size", unsafe { &self.log_frag_size }) 160 | .field("blocks_per_group", unsafe { &self.blocks_per_group }) 161 | .field("frags_per_group", unsafe { &self.frags_per_group }) 162 | .field("inodes_per_group", unsafe { &self.inodes_per_group }) 163 | .field("mtime", unsafe { &self.mtime }) 164 | .field("wtime", unsafe { &self.wtime }) 165 | .field("mnt_count", unsafe { &self.mnt_count }) 166 | .field("max_mnt_count", unsafe { &self.max_mnt_count }) 167 | .field("magic", unsafe { &self.magic }) 168 | .field("state", unsafe { &self.state }) 169 | .field("errors", unsafe { &self.errors }) 170 | .field("rev_minor", unsafe { &self.rev_minor }) 171 | .field("lastcheck", unsafe { &self.lastcheck }) 172 | .field("checkinterval", unsafe { &self.checkinterval }) 173 | .field("creator_os", unsafe { &self.creator_os }) 174 | .field("rev_major", unsafe { &self.rev_major }) 175 | .field("block_uid", unsafe { &self.block_uid }) 176 | .field("block_gid", unsafe { &self.block_gid }) 177 | .field("first_inode", unsafe { &self.first_inode }) 178 | .field("inode_size", unsafe { &self.inode_size }) 179 | .field("block_group", unsafe { &self.block_group }) 180 | .field("features_opt", unsafe { &self.features_opt }) 181 | .field("features_req", unsafe { &self.features_req }) 182 | .field("features_ronly", unsafe { &self.features_ronly }) 183 | .field("fs_id", &self.fs_id) 184 | .field("volume_name", &self.volume_name) 185 | .field("last_mnt_path", &self.last_mnt_path.as_ref()) 186 | .field("compression", unsafe { &self.compression }) 187 | .field("prealloc_blocks_files", &self.prealloc_blocks_files) 188 | .field("prealloc_blocks_dirs", &self.prealloc_blocks_dirs) 189 | .field("journal_id", &self.journal_id) 190 | .field("journal_inode", unsafe { &self.journal_inode }) 191 | .field("journal_dev", unsafe { &self.journal_dev }) 192 | .field("journal_orphan_head", unsafe { &self.journal_orphan_head }) 193 | .finish() 194 | } 195 | } 196 | 197 | impl Superblock { 198 | pub unsafe fn find>( 199 | haystack: &V, 200 | ) -> Result<(Superblock, Address), Error> { 201 | let offset = Address::from(1024_usize); 202 | let end = offset + Address::from(mem::size_of::()); 203 | if haystack.size() < end { 204 | return Err(Error::AddressOutOfBounds { 205 | sector: end.sector(), 206 | offset: end.offset(), 207 | size: end.sector_size(), 208 | }); 209 | } 210 | 211 | let superblock = { 212 | haystack 213 | .slice_unchecked(offset..end) 214 | .dynamic_cast::() 215 | }; 216 | 217 | if superblock.0.magic != EXT2_MAGIC { 218 | Err(Error::BadMagic { 219 | magic: superblock.0.magic, 220 | }) 221 | } else { 222 | Ok(superblock) 223 | } 224 | } 225 | 226 | #[inline] 227 | pub fn block_size(&self) -> usize { 228 | 1024 << self.log_block_size 229 | } 230 | 231 | #[inline] 232 | pub fn frag_size(&self) -> usize { 233 | 1024 << self.log_frag_size 234 | } 235 | 236 | pub fn block_group_count(&self) -> Result { 237 | let blocks_mod = self.blocks_count % self.blocks_per_group; 238 | let inodes_mod = self.inodes_count % self.inodes_per_group; 239 | let blocks_inc = if blocks_mod == 0 { 0 } else { 1 }; 240 | let inodes_inc = if inodes_mod == 0 { 0 } else { 1 }; 241 | let by_blocks = self.blocks_count / self.blocks_per_group + blocks_inc; 242 | let by_inodes = self.inodes_count / self.inodes_per_group + inodes_inc; 243 | if by_blocks == by_inodes { 244 | Ok(by_blocks) 245 | } else { 246 | Err((by_blocks, by_inodes)) 247 | } 248 | } 249 | } 250 | 251 | bitflags! { 252 | /// Optional features 253 | pub struct FeaturesOptional: u32 { 254 | /// Preallocate some number of (contiguous?) blocks (see 255 | /// `Superblock::prealloc_blocks_dirs`) to a directory when creating a new one 256 | const PREALLOCATE = 0x0001; 257 | /// AFS server inodes exist 258 | const AFS = 0x0002; 259 | /// File system has a journal (Ext3) 260 | const JOURNAL = 0x0004; 261 | /// Inodes have extended attributes 262 | const EXTENDED_INODE = 0x0008; 263 | /// File system can resize itself for larger partitions 264 | const SELF_RESIZE = 0x0010; 265 | /// Directories use hash index 266 | const HASH_INDEX = 0x0020; 267 | } 268 | } 269 | 270 | bitflags! { 271 | /// Required features. If these are not supported; can't mount 272 | pub struct FeaturesRequired: u32 { 273 | /// Compression is used 274 | const REQ_COMPRESSION = 0x0001; 275 | /// Directory entries contain a type field 276 | const REQ_DIRECTORY_TYPE = 0x0002; 277 | /// File system needs to replay its journal 278 | const REQ_REPLAY_JOURNAL = 0x0004; 279 | /// File system uses a journal device 280 | const REQ_JOURNAL_DEVICE = 0x0008; 281 | } 282 | } 283 | 284 | bitflags! { 285 | /// ROnly features. If these are not supported; remount as read-only 286 | pub struct FeaturesROnly: u32 { 287 | /// Sparse superblocks and group descriptor tables 288 | const RONLY_SPARSE = 0x0001; 289 | /// File system uses a 64-bit file size 290 | const RONLY_FILE_SIZE_64 = 0x0002; 291 | /// Directory contents are stored in the form of a Binary Tree 292 | const RONLY_BTREE_DIRECTORY = 0x0004; 293 | } 294 | } 295 | 296 | #[cfg(test)] 297 | mod tests { 298 | use sector::Size512; 299 | use super::*; 300 | 301 | #[test] 302 | fn find() { 303 | let mut volume = vec![0_u8; 4096]; 304 | // magic 305 | volume[1024 + 56] = EXT2_MAGIC as u8; 306 | volume[1024 + 57] = (EXT2_MAGIC >> 8) as u8; 307 | let superblock = unsafe { Superblock::find::(&volume) }; 308 | assert!( 309 | superblock.is_ok(), 310 | "Err({:?})", 311 | superblock.err().unwrap_or_else(|| unreachable!()), 312 | ); 313 | } 314 | 315 | #[test] 316 | fn superblock() { 317 | use std::cell::RefCell; 318 | use std::fs::File; 319 | 320 | let file = RefCell::new(File::open("ext2.img").unwrap()); 321 | let superblock = unsafe { Superblock::find::(&file) }; 322 | assert!( 323 | superblock.is_ok(), 324 | "Err({:?})", 325 | superblock.err().unwrap_or_else(|| unreachable!()), 326 | ); 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /src/volume/mod.rs: -------------------------------------------------------------------------------- 1 | use core::mem; 2 | use core::slice; 3 | use core::ops::{Deref, DerefMut, Range}; 4 | 5 | use alloc::Vec; 6 | use alloc::boxed::Box; 7 | use alloc::borrow::{Cow, ToOwned}; 8 | 9 | use error::Error; 10 | use sector::{Address, SectorSize}; 11 | 12 | pub mod size; 13 | use self::size::Size; 14 | 15 | pub trait Volume { 16 | type Error: Into; 17 | 18 | fn size(&self) -> Size; 19 | fn commit( 20 | &mut self, 21 | slice: Option>, 22 | ) -> Result<(), Self::Error>; 23 | unsafe fn slice_unchecked<'a>( 24 | &'a self, 25 | range: Range>, 26 | ) -> VolumeSlice<'a, T, S>; 27 | 28 | fn slice<'a>( 29 | &'a self, 30 | range: Range>, 31 | ) -> Result, Self::Error>; 32 | } 33 | 34 | #[derive(Debug, Clone, PartialEq, Hash)] 35 | pub struct VolumeSlice<'a, T: 'a + Clone, S: SectorSize> { 36 | inner: Cow<'a, [T]>, 37 | index: Address, 38 | } 39 | 40 | impl VolumeSlice<'static, T, S> { 41 | pub fn with_static(inner: &'static [T]) -> VolumeSlice<'static, T, S> { 42 | VolumeSlice { 43 | inner: Cow::Borrowed(inner), 44 | index: Address::new(0, 0), 45 | } 46 | } 47 | 48 | pub fn new_owned( 49 | inner: <[T] as ToOwned>::Owned, 50 | index: Address, 51 | ) -> VolumeSlice<'static, T, S> { 52 | VolumeSlice { 53 | inner: Cow::Owned(inner), 54 | index, 55 | } 56 | } 57 | } 58 | 59 | impl<'a, T: Clone, S: SectorSize> VolumeSlice<'a, T, S> { 60 | pub fn new(inner: &'a [T], index: Address) -> VolumeSlice<'a, T, S> { 61 | VolumeSlice { 62 | inner: Cow::Borrowed(inner), 63 | index, 64 | } 65 | } 66 | 67 | pub fn is_mutated(&self) -> bool { 68 | match self.inner { 69 | Cow::Borrowed(_) => false, 70 | Cow::Owned(_) => true, 71 | } 72 | } 73 | 74 | pub fn address(&self) -> Address { 75 | self.index 76 | } 77 | } 78 | 79 | impl<'a, S: SectorSize> VolumeSlice<'a, u8, S> { 80 | pub unsafe fn dynamic_cast(&self) -> (T, Address) { 81 | assert!(self.inner.len() >= mem::size_of::()); 82 | let index = self.index; 83 | let cast = mem::transmute_copy(self.inner.as_ptr().as_ref().unwrap()); 84 | (cast, index) 85 | } 86 | 87 | pub fn from_cast( 88 | cast: &'a T, 89 | index: Address, 90 | ) -> VolumeSlice<'a, u8, S> { 91 | let len = mem::size_of::(); 92 | let ptr = cast as *const T as *const u8; 93 | let slice = unsafe { slice::from_raw_parts(ptr, len) }; 94 | VolumeSlice::new(slice, index) 95 | } 96 | } 97 | 98 | impl<'a, T: Clone, S: SectorSize> VolumeSlice<'a, T, S> { 99 | pub fn commit(self) -> Option> { 100 | if self.is_mutated() { 101 | Some(VolumeCommit::new(self.inner.into_owned(), self.index)) 102 | } else { 103 | None 104 | } 105 | } 106 | } 107 | 108 | impl<'a, T: Clone, S: SectorSize> AsRef<[T]> for VolumeSlice<'a, T, S> { 109 | fn as_ref(&self) -> &[T] { 110 | self.inner.as_ref() 111 | } 112 | } 113 | 114 | impl<'a, T: Clone, S: SectorSize> AsMut<[T]> for VolumeSlice<'a, T, S> { 115 | fn as_mut(&mut self) -> &mut [T] { 116 | self.inner.to_mut().as_mut() 117 | } 118 | } 119 | 120 | impl<'a, T: Clone, S: SectorSize> Deref for VolumeSlice<'a, T, S> { 121 | type Target = [T]; 122 | 123 | fn deref(&self) -> &Self::Target { 124 | self.as_ref() 125 | } 126 | } 127 | 128 | impl<'a, T: Clone, S: SectorSize> DerefMut for VolumeSlice<'a, T, S> { 129 | fn deref_mut(&mut self) -> &mut Self::Target { 130 | self.as_mut() 131 | } 132 | } 133 | 134 | pub struct VolumeCommit { 135 | inner: Vec, 136 | index: Address, 137 | } 138 | 139 | impl VolumeCommit { 140 | pub fn with_vec(inner: Vec) -> VolumeCommit { 141 | VolumeCommit { 142 | inner, 143 | index: Address::new(0, 0), 144 | } 145 | } 146 | } 147 | 148 | impl VolumeCommit { 149 | pub fn new(inner: Vec, index: Address) -> VolumeCommit { 150 | VolumeCommit { inner, index } 151 | } 152 | 153 | pub fn into_inner(self) -> Vec { 154 | self.inner 155 | } 156 | 157 | pub fn address(&self) -> Address { 158 | self.index 159 | } 160 | } 161 | 162 | impl AsRef<[T]> for VolumeCommit { 163 | fn as_ref(&self) -> &[T] { 164 | self.inner.as_ref() 165 | } 166 | } 167 | 168 | impl AsMut<[T]> for VolumeCommit { 169 | fn as_mut(&mut self) -> &mut [T] { 170 | self.inner.as_mut() 171 | } 172 | } 173 | 174 | impl Deref for VolumeCommit { 175 | type Target = [T]; 176 | 177 | fn deref(&self) -> &Self::Target { 178 | self.as_ref() 179 | } 180 | } 181 | 182 | impl DerefMut for VolumeCommit { 183 | fn deref_mut(&mut self) -> &mut Self::Target { 184 | self.as_mut() 185 | } 186 | } 187 | 188 | macro_rules! impl_slice { 189 | (@inner $volume:ty $( , $lt:lifetime )* ) => { 190 | impl<$( $lt, )* T: Clone, S: SectorSize> Volume 191 | for $volume 192 | { 193 | type Error = Error; 194 | 195 | fn size(&self) -> Size { 196 | Size::Bounded( 197 | Address::from(>::as_ref(self).len()) 198 | ) 199 | } 200 | 201 | fn commit( 202 | &mut self, 203 | slice: Option>, 204 | ) -> Result<(), Self::Error> { 205 | slice.map(|slice| { 206 | let index = slice.address().into_index() as usize; 207 | let end = index + slice.as_ref().len(); 208 | // XXX: it would be much better to drop the contents of dst 209 | // and move the contents of slice instead of cloning 210 | let dst = 211 | &mut >::as_mut(self)[index..end]; 212 | dst.clone_from_slice(slice.as_ref()); 213 | }); 214 | Ok(()) 215 | } 216 | 217 | unsafe fn slice_unchecked<'a>( 218 | &'a self, 219 | range: Range>, 220 | ) -> VolumeSlice<'a, T, S> { 221 | let index = range.start; 222 | let range = range.start.into_index() as usize 223 | ..range.end.into_index() as usize; 224 | VolumeSlice::new( 225 | >::as_ref(self).get_unchecked(range), 226 | index, 227 | ) 228 | } 229 | 230 | fn slice<'a>( 231 | &'a self, 232 | range: Range>, 233 | ) -> Result, Self::Error> { 234 | if self.size() >= range.end { 235 | unsafe { Ok(self.slice_unchecked(range)) } 236 | } else { 237 | Err(Error::AddressOutOfBounds { 238 | sector: range.end.sector(), 239 | offset: range.end.offset(), 240 | size: range.end.sector_size() 241 | }) 242 | } 243 | } 244 | } 245 | }; 246 | ($volume:ty) => { 247 | impl_slice!(@inner $volume); 248 | }; 249 | ($volume:ty $( , $lt:lifetime )* ) => { 250 | impl_slice!(@inner $volume $( , $lt )* ); 251 | }; 252 | } 253 | 254 | impl_slice!(&'b mut [T], 'b); 255 | impl_slice!(Vec); 256 | impl_slice!(Box<[T]>); 257 | 258 | #[cfg(any(test, not(feature = "no_std")))] 259 | mod file { 260 | use std::ops::Range; 261 | use std::io::{self, Read, Seek, SeekFrom, Write}; 262 | use std::fs::File; 263 | use std::cell::RefCell; 264 | 265 | use sector::{Address, SectorSize}; 266 | 267 | use super::{Volume, VolumeCommit, VolumeSlice}; 268 | use super::size::Size; 269 | 270 | impl Volume for RefCell { 271 | type Error = io::Error; 272 | 273 | fn size(&self) -> Size { 274 | Size::Bounded( 275 | self.borrow() 276 | .metadata() 277 | .map(|data| Address::from(data.len())) 278 | .unwrap_or(Address::new(0, 0)), 279 | ) 280 | } 281 | 282 | fn commit( 283 | &mut self, 284 | slice: Option>, 285 | ) -> Result<(), Self::Error> { 286 | slice 287 | .map(|slice| { 288 | let index = slice.address(); 289 | let mut refmut = self.borrow_mut(); 290 | refmut 291 | .seek(SeekFrom::Start(index.into_index())) 292 | .and_then(|_| refmut.write(slice.as_ref())) 293 | .map(|_| ()) 294 | }) 295 | .unwrap_or(Ok(())) 296 | } 297 | 298 | unsafe fn slice_unchecked<'a>( 299 | &'a self, 300 | range: Range>, 301 | ) -> VolumeSlice<'a, u8, S> { 302 | let index = range.start; 303 | let len = range.end - range.start; 304 | let mut vec = Vec::with_capacity(len.into_index() as usize); 305 | vec.set_len(len.into_index() as usize); 306 | let mut refmut = self.borrow_mut(); 307 | refmut 308 | .seek(SeekFrom::Start(index.into_index())) 309 | .and_then(|_| refmut.read_exact(&mut vec[..])) 310 | .unwrap_or_else(|err| { 311 | panic!("could't read from File Volume: {:?}", err) 312 | }); 313 | VolumeSlice::new_owned(vec, index) 314 | } 315 | 316 | fn slice<'a>( 317 | &'a self, 318 | range: Range>, 319 | ) -> Result, Self::Error> { 320 | let index = range.start; 321 | let mut vec = Vec::with_capacity((range.end - range.start) 322 | .into_index() 323 | as usize); 324 | unsafe { 325 | vec.set_len((range.end - range.start).into_index() as usize); 326 | } 327 | let mut refmut = self.borrow_mut(); 328 | refmut 329 | .seek(SeekFrom::Start(index.into_index())) 330 | .and_then(|_| refmut.read_exact(&mut vec[..])) 331 | .map(move |_| VolumeSlice::new_owned(vec, index)) 332 | } 333 | } 334 | } 335 | 336 | #[cfg(test)] 337 | mod tests { 338 | use sector::{Address, Size512}; 339 | use super::*; 340 | 341 | #[test] 342 | fn volume() { 343 | let mut volume = vec![0; 1024]; 344 | let commit = { 345 | let mut slice = volume 346 | .slice( 347 | Address::::from(256_u64) 348 | ..Address::::from(512_u64), 349 | ) 350 | .unwrap(); 351 | slice.iter_mut().for_each(|x| *x = 1); 352 | slice.commit() 353 | }; 354 | assert!(volume.commit(commit).is_ok()); 355 | 356 | for (i, &x) in volume.iter().enumerate() { 357 | if i < 256 || i >= 512 { 358 | assert_eq!(x, 0); 359 | } else { 360 | assert_eq!(x, 1); 361 | } 362 | } 363 | } 364 | } 365 | -------------------------------------------------------------------------------- /src/volume/size.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, Display}; 2 | use core::cmp::Ordering; 3 | 4 | use sector::{Address, SectorSize}; 5 | 6 | #[derive(Clone, Copy, Debug, Hash)] 7 | pub enum Size { 8 | Unbounded, 9 | Bounded(Address), 10 | } 11 | 12 | impl Size { 13 | pub fn try_len(&self) -> Option> { 14 | match *self { 15 | Size::Unbounded => None, 16 | Size::Bounded(n) => Some(n), 17 | } 18 | } 19 | 20 | pub unsafe fn len(&self) -> Address { 21 | match *self { 22 | Size::Unbounded => panic!( 23 | "attempt to convert `Size::Unbounded` to a concrete length" 24 | ), 25 | Size::Bounded(n) => n, 26 | } 27 | } 28 | } 29 | 30 | impl Size { 31 | pub fn is_bounded(&self) -> bool { 32 | match *self { 33 | Size::Unbounded => false, 34 | Size::Bounded(_) => true, 35 | } 36 | } 37 | } 38 | 39 | impl Display for Size { 40 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 41 | match *self { 42 | Size::Unbounded => write!(f, "Unbounded"), 43 | Size::Bounded(n) => write!(f, "Bounded({})", n), 44 | } 45 | } 46 | } 47 | 48 | impl PartialEq for Size { 49 | fn eq(&self, rhs: &Self) -> bool { 50 | match (self, rhs) { 51 | (&Size::Unbounded, _) => false, 52 | (_, &Size::Unbounded) => false, 53 | (&Size::Bounded(ref a), &Size::Bounded(ref b)) => a.eq(b), 54 | } 55 | } 56 | 57 | fn ne(&self, rhs: &Self) -> bool { 58 | match (self, rhs) { 59 | (&Size::Unbounded, _) => false, 60 | (_, &Size::Unbounded) => false, 61 | (&Size::Bounded(ref a), &Size::Bounded(ref b)) => a.ne(b), 62 | } 63 | } 64 | } 65 | 66 | impl PartialEq> for Size { 67 | fn eq(&self, rhs: &Address) -> bool { 68 | match *self { 69 | Size::Unbounded => false, 70 | Size::Bounded(ref n) => n.eq(rhs), 71 | } 72 | } 73 | 74 | fn ne(&self, rhs: &Address) -> bool { 75 | match *self { 76 | Size::Unbounded => false, 77 | Size::Bounded(ref n) => n.eq(rhs), 78 | } 79 | } 80 | } 81 | 82 | impl PartialOrd for Size { 83 | fn partial_cmp(&self, rhs: &Self) -> Option { 84 | match (self, rhs) { 85 | (&Size::Unbounded, &Size::Unbounded) => None, 86 | (&Size::Unbounded, _) => Some(Ordering::Greater), 87 | (_, &Size::Unbounded) => Some(Ordering::Less), 88 | (&Size::Bounded(ref a), &Size::Bounded(ref b)) => a.partial_cmp(b), 89 | } 90 | } 91 | } 92 | 93 | impl PartialOrd> for Size { 94 | fn partial_cmp(&self, rhs: &Address) -> Option { 95 | match *self { 96 | Size::Unbounded => Some(Ordering::Greater), 97 | Size::Bounded(ref n) => n.partial_cmp(rhs), 98 | } 99 | } 100 | } 101 | --------------------------------------------------------------------------------