[pbs-devel] [PATCH v3 proxmox-backup 1/2] fix: #4761: unlink existing entries for hard/symlinks when overwrite

Christian Ebner c.ebner at proxmox.com
Wed Aug 16 11:57:45 CEST 2023


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 <c.ebner at proxmox.com>
---

changes since v1:
* rebased to current master

changes since v2:
* Capture `err` in match pattern
* Deduplicate code by introducing `dolink` closure

 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(())
     }
-- 
2.39.2






More information about the pbs-devel mailing list