enable reading the entire contents of inodes

This commit is contained in:
Szymon Walter 2018-03-21 23:05:41 +01:00
parent 117b8d4e75
commit f83ad71b3c
2 changed files with 153 additions and 52 deletions

BIN
ext2.img

Binary file not shown.

205
src/fs.rs
View file

@ -92,25 +92,24 @@ impl<S: Size, V: Volume<u8, Address<S>>> Ext2<S, V> {
inode: &Inode<'a, S, V>, inode: &Inode<'a, S, V>,
) -> Result<usize, Error> { ) -> Result<usize, Error> {
let total_size = inode.size(); let total_size = inode.size();
let mut read_size = 0;
let block_size = self.block_size(); let block_size = self.block_size();
let offset = 0; let mut offset = 0;
for block in InodeBlocks::new(&inode) { for block in InodeBlocks::new(&inode) {
match block { match block {
Ok((data, _)) => { Ok((data, _)) => {
let data_size = block_size let data_size = block_size
.min(total_size - read_size) .min(total_size - offset)
.min(buf.len() - offset); .min(buf.len() - offset);
let end = offset + data_size; let end = offset + data_size;
buf[offset..end].copy_from_slice(&data[..data_size]); buf[offset..end].copy_from_slice(&data[..data_size]);
read_size += data_size; offset += data_size;
} }
Err(err) => return Err(err.into()), Err(err) => return Err(err.into()),
} }
} }
Ok(read_size) Ok(offset)
} }
pub fn write_inode<'a>( pub fn write_inode<'a>(
@ -273,6 +272,13 @@ impl<'a, S: 'a + Size, V: 'a + Volume<u8, Address<S>>> Inode<'a, S, V> {
} }
pub fn block(&self, index: usize) -> Option<NonZero<u32>> { pub fn block(&self, index: usize) -> Option<NonZero<u32>> {
self.try_block(index).ok().and_then(|block| block)
}
pub fn try_block(
&self,
mut index: usize,
) -> Result<Option<NonZero<u32>>, Error> {
// number of blocks in direct table: 12 // number of blocks in direct table: 12
// number of blocks in indirect table: block_size/4 // number of blocks in indirect table: block_size/4
// why? // why?
@ -287,29 +293,95 @@ impl<'a, S: 'a + Size, V: 'a + Volume<u8, Address<S>>> Inode<'a, S, V> {
// - that's n/4 blocks with n/4 pointers each = (n/4)^2 // - that's n/4 blocks with n/4 pointers each = (n/4)^2
// number of blocks in triply table: (block_size/4)^3 // number of blocks in triply table: (block_size/4)^3
let bs4 = self.fs.block_size() / 4; fn block_index<S: Size, V: Volume<u8, Address<S>>>(
if index < 12 { volume: &V,
NonZero::new(self.inner.direct_pointer[index]) block: u32,
} else if index < bs4 + 12 { index: usize,
let block = self.inner.indirect_pointer; log_block_size: u32,
let offset = index - 12; ) -> Result<Option<NonZero<u32>>, Error> {
let addr = Address::with_block_size( let offset = (index * 4) as i32;
block, let end = offset + 4;
offset as i32, let addr = Address::with_block_size(block, offset, log_block_size);
self.fs.log_block_size(), let end = Address::with_block_size(block, end, log_block_size);
); let block = volume.slice(addr..end);
let size = Address::from(4_u64); match block {
let slice = self.fs.volume.slice(addr..addr + size); Ok(block) => unsafe {
slice.ok().and_then(|slice| unsafe { Ok(NonZero::new(block.dynamic_cast::<u32>().0))
NonZero::new(u32::from_le(slice.dynamic_cast::<u32>().0)) },
}) Err(err) => Err(err.into()),
} else if index < bs4 * bs4 + bs4 + 12 { }
unimplemented!("doubly indirect pointer table");
} else if index < bs4 * bs4 * bs4 + bs4 * bs4 + bs4 + 12 {
unimplemented!("triply indirect pointer table");
} else {
None
} }
let bs4 = self.fs.block_size() / 4;
let log_block_size = self.fs.log_block_size();
if index < 12 {
return Ok(NonZero::new(self.inner.direct_pointer[index]));
}
index -= 12;
if index < bs4 {
let block = self.inner.indirect_pointer;
return block_index(&self.fs.volume, block, index, log_block_size);
}
index -= bs4;
if index < bs4 * bs4 {
let indirect_index = index >> (log_block_size + 2);
let block = match block_index(
&self.fs.volume,
self.inner.doubly_indirect,
indirect_index,
log_block_size,
) {
Ok(Some(block)) => block.get(),
Ok(None) => return Ok(None),
Err(err) => return Err(err),
};
return block_index(
&self.fs.volume,
block,
index & (bs4 - 1),
log_block_size,
);
}
index -= bs4 * bs4;
if index < bs4 * bs4 * bs4 {
let doubly_index = index >> (2 * log_block_size + 4);
let indirect = match block_index(
&self.fs.volume,
self.inner.triply_indirect,
doubly_index,
log_block_size,
) {
Ok(Some(block)) => block.get(),
Ok(None) => return Ok(None),
Err(err) => return Err(err),
};
let indirect_index = (index >> (log_block_size + 2)) & (bs4 - 1);
let block = match block_index(
&self.fs.volume,
indirect as u32,
indirect_index,
log_block_size,
) {
Ok(Some(block)) => block.get(),
Ok(None) => return Ok(None),
Err(err) => return Err(err),
};
return block_index(
&self.fs.volume,
block,
index & (bs4 - 1),
log_block_size,
);
}
Ok(None)
} }
pub fn in_use(&self) -> bool { pub fn in_use(&self) -> bool {
@ -362,33 +434,30 @@ impl<'a, 'b, S: Size, V: 'a + Volume<u8, Address<S>>>
impl<'a, 'b, S: Size, V: 'a + Volume<u8, Address<S>>> Iterator impl<'a, 'b, S: Size, V: 'a + Volume<u8, Address<S>>> Iterator
for InodeBlocks<'a, 'b, S, V> for InodeBlocks<'a, 'b, S, V>
{ {
type Item = Result<(VolumeSlice<'a, u8, Address<S>>, Address<S>), V::Error>; type Item = Result<(VolumeSlice<'a, u8, Address<S>>, Address<S>), Error>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let block = self.inode.block(self.index); let block = self.inode.try_block(self.index);
block let block = match block {
.map(|block| { Ok(Some(ok)) => ok,
let block = block.get(); Ok(None) => return None,
self.index += 1; Err(err) => return Some(Err(err)),
Address::with_block_size( };
block,
0, self.index += 1;
self.inode.fs.log_block_size(),
) let block = block.get();
..Address::with_block_size( let log_block_size = self.inode.fs.log_block_size();
block + 1, let offset = Address::with_block_size(block, 0, log_block_size);
0, let end = Address::with_block_size(block + 1, 0, log_block_size);
self.inode.fs.log_block_size(),
) let slice = self.inode
}) .fs
.map(|block| { .volume
let offset = block.start; .slice(offset..end)
self.inode .map(|slice| (slice, offset))
.fs .map_err(|err| err.into());
.volume Some(slice)
.slice(block)
.map(|slice| (slice, offset))
})
} }
} }
@ -502,4 +571,36 @@ mod tests {
} }
} }
} }
#[test]
fn read_big() {
let file = RefCell::new(File::open("ext2.img").unwrap());
let fs = Ext2::<Size512, _>::new(file).unwrap();
let inodes = fs.inodes().filter(|inode| {
inode.0.in_use() && inode.0.uid() == 1000
&& inode.0.size() == 537600
});
for (inode, _) in inodes {
let mut buf = Vec::with_capacity(inode.size());
unsafe {
buf.set_len(inode.size());
}
let size = fs.read_inode(&mut buf[..], &inode);
assert!(size.is_ok());
let size = size.unwrap();
assert_eq!(size, inode.size());
unsafe {
buf.set_len(size);
}
for (i, &x) in buf.iter().enumerate() {
if i & 1 == 0 {
assert_eq!(x, b'u', "{}", i);
} else {
assert_eq!(x, b'\n', "{}", i);
}
}
}
}
} }