added superblock, group descriptors, and mmp structured
This commit is contained in:
parent
9de87bd6b2
commit
75d23682c0
10 changed files with 824 additions and 2 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1 +1,2 @@
|
||||||
/target
|
/target
|
||||||
|
/tests
|
||||||
|
|
|
||||||
81
Cargo.lock
generated
81
Cargo.lock
generated
|
|
@ -3,5 +3,84 @@
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ext4-rs"
|
name = "anyhow"
|
||||||
|
version = "1.0.98"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ext4"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"bitflags",
|
||||||
|
"static_assertions",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.95"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.104"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "2.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||||
|
|
|
||||||
10
Cargo.toml
10
Cargo.toml
|
|
@ -1,6 +1,14 @@
|
||||||
[package]
|
[package]
|
||||||
name = "ext4-rs"
|
name = "ext4"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "dump"
|
||||||
|
path = "src/dump.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = "1.0.98"
|
||||||
|
bitflags = "2.9.1"
|
||||||
|
static_assertions = "1.1.0"
|
||||||
|
thiserror = "2.0.12"
|
||||||
|
|
|
||||||
3
README.md
Normal file
3
README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
for specification, see: [https://docs.kernel.org/filesystems/ext4/](https://docs.kernel.org/filesystems/ext4/)
|
||||||
|
|
||||||
|
[https://blogs.oracle.com/linux/post/understanding-ext4-disk-layout-part-1](https://blogs.oracle.com/linux/post/understanding-ext4-disk-layout-part-1)
|
||||||
48
src/dump.rs
Normal file
48
src/dump.rs
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
use std::io::{Read, Seek};
|
||||||
|
|
||||||
|
use ext4::{Superblock, group_has_superblock};
|
||||||
|
|
||||||
|
pub fn main() -> anyhow::Result<()> {
|
||||||
|
let args = std::env::args();
|
||||||
|
|
||||||
|
if args.len() != 2 {
|
||||||
|
anyhow::bail!("usage: dump <path>");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file = std::fs::File::open(args.skip(1).next().unwrap())?;
|
||||||
|
|
||||||
|
// skip first 1024 bytes
|
||||||
|
file.seek(std::io::SeekFrom::Start(1024))?;
|
||||||
|
|
||||||
|
let mut buf = [0; 1024];
|
||||||
|
|
||||||
|
file.read_exact(&mut buf)?;
|
||||||
|
|
||||||
|
let superblock = Superblock::from_bytes(&buf)?;
|
||||||
|
|
||||||
|
println!("Superblock 0 (offset: 1024): {:?}", superblock);
|
||||||
|
|
||||||
|
let num_groups = superblock.blocks_count() / superblock.blocks_per_group() as u64;
|
||||||
|
|
||||||
|
let group_size = (superblock.blocks_per_group() as u64) * superblock.blocksize();
|
||||||
|
|
||||||
|
println!("group_size: 0x{group_size:08X}");
|
||||||
|
|
||||||
|
file.seek(std::io::SeekFrom::Start(0))?;
|
||||||
|
|
||||||
|
for group in 1..num_groups {
|
||||||
|
if group_has_superblock(group) {
|
||||||
|
let offset = group * group_size;
|
||||||
|
|
||||||
|
file.seek(std::io::SeekFrom::Start(offset))?;
|
||||||
|
|
||||||
|
file.read_exact(&mut buf)?;
|
||||||
|
|
||||||
|
let superblock2 = Superblock::from_bytes(&buf)?;
|
||||||
|
|
||||||
|
println!("Superblock {group: >2} (offset: 0x{:08X})", offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
55
src/group_descriptor.rs
Normal file
55
src/group_descriptor.rs
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
use bitflags::bitflags;
|
||||||
|
use static_assertions::const_assert_eq;
|
||||||
|
|
||||||
|
// #[derive(Debug, thiserror::Error)]
|
||||||
|
// pub struct GroupDescriptorError {}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct Flags: u16 {
|
||||||
|
const INODE_UNINIT = 0x1; // inode table and bitmap are not initialized
|
||||||
|
const BLOCK_UNINIT = 0x2; // block bitmap is not initialized
|
||||||
|
const INODE_ZEROED = 0x4; // inode table is zeroed
|
||||||
|
|
||||||
|
const _ = !0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct GroupDescriptor {
|
||||||
|
block_bitmap_lo: u32,
|
||||||
|
inode_bitmap_lo: u32,
|
||||||
|
inode_table_lo: u32,
|
||||||
|
free_blocks_count_lo: u16,
|
||||||
|
free_inodes_count_lo: u16,
|
||||||
|
used_dirs_count_lo: u16,
|
||||||
|
flags: u16,
|
||||||
|
exclude_bitmap_lo: u32,
|
||||||
|
block_bitmap_csum_lo: u16,
|
||||||
|
inode_bitmap_csum_lo: u16,
|
||||||
|
itable_unused_lo: u16,
|
||||||
|
checksum: u16,
|
||||||
|
block_bitmap_hi: u32,
|
||||||
|
inode_bitmap_hblock_bitmap_csum_hii: u32,
|
||||||
|
inode_table_hi: u32,
|
||||||
|
free_blocks_count_hi: u16,
|
||||||
|
free_inodes_count_hi: u16,
|
||||||
|
used_dirs_count_hi: u16,
|
||||||
|
itable_unused_hi: u16,
|
||||||
|
exclude_bitmap_hi: u32,
|
||||||
|
block_bitmap_csum_hi: u16,
|
||||||
|
inode_bitmap_csum_hi: u16,
|
||||||
|
reserved: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
const_assert_eq!(std::mem::size_of::<GroupDescriptor>(), 64);
|
||||||
|
|
||||||
|
impl GroupDescriptor {
|
||||||
|
pub fn from_bytes(bytes: &[u8]) -> Result<GroupDescriptor, ()> {
|
||||||
|
assert_eq!(bytes.len(), std::mem::size_of::<GroupDescriptor>());
|
||||||
|
|
||||||
|
let group_desc = unsafe { std::ptr::read(bytes.as_ptr() as *const GroupDescriptor) };
|
||||||
|
|
||||||
|
Ok(group_desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
93
src/lib.rs
93
src/lib.rs
|
|
@ -1 +1,94 @@
|
||||||
|
use std::io::SeekFrom;
|
||||||
|
|
||||||
|
pub use crate::superblock::{SUPERBLOCK_SIZE, Superblock};
|
||||||
|
pub use crate::utils::group_has_superblock;
|
||||||
|
|
||||||
|
mod group_descriptor;
|
||||||
|
mod mmp;
|
||||||
|
mod superblock;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
|
pub trait FileLike {
|
||||||
|
fn read_at_offset(&mut self, offset: u64, buf: &mut [u8]) -> anyhow::Result<()>;
|
||||||
|
|
||||||
|
fn write_at_offset(&mut self, offset: u64, bytes: &[u8]) -> anyhow::Result<()>;
|
||||||
|
|
||||||
|
fn read_at_offset_const<const N: usize>(&mut self, offset: u64) -> anyhow::Result<[u8; N]> {
|
||||||
|
let mut buf = [0; N];
|
||||||
|
|
||||||
|
self.read_at_offset(offset, &mut buf)?;
|
||||||
|
|
||||||
|
Ok(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: std::io::Read + std::io::Write + std::io::Seek> FileLike for F {
|
||||||
|
fn read_at_offset(&mut self, offset: u64, buf: &mut [u8]) -> anyhow::Result<()> {
|
||||||
|
self.seek(SeekFrom::Start(offset))?;
|
||||||
|
|
||||||
|
self.read_exact(buf)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_at_offset(&mut self, offset: u64, bytes: &[u8]) -> anyhow::Result<()> {
|
||||||
|
self.seek(SeekFrom::Start(offset))?;
|
||||||
|
|
||||||
|
self.write_all(bytes)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl FileLike for &mut [u8] {
|
||||||
|
// fn read_at_offset(&mut self, offset: u64, buf: &mut [u8]) -> anyhow::Result<()> {
|
||||||
|
// anyhow::ensure!(
|
||||||
|
// offset as usize + buf.len() <= self.len(),
|
||||||
|
// "reading {} bytes at offset {} is out of bounds for slice of len {}",
|
||||||
|
// buf.len(),
|
||||||
|
// offset,
|
||||||
|
// self.len()
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// buf.copy_from_slice(&self[offset as usize..][..buf.len()]);
|
||||||
|
//
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fn write_at_offset(&mut self, offset: u64, bytes: &[u8]) -> anyhow::Result<()> {
|
||||||
|
// anyhow::ensure!(
|
||||||
|
// offset as usize + bytes.len() <= self.len(),
|
||||||
|
// "writing {} bytes at offset {} is out of bounds for slice of len {}",
|
||||||
|
// bytes.len(),
|
||||||
|
// offset,
|
||||||
|
// self.len()
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// self[offset as usize..][..bytes.len()].copy_from_slice(bytes);
|
||||||
|
//
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub struct Ext4Fs<F: FileLike> {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
file: F,
|
||||||
|
|
||||||
|
superblock: superblock::Superblock,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FileLike> Ext4Fs<F> {
|
||||||
|
pub fn load(file: F) -> Result<Ext4Fs<F>, anyhow::Error> {
|
||||||
|
let mut file = file;
|
||||||
|
|
||||||
|
let superblock_bytes = file.read_at_offset_const::<SUPERBLOCK_SIZE>(1024)?;
|
||||||
|
|
||||||
|
let superblock = Superblock::from_bytes(&superblock_bytes)?;
|
||||||
|
|
||||||
|
Ok(Ext4Fs { file, superblock })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn super_block(&self) -> &Superblock {
|
||||||
|
&self.superblock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
23
src/mmp.rs
Normal file
23
src/mmp.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Mmp {
|
||||||
|
magic: u32, // Magic number for MMP (0x004D4D50 (“MMP”))
|
||||||
|
seq: u32, // Sequence number, updated periodically
|
||||||
|
time: u64, // Time that the MMP block was last updated
|
||||||
|
node_name: [u8; 64], // Hostname of the node that opened the filesystem
|
||||||
|
bdev_name: [u8; 32], // Block device name of the filesystem
|
||||||
|
check_interval: u16, // The MMP re-check interval, in seconds
|
||||||
|
pad1: u16, // zero
|
||||||
|
pad2: [u32; 226], // zero
|
||||||
|
checksum: u32, // Checksum of the MMP block
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mmp {
|
||||||
|
pub fn from_bytes(bytes: &[u8]) -> Result<Mmp, ()> {
|
||||||
|
assert_eq!(bytes.len(), std::mem::size_of::<Mmp>());
|
||||||
|
|
||||||
|
let group_desc = unsafe { std::ptr::read(bytes.as_ptr() as *const Mmp) };
|
||||||
|
|
||||||
|
Ok(group_desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
429
src/superblock.rs
Normal file
429
src/superblock.rs
Normal file
|
|
@ -0,0 +1,429 @@
|
||||||
|
use bitflags::bitflags;
|
||||||
|
use static_assertions::const_assert_eq;
|
||||||
|
|
||||||
|
use crate::utils::{combine_lo_hi, crc32};
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct State: u16 {
|
||||||
|
const VALID_FS = 0x0001; // cleanly unmounted
|
||||||
|
const ERROR_FS = 0x0002; // errors detected
|
||||||
|
const ORPHAN_FS = 0x0004; // orphans being recovered
|
||||||
|
|
||||||
|
const _ = !0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum CreatorOs {
|
||||||
|
Linux = 0,
|
||||||
|
Hurd = 1,
|
||||||
|
Masix = 2,
|
||||||
|
FreeBSD = 3,
|
||||||
|
Lites = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum Revision {
|
||||||
|
GoodOldExt4 = 0,
|
||||||
|
DynamicInodes = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct CompatibleFlags: u32 {
|
||||||
|
const DIR_PREALLOC = 0x1; // Directory preallocation
|
||||||
|
const IMAGIC_INODES = 0x2; // “imagic inodes”. Not clear from the code what this does
|
||||||
|
const HAS_JOURNAL = 0x4; // Has a journal
|
||||||
|
const EXT_ATTR = 0x8; // Supports extended attributes
|
||||||
|
const RESIZE_INODE = 0x10; // Has reserved GDT blocks for filesystem expansion
|
||||||
|
const DIR_INDEX = 0x20; // Has directory indices
|
||||||
|
const LAZY_BG = 0x40; /* “Lazy BG”. Not in Linux kernel, seems to have been for
|
||||||
|
uninitialized block groups? */
|
||||||
|
const EXCLUDE_INODE = 0x80; // “Exclude inode”. Not used.
|
||||||
|
const EXCLUDE_BITMAP = 0x100; /* “Exclude bitmap”. Seems to be used to indicate the
|
||||||
|
presence of snapshot-related exclude bitmaps? Not in
|
||||||
|
kernel or used in e2fsprogs */
|
||||||
|
const SPARSE_SUPER2 = 0x200; /* Sparse Super Block, v2. If this flag is set, the SB
|
||||||
|
field s_backup_bgs points to the two block that
|
||||||
|
contain backup superblocks */
|
||||||
|
const FAST_COMMIT = 0x400; /* Fast commits supported. Although fast commits blocks
|
||||||
|
are backward incompatible, fast commit blocks are not
|
||||||
|
always present in the journal. If fast commit blocks
|
||||||
|
are present in the journal, JBD2 incompat feature
|
||||||
|
(JBD2_FEATURE_INFAST_COMMIT) gets set */
|
||||||
|
const STABLE_INODES = 0x800;
|
||||||
|
const ORPHAN_FILE = 0x1000; /* Orphan file allocated. This is the special file for
|
||||||
|
more efficient tracking of unlinked but still open
|
||||||
|
inodes. When there may be any entries the file, we
|
||||||
|
additionally set proper rocompat feature
|
||||||
|
ORPHAN_PRESENT */
|
||||||
|
|
||||||
|
const _ = !0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct IncompatibleFeatures: u32 {
|
||||||
|
const COMPRESSION = 0x1; // Compression
|
||||||
|
const FILETYPE = 0x2; /* Directory entries record the file type. See
|
||||||
|
ext4_dir_entry_2 below */
|
||||||
|
const RECOVER = 0x4; // Filesystem needs recovery
|
||||||
|
const JOURNAL_DEV = 0x8; // Filesystem has a separate journal device
|
||||||
|
const META_BG = 0x10; /* Meta block groups. See the earlier discussion of this
|
||||||
|
feature */
|
||||||
|
const EXTENTS = 0x40; // Files in this filesystem use extents
|
||||||
|
const _64BIT = 0x80; // Enable a filesystem size of 2^64 blocks
|
||||||
|
const MMP = 0x100; // Multiple mount protection
|
||||||
|
const FLEX_BG = 0x200; /* Flexible block groups. See the earlier discussion of this
|
||||||
|
feature */
|
||||||
|
const EA_INODE = 0x400; // Inodes can be used to store large extended attribute values
|
||||||
|
const DIRDATA = 0x1_000; // Data in directory entry
|
||||||
|
const CSUM_SEED = 0x2_000; /* Metadata checksum seed is stored in the superblock. This
|
||||||
|
feature enables the administrator to change the UUID of a
|
||||||
|
metadata_csum filesystem while the filesystem is mounted;
|
||||||
|
without it, the checksum definition requires all metadata
|
||||||
|
blocks to be rewritten */
|
||||||
|
const LARGEDIR = 0x4_000; /* Large directory >2GB or 3-level htree (LARGEDIR). Prior to
|
||||||
|
this feature, directories could not be larger than 4GiB and
|
||||||
|
could not have an htree more than 2 levels deep.
|
||||||
|
If this feature is enabled, directories can be larger than
|
||||||
|
4GiB and have a maximum htree depth of 3. */
|
||||||
|
const INLINE_DATA = 0x8_000; // Data in inode
|
||||||
|
const ENCRYPT = 0x10_000; // Encrypted inodes are present on the filesystem
|
||||||
|
|
||||||
|
const _ = !0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct RoCompatibleFeatures: u32 {
|
||||||
|
const SPARSE_SUPER = 0x1; /* Sparse superblocks. See the earlier discussion of this
|
||||||
|
feature */
|
||||||
|
const LARGE_FILE = 0x2; /* This filesystem has been used to store a file greater
|
||||||
|
than 2GiB */
|
||||||
|
const BTREE_DIR = 0x4; // Not used in kernel or e2fsprogs
|
||||||
|
const HUGE_FILE = 0x8; /* This filesystem has files whose sizes are represented
|
||||||
|
in units of logical blocks, not 512-byte sectors. This
|
||||||
|
implies a very large file indeed! */
|
||||||
|
const GDT_CSUM = 0x10; /* Group descriptors have checksums. In addition to
|
||||||
|
detecting corruption, this is useful for lazy
|
||||||
|
formatting with uninitialized groups */
|
||||||
|
const DIR_NLINK = 0x20; /* Indicates that the old ext3 32,000 subdirectory limit
|
||||||
|
no longer applies.
|
||||||
|
A directory’s i_links_count will be set to 1 if it is
|
||||||
|
incremented past 64,999*/
|
||||||
|
const EXTRA_ISIZE = 0x40; // Indicates that large inodes exist on this filesystem
|
||||||
|
const HAS_SNAPSHOT = 0x80; // This filesystem has a snapshot
|
||||||
|
const QUOTA = 0x100; // Quota
|
||||||
|
const BIGALLOC = 0x200; /* This filesystem supports “bigalloc”, which means that
|
||||||
|
file extents are tracked in units of clusters
|
||||||
|
(of blocks) instead of blocks */
|
||||||
|
const METADATA_CSUM = 0x400; /* This filesystem supports metadata checksumming.
|
||||||
|
(implies RO_COMPAT_GDT_CSUM, though GDT_CSUM must not
|
||||||
|
be set) */
|
||||||
|
const REPLICA = 0x800; /* Filesystem supports replicas. This feature is neither
|
||||||
|
in the kernel nor e2fsprogs. */
|
||||||
|
const READONLY = 0x1_000; /* Read-only filesystem image; the kernel will not mount
|
||||||
|
this image read-write and most tools will refuse to
|
||||||
|
write to the image */
|
||||||
|
const PROJECT = 0x2_000; // Filesystem tracks project quotas
|
||||||
|
const VERITY = 0x8_000; // Verity inodes may be present on the filesystem
|
||||||
|
const ORPHAN_PRESENT = 0x10_000; /* Indicates orphan file may have valid orphan entries and
|
||||||
|
thus we need to clean them up when mounting the
|
||||||
|
filesystem */
|
||||||
|
|
||||||
|
const _ = !0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum DefHashVersion {
|
||||||
|
Legacy = 0x0,
|
||||||
|
HalfMd4 = 0x1,
|
||||||
|
Tea = 0x2,
|
||||||
|
LegacyUnsigned = 0x3,
|
||||||
|
HalfMd4Unsigned = 0x4,
|
||||||
|
TeaUnsigned = 0x5,
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct DefaultMountOpts: u32 {
|
||||||
|
const EXT4_DEFM_DEBUG = 0x1;
|
||||||
|
const EXT4_DEFM_BSDGROUPS = 0x2;
|
||||||
|
const EXT4_DEFM_XATTR_USER = 0x4;
|
||||||
|
const EXT4_DEFM_ACL = 0x8;
|
||||||
|
const EXT4_DEFM_UID16 = 0x10;
|
||||||
|
const EXT4_DEFM_JMODE_DATA = 0x20;
|
||||||
|
const EXT4_DEFM_JMODE_ORDERED = 0x40;
|
||||||
|
const EXT4_DEFM_JMODE_WBACK = 0x60;
|
||||||
|
const EXT4_DEFM_NOBARRIER = 0x100;
|
||||||
|
const EXT4_DEFM_BLOCK_VALIDITY = 0x200;
|
||||||
|
const EXT4_DEFM_DISCARD = 0x400;
|
||||||
|
const EXT4_DEFM_NODELALLOC = 0x800;
|
||||||
|
|
||||||
|
const _ = !0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct Flags: u32 {
|
||||||
|
const SignedHash = 0x1; // Signed dirhash in use
|
||||||
|
const UnsignedHash = 0x2; // Unsigned dirhash in use
|
||||||
|
const TestFilesys = 0x4; // to test development code
|
||||||
|
|
||||||
|
const _ = !0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum EncryptAlgos {
|
||||||
|
Invalid = 0x0,
|
||||||
|
Aes256Xts = 0x1,
|
||||||
|
Aes256Gcm = 0x2,
|
||||||
|
Aes256Cbc = 0x3,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum SuperblockError {
|
||||||
|
#[error("invalid value {value} for field {field}")]
|
||||||
|
InvalidFieldValue { field: String, value: String },
|
||||||
|
#[error("invalid checksum: expected {expected:X}, but found {actual:X}")]
|
||||||
|
InvalidChecksum { expected: u32, actual: u32 },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Superblock {
|
||||||
|
inodes_count: u32, // Total inode count
|
||||||
|
blocks_count_lo: u32, // Total block count
|
||||||
|
r_blocks_count_lo: u32, // This number of blocks can only be allocated by the super-user
|
||||||
|
free_blocks_count_lo: u32, // Free block count
|
||||||
|
free_inodes_count: u32, // Free inode count
|
||||||
|
first_data_block: u32, /* First data block. This must be at least 1 for 1k-block
|
||||||
|
* filesystems and is typically 0 for all other block sizes */
|
||||||
|
log_block_size: u32, // Block size is 2 ^ (10 + s_log_block_size)
|
||||||
|
log_cluster_size: u32, /* Cluster size is 2 ^ (10 + s_log_cluster_size) blocks if bigalloc
|
||||||
|
* is enabled. Otherwise s_log_cluster_size must equal
|
||||||
|
* s_log_block_size */
|
||||||
|
blocks_per_groups: u32, // Blocks per group
|
||||||
|
clusters_per_group: u32, /* Clusters per group, if bigalloc is enabled. Otherwise
|
||||||
|
* s_clusters_per_group must equal s_blocks_per_group */
|
||||||
|
inodes_per_group: u32, // Inodes per group
|
||||||
|
mtime: u32, // Mount time, in seconds since the epoch
|
||||||
|
wtime: u32, // Write time, in seconds since the epoch
|
||||||
|
mnt_count: u16, // Number of mounts since the last fsck
|
||||||
|
max_mnt_count: u16, // Number of mounts beyond which a fsck is needed
|
||||||
|
magic: u16, // Magic signature, 0xEF53
|
||||||
|
state: u16, // File system state. See super_state for more info
|
||||||
|
errors: u16, // Behaviour when detecting errors. See super_errors for more info
|
||||||
|
minor_rev_level: u16, // Minor revision level
|
||||||
|
lastcheck: u32, // Time of last check, in seconds since the epoch
|
||||||
|
checkinterval: u32, // Maximum time between checks, in seconds
|
||||||
|
creator_os: u32, // Creator OS. See the table super_creator for more info
|
||||||
|
rev_level: u32, // Revision level. See the table super_revision for more info
|
||||||
|
def_resuid: u16, // Default uid for reserved blocks
|
||||||
|
def_resgid: u16, // Default gid for reserved blocks
|
||||||
|
|
||||||
|
// These fields are for EXT4_DYNAMIC_REV superblocks only.
|
||||||
|
//
|
||||||
|
// Note: the difference between the compatible feature set and the
|
||||||
|
// incompatible feature set is that if there is a bit set in the
|
||||||
|
// incompatible feature set that the kernel doesn’t know about, it
|
||||||
|
// should refuse to mount the filesystem.
|
||||||
|
//
|
||||||
|
// e2fsck’s requirements are more strict; if it doesn’t know about a
|
||||||
|
// feature in either the compatible or incompatible feature set, it
|
||||||
|
// must abort and not try to meddle with things it doesn’t
|
||||||
|
// understand...
|
||||||
|
first_ino: u32, // First non-reserved inode
|
||||||
|
inode_size: u16, // Size of inode structure, in bytes
|
||||||
|
block_group_nr: u16, // Block group # of this superblock
|
||||||
|
feature_compat: u32, /* Compatible feature set flags. Kernel can still read/write this fs
|
||||||
|
* even if it doesn’t understand a flag; fsck should not do that. See
|
||||||
|
* the super_compat table for more info */
|
||||||
|
feature_incompat: u32, /* Incompatible feature set. If the kernel or fsck doesn’t understand
|
||||||
|
* one of these bits, it should stop. See the super_incompat table
|
||||||
|
* for more info. */
|
||||||
|
feature_ro_compat: u32, /* Readonly-compatible feature set. If the kernel doesn’t understand
|
||||||
|
* one of these bits, it can still mount read-only. See the
|
||||||
|
* super_rocompat table for more info */
|
||||||
|
uuid: [u8; 16], // 128-bit UUID for volume
|
||||||
|
volume_name: [u8; 16], // Volume label
|
||||||
|
last_mounted: [u8; 64], // Directory where filesystem was last mounted
|
||||||
|
algorithm_usage_bitmap: u32, // For compression (Not used in e2fsprogs/Linux)
|
||||||
|
|
||||||
|
// Performance hints. Directory preallocation should only happen if
|
||||||
|
// the EXT4_FEATURE_INCOMPAT_DIR_PREALLOC flag is on.
|
||||||
|
prealloc_blocks: u8, /* #. of blocks to try to preallocate for ... files? (Not used in
|
||||||
|
* e2fsprogs/Linux) */
|
||||||
|
prealloc_dir_blocks: u8, /* #. of blocks to preallocate for directories. (Not used in
|
||||||
|
* e2fsprogs/Linux) */
|
||||||
|
reserved_gdt_blocks: u16, // Number of reserved GDT entries for future filesystem expansion
|
||||||
|
|
||||||
|
// Journalling support is valid only if
|
||||||
|
// EXT4_FEATURE_COMPAT_HAS_JOURNAL is set
|
||||||
|
journal_uuid: [u8; 16], // UUID of journal superblock
|
||||||
|
journal_inum: u32, // inode number of journal file
|
||||||
|
journal_dev: u32, /* Device number of journal file, if the external journal feature
|
||||||
|
* flag is set */
|
||||||
|
last_orphan: u32, // Start of list of orphaned inodes to delete
|
||||||
|
hash_seed: [u32; 4], // HTREE hash seed
|
||||||
|
def_hash_version: u8, /* Default hash algorithm to use for directory hashes. See
|
||||||
|
* super_def_hash for more info */
|
||||||
|
jnl_backup_type: u8, /* If this value is 0 or EXT3_JNL_BACKUP_BLOCKS (1), then the
|
||||||
|
* s_jnl_blocks field contains a duplicate copy of the inode’s
|
||||||
|
* i_block[] array and i_size */
|
||||||
|
desc_size: u16, /* Size of group descriptors, in bytes, if the 64bit incompat feature flag
|
||||||
|
* is set */
|
||||||
|
default_mount_opts: u32, // Default mount options. See the super_mountopts table for more info
|
||||||
|
first_meta_bg: u32, // First metablock block group, if the meta_bg feature is enabled
|
||||||
|
mkfs_time: u32, // When the filesystem was created, in seconds since the epoch
|
||||||
|
jnl_blocks: [u32; 17], /* Backup copy of the journal inode’s i_block[] array in the first
|
||||||
|
* 15 elements and i_size_high and i_size in the 16th and 17th
|
||||||
|
* elements, respectively */
|
||||||
|
|
||||||
|
// 64bit support is valid only if EXT4_FEATURE_COMPAT_64BIT is set
|
||||||
|
blocks_count_hi: u32, // High 32-bits of the block count
|
||||||
|
r_blocks_count_hi: u32, // High 32-bits of the reserved block count
|
||||||
|
free_blocks_count_hi: u32, // High 32-bits of the free block count
|
||||||
|
min_extra_isize: u16, // All inodes have at least # bytes
|
||||||
|
want_extra_isize: u16, // New inodes should reserve # bytes
|
||||||
|
flags: u32, // Miscellaneous flags. See the super_flags table for more info
|
||||||
|
raid_stride: u16, /* RAID stride. This is the number of logical blocks read from or
|
||||||
|
* written to the disk before moving to the next disk. This
|
||||||
|
* affects the placement of filesystem metadata, which will
|
||||||
|
* hopefully make RAID storage faster */
|
||||||
|
mmp_interval: u16, /* #. seconds to wait in multi-mount prevention (MMP) checking. In
|
||||||
|
* theory, MMP is a mechanism to record in the superblock which host and
|
||||||
|
* device have mounted the filesystem, in order to prevent multiple
|
||||||
|
* mounts. This feature does not seem to be implemented... */
|
||||||
|
mmp_block: u64, // Block # for multi-mount protection data
|
||||||
|
raid_stripe_width: u32, /* RAID stripe width. This is the number of logical blocks read from
|
||||||
|
* or written to the disk before coming back to the current disk.
|
||||||
|
* This is used by the block allocator to try to reduce the number
|
||||||
|
* of read-modify-write operations in a RAID5/6 */
|
||||||
|
log_groups_per_flex: u8, // Size of a flexible block group is 2 ^ s_log_groups_per_flex
|
||||||
|
checksum_type: u8, /* Metadata checksum algorithm type. The only valid value is 1
|
||||||
|
* (crc32c) */
|
||||||
|
encryption_level: u8, // Versioning level for encryption
|
||||||
|
reserved_pad: u8, // Padding to next 32bits
|
||||||
|
kbytes_written: u64, // Number of KiB written to this filesystem over its lifetime
|
||||||
|
snapshot_inum: u32, /* inode number of active snapshot. (Not used in
|
||||||
|
* e2fsprogs/Linux.) */
|
||||||
|
snapshot_id: i32, // Sequential ID of active snapshot. (Not used in e2fsprogs/Linux.)
|
||||||
|
snapshot_r_blocks_count: u64, /* Number of blocks reserved for active snapshot’s future use.
|
||||||
|
* (Not used in e2fsprogs/Linux.) */
|
||||||
|
snapshot_list: u32, /* inode number of the head of the on-disk snapshot list. (Not used in
|
||||||
|
* e2fsprogs/Linux.) */
|
||||||
|
error_count: u32, // Number of errors seen
|
||||||
|
first_error_time: u32, // First time an error happened, in seconds since the epoch
|
||||||
|
first_error_ino: u32, // inode involved in first error
|
||||||
|
first_error_block: u64, // Number of block involved of first error
|
||||||
|
first_error_func: [u8; 32], // Name of function where the error happened
|
||||||
|
first_error_line: u32, // Line number where error happened
|
||||||
|
last_error_time: u32, // Time of most recent error, in seconds since the epoch
|
||||||
|
last_error_ino: u32, // inode involved in most recent error
|
||||||
|
last_error_line: u32, // Line number where most recent error happened
|
||||||
|
last_error_block: u64, // Number of block involved in most recent error
|
||||||
|
last_error_func: [u8; 32], // Name of function where the most recent error happened
|
||||||
|
mount_opts: [u8; 64], // ASCIIZ string of mount options
|
||||||
|
usr_quota_inum: u32, // Inode number of user quota file
|
||||||
|
grp_quota_inum: u32, // Inode number of group quota file
|
||||||
|
overhead_blocks: u32, /* Overhead blocks/clusters in fs. (Huh? This field is always
|
||||||
|
* zero, which means that the
|
||||||
|
* kernel calculates it dynamically.) */
|
||||||
|
backup_bgs: [u32; 2], // Block groups containing superblock backups (if sparse_super2)
|
||||||
|
encrypt_algos: [u8; 4], /* Encryption algorithms in use. There can be up to four algorithms in
|
||||||
|
* use at any time; valid algorithm codes are given in the
|
||||||
|
* super_encrypt table below */
|
||||||
|
encrypt_pw_salt: [u8; 16], // Salt for the string2key algorithm for encryption
|
||||||
|
lpf_ino: u32, // Inode number of lost+found
|
||||||
|
prj_quota_inum: u32, // Inode that tracks project quotas
|
||||||
|
checksum_seed: u32, /* Checksum seed used for metadata_csum calculations. This value
|
||||||
|
* is crc32c(~0, $orig_fs_uuid) */
|
||||||
|
wtime_hi: u8, // Upper 8 bits of the s_wtime field
|
||||||
|
mtime_hi: u8, // Upper 8 bits of the s_mtime field
|
||||||
|
mkfs_time_hi: u8, // Upper 8 bits of the s_mkfs_time field
|
||||||
|
lastcheck_hi: u8, // Upper 8 bits of the s_lastcheck field
|
||||||
|
first_error_time_hi: u8, // Upper 8 bits of the s_first_error_time field
|
||||||
|
last_error_time_hi: u8, // Upper 8 bits of the s_last_error_time field
|
||||||
|
first_error_errcode: u8,
|
||||||
|
last_error_errcode: u8,
|
||||||
|
encoding: u16, // Filename charset encoding
|
||||||
|
encoding_flags: u16, // Filename charset encoding flags
|
||||||
|
orphan_file_inum: u32, // Orphan file inode number
|
||||||
|
reserved: [u32; 94], // Padding to the end of the block
|
||||||
|
checksum: u32, // Superblock checksum
|
||||||
|
}
|
||||||
|
|
||||||
|
// offset of checksum + size of checksum
|
||||||
|
pub const SUPERBLOCK_SIZE: usize = 1024;
|
||||||
|
|
||||||
|
const_assert_eq!(std::mem::size_of::<Superblock>(), SUPERBLOCK_SIZE);
|
||||||
|
|
||||||
|
impl Superblock {
|
||||||
|
pub fn from_bytes(bytes: &[u8]) -> Result<Superblock, SuperblockError> {
|
||||||
|
assert_eq!(bytes.len(), std::mem::size_of::<Superblock>());
|
||||||
|
|
||||||
|
let super_block = unsafe { std::ptr::read(bytes.as_ptr() as *const Superblock) };
|
||||||
|
|
||||||
|
let checksum = super_block.calc_checksum();
|
||||||
|
|
||||||
|
if super_block.checksum != checksum {
|
||||||
|
return Err(SuperblockError::InvalidChecksum {
|
||||||
|
expected: checksum,
|
||||||
|
actual: super_block.checksum,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(super_block)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calc_checksum(&self) -> u32 {
|
||||||
|
// let mut crc: u32 = !0;
|
||||||
|
|
||||||
|
let bytes = unsafe {
|
||||||
|
let ptr = self as *const Superblock as *const u8;
|
||||||
|
let len = std::mem::size_of::<Superblock>();
|
||||||
|
|
||||||
|
let slice = std::slice::from_raw_parts(ptr, len);
|
||||||
|
|
||||||
|
&slice[..len - 4]
|
||||||
|
};
|
||||||
|
|
||||||
|
crc32(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inodes_count(&self) -> u32 {
|
||||||
|
self.inodes_count
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blocks_count(&self) -> u64 {
|
||||||
|
combine_lo_hi(self.blocks_count_lo, self.blocks_count_hi)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn r_blocks_count(&self) -> u64 {
|
||||||
|
combine_lo_hi(self.r_blocks_count_lo, self.r_blocks_count_hi)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn free_blocks_count(&self) -> u64 {
|
||||||
|
combine_lo_hi(self.free_blocks_count_lo, self.free_blocks_count_hi)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn free_inodes_count(&self) -> u32 {
|
||||||
|
self.free_inodes_count
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blocksize(&self) -> u64 {
|
||||||
|
1 << (10 + self.log_block_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blocks_per_group(&self) -> u32 {
|
||||||
|
self.blocks_per_groups
|
||||||
|
}
|
||||||
|
}
|
||||||
83
src/utils.rs
Normal file
83
src/utils.rs
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
const CRC_TABLE: [u32; 256] = [
|
||||||
|
0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb,
|
||||||
|
0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24,
|
||||||
|
0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b, 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384,
|
||||||
|
0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, 0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b,
|
||||||
|
0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35,
|
||||||
|
0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5, 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa,
|
||||||
|
0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, 0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a,
|
||||||
|
0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595,
|
||||||
|
0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48, 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957,
|
||||||
|
0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, 0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198,
|
||||||
|
0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38,
|
||||||
|
0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8, 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7,
|
||||||
|
0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, 0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789,
|
||||||
|
0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46,
|
||||||
|
0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9, 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6,
|
||||||
|
0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, 0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829,
|
||||||
|
0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93,
|
||||||
|
0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043, 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c,
|
||||||
|
0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, 0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc,
|
||||||
|
0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033,
|
||||||
|
0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652, 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d,
|
||||||
|
0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, 0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982,
|
||||||
|
0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622,
|
||||||
|
0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2, 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed,
|
||||||
|
0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, 0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f,
|
||||||
|
0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0,
|
||||||
|
0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f, 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540,
|
||||||
|
0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, 0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f,
|
||||||
|
0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1,
|
||||||
|
0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321, 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e,
|
||||||
|
0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, 0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e,
|
||||||
|
0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351,
|
||||||
|
];
|
||||||
|
|
||||||
|
pub fn crc32(bytes: &[u8]) -> u32 {
|
||||||
|
let mut crc: u32 = !0;
|
||||||
|
|
||||||
|
for &byte in bytes {
|
||||||
|
crc = (crc >> 8) ^ CRC_TABLE[(crc as u8 ^ byte) as usize];
|
||||||
|
}
|
||||||
|
|
||||||
|
crc
|
||||||
|
}
|
||||||
|
|
||||||
|
// a group has a superblock if it is a power of 3, of 5, or of 7
|
||||||
|
// (see https://docs.kernel.org/filesystems/ext4/globals.html#super-block)
|
||||||
|
pub fn group_has_superblock(group_num: u64) -> bool {
|
||||||
|
// this function calculates the max power of n that can be represented by a u64
|
||||||
|
const fn max_power_in_u64(n: u64) -> u64 {
|
||||||
|
let mut max = n;
|
||||||
|
|
||||||
|
while let Some(new_max) = max.checked_mul(n) {
|
||||||
|
max = new_max;
|
||||||
|
}
|
||||||
|
|
||||||
|
max
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAX_POWER_OF_3: u64 = max_power_in_u64(3);
|
||||||
|
const MAX_POWER_OF_5: u64 = max_power_in_u64(5);
|
||||||
|
const MAX_POWER_OF_7: u64 = max_power_in_u64(7);
|
||||||
|
|
||||||
|
// fast path: groups 0 and 1 have superblocks
|
||||||
|
if group_num <= 1 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fast path: even groups above 1 never have superblocks
|
||||||
|
if group_num % 2 == 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the way this works is that a smaller power of n will always divide a larger power of n, thus
|
||||||
|
// for u64s, x is a power of n iff it divides the largest power of n
|
||||||
|
MAX_POWER_OF_3 % group_num == 0
|
||||||
|
|| MAX_POWER_OF_5 % group_num == 0
|
||||||
|
|| MAX_POWER_OF_7 % group_num == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn combine_lo_hi(lo: u32, hi: u32) -> u64 {
|
||||||
|
lo as u64 | ((hi as u64) << 32)
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue