From dd95456ec490d9119274f31e2f4fc4d1679477fc Mon Sep 17 00:00:00 2001 From: tzlil Date: Fri, 17 Feb 2023 13:25:23 +0200 Subject: initial commit --- .envrc | 1 + .github/workflows/build_nix.yml | 13 +++ .gitignore | 9 ++ Cargo.lock | 130 +++++++++++++++++++++ Cargo.toml | 12 ++ README | 1 + default.nix | 7 ++ ext2.img | Bin 0 -> 33554432 bytes flake.lock | 82 +++++++++++++ flake.nix | 64 ++++++++++ shell.nix | 7 ++ src/main.rs | 213 +++++++++++++++++++++++++++++++++ src/structs.rs | 253 ++++++++++++++++++++++++++++++++++++++++ 13 files changed, 792 insertions(+) create mode 100644 .envrc create mode 100644 .github/workflows/build_nix.yml create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README create mode 100644 default.nix create mode 100644 ext2.img create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 shell.nix create mode 100644 src/main.rs create mode 100644 src/structs.rs diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.github/workflows/build_nix.yml b/.github/workflows/build_nix.yml new file mode 100644 index 0000000..2f684e1 --- /dev/null +++ b/.github/workflows/build_nix.yml @@ -0,0 +1,13 @@ +name: "Build legacy Nix package on Ubuntu" + +on: + push: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: cachix/install-nix-action@v12 + - name: Building package + run: nix-build . -A defaultPackage.x86_64-linux diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7bb1a2e --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +/target +.vscode/settings.json +.direnv + +# Added by cargo +# +# already existing elements were commented out + +#/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..9a0d9fe --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,130 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "byte-strings-proc_macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e78e8673d97234c7a07636474b02c92fad06a0f26f70581aa46aee124c508e5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "ext2" +version = "0.1.0" +dependencies = [ + "bitflags", + "null-terminated", + "uuid", + "zerocopy", +] + +[[package]] +name = "null-terminated" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3ab2c625aa6bd21571b3599f3c4318b4456ec89efc62c0b47c1a8de14e30bf" +dependencies = [ + "byte-strings-proc_macros", + "unreachable", + "utf", +] + +[[package]] +name = "proc-macro2" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +dependencies = [ + "void", +] + +[[package]] +name = "utf" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8704f91734e57e97633bff5afe5c2893e5aca5bd83fac44736460deca2611a76" + +[[package]] +name = "uuid" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "zerocopy" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "332f188cc1bcf1fe1064b8c58d150f497e697f49774aa846f2dc949d9a25f236" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6505e6815af7de1746a08f69c69606bb45695a17149517680f3b2149713b19a3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6ac9b4c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "ext2" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +zerocopy = "0.6.1" +bitflags = "1.3.2" +uuid = "1.3.0" +null-terminated = "0.3.17" \ No newline at end of file diff --git a/README b/README new file mode 100644 index 0000000..ab18136 --- /dev/null +++ b/README @@ -0,0 +1 @@ +zerocopy ext2 parser diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..39bacff --- /dev/null +++ b/default.nix @@ -0,0 +1,7 @@ +(import ( + fetchTarball { + url = "https://github.com/edolstra/flake-compat/archive/99f1c2157fba4bfe6211a321fd0ee43199025dbf.tar.gz"; + sha256 = "0x2jn3vrawwv9xp15674wjz9pixwjyj3j771izayl962zziivbx2"; } +) { + src = ./.; +}).defaultNix diff --git a/ext2.img b/ext2.img new file mode 100644 index 0000000..bac35bb Binary files /dev/null and b/ext2.img differ diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..9a2d857 --- /dev/null +++ b/flake.lock @@ -0,0 +1,82 @@ +{ + "nodes": { + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1676096558, + "narHash": "sha256-FRJWsgaJ45L/fPP8a7WJMiU/DxMaaKdRFCbbEPcYreg=", + "owner": "nix-community", + "repo": "fenix", + "rev": "180919967713768e37ad2def247bea41cef263b3", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1659446231, + "narHash": "sha256-hekabNdTdgR/iLsgce5TGWmfIDZ86qjPhxDg/8TlzhE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "eabc38219184cc3e04a974fe31857d8e0eac098d", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-21.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "fenix": "fenix", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1676059854, + "narHash": "sha256-X3U/sewOuNVMi1k3Rt4E5cCL68TeCvFx+e/C0X39+U0=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "8011029d3a0f4014217e1ade75688c0f3c5305db", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..ff46275 --- /dev/null +++ b/flake.nix @@ -0,0 +1,64 @@ + +{ + # XXX: Change project description + description = "Rust project"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-21.11"; + fenix = { + url = "github:nix-community/fenix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, fenix, flake-utils }: + flake-utils.lib.eachDefaultSystem + (system: + let + pkgs = nixpkgs.legacyPackages."${system}"; + + # XXX: Change target platform + rust-target = "x86_64-unknown-linux-gnu"; + rust-toolchain = with fenix.packages."${system}"; let + rust-toolchain-spec = { + # XXX: Change compiler version + # see `toolchainOf` https://github.com/nix-community/fenix + # for supported options + channel = "nightly"; + sha256 = "sha256-E4uFH1xyP6u3KhsYf3gnuqWyee61O9uoLLvBEIF1+ko="; + }; + # Toolchain for the builder + host-toolchain = toolchainOf rust-toolchain-spec; + # Toolchain for the platform where the binary will run + # target-toolchain = targets."${rust-target}".toolchainOf rust-toolchain-spec; + in + combine [ + # Build tools are taken from the host + host-toolchain.rustc + host-toolchain.cargo + host-toolchain.clippy + host-toolchain.rust-docs + # Standard library is taken from the target + host-toolchain.rust-std + host-toolchain.rust-src + ]; + in + { + devShells.default = pkgs.mkShell { + # XXX: Change to project name + name = "rust-project"; + + # Build tools + nativeBuildInputs = with pkgs; [ + rust-toolchain + rust-analyzer + rustfmt + ]; + + RUST_BACKTRACE = 1; + # CARGO_BUILD_TARGET = rust-target; + }; + devShell = self.devShells."${system}".default; + }); +} \ No newline at end of file diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..77db547 --- /dev/null +++ b/shell.nix @@ -0,0 +1,7 @@ +(import ( + fetchTarball { + url = "https://github.com/edolstra/flake-compat/archive/99f1c2157fba4bfe6211a321fd0ee43199025dbf.tar.gz"; + sha256 = "0x2jn3vrawwv9xp15674wjz9pixwjyj3j771izayl962zziivbx2"; } +) { + src = ./.; +}).shellNix diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e1f68ce --- /dev/null +++ b/src/main.rs @@ -0,0 +1,213 @@ +#![feature(int_roundings)] + +mod structs; +use crate::structs::{Superblock,BlockGroupDescriptor,Inode,DirectoryEntry}; +use zerocopy::{ByteSlice}; +use uuid::Uuid; + +#[repr(C)] +#[derive(Debug)] +pub struct Ext2 { + pub superblock: &'static Superblock, + pub block_groups: &'static [BlockGroupDescriptor], + pub blocks: Vec<&'static [u8]>, + pub block_size: usize, + pub uuid: Uuid, +} + + +const EXT2_MAGIC: u16 = 0xef53; +const EXT2_SUPERBLOCK_OFFSET: usize = 2048; +impl Ext2 { + pub fn new(bytes: B) -> Ext2 { + // https://wiki.osdev.org/Ext2#Superblock + // parse into Ext2 struct without copying much + + + let x = bytes.split_at(EXT2_SUPERBLOCK_OFFSET); + // let y = x.0.split_at(1024).1; + // let (prefix, superblock, suffix) = unsafe { bytes.align_to::() }; + // println!("{:?}", x.len()); + let superblock = unsafe { &*(x.0.split_at(1024).1.as_ptr() as *const Superblock) }; + assert_eq!(superblock.magic, EXT2_MAGIC); + // println!("{:?} {:?}", prefix, suffix); + + let block_group_count = superblock + .blocks_count + .div_ceil(superblock.blocks_per_group) as usize + - 3; + // assert!(block_igroup_count <= MAX_BLOCK_GROUPS); + // let y = x.1, + // let (prefix, block_groups, suffix) = unsafe { bytes.align_to::() }; + // println!("{:?} {:?}", prefix, suffix); + let block_size: usize = 1024 << superblock.log_block_size; + let y = x.1.split_at(block_size); + + let block_groups = unsafe { + std::slice::from_raw_parts( + y.0.as_ptr() as *const BlockGroupDescriptor, + block_group_count, + ) + }; + + // i determined the root table to be at inode 5, but i need a better way of finding that + // let root_inode_group: usize = (5 - 1) / superblock.inodes_per_group as usize; + // let root_inode_index: usize = (5 - 1) % superblock.inodes_per_group as usize; + // let root_inode_block = (root_inode_index * superblock.inode_size as usize) as usize / block_size; + // println!("{:?}", root_inode_group); + // println!("{:?}", root_inode_index); + // println!("{:?}", root_inode_block); + + let blocks = unsafe { + std::slice::from_raw_parts( + y.1.as_ptr() as *const u8, + superblock.blocks_count as usize * block_size, + ) + } + .chunks(block_size) + .collect::>(); + + // let blocks = unsafe { std::slice::from_raw_parts( + // y.1.as_ptr() as *const [u8;1024], + // superblock.blocks_count as usize) }; + + // let inodes_per_block = block_size / superblock.inode_size as usize; + // let inode_table_block_count: usize = superblock.inodes_per_group as usize / inodes_per_block; + + // let inode_table = unsafe { std::slice::from_raw_parts( + // blocks[(block_groups[root_inode_group].inode_table_block-1) as usize].as_ptr() as *const Inode, + // inode_table_block_count) }; + + // boot block + group descriptors block + inode table blocks + // let data_blocks = unsafe { std::slice::from_raw_parts( + // blocks[1 + 1 + inode_table_block_count].as_ptr() as *const Inode, + // inode_table_block_count) }; + + // let root_inode = &inode_table[root_inode_index]; + // let test_inode = &inode_table[6]; + + // println!("{:?}", unsafe { std::ffi::CStr::from_ptr(blocks[test_inode.direct_pointer[0] as usize-3].as_ptr() as *const i8) }); + + // for inode in &inode_table[0..10] { + // println!("{:?}", inode); + // } + // for data_block in root_inode.direct_pointer { + // if data_block == 0 { break } + // println!("{:?}", blocks[data_block as usize-3]); + // } + + // for ptr in root_inode.direct_pointer { + // println!("{:?}", blocks[ptr as usize - 1]); + // } + + // let block_goups = <[BlockGroupDescriptor]>::read_from_prefix(x.1).unwrap(); + // println!("{:?}", y.1.split_at(block_size * (block_groups[root_inode_group].inode_table_block-1) as usize).1.split_at(block_size).0); + + let uuid = Uuid::from_bytes(superblock.fs_id); + Ext2 { + superblock, + block_groups, + blocks, + block_size, + uuid, + } + } + + // pub fn read_dir_inode(&self, inode: usize) -> std::io::Result> { + // for ptr in root.direct_pointer { + // println!("{:?}",ext2.blocks[ptr as usize - 4] ); + // } + // let directory = ext2.blocks[root.direct_pointer[0] as usize - 3].as_ptr() as *const DirectoryEntry; + // Ok(vec!["/"]) + // } + + // pub fn read_dir_inode(&self, inode: usize) { + // let dir = self.get_inode(inode); + + // } + + pub fn get_inode(&self, inode: usize) -> &Inode { + let group: usize = (inode - 1) / self.superblock.inodes_per_group as usize; + let index: usize = (inode - 1) % self.superblock.inodes_per_group as usize; + + let inode_table = unsafe { + std::slice::from_raw_parts( + self.blocks[(self.block_groups[group].inode_table_block - 1) as usize].as_ptr() + as *const Inode, + index + 1, + ) + }; + // dbg!(inode_table); + &inode_table[index] + } + + pub fn read_dir<'a>(&'a self, inode: &'a Inode) -> Directory<'_> { + Directory { inode, disk: self, offset: 0 } + } +} + +#[derive(Debug)] +pub struct Directory<'a> { + inode: &'a Inode, + disk: &'a Ext2, + offset: usize, +} + +impl<'a> Iterator for Directory<'a> { + type Item = &'a DirectoryEntry; + fn next(&mut self) -> Option { + if self.offset > 0 && self.offset % self.disk.block_size == 0 { + return None; + } + if self.inode.direct_pointer[self.offset / self.disk.block_size] == 0 { + return None; + } + let dir = unsafe { + &*(self.disk.blocks + [self.inode.direct_pointer[self.offset / self.disk.block_size] as usize - 4] + .as_ptr() + .add(self.offset % self.disk.block_size) as *const DirectoryEntry) + }; + self.offset += dir.entry_size as usize; + + Some(dir) + } +} + +fn main() { + let disk = include_bytes!("../ext2.img"); + let ext2 = Ext2::new(&disk[..]); + println!("{:?}", ext2.uuid); + + // root inode always starts at 2 + let root = ext2.get_inode(5); + dbg!(root); + + let dir: Vec<_> = ext2.read_dir(root).collect(); + // dbg!(dir); + + dbg!(dir.clone()); + let file = ext2.get_inode(7); + + dbg!(file); + println!("{:?}", unsafe { + std::ffi::CStr::from_ptr( + ext2.blocks[file.direct_pointer[0] as usize - 3].as_ptr() as *const i8 + ) + }); + // dbg!(dir.next()); + // dbg!(dir.next()); + + // let file = ext2.get_inode(dir.next().unwrap().inode as usize - 8); + // dbg!(file); + + // for ptr in root.direct_pointer { + // println!("{:?}",ext2.blocks[ptr as usize - 4] ); + + // } + // std::vec::Vec::from_raw_parts(ext2.blocks[root.direct_pointer[0] as usize - 4].as_ptr() as *const DirectoryEntry, 5, 5) + // let directory = ext2.blocks[root.direct_pointer[0] as usize - 4].as_ptr() as *const DirectoryEntry; + // dbg!( unsafe { &*directory } ); + // println!("{:?}",ext2.blocks[root.direct_pointer[0] as usize - 3] ); + // println!("{:?}", unsafe { std::ffi::CStr::from_ptr(ext2.blocks[root.direct_pointer[0] as usize - 3].as_ptr()+ as *const i8) }); +} diff --git a/src/structs.rs b/src/structs.rs new file mode 100644 index 0000000..55f6623 --- /dev/null +++ b/src/structs.rs @@ -0,0 +1,253 @@ +use bitflags::bitflags; +use null_terminated::NulStr; +use zerocopy::FromBytes; + +#[repr(C)] +#[derive(Debug)] +// https://wiki.osdev.org/Ext2 +pub struct Superblock { + // taken from https://wiki.osdev.org/Ext2 + /// Total number of inodes in file system + pub inodes_count: u32, + /// Total number of blocks in file system + pub blocks_count: u32, + /// Number of blocks reserved for superuser (see offset 80) + pub r_blocks_count: u32, + /// Total number of unallocated blocks + pub free_blocks_count: u32, + /// Total number of unallocated inodes + pub free_inodes_count: u32, + /// Block number of the block containing the superblock + pub first_data_block: u32, + /// log2 (block size) - 10. (In other words, the number to shift 1,024 + /// to the left by to obtain the block size) + pub log_block_size: u32, + /// log2 (fragment size) - 10. (In other words, the number to shift + /// 1,024 to the left by to obtain the fragment size) + pub log_frag_size: i32, + /// Number of blocks in each block group + pub blocks_per_group: u32, + /// Number of fragments in each block group + pub frags_per_group: u32, + /// Number of inodes in each block group + pub inodes_per_group: u32, + /// Last mount time (in POSIX time) + pub mtime: u32, + /// Last written time (in POSIX time) + pub wtime: u32, + /// Number of times the volume has been mounted since its last + /// consistency check (fsck) + pub mnt_count: u16, + /// Number of mounts allowed before a consistency check (fsck) must be + /// done + pub max_mnt_count: i16, + /// Ext2 signature (0xef53), used to help confirm the presence of Ext2 + /// on a volume + pub magic: u16, + /// File system state (see `FS_CLEAN` and `FS_ERR`) + pub state: u16, + /// What to do when an error is detected (see `ERR_IGNORE`, `ERR_RONLY` and + /// `ERR_PANIC`) + pub errors: u16, + /// Minor portion of version (combine with Major portion below to + /// construct full version field) + pub rev_minor: u16, + /// POSIX time of last consistency check (fsck) + pub lastcheck: u32, + /// Interval (in POSIX time) between forced consistency checks (fsck) + pub checkinterval: u32, + /// Operating system ID from which the filesystem on this volume was + /// created + pub creator_os: u32, + /// Major portion of version (combine with Minor portion above to + /// construct full version field) + pub rev_major: u32, + /// User ID that can use reserved blocks + pub block_uid: u16, + /// Group ID that can use reserved blocks + pub block_gid: u16, + + /// First non-reserved inode in file system. + pub first_inode: u32, + /// SectorSize of each inode structure in bytes. + pub inode_size: u16, + /// Block group that this superblock is part of (if backup copy) + pub block_group: u16, + /// Optional features present (features that are not required to read + /// or write, but usually result in a performance increase) + pub features_opt: u32, + /// Required features present (features that are required to be + /// supported to read or write) + pub features_req: u32, + /// Features that if not supported, the volume must be mounted + /// read-only) + pub features_ronly: u32, + /// File system ID (what is output by blkid) + pub fs_id: [u8; 16], + /// Volume name (C-style string: characters terminated by a 0 byte) + pub volume_name: [u8; 16], + /// Path volume was last mounted to (C-style string: characters + /// terminated by a 0 byte) + pub last_mnt_path: [u8; 64], + /// Compression algorithms used (see Required features above) + pub compression: u32, + /// Number of blocks to preallocate for files + pub prealloc_blocks_files: u8, + /// Number of blocks to preallocate for directories + pub prealloc_blocks_dirs: u8, + #[doc(hidden)] + _unused: [u8; 2], + /// Journal ID (same style as the File system ID above) + pub journal_id: [u8; 16], + /// Journal inode + pub journal_inode: u32, + /// Journal device + pub journal_dev: u32, + /// Head of orphan inode list + pub journal_orphan_head: u32, +} + +#[repr(C)] +#[derive(Debug)] +pub struct BlockGroupDescriptor { + /// Block address of block usage bitmap + pub block_usage_addr: u32, + /// Block address of inode usage bitmap + pub inode_usage_addr: u32, + /// Starting block address of inode table + pub inode_table_block: u32, + /// Number of unallocated blocks in group + pub free_blocks_count: u16, + /// Number of unallocated inodes in group + pub free_inodes_count: u16, + /// Number of directories in group + pub dirs_count: u16, + + _reserved: [u8; 14], +} + +#[repr(C)] +#[derive(Debug)] +pub struct Inode { + /// Type and Permissions (see below) + pub type_perm: TypePerm, + /// User ID + pub uid: u16, + /// Lower 32 bits of size in bytes + pub size_low: u32, + /// Last Access Time (in POSIX time) + pub atime: u32, + /// Creation Time (in POSIX time) + pub ctime: u32, + /// Last Modification time (in POSIX time) + pub mtime: u32, + /// Deletion time (in POSIX time) + pub dtime: u32, + /// Group ID + pub gid: u16, + /// Count of hard links (directory entries) to this inode. When this + /// reaches 0, the data blocks are marked as unallocated. + pub hard_links: u16, + /// Count of disk sectors (not Ext2 blocks) in use by this inode, not + /// counting the actual inode structure nor directory entries linking + /// to the inode. + pub sectors_count: u32, + /// Flags + pub flags: u32, + /// Operating System Specific value #1 + pub _os_specific_1: [u8; 4], + /// Direct block pointers + pub direct_pointer: [u32; 12], + /// Singly Indirect Block Pointer (Points to a block that is a list of + /// block pointers to data) + pub indirect_pointer: u32, + /// Doubly Indirect Block Pointer (Points to a block that is a list of + /// block pointers to Singly Indirect Blocks) + pub doubly_indirect: u32, + /// Triply Indirect Block Pointer (Points to a block that is a list of + /// block pointers to Doubly Indirect Blocks) + pub triply_indirect: u32, + /// Generation number (Primarily used for NFS) + pub gen_number: u32, + /// In Ext2 version 0, this field is reserved. In version >= 1, + /// Extended attribute block (File ACL). + pub ext_attribute_block: u32, + /// In Ext2 version 0, this field is reserved. In version >= 1, Upper + /// 32 bits of file size (if feature bit set) if it's a file, + /// Directory ACL if it's a directory + pub size_high: u32, + /// Block address of fragment + pub frag_block_addr: u32, + /// Operating System Specific Value #2 + pub _os_specific_2: [u8; 12], +} + +#[repr(C)] +#[derive(Debug)] +pub struct DirectoryEntry { + /// Inode + pub inode: u32, + /// Total size of this entry (Including all subfields) + pub entry_size: u16, + /// Name Length least-significant 8 bits + pub name_length: u8, + /// Type indicator (only if the feature bit for "directory entries have file type byte" is set, else this is the most-significant 8 bits of the Name Length) + pub type_indicator: TypeIndicator, + + pub name: NulStr, +} + +#[derive(Debug)] +pub enum TypeIndicator { + Unknown, + Regular, + Directory, + Character, + Block, + Fifo, + Socket, + Symlink, +} + +bitflags! { + pub struct TypePerm: u16 { + /// FIFO + const FIFO = 0x1000; + /// Character device + const CHAR_DEVICE = 0x2000; + /// Directory + const DIRECTORY = 0x4000; + /// Block device + const BLOCK_DEVICE = 0x6000; + /// Regular file + const FILE = 0x8000; + /// Symbolic link + const SYMLINK = 0xA000; + /// Unix socket + const SOCKET = 0xC000; + /// Other—execute permission + const O_EXEC = 0x001; + /// Other—write permission + const O_WRITE = 0x002; + /// Other—read permission + const O_READ = 0x004; + /// Group—execute permission + const G_EXEC = 0x008; + /// Group—write permission + const G_WRITE = 0x010; + /// Group—read permission + const G_READ = 0x020; + /// User—execute permission + const U_EXEC = 0x040; + /// User—write permission + const U_WRITE = 0x080; + /// User—read permission + const U_READ = 0x100; + /// Sticky Bit + const STICKY = 0x200; + /// Set group ID + const SET_GID = 0x400; + /// Set user ID + const SET_UID = 0x800; + } +} -- cgit 1.4.1