1from tarfile import TarFile, \
2    TarInfo
3import os
4import stat
5from itertools import chain
6from .misc import filehash
7
8
9def normalized_recursive_directory_iterator(path):
10    Q = iter([ path ])
11    name = next(Q, None)
12    while name:
13        st = os.lstat(name)
14        if stat.S_ISDIR(st.st_mode):
15            L = sorted(os.listdir(name))
16            def create_map(name, L):
17                return map(lambda x: os.path.join(name, x), L)
18            L = create_map(name, L)
19            yield (os.path.relpath(name, path), name, st.st_mode, st.st_uid, st.st_gid, st.st_size, st.st_mtime)
20            Q = chain(L, Q)
21        else: # file
22            yield (os.path.relpath(name, path), name, st.st_mode, st.st_uid, st.st_gid, st.st_size, st.st_mtime)
23        name = next(Q, None)
24
25
26def _export_diff(mountpoint, origin_mountpoint, output_directory):
27
28    mit = normalized_recursive_directory_iterator(mountpoint)
29    oit = normalized_recursive_directory_iterator(origin_mountpoint)
30
31    def emit_removal(o):
32        print('Removal:', o[0])
33
34    def emit_creation(m):
35        print('Creation:', m[0])
36
37    def emit_compare(m, o):
38        if m[2] != o[2] or m[3] != o[3] or m[4] != o[4]:
39            print('Metadata change:', o[0])
40        elif stat.S_ISREG(m[2]) and m[5] != o[5]:
41            print('Size change:', o[0])
42        elif stat.S_ISREG(m[2]) and m[6] != o[6] and filehash(m[1]) != filehash(o[1]):
43            print('Content change:', o[0])
44        else: # no change
45            pass # print('Compare:', m[0])
46
47    m = next(mit, None)
48    o = next(oit, None)
49    while True:
50        if m is None and o is None:
51            break
52        elif m is None and o is not None:
53            emit_removal(o)
54        elif m is not None and o is None:
55            emit_creation(m)
56        elif m[0] < o[0]:
57            emit_creation(m)
58            m = next(mit, None)
59        elif m[0] > o[0]:
60            emit_removal(o)
61            o = next(oit, None)
62        elif stat.S_ISDIR(m[2]) != stat.S_ISDIR(o[2]):
63            emit_creation(m)
64            m = next(mit, None)
65            o = next(oit, None)
66        else:
67            emit_compare(m, o)
68            m = next(mit, None)
69            o = next(oit, None)
70
71    #for root, dirs, files in os.walk(path):
72     #  dirs.sort()
73      # for dirname in dirs:
74        #    print(os.path.join(root, dirname))
75
76
77def command_export(args):
78    name, _ = zfs_find(args.reference, focker_type='image')
79    mountpoint = zfs_mountpoint(name)
80    origin = zfs_parse_output(['zfs', 'get', '-H', 'origin', name])
81    origin = origin[2].split('@')[0]
82    origin_mountpoint = zfs_mountpoint(origin)
83    Q = [ zfs_mountpoint(name) ]
84    while len(Q) > 0:
85        el = Q.pop()
86