From 565e2c52d4f2e24286cdff747d2861882f4d58c0 Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Tue, 7 Feb 2023 10:34:09 -0500 Subject: [PATCH] Add `swaplabel` applet --- Cargo.lock | 1 + Cargo.toml | 3 +- README.md | 18 ++++--- utilbox/commands/mod.rs | 8 ++- utilbox/commands/swaplabel/mod.rs | 81 +++++++++++++++++++++++++++++++ utilbox/commands/swapoff/mod.rs | 1 + utilbox/commands/swapon/mod.rs | 1 + 7 files changed, 104 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 249b90d..002268d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -579,6 +579,7 @@ dependencies = [ "clap_mangen", "data-encoding", "digest", + "lazy_static", "libc", "md-5", "mount", diff --git a/Cargo.toml b/Cargo.toml index 3d3641f..bfe2f08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,8 @@ clap_complete = "4.1" clap_complete_nushell = "0.1" clap_mangen = "0.2" data-encoding = "2.3" -digest = "0.10.6" +digest = "0.10" +lazy_static = "1.4" libc = { workspace = true } md5 = { version = "0.10", package = "md-5" } sc = { workspace = true } diff --git a/README.md b/README.md index b3d9bf2..37aae6f 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,10 @@ worth. Like Busybox it is a multi-call binary which is therefore able to share code between applets, making for an overall smaller binary. ## Provided Commands +- b2sum - base32 - base64 - basename -- bootstrap - chgrp - chmod - chown @@ -37,6 +37,7 @@ code between applets, making for an overall smaller binary. - hostid - hostname - link +- ln - logname - md5sum - mkfifo @@ -57,10 +58,11 @@ code between applets, making for an overall smaller binary. - sha256sum - sha384sum - sha512sum -- shitbox - sleep +- swaplabel - sync - true +- umount - unlink - wc - which @@ -75,9 +77,8 @@ for the project include: - Network servers - Kernel module handling utilities - Anything requiring suid or sgid bits such as `su` or `sudo` -The code aims to be portable across **Unix** variants, ie Linux and BSD, but not -MacOS or Windows. Development occurs on Linux, so if your OS is more exotic then -YMMV. +The code is not intended to be portable across different architectures, only +Linux is supported. ## Installation Building is done using the official Rust toolchain. It is recommended that you @@ -86,11 +87,16 @@ versions are not supported. ```Sh cargo build --release ``` +This produces three binaries: corebox, hashbox and utilbox. Most of the commands +ordinarily provided by GNU coreutils are applets of `corebox`, with the exception +of cryptographic hash related commands which are split out into `hashbox`. Linux +swpecific commands ordinarily provided by the util-linux package are in `utilbox`. + The `bootstrap` applet provides facility for installing the binary, creating all required symlinks and installing some nice to haves such as **Unix man pages** and **shell completions** [see below]. ```Sh -target/release/shitbox help bootstrap +target/release/corebox help bootstrap ``` ### Supported shells for completions - Bash diff --git a/utilbox/commands/mod.rs b/utilbox/commands/mod.rs index 09ed4b0..03b926a 100644 --- a/utilbox/commands/mod.rs +++ b/utilbox/commands/mod.rs @@ -4,9 +4,12 @@ mod blkid; mod clear; mod mount; mod mountpoint; +mod swaplabel; +mod swapoff; +mod swapon; mod umount; mod utilbox; -/// + /// Parses a string into a command to run #[must_use] #[allow(clippy::box_default)] @@ -14,10 +17,11 @@ pub fn get(name: &str) -> Option> { match name { "clear" => Some(Box::new(clear::Clear::default())), "mountpoint" => Some(Box::new(mountpoint::Mountpoint::default())), + "swaplabel" => Some(Box::new(swaplabel::Swaplabel::default())), "umount" => Some(Box::new(umount::Umount::default())), "utilbox" => Some(Box::new(utilbox::Utilbox::default())), _ => None, } } -pub static COMMANDS: [&str; 4] = ["clear", "mountpoint", "umount", "utilbox"]; +pub static COMMANDS: [&str; 5] = ["clear", "mountpoint", "swaplabel", "umount", "utilbox"]; diff --git a/utilbox/commands/swaplabel/mod.rs b/utilbox/commands/swaplabel/mod.rs index e69de29..16850d0 100644 --- a/utilbox/commands/swaplabel/mod.rs +++ b/utilbox/commands/swaplabel/mod.rs @@ -0,0 +1,81 @@ +use clap::{Arg, ArgMatches, Command}; +use lazy_static::lazy_static; +use shitbox::Cmd; +use std::{ + error::Error, + ffi::CString, + fs::OpenOptions, + io::{self, Read, Seek, SeekFrom, Write}, +}; + +const SWAP_MAGIC1: &'static str = "SWAPSPACE2"; +const SWAP_MAGIC2: &'static str = "SWAP_SPACE"; +const SWAP_MAGIC_LENGTH: u64 = 10; +const SWAP_LABEL_LENGTH: u64 = 16; +const SWAP_LABEL_OFFSET: u64 = 1024 + 4 + 4 + 4 + 16; + +lazy_static! { + static ref SWAP_MAGIC_OFFSET: u64 = + unsafe { libc::sysconf(libc::_SC_PAGESIZE) as u64 - SWAP_MAGIC_LENGTH }; +} + +#[derive(Debug, Default)] +pub struct Swaplabel; + +impl Cmd for Swaplabel { + fn cli(&self) -> Command { + Command::new("swaplabel") + .about("set the label of a swap filesystem") + .author("Nathan Fisher") + .version(env!("CARGO_PKG_VERSION")) + .args([ + Arg::new("label") + .help("set the label") + .short('L') + .visible_short_alias('l') + .long("label") + .num_args(1) + .required(false), + Arg::new("device").num_args(1).required(true), + ]) + } + + fn run(&self, matches: &ArgMatches) -> Result<(), Box> { + let mut opts = OpenOptions::new(); + let device = matches + .get_one::("device") + .ok_or(io::Error::new(io::ErrorKind::Other, "no device given"))?; + let mut fd = opts.read(true).write(true).open(device)?; + fd.seek(SeekFrom::Start(*SWAP_MAGIC_OFFSET))?; + let mut magic = [0; SWAP_MAGIC_LENGTH as usize]; + fd.read_exact(&mut magic)?; + let m: String = magic.iter().map(|x| *x as char).collect(); + if m.as_str() != SWAP_MAGIC1 && m.as_str() != SWAP_MAGIC2 { + let msg = format!("{device} is not a swap file or partition"); + println!("Magic String: {m}"); + return Err(io::Error::new(io::ErrorKind::Other, msg).into()); + } + fd.seek(SeekFrom::Start(SWAP_LABEL_OFFSET))?; + if let Some(label) = matches.get_one::("label") { + if label.len() + 1 > SWAP_LABEL_LENGTH as usize { + return Err(io::Error::new(io::ErrorKind::Other, "label is too long").into()); + } + let label = CString::new(label.as_str())?; + fd.write(label.as_bytes())?; + } else { + let mut label = [0; SWAP_LABEL_LENGTH as usize]; + fd.read_exact(&mut label)?; + let label: String = label + .iter() + .map(|x| *x as char) + .take_while(|x| *x != '\0') + .collect(); + println!("label: {label}"); + } + Ok(()) + } + + fn path(&self) -> Option { + Some(shitbox::Path::UsrSbin) + } +} diff --git a/utilbox/commands/swapoff/mod.rs b/utilbox/commands/swapoff/mod.rs index e69de29..8b13789 100644 --- a/utilbox/commands/swapoff/mod.rs +++ b/utilbox/commands/swapoff/mod.rs @@ -0,0 +1 @@ + diff --git a/utilbox/commands/swapon/mod.rs b/utilbox/commands/swapon/mod.rs index e69de29..8b13789 100644 --- a/utilbox/commands/swapon/mod.rs +++ b/utilbox/commands/swapon/mod.rs @@ -0,0 +1 @@ +