ableos/ext2-rs/src/sys/superblock.rs
2022-07-27 19:51:13 -05:00

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!()),
);
}
}