Browse Source

move/refactor Archive._open_rb function to helpers.os_open

also:
- add and use OsOpen context manager
- add O_NONBLOCK, O_NOFOLLOW, O_NOCTTY (inspired by gnu tar)
Thomas Waldmann 7 years ago
parent
commit
8220c6eac8
3 changed files with 44 additions and 21 deletions
  1. 12 20
      src/borg/archive.py
  2. 30 0
      src/borg/helpers/fs.py
  3. 2 1
      src/borg/testsuite/archiver.py

+ 12 - 20
src/borg/archive.py

@@ -38,6 +38,7 @@ from .helpers import StableDict
 from .helpers import bin_to_hex
 from .helpers import bin_to_hex
 from .helpers import safe_ns
 from .helpers import safe_ns
 from .helpers import ellipsis_truncate, ProgressIndicatorPercent, log_multi
 from .helpers import ellipsis_truncate, ProgressIndicatorPercent, log_multi
+from .helpers import os_open, flags_normal
 from .helpers import msgpack
 from .helpers import msgpack
 from .patterns import PathPrefixPattern, FnmatchPattern, IECommand
 from .patterns import PathPrefixPattern, FnmatchPattern, IECommand
 from .item import Item, ArchiveItem, ItemDiff
 from .item import Item, ArchiveItem, ItemDiff
@@ -47,9 +48,6 @@ from .repository import Repository, LIST_SCAN_LIMIT
 
 
 has_lchmod = hasattr(os, 'lchmod')
 has_lchmod = hasattr(os, 'lchmod')
 
 
-flags_normal = os.O_RDONLY | getattr(os, 'O_BINARY', 0)
-flags_noatime = flags_normal | getattr(os, 'O_NOATIME', 0)
-
 
 
 class Statistics:
 class Statistics:
 
 
@@ -197,6 +195,16 @@ def backup_io_iter(iterator):
         yield item
         yield item
 
 
 
 
+@contextmanager
+def OsOpen(path, flags, noatime=False, op='open'):
+    with backup_io(op):
+        fd = os_open(path, flags, noatime)
+    try:
+        yield fd
+    finally:
+        os.close(fd)
+
+
 class DownloadPipeline:
 class DownloadPipeline:
 
 
     def __init__(self, repository, key):
     def __init__(self, repository, key):
@@ -826,18 +834,6 @@ Utilization of max. archive size: {csize_max:.0%}
             logger.warning('forced deletion succeeded, but the deleted archive was corrupted.')
             logger.warning('forced deletion succeeded, but the deleted archive was corrupted.')
             logger.warning('borg check --repair is required to free all space.')
             logger.warning('borg check --repair is required to free all space.')
 
 
-    @staticmethod
-    def _open_rb(path):
-        try:
-            # if we have O_NOATIME, this likely will succeed if we are root or owner of file:
-            return os.open(path, flags_noatime)
-        except PermissionError:
-            if flags_noatime == flags_normal:
-                # we do not have O_NOATIME, no need to try again:
-                raise
-            # Was this EPERM due to the O_NOATIME flag? Try again without it:
-            return os.open(path, flags_normal)
-
     @staticmethod
     @staticmethod
     def compare_archives_iter(archive1, archive2, matcher=None, can_compare_chunk_ids=False):
     def compare_archives_iter(archive1, archive2, matcher=None, can_compare_chunk_ids=False):
         """
         """
@@ -1126,9 +1122,7 @@ class FilesystemObjectProcessors:
 
 
     def process_file(self, path, st, cache):
     def process_file(self, path, st, cache):
         with self.create_helper(path, st, None) as (item, status, hardlinked, hardlink_master):  # no status yet
         with self.create_helper(path, st, None) as (item, status, hardlinked, hardlink_master):  # no status yet
-            with backup_io('open'):
-                fd = Archive._open_rb(path)
-            try:
+            with OsOpen(path, flags_normal, noatime=True) as fd:
                 with backup_io('fstat'):
                 with backup_io('fstat'):
                     curr_st = os.fstat(fd)
                     curr_st = os.fstat(fd)
                 # XXX do some checks here: st vs. curr_st
                 # XXX do some checks here: st vs. curr_st
@@ -1178,8 +1172,6 @@ class FilesystemObjectProcessors:
                     # we processed a special file like a regular file. reflect that in mode,
                     # we processed a special file like a regular file. reflect that in mode,
                     # so it can be extracted / accessed in FUSE mount like a regular file:
                     # so it can be extracted / accessed in FUSE mount like a regular file:
                     item.mode = stat.S_IFREG | stat.S_IMODE(item.mode)
                     item.mode = stat.S_IFREG | stat.S_IMODE(item.mode)
-            finally:
-                os.close(fd)
             return status
             return status
 
 
 
 

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

@@ -189,6 +189,36 @@ def dash_open(path, mode):
         return open(path, mode)
         return open(path, mode)
 
 
 
 
+def O_(*flags):
+    result = 0
+    for flag in flags:
+        result |= getattr(os, 'O_' + flag, 0)
+    return result
+
+
+flags_base = O_('BINARY', 'NONBLOCK', 'NOCTTY')  # later: add 'NOFOLLOW'
+flags_normal = flags_base | O_('RDONLY')
+flags_noatime = flags_normal | O_('NOATIME')
+
+
+def os_open(path, flags, noatime=False):
+    _flags_normal = flags
+    if noatime:
+        _flags_noatime = _flags_normal | O_('NOATIME')
+        try:
+            # if we have O_NOATIME, this likely will succeed if we are root or owner of file:
+            fd = os.open(path, _flags_noatime)
+        except PermissionError:
+            if _flags_noatime == _flags_normal:
+                # we do not have O_NOATIME, no need to try again:
+                raise
+            # Was this EPERM due to the O_NOATIME flag? Try again without it:
+            fd = os.open(path, _flags_normal)
+    else:
+        fd = os.open(path, _flags_normal)
+    return fd
+
+
 def umount(mountpoint):
 def umount(mountpoint):
     env = prepare_subprocess_env(system=True)
     env = prepare_subprocess_env(system=True)
     try:
     try:

+ 2 - 1
src/borg/testsuite/archiver.py

@@ -32,7 +32,7 @@ except ImportError:
 
 
 import borg
 import borg
 from .. import xattr, helpers, platform
 from .. import xattr, helpers, platform
-from ..archive import Archive, ChunkBuffer, flags_noatime, flags_normal
+from ..archive import Archive, ChunkBuffer
 from ..archiver import Archiver, parse_storage_quota
 from ..archiver import Archiver, parse_storage_quota
 from ..cache import Cache, LocalCache
 from ..cache import Cache, LocalCache
 from ..constants import *  # NOQA
 from ..constants import *  # NOQA
@@ -46,6 +46,7 @@ from ..helpers import EXIT_SUCCESS, EXIT_WARNING, EXIT_ERROR
 from ..helpers import bin_to_hex
 from ..helpers import bin_to_hex
 from ..helpers import MAX_S
 from ..helpers import MAX_S
 from ..helpers import msgpack
 from ..helpers import msgpack
+from ..helpers import flags_noatime, flags_normal
 from ..nanorst import RstToTextLazy, rst_to_terminal
 from ..nanorst import RstToTextLazy, rst_to_terminal
 from ..patterns import IECommand, PatternMatcher, parse_pattern
 from ..patterns import IECommand, PatternMatcher, parse_pattern
 from ..item import Item, ItemDiff
 from ..item import Item, ItemDiff