hadris-cpio 2.1.0

A rust implementation of the CPIO archive format (newc/SVR4).
Documentation
use core::fmt;

/// Errors that can occur during CPIO archive operations.
#[derive(Debug)]
pub enum Error {
    /// An I/O error occurred while reading or writing the archive.
    Io(hadris_io::Error),
    /// The header magic bytes are not `070701` or `070702`.
    InvalidMagic {
        /// Six bytes read from the archive magic field.
        found: [u8; 6],
    },
    /// A header field contains non-hexadecimal characters.
    InvalidHexField {
        /// Name of the malformed header field.
        field: &'static str,
    },
    /// The entry filename is empty or could not be read.
    InvalidFilename,
    /// A decoded header violates a newc field invariant.
    InvalidHeader {
        /// Description of the violated invariant.
        reason: &'static str,
    },
    /// The archive ended without a `TRAILER!!!` sentinel.
    MissingTrailer,
    /// The CRC checksum in a `070702` entry does not match the computed value.
    ChecksumMismatch {
        /// Checksum stored in the entry header.
        expected: u32,
        /// Checksum computed from the entry contents.
        computed: u32,
    },
    /// A caller-provided data buffer does not match the entry's file size.
    BufferSizeMismatch {
        /// The entry's file size in bytes.
        expected: usize,
        /// The length of the provided buffer.
        actual: usize,
    },
    /// A hard link references a target path that was not seen earlier in the archive.
    #[cfg(feature = "write")]
    UnresolvedHardLink {
        /// Inode number assigned to the unresolved target.
        ino: u32,
    },
    /// The filename exceeds the maximum length representable in a newc header.
    #[cfg(feature = "write")]
    FilenameTooLong,
    /// The file data exceeds the maximum size representable in a newc header (4 GiB).
    #[cfg(feature = "write")]
    FileTooLarge,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Io(e) => write!(f, "I/O error: {e:?}"),
            Self::InvalidMagic { found } => {
                write!(
                    f,
                    "invalid CPIO magic: expected 070701 or 070702, found {:?}",
                    core::str::from_utf8(found).unwrap_or("<invalid>")
                )
            }
            Self::InvalidHexField { field } => {
                write!(f, "invalid hex field: {field}")
            }
            Self::InvalidFilename => write!(f, "invalid filename"),
            Self::InvalidHeader { reason } => write!(f, "invalid CPIO header: {reason}"),
            Self::MissingTrailer => write!(f, "missing TRAILER!!! sentinel"),
            Self::ChecksumMismatch { expected, computed } => {
                write!(
                    f,
                    "checksum mismatch: expected {expected:#010x}, computed {computed:#010x}"
                )
            }
            Self::BufferSizeMismatch { expected, actual } => {
                write!(
                    f,
                    "buffer size mismatch: entry is {expected} bytes, buffer is {actual} bytes"
                )
            }
            #[cfg(feature = "write")]
            Self::UnresolvedHardLink { ino } => {
                write!(f, "unresolved hard link: inode {ino}")
            }
            #[cfg(feature = "write")]
            Self::FilenameTooLong => write!(f, "filename too long"),
            #[cfg(feature = "write")]
            Self::FileTooLarge => write!(f, "file too large"),
        }
    }
}

#[cfg(feature = "std")]
impl std::error::Error for Error {}

impl<E: hadris_io::IoError> From<hadris_io::Error<E>> for Error {
    fn from(e: hadris_io::Error<E>) -> Self {
        Self::Io(e.erase())
    }
}

/// Convenience type alias for `core::result::Result<T, Error>`.
pub type Result<T> = core::result::Result<T, Error>;