diff --git a/Cargo.toml b/Cargo.toml index a216e65..8330f1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ authors = ["Szymon Walter "] [dependencies] bitflags = "1.0" rlibc = { version = "1.0", optional = true } +spin = "0.4" [features] default = ["no_std"] diff --git a/src/fs/mod.rs b/src/fs/mod.rs new file mode 100644 index 0000000..8feccfa --- /dev/null +++ b/src/fs/mod.rs @@ -0,0 +1,171 @@ +use core::mem; + +use alloc::Vec; + +use error::Error; +use sector::{Address, SectorSize}; +use volume::Volume; +use sys::superblock::Superblock; +use sys::block_group::BlockGroupDescriptor; +use sys::inode::Inode as RawInode; + +pub mod sync; + +pub(crate) struct Struct { + pub inner: T, + pub offset: Address, +} + +impl From<(T, Address)> for Struct { + #[inline] + fn from((inner, offset): (T, Address)) -> Struct { + Struct { inner, offset } + } +} + +/// Safe wrapper for raw sys structs +pub struct Ext2> { + // TODO: should this have some different vis? + pub(crate) volume: V, + pub(crate) superblock: Struct, + pub(crate) block_groups: Struct, S>, +} + +impl> Ext2 { + pub fn new(volume: V) -> Result, Error> { + let superblock = unsafe { Struct::from(Superblock::find(&volume)?) }; + let block_groups_offset = Address::with_block_size( + superblock.inner.first_data_block + 1, + 0, + superblock.inner.log_block_size + 10, + ); + let block_groups_count = superblock + .inner + .block_group_count() + .map(|count| count as usize) + .map_err(|(a, b)| Error::BadBlockGroupCount { + by_blocks: a, + by_inodes: b, + })?; + let block_groups = unsafe { + BlockGroupDescriptor::find_descriptor_table( + &volume, + block_groups_offset, + block_groups_count, + )? + }; + let block_groups = Struct::from(block_groups); + Ok(Ext2 { + volume, + superblock, + block_groups, + }) + } + + pub fn version(&self) -> (u32, u16) { + ( + self.superblock.inner.rev_major, + self.superblock.inner.rev_minor, + ) + } + + pub fn inode_size(&self) -> usize { + if self.version().0 == 0 { + mem::size_of::() + } else { + // note: inodes bigger than 128 are not supported + self.superblock.inner.inode_size as usize + } + } + + pub fn inodes_count(&self) -> usize { + self.superblock.inner.inodes_per_group as _ + } + + pub fn total_inodes_count(&self) -> usize { + self.superblock.inner.inodes_count as _ + } + + pub fn block_group_count(&self) -> Result { + self.superblock + .inner + .block_group_count() + .map(|count| count as usize) + .map_err(|(a, b)| Error::BadBlockGroupCount { + by_blocks: a, + by_inodes: b, + }) + } + + pub fn total_block_count(&self) -> usize { + self.superblock.inner.blocks_count as _ + } + + pub fn free_block_count(&self) -> usize { + self.superblock.inner.free_blocks_count as _ + } + + pub fn block_size(&self) -> usize { + self.superblock.inner.block_size() + } + + pub fn log_block_size(&self) -> u32 { + self.superblock.inner.log_block_size + 10 + } + + pub fn sector_size(&self) -> usize { + S::SIZE + } + + pub fn log_sector_size(&self) -> u32 { + S::LOG_SIZE + } +} + +#[cfg(test)] +mod tests { + use std::fs::File; + use std::cell::RefCell; + + use sector::{Address, Size512}; + use volume::Volume; + + use super::Ext2; + + #[test] + fn file_len() { + let file = RefCell::new(File::open("ext2.img").unwrap()); + assert_eq!( + Address::::from(2048_u64) + - Address::::from(1024_u64), + Address::::new(2, 0) + ); + assert_eq!( + unsafe { + file.slice_unchecked( + Address::::from(1024_u64) + ..Address::::from(2048_u64), + ).len() + }, + 1024 + ); + } + + #[test] + fn file() { + let file = RefCell::new(File::open("ext2.img").unwrap()); + let fs = Ext2::::new(file); + + assert!( + fs.is_ok(), + "Err({:?})", + fs.err().unwrap_or_else(|| unreachable!()), + ); + + let fs = fs.unwrap(); + + let vers = fs.version(); + println!("version: {}.{}", vers.0, vers.1); + assert_eq!(128, fs.inode_size()); + } +} diff --git a/src/fs.rs b/src/fs/sync.rs similarity index 57% rename from src/fs.rs rename to src/fs/sync.rs index 17138a1..e9e0e00 100644 --- a/src/fs.rs +++ b/src/fs/sync.rs @@ -1,102 +1,155 @@ -use core::mem; -use core::slice; use core::fmt::{self, Debug}; use core::nonzero::NonZero; use alloc::Vec; +use alloc::arc::Arc; + +use spin::{Mutex, MutexGuard}; use error::Error; use sector::{Address, SectorSize}; -use volume::{Volume, VolumeSlice}; -use sys::superblock::Superblock; -use sys::block_group::BlockGroupDescriptor; +use volume::Volume; use sys::inode::Inode as RawInode; -struct Struct { - pub inner: T, - pub offset: Address, +use super::Ext2; + +pub struct Synced { + inner: Arc>, } -impl From<(T, Address)> for Struct { - #[inline] - fn from((inner, offset): (T, Address)) -> Struct { - Struct { inner, offset } +impl Synced { + pub fn with_inner(inner: T) -> Synced { + Synced { + inner: Arc::new(Mutex::new(inner)), + } + } + + pub fn inner<'a>(&'a self) -> MutexGuard<'a, T> { + self.inner.lock() } } -/// Safe wrapper for raw sys structs -pub struct Ext2> { - volume: V, - superblock: Struct, - block_groups: Struct, S>, +impl Clone for Synced { + fn clone(&self) -> Self { + Synced { + inner: self.inner.clone(), + } + } } -impl> Ext2 { - pub fn new(volume: V) -> Result, Error> { - let superblock = unsafe { Struct::from(Superblock::find(&volume)?) }; - let block_groups_offset = Address::with_block_size( - superblock.inner.first_data_block + 1, - 0, - superblock.inner.log_block_size + 10, - ); - let block_groups_count = superblock - .inner - .block_group_count() - .map(|count| count as usize) - .map_err(|(a, b)| Error::BadBlockGroupCount { - by_blocks: a, - by_inodes: b, - })?; - let block_groups = unsafe { - BlockGroupDescriptor::find_descriptor_table( - &volume, - block_groups_offset, - block_groups_count, - )? - }; - let block_groups = Struct::from(block_groups); - Ok(Ext2 { - volume, - superblock, - block_groups, - }) +impl> Synced> { + pub fn new(volume: V) -> Result>, Error> { + Ext2::new(volume).map(|inner| Synced::with_inner(inner)) } - #[allow(dead_code)] - fn update_global(&mut self) -> Result<(), Error> { - // superblock - { - let slice = VolumeSlice::from_cast( - &self.superblock.inner, - self.superblock.offset, + pub fn root_inode(&self) -> (Inode, Address) { + self.inode_nth(2).unwrap() + } + + pub fn inode_nth(&self, index: usize) -> Option<(Inode, Address)> { + self.inodes_nth(index).next() + } + + pub fn inodes(&self) -> Inodes { + self.inodes_nth(1) + } + + pub fn inodes_nth(&self, index: usize) -> Inodes { + assert!(index > 0, "inodes are 1-indexed"); + let inner = self.inner(); + Inodes { + fs: self.clone(), + log_block_size: inner.log_block_size(), + inode_size: inner.inode_size(), + inodes_per_group: inner.inodes_count(), + inodes_count: inner.total_inodes_count(), + index, + } + } + + pub fn sector_size(&self) -> usize { + S::SIZE + } + + pub fn log_sector_size(&self) -> u32 { + S::LOG_SIZE + } +} + +impl> Debug for Synced> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Synced>", S::SIZE) + } +} + +#[derive(Debug, Clone)] +pub struct Inodes> { + fs: Synced>, + log_block_size: u32, + inode_size: usize, + inodes_per_group: usize, + inodes_count: usize, + index: usize, +} + +impl> Iterator for Inodes { + type Item = (Inode, Address); + + fn next(&mut self) -> Option { + if self.index < self.inodes_count { + let block_group = (self.index - 1) / self.inodes_per_group; + let index = (self.index - 1) % self.inodes_per_group; + self.index += 1; + + let fs = self.fs.inner(); + + let inodes_block = + fs.block_groups.inner[block_group].inode_table_block; + + let offset = Address::with_block_size( + inodes_block, + (index * self.inode_size) as i32, + self.log_block_size, ); - let commit = slice.commit(); - self.volume.commit(commit).map_err(|err| err.into())?; + let raw = unsafe { + RawInode::find_inode(&fs.volume, offset, self.inode_size).ok() + }; + raw.map(|(raw, offset)| (Inode::new(self.fs.clone(), raw), offset)) + } else { + None } + } +} - // block group descriptors - let mut offset = self.block_groups.offset; - for descr in &self.block_groups.inner { - let slice = VolumeSlice::from_cast(descr, offset); - let commit = slice.commit(); - self.volume.commit(commit).map_err(|err| err.into())?; - offset = - offset + Address::from(mem::size_of::()); +#[derive(Debug)] +pub struct Inode> { + fs: Synced>, + inner: RawInode, +} + +impl> Clone for Inode { + fn clone(&self) -> Self { + Inode { + fs: self.fs.clone(), + inner: self.inner, } + } +} - Ok(()) +impl> Inode { + pub fn new(fs: Synced>, inner: RawInode) -> Inode { + Inode { fs, inner } } - pub fn read_inode<'vol>( - &'vol self, - buf: &mut [u8], - inode: &Inode<'vol, S, V>, - ) -> Result { - let total_size = inode.size(); - let block_size = self.block_size(); + pub fn read(&self, buf: &mut [u8]) -> Result { + let total_size = self.size(); + let block_size = { + let fs = self.fs.inner(); + fs.block_size() + }; let mut offset = 0; - for block in inode.blocks() { + for block in self.blocks() { match block { Ok((data, _)) => { let data_size = block_size @@ -113,180 +166,24 @@ impl> Ext2 { Ok(offset) } - pub fn write_inode<'vol>( - &'vol self, - _inode: &(Inode<'vol, S, V>, Address), - _buf: &[u8], - ) -> Result { - unimplemented!() - } - - pub fn root_inode<'vol>(&'vol self) -> (Inode<'vol, S, V>, Address) { - self.inode_nth(2).unwrap() - } - - pub fn inode_nth<'vol>( - &'vol self, - index: usize, - ) -> Option<(Inode<'vol, S, V>, Address)> { - self.inodes_nth(index).next() - } - - pub fn inodes<'vol>(&'vol self) -> Inodes<'vol, S, V> { - self.inodes_nth(1) - } - - pub fn inodes_nth<'vol>(&'vol self, index: usize) -> Inodes<'vol, S, V> { - assert!(index > 0, "inodes are 1-indexed"); - Inodes { - fs: self, - block_groups: &self.block_groups.inner, - log_block_size: self.log_block_size(), - inode_size: self.inode_size(), - inodes_per_group: self.inodes_count(), - inodes_count: self.total_inodes_count(), - index, - } - } - - fn superblock(&self) -> &Superblock { - &self.superblock.inner - } - - #[allow(dead_code)] - fn superblock_mut(&mut self) -> &mut Superblock { - &mut self.superblock.inner - } - - pub fn version(&self) -> (u32, u16) { - (self.superblock().rev_major, self.superblock().rev_minor) - } - - pub fn inode_size<'vol>(&'vol self) -> usize { - if self.version().0 == 0 { - mem::size_of::>() - } else { - // note: inodes bigger than 128 are not supported - self.superblock().inode_size as usize - } - } - - pub fn inodes_count(&self) -> usize { - self.superblock().inodes_per_group as _ - } - - pub fn total_inodes_count(&self) -> usize { - self.superblock().inodes_count as _ - } - - pub fn block_group_count(&self) -> Result { - self.superblock() - .block_group_count() - .map(|count| count as usize) - .map_err(|(a, b)| Error::BadBlockGroupCount { - by_blocks: a, - by_inodes: b, - }) - } - - pub fn total_block_count(&self) -> usize { - self.superblock().blocks_count as _ - } - - pub fn free_block_count(&self) -> usize { - self.superblock().free_blocks_count as _ - } - - pub fn block_size(&self) -> usize { - self.superblock().block_size() - } - - pub fn log_block_size(&self) -> u32 { - self.superblock().log_block_size + 10 - } - - pub fn sector_size(&self) -> usize { - S::SIZE - } - - pub fn log_sector_size(&self) -> u32 { - S::LOG_SIZE - } -} - -impl> Debug for Ext2 { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Ext2<{}>", S::SIZE) - } -} - -pub struct Inodes<'vol, S: SectorSize, V: 'vol + Volume> { - fs: &'vol Ext2, - block_groups: &'vol [BlockGroupDescriptor], - log_block_size: u32, - inode_size: usize, - inodes_per_group: usize, - inodes_count: usize, - index: usize, -} - -impl<'vol, S: SectorSize, V: 'vol + Volume> Iterator - for Inodes<'vol, S, V> -{ - type Item = (Inode<'vol, S, V>, Address); - - fn next(&mut self) -> Option { - if self.index < self.inodes_count { - let block_group = (self.index - 1) / self.inodes_per_group; - let index = (self.index - 1) % self.inodes_per_group; - self.index += 1; - - let inodes_block = self.block_groups[block_group].inode_table_block; - - let offset = Address::with_block_size( - inodes_block, - (index * self.inode_size) as i32, - self.log_block_size, - ); - let raw = unsafe { - RawInode::find_inode(&self.fs.volume, offset, self.inode_size) - .ok() - }; - raw.map(|(raw, offset)| (Inode::new(self.fs, raw), offset)) - } else { - None - } - } -} - -#[derive(Debug, Clone)] -pub struct Inode<'vol, S: SectorSize, V: 'vol + Volume> { - fs: &'vol Ext2, - inner: RawInode, -} - -impl<'vol, S: SectorSize, V: 'vol + Volume> Inode<'vol, S, V> { - pub fn new(fs: &'vol Ext2, inner: RawInode) -> Inode<'vol, S, V> { - Inode { fs, inner } - } - - pub fn blocks<'inode>(&'inode self) -> InodeBlocks<'vol, 'inode, S, V> { + pub fn blocks(&self) -> InodeBlocks { InodeBlocks { - inode: self, + inode: self.clone(), index: 0, } } - pub fn directory<'inode>( - &'inode self, - ) -> Option> { + pub fn directory(&self) -> Option> { use sys::inode::TypePerm; if unsafe { self.inner.type_perm.contains(TypePerm::DIRECTORY) } { Some(Directory { blocks: self.blocks(), offset: 0, buffer: None, - block_size: self.fs.block_size(), + block_size: { + let fs = self.fs.inner(); + fs.block_size() + }, }) } else { None @@ -334,8 +231,10 @@ impl<'vol, S: SectorSize, V: 'vol + Volume> Inode<'vol, S, V> { } } - let bs4 = self.fs.block_size() / 4; - let log_block_size = self.fs.log_block_size(); + let fs = self.fs.inner(); + + let bs4 = fs.block_size() / 4; + let log_block_size = fs.log_block_size(); if index < 12 { return Ok(NonZero::new(self.inner.direct_pointer[index])); @@ -345,7 +244,7 @@ impl<'vol, S: SectorSize, V: 'vol + Volume> Inode<'vol, S, V> { if index < bs4 { let block = self.inner.indirect_pointer; - return block_index(&self.fs.volume, block, index, log_block_size); + return block_index(&fs.volume, block, index, log_block_size); } index -= bs4; @@ -353,7 +252,7 @@ impl<'vol, S: SectorSize, V: 'vol + Volume> Inode<'vol, S, V> { if index < bs4 * bs4 { let indirect_index = index >> (log_block_size + 2); let block = match block_index( - &self.fs.volume, + &fs.volume, self.inner.doubly_indirect, indirect_index, log_block_size, @@ -363,7 +262,7 @@ impl<'vol, S: SectorSize, V: 'vol + Volume> Inode<'vol, S, V> { Err(err) => return Err(err), }; return block_index( - &self.fs.volume, + &fs.volume, block, index & (bs4 - 1), log_block_size, @@ -375,7 +274,7 @@ impl<'vol, S: SectorSize, V: 'vol + Volume> Inode<'vol, S, V> { if index < bs4 * bs4 * bs4 { let doubly_index = index >> (2 * log_block_size + 4); let indirect = match block_index( - &self.fs.volume, + &fs.volume, self.inner.triply_indirect, doubly_index, log_block_size, @@ -386,7 +285,7 @@ impl<'vol, S: SectorSize, V: 'vol + Volume> Inode<'vol, S, V> { }; let indirect_index = (index >> (log_block_size + 2)) & (bs4 - 1); let block = match block_index( - &self.fs.volume, + &fs.volume, indirect as u32, indirect_index, log_block_size, @@ -396,7 +295,7 @@ impl<'vol, S: SectorSize, V: 'vol + Volume> Inode<'vol, S, V> { Err(err) => return Err(err), }; return block_index( - &self.fs.volume, + &fs.volume, block, index & (bs4 - 1), log_block_size, @@ -439,20 +338,14 @@ impl<'vol, S: SectorSize, V: 'vol + Volume> Inode<'vol, S, V> { } } -pub struct InodeBlocks< - 'vol: 'inode, - 'inode, - S: SectorSize, - V: 'vol + Volume, -> { - inode: &'inode Inode<'vol, S, V>, +#[derive(Debug, Clone)] +pub struct InodeBlocks> { + inode: Inode, index: usize, } -impl<'vol, 'inode, S: SectorSize, V: 'vol + Volume> Iterator - for InodeBlocks<'vol, 'inode, S, V> -{ - type Item = Result<(VolumeSlice<'vol, u8, S>, Address), Error>; +impl> Iterator for InodeBlocks { + type Item = Result<(Vec, Address), Error>; fn next(&mut self) -> Option { let block = self.inode.try_block(self.index); @@ -463,38 +356,31 @@ impl<'vol, 'inode, S: SectorSize, V: 'vol + Volume> Iterator }; self.index += 1; + let fs = self.inode.fs.inner(); let block = block.get(); - let log_block_size = self.inode.fs.log_block_size(); + let log_block_size = fs.log_block_size(); let offset = Address::with_block_size(block, 0, log_block_size); let end = Address::with_block_size(block + 1, 0, log_block_size); - let slice = self.inode - .fs - .volume + let slice = fs.volume .slice(offset..end) - .map(|slice| (slice, offset)) + .map(|slice| (slice.to_vec(), offset)) .map_err(|err| err.into()); Some(slice) } } -pub struct Directory< - 'vol: 'inode, - 'inode, - S: SectorSize, - V: 'vol + Volume, -> { - blocks: InodeBlocks<'vol, 'inode, S, V>, +#[derive(Debug, Clone)] +pub struct Directory> { + blocks: InodeBlocks, offset: usize, - buffer: Option>, + buffer: Option>, block_size: usize, } -impl<'vol, 'inode, S: SectorSize, V: 'vol + Volume> Iterator - for Directory<'vol, 'inode, S, V> -{ - type Item = Result, Error>; +impl> Iterator for Directory { + type Item = Result; fn next(&mut self) -> Option { if self.buffer.is_none() || self.offset >= self.block_size { @@ -520,8 +406,7 @@ impl<'vol, 'inode, S: SectorSize, V: 'vol + Volume> Iterator let len = buffer[6]; let ty = buffer[7]; - let ptr = unsafe { buffer.as_ptr().add(8) }; - let name = unsafe { slice::from_raw_parts(ptr, len as usize) }; + let name = buffer[8..8 + len as usize].to_vec(); self.offset += size as usize; @@ -533,8 +418,9 @@ impl<'vol, 'inode, S: SectorSize, V: 'vol + Volume> Iterator } } -pub struct DirectoryEntry<'a> { - pub name: &'a [u8], +#[derive(Clone)] +pub struct DirectoryEntry { + pub name: Vec, pub inode: usize, pub ty: u8, } @@ -544,34 +430,15 @@ mod tests { use std::fs::File; use std::cell::RefCell; - use sector::{Address, SectorSize, Size512}; + use sector::{SectorSize, Size512}; use volume::Volume; - use super::{Ext2, Inode}; - - #[test] - fn file_len() { - let file = RefCell::new(File::open("ext2.img").unwrap()); - assert_eq!( - Address::::from(2048_u64) - - Address::::from(1024_u64), - Address::::new(2, 0) - ); - assert_eq!( - unsafe { - file.slice_unchecked( - Address::::from(1024_u64) - ..Address::::from(2048_u64), - ).len() - }, - 1024 - ); - } + use super::{Ext2, Inode, Synced}; #[test] fn file() { let file = RefCell::new(File::open("ext2.img").unwrap()); - let fs = Ext2::::new(file); + let fs = Synced::>::new(file); assert!( fs.is_ok(), @@ -580,16 +447,17 @@ mod tests { ); let fs = fs.unwrap(); + let inner = fs.inner(); - let vers = fs.version(); + let vers = inner.version(); println!("version: {}.{}", vers.0, vers.1); - assert_eq!(128, fs.inode_size()); + assert_eq!(128, inner.inode_size()); } #[test] fn inodes() { let file = RefCell::new(File::open("ext2.img").unwrap()); - let fs = Ext2::::new(file); + let fs = Synced::>::new(file); assert!( fs.is_ok(), @@ -609,7 +477,7 @@ mod tests { fn inode_blocks() { use std::str; let file = RefCell::new(File::open("ext2.img").unwrap()); - let fs = Ext2::::new(file).unwrap(); + let fs = Synced::>::new(file).unwrap(); let inodes = fs.inodes().filter(|inode| { inode.0.in_use() && inode.0.uid() == 1000 && inode.0.size() < 1024 @@ -619,7 +487,10 @@ mod tests { let size = inode.0.size(); for block in inode.0.blocks() { let (data, _) = block.unwrap(); - assert_eq!(data.len(), fs.block_size()); + assert_eq!(data.len(), { + let fs = fs.inner(); + fs.block_size() + }); println!("{:?}", &data[..size]); let _ = str::from_utf8(&data[..size]) .map(|string| println!("{}", string)); @@ -630,7 +501,7 @@ mod tests { #[test] fn read_inode() { let file = RefCell::new(File::open("ext2.img").unwrap()); - let fs = Ext2::::new(file).unwrap(); + let fs = Synced::>::new(file).unwrap(); let inodes = fs.inodes().filter(|inode| { inode.0.in_use() && inode.0.uid() == 1000 && inode.0.size() < 1024 @@ -640,7 +511,7 @@ mod tests { unsafe { buf.set_len(inode.size()); } - let size = fs.read_inode(&mut buf[..], &inode); + let size = inode.read(&mut buf[..]); assert!(size.is_ok()); let size = size.unwrap(); assert_eq!(size, inode.size()); @@ -653,7 +524,7 @@ mod tests { #[test] fn read_big() { let file = RefCell::new(File::open("ext2.img").unwrap()); - let fs = Ext2::::new(file).unwrap(); + let fs = Synced::>::new(file).unwrap(); let inodes = fs.inodes().filter(|inode| { inode.0.in_use() && inode.0.uid() == 1000 @@ -664,7 +535,7 @@ mod tests { unsafe { buf.set_len(inode.size()); } - let size = fs.read_inode(&mut buf[..], &inode); + let size = inode.read(&mut buf[..]); assert!(size.is_ok()); let size = size.unwrap(); assert_eq!(size, inode.size()); @@ -687,15 +558,15 @@ mod tests { use std::str; fn walk<'vol, S: SectorSize, V: Volume>( - fs: &'vol Ext2, - inode: Inode<'vol, S, V>, + fs: &'vol Synced>, + inode: Inode, name: String, ) { inode.directory().map(|dir| { for entry in dir { assert!(entry.is_ok()); let entry = entry.unwrap(); - let entry_name = str::from_utf8(entry.name).unwrap_or("?"); + let entry_name = str::from_utf8(&entry.name).unwrap_or("?"); println!("{}/{} => {}", name, entry_name, entry.inode,); if entry_name != "." && entry_name != ".." { walk( @@ -709,7 +580,7 @@ mod tests { } let file = RefCell::new(File::open("ext2.img").unwrap()); - let fs = Ext2::::new(file).unwrap(); + let fs = Synced::>::new(file).unwrap(); let (root, _) = fs.root_inode(); walk(&fs, root, String::new()); diff --git a/src/lib.rs b/src/lib.rs index 3b3be42..f6e749d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ extern crate alloc; #[macro_use] extern crate bitflags; +extern crate spin; #[cfg(any(test, not(feature = "no_std")))] extern crate core; diff --git a/src/volume/mod.rs b/src/volume/mod.rs index c5eaebf..f1b08e3 100644 --- a/src/volume/mod.rs +++ b/src/volume/mod.rs @@ -31,6 +31,7 @@ pub trait Volume { ) -> Result, Self::Error>; } +#[derive(Debug, Clone, PartialEq, Hash)] pub struct VolumeSlice<'a, T: 'a + Clone, S: SectorSize> { inner: Cow<'a, [T]>, index: Address,