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 anyhow
::{bail, Error}
;
7 use std
::ffi
::{CString, CStr}
;
8 use tokio
::io
::{copy, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}
;
10 /// Write a cpio file entry to an AsyncWrite.
11 pub async
fn append_file
<W
: AsyncWrite
+ Unpin
, R
: AsyncRead
+ Unpin
>(
19 // negative mtimes are generally valid, but cpio defines all fields as unsigned
21 // c_filesize has 8 bytes, but man page claims that 4 GB files are the maximum, let's be safe
23 ) -> Result
<(), Error
> {
24 let name
= name
.to_bytes_with_nul();
26 target
.write_all(b
"070701").await?
; // c_magic
27 print_cpio_hex(&mut target
, inode
as u64).await?
; // c_ino
28 print_cpio_hex(&mut target
, mode
as u64).await?
; // c_mode
29 print_cpio_hex(&mut target
, uid
as u64).await?
; // c_uid
30 print_cpio_hex(&mut target
, gid
as u64).await?
; // c_gid
31 print_cpio_hex(&mut target
, 0).await?
; // c_nlink
32 print_cpio_hex(&mut target
, mtime
as u64).await?
; // c_mtime
33 print_cpio_hex(&mut target
, size
as u64).await?
; // c_filesize
34 print_cpio_hex(&mut target
, 0).await?
; // c_devmajor
35 print_cpio_hex(&mut target
, 0).await?
; // c_devminor
36 print_cpio_hex(&mut target
, 0).await?
; // c_rdevmajor
37 print_cpio_hex(&mut target
, 0).await?
; // c_rdevminor
38 print_cpio_hex(&mut target
, name
.len() as u64).await?
; // c_namesize
39 print_cpio_hex(&mut target
, 0).await?
; // c_check (ignored for newc)
41 target
.write_all(name
).await?
;
42 let header_size
= 6 + 8*13 + name
.len();
43 let mut name_pad
= header_size
;
44 while name_pad
& 3 != 0 {
45 target
.write_u8(0).await?
;
49 let mut content
= content
.take(size
as u64);
50 let copied
= copy(&mut content
, &mut target
).await?
;
51 if copied
< size
as u64 {
52 bail
!("cpio: not enough data, or size to big - encoding invalid");
54 let mut data_pad
= copied
;
55 while data_pad
& 3 != 0 {
56 target
.write_u8(0).await?
;
63 /// Write the TRAILER!!! file to an AsyncWrite, signifying the end of a cpio archive. Note that you
64 /// can immediately add more files after, to create a concatenated archive, the kernel for example
65 /// will merge these upon loading an initramfs.
66 pub async
fn append_trailer
<W
: AsyncWrite
+ Unpin
>(target
: W
) -> Result
<(), Error
> {
67 let name
= CString
::new("TRAILER!!!").unwrap();
68 append_file(target
, tokio
::io
::empty(), &name
, 0, 0, 0, 0, 0, 0).await
71 async
fn print_cpio_hex
<W
: AsyncWrite
+ Unpin
>(target
: &mut W
, value
: u64) -> Result
<(), Error
> {
72 target
.write_all(format
!("{:08x}", value
).as_bytes()).await
.map_err(|e
| e
.into())