X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fbup%2Fdrecurse.py;h=a5a04e9a424f785e7fb97041d91a2121362cdc0f;hb=6a5162606f8c6e68539792b240cce085c4816636;hp=ac0115e9f1685c965fad86bf883e44a045a72383;hpb=94c3b43be36773624aa073583806751bb97ba023;p=bup.git diff --git a/lib/bup/drecurse.py b/lib/bup/drecurse.py index ac0115e..a5a04e9 100644 --- a/lib/bup/drecurse.py +++ b/lib/bup/drecurse.py @@ -1,102 +1,130 @@ -import stat -from bup.helpers import * -try: - O_LARGEFILE = os.O_LARGEFILE -except AttributeError: - O_LARGEFILE = 0 +from __future__ import absolute_import +import stat, os +from bup.helpers \ + import (add_error, + debug1, + finalized, + resolve_parent, + should_rx_exclude_path) +from bup.io import path_msg +import bup.xstat as xstat # the use of fchdir() and lstat() is for two reasons: # - help out the kernel by not making it repeatedly look up the absolute path # - avoid race conditions caused by doing listdir() on a changing symlink -class OsFile: - def __init__(self, path): - self.fd = None - self.fd = os.open(path, - os.O_RDONLY|O_LARGEFILE|os.O_NOFOLLOW|os.O_NDELAY) - - def __del__(self): - if self.fd: - fd = self.fd - self.fd = None - os.close(fd) - def fchdir(self): - os.fchdir(self.fd) +try: + O_LARGEFILE = os.O_LARGEFILE +except AttributeError: + O_LARGEFILE = 0 +try: + O_NOFOLLOW = os.O_NOFOLLOW +except AttributeError: + O_NOFOLLOW = 0 - def stat(self): - return os.fstat(self.fd) + +def finalized_fd(path): + fd = os.open(path, os.O_RDONLY|O_LARGEFILE|O_NOFOLLOW|os.O_NDELAY) + return finalized(fd, lambda x: os.close(x)) -_IFMT = stat.S_IFMT(0xffffffff) # avoid function call in inner loop def _dirlist(): l = [] - for n in os.listdir('.'): + for n in os.listdir(b'.'): try: - st = os.lstat(n) - except OSError, e: - add_error(Exception('%s: %s' % (realpath(n), str(e)))) + st = xstat.lstat(n) + except OSError as e: + add_error(Exception('%s: %s' % (resolve_parent(n), str(e)))) continue - if (st.st_mode & _IFMT) == stat.S_IFDIR: - n += '/' + if stat.S_ISDIR(st.st_mode): + n += b'/' l.append((n,st)) l.sort(reverse=True) return l - -def _recursive_dirlist(prepend, xdev): +def _recursive_dirlist(prepend, xdev, bup_dir=None, + excluded_paths=None, + exclude_rxs=None, + xdev_exceptions=frozenset()): for (name,pst) in _dirlist(): - if name.endswith('/'): - if xdev != None and pst.st_dev != xdev: - log('Skipping %r: different filesystem.\n' % (prepend+name)) + path = prepend + name + if excluded_paths: + if os.path.normpath(path) in excluded_paths: + debug1('Skipping %r: excluded.\n' % path_msg(path)) continue - try: - OsFile(name).fchdir() - except OSError, e: - add_error('%s: %s' % (prepend, e)) + if exclude_rxs and should_rx_exclude_path(path, exclude_rxs): + continue + if name.endswith(b'/'): + if bup_dir != None: + if os.path.normpath(path) == bup_dir: + debug1('Skipping BUP_DIR.\n') + continue + if xdev != None and pst.st_dev != xdev \ + and path not in xdev_exceptions: + debug1('Skipping contents of %r: different filesystem.\n' + % path_msg(path)) else: - for i in _recursive_dirlist(prepend=prepend+name, xdev=xdev): - yield i - os.chdir('..') - yield (prepend + name, pst) + try: + with finalized_fd(name) as fd: + os.fchdir(fd) + except OSError as e: + add_error('%s: %s' % (prepend, e)) + else: + for i in _recursive_dirlist(prepend=prepend+name, xdev=xdev, + bup_dir=bup_dir, + excluded_paths=excluded_paths, + exclude_rxs=exclude_rxs, + xdev_exceptions=xdev_exceptions): + yield i + os.chdir(b'..') + yield (path, pst) -def recursive_dirlist(paths, xdev): - startdir = OsFile('.') - try: - assert(type(paths) != type('')) - for path in paths: - try: - pst = os.lstat(path) - if stat.S_ISLNK(pst.st_mode): - yield (path, pst) - continue - except OSError, e: - add_error('recursive_dirlist: %s' % e) - continue - try: - pfile = OsFile(path) - except OSError, e: - add_error(e) - continue - pst = pfile.stat() - if xdev: - xdev = pst.st_dev - else: - xdev = None - if stat.S_ISDIR(pst.st_mode): - pfile.fchdir() - prepend = os.path.join(path, '') - for i in _recursive_dirlist(prepend=prepend, xdev=xdev): - yield i - startdir.fchdir() - else: - prepend = path - yield (prepend,pst) - except: +def recursive_dirlist(paths, xdev, bup_dir=None, + excluded_paths=None, + exclude_rxs=None, + xdev_exceptions=frozenset()): + with finalized_fd(b'.') as startdir: try: - startdir.fchdir() + assert not isinstance(paths, str) + for path in paths: + try: + pst = xstat.lstat(path) + if stat.S_ISLNK(pst.st_mode): + yield (path, pst) + continue + except OSError as e: + add_error('recursive_dirlist: %s' % e) + continue + try: + opened_pfile = finalized_fd(path) + except OSError as e: + add_error(e) + continue + with opened_pfile as pfile: + pst = xstat.fstat(pfile) + if xdev: + xdev = pst.st_dev + else: + xdev = None + if stat.S_ISDIR(pst.st_mode): + os.fchdir(pfile) + prepend = os.path.join(path, b'') + for i in _recursive_dirlist(prepend=prepend, xdev=xdev, + bup_dir=bup_dir, + excluded_paths=excluded_paths, + exclude_rxs=exclude_rxs, + xdev_exceptions=xdev_exceptions): + yield i + os.fchdir(startdir) + else: + prepend = path + yield (prepend,pst) except: - pass - raise + try: + os.fchdir(startdir) + except: + pass + raise