3 # Directory integrity scanner.
8 from os
.path
import join
10 from cPickle
import dump
, load
14 """Root of directory generator"""
15 topstat
= os
.lstat(top
)
16 for x
in walker(top
, '.', topstat
):
19 def walker(path
, name
, topstat
):
20 """Directory tree generator.
22 At one point, this started as a copy of os.walk from Python's
23 library. Even the arguments are different now.
27 names
= os
.listdir(path
)
29 sys
.stderr
.write("Warning, can't read dir: %s\n" % path
)
32 # The verification algorithm requires the names to be sorted.
35 # Stat each name found, and put the result in one of two lists.
36 dirs
, nondirs
= [], []
38 st
= os
.lstat(join(path
, onename
))
39 if S_ISDIR(st
.st_mode
):
40 dirs
.append((onename
, st
))
42 nondirs
.append((onename
, st
))
44 # Indicate "entering" the directory.
47 # Then recursively walk into all of the subdirectories.
48 for (onename
, st
) in dirs
:
49 subpath
= join(path
, onename
)
50 if st
.st_dev
== topstat
.st_dev
:
51 for x
in walker(subpath
, onename
, topstat
):
54 # Then yield each entry that is not a subdirectory.
55 for (onename
, st
) in nondirs
:
58 # Last, yield the leaving.
61 def compare_trees(prior
, cur
):
62 """Compare two scanned trees."""
65 raise "Scan doesn't start with a directory"
68 raise "Tree walk doesn't start with a directory"
70 # We don't concern ourselves with whether the names are the same
74 def comp_walk(prior
, cur
, depth
=1):
77 Recursively walks both the "prior" and "cur" directories,
78 comparing the trees found inside. Returns when each has left this
85 # print "Comparing (%d) %s and %s" % (depth, a, b)
87 # Both are 'leave' nodes.
88 if a
[0] == 'u' and b
[0] == 'u':
92 if a
[0] == 'd' and b
[0] == 'd':
93 # Both are looking at a child subdirectory.
96 # Same name, just walk this tree.
97 # print "...same dir, enter"
98 comp_walk(prior
, cur
, depth
+1)
104 # A directory has been deleted.
105 print "Delete dir: %s" % a
[1]
111 # A directory has been added.
112 print "Add dir: %s" % b
[1]
117 elif a
[0] == '-' and b
[0] == '-':
118 # Both are looking at a non-dir.
120 # Same name, all is well.
121 # print "...same file"
127 print "Delete nondir: %s" % a
[1]
132 print "Add nondir: %s" % b
[1]
136 elif a
[0] == '-' and b
[0] == 'u':
137 print "Delete nondir: %s" % a
[1]
141 elif a
[0] == 'u' and b
[0] == '-':
142 print "New nondir: %s" % b
[1]
146 elif a
[0] == 'd' and b
[0] == '-':
147 print "Delete dir: %s" % a
[1]
152 elif (a
[0] == '-' or a
[0] == 'u') and b
[0] == 'd':
153 print "Add dir: %s" % b
[1]
159 print "Unhandled case: prior: %s, cur: %s" % (a
[0], b
[0])
162 def consume_dir(iter):
163 """Consume entries until this directory has been exhausted"""
171 version
= 'Asure scan version 1.0'
174 """Iterate over a previously written dump"""
175 fd
= gzip
.open(path
, 'rb')
178 raise "incompatible version of asure file"
185 def writer(path
, tmppath
, iter):
186 """Write the given item (probably assembled iterator)"""
187 fd
= gzip
.open(tmppath
, 'wb')
188 dump(version
, fd
, -1)
192 os
.rename(tmppath
, path
)
195 """Perform a fresh scan of the filesystem"""
196 writer('asure.dat.gz', 'asure.0.gz', walk('.'))
199 """Perform a scan of the filesystem, and compare it with the scan
200 file. reports differences."""
201 prior
= reader('asure.dat.gz')
203 compare_trees(prior
, cur
)
208 if argv
[0] == 'scan':
210 elif argv
[0] == 'update':
212 elif argv
[0] == 'check':
214 elif argv
[0] == 'show':
215 for i
in reader('asure.dat.gz'):
219 print "Usage: asure {scan|update|check}"
222 if __name__
== '__main__':
225 #for info in walk('/home/davidb/wd/asure'):