From af51880a81ae558750f86cf6f4cc261247e2816e Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 15 Aug 2025 17:44:30 -0700 Subject: [PATCH] Introduce CmdArgs for argument parsing in Rust --- native/src/base/cstr.rs | 7 +++++ native/src/base/misc.rs | 60 +++++++++++++++++++++++++++----------- native/src/boot/cli.rs | 6 ++-- native/src/sepolicy/cli.rs | 7 +++-- 4 files changed, 57 insertions(+), 23 deletions(-) diff --git a/native/src/base/cstr.rs b/native/src/base/cstr.rs index 72d74641e..06ac6fb2e 100644 --- a/native/src/base/cstr.rs +++ b/native/src/base/cstr.rs @@ -300,6 +300,13 @@ impl Utf8CStr { } } + pub unsafe fn from_raw_parts<'a>(ptr: *const c_char, len: usize) -> &'a Utf8CStr { + unsafe { + let bytes = slice::from_raw_parts(ptr.cast(), len); + Self::from_bytes_unchecked(bytes) + } + } + #[inline(always)] pub fn as_bytes_with_nul(&self) -> &[u8] { &self.0 diff --git a/native/src/base/misc.rs b/native/src/base/misc.rs index c72ea5615..07877e380 100644 --- a/native/src/base/misc.rs +++ b/native/src/base/misc.rs @@ -1,13 +1,16 @@ -use crate::{StrErr, Utf8CStr, ffi}; +use crate::{Utf8CStr, cstr, ffi}; use argh::EarlyExit; use libc::c_char; -use std::fmt::Arguments; -use std::io::Write; -use std::mem::ManuallyDrop; -use std::process::exit; -use std::sync::Arc; -use std::sync::atomic::{AtomicPtr, Ordering}; -use std::{fmt, slice, str}; +use std::{ + fmt, + fmt::Arguments, + io::Write, + mem::ManuallyDrop, + process::exit, + slice, str, + sync::Arc, + sync::atomic::{AtomicPtr, Ordering}, +}; pub fn errno() -> &'static mut i32 { unsafe { &mut *libc::__errno() } @@ -76,15 +79,6 @@ impl + ?Sized> MutBytesExt for T { } } -// SAFETY: libc guarantees argc and argv are properly setup and are static -#[allow(clippy::not_unsafe_ptr_arg_deref)] -pub fn map_args(argc: i32, argv: *const *const c_char) -> Result, StrErr> { - unsafe { slice::from_raw_parts(argv, argc as usize) } - .iter() - .map(|s| unsafe { Utf8CStr::from_ptr(*s) }.map(|s| s.as_str())) - .collect() -} - pub trait EarlyExitExt { fn on_early_exit(self, print_help_msg: F) -> T; } @@ -227,3 +221,35 @@ impl Chunker { chunk } } + +pub struct CmdArgs(pub Vec<&'static str>); + +impl CmdArgs { + #[allow(clippy::not_unsafe_ptr_arg_deref)] + pub fn new(argc: i32, argv: *const *const c_char) -> CmdArgs { + CmdArgs( + // SAFETY: libc guarantees argc and argv are properly setup and are static + unsafe { slice::from_raw_parts(argv, argc as usize) } + .iter() + .map(|s| unsafe { Utf8CStr::from_ptr(*s) }) + .map(|r| r.unwrap_or(cstr!(""))) + .map(Utf8CStr::as_str) + .collect(), + ) + } + + pub fn as_slice(&self) -> &[&'static str] { + self.0.as_slice() + } + + pub fn iter(&self) -> slice::Iter<'_, &'static str> { + self.0.iter() + } + + pub fn cstr_iter(&self) -> impl Iterator { + // SAFETY: libc guarantees null terminated strings + self.0 + .iter() + .map(|s| unsafe { Utf8CStr::from_raw_parts(s.as_ptr().cast(), s.len() + 1) }) + } +} diff --git a/native/src/boot/cli.rs b/native/src/boot/cli.rs index fb58c0663..dd6864d31 100644 --- a/native/src/boot/cli.rs +++ b/native/src/boot/cli.rs @@ -7,8 +7,8 @@ use crate::payload::extract_boot_from_payload; use crate::sign::sha1_hash; use argh::FromArgs; use base::{ - EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr, cmdline_logging, cstr, - libc::umask, log_err, map_args, + CmdArgs, EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr, cmdline_logging, cstr, + libc::umask, log_err, }; use std::ffi::c_char; use std::str::FromStr; @@ -264,7 +264,7 @@ pub extern "C" fn main(argc: i32, argv: *const *const c_char, _envp: *const *con cmdline_logging(); unsafe { umask(0) }; let res: LoggedResult<()> = try { - let mut cmds = map_args(argc, argv)?; + let mut cmds = CmdArgs::new(argc, argv).0; if argc < 2 { print_usage(cmds.first().unwrap_or(&"magiskboot")); return 1; diff --git a/native/src/sepolicy/cli.rs b/native/src/sepolicy/cli.rs index 85240359a..bf82b9cf2 100644 --- a/native/src/sepolicy/cli.rs +++ b/native/src/sepolicy/cli.rs @@ -2,8 +2,8 @@ use crate::ffi::SePolicy; use crate::statement::format_statement_help; use argh::FromArgs; use base::{ - EarlyExitExt, FmtAdaptor, LoggedResult, Utf8CStr, cmdline_logging, cstr, libc::umask, log_err, - map_args, + CmdArgs, EarlyExitExt, FmtAdaptor, LoggedResult, Utf8CStr, cmdline_logging, cstr, libc::umask, + log_err, }; use std::ffi::c_char; use std::io::stderr; @@ -79,7 +79,8 @@ pub unsafe extern "C" fn main( } let res: LoggedResult<()> = try { - let cmds = map_args(argc, argv)?; + let cmds = CmdArgs::new(argc, argv); + let cmds = cmds.as_slice(); if argc < 2 { print_usage(cmds.first().unwrap_or(&"magiskpolicy")); return 1;