From 9f210a87565bf02992bbb653b30c45aeffa22636 Mon Sep 17 00:00:00 2001 From: llenotre Date: Thu, 4 Jul 2024 01:13:40 +0200 Subject: [PATCH] refactor: move `fdisk` and `mkfs` to the common executable --- fdisk/Cargo.toml | 16 ------ mkfs/Cargo.toml | 7 --- {fdisk/src => src/fdisk}/crc32.rs | 0 {fdisk/src => src/fdisk}/disk.rs | 2 +- {fdisk/src => src/fdisk}/guid.rs | 0 {fdisk/src => src/fdisk}/lib.rs | 0 fdisk/src/main.rs => src/fdisk/mod.rs | 81 ++++++++++----------------- {fdisk/src => src/fdisk}/partition.rs | 66 +++++++++++----------- src/main.rs | 10 +++- {mkfs/src => src/mkfs}/ext2.rs | 2 +- mkfs/src/main.rs => src/mkfs/mod.rs | 64 +++++++-------------- src/umount.rs | 15 +++-- 12 files changed, 102 insertions(+), 161 deletions(-) delete mode 100644 fdisk/Cargo.toml delete mode 100644 mkfs/Cargo.toml rename {fdisk/src => src/fdisk}/crc32.rs (100%) rename {fdisk/src => src/fdisk}/disk.rs (99%) rename {fdisk/src => src/fdisk}/guid.rs (100%) rename {fdisk/src => src/fdisk}/lib.rs (100%) rename fdisk/src/main.rs => src/fdisk/mod.rs (83%) rename {fdisk/src => src/fdisk}/partition.rs (97%) rename {mkfs/src => src/mkfs}/ext2.rs (99%) rename mkfs/src/main.rs => src/mkfs/mod.rs (64%) diff --git a/fdisk/Cargo.toml b/fdisk/Cargo.toml deleted file mode 100644 index 4f6af7e..0000000 --- a/fdisk/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "fdisk" -version = "0.1.0" -edition = "2021" - -[lib] -name = "fdisk" -path = "src/lib.rs" - -[[bin]] -name = "fdisk" -path = "src/main.rs" - -[dependencies] -libc = "*" -utils = { path = "../utils" } diff --git a/mkfs/Cargo.toml b/mkfs/Cargo.toml deleted file mode 100644 index d745215..0000000 --- a/mkfs/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "mkfs" -version = "0.1.0" -edition = "2021" - -[dependencies] -utils = { path = "../utils" } diff --git a/fdisk/src/crc32.rs b/src/fdisk/crc32.rs similarity index 100% rename from fdisk/src/crc32.rs rename to src/fdisk/crc32.rs diff --git a/fdisk/src/disk.rs b/src/fdisk/disk.rs similarity index 99% rename from fdisk/src/disk.rs rename to src/fdisk/disk.rs index 4de34be..138b258 100644 --- a/fdisk/src/disk.rs +++ b/src/fdisk/disk.rs @@ -1,6 +1,6 @@ //! TODO doc -use crate::partition::PartitionTable; +use super::partition::PartitionTable; use libc::c_long; use libc::ioctl; use std::fmt; diff --git a/fdisk/src/guid.rs b/src/fdisk/guid.rs similarity index 100% rename from fdisk/src/guid.rs rename to src/fdisk/guid.rs diff --git a/fdisk/src/lib.rs b/src/fdisk/lib.rs similarity index 100% rename from fdisk/src/lib.rs rename to src/fdisk/lib.rs diff --git a/fdisk/src/main.rs b/src/fdisk/mod.rs similarity index 83% rename from fdisk/src/main.rs rename to src/fdisk/mod.rs index d6b4b21..f137942 100644 --- a/fdisk/src/main.rs +++ b/src/fdisk/mod.rs @@ -1,18 +1,16 @@ -//! `fdisk` is an utility command used to manipulate disk partition tables. +//! `fdisk` is a utility command used to manipulate disk partition tables. //! //! The `sfdisk` is also implemented in the same program, it has the purpose as `fdisk`, except it //! uses scripting instead of prompting. -#![feature(iter_array_chunks)] - mod crc32; mod disk; mod guid; mod partition; -use crate::partition::PartitionTable; use disk::Disk; -use std::env; +use partition::PartitionTable; +use std::env::ArgsOs; use std::fs; use std::fs::OpenOptions; use std::io; @@ -21,22 +19,16 @@ use std::path::Path; use std::path::PathBuf; use std::process::exit; use std::str::FromStr; +use utils::error; use utils::prompt::prompt; /// Structure storing command line arguments. #[derive(Default)] struct Args { - /// The name of the current program used in command line. - prog: String, - /// Tells whether the command is run in scripting mode. - script: bool, - /// If true, print command line help. help: bool, - /// If true, list partitions instead of modifying the table. list: bool, - /// The list of disk devices. disks: Vec, } @@ -51,38 +43,34 @@ impl Args { } } -fn parse_args() -> Args { - let mut args: Args = Default::default(); - let mut iter = env::args(); - args.prog = iter.next().unwrap_or("fdisk".to_owned()); - args.script = args.prog.split('/').last() == Some("sfdisk"); - for arg in iter { - match arg.as_str() { - "-h" | "--help" => args.help = true, - "-l" | "--list" => args.list = true, +fn parse_args(args: ArgsOs) -> Args { + let mut res: Args = Default::default(); + for arg in args { + match arg.to_str() { + Some("-h" | "--help") => res.help = true, + Some("-l" | "--list") => res.list = true, // TODO implement other options - _ => args.disks.push(arg.into()), + _ => res.disks.push(arg.into()), } } - args + res } /// Prints command usage. -/// -/// `prog` is the name of the current program. -fn print_usage(prog: &str) { - eprintln!("{prog}: bad usage"); - eprintln!("Try '{prog} --help' for more information."); +fn print_usage() { + eprintln!("fdisk: bad usage"); + eprintln!("Try 'fdisk --help' for more information."); } /// Prints command help. /// /// - `prog` is the name of the current program. /// - `script` tells whether the program is run as `sfdisk`. -fn print_help(prog: &str, script: bool) { +fn print_help(script: bool) { + let bin = if !script { "fdisk" } else { "sfdisk" }; println!(); println!("Usage:"); - println!(" {prog} [options] [disks...]"); + println!(" {bin} [options] [disks...]"); println!(); println!("Prints the list of partitions or modify it."); println!(); @@ -268,15 +256,15 @@ fn handle_cmd(cmd: &str, disk_path: &Path, disk: &mut Disk) { println!(); } -fn main() { - let args = parse_args(); +pub fn main(script: bool, args: ArgsOs) { + let args = parse_args(args); if !args.is_valid() { - print_usage(&args.prog); + print_usage(); exit(1); } if args.help { - print_help(&args.prog, args.script); + print_help(script); exit(0); } @@ -286,38 +274,27 @@ fn main() { } else { match Disk::list() { Ok(disks) => disks.into_iter(), - - Err(e) => { - eprintln!("{}: cannot list disks: {e}", args.prog); - exit(1); - } + Err(e) => error("fdisk", format_args!("cannot list disks: {e}")), } }; - for path in iter { match Disk::read(path.clone()) { - Ok(Some(disk)) => print!("{}", disk), - + Ok(Some(disk)) => print!("{disk}"), Ok(None) => { - eprintln!( - "{}: cannot open {}: Invalid argument", - args.prog, - path.display() + error( + "fdisk", + format_args!("cannot open {}: Invalid argument", path.display()), ); } - - Err(e) => { - eprintln!("{}: cannot open {}: {e}", args.prog, path.display()); - } + Err(e) => error("fdisk", format_args!("cannot open {}: {e}", path.display())), } } - return; } let disk_path = &args.disks[0]; - if !args.script { + if !script { let mut disk = Disk::read(disk_path.clone()) .unwrap() // TODO handle error .unwrap(); // TODO handle error diff --git a/fdisk/src/partition.rs b/src/fdisk/partition.rs similarity index 97% rename from fdisk/src/partition.rs rename to src/fdisk/partition.rs index a26f146..5e8bfac 100644 --- a/fdisk/src/partition.rs +++ b/src/fdisk/partition.rs @@ -1,7 +1,7 @@ //! TODO -use crate::crc32; -use crate::guid::Guid; +use super::crc32; +use super::guid::Guid; use std::cmp::max; use std::cmp::min; use std::fmt; @@ -49,10 +49,10 @@ fn translate_lba(lba: i64, storage_size: u64) -> Option { } } -/// Structure representing a MBR partition. +/// A MBR partition. #[repr(C, packed)] #[derive(Clone, Copy, Default)] -struct MBRPartition { +struct MbrPartition { /// Partition attributes. attrs: u8, /// CHS address of partition start. @@ -67,16 +67,16 @@ struct MBRPartition { sectors_count: u32, } -impl MBRPartition { +impl MbrPartition { /// Tells whether the partition is active. pub fn is_active(&self) -> bool { self.attrs & (1 << 7) != 0 } } -/// Structure representing a MBR partition table. +/// A MBR partition table. #[repr(C, packed)] -pub struct MBRTable { +pub struct MbrTable { /// The boot code. boot: [u8; 440], /// The disk signature (optional). @@ -84,14 +84,14 @@ pub struct MBRTable { /// Zero. zero: u16, /// The list of partitions. - partitions: [MBRPartition; 4], + partitions: [MbrPartition; 4], /// The partition table signature. signature: u16, } -/// Structure representing a GPT entry. +/// A GPT entry. #[repr(C, packed)] -struct GPTEntry { +struct GptEntry { /// The partition type's GUID. partition_type: Guid, /// The partition's GUID. @@ -106,10 +106,10 @@ struct GPTEntry { name: [u16; 36], } -/// Structure representing the GPT header. +/// The GPT header. #[derive(Clone, Copy)] #[repr(C, packed)] -pub struct GPT { +pub struct Gpt { /// The header's signature. signature: [u8; 8], /// The header's revision. @@ -889,11 +889,11 @@ impl PartitionTableType { pub fn read(&self, dev: &mut File, sectors_count: u64) -> io::Result>> { match self { Self::Mbr => { - let mut buff: [u8; size_of::()] = [0; size_of::()]; + let mut buff: [u8; size_of::()] = [0; size_of::()]; dev.seek(SeekFrom::Start(0))?; dev.read_exact(&mut buff)?; - let mbr = unsafe { &*(buff.as_ptr() as *const MBRTable) }; + let mbr = unsafe { &*(buff.as_ptr() as *const MbrTable) }; if mbr.signature != MBR_SIGNATURE { return Ok(None); } @@ -917,11 +917,11 @@ impl PartitionTableType { } Self::Gpt => { - let mut buff: [u8; size_of::()] = [0; size_of::()]; + let mut buff: [u8; size_of::()] = [0; size_of::()]; dev.seek(SeekFrom::Start(512))?; dev.read_exact(&mut buff)?; - let hdr = unsafe { &mut *(buff.as_mut_ptr() as *mut GPT) }; + let hdr = unsafe { &mut *(buff.as_mut_ptr() as *mut Gpt) }; // Check signature if hdr.signature != GPT_SIGNATURE { return Ok(None); @@ -955,7 +955,7 @@ impl PartitionTableType { dev.seek(SeekFrom::Start(off as _))?; dev.read_exact(&mut buff)?; - let entry = unsafe { &*(buff.as_ptr() as *const GPTEntry) }; + let entry = unsafe { &*(buff.as_ptr() as *const GptEntry) }; // If entry is unused, skip if entry.guid.0.iter().all(|i| *i == 0) { @@ -985,8 +985,8 @@ impl PartitionTableType { dev: &mut File, storage_size: u64, hdr_off: i64, - hdr: &GPT, - parts: &[GPTEntry], + hdr: &Gpt, + parts: &[GptEntry], ) -> io::Result<()> { let sector_size = 512; // TODO @@ -994,17 +994,17 @@ impl PartitionTableType { let entries_off = translate_lba(hdr.entries_start, storage_size).unwrap() * sector_size; for (i, entry) in parts.iter().enumerate() { - let off = entries_off + i as u64 * size_of::() as u64; + let off = entries_off + i as u64 * size_of::() as u64; let entry_slice = unsafe { - slice::from_raw_parts(entry as *const _ as *const _, size_of::()) + slice::from_raw_parts(entry as *const _ as *const _, size_of::()) }; dev.seek(SeekFrom::Start(off))?; dev.write_all(entry_slice)?; } let hdr_slice = - unsafe { slice::from_raw_parts(hdr as *const _ as *const _, size_of::()) }; + unsafe { slice::from_raw_parts(hdr as *const _ as *const _, size_of::()) }; dev.seek(SeekFrom::Start(hdr_off))?; dev.write_all(hdr_slice)?; @@ -1025,11 +1025,11 @@ impl PartitionTableType { ) -> io::Result<()> { match self { Self::Mbr => { - let mut mbr = MBRTable { + let mut mbr = MbrTable { boot: [0; 440], disk_signature: 0, zero: 0, - partitions: [MBRPartition::default(); 4], + partitions: [MbrPartition::default(); 4], signature: MBR_SIGNATURE, }; @@ -1042,7 +1042,7 @@ impl PartitionTableType { let PartitionType::Mbr(partition_type) = p.part_type else { panic!("invalid partition type of MBR table"); }; - mbr.partitions[i] = MBRPartition { + mbr.partitions[i] = MbrPartition { attrs: 0, chs_start: [0; 3], partition_type, @@ -1055,7 +1055,7 @@ impl PartitionTableType { let slice = unsafe { slice::from_raw_parts( (&mbr as *const _ as *const u8).add(mbr.boot.len()), - size_of::() - mbr.boot.len(), + size_of::() - mbr.boot.len(), ) }; dev.seek(SeekFrom::Start(mbr.boot.len() as _))?; @@ -1085,10 +1085,10 @@ impl PartitionTableType { )?; // Primary table - let mut gpt = GPT { + let mut gpt = Gpt { signature: [0; 8], revision: 0x010000, - hdr_size: size_of::() as _, + hdr_size: size_of::() as _, checksum: 0, reserved: 0, hdr_lba: 1, @@ -1103,13 +1103,13 @@ impl PartitionTableType { }; gpt.signature.copy_from_slice(GPT_SIGNATURE); - let parts: Vec = partitions + let parts: Vec = partitions .iter() .map(|p| { let PartitionType::Gpt(partition_type) = p.part_type else { panic!("invalid partition type of GPT table"); }; - GPTEntry { + GptEntry { partition_type, guid: p.uuid.unwrap(), start: p.start as _, @@ -1126,13 +1126,13 @@ impl PartitionTableType { let parts_slice = unsafe { slice::from_raw_parts( parts.as_ptr() as *const u8, - parts.len() * size_of::(), + parts.len() * size_of::(), ) }; gpt.entries_checksum = crc32::compute(parts_slice, &crc32_table); let hdr_slice = unsafe { - slice::from_raw_parts(&gpt as *const _ as *const u8, size_of::()) + slice::from_raw_parts(&gpt as *const _ as *const u8, size_of::()) }; gpt.checksum = crc32::compute(hdr_slice, &crc32_table); @@ -1143,7 +1143,7 @@ impl PartitionTableType { gpt.alternate_hdr_lba = 1; gpt.entries_start = -33; let hdr_slice = unsafe { - slice::from_raw_parts(&gpt as *const _ as *const u8, size_of::()) + slice::from_raw_parts(&gpt as *const _ as *const u8, size_of::()) }; gpt.checksum = crc32::compute(hdr_slice, &crc32_table); Self::write_gpt(dev, sectors_count, -1, &gpt, &parts)?; diff --git a/src/main.rs b/src/main.rs index b34822f..e9cc39e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,14 @@ //! Main of all commands that **do not require** the SUID flag. +#![feature(iter_array_chunks)] #![feature(option_get_or_insert_default)] #![feature(os_str_display)] mod dmesg; +mod fdisk; mod insmod; mod lsmod; +mod mkfs; mod mount; mod nologin; mod ps; @@ -25,14 +28,15 @@ fn main() { }); match bin.as_str() { "dmesg" => dmesg::main(), - "fdisk" => todo!(), + "fdisk" => fdisk::main(false, args), + "sfdisk" => fdisk::main(true, args), "insmod" => insmod::main(args), "lsmod" => lsmod::main(), "rmmod" => rmmod::main(args), bin @ ("mkfs" | "mkfs.ext2") => { // TODO change default fs to `ext4` when implemented - let fs_name = bin.find(".").map(|i| &bin[(i + 1)..]).unwrap_or("ext2"); - todo!() + let fs_name = bin.find('.').map(|i| &bin[(i + 1)..]).unwrap_or("ext2"); + mkfs::main(fs_name, args); } "mount" => mount::main(args), "umount" => umount::main(args), diff --git a/mkfs/src/ext2.rs b/src/mkfs/ext2.rs similarity index 99% rename from mkfs/src/ext2.rs rename to src/mkfs/ext2.rs index 2df2728..e09df98 100644 --- a/mkfs/src/ext2.rs +++ b/src/mkfs/ext2.rs @@ -1,6 +1,6 @@ //! Module handling the `ext2` filesystem. -use crate::FSFactory; +use super::FSFactory; use std::cmp::min; use std::fs::File; use std::io; diff --git a/mkfs/src/main.rs b/src/mkfs/mod.rs similarity index 64% rename from mkfs/src/main.rs rename to src/mkfs/mod.rs index d5de670..504e80e 100644 --- a/mkfs/src/main.rs +++ b/src/mkfs/mod.rs @@ -1,58 +1,42 @@ //! The `mkfs` tool allows to create a filesystem on a device. -#![feature(int_roundings)] - mod ext2; use std::collections::HashMap; -use std::env; +use std::env::ArgsOs; use std::fs::File; use std::fs::OpenOptions; use std::io; use std::path::PathBuf; use std::process::exit; +use utils::error; use utils::prompt::prompt; /// Structure storing command line arguments. #[derive(Default)] struct Args { - /// The name of the current program used in command line. - prog: String, /// The select filesystem type. fs_type: String, - /// If true, print command line help. help: bool, - /// The path to the device file on which the filesystem will be created. device_path: Option, } -fn parse_args() -> Args { - let mut args: Args = Default::default(); - let mut iter = env::args(); - - args.prog = iter.next().unwrap_or("mkfs".to_owned()); - - let fs_type = if args.prog.contains('.') { - args.prog.split('.').last() - } else { - None - }; - args.fs_type = fs_type.unwrap_or("ext2").to_owned(); - - for arg in iter { - match arg.as_str() { - "-h" | "--help" => args.help = true, +fn parse_args(args: ArgsOs) -> Args { + let mut res: Args = Default::default(); + for arg in args { + match arg.to_str() { + Some("-h" | "--help") => res.help = true, // TODO implement other options // TODO get device path _ => { // TODO handle case when several devices are given - args.device_path = Some(PathBuf::from(arg)); + res.device_path = Some(PathBuf::from(arg)); } } } - args + res } /// A trait representing an object used to create a filesystem on a device. @@ -65,8 +49,8 @@ pub trait FSFactory { fn create(&self, dev: &mut File) -> io::Result<()>; } -fn main() { - let args = parse_args(); +pub fn main(fs_name: &str, args: ArgsOs) { + let args = parse_args(args); // TODO build factory according to arguments let factories = HashMap::<&str, Box>::from([( @@ -74,35 +58,31 @@ fn main() { Box::::default() as Box, )]); let factory = factories.get(args.fs_type.as_str()).unwrap_or_else(|| { - eprintln!("{}: invalid filesystem type `{}`", args.prog, args.fs_type); - exit(1); + error( + "mkfs", + format_args!("invalid filesystem type `{}`", args.fs_type), + ); }); - let device_path = args.device_path.unwrap_or_else(|| { - eprintln!("{}: specify path to a device", args.prog); - exit(1); + error("mkfs", "specify path to a device"); }); - let mut file = OpenOptions::new() .read(true) .write(true) .open(&device_path) .unwrap_or_else(|e| { - eprintln!("{}: {}: {}", args.prog, device_path.display(), e); - exit(1); + error("mkfs", format_args!("{}: {e}", device_path.display())); }); let prev_fs = factories.iter().find(|(_, factory)| { factory.is_present(&mut file).unwrap_or_else(|e| { - eprintln!("{}: {}: {}", args.prog, device_path.display(), e); - exit(1); + error("mkfs", format_args!("{}: {e}", device_path.display())); }) }); if let Some((prev_fs_type, _prev_fs_factory)) = prev_fs { println!( - "{} contains a file system of type: {}", - device_path.display(), - prev_fs_type + "{} contains a file system of type: {prev_fs_type}", + device_path.display() ); // TODO print details on fs (use factory) @@ -114,9 +94,7 @@ fn main() { exit(1); } } - factory.create(&mut file).unwrap_or_else(|e| { - eprintln!("{}: failed to create filesystem: {}", args.prog, e); - exit(1); + error("mkfs", format_args!("failed to create filesystem: {e}")); }); } diff --git a/src/umount.rs b/src/umount.rs index 484a015..fcdf732 100644 --- a/src/umount.rs +++ b/src/umount.rs @@ -36,9 +36,8 @@ pub fn main(args: ArgsOs) { match &args[..] { [opt, path] if opt == "-R" => { // List active mount points - let content = fs::read_to_string("/etc/mtab").unwrap_or_else(|e| { - error("umount", format_args!("cannot list mount points: {e}")) - }); + let content = fs::read_to_string("/etc/mtab") + .unwrap_or_else(|e| error("umount", format_args!("cannot list mount points: {e}"))); let mut mps: Vec<_> = content .split('\n') .filter_map(|entry| Some(entry.split(' ').nth(1)?.into())) @@ -50,14 +49,20 @@ pub fn main(args: ArgsOs) { for mp in mps.into_iter().rev() { let s = CString::new(mp.as_os_str().as_bytes()).unwrap(); unmount_fs(&s).unwrap_or_else(|e| { - error("umount", format_args!("cannot unmount `{}`: {e}", path.display())); + error( + "umount", + format_args!("cannot unmount `{}`: {e}", path.display()), + ); }); } } [path] => { let s = CString::new(args[1].as_bytes()).unwrap(); unmount_fs(&s).unwrap_or_else(|e| { - error("umount", format_args!("cannot unmount `{}`: {e}", path.display())); + error( + "umount", + format_args!("cannot unmount `{}`: {e}", path.display()), + ); }); } _ => {