Skip to content

cpio.py

ofrak.core.cpio

CpioArchiveType (Enum)

CPIO has several unrelated, incompatible variants. They're described in the man page: https://linux.die.net/man/1/cpio

CpioFilesystem (GenericBinary, FilesystemRoot) dataclass

Filesystem stored in a CPIO archive.

CpioFilesystemAnalyzer (Analyzer)

Extracts CPIO archive metadata including archive format type (newc, crc, old binary, etc.), number of files, total archive size, and format-specific parameters. Use before modifying CPIO archives to preserve the correct format, ensure compatibility with target systems (some bootloaders only support specific formats), or understand archive characteristics. Essential for maintaining format compatibility during repacking.

analyze(self, resource, config=None) async

Analyze a resource for to extract specific ResourceAttributes.

Users should not call this method directly; rather, they should run Resource.run or Resource.analyze.

Parameters:

Name Type Description Default
resource Resource

The resource that is being analyzed

required
config

Optional config for analyzing. If an implementation provides a default, this default will always be used when config would otherwise be None. Note that a copy of the default config will be passed, so the default config values cannot be modified persistently by a component run.

None

Returns:

Type Description
CpioFilesystem

The analysis results

Source code in ofrak/core/cpio.py
async def analyze(self, resource: Resource, config=None) -> CpioFilesystem:
    _magic = await resource.analyze(Magic)
    magic_description = _magic.descriptor
    if magic_description.startswith("ASCII cpio archive (SVR4 with no CRC)"):
        archive_type = CpioArchiveType.NEW_ASCII
    elif magic_description.startswith("ASCII cpio archive (pre-SVR4 or odc)"):
        archive_type = CpioArchiveType.OLD_ASCII
    elif magic_description.startswith("ASCII cpio archive (SVR4 with CRC)"):
        archive_type = CpioArchiveType.CRC_ASCII
    elif magic_description.startswith("cpio archive"):
        archive_type = CpioArchiveType.BINARY
    else:
        raise NotImplementedError(
            f"Please add support for CPIO archive type {magic_description}"
        )

    return CpioFilesystem(archive_type)

CpioPacker (Packer)

Packages files into a CPIO archive format suitable for use as Linux initial ramdisk (initramfs) or embedded firmware packaging. Use after modifying extracted CPIO contents to recreate initramfs images or firmware archives. Critical for modifying Linux boot process and embedded system initialization.

pack(self, resource, config=None) async

Pack the given resource.

Users should not call this method directly; rather, they should run Resource.run or Resource.pack.

Parameters:

Name Type Description Default
resource Resource required
config

Optional config for packing. If an implementation provides a default, this default will always be used when config would otherwise be None. Note that a copy of the default config will be passed, so the default config values cannot be modified persistently by a component run.

None
Source code in ofrak/core/cpio.py
async def pack(self, resource: Resource, config=None):
    cpio_v: CpioFilesystem = await resource.view_as(CpioFilesystem)
    temp_flush_dir = await cpio_v.flush_to_disk()
    cpio_format = cpio_v.archive_type.value
    list_files_cmd = [
        "find",
        "-print",
    ]
    list_files_proc = await asyncio.create_subprocess_exec(
        *list_files_cmd,
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE,
        cwd=temp_flush_dir,
    )
    list_files_list, stderr = await list_files_proc.communicate()
    if list_files_proc.returncode:
        raise CalledProcessError(returncode=list_files_proc.returncode, cmd=list_files_cmd)

    cpio_pack_cmd = [
        "cpio",
        "-o",
        f"--format={cpio_format}",
    ]
    cpio_pack_proc = await asyncio.create_subprocess_exec(
        *cpio_pack_cmd,
        stdin=asyncio.subprocess.PIPE,
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE,
        cwd=temp_flush_dir,
    )
    cpio_pack_output, stderr = await cpio_pack_proc.communicate(input=list_files_list)
    if cpio_pack_proc.returncode:
        raise CalledProcessError(returncode=cpio_pack_proc.returncode, cmd=cpio_pack_cmd)
    # Passing in the original range effectively replaces the original data with the new data
    resource.queue_patch(Range(0, await resource.get_data_length()), cpio_pack_output)

CpioUnpacker (Unpacker)

Extracts files and directories from CPIO archive formats, which are commonly used in Linux initial ramdisk (initramfs) images and some embedded system firmware packages. CPIO archives can contain regular files, directories, symbolic links, and special device files with preserved permissions and ownership. Use when analyzing Linux boot processes (initramfs/initrd), embedded firmware that uses CPIO for packaging, or any CPIO archive. Essential for examining and modifying initramfs filesystems.

unpack(self, resource, config=None) async

Unpack the given resource.

Users should not call this method directly; rather, they should run Resource.run or Resource.unpack.

Parameters:

Name Type Description Default
resource Resource

The resource that is being unpacked

required
config

Optional config for unpacking. If an implementation provides a default, this default will always be used when config would otherwise be None. Note that a copy of the default config will be passed, so the default config values cannot be modified persistently by a component run.

None
Source code in ofrak/core/cpio.py
async def unpack(self, resource: Resource, config=None):
    cpio_v = await resource.view_as(CpioFilesystem)
    resource_data = await cpio_v.resource.get_data()
    with tempfile.TemporaryDirectory() as temp_flush_dir:
        cmd = [
            "cpio",
            "-id",
        ]
        proc = await asyncio.create_subprocess_exec(
            *cmd,
            stdin=asyncio.subprocess.PIPE,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE,
            cwd=temp_flush_dir,
        )
        await proc.communicate(input=resource_data)
        if proc.returncode:
            raise CalledProcessError(returncode=proc.returncode, cmd=cmd)
        await cpio_v.initialize_from_disk(temp_flush_dir)