diff options
Diffstat (limited to 'src/lib.rs')
-rw-r--r-- | src/lib.rs | 275 |
1 files changed, 172 insertions, 103 deletions
@@ -20,7 +20,8 @@ //! //! [squashfs-tools-ng]: https://github.com/AgentD/squashfs-tools-ng/ -#[macro_use] extern crate lazy_static; +#[macro_use] +extern crate lazy_static; extern crate libc; extern crate memmap; extern crate num_derive; @@ -29,21 +30,21 @@ extern crate owning_ref; extern crate walkdir; extern crate xattr; -use std::mem::MaybeUninit; +use num_derive::FromPrimitive; +use num_traits::FromPrimitive; use std::ffi::{OsStr, OsString}; +use std::mem::MaybeUninit; use std::path::PathBuf; use std::ptr; -use num_derive::FromPrimitive; -use num_traits::FromPrimitive; use thiserror::Error; #[cfg(not(feature = "hermetic"))] mod bindings { - #![allow(non_camel_case_types)] - #![allow(non_snake_case)] - #![allow(non_upper_case_globals)] - #![allow(dead_code)] - include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + #![allow(non_camel_case_types)] + #![allow(non_snake_case)] + #![allow(non_upper_case_globals)] + #![allow(dead_code)] + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); } #[cfg(feature = "hermetic")] mod bindings; @@ -63,23 +64,40 @@ type BoxedError = Box<dyn std::error::Error + std::marker::Send + std::marker::S #[derive(Error, Debug, FromPrimitive)] #[repr(i32)] pub enum LibError { - #[error("Failed to allocate memory")] Alloc = SQFS_ERROR_SQFS_ERROR_ALLOC, - #[error("Generic I/O failure")] Io = SQFS_ERROR_SQFS_ERROR_IO, - #[error("Compressor failed to extract data")] Compressor = SQFS_ERROR_SQFS_ERROR_COMPRESSOR, - #[error("Internal error")] Internal = SQFS_ERROR_SQFS_ERROR_INTERNAL, - #[error("Archive file appears to be corrupted")] Corrupted = SQFS_ERROR_SQFS_ERROR_CORRUPTED, - #[error("Unsupported feature used")] Unsupported = SQFS_ERROR_SQFS_ERROR_UNSUPPORTED, - #[error("Archive would overflow memory")] Overflow = SQFS_ERROR_SQFS_ERROR_OVERFLOW, - #[error("Out-of-bounds access attempted")] OutOfBounds = SQFS_ERROR_SQFS_ERROR_OUT_OF_BOUNDS, - #[error("Superblock magic number incorrect")] SuperMagic = SQFS_ERROR_SFQS_ERROR_SUPER_MAGIC, - #[error("Unsupported archive version")] SuperVersion = SQFS_ERROR_SFQS_ERROR_SUPER_VERSION, - #[error("Archive block size is invalid")] SuperBlockSize = SQFS_ERROR_SQFS_ERROR_SUPER_BLOCK_SIZE, - #[error("Not a directory")] NotDir = SQFS_ERROR_SQFS_ERROR_NOT_DIR, - #[error("Path does not exist")] NoEntry = SQFS_ERROR_SQFS_ERROR_NO_ENTRY, - #[error("Hard link loop detected")] LinkLoop = SQFS_ERROR_SQFS_ERROR_LINK_LOOP, - #[error("Not a regular file")] NotFile = SQFS_ERROR_SQFS_ERROR_NOT_FILE, - #[error("Invalid argument passed")] ArgInvalid = SQFS_ERROR_SQFS_ERROR_ARG_INVALID, - #[error("Library operations performed in incorrect order")] Sequence = SQFS_ERROR_SQFS_ERROR_SEQUENCE, + #[error("Failed to allocate memory")] + Alloc = SQFS_ERROR_SQFS_ERROR_ALLOC, + #[error("Generic I/O failure")] + Io = SQFS_ERROR_SQFS_ERROR_IO, + #[error("Compressor failed to extract data")] + Compressor = SQFS_ERROR_SQFS_ERROR_COMPRESSOR, + #[error("Internal error")] + Internal = SQFS_ERROR_SQFS_ERROR_INTERNAL, + #[error("Archive file appears to be corrupted")] + Corrupted = SQFS_ERROR_SQFS_ERROR_CORRUPTED, + #[error("Unsupported feature used")] + Unsupported = SQFS_ERROR_SQFS_ERROR_UNSUPPORTED, + #[error("Archive would overflow memory")] + Overflow = SQFS_ERROR_SQFS_ERROR_OVERFLOW, + #[error("Out-of-bounds access attempted")] + OutOfBounds = SQFS_ERROR_SQFS_ERROR_OUT_OF_BOUNDS, + #[error("Superblock magic number incorrect")] + SuperMagic = SQFS_ERROR_SFQS_ERROR_SUPER_MAGIC, + #[error("Unsupported archive version")] + SuperVersion = SQFS_ERROR_SFQS_ERROR_SUPER_VERSION, + #[error("Archive block size is invalid")] + SuperBlockSize = SQFS_ERROR_SQFS_ERROR_SUPER_BLOCK_SIZE, + #[error("Not a directory")] + NotDir = SQFS_ERROR_SQFS_ERROR_NOT_DIR, + #[error("Path does not exist")] + NoEntry = SQFS_ERROR_SQFS_ERROR_NO_ENTRY, + #[error("Hard link loop detected")] + LinkLoop = SQFS_ERROR_SQFS_ERROR_LINK_LOOP, + #[error("Not a regular file")] + NotFile = SQFS_ERROR_SQFS_ERROR_NOT_FILE, + #[error("Invalid argument passed")] + ArgInvalid = SQFS_ERROR_SQFS_ERROR_ARG_INVALID, + #[error("Library operations performed in incorrect order")] + Sequence = SQFS_ERROR_SQFS_ERROR_SEQUENCE, } /// Errors encountered while reading or writing an archive. @@ -88,67 +106,98 @@ pub enum LibError { /// operation. #[derive(Error, Debug)] pub enum SquashfsError { - #[error("Input contains an invalid null character")] NullInput(#[from] std::ffi::NulError), - #[error("Encoded string is not valid UTF-8")] Utf8(#[from] std::string::FromUtf8Error), - #[error("OS string is not valid UTF-8")] OsUtf8(OsString), - #[error("{0}: {1}")] LibraryError(String, LibError), - #[error("{0}: Unknown error {1} in SquashFS library")] UnknownLibraryError(String, i32), - #[error("{0}: Squashfs library did not return expected value")] LibraryReturnError(String), - #[error("{0}")] LibraryNullError(String), - #[error("Symbolic link chain exceeds {0} elements")] LinkChain(i32), // Can I use a const in the formatting string? - #[error("Symbolic link loop detected containing {0}")] LinkLoop(PathBuf), - #[error("Dangling symbolic link from {0} to {1}")] DanglingLink(PathBuf, PathBuf), - #[error("{0} is type {1}, not {2}")] WrongType(String, String, String), - #[error("Tried to copy an object that can't be copied")] Copy, - #[error("Tried to get parent of a node with an unknown path")] NoPath, - #[error("Inode index {0} is not within limits 1..{1}")] Range(u64, u64), - #[error("Couldn't read file: {0}")] Read(#[from] std::io::Error), - #[error("The filesystem does not support the feature: {0}")] Unsupported(String), - #[error("Memory mapping failed: {0}")] Mmap(std::io::Error), - #[error("Couldn't get the current system time: {0}")] Time(#[from] std::time::SystemTimeError), - #[error("Refusing to create empty archive")] Empty, - #[error("Tried to write parent directory before child node {0}")] WriteOrder(u32), - #[error("Tried to write unknown or unsupported file type")] WriteType(std::fs::FileType), - #[error("Callback returned an error")] WrappedError(BoxedError), - #[error("Failed to retrieve xattrs for {0}: {1}")] Xattr(PathBuf, std::io::Error), - #[error("Tried to add files to a writer that was already finished")] Finished, - #[error("Internal error: {0}")] Internal(String), + #[error("Input contains an invalid null character")] + NullInput(#[from] std::ffi::NulError), + #[error("Encoded string is not valid UTF-8")] + Utf8(#[from] std::string::FromUtf8Error), + #[error("OS string is not valid UTF-8")] + OsUtf8(OsString), + #[error("{0}: {1}")] + LibraryError(String, LibError), + #[error("{0}: Unknown error {1} in SquashFS library")] + UnknownLibraryError(String, i32), + #[error("{0}: Squashfs library did not return expected value")] + LibraryReturnError(String), + #[error("{0}")] + LibraryNullError(String), + #[error("Symbolic link chain exceeds {0} elements")] + LinkChain(i32), // Can I use a const in the formatting string? + #[error("Symbolic link loop detected containing {0}")] + LinkLoop(PathBuf), + #[error("Dangling symbolic link from {0} to {1}")] + DanglingLink(PathBuf, PathBuf), + #[error("{0} is type {1}, not {2}")] + WrongType(String, String, String), + #[error("Tried to copy an object that can't be copied")] + Copy, + #[error("Tried to get parent of a node with an unknown path")] + NoPath, + #[error("Inode index {0} is not within limits 1..{1}")] + Range(u64, u64), + #[error("Couldn't read file: {0}")] + Read(#[from] std::io::Error), + #[error("The filesystem does not support the feature: {0}")] + Unsupported(String), + #[error("Memory mapping failed: {0}")] + Mmap(std::io::Error), + #[error("Couldn't get the current system time: {0}")] + Time(#[from] std::time::SystemTimeError), + #[error("Refusing to create empty archive")] + Empty, + #[error("Tried to write parent directory before child node {0}")] + WriteOrder(u32), + #[error("Tried to write unknown or unsupported file type")] + WriteType(std::fs::FileType), + #[error("Callback returned an error")] + WrappedError(BoxedError), + #[error("Failed to retrieve xattrs for {0}: {1}")] + Xattr(PathBuf, std::io::Error), + #[error("Tried to add files to a writer that was already finished")] + Finished, + #[error("Internal error: {0}")] + Internal(String), } /// Result type returned by SquashFS library operations. pub type Result<T> = std::result::Result<T, SquashfsError>; fn sfs_check(code: i32, desc: &str) -> Result<i32> { - match code { - i if i >= 0 => Ok(i), - i => match FromPrimitive::from_i32(i) { - Some(e) => Err(SquashfsError::LibraryError(desc.to_string(), e)), - None => Err(SquashfsError::UnknownLibraryError(desc.to_string(), i)), - } - } + match code { + i if i >= 0 => Ok(i), + i => match FromPrimitive::from_i32(i) { + Some(e) => Err(SquashfsError::LibraryError(desc.to_string(), e)), + None => Err(SquashfsError::UnknownLibraryError(desc.to_string(), i)), + }, + } } fn sfs_destroy<T>(x: *mut T) { - unsafe { - let obj = x as *mut sqfs_object_t; - ((*obj).destroy.expect("SquashFS object did not provide a destroy callback"))(obj); - } + unsafe { + let obj = x as *mut sqfs_object_t; + ((*obj) + .destroy + .expect("SquashFS object did not provide a destroy callback"))(obj); + } } fn libc_free<T>(x: *mut T) { - unsafe { libc::free(x as *mut _ as *mut libc::c_void); } + unsafe { + libc::free(x as *mut _ as *mut libc::c_void); + } } fn rust_dealloc<T>(x: *mut T) { - unsafe { std::alloc::dealloc(x as *mut u8, std::alloc::Layout::new::<T>()) } + unsafe { std::alloc::dealloc(x as *mut u8, std::alloc::Layout::new::<T>()) } } fn unpack_meta_ref(meta_ref: u64) -> (u64, u64) { - (meta_ref >> 16 & 0xffffffff, meta_ref & 0xffff) + (meta_ref >> 16 & 0xffffffff, meta_ref & 0xffff) } fn os_to_string(s: &OsStr) -> Result<String> { - Ok(s.to_str().ok_or_else(|| SquashfsError::OsUtf8(s.to_os_string()))?.to_string()) + Ok(s.to_str() + .ok_or_else(|| SquashfsError::OsUtf8(s.to_os_string()))? + .to_string()) } const NO_XATTRS: u32 = 0xffffffff; @@ -158,65 +207,85 @@ const BLOCK_BUF_SIZE: usize = 4096; const PAD_TO: usize = 4096; struct ManagedPointer<T> { - ptr: *mut T, - destroy: fn(*mut T), + ptr: *mut T, + destroy: fn(*mut T), } impl<T> ManagedPointer<T> { - fn null(destroy: fn(*mut T)) -> Self { - Self { ptr: ptr::null_mut(), destroy: destroy } - } + fn null(destroy: fn(*mut T)) -> Self { + Self { + ptr: ptr::null_mut(), + destroy: destroy, + } + } + + fn new(ptr: *mut T, destroy: fn(*mut T)) -> Self { + Self { + ptr: ptr, + destroy: destroy, + } + } - fn new(ptr: *mut T, destroy: fn(*mut T)) -> Self { - Self { ptr: ptr, destroy: destroy } - } - - fn as_const(&self) -> *const T { - self.ptr as *const T - } + fn as_const(&self) -> *const T { + self.ptr as *const T + } } impl<T> std::ops::Deref for ManagedPointer<T> { - type Target = *mut T; + type Target = *mut T; - fn deref(&self) -> &Self::Target { - &self.ptr - } + fn deref(&self) -> &Self::Target { + &self.ptr + } } impl<T> std::ops::DerefMut for ManagedPointer<T> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.ptr - } + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.ptr + } } impl<T> Drop for ManagedPointer<T> { - fn drop(&mut self) { - (self.destroy)(**self) - } + fn drop(&mut self) { + (self.destroy)(**self) + } } impl<T> std::fmt::Debug for ManagedPointer<T> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "ManagedPointer({:?})", self.ptr) - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "ManagedPointer({:?})", self.ptr) + } } fn sfs_init<T>(init: &dyn Fn(*mut T) -> i32, err: &str) -> Result<T> { - let mut ret: MaybeUninit<T> = MaybeUninit::uninit(); - sfs_check(init(ret.as_mut_ptr()), err)?; - Ok(unsafe { ret.assume_init() }) + let mut ret: MaybeUninit<T> = MaybeUninit::uninit(); + sfs_check(init(ret.as_mut_ptr()), err)?; + Ok(unsafe { ret.assume_init() }) } -fn sfs_init_ptr<T>(init: &dyn Fn(*mut *mut T) -> i32, err: &str, destroy: fn(*mut T)) -> Result<ManagedPointer<T>> { - let mut ret: *mut T = ptr::null_mut(); - sfs_check(init(&mut ret), err)?; - if ret.is_null() { Err(SquashfsError::LibraryReturnError(err.to_string())) } - else { Ok(ManagedPointer::new(ret, destroy)) } +fn sfs_init_ptr<T>( + init: &dyn Fn(*mut *mut T) -> i32, + err: &str, + destroy: fn(*mut T), +) -> Result<ManagedPointer<T>> { + let mut ret: *mut T = ptr::null_mut(); + sfs_check(init(&mut ret), err)?; + if ret.is_null() { + Err(SquashfsError::LibraryReturnError(err.to_string())) + } else { + Ok(ManagedPointer::new(ret, destroy)) + } } -fn sfs_init_check_null<T>(init: &dyn Fn() -> *mut T, err: &str, destroy: fn(*mut T)) -> Result<ManagedPointer<T>> { - let ret = init(); - if ret.is_null() { Err(SquashfsError::LibraryNullError(err.to_string())) } - else { Ok(ManagedPointer::new(ret, destroy)) } +fn sfs_init_check_null<T>( + init: &dyn Fn() -> *mut T, + err: &str, + destroy: fn(*mut T), +) -> Result<ManagedPointer<T>> { + let ret = init(); + if ret.is_null() { + Err(SquashfsError::LibraryNullError(err.to_string())) + } else { + Ok(ManagedPointer::new(ret, destroy)) + } } |