]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/scripts/debug.py
decentralized directories: integration and testing
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / scripts / debug.py
1
2 # do not import any allmydata modules at this level. Do that from inside
3 # individual functions instead.
4 import sys, struct, time
5 from twisted.python import usage
6
7 class DumpOptions(usage.Options):
8     optParameters = [
9         ["filename", "f", None, "which file to dump"],
10         ]
11
12     def parseArgs(self, filename=None):
13         if filename:
14             self['filename'] = filename
15
16     def postOptions(self):
17         if not self['filename']:
18             raise usage.UsageError("<filename> parameter is required")
19
20 def dump_share(config, out=sys.stdout, err=sys.stderr):
21     from allmydata import uri, storage
22
23     # check the version, to see if we have a mutable or immutable share
24     f = open(config['filename'], "rb")
25     prefix = f.read(32)
26     f.close()
27     if prefix == storage.MutableShareFile.MAGIC:
28         return dump_mutable_share(config, out, err)
29     # otherwise assume it's immutable
30     f = storage.ShareFile(config['filename'])
31     # use a ReadBucketProxy to parse the bucket and find the uri extension
32     bp = storage.ReadBucketProxy(None)
33     offsets = bp._parse_offsets(f.read_share_data(0, 0x24))
34     seek = offsets['uri_extension']
35     length = struct.unpack(">L", f.read_share_data(seek, 4))[0]
36     seek += 4
37     data = f.read_share_data(seek, length)
38
39     unpacked = uri.unpack_extension_readable(data)
40     keys1 = ("size", "num_segments", "segment_size",
41              "needed_shares", "total_shares")
42     keys2 = ("codec_name", "codec_params", "tail_codec_params")
43     keys3 = ("plaintext_hash", "plaintext_root_hash",
44              "crypttext_hash", "crypttext_root_hash",
45              "share_root_hash")
46     display_keys = {"size": "file_size"}
47     for k in keys1:
48         if k in unpacked:
49             dk = display_keys.get(k, k)
50             print >>out, "%19s: %s" % (dk, unpacked[k])
51     print >>out
52     for k in keys2:
53         if k in unpacked:
54             dk = display_keys.get(k, k)
55             print >>out, "%19s: %s" % (dk, unpacked[k])
56     print >>out
57     for k in keys3:
58         if k in unpacked:
59             dk = display_keys.get(k, k)
60             print >>out, "%19s: %s" % (dk, unpacked[k])
61
62     leftover = set(unpacked.keys()) - set(keys1 + keys2 + keys3)
63     if leftover:
64         print >>out
65         print >>out, "LEFTOVER:"
66         for k in sorted(leftover):
67             print >>out, "%s: %s" % (k, unpacked[k])
68
69     sizes = {}
70     sizes['data'] = bp._data_size
71     sizes['validation'] = (offsets['uri_extension'] -
72                            offsets['plaintext_hash_tree'])
73     sizes['uri-extension'] = len(data)
74     print >>out
75     print >>out, "Size of data within the share:"
76     for k in sorted(sizes):
77         print >>out, "%19s: %s" % (k, sizes[k])
78
79     # display lease information too
80     leases = list(f.iter_leases())
81     if leases:
82         for i,lease in enumerate(leases):
83             (owner_num, renew_secret, cancel_secret, expiration_time) = lease
84             when = format_expiration_time(expiration_time)
85             print >>out, "Lease #%d: owner=%d, expire in %s" % (i, owner_num,
86                                                                 when)
87     else:
88         print >>out, "No leases."
89
90     print >>out
91     return 0
92
93 def format_expiration_time(expiration_time):
94     now = time.time()
95     remains = expiration_time - now
96     when = "%ds" % remains
97     if remains > 24*3600:
98         when += " (%d days)" % (remains / (24*3600))
99     elif remains > 3600:
100         when += " (%d hours)" % (remains / 3600)
101     return when
102
103
104 def dump_mutable_share(config, out, err):
105     from allmydata import storage
106     from allmydata.util import idlib
107     m = storage.MutableShareFile(config['filename'])
108     f = open(config['filename'], "rb")
109     WE, nodeid = m._read_write_enabler_and_nodeid(f)
110     num_extra_leases = m._read_num_extra_leases(f)
111     data_length = m._read_data_length(f)
112     extra_lease_offset = m._read_extra_lease_offset(f)
113     container_size = extra_lease_offset - m.DATA_OFFSET
114     leases = list(m._enumerate_leases(f))
115
116     share_type = "unknown"
117     f.seek(m.DATA_OFFSET)
118     if f.read(1) == "\x00":
119         # this slot contains an SMDF share
120         share_type = "SDMF"
121     f.close()
122
123     print >>out
124     print >>out, "Mutable slot found:"
125     print >>out, " share_type: %s" % share_type
126     print >>out, " write_enabler: %s" % idlib.b2a(WE)
127     print >>out, " WE for nodeid: %s" % idlib.nodeid_b2a(nodeid)
128     print >>out, " num_extra_leases: %d" % num_extra_leases
129     print >>out, " container_size: %d" % container_size
130     print >>out, " data_length: %d" % data_length
131     if leases:
132         for (leasenum, (oid,et,rs,cs,anid)) in leases:
133             print >>out
134             print >>out, " Lease #%d:" % leasenum
135             print >>out, "  ownerid: %d" % oid
136             when = format_expiration_time(et)
137             print >>out, "  expires in %s" % when
138             print >>out, "  renew_secret: %s" % idlib.b2a(rs)
139             print >>out, "  cancel_secret: %s" % idlib.b2a(cs)
140             print >>out, "  secrets are for nodeid: %s" % idlib.nodeid_b2a(anid)
141     else:
142         print >>out, "No leases."
143     print >>out
144
145     if share_type == "SDMF":
146         dump_SDMF_share(m.DATA_OFFSET, data_length, config, out, err)
147
148     return 0
149
150 def dump_SDMF_share(offset, length, config, out, err):
151     from allmydata import mutable
152     from allmydata.util import idlib
153
154     f = open(config['filename'], "rb")
155     f.seek(offset)
156     data = f.read(min(length, 2000))
157     f.close()
158
159     try:
160         pieces = mutable.unpack_share(data)
161     except mutable.NeedMoreDataError, e:
162         # retry once with the larger size
163         size = e.needed_bytes
164         f = open(config['filename'], "rb")
165         f.seek(offset)
166         data = f.read(min(length, size))
167         f.close()
168         pieces = mutable.unpack_share(data)
169
170     (seqnum, root_hash, IV, k, N, segsize, datalen,
171      pubkey, signature, share_hash_chain, block_hash_tree,
172      share_data, enc_privkey) = pieces
173
174     print >>out, " SDMF contents:"
175     print >>out, "  seqnum: %d" % seqnum
176     print >>out, "  root_hash: %s" % idlib.b2a(root_hash)
177     print >>out, "  IV: %s" % idlib.b2a(IV)
178     print >>out, "  required_shares: %d" % k
179     print >>out, "  total_shares: %d" % N
180     print >>out, "  segsize: %d" % segsize
181     print >>out, "  datalen: %d" % datalen
182     share_hash_ids = ",".join([str(hid) for (hid,hash) in share_hash_chain])
183     print >>out, "  share_hash_chain: %s" % share_hash_ids
184     print >>out, "  block_hash_tree: %d nodes" % len(block_hash_tree)
185
186     print >>out
187
188
189 subCommands = [
190     ["dump-share", None, DumpOptions,
191      "Unpack and display the contents of a share (uri_extension and leases)."],
192     ]
193
194 dispatch = {
195     "dump-share": dump_share,
196     }