Implement a directory entry iterator for inode
Calling `directory()` on an `Inode` returns an `Iterator` over entries in a directory at that `Inode`, it it's a directory, or `None` otherwise. An example of recursively walking the directory tree is included in test `walkdir`. This takes us a step closer to `fopen` (#2)
This commit is contained in:
parent
f83ad71b3c
commit
bebfa33564
128
src/fs.rs
128
src/fs.rs
|
@ -1,4 +1,5 @@
|
||||||
use core::mem;
|
use core::mem;
|
||||||
|
use core::slice;
|
||||||
use core::fmt::{self, Debug};
|
use core::fmt::{self, Debug};
|
||||||
use core::nonzero::NonZero;
|
use core::nonzero::NonZero;
|
||||||
|
|
||||||
|
@ -95,7 +96,7 @@ impl<S: Size, V: Volume<u8, Address<S>>> Ext2<S, V> {
|
||||||
let block_size = self.block_size();
|
let block_size = self.block_size();
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
|
|
||||||
for block in InodeBlocks::new(&inode) {
|
for block in inode.blocks() {
|
||||||
match block {
|
match block {
|
||||||
Ok((data, _)) => {
|
Ok((data, _)) => {
|
||||||
let data_size = block_size
|
let data_size = block_size
|
||||||
|
@ -147,9 +148,7 @@ impl<S: Size, V: Volume<u8, Address<S>>> Ext2<S, V> {
|
||||||
index,
|
index,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: Size, V: Volume<u8, Address<S>>> Ext2<S, V> {
|
|
||||||
fn superblock(&self) -> &Superblock {
|
fn superblock(&self) -> &Superblock {
|
||||||
&self.superblock.inner
|
&self.superblock.inner
|
||||||
}
|
}
|
||||||
|
@ -271,6 +270,27 @@ impl<'a, S: 'a + Size, V: 'a + Volume<u8, Address<S>>> Inode<'a, S, V> {
|
||||||
Inode { fs, inner }
|
Inode { fs, inner }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn blocks<'b>(&'b self) -> InodeBlocks<'a, 'b, S, V> {
|
||||||
|
InodeBlocks {
|
||||||
|
inode: self,
|
||||||
|
index: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn directory<'b>(&'b self) -> Option<Directory<'a, 'b, S, V>> {
|
||||||
|
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(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
self.try_block(index).ok().and_then(|block| block)
|
||||||
}
|
}
|
||||||
|
@ -423,14 +443,6 @@ pub struct InodeBlocks<'a: 'b, 'b, S: 'a + Size, V: 'a + Volume<u8, Address<S>>>
|
||||||
index: usize,
|
index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b, S: Size, V: 'a + Volume<u8, Address<S>>>
|
|
||||||
InodeBlocks<'a, 'b, S, V>
|
|
||||||
{
|
|
||||||
pub fn new(inode: &'b Inode<'a, S, V>) -> InodeBlocks<'a, 'b, S, V> {
|
|
||||||
InodeBlocks { inode, index: 0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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>
|
||||||
{
|
{
|
||||||
|
@ -461,15 +473,70 @@ impl<'a, 'b, S: Size, V: 'a + Volume<u8, Address<S>>> Iterator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Directory<'a: 'b, 'b, S: 'a + Size, V: 'a + Volume<u8, Address<S>>> {
|
||||||
|
blocks: InodeBlocks<'a, 'b, S, V>,
|
||||||
|
offset: usize,
|
||||||
|
buffer: Option<VolumeSlice<'a, u8, Address<S>>>,
|
||||||
|
block_size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b, S: Size, V: 'a + Volume<u8, Address<S>>> Iterator
|
||||||
|
for Directory<'a, 'b, S, V>
|
||||||
|
{
|
||||||
|
type Item = Result<DirectoryEntry<'a>, Error>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.buffer.is_none() || self.offset >= self.block_size {
|
||||||
|
self.buffer = match self.blocks.next() {
|
||||||
|
None => return None,
|
||||||
|
Some(Ok((block, _))) => Some(block),
|
||||||
|
Some(Err(err)) => return Some(Err(err)),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let buffer = &self.buffer.as_ref().unwrap()[self.offset..];
|
||||||
|
|
||||||
|
let inode = buffer[0] as u32 | (buffer[1] as u32) << 8
|
||||||
|
| (buffer[2] as u32) << 16
|
||||||
|
| (buffer[3] as u32) << 24;
|
||||||
|
if inode == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let size = buffer[4] as u16 | (buffer[5] as u16) << 8;
|
||||||
|
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) };
|
||||||
|
|
||||||
|
self.offset += size as usize;
|
||||||
|
|
||||||
|
Some(Ok(DirectoryEntry {
|
||||||
|
name: name,
|
||||||
|
inode: inode as usize,
|
||||||
|
ty: ty,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DirectoryEntry<'a> {
|
||||||
|
pub name: &'a [u8],
|
||||||
|
pub inode: usize,
|
||||||
|
pub ty: u8,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
use sector::{Address, Size512};
|
use sector::{Address, Size, Size512};
|
||||||
use volume::Volume;
|
use volume::Volume;
|
||||||
|
|
||||||
use super::{Ext2, InodeBlocks};
|
use super::{Ext2, Inode};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn file_len() {
|
fn file_len() {
|
||||||
|
@ -539,7 +606,7 @@ mod tests {
|
||||||
for inode in inodes {
|
for inode in inodes {
|
||||||
println!("{:?}", inode.0);
|
println!("{:?}", inode.0);
|
||||||
let size = inode.0.size();
|
let size = inode.0.size();
|
||||||
for block in InodeBlocks::new(&inode.0) {
|
for block in inode.0.blocks() {
|
||||||
let (data, _) = block.unwrap();
|
let (data, _) = block.unwrap();
|
||||||
assert_eq!(data.len(), fs.block_size());
|
assert_eq!(data.len(), fs.block_size());
|
||||||
println!("{:?}", &data[..size]);
|
println!("{:?}", &data[..size]);
|
||||||
|
@ -603,4 +670,37 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn walkdir() {
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
fn walk<'a, S: Size, V: Volume<u8, Address<S>>>(
|
||||||
|
fs: &'a Ext2<S, V>,
|
||||||
|
inode: Inode<'a, S, V>,
|
||||||
|
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("?");
|
||||||
|
println!("{}/{} => {}", name, entry_name, entry.inode,);
|
||||||
|
if entry_name != "." && entry_name != ".." {
|
||||||
|
walk(
|
||||||
|
fs,
|
||||||
|
fs.inode_nth(entry.inode).unwrap().0,
|
||||||
|
format!("{}/{}", name, entry_name),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = RefCell::new(File::open("ext2.img").unwrap());
|
||||||
|
let fs = Ext2::<Size512, _>::new(file).unwrap();
|
||||||
|
|
||||||
|
let (root, _) = fs.root_inode();
|
||||||
|
walk(&fs, root, String::new());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ use volume::Volume;
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Inode {
|
pub struct Inode {
|
||||||
/// Type and Permissions (see below)
|
/// Type and Permissions (see below)
|
||||||
pub type_perm: u16,
|
pub type_perm: TypePerm,
|
||||||
/// User ID
|
/// User ID
|
||||||
pub uid: u16,
|
pub uid: u16,
|
||||||
/// Lower 32 bits of size in bytes
|
/// Lower 32 bits of size in bytes
|
||||||
|
@ -39,7 +39,7 @@ pub struct Inode {
|
||||||
/// to the inode.
|
/// to the inode.
|
||||||
pub sectors_count: u32,
|
pub sectors_count: u32,
|
||||||
/// Flags
|
/// Flags
|
||||||
pub flags: u32,
|
pub flags: Flags,
|
||||||
/// Operating System Specific value #1
|
/// Operating System Specific value #1
|
||||||
pub _os_specific_1: [u8; 4],
|
pub _os_specific_1: [u8; 4],
|
||||||
/// Direct block pointers
|
/// Direct block pointers
|
||||||
|
@ -167,7 +167,7 @@ bitflags! {
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
pub struct InodeFlags: u32 {
|
pub struct Flags: u32 {
|
||||||
/// Secure deletion (not used)
|
/// Secure deletion (not used)
|
||||||
const SECURE_DEL = 0x00000001;
|
const SECURE_DEL = 0x00000001;
|
||||||
/// Keep a copy of data when deleted (not used)
|
/// Keep a copy of data when deleted (not used)
|
||||||
|
@ -192,3 +192,20 @@ bitflags! {
|
||||||
const JOURNAL_DATA = 0x00040000;
|
const JOURNAL_DATA = 0x00040000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Unknown entry type
|
||||||
|
pub const UNKNOWN: u8 = 0;
|
||||||
|
/// FIFO entry type
|
||||||
|
pub const FIFO: u8 = 1;
|
||||||
|
/// Character device entry type
|
||||||
|
pub const CHAR_DEVICE: u8 = 2;
|
||||||
|
/// Directory entry type
|
||||||
|
pub const DIRECTORY: u8 = 3;
|
||||||
|
/// Block device entry type
|
||||||
|
pub const BLOCK_DEVICE: u8 = 4;
|
||||||
|
/// Regular file entry type
|
||||||
|
pub const FILE: u8 = 5;
|
||||||
|
/// Symbolic link entry type
|
||||||
|
pub const SYMLINK: u8 = 6;
|
||||||
|
/// Unix socket entry type
|
||||||
|
pub const SOCKET: u8 = 7;
|
||||||
|
|
Reference in a new issue