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