Sign in

    moon-cpio

    Safe cpio parsing, streaming, deterministic writing, and CLI extraction.

    archive
    binary
    cpio
    cli
    initramfs
    odc
    parser
    streaming
    unix
    writer
    Download zip
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    yesterday
    Downloads
    4

    Dependencies

    #moon-cpio

    Safe, pure-MoonBit reading and writing for newc, odc, and binary cpio archives.

    The current implementation provides read-only parsers for 070701 newc, 070702 newc-crc, portable ASCII 070707 odc, and both old and new binary cpio archives used by initramfs images, Unix archive tooling, and RPM payloads. A zero-copy streaming API processes entries one at a time for bounded reader overhead. A deterministic writer generates reproducible newc archives. The native CLI lists, validates, and safely extracts archives.

    #Status

    Supported:

    • Parse multiple newc entries in one archive.
    • Parse both 070701 and 070702 magic values.
    • Parse portable ASCII 070707 odc archives without implicit padding.
    • Parse old binary cpio and new binary cpio with explicit little- or big-endian word order.
    • Stream entries from every supported format without copying file data.
    • Write deterministic 070701 and 070702 newc archives with fixed metadata ordering, sequential inodes, and canonical padding.
    • List, validate, and safely extract archives with the CLI.
    • Validate the 070702 byte-sum check field. Despite its name, the format uses an unsigned 32-bit byte sum rather than a cyclic redundancy check.
    • Preserve exact entry name bytes and expose a validated UTF-8 name.
    • Decode all 13 newc header fields.
    • Validate 4-byte alignment, file sizes, hexadecimal fields, names, and the TRAILER!!! record.
    • Return byte offsets for malformed input instead of panicking.

    Not implemented yet:

    • Writing odc or binary cpio archives.
    • Symlink, hardlink, and special-file extraction.

    #Install

    moon add oilleelssq-wq/moon-cpio

    #Use

    ///|
    import {
    "oilleelssq-wq/moon-cpio" @cpio,
    }

    ///|
    fn inspect_archive(bytes : Bytes) -> Unit raise {
    let archive = @cpio.parse_newc(bytes).unwrap()
    for entry in archive.entries {
    println("\{entry.name}: \{entry.data.length()} bytes")
    }
    }

    parse_newc returns Result[Archive, CpioError]. Errors carry byte offsets, and entry.name_bytes preserves the original name even when entry.name is used as the validated UTF-8 view.

    parse_odc returns Result[OdcArchive, CpioError] with the same name and offset guarantees.

    parse_binary_old and parse_binary_new return BinaryArchive values. The binary APIs require an explicit byte order because the old and new layouts share the same magic and cannot be reliably distinguished from bytes alone.

    stream_entries accepts a CpioFormat and an entry callback. Entry names and data are exposed as BytesView values over the original input buffer.

    write_newc accepts NewcWriteEntry values and an optional NewcFormat. Identical inputs always produce identical archive bytes.

    #CLI

    moon run cmd/main -- list --format auto archive.cpio moon run cmd/main -- validate --format auto archive.cpio moon run cmd/main -- extract --format auto archive.cpio ./output

    extract requires a new destination directory. It preflights every pathname and rejects absolute paths, drive prefixes, backslashes, empty components, . and .. before writing anything. Only directories and regular files are extracted.

    #Development

    moon check moon test moon fmt

    See PROPOSAL.md for the contest scope, delivery boundaries, and acceptance mapping.

    See CHANGELOG.md, SECURITY.md, and RELEASE_CHECKLIST.md for release and security information.

    See CHANGELOG.md, SECURITY.md, and RELEASE_CHECKLIST.md for release and security information.

    The test suite covers all readers, streaming, deterministic writing, round trips, CLI parsing and extraction, path traversal rejection, UTF-8 names, checksums, invalid numeric fields, padding, truncated data, invalid trailer payloads, and missing trailers.

    #Roadmap

    1. odc and binary writers, hardlinks, and symlink policy.
    2. Release audit and mooncakes.io publication.

    #License

    Apache-2.0.

    Archive

    pub(all) struct Archive {
    format : NewcFormat
    entries : Array[Entry]
    } derive(Eq,
    Debug
    )

    A successfully parsed archive.

    ArchivePathError

    pub(all) enum ArchivePathError {
    EmptyPath
    AbsolutePath
    Backslash
    Colon
    EmptyComponent
    CurrentComponent
    ParentComponent
    } derive(Eq,
    Debug
    )

    Reasons an archive path cannot be used for safe extraction.

    BinaryArchive

    pub(all) struct BinaryArchive {
    variant : BinaryVariant
    byte_order : BinaryByteOrder
    entries : Array[BinaryEntry]
    } derive(Eq,
    Debug
    )

    A successfully parsed binary cpio archive.

    BinaryByteOrder

    pub(all) enum BinaryByteOrder {
    Little
    Big
    } derive(Eq,
    Debug
    )

    16-bit word byte order used by new binary cpio.

    BinaryEntry

    pub(all) struct BinaryEntry {
    name : String
    name_bytes : Bytes
    header : BinaryHeader
    data : Bytes
    } derive(Eq,
    Debug
    )

    One binary cpio entry.

    BinaryHeader

    pub(all) struct BinaryHeader {
    variant : BinaryVariant
    dev : UInt64
    ino : UInt64
    mode : UInt64
    uid : UInt64
    gid : UInt64
    nlink : UInt64
    rdev : UInt64
    mtime : UInt64
    file_size : UInt64
    name_size : UInt64
    } derive(Eq,
    Debug
    )

    Parsed binary cpio header fields.

    BinaryVariant

    pub(all) enum BinaryVariant {
    Old
    New
    } derive(Eq,
    Debug
    )

    Binary cpio layout. The two variants differ in file type semantics, while old and new little-endian headers share the same physical encoding.

    CpioError

    pub(all) enum CpioError {
    TruncatedHeader(Int)
    UnsupportedMagic(Int)
    InvalidHex(Int)
    InvalidOctal(Int)
    InvalidNameSize(Int)
    TruncatedName(Int)
    MissingNameTerminator(Int)
    EmbeddedNul(Int)
    InvalidUtf8Name(Int)
    TruncatedData(Int)
    ChecksumMismatch(Int, UInt64, UInt64)
    MixedFormats(Int)
    InvalidTrailer(Int)
    } derive(Eq,
    Debug
    )

    Errors reported while parsing a newc cpio archive.

    Numeric payloads are byte offsets into the original input.

    CpioFormat

    pub(all) enum CpioFormat {
    Newc
    Odc
    BinaryOld
    BinaryNew(BinaryByteOrder)
    } derive(Eq,
    Debug
    )

    Input format selector for the streaming reader.

    Entry

    pub(all) struct Entry {
    name : String
    name_bytes : Bytes
    header : Header
    data : Bytes
    } derive(Eq,
    Debug
    )

    One archive entry. name_bytes preserves the exact bytes from the archive, while name is the validated UTF-8 representation.
    pub(all) struct Header {
    format : NewcFormat
    ino : UInt64
    mode : UInt64
    uid : UInt64
    gid : UInt64
    nlink : UInt64
    mtime : UInt64
    file_size : UInt64
    dev_major : UInt64
    dev_minor : UInt64
    rdev_major : UInt64
    rdev_minor : UInt64
    name_size : UInt64
    check : UInt64
    } derive(Eq,
    Debug
    )

    Parsed newc header fields.

    NewcFormat

    pub(all) enum NewcFormat {
    Newc
    NewcCrc
    } derive(Eq,
    Debug
    )

    newc archive variants.

    NewcWriteEntry

    pub(all) struct NewcWriteEntry {
    name : String
    data : Bytes
    mode : UInt64
    uid : UInt64
    gid : UInt64
    mtime : UInt64
    } derive(Eq,
    Debug
    )

    Minimal deterministic newc entry input.

    NewcWriteEntry::new

    fn NewcWriteEntry::new(name : String, data : Bytes, mode? : UInt64, uid? : UInt64, gid? : UInt64, mtime? : UInt64) -> NewcWriteEntry

    OdcArchive

    pub(all) struct OdcArchive {
    entries : Array[OdcEntry]
    } derive(Eq,
    Debug
    )

    A successfully parsed odc archive.

    OdcEntry

    pub(all) struct OdcEntry {
    name : String
    name_bytes : Bytes
    header : OdcHeader
    data : Bytes
    } derive(Eq,
    Debug
    )

    One odc archive entry.

    OdcHeader

    pub(all) struct OdcHeader {
    dev : UInt64
    ino : UInt64
    mode : UInt64
    uid : UInt64
    gid : UInt64
    nlink : UInt64
    rdev : UInt64
    mtime : UInt64
    file_size : UInt64
    name_size : UInt64
    } derive(Eq,
    Debug
    )

    Parsed odc header fields.

    StreamEntry

    pub(all) struct StreamEntry {
    name : String
    name_bytes : BytesView
    header : StreamHeader
    data : BytesView
    } derive(Eq,
    Debug
    )

    One zero-copy entry delivered by stream_entries.

    name_bytes and data are views into the input buffer and remain valid only while that buffer is alive.

    StreamHeader

    pub(all) enum StreamHeader {
    Newc(Header)
    Odc(OdcHeader)
    Binary(BinaryHeader)
    } derive(Eq,
    Debug
    )

    Format-specific metadata delivered for each streamed entry.

    WriteError

    pub(all) enum WriteError {
    EmptyName
    EmbeddedNul(Int)
    FieldOverflow(Int)
    } derive(Eq,
    Debug
    )

    Errors reported while writing deterministic newc archives.

    FieldOverflow carries the newc field index: ino(0), mode(1), uid(2), gid(3), mtime(4), file_size(5), or name_size(6).

    BINARY_HEADER_SIZE

    let BINARY_HEADER_SIZE : Int

    NEWC_HEADER_SIZE

    let NEWC_HEADER_SIZE : Int

    ODC_HEADER_SIZE

    let ODC_HEADER_SIZE : Int

    archive_path_to_fs

    fn archive_path_to_fs(root : String, path : StringView) -> Result[String, ArchivePathError]

    Converts a validated archive pathname to a filesystem path under root.

    newc_checksum

    fn newc_checksum(data : BytesView) -> UInt64

    Computes the byte-sum value stored in a 070702 newc check field.

    Despite the format's "CRC" name, the field is not a cyclic redundancy check. It is the low 32 bits of the unsigned sum of all data bytes.

    parse_binary_new

    fn parse_binary_new(input : Bytes, byte_order : BinaryByteOrder) -> Result[BinaryArchive, CpioError]

    Parses a new binary cpio archive with caller-specified word byte order.

    Auto-detection between old and new layouts is intentionally not attempted: the formats use the same magic and can share identical little-endian bytes.

    parse_binary_old

    fn parse_binary_old(input : Bytes) -> Result[BinaryArchive, CpioError]

    Parses a PWB old binary cpio archive with PDP little-endian words.

    parse_newc

    fn parse_newc(input : Bytes) -> Result[Archive, CpioError]

    Parses a SVR4 newc cpio archive (070701).

    The parser is read-only and does not extract to the filesystem. Every length and field is checked before conversion to an index, so malformed archives return Err instead of panicking.

    parse_odc

    fn parse_odc(input : Bytes) -> Result[OdcArchive, CpioError]

    Parses a portable ASCII odc cpio archive (070707).

    The parser is read-only. odc has no padding between the name, data, or following header, so every entry starts immediately after the prior data.

    stream_entries

    fn stream_entries(input : Bytes, format : CpioFormat, on_entry : (StreamEntry) -> Unit) -> Result[Unit, CpioError]

    Streams entries one at a time without constructing an archive array or copying entry data.

    The callback executes synchronously for each entry. Binary old/new layout ambiguity is resolved by the explicit CpioFormat.

    validate_archive_path

    fn validate_archive_path(path : StringView) -> Result[Unit, ArchivePathError]

    Validates a cpio pathname for safe extraction beneath a target directory.

    Archive paths must be relative, use / as the separator, and contain no . or .. components.

    write_newc

    fn write_newc(entries : ArrayView[NewcWriteEntry], format? : NewcFormat) -> Result[Bytes, WriteError]

    Writes a deterministic 070701 or 070702 newc archive.

    Inodes are assigned sequentially from 1, device fields are zero, and the caller supplies mode, uid, gid, and mtime. No wall clock or random value is read, so identical inputs always produce identical bytes.

    Powered by MoonBit

    Site sourceReport issuePackagesBuild queueSkillsStatistics

    © 2026 mooncakes.io