]> arthur.barton.de Git - bup.git/blob - lib/bup/drecurse.py
ba41a12758268da40509726926c818efa7208250
[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 def _dirlist():
41     l = []
42     for n in os.listdir(b'.'):
43         try:
44             st = xstat.lstat(n)
45         except OSError as e:
46             add_error(Exception('%s: %s' % (resolve_parent(n), str(e))))
47             continue
48         if stat.S_ISDIR(st.st_mode):
49             n += b'/'
50         l.append((n,st))
51     l.sort(reverse=True)
52     return l
53
54 def _recursive_dirlist(prepend, xdev, bup_dir=None,
55                        excluded_paths=None,
56                        exclude_rxs=None,
57                        xdev_exceptions=frozenset()):
58     for (name,pst) in _dirlist():
59         path = prepend + name
60         if excluded_paths:
61             if os.path.normpath(path) in excluded_paths:
62                 debug1('Skipping %r: excluded.\n' % path_msg(path))
63                 continue
64         if exclude_rxs and should_rx_exclude_path(path, exclude_rxs):
65             continue
66         if name.endswith(b'/'):
67             if bup_dir != None:
68                 if os.path.normpath(path) == bup_dir:
69                     debug1('Skipping BUP_DIR.\n')
70                     continue
71             if xdev != None and pst.st_dev != xdev \
72                and path not in xdev_exceptions:
73                 debug1('Skipping contents of %r: different filesystem.\n'
74                        % path_msg(path))
75             else:
76                 try:
77                     OsFile(name).fchdir()
78                 except OSError as e:
79                     add_error('%s: %s' % (prepend, e))
80                 else:
81                     for i in _recursive_dirlist(prepend=prepend+name, xdev=xdev,
82                                                 bup_dir=bup_dir,
83                                                 excluded_paths=excluded_paths,
84                                                 exclude_rxs=exclude_rxs,
85                                                 xdev_exceptions=xdev_exceptions):
86                         yield i
87                     os.chdir(b'..')
88         yield (path, pst)
89
90
91 def recursive_dirlist(paths, xdev, bup_dir=None,
92                       excluded_paths=None,
93                       exclude_rxs=None,
94                       xdev_exceptions=frozenset()):
95     startdir = OsFile(b'.')
96     try:
97         assert(type(paths) != type(''))
98         for path in paths:
99             try:
100                 pst = xstat.lstat(path)
101                 if stat.S_ISLNK(pst.st_mode):
102                     yield (path, pst)
103                     continue
104             except OSError as e:
105                 add_error('recursive_dirlist: %s' % e)
106                 continue
107             try:
108                 pfile = OsFile(path)
109             except OSError as e:
110                 add_error(e)
111                 continue
112             pst = pfile.stat()
113             if xdev:
114                 xdev = pst.st_dev
115             else:
116                 xdev = None
117             if stat.S_ISDIR(pst.st_mode):
118                 pfile.fchdir()
119                 prepend = os.path.join(path, b'')
120                 for i in _recursive_dirlist(prepend=prepend, xdev=xdev,
121                                             bup_dir=bup_dir,
122                                             excluded_paths=excluded_paths,
123                                             exclude_rxs=exclude_rxs,
124                                             xdev_exceptions=xdev_exceptions):
125                     yield i
126                 startdir.fchdir()
127             else:
128                 prepend = path
129             yield (prepend,pst)
130     except:
131         try:
132             startdir.fchdir()
133         except:
134             pass
135         raise