2
0
Эх сурвалжийг харах

Merge pull request #7750 from ThomasWaldmann/fix-unreadable-parent-of-root-master

do not try to read parent dir of recursion root (master)
TW 1 жил өмнө
parent
commit
1c8da8f98a

+ 23 - 34
src/borg/archiver/create_cmd.py

@@ -24,7 +24,7 @@ from ..helpers import get_cache_dir, os_stat
 from ..helpers import dir_is_tagged
 from ..helpers import log_multi
 from ..helpers import basic_json_data, json_print
-from ..helpers import flags_root, flags_dir, flags_special_follow, flags_special
+from ..helpers import flags_dir, flags_special_follow, flags_special
 from ..helpers import sig_int, ignore_sigint
 from ..helpers import iter_separated
 from ..helpers import MakePathSafeAction
@@ -156,41 +156,30 @@ class CreateMixIn:
                             fso.stats.files_stats[status] += 1
                         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:

+ 1 - 1
src/borg/helpers/__init__.py

@@ -15,7 +15,7 @@ from .fs import ensure_dir, join_base_dir, get_socket_filename
 from .fs import get_security_dir, get_keys_dir, get_base_dir, get_cache_dir, get_config_dir, get_runtime_dir
 from .fs import dir_is_tagged, dir_is_cachedir, remove_dotdot_prefixes, make_path_safe, scandir_inorder
 from .fs import secure_erase, safe_unlink, dash_open, os_open, os_stat, umount
-from .fs import O_, flags_root, flags_dir, flags_special_follow, flags_special, flags_base, flags_normal, flags_noatime
+from .fs import O_, flags_dir, flags_special_follow, flags_special, flags_base, flags_normal, flags_noatime
 from .fs import HardLinkManager
 from .misc import sysinfo, log_multi, consume
 from .misc import ChunkIteratorFileWrapper, open_item, chunkit, iter_separated, ErrorIgnoringTextIOWrapper

+ 0 - 1
src/borg/helpers/fs.py

@@ -450,7 +450,6 @@ flags_special = flags_base | O_("NOFOLLOW")  # BLOCK == wait when reading device
 flags_special_follow = flags_base  # BLOCK == wait when reading symlinked devices or fifos
 flags_normal = flags_base | O_("NONBLOCK", "NOFOLLOW")
 flags_noatime = flags_normal | O_("NOATIME")
-flags_root = O_("RDONLY")
 flags_dir = O_("DIRECTORY", "RDONLY", "NOFOLLOW")
 
 

+ 11 - 0
src/borg/testsuite/archiver/create_cmd.py

@@ -167,6 +167,17 @@ def test_create_duplicate_root(archivers, request):
     assert sorted(paths) == ["input", "input/a", "input/a/hardlink", "input/b", "input/b/hardlink"]
 
 
+def test_create_unreadable_parent(archiver):
+    parent_dir = os.path.join(archiver.input_path, "parent")
+    root_dir = os.path.join(archiver.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
+    cmd(archiver, "rcreate", "--encryption=none")
+    # issue #7746: we *can* read root_dir and we *can* traverse parent_dir, so this should work:
+    cmd(archiver, "create", "test", root_dir)
+
+
 @pytest.mark.skipif(is_win32, reason="unix sockets not available on windows")
 def test_unix_socket(archivers, request, monkeypatch):
     archiver = request.getfixturevalue(archivers)