]> arthur.barton.de Git - bup.git/blob - lib/bup/drecurse.py
get: adjust for python 3 and test there
[bup.git] / lib / bup / drecurse.py
1
2 from __future__ import absolute_import
3 import stat, os
4
5 from bup.helpers import add_error, should_rx_exclude_path, debug1, resolve_parent
6 from bup.io import path_msg
7 import bup.xstat as xstat
8
9
10 try:
11     O_LARGEFILE = os.O_LARGEFILE
12 except AttributeError:
13     O_LARGEFILE = 0
14 try:
15     O_NOFOLLOW = os.O_NOFOLLOW
16 except AttributeError:
17     O_NOFOLLOW = 0
18
19
20 # the use of fchdir() and lstat() is for two reasons:
21 #  - help out the kernel by not making it repeatedly look up the absolute path
22 #  - avoid race conditions caused by doing listdir() on a changing symlink
23 class OsFile:
24     def __init__(self, path):
25         self.fd = None
26         self.fd = os.open(path, os.O_RDONLY|O_LARGEFILE|O_NOFOLLOW|os.O_NDELAY)
27         
28     def __del__(self):
29         if self.fd:
30             fd = self.fd
31             self.fd = None
32             os.close(fd)
33
34     def fchdir(self):
35         os.fchdir(self.fd)
36
37     def stat(self):
38         return xstat.fstat(self.fd)
39
40
41 _IFMT = stat.S_IFMT(0xffffffff)  # avoid function call in inner loop
42 def _dirlist():
43     l = []
44     for n in os.listdir(b'.'):
45         try:
46             st = xstat.lstat(n)
47         except OSError as e:
48             add_error(Exception('%s: %s' % (resolve_parent(n), str(e))))
49             continue
50         if (st.st_mode & _IFMT) == stat.S_IFDIR:
51             n += b'/'
52         l.append((n,st))
53     l.sort(reverse=True)
54     return l
55
56
57 def _recursive_dirlist(prepend, xdev, bup_dir=None,
58                        excluded_paths=None,
59                        exclude_rxs=None,
60                        xdev_exceptions=frozenset()):
61     for (name,pst) in _dirlist():
62         path = prepend + name
63         if excluded_paths:
64             if os.path.normpath(path) in excluded_paths:
65                 debug1('Skipping %r: excluded.\n' % path_msg(path))
66                 continue
67         if exclude_rxs and should_rx_exclude_path(path, exclude_rxs):
68             continue
69         if name.endswith(b'/'):
70             if bup_dir != None:
71                 if os.path.normpath(path) == bup_dir:
72                     debug1('Skipping BUP_DIR.\n')
73                     continue
74             if xdev != None and pst.st_dev != xdev \
75                and path not in xdev_exceptions:
76                 debug1('Skipping contents of %r: different filesystem.\n'
77                        % path_msg(path))
78             else:
79                 try:
80                     OsFile(name).fchdir()
81                 except OSError as e:
82                     add_error('%s: %s' % (prepend, e))
83                 else:
84                     for i in _recursive_dirlist(prepend=prepend+name, xdev=xdev,
85                                                 bup_dir=bup_dir,
86                                                 excluded_paths=excluded_paths,
87                                                 exclude_rxs=exclude_rxs,
88                                                 xdev_exceptions=xdev_exceptions):
89                         yield i
90                     os.chdir(b'..')
91         yield (path, pst)
92
93
94 def recursive_dirlist(paths, xdev, bup_dir=None,
95                       excluded_paths=None,
96                       exclude_rxs=None,
97                       xdev_exceptions=frozenset()):
98     startdir = OsFile(b'.')
99     try:
100         assert(type(paths) != type(''))
101         for path in paths:
102             try:
103                 pst = xstat.lstat(path)
104                 if stat.S_ISLNK(pst.st_mode):
105                     yield (path, pst)
106                     continue
107             except OSError as e:
108                 add_error('recursive_dirlist: %s' % e)
109                 continue
110             try:
111                 pfile = OsFile(path)
112             except OSError as e:
113                 add_error(e)
114                 continue
115             pst = pfile.stat()
116             if xdev:
117                 xdev = pst.st_dev
118             else:
119                 xdev = None
120             if stat.S_ISDIR(pst.st_mode):
121                 pfile.fchdir()
122                 prepend = os.path.join(path, b'')
123                 for i in _recursive_dirlist(prepend=prepend, xdev=xdev,
124                                             bup_dir=bup_dir,
125                                             excluded_paths=excluded_paths,
126                                             exclude_rxs=exclude_rxs,
127                                             xdev_exceptions=xdev_exceptions):
128                     yield i
129                 startdir.fchdir()
130             else:
131                 prepend = path
132             yield (prepend,pst)
133     except:
134         try:
135             startdir.fchdir()
136         except:
137             pass
138         raise