fix openat/statat issues for root directory, fixes #4405

basename('/') == '' and we can't use that for openat/statat.
also, there is no parent FD for the root directory...
This commit is contained in:
Thomas Waldmann 2019-02-26 11:18:37 +01:00
parent 009bac2e9f
commit db2b385a8b
2 changed files with 31 additions and 5 deletions

View File

@ -50,7 +50,7 @@ from .helpers import format_timedelta, format_file_size, parse_file_size, format
from .helpers import safe_encode, remove_surrogates, bin_to_hex, prepare_dump_dict
from .helpers import interval, prune_within, prune_split, PRUNING_PATTERNS
from .helpers import timestamp
from .helpers import get_cache_dir
from .helpers import get_cache_dir, os_stat
from .helpers import Manifest, AI_HUMAN_SORT_KEYS
from .helpers import hardlinkable
from .helpers import StableDict
@ -486,9 +486,11 @@ class Archiver:
path = os.path.normpath(path)
parent_dir = os.path.dirname(path) or '.'
name = os.path.basename(path)
# 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(name, dir_fd=parent_fd, follow_symlinks=False)
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
@ -565,7 +567,7 @@ class Archiver:
recurse_excluded_dir = False
if matcher.match(path):
with backup_io('stat'):
st = os.stat(name, dir_fd=parent_fd, follow_symlinks=False)
st = os_stat(path=path, parent_fd=parent_fd, name=name, follow_symlinks=False)
else:
self.print_file_status('x', path)
# get out here as quickly as possible:
@ -575,7 +577,7 @@ class Archiver:
if not matcher.recurse_dir:
return
with backup_io('stat'):
st = os.stat(name, dir_fd=parent_fd, follow_symlinks=False)
st = os_stat(path=path, parent_fd=parent_fd, name=name, follow_symlinks=False)
recurse_excluded_dir = stat.S_ISDIR(st.st_mode)
if not recurse_excluded_dir:
return

View File

@ -224,7 +224,11 @@ def os_open(*, flags, path=None, parent_fd=None, name=None, noatime=False):
:param noatime: True if access time shall be preserved
:return: file descriptor
"""
fname = name if name is not None and parent_fd is not None else path
if name and parent_fd is not None:
# name is neither None nor empty, parent_fd given.
fname = name # use name relative to parent_fd
else:
fname, parent_fd = path, None # just use the path
_flags_normal = flags
if noatime:
_flags_noatime = _flags_normal | O_('NOATIME')
@ -242,6 +246,26 @@ def os_open(*, flags, path=None, parent_fd=None, name=None, noatime=False):
return fd
def os_stat(*, path=None, parent_fd=None, name=None, follow_symlinks=False):
"""
Use os.stat to open a fs item.
If parent_fd and name are given, they are preferred and statat will be used,
path is not used in this case.
:param path: full (but not necessarily absolute) path
:param parent_fd: open directory file descriptor
:param name: name relative to parent_fd
:return: stat info
"""
if name and parent_fd is not None:
# name is neither None nor empty, parent_fd given.
fname = name # use name relative to parent_fd
else:
fname, parent_fd = path, None # just use the path
return os.stat(fname, dir_fd=parent_fd, follow_symlinks=follow_symlinks)
def umount(mountpoint):
env = prepare_subprocess_env(system=True)
try: