indygreg / apple-platform-rs

Rust crates supporting Apple platform development
565 stars 38 forks source link

Extract PKG payload using apple-flat-package #138

Closed bonigarcia closed 5 months ago

bonigarcia commented 5 months ago

Hello. I need a pure Rust implementation to extract the content of a PKG file. As recommended in #90, the apple-flat-package crate in this repo should support this decoding.

I've been trying the apple-flat-package, but so far, I only managed to list the Payload content (i.e., a cpio archive). My current solution is the following:

use apple_flat_package::reader::PkgReader;
use std::error::Error;
use std::fs::File;

fn main() -> Result<(), Box<dyn Error>> {
    // Source file obtained from
    // https://msedge.sf.dl.delivery.mp.microsoft.com/filestreamingservice/files/61b13da3-c921-482a-9166-743689310b71/MicrosoftEdge-122.0.2365.92.pkg
    let source = r#"C:\Users\boni\Downloads\MicrosoftEdge-122.0.2365.92.pkg"#;

    let mut reader = PkgReader::new(File::open(source)?)?;
    let packages = reader.component_packages()?;
    println!("Number of components: {}", packages.len());
    for package in packages.iter() {
        if let Some(cpio_reader) = package.payload_reader()? {
            for entry in cpio_reader {
                let e = entry?;
                let name = e.name();
                println!("---> {}", name);
            }
        }
    }

    Ok(())
}

Instead of simply listing the content, I need to extract the content of the Payload to a target path in my file system. I believe I'm close, but I don't know how to do it.

Can you please guide me? I really appreciate any help you can provide.

roblabla commented 5 months ago

The interface for cpio_reader is... awkward. It's both an Iterator and a Reader for the current archive. So you can do something like this:

    for package in packages.iter() {
        if let Some(cpio_reader) = package.payload_reader()? {
            while let Some(e) = cpio_reader.next() {
                let e = entry?;
                let name = e.name();
                let mut contents = Vec::new();
                cpio_reader.read_to_end(&mut contents)?;
                // Write contents somewhere.
                println!("---> {}", name);
            }
        }
    }
bonigarcia commented 5 months ago

That worked, many thanks for the quick reply, @roblabla! I updated my example just in case it is helpful for someone.