Browse Source

Merge pull request #7749 from ThomasWaldmann/fix-unreadable-parent-of-root-1.2

do not try to read parent dir of recursion root (1.2-maint)
TW 1 year ago
parent
commit
ef52d13608
2 changed files with 22 additions and 23 deletions
  1. 12 23
      src/borg/archiver.py
  2. 10 0
      src/borg/testsuite/archiver.py

+ 12 - 23
src/borg/archiver.py

@@ -590,31 +590,20 @@ class Archiver:
                         self.print_file_status(status, path)
                         continue
                     path = os.path.normpath(path)
-                    parent_dir = os.path.dirname(path) or '.'
-                    name = os.path.basename(path)
                     try:
-                        # note: for path == '/':  name == '' and parent_dir == '/'.
-                        # the empty name will trigger a fall-back to path-based processing in os_stat and os_open.
-                        with OsOpen(path=parent_dir, flags=flags_root, noatime=True, op='open_root') as parent_fd:
-                            try:
-                                st = os_stat(path=path, parent_fd=parent_fd, name=name, follow_symlinks=False)
-                            except OSError as e:
-                                self.print_warning('%s: %s', path, e)
-                                continue
-                            if args.one_file_system:
-                                restrict_dev = st.st_dev
-                            else:
-                                restrict_dev = None
-                            self._rec_walk(path=path, parent_fd=parent_fd, name=name,
-                                           fso=fso, cache=cache, matcher=matcher,
-                                           exclude_caches=args.exclude_caches, exclude_if_present=args.exclude_if_present,
-                                           keep_exclude_tags=args.keep_exclude_tags, skip_inodes=skip_inodes,
-                                           restrict_dev=restrict_dev, read_special=args.read_special, dry_run=dry_run)
-                            # if we get back here, we've finished recursing into <path>,
-                            # we do not ever want to get back in there (even if path is given twice as recursion root)
-                            skip_inodes.add((st.st_ino, st.st_dev))
+                        with backup_io('stat'):
+                            st = os_stat(path=path, parent_fd=None, name=None, follow_symlinks=False)
+                        restrict_dev = st.st_dev if args.one_file_system else None
+                        self._rec_walk(path=path, parent_fd=None, name=None,
+                                       fso=fso, cache=cache, matcher=matcher,
+                                       exclude_caches=args.exclude_caches, exclude_if_present=args.exclude_if_present,
+                                       keep_exclude_tags=args.keep_exclude_tags, skip_inodes=skip_inodes,
+                                       restrict_dev=restrict_dev, read_special=args.read_special, dry_run=dry_run)
+                        # if we get back here, we've finished recursing into <path>,
+                        # we do not ever want to get back in there (even if path is given twice as recursion root)
+                        skip_inodes.add((st.st_ino, st.st_dev))
                     except (BackupOSError, BackupError) as e:
-                        # this comes from OsOpen, self._rec_walk has own exception handler
+                        # this comes from os.stat, self._rec_walk has own exception handler
                         self.print_warning('%s: %s', path, e)
                         continue
             if not dry_run:

+ 10 - 0
src/borg/testsuite/archiver.py

@@ -487,6 +487,16 @@ class ArchiverTestCase(ArchiverTestCaseBase):
         self.cmd('init', '--encryption=none', '--make-parent-dirs', repository_location)
         assert os.path.exists(parent_path)
 
+    def test_create_unreadable_parent(self):
+        parent_dir = os.path.join(self.input_path, 'parent')
+        root_dir = os.path.join(self.input_path, 'parent', 'root')
+        os.mkdir(parent_dir)
+        os.mkdir(root_dir)
+        os.chmod(parent_dir, 0o111)  # --x--x--x == parent dir traversable, but not readable
+        self.cmd('init', '--encryption=none', self.repository_location)
+        # issue #7746: we *can* read root_dir and we *can* traverse parent_dir, so this should work:
+        self.cmd('create', self.repository_location + '::test', root_dir)
+
     def test_unix_socket(self):
         self.cmd('init', '--encryption=repokey', self.repository_location)
         try: