From e22da2f40a07c08230718ab8004e66645ab7e719 Mon Sep 17 00:00:00 2001 From: Christian Ebner Date: Wed, 16 Aug 2023 11:57:45 +0200 Subject: [PATCH] fix: #4761: unlink existing entries for hard/symlinks when overwrite Creating symlinks or hardlinks might fail if a directory entry with the same name is already present on the filesystem during restore. When the overwrite flag is given, on failure unlink the existing entry (except directories) and retry hard/symlink creation. Signed-off-by: Christian Ebner --- pbs-client/src/pxar/extract.rs | 46 ++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/pbs-client/src/pxar/extract.rs b/pbs-client/src/pxar/extract.rs index 4dbaf52d..9a95faa0 100644 --- a/pbs-client/src/pxar/extract.rs +++ b/pbs-client/src/pxar/extract.rs @@ -547,7 +547,21 @@ impl Extractor { link: &OsStr, ) -> Result<(), Error> { let parent = self.parent_fd()?; - nix::unistd::symlinkat(link, Some(parent), file_name)?; + + match nix::unistd::symlinkat(link, Some(parent), file_name) { + Ok(()) => {} + Err(err @ nix::errno::Errno::EEXIST) => { + if !self.overwrite { + return Err(err.into()); + } + // Never unlink directories + let flag = nix::unistd::UnlinkatFlags::NoRemoveDir; + nix::unistd::unlinkat(Some(parent), file_name, flag)?; + nix::unistd::symlinkat(link, Some(parent), file_name)?; + } + Err(err) => return Err(err.into()) + } + metadata::apply_at( self.feature_flags, metadata, @@ -564,13 +578,29 @@ impl Extractor { let parent = self.parent_fd()?; let root = self.dir_stack.root_dir_fd()?; let target = CString::new(link.as_bytes())?; - nix::unistd::linkat( - Some(root.as_raw_fd()), - target.as_c_str(), - Some(parent), - file_name, - nix::unistd::LinkatFlags::NoSymlinkFollow, - )?; + let dolink = || { + nix::unistd::linkat( + Some(root.as_raw_fd()), + target.as_c_str(), + Some(parent), + file_name, + nix::unistd::LinkatFlags::NoSymlinkFollow, + ) + }; + + match dolink() { + Ok(()) => {} + Err(err @ nix::errno::Errno::EEXIST) => { + if !self.overwrite { + return Err(err.into()); + } + // Never unlink directories + let flag = nix::unistd::UnlinkatFlags::NoRemoveDir; + nix::unistd::unlinkat(Some(parent), file_name, flag)?; + dolink()?; + } + Err(err) => return Err(err.into()) + } Ok(()) }