use super::JournalConfig;
use crate::containerization_archive::ArchiveReader;
use crate::containerization_archive::Filter;
use crate::containerization_archive::Format;
use crate::containerization_ext4::FileTimestamps;
use crate::containerization_extras::ProgressHandler;
use crate::error::Error;
use crate::platform;
use crate::platform::ffi;
use std::collections::BTreeMap;
use std::path::Path;
use std::path::PathBuf;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct FormatterOptions {
pub block_size: u32,
pub min_disk_size: u64,
pub journal: Option<JournalConfig>,
}
impl Default for FormatterOptions {
fn default() -> Self {
Self {
block_size: 4096,
min_disk_size: 256 * 1024,
journal: None,
}
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct CreateOptions<'a> {
pub link: Option<PathBuf>,
pub ts: FileTimestamps,
pub buf: Option<&'a [u8]>,
pub uid: Option<u32>,
pub gid: Option<u32>,
pub xattrs: Option<BTreeMap<String, Vec<u8>>>,
pub recursion: bool,
}
pub struct UnpackOptions {
pub format: Format,
pub compression: Filter,
pub progress: Option<ProgressHandler>,
}
impl Default for UnpackOptions {
fn default() -> Self {
Self {
format: Format::PaxRestricted,
compression: Filter::Gzip,
progress: None,
}
}
}
pub struct Formatter {
handle: ffi::CzExt4Formatter,
}
unsafe impl Send for Formatter {}
impl Formatter {
pub fn new(device_path: &Path, options: FormatterOptions) -> Result<Self, Error> {
platform::outcome(
ffi::cz_ext4_formatter_new(&device_path.display().to_string(), options),
format!("format {}", device_path.display()),
)
.map(|outcome| Self {
handle: outcome.ext4_formatter(),
})
}
pub fn link(&self, link: &Path, target: &Path) -> Result<(), Error> {
platform::outcome(
self
.handle
.link(&link.display().to_string(), &target.display().to_string()),
format!("link {} to {}", link.display(), target.display()),
)
.map(drop)
}
pub fn unlink(&self, path: &Path, directory_whiteout: bool) -> Result<(), Error> {
platform::outcome(
self
.handle
.unlink(&path.display().to_string(), directory_whiteout),
format!("unlink {}", path.display()),
)
.map(drop)
}
pub fn create(&self, path: &Path, mode: u16, options: CreateOptions<'_>) -> Result<(), Error> {
let ts = options.ts;
let xattrs = options.xattrs.as_ref();
platform::outcome(
self.handle.create(
&path.display().to_string(),
options.link.map(|link| link.display().to_string()),
mode,
platform::seconds(ts.access),
platform::seconds(ts.modification),
platform::seconds(ts.creation),
platform::seconds(ts.now),
options.buf.is_some(),
options.buf.unwrap_or_default().to_vec(),
options.uid,
options.gid,
xattrs.is_some(),
xattrs
.map(|xattrs| xattrs.keys().cloned().collect())
.unwrap_or_default(),
xattrs
.map(|xattrs| xattrs.values().map(|value| value.len() as u64).collect())
.unwrap_or_default(),
xattrs
.map(|xattrs| xattrs.values().flatten().copied().collect())
.unwrap_or_default(),
options.recursion,
),
format!("create {}", path.display()),
)
.map(drop)
}
pub fn close(&self) -> Result<(), Error> {
platform::outcome(self.handle.close(), "close a filesystem").map(drop)
}
pub fn unpack(&self, source: &Path, options: UnpackOptions) -> Result<(), Error> {
platform::outcome(
self.handle.unpack(
&source.display().to_string(),
options.format.raw_value(),
options.compression.raw_value(),
platform::Progress(options.progress),
),
format!("unpack {}", source.display()),
)
.map(drop)
}
pub fn unpack_reader(&self, reader: &ArchiveReader, progress: Option<ProgressHandler>) -> Result<(), Error> {
platform::outcome(
self
.handle
.unpack_reader(reader.handle.duplicate(), platform::Progress(progress)),
"unpack an archive",
)
.map(drop)
}
pub fn scan_archive_headers(format: Format, filter: Filter, file: &Path) -> Result<(i64, isize), Error> {
platform::outcome(
ffi::cz_ext4_formatter_scan_archive_headers(format.raw_value(), filter.raw_value(), &file.display().to_string()),
format!("scan the archive {}", file.display()),
)
.map(|outcome| (outcome.scanned_size(), outcome.scanned_items()))
}
}