From 1a77273e6e6e4b9b097097f07c1fe2b64cba022a Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Tue, 5 Jul 2022 15:21:29 +0200 Subject: [PATCH 01/10] backup: Add support for special files --- src/backend/ignore.rs | 12 +++++++++++- src/backend/node.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/backend/ignore.rs b/src/backend/ignore.rs index ef9b66a..1218cff 100644 --- a/src/backend/ignore.rs +++ b/src/backend/ignore.rs @@ -1,5 +1,5 @@ use std::fs::{read_link, File}; -use std::os::unix::fs::MetadataExt; +use std::os::unix::fs::{FileTypeExt, MetadataExt}; use std::path::{Path, PathBuf}; use anyhow::Result; @@ -192,11 +192,21 @@ fn map_entry(entry: DirEntry, with_atime: bool, cache: &UsersCache) -> Result<(P device_id, links, }; + let filetype = m.file_type(); + let node = if m.is_dir() { Node::new_dir(name, meta) } else if m.is_symlink() { let target = read_link(entry.path())?; Node::new_symlink(name, target, meta) + } else if filetype.is_block_device() { + Node::new_dev(name, meta, m.rdev()) + } else if filetype.is_char_device() { + Node::new_chardev(name, meta, m.rdev()) + } else if filetype.is_fifo() { + Node::new_fifo(name, meta) + } else if filetype.is_socket() { + Node::new_socket(name, meta) } else { Node::new_file(name, meta) }; diff --git a/src/backend/node.rs b/src/backend/node.rs index fbd56cc..65de842 100644 --- a/src/backend/node.rs +++ b/src/backend/node.rs @@ -108,6 +108,46 @@ impl Node { } } + pub fn new_dev(name: OsString, meta: Metadata, device: u64) -> Self { + Self { + name: name.to_str().expect("no unicode").to_string(), + node_type: NodeType::Dev { device }, + content: None, + subtree: None, + meta, + } + } + + pub fn new_chardev(name: OsString, meta: Metadata, device: u64) -> Self { + Self { + name: name.to_str().expect("no unicode").to_string(), + node_type: NodeType::Chardev { device }, + content: None, + subtree: None, + meta, + } + } + + pub fn new_fifo(name: OsString, meta: Metadata) -> Self { + Self { + name: name.to_str().expect("no unicode").to_string(), + node_type: NodeType::Fifo, + content: None, + subtree: None, + meta, + } + } + + pub fn new_socket(name: OsString, meta: Metadata) -> Self { + Self { + name: name.to_str().expect("no unicode").to_string(), + node_type: NodeType::Socket, + content: None, + subtree: None, + meta, + } + } + pub fn is_dir(&self) -> bool { self.node_type == NodeType::Dir } From a63c82e77317bf8298b7cfd0ba5657e1f69a518d Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Tue, 5 Jul 2022 18:36:20 +0200 Subject: [PATCH 02/10] correct go filemode representation --- src/backend/ignore.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/backend/ignore.rs b/src/backend/ignore.rs index 1218cff..4e73148 100644 --- a/src/backend/ignore.rs +++ b/src/backend/ignore.rs @@ -217,15 +217,15 @@ const MODE_PERM: u32 = 0o777; // permission bits // consts from https://pkg.go.dev/io/fs#ModeType const GO_MODE_DIR: u32 = 0b10000000000000000000000000000000; -const GO_MODE_SYMLINK: u32 = 0b00000100000000000000000000000000; -const GO_MODE_DEVICE: u32 = 0b00000010000000000000000000000000; -const GO_MODE_FIFO: u32 = 0b00000001000000000000000000000000; -const GO_MODE_SOCKET: u32 = 0b00000000100000000000000000000000; -const GO_MODE_SETUID: u32 = 0b00000000010000000000000000000000; -const GO_MODE_SETGID: u32 = 0b00000000001000000000000000000000; -const GO_MODE_CHARDEV: u32 = 0b00000000000100000000000000000000; -const GO_MODE_STICKY: u32 = 0b00000000000010000000000000000000; -const GO_MODE_IRREG: u32 = 0b00000000000001000000000000000000; +const GO_MODE_SYMLINK: u32 = 0b00001000000000000000000000000000; +const GO_MODE_DEVICE: u32 = 0b00000100000000000000000000000000; +const GO_MODE_FIFO: u32 = 0b00000010000000000000000000000000; +const GO_MODE_SOCKET: u32 = 0b00000001000000000000000000000000; +const GO_MODE_SETUID: u32 = 0b00000000100000000000000000000000; +const GO_MODE_SETGID: u32 = 0b00000000010000000000000000000000; +const GO_MODE_CHARDEV: u32 = 0b00000000001000000000000000000000; +const GO_MODE_STICKY: u32 = 0b00000000000100000000000000000000; +const GO_MODE_IRREG: u32 = 0b00000000000010000000000000000000; // consts from man page inode(7) const S_IFFORMAT: u32 = 0o170000; // File mask @@ -252,7 +252,7 @@ fn map_mode_to_go(mode: u32) -> u32 { S_IFLNK => go_mode |= GO_MODE_SYMLINK, S_IFBLK => go_mode |= GO_MODE_DEVICE, S_IFDIR => go_mode |= GO_MODE_DIR, - S_IFCHR => go_mode |= GO_MODE_CHARDEV, + S_IFCHR => go_mode |= GO_MODE_CHARDEV & GO_MODE_DEVICE, // no idea why go sets both for char devices... S_IFIFO => go_mode |= GO_MODE_FIFO, // note that POSIX specifies regular files, whereas golang specifies irregular files S_IFREG => {} From 4dd22c969215c509830fec5a4047eb60c53cc111 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Tue, 5 Jul 2022 18:42:10 +0200 Subject: [PATCH 03/10] restore: create special files --- Cargo.lock | 22 +++++++++++++++++++++ Cargo.toml | 1 + src/backend/local.rs | 43 +++++++++++++++++++++++++++++++++-------- src/commands/restore.rs | 4 +--- 4 files changed, 59 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 54741c7..6c50e95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -916,6 +916,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "mime" version = "0.3.16" @@ -934,6 +943,18 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "nix" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f17df307904acd05aa8e32e97bb20f2a0df1728bbc2d771ae8f9a90463441e9" +dependencies = [ + "bitflags", + "cfg-if", + "libc", + "memoffset", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -1341,6 +1362,7 @@ dependencies = [ "indicatif", "itertools", "lazy_static", + "nix", "path-absolutize", "prettytable-rs", "rand", diff --git a/Cargo.toml b/Cargo.toml index e905393..3bd6b1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ zstd = "0.11" # local backend walkdir = "2" ignore = "0.4" +nix = "0.24" # rest backend reqwest = {version = "0.11", default-features = false, features = ["json", "rustls-tls", "stream"] } # cache diff --git a/src/backend/local.rs b/src/backend/local.rs index 56954e6..9035e79 100644 --- a/src/backend/local.rs +++ b/src/backend/local.rs @@ -1,15 +1,16 @@ use std::fs::{self, File}; use std::io::{copy, Read, Seek, SeekFrom, Write}; -use std::os::unix::fs::FileExt; -use std::os::unix::fs::PermissionsExt; +use std::os::unix::fs::{symlink, FileExt, PermissionsExt}; use std::path::{Path, PathBuf}; use anyhow::Result; use async_trait::async_trait; +use nix::sys::stat::{mknod, Mode, SFlag}; use vlog::*; use walkdir::WalkDir; -use super::{node::Metadata, FileType, Id, ReadBackend, WriteBackend, ALL_FILE_TYPES}; +use super::node::{Metadata, Node, NodeType}; +use super::{FileType, Id, ReadBackend, WriteBackend, ALL_FILE_TYPES}; #[derive(Clone)] pub struct LocalBackend { @@ -190,11 +191,6 @@ impl LocalBackend { fs::create_dir(&dirname).unwrap(); } - pub fn create_symlink(&self, item: impl AsRef, dest: impl AsRef) { - let filename = self.path.join(item); - std::os::unix::fs::symlink(dest, filename).unwrap(); - } - // TODO: uid/gid and times pub fn set_metadata(&self, item: impl AsRef, meta: &Metadata) { let mode = *meta.mode(); @@ -212,6 +208,37 @@ impl LocalBackend { f.set_len(size).unwrap(); } + pub fn create_special(&self, item: impl AsRef, node: &Node) { + let filename = self.path.join(item); + + match node.node_type() { + NodeType::Symlink { linktarget } => { + symlink(linktarget, filename).unwrap(); + } + NodeType::Dev { device } => { + #[cfg(not(target_os = "macos"))] + let device = *device; + #[cfg(target_os = "macos")] + let device = *device as i32; + mknod(&filename, SFlag::S_IFBLK, Mode::empty(), device).unwrap(); + } + NodeType::Chardev { device } => { + #[cfg(not(target_os = "macos"))] + let device = *device; + #[cfg(target_os = "macos")] + let device = *device as i32; + mknod(&filename, SFlag::S_IFCHR, Mode::empty(), device).unwrap(); + } + NodeType::Fifo => { + mknod(&filename, SFlag::S_IFIFO, Mode::empty(), 0).unwrap(); + } + NodeType::Socket => { + mknod(&filename, SFlag::S_IFSOCK, Mode::empty(), 0).unwrap(); + } + _ => {} + } + } + pub fn write_at(&self, item: impl AsRef, offset: u64, data: &[u8]) { let filename = self.path.join(item); let file = fs::OpenOptions::new() diff --git a/src/commands/restore.rs b/src/commands/restore.rs index ea3da4e..ac9c675 100644 --- a/src/commands/restore.rs +++ b/src/commands/restore.rs @@ -150,9 +150,7 @@ async fn restore_metadata( let mut node_streamer = NodeStreamer::new(index, tree).await?; while let Some((path, node)) = node_streamer.try_next().await? { if !opts.dry_run { - if let NodeType::Symlink { linktarget } = node.node_type() { - dest.create_symlink(&path, linktarget); - } + dest.create_special(&path, &node); dest.set_metadata(&path, node.meta()); } } From 07db2fec199229914c247263ddc2ba28b2d85379 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Tue, 5 Jul 2022 20:17:51 +0200 Subject: [PATCH 04/10] restore: use correct file modes when restoring --- src/backend/ignore.rs | 35 +++++++++++++++++++++++++++++++++++ src/backend/local.rs | 7 ++----- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/backend/ignore.rs b/src/backend/ignore.rs index 4e73148..21c633c 100644 --- a/src/backend/ignore.rs +++ b/src/backend/ignore.rs @@ -271,3 +271,38 @@ fn map_mode_to_go(mode: u32) -> u32 { go_mode } + +/// map gloangs mode definition (https://pkg.go.dev/io/fs#ModeType) to t_mode from POSIX (inode(7)) +/// This is the inverse function to map_mode_to_go() +pub fn map_mode_from_go(go_mode: u32) -> u32 { + let mut mode = go_mode & MODE_PERM; + + if go_mode & GO_MODE_SOCKET > 0 { + mode |= S_IFSOCK + } else if go_mode & GO_MODE_SYMLINK > 0 { + mode |= S_IFLNK + } else if go_mode & GO_MODE_DEVICE > 0 && go_mode & GO_MODE_CHARDEV == 0 { + mode |= S_IFBLK; + } else if go_mode & GO_MODE_DIR > 0 { + mode |= S_IFDIR; + } else if go_mode & (GO_MODE_CHARDEV | GO_MODE_DEVICE) > 0 { + mode |= S_IFCHR; + } else if go_mode & GO_MODE_FIFO > 0 { + mode |= S_IFIFO; + } else if go_mode & GO_MODE_IRREG > 0 { + } else { + mode |= S_IFREG; + } + + if go_mode & GO_MODE_SETUID > 0 { + mode |= S_ISUID; + } + if go_mode & GO_MODE_SETGID > 0 { + mode |= S_ISGID; + } + if go_mode & GO_MODE_STICKY > 0 { + mode |= S_ISVTX; + } + + mode +} diff --git a/src/backend/local.rs b/src/backend/local.rs index 9035e79..e375d83 100644 --- a/src/backend/local.rs +++ b/src/backend/local.rs @@ -10,7 +10,7 @@ use vlog::*; use walkdir::WalkDir; use super::node::{Metadata, Node, NodeType}; -use super::{FileType, Id, ReadBackend, WriteBackend, ALL_FILE_TYPES}; +use super::{map_mode_from_go, FileType, Id, ReadBackend, WriteBackend, ALL_FILE_TYPES}; #[derive(Clone)] pub struct LocalBackend { @@ -193,11 +193,8 @@ impl LocalBackend { // TODO: uid/gid and times pub fn set_metadata(&self, item: impl AsRef, meta: &Metadata) { - let mode = *meta.mode(); - if mode == 0 { - return; - } let filename = self.path.join(item); + let mode = map_mode_from_go(*meta.mode()); std::fs::set_permissions(&filename, fs::Permissions::from_mode(mode)) .unwrap_or_else(|_| panic!("error chmod {:?}", filename)); } From 297e337dfc944fc14c0820515f88b35defca770a Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Wed, 6 Jul 2022 12:16:17 +0200 Subject: [PATCH 05/10] backup: Always store uid/gid --- src/backend/ignore.rs | 4 ++-- src/backend/node.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/backend/ignore.rs b/src/backend/ignore.rs index 21c633c..8cb4ef6 100644 --- a/src/backend/ignore.rs +++ b/src/backend/ignore.rs @@ -184,8 +184,8 @@ fn map_entry(entry: DirEntry, with_atime: bool, cache: &UsersCache) -> Result<(P atime, ctime, mode, - uid, - gid, + uid: Some(uid), + gid: Some(gid), user, group, inode, diff --git a/src/backend/node.rs b/src/backend/node.rs index 65de842..54b3a65 100644 --- a/src/backend/node.rs +++ b/src/backend/node.rs @@ -53,10 +53,10 @@ pub struct Metadata { pub atime: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub ctime: Option>, - #[serde(default, skip_serializing_if = "is_default")] - pub uid: u32, - #[serde(default, skip_serializing_if = "is_default")] - pub gid: u32, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub uid: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub gid: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub user: Option, #[serde(default, skip_serializing_if = "Option::is_none")] From 739b20744140ea006387e8f54b7740611931ae1b Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Wed, 6 Jul 2022 12:23:31 +0200 Subject: [PATCH 06/10] restore: Restore user/group --- src/backend/local.rs | 29 +++++++++++++++++++++++++++-- src/commands/restore.rs | 3 ++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/backend/local.rs b/src/backend/local.rs index e375d83..40741f9 100644 --- a/src/backend/local.rs +++ b/src/backend/local.rs @@ -6,6 +6,8 @@ use std::path::{Path, PathBuf}; use anyhow::Result; use async_trait::async_trait; use nix::sys::stat::{mknod, Mode, SFlag}; +use nix::unistd::chown; +use nix::unistd::{Gid, Group, Uid, User}; use vlog::*; use walkdir::WalkDir; @@ -191,9 +193,32 @@ impl LocalBackend { fs::create_dir(&dirname).unwrap(); } - // TODO: uid/gid and times - pub fn set_metadata(&self, item: impl AsRef, meta: &Metadata) { + // TODO: times + pub fn set_user_group(&self, item: impl AsRef, meta: &Metadata) { let filename = self.path.join(item); + + // set uid/gid + let user = meta + .user + .as_ref() + .and_then(|name| User::from_name(name).unwrap()); + + // use uid from user if valid, else from saved uid (if saved) + let uid = user.map(|u| u.uid).or_else(|| meta.uid.map(Uid::from_raw)); + + let group = meta + .group + .as_ref() + .and_then(|name| Group::from_name(name).unwrap()); + // use gid from group if valid, else from saved gid (if saved) + let gid = group.map(|g| g.gid).or_else(|| meta.gid.map(Gid::from_raw)); + + chown(&filename, uid, gid).unwrap(); + } + + pub fn set_permission(&self, item: impl AsRef, meta: &Metadata) { + let filename = self.path.join(item); + let mode = map_mode_from_go(*meta.mode()); std::fs::set_permissions(&filename, fs::Permissions::from_mode(mode)) .unwrap_or_else(|_| panic!("error chmod {:?}", filename)); diff --git a/src/commands/restore.rs b/src/commands/restore.rs index ac9c675..2dd7cc8 100644 --- a/src/commands/restore.rs +++ b/src/commands/restore.rs @@ -151,7 +151,8 @@ async fn restore_metadata( while let Some((path, node)) = node_streamer.try_next().await? { if !opts.dry_run { dest.create_special(&path, &node); - dest.set_metadata(&path, node.meta()); + dest.set_user_group(&path, node.meta()); + dest.set_permission(&path, node.meta()); } } From 68d28b413a00b6821cdc70eb0875f77f9f9f2bdb Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Wed, 6 Jul 2022 13:12:53 +0200 Subject: [PATCH 07/10] restore: Add option --numeric-id --- src/backend/local.rs | 11 +++++++++-- src/commands/restore.rs | 10 +++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/backend/local.rs b/src/backend/local.rs index 40741f9..0094dd0 100644 --- a/src/backend/local.rs +++ b/src/backend/local.rs @@ -193,11 +193,9 @@ impl LocalBackend { fs::create_dir(&dirname).unwrap(); } - // TODO: times pub fn set_user_group(&self, item: impl AsRef, meta: &Metadata) { let filename = self.path.join(item); - // set uid/gid let user = meta .user .as_ref() @@ -216,6 +214,15 @@ impl LocalBackend { chown(&filename, uid, gid).unwrap(); } + pub fn set_uid_gid(&self, item: impl AsRef, meta: &Metadata) { + let filename = self.path.join(item); + + let uid = meta.uid.map(Uid::from_raw); + let gid = meta.gid.map(Gid::from_raw); + + chown(&filename, uid, gid).unwrap(); + } + pub fn set_permission(&self, item: impl AsRef, meta: &Metadata) { let filename = self.path.join(item); diff --git a/src/commands/restore.rs b/src/commands/restore.rs index 2dd7cc8..0317a13 100644 --- a/src/commands/restore.rs +++ b/src/commands/restore.rs @@ -26,6 +26,10 @@ pub(super) struct Opts { #[clap(long)] delete: bool, + /// use numeric ids instead of user/groug when restoring uid/gui + #[clap(long)] + numeric_id: bool, + /// snapshot to restore id: String, @@ -151,7 +155,11 @@ async fn restore_metadata( while let Some((path, node)) = node_streamer.try_next().await? { if !opts.dry_run { dest.create_special(&path, &node); - dest.set_user_group(&path, node.meta()); + if opts.numeric_id { + dest.set_uid_gid(&path, node.meta()); + } else { + dest.set_user_group(&path, node.meta()); + } dest.set_permission(&path, node.meta()); } } From b9656a720c84e5ea3747f58948dfc3da514881ad Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Wed, 6 Jul 2022 13:30:00 +0200 Subject: [PATCH 08/10] restore: restore times --- Cargo.lock | 13 +++++++++++++ Cargo.toml | 1 + src/backend/local.rs | 11 +++++++++++ src/commands/restore.rs | 1 + 4 files changed, 26 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 6c50e95..b5b3a47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -471,6 +471,18 @@ dependencies = [ "instant", ] +[[package]] +name = "filetime" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.2.13", + "windows-sys", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1354,6 +1366,7 @@ dependencies = [ "derive-getters", "derive_more", "dirs 4.0.0", + "filetime", "futures", "gethostname", "hex", diff --git a/Cargo.toml b/Cargo.toml index 3bd6b1c..cc30e6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ zstd = "0.11" walkdir = "2" ignore = "0.4" nix = "0.24" +filetime = "0.2" # rest backend reqwest = {version = "0.11", default-features = false, features = ["json", "rustls-tls", "stream"] } # cache diff --git a/src/backend/local.rs b/src/backend/local.rs index 0094dd0..1675db7 100644 --- a/src/backend/local.rs +++ b/src/backend/local.rs @@ -5,6 +5,7 @@ use std::path::{Path, PathBuf}; use anyhow::Result; use async_trait::async_trait; +use filetime::{set_file_atime, set_file_mtime, FileTime}; use nix::sys::stat::{mknod, Mode, SFlag}; use nix::unistd::chown; use nix::unistd::{Gid, Group, Uid, User}; @@ -193,6 +194,16 @@ impl LocalBackend { fs::create_dir(&dirname).unwrap(); } + pub fn set_times(&self, item: impl AsRef, meta: &Metadata) { + let filename = self.path.join(item); + if let Some(mtime) = meta.mtime.map(|t| FileTime::from_system_time(t.into())) { + set_file_mtime(&filename, mtime).unwrap(); + } + if let Some(atime) = meta.atime.map(|t| FileTime::from_system_time(t.into())) { + set_file_atime(&filename, atime).unwrap(); + } + } + pub fn set_user_group(&self, item: impl AsRef, meta: &Metadata) { let filename = self.path.join(item); diff --git a/src/commands/restore.rs b/src/commands/restore.rs index 0317a13..2b20f3a 100644 --- a/src/commands/restore.rs +++ b/src/commands/restore.rs @@ -161,6 +161,7 @@ async fn restore_metadata( dest.set_user_group(&path, node.meta()); } dest.set_permission(&path, node.meta()); + dest.set_times(&path, node.meta()) } } From b2f251c0869dfaec2d4fdc68925f7d4ecc91e48c Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Thu, 7 Jul 2022 12:08:52 +0200 Subject: [PATCH 09/10] restore: Add error handling --- src/backend/local.rs | 36 ++++++++++++++++++++---------------- src/commands/restore.rs | 14 ++++++++++---- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/backend/local.rs b/src/backend/local.rs index 1675db7..620a022 100644 --- a/src/backend/local.rs +++ b/src/backend/local.rs @@ -194,17 +194,18 @@ impl LocalBackend { fs::create_dir(&dirname).unwrap(); } - pub fn set_times(&self, item: impl AsRef, meta: &Metadata) { + pub fn set_times(&self, item: impl AsRef, meta: &Metadata) -> Result<()> { let filename = self.path.join(item); if let Some(mtime) = meta.mtime.map(|t| FileTime::from_system_time(t.into())) { - set_file_mtime(&filename, mtime).unwrap(); + set_file_mtime(&filename, mtime)?; } if let Some(atime) = meta.atime.map(|t| FileTime::from_system_time(t.into())) { - set_file_atime(&filename, atime).unwrap(); + set_file_atime(&filename, atime)?; } + Ok(()) } - pub fn set_user_group(&self, item: impl AsRef, meta: &Metadata) { + pub fn set_user_group(&self, item: impl AsRef, meta: &Metadata) -> Result<()> { let filename = self.path.join(item); let user = meta @@ -222,24 +223,26 @@ impl LocalBackend { // use gid from group if valid, else from saved gid (if saved) let gid = group.map(|g| g.gid).or_else(|| meta.gid.map(Gid::from_raw)); - chown(&filename, uid, gid).unwrap(); + chown(&filename, uid, gid)?; + Ok(()) } - pub fn set_uid_gid(&self, item: impl AsRef, meta: &Metadata) { + pub fn set_uid_gid(&self, item: impl AsRef, meta: &Metadata) -> Result<()> { let filename = self.path.join(item); let uid = meta.uid.map(Uid::from_raw); let gid = meta.gid.map(Gid::from_raw); - chown(&filename, uid, gid).unwrap(); + chown(&filename, uid, gid)?; + Ok(()) } - pub fn set_permission(&self, item: impl AsRef, meta: &Metadata) { + pub fn set_permission(&self, item: impl AsRef, meta: &Metadata) -> Result<()> { let filename = self.path.join(item); let mode = map_mode_from_go(*meta.mode()); - std::fs::set_permissions(&filename, fs::Permissions::from_mode(mode)) - .unwrap_or_else(|_| panic!("error chmod {:?}", filename)); + std::fs::set_permissions(&filename, fs::Permissions::from_mode(mode))?; + Ok(()) } pub fn create_file(&self, item: impl AsRef, size: u64) { @@ -248,35 +251,36 @@ impl LocalBackend { f.set_len(size).unwrap(); } - pub fn create_special(&self, item: impl AsRef, node: &Node) { + pub fn create_special(&self, item: impl AsRef, node: &Node) -> Result<()> { let filename = self.path.join(item); match node.node_type() { NodeType::Symlink { linktarget } => { - symlink(linktarget, filename).unwrap(); + symlink(linktarget, filename)?; } NodeType::Dev { device } => { #[cfg(not(target_os = "macos"))] let device = *device; #[cfg(target_os = "macos")] let device = *device as i32; - mknod(&filename, SFlag::S_IFBLK, Mode::empty(), device).unwrap(); + mknod(&filename, SFlag::S_IFBLK, Mode::empty(), device)?; } NodeType::Chardev { device } => { #[cfg(not(target_os = "macos"))] let device = *device; #[cfg(target_os = "macos")] let device = *device as i32; - mknod(&filename, SFlag::S_IFCHR, Mode::empty(), device).unwrap(); + mknod(&filename, SFlag::S_IFCHR, Mode::empty(), device)?; } NodeType::Fifo => { - mknod(&filename, SFlag::S_IFIFO, Mode::empty(), 0).unwrap(); + mknod(&filename, SFlag::S_IFIFO, Mode::empty(), 0)?; } NodeType::Socket => { - mknod(&filename, SFlag::S_IFSOCK, Mode::empty(), 0).unwrap(); + mknod(&filename, SFlag::S_IFSOCK, Mode::empty(), 0)?; } _ => {} } + Ok(()) } pub fn write_at(&self, item: impl AsRef, offset: u64, data: &[u8]) { diff --git a/src/commands/restore.rs b/src/commands/restore.rs index 2b20f3a..c41ce99 100644 --- a/src/commands/restore.rs +++ b/src/commands/restore.rs @@ -154,14 +154,20 @@ async fn restore_metadata( let mut node_streamer = NodeStreamer::new(index, tree).await?; while let Some((path, node)) = node_streamer.try_next().await? { if !opts.dry_run { - dest.create_special(&path, &node); + dest.create_special(&path, &node) + .unwrap_or_else(|_| eprintln!("restore {:?}: creating special file failed.", path)); if opts.numeric_id { - dest.set_uid_gid(&path, node.meta()); + dest.set_uid_gid(&path, node.meta()) + .unwrap_or_else(|_| eprintln!("restore {:?}: setting UID/GID failed.", path)); } else { - dest.set_user_group(&path, node.meta()); + dest.set_user_group(&path, node.meta()).unwrap_or_else(|_| { + eprintln!("restore {:?}: setting User/Group failed.", path) + }); } - dest.set_permission(&path, node.meta()); + dest.set_permission(&path, node.meta()) + .unwrap_or_else(|_| eprintln!("restore {:?}: chmod failed.", path)); dest.set_times(&path, node.meta()) + .unwrap_or_else(|_| eprintln!("restore {:?}: setting file times failed.", path)); } } From 96288d888dbc6ce95d22ebcb6bdda75829b4877c Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Thu, 7 Jul 2022 12:15:18 +0200 Subject: [PATCH 10/10] treat missing mode correctly --- src/backend/ignore.rs | 2 +- src/backend/local.rs | 6 ++++-- src/backend/node.rs | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/backend/ignore.rs b/src/backend/ignore.rs index 8cb4ef6..65f372e 100644 --- a/src/backend/ignore.rs +++ b/src/backend/ignore.rs @@ -183,7 +183,7 @@ fn map_entry(entry: DirEntry, with_atime: bool, cache: &UsersCache) -> Result<(P mtime, atime, ctime, - mode, + mode: Some(mode), uid: Some(uid), gid: Some(gid), user, diff --git a/src/backend/local.rs b/src/backend/local.rs index 620a022..61068bd 100644 --- a/src/backend/local.rs +++ b/src/backend/local.rs @@ -240,8 +240,10 @@ impl LocalBackend { pub fn set_permission(&self, item: impl AsRef, meta: &Metadata) -> Result<()> { let filename = self.path.join(item); - let mode = map_mode_from_go(*meta.mode()); - std::fs::set_permissions(&filename, fs::Permissions::from_mode(mode))?; + if let Some(mode) = meta.mode() { + let mode = map_mode_from_go(*mode); + std::fs::set_permissions(&filename, fs::Permissions::from_mode(mode))?; + } Ok(()) } diff --git a/src/backend/node.rs b/src/backend/node.rs index 54b3a65..28f1a19 100644 --- a/src/backend/node.rs +++ b/src/backend/node.rs @@ -45,8 +45,8 @@ pub enum NodeType { #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, Getters)] pub struct Metadata { - #[serde(default, skip_serializing_if = "is_default")] - pub mode: u32, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mode: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub mtime: Option>, #[serde(default, skip_serializing_if = "Option::is_none")]