1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
pub mod newc;
pub use newc::{NewcHeader, NewcReader};
pub mod odc;
pub use odc::{OdcBuilder, OdcHeader, OdcReader};
use {
chrono::{DateTime, NaiveDateTime, Utc},
std::{
fmt::Debug,
io::{Chain, Cursor, Read},
path::PathBuf,
},
};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("bad magic value encountered")]
BadMagic,
#[error("value in header is not an ASCII string")]
BadHeaderString,
#[error("string value in header is not in hex: {0}")]
BadHeaderHex(String),
#[error("filename could not be decoded")]
FilenameDecode,
#[error("Numeric value too large to be encoded")]
ValueTooLarge,
#[error("Size mismatch between header length and provided data")]
SizeMismatch,
#[error("path is not a file: {0}")]
NotAFile(PathBuf),
}
/// Result type for this crate.
pub type CpioResult<T> = Result<T, Error>;
/// Common behavior for a header/entry in a cpio archive.
pub trait CpioHeader: Debug {
/// Device number.
fn device(&self) -> u32;
/// Inode number.
fn inode(&self) -> u32;
/// File mode.
fn mode(&self) -> u32;
/// User ID.
fn uid(&self) -> u32;
/// Group ID.
fn gid(&self) -> u32;
/// Number of links.
fn nlink(&self) -> u32;
/// Associated device number.
fn rdev(&self) -> u32;
/// Modified time as seconds since UNIX epoch.
fn mtime(&self) -> u32;
/// Modified time as a [DateTime].
fn modified_time(&self) -> DateTime<Utc> {
DateTime::<Utc>::from_naive_utc_and_offset(
NaiveDateTime::from_timestamp_opt(self.mtime() as _, 0)
.expect("out of range timestamp"),
Utc,
)
}
/// File size in bytes.
fn file_size(&self) -> u64;
/// File name.
fn name(&self) -> &str;
}
/// Common interface for cpio archive reading.
///
/// In addition to the members of this trait, instances implement [Iterator] over
/// the members of the archive and [Read] to obtain a reader for the current
/// archive member.
///
/// Instances behave like a cursor over members of the archive. The cursor is
/// advanced by calling [Self::read_next]. When the cursor is advanced, the
/// [Read] trait will read data for this and only this archive member. The reader
/// will hit EOF at the end of the current archive member.
pub trait CpioReader<T>: Iterator<Item = CpioResult<Box<dyn CpioHeader>>> + Read
where
T: Read + Sized,
{
/// Construct a new instance from a reader.
fn new(reader: T) -> Self
where
Self: Sized;
/// Read the next header from the archive.
///
/// `Some` on another file entry. `None` if at end of file.
///
/// The special `TRAILER!!!` entry is not emitted.
fn read_next(&mut self) -> CpioResult<Option<Box<dyn CpioHeader>>>;
/// Finish reading the current member.
///
/// This will advance the reader to the next archive member if the
/// current member hasn't been fully consumed.
fn finish(&mut self) -> CpioResult<()>;
}
pub type ChainedCpioReader<T> = dyn CpioReader<Chain<Cursor<Vec<u8>>, T>>;
/// Construct a new cpio archive reader.
///
/// This will sniff the type of the cpio archive and return an appropriate
/// instance.
pub fn reader<T: 'static + Read + Sized>(mut reader: T) -> CpioResult<Box<ChainedCpioReader<T>>> {
let mut magic = vec![0u8; 6];
reader.read_exact(&mut magic)?;
match magic.as_ref() {
crate::newc::MAGIC => Ok(Box::new(NewcReader::new(Cursor::new(magic).chain(reader)))),
crate::odc::MAGIC => Ok(Box::new(OdcReader::new(Cursor::new(magic).chain(reader)))),
_ => Err(Error::BadMagic),
}
}