blob: c44c07b3ab688867daf46e4af06f6c804cce1bad [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::METADATA_PATH;
use {
ext4_metadata::{ExtendedAttributes, Metadata},
ext4_read_only::structs::INode,
std::{
collections::HashMap,
io::{Error, ErrorKind, Result},
},
};
pub struct Writer {
out_dir: String,
metadata: Metadata,
manifest: HashMap<String, String>,
}
impl Writer {
/// Creates a remote bundle writer which will store its files at `out_dir`.
pub fn new(
out_dir: &impl AsRef<str>,
inode: u64,
mode: u16,
owner: Owner,
xattr: ExtendedAttributes,
) -> Result<Writer> {
let mut metadata = Metadata::new();
// Insert the root entry.
metadata.insert_directory(inode, mode, owner.uid, owner.gid, xattr);
let out_dir: String = out_dir.as_ref().to_owned();
Ok(Writer { out_dir, metadata, manifest: HashMap::new() })
}
/// Add the contents of `data` as a file at `path` in the remote bundle.
pub fn add_file(
&mut self,
path: &[&str],
data: &mut impl std::io::Read,
inode: u64,
mode: u16,
owner: Owner,
xattr: ExtendedAttributes,
) -> Result<()> {
self.metadata.insert_file(inode, mode, owner.uid, owner.gid, xattr);
self.metadata.add_child(path, inode);
let out_dir = &self.out_dir;
let blob_path = format!("{out_dir}/{inode}");
let mut file = std::fs::File::create(&blob_path)?;
std::io::copy(data, &mut file)?;
self.manifest.insert(format!("{inode}"), blob_path);
Ok(())
}
/// Add an empty directory at `path` in the remote bundle.
pub fn add_directory(
&mut self,
path: &[&str],
inode: u64,
mode: u16,
owner: Owner,
xattr: ExtendedAttributes,
) {
self.metadata.insert_directory(inode, mode, owner.uid, owner.gid, xattr);
self.metadata.add_child(path, inode);
}
/// Add the contents of `data` as a symlink at `path` in the remote bundle.
pub fn add_symlink(
&mut self,
path: &[&str],
data: Vec<u8>,
inode: u64,
mode: u16,
owner: Owner,
xattr: ExtendedAttributes,
) -> Result<()> {
let target = String::from_utf8(data).map_err(|e| {
Error::new(
ErrorKind::InvalidInput,
format!(
"Symbolic link at {} has non-utf8 target: {:?}",
path.join("/"),
e.into_bytes()
),
)
})?;
self.metadata.insert_symlink(inode, target, mode, owner.uid, owner.gid, xattr);
self.metadata.add_child(path, inode);
Ok(())
}
/// Finish writing and return a map of the destination to source pairs.
pub fn export(mut self) -> Result<HashMap<String, String>> {
let out_dir = self.out_dir;
let metadata_path = format!("{out_dir}/{METADATA_PATH}");
std::fs::write(&metadata_path, self.metadata.serialize())?;
self.manifest.insert(METADATA_PATH.to_string(), metadata_path);
Ok(self.manifest)
}
}
#[derive(Clone, Copy)]
pub struct Owner {
pub uid: u16,
pub gid: u16,
}
impl Owner {
pub fn root() -> Owner {
Owner { uid: 0, gid: 0 }
}
pub fn from_inode(inode: &INode) -> Owner {
Owner { uid: inode.e2di_uid.get(), gid: inode.e2di_gid.get() }
}
}