diff --git a/src/cache/mod.rs b/src/cache/mod.rs index e49571e..ceff474 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -59,13 +59,19 @@ where /// get a file block either from cache or from remote if it's already /// not cached pub async fn get(&self, block: &Block) -> Result<(u64, File)> { - let mut file = self.prepare(&block.id).await?; + let mut file = self + .prepare(&block.id) + .await + .context("failed to prepare cache block")?; // TODO: locking must happen here so no // other processes start downloading the same chunk let locker = Locker::new(&file); locker.lock().await?; - let meta = file.metadata().await?; + let meta = file + .metadata() + .await + .context("failed to get block metadata")?; if meta.len() > 0 { // chunk is already downloaded debug!("block cache hit: {}", block.id.as_slice().hex()); @@ -74,7 +80,10 @@ where } debug!("downloading block: {}", block.id.as_slice().hex()); - let size = self.download(&mut file, block).await?; + let size = self + .download(&mut file, block) + .await + .context("failed to download block")?; // if file is just downloaded, we need // to seek to beginning of the file. @@ -92,7 +101,7 @@ where let (_, mut chunk) = self.get(block).await?; copy(&mut chunk, out) .await - .with_context(|| format!("failed to download block {}", index))?; + .with_context(|| format!("failed to copy block {}", index))?; } Ok(()) diff --git a/src/lib.rs b/src/lib.rs index cc0c7ae..6fc4147 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ extern crate log; use anyhow::Context; use fungi::meta::Ino; use fungi::Writer; +use nix::unistd::{fchownat, FchownatFlags, Gid, Uid}; use std::collections::LinkedList; use std::ffi::OsString; use std::fs::Metadata; @@ -29,6 +30,7 @@ struct CopyVisitor<'a, S> where S: store::Store, { + preserve: bool, meta: &'a fungi::Reader, cache: &'a cache::Cache, root: &'a Path, @@ -38,8 +40,18 @@ impl<'a, S> CopyVisitor<'a, S> where S: store::Store, { - pub fn new(meta: &'a fungi::Reader, cache: &'a Cache, root: &'a Path) -> Self { - Self { meta, cache, root } + pub fn new( + meta: &'a fungi::Reader, + cache: &'a Cache, + root: &'a Path, + preserve: bool, + ) -> Self { + Self { + meta, + cache, + root, + preserve, + } } } @@ -49,6 +61,8 @@ where S: Store, { async fn visit(&mut self, path: &Path, node: &Inode) -> Result { + use std::fs::Permissions; + use std::os::unix::fs::PermissionsExt; use tokio::fs::OpenOptions; let rooted = self.root.join(path.strip_prefix("/").unwrap()); @@ -60,10 +74,9 @@ where } FileType::Regular => { let mut fd = OpenOptions::new() - .create(true) + .create_new(true) .write(true) .truncate(true) - .mode(node.mode.mode()) .open(&rooted) .await .with_context(|| format!("failed to create file '{:?}'", rooted))?; @@ -72,7 +85,10 @@ where self.cache .direct(&blocks, &mut fd) .await - .with_context(|| format!("failed to create download file '{:?}'", rooted))?; + .with_context(|| format!("failed to download file '{:?}'", rooted))?; + + fd.set_permissions(Permissions::from_mode(node.mode.mode())) + .await?; } FileType::Link => { let target = node @@ -91,10 +107,22 @@ where .with_context(|| format!("failed to create symlink '{:?}'", rooted))?; } _ => { - debug!("unknown file kind: {:?}", node.mode.file_type()); + warn!("unknown file kind: {:?}", node.mode.file_type()); + return Ok(Walk::Continue); } }; + if self.preserve { + fchownat( + None, + &rooted, + Some(Uid::from_raw(node.uid)), + Some(Gid::from_raw(node.gid)), + FchownatFlags::NoFollowSymlink, + ) + .with_context(|| format!("failed to change ownership of '{:?}'", &rooted))?; + } + Ok(Walk::Continue) } } @@ -105,8 +133,9 @@ pub async fn unpack, S: Store>( meta: &Reader, cache: &Cache, root: P, + preserve: bool, ) -> Result<()> { - let mut visitor = CopyVisitor::new(meta, cache, root.as_ref()); + let mut visitor = CopyVisitor::new(meta, cache, root.as_ref(), preserve); meta.walk(&mut visitor).await } @@ -333,7 +362,7 @@ mod test { assert_eq!((routers[0].start, routers[0].end), (0x00, 0x7f)); assert_eq!((routers[1].start, routers[1].end), (0x80, 0xff)); - unpack(&reader, &cache, root.join("destination")) + unpack(&reader, &cache, root.join("destination"), false) .await .unwrap(); diff --git a/src/main.rs b/src/main.rs index b061c54..1c2a3de 100644 --- a/src/main.rs +++ b/src/main.rs @@ -81,6 +81,11 @@ struct UnpackOptions { #[clap(short, long, default_value_t = String::from("/tmp/cache"))] cache: String, + /// preserve files ownership from the FL, otherwise use the current user ownership + /// setting this flag to true normally requires sudo + #[clap(short, long, default_value_t = false)] + preserve_ownership: bool, + /// target directory to upload target: String, } @@ -132,7 +137,7 @@ fn unpack(opts: UnpackOptions) -> Result<()> { let router = get_router(&meta).await?; let cache = cache::Cache::new(opts.cache, router); - rfs::unpack(&meta, &cache, opts.target).await?; + rfs::unpack(&meta, &cache, opts.target, opts.preserve_ownership).await?; Ok(()) }) }