1 //! Provides a very basic "newc" format cpio encoder.
2 //! See 'man 5 cpio' for format details, as well as:
3 //! https://www.kernel.org/doc/html/latest/driver-api/early-userspace/buffer-format.html
4 //! This does not provide full support for the format, only what is needed to include files in an
5 //! initramfs intended for a linux kernel.
6 use std
::ffi
::{CString, CStr}
;
8 use anyhow
::{bail, Error}
;
9 use tokio
::io
::{copy, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}
;
11 /// Write a cpio file entry to an AsyncWrite.
12 pub async
fn append_file
<W
: AsyncWrite
+ Unpin
, R
: AsyncRead
+ Unpin
>(
20 // negative mtimes are generally valid, but cpio defines all fields as unsigned
22 // c_filesize has 8 bytes, but man page claims that 4 GB files are the maximum, let's be safe
24 ) -> Result
<(), Error
> {
25 let name
= name
.to_bytes_with_nul();
27 target
.write_all(b
"070701").await?
; // c_magic
28 print_cpio_hex(&mut target
, inode
as u64).await?
; // c_ino
29 print_cpio_hex(&mut target
, mode
as u64).await?
; // c_mode
30 print_cpio_hex(&mut target
, uid
as u64).await?
; // c_uid
31 print_cpio_hex(&mut target
, gid
as u64).await?
; // c_gid
32 print_cpio_hex(&mut target
, 0).await?
; // c_nlink
33 print_cpio_hex(&mut target
, mtime
as u64).await?
; // c_mtime
34 print_cpio_hex(&mut target
, size
as u64).await?
; // c_filesize
35 print_cpio_hex(&mut target
, 0).await?
; // c_devmajor
36 print_cpio_hex(&mut target
, 0).await?
; // c_devminor
37 print_cpio_hex(&mut target
, 0).await?
; // c_rdevmajor
38 print_cpio_hex(&mut target
, 0).await?
; // c_rdevminor
39 print_cpio_hex(&mut target
, name
.len() as u64).await?
; // c_namesize
40 print_cpio_hex(&mut target
, 0).await?
; // c_check (ignored for newc)
42 target
.write_all(name
).await?
;
43 let header_size
= 6 + 8*13 + name
.len();
44 let mut name_pad
= header_size
;
45 while name_pad
& 3 != 0 {
46 target
.write_u8(0).await?
;
50 let mut content
= content
.take(size
as u64);
51 let copied
= copy(&mut content
, &mut target
).await?
;
52 if copied
< size
as u64 {
53 bail
!("cpio: not enough data, or size to big - encoding invalid");
55 let mut data_pad
= copied
;
56 while data_pad
& 3 != 0 {
57 target
.write_u8(0).await?
;
64 /// Write the TRAILER!!! file to an AsyncWrite, signifying the end of a cpio archive. Note that you
65 /// can immediately add more files after, to create a concatenated archive, the kernel for example
66 /// will merge these upon loading an initramfs.
67 pub async
fn append_trailer
<W
: AsyncWrite
+ Unpin
>(target
: W
) -> Result
<(), Error
> {
68 let name
= CString
::new("TRAILER!!!").unwrap();
69 append_file(target
, tokio
::io
::empty(), &name
, 0, 0, 0, 0, 0, 0).await
72 async
fn print_cpio_hex
<W
: AsyncWrite
+ Unpin
>(target
: &mut W
, value
: u64) -> Result
<(), Error
> {
73 target
.write_all(format
!("{:08x}", value
).as_bytes()).await
.map_err(|e
| e
.into())