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