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>,
) -> Result<usize, Error> {
let total_size = inode.size();
let mut read_size = 0;
let block_size = self.block_size();
let offset = 0;
let mut offset = 0;
for block in InodeBlocks::new(&inode) {
match block {
Ok((data, _)) => {
let data_size = block_size
.min(total_size - read_size)
.min(total_size - offset)
.min(buf.len() - offset);
let end = offset + data_size;
buf[offset..end].copy_from_slice(&data[..data_size]);
read_size += data_size;
offset += data_size;
}
Err(err) => return Err(err.into()),
}
}
Ok(read_size)
Ok(offset)
}
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>> {
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 indirect table: block_size/4
// 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
// number of blocks in triply table: (block_size/4)^3
let bs4 = self.fs.block_size() / 4;
if index < 12 {
NonZero::new(self.inner.direct_pointer[index])
} else if index < bs4 + 12 {
let block = self.inner.indirect_pointer;
let offset = index - 12;
let addr = Address::with_block_size(
block,
offset as i32,
self.fs.log_block_size(),
);
let size = Address::from(4_u64);
let slice = self.fs.volume.slice(addr..addr + size);
slice.ok().and_then(|slice| unsafe {
NonZero::new(u32::from_le(slice.dynamic_cast::<u32>().0))
})
} 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
fn block_index<S: Size, V: Volume<u8, Address<S>>>(
volume: &V,
block: u32,
index: usize,
log_block_size: u32,
) -> Result<Option<NonZero<u32>>, Error> {
let offset = (index * 4) as i32;
let end = offset + 4;
let addr = Address::with_block_size(block, offset, log_block_size);
let end = Address::with_block_size(block, end, log_block_size);
let block = volume.slice(addr..end);
match block {
Ok(block) => unsafe {
Ok(NonZero::new(block.dynamic_cast::<u32>().0))
},
Err(err) => Err(err.into()),
}
}
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 {
@ -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
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> {
let block = self.inode.block(self.index);
block
.map(|block| {
let block = block.get();
self.index += 1;
Address::with_block_size(
block,
0,
self.inode.fs.log_block_size(),
)
..Address::with_block_size(
block + 1,
0,
self.inode.fs.log_block_size(),
)
})
.map(|block| {
let offset = block.start;
self.inode
.fs
.volume
.slice(block)
.map(|slice| (slice, offset))
})
let block = self.inode.try_block(self.index);
let block = match block {
Ok(Some(ok)) => ok,
Ok(None) => return None,
Err(err) => return Some(Err(err)),
};
self.index += 1;
let block = block.get();
let log_block_size = self.inode.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
.slice(offset..end)
.map(|slice| (slice, offset))
.map_err(|err| err.into());
Some(slice)
}
}
@ -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);
}
}
}
}
}