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