summary refs log tree commit diff
diff options
context:
space:
mode:
authortzlil <tzlils@protonmail.com>2023-02-17 13:25:23 +0200
committertzlil <tzlils@protonmail.com>2023-02-17 13:25:23 +0200
commitdd95456ec490d9119274f31e2f4fc4d1679477fc (patch)
tree77c85ed94970517f4ba0bf33bbed0c9e4a7785af
initial commit
-rw-r--r--.envrc1
-rw-r--r--.github/workflows/build_nix.yml13
-rw-r--r--.gitignore9
-rw-r--r--Cargo.lock130
-rw-r--r--Cargo.toml12
-rw-r--r--README1
-rw-r--r--default.nix7
-rw-r--r--ext2.imgbin0 -> 33554432 bytes
-rw-r--r--flake.lock82
-rw-r--r--flake.nix64
-rw-r--r--shell.nix7
-rw-r--r--src/main.rs213
-rw-r--r--src/structs.rs253
13 files changed, 792 insertions, 0 deletions
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
--- /dev/null
+++ b/ext2.img
Binary files differdiff --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<B: ByteSlice + std::fmt::Debug>(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::<Superblock>() };
+        // 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::<BlockGroupDescriptor>() };
+        // 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::<Vec<_>>();
+
+        // 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<Vec<(usize, &str)>> {
+    //     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<Self::Item> {
+        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;
+    }
+}