forked from AbleOS/ableos
285 lines
9.5 KiB
Rust
285 lines
9.5 KiB
Rust
//! Superblock information
|
|
|
|
use {
|
|
core::{fmt::Debug, mem},
|
|
error::Error,
|
|
sector::{Address, SectorSize},
|
|
volume::Volume,
|
|
};
|
|
|
|
/// Ext2 signature (0xef53), used to help confirm the presence of Ext2 on a
|
|
/// volume
|
|
pub const EXT2_MAGIC: u16 = 0xef53;
|
|
|
|
/// Filesystem is free of errors
|
|
pub const FS_CLEAN: u16 = 1;
|
|
/// Filesystem has errors
|
|
pub const FS_ERR: u16 = 2;
|
|
|
|
/// Ignore errors
|
|
pub const ERR_IGNORE: u16 = 1;
|
|
/// Remount as read-only on error
|
|
pub const ERR_RONLY: u16 = 2;
|
|
/// Panic on error
|
|
pub const ERR_PANIC: u16 = 3;
|
|
|
|
/// Creator OS is Linux
|
|
pub const OS_LINUX: u32 = 0;
|
|
/// Creator OS is Hurd
|
|
pub const OS_HURD: u32 = 1;
|
|
/// Creator OS is Masix
|
|
pub const OS_MASIX: u32 = 2;
|
|
/// Creator OS is FreeBSD
|
|
pub const OS_FREEBSD: u32 = 3;
|
|
/// Creator OS is a BSD4.4-Lite derivative
|
|
pub const OS_LITE: u32 = 4;
|
|
|
|
/// The Superblock contains all information about the layout of the file system
|
|
/// and possibly contains other important information like what optional
|
|
/// features were used to create the file system.
|
|
///
|
|
/// The Superblock is always located at byte 1024 from the beginning of the
|
|
/// volume and is exactly 1024 bytes in length. For example, if the disk uses
|
|
/// 512 byte sectors, the Superblock will begin at LBA 2 and will occupy all of
|
|
/// sector 2 and 3.
|
|
#[repr(C, packed)]
|
|
#[derive(Clone, Debug, Copy)]
|
|
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: FeaturesOptional,
|
|
/// Required features present (features that are required to be
|
|
/// supported to read or write)
|
|
pub features_req: FeaturesRequired,
|
|
/// Features that if not supported, the volume must be mounted
|
|
/// read-only)
|
|
pub features_ronly: FeaturesROnly,
|
|
/// 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,
|
|
#[doc(hidden)]
|
|
_reserved: [u8; 788],
|
|
}
|
|
impl Superblock {
|
|
/// Discover the location of the superblock in the given block device.
|
|
pub unsafe fn find<S: SectorSize, V: Volume<u8, S>>(
|
|
haystack: &V,
|
|
) -> Result<(Superblock, Address<S>), Error> {
|
|
let offset = Address::from(1024_usize);
|
|
let end = offset + Address::from(mem::size_of::<Superblock>());
|
|
if haystack.size() < end {
|
|
return Err(Error::AddressOutOfBounds {
|
|
sector: end.sector(),
|
|
offset: end.offset(),
|
|
size: end.sector_size(),
|
|
});
|
|
}
|
|
|
|
let superblock = {
|
|
haystack
|
|
.slice_unchecked(offset..end)
|
|
.dynamic_cast::<Superblock>()
|
|
};
|
|
|
|
if superblock.0.magic != EXT2_MAGIC {
|
|
Err(Error::BadMagic {
|
|
magic: superblock.0.magic,
|
|
})
|
|
} else {
|
|
Ok(superblock)
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
/// Return the block size
|
|
pub fn block_size(&self) -> usize {
|
|
1024 << self.log_block_size
|
|
}
|
|
|
|
#[inline]
|
|
/// Return the fragment size
|
|
pub fn frag_size(&self) -> usize {
|
|
1024 << self.log_frag_size
|
|
}
|
|
/// Return the number of blocks per group
|
|
pub fn block_group_count(&self) -> Result<u32, (u32, u32)> {
|
|
let blocks_mod = self.blocks_count % self.blocks_per_group;
|
|
let inodes_mod = self.inodes_count % self.inodes_per_group;
|
|
let blocks_inc = if blocks_mod == 0 { 0 } else { 1 };
|
|
let inodes_inc = if inodes_mod == 0 { 0 } else { 1 };
|
|
let by_blocks = self.blocks_count / self.blocks_per_group + blocks_inc;
|
|
let by_inodes = self.inodes_count / self.inodes_per_group + inodes_inc;
|
|
if by_blocks == by_inodes {
|
|
Ok(by_blocks)
|
|
} else {
|
|
Err((by_blocks, by_inodes))
|
|
}
|
|
}
|
|
}
|
|
|
|
bitflags! {
|
|
/// Optional features
|
|
pub struct FeaturesOptional: u32 {
|
|
/// Preallocate some number of (contiguous?) blocks (see
|
|
/// `Superblock::prealloc_blocks_dirs`) to a directory when creating a new one
|
|
const PREALLOCATE = 0x0001;
|
|
/// AFS server inodes exist
|
|
const AFS = 0x0002;
|
|
/// File system has a journal (Ext3)
|
|
const JOURNAL = 0x0004;
|
|
/// Inodes have extended attributes
|
|
const EXTENDED_INODE = 0x0008;
|
|
/// File system can resize itself for larger partitions
|
|
const SELF_RESIZE = 0x0010;
|
|
/// Directories use hash index
|
|
const HASH_INDEX = 0x0020;
|
|
}
|
|
}
|
|
|
|
bitflags! {
|
|
/// Required features. If these are not supported; can't mount
|
|
pub struct FeaturesRequired: u32 {
|
|
/// Compression is used
|
|
const REQ_COMPRESSION = 0x0001;
|
|
/// Directory entries contain a type field
|
|
const REQ_DIRECTORY_TYPE = 0x0002;
|
|
/// File system needs to replay its journal
|
|
const REQ_REPLAY_JOURNAL = 0x0004;
|
|
/// File system uses a journal device
|
|
const REQ_JOURNAL_DEVICE = 0x0008;
|
|
}
|
|
}
|
|
|
|
bitflags! {
|
|
/// ROnly features. If these are not supported; remount as read-only
|
|
pub struct FeaturesROnly: u32 {
|
|
/// Sparse superblocks and group descriptor tables
|
|
const RONLY_SPARSE = 0x0001;
|
|
/// File system uses a 64-bit file size
|
|
const RONLY_FILE_SIZE_64 = 0x0002;
|
|
/// Directory contents are stored in the form of a Binary Tree
|
|
const RONLY_BTREE_DIRECTORY = 0x0004;
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use sector::Size512;
|
|
|
|
#[test]
|
|
fn find() {
|
|
let mut volume = vec![0_u8; 4096];
|
|
// magic
|
|
volume[1024 + 56] = EXT2_MAGIC as u8;
|
|
volume[1024 + 57] = (EXT2_MAGIC >> 8) as u8;
|
|
let superblock = unsafe { Superblock::find::<Size512, _>(&volume) };
|
|
assert!(
|
|
superblock.is_ok(),
|
|
"Err({:?})",
|
|
superblock.err().unwrap_or_else(|| unreachable!()),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn superblock() {
|
|
use std::cell::RefCell;
|
|
use std::fs::File;
|
|
|
|
let file = RefCell::new(File::open("ext2.img").unwrap());
|
|
let superblock = unsafe { Superblock::find::<Size512, _>(&file) };
|
|
assert!(
|
|
superblock.is_ok(),
|
|
"Err({:?})",
|
|
superblock.err().unwrap_or_else(|| unreachable!()),
|
|
);
|
|
}
|
|
}
|