From d575ccba2864cbf701ff51ea76b7cf15b0f771ee Mon Sep 17 00:00:00 2001
From: Brian Warner <warner@lothar.com>
Date: Sun, 28 Aug 2011 01:09:31 -0700
Subject: [PATCH] Teach 'tahoe debug catalog-shares about MDMF. Closes #1507.

---
 src/allmydata/scripts/debug.py     | 34 +++++++++++++++++++++++++++++-
 src/allmydata/test/test_mutable.py | 20 ++++++++++++++++++
 2 files changed, 53 insertions(+), 1 deletion(-)

diff --git a/src/allmydata/scripts/debug.py b/src/allmydata/scripts/debug.py
index a507b7cf..16e57b33 100644
--- a/src/allmydata/scripts/debug.py
+++ b/src/allmydata/scripts/debug.py
@@ -728,9 +728,12 @@ def describe_share(abs_sharefile, si_s, shnum_s, now, out):
 
         share_type = "unknown"
         f.seek(m.DATA_OFFSET)
-        if f.read(1) == "\x00":
+        version = f.read(1)
+        if version == "\x00":
             # this slot contains an SMDF share
             share_type = "SDMF"
+        elif version == "\x01":
+            share_type = "MDMF"
 
         if share_type == "SDMF":
             f.seek(m.DATA_OFFSET)
@@ -752,6 +755,35 @@ def describe_share(abs_sharefile, si_s, shnum_s, now, out):
                   (si_s, k, N, datalen,
                    seqnum, base32.b2a(root_hash),
                    expiration, quote_output(abs_sharefile))
+        elif share_type == "MDMF":
+            from allmydata.mutable.layout import MDMFSlotReadProxy
+            fake_shnum = 0
+            # TODO: factor this out with dump_MDMF_share()
+            class ShareDumper(MDMFSlotReadProxy):
+                def _read(self, readvs, force_remote=False, queue=False):
+                    data = []
+                    for (where,length) in readvs:
+                        f.seek(m.DATA_OFFSET+where)
+                        data.append(f.read(length))
+                    return defer.succeed({fake_shnum: data})
+
+            p = ShareDumper(None, "fake-si", fake_shnum)
+            def extract(func):
+                stash = []
+                # these methods return Deferreds, but we happen to know that
+                # they run synchronously when not actually talking to a
+                # remote server
+                d = func()
+                d.addCallback(stash.append)
+                return stash[0]
+
+            verinfo = extract(p.get_verinfo)
+            (seqnum, root_hash, salt_to_use, segsize, datalen, k, N, prefix,
+             offsets) = verinfo
+            print >>out, "MDMF %s %d/%d %d #%d:%s %d %s" % \
+                  (si_s, k, N, datalen,
+                   seqnum, base32.b2a(root_hash),
+                   expiration, quote_output(abs_sharefile))
         else:
             print >>out, "UNKNOWN mutable %s" % quote_output(abs_sharefile)
 
diff --git a/src/allmydata/test/test_mutable.py b/src/allmydata/test/test_mutable.py
index 45622220..6a4af6c0 100644
--- a/src/allmydata/test/test_mutable.py
+++ b/src/allmydata/test/test_mutable.py
@@ -2990,6 +2990,26 @@ class Version(GridTestMixin, unittest.TestCase, testutil.ShouldFailMixin, \
             self.failUnless("  datalen: %d" % len(self.data) in lines, output)
             vcap = n.get_verify_cap().to_string()
             self.failUnless("  verify-cap: %s" % vcap in lines, output)
+
+            cso = debug.CatalogSharesOptions()
+            cso.nodedirs = fso.nodedirs
+            cso.stdout = StringIO()
+            cso.stderr = StringIO()
+            debug.catalog_shares(cso)
+            shares = cso.stdout.getvalue().splitlines()
+            oneshare = shares[0] # all shares should be MDMF
+            self.failIf(oneshare.startswith("UNKNOWN"), oneshare)
+            self.failUnless(oneshare.startswith("MDMF"), oneshare)
+            fields = oneshare.split()
+            self.failUnlessEqual(fields[0], "MDMF")
+            self.failUnlessEqual(fields[1], storage_index)
+            self.failUnlessEqual(fields[2], "3/10")
+            self.failUnlessEqual(fields[3], "%d" % len(self.data))
+            self.failUnless(fields[4].startswith("#1:"), fields[3])
+            # the rest of fields[4] is the roothash, which depends upon
+            # encryption salts and is not constant. fields[5] is the
+            # remaining time on the longest lease, which is timing dependent.
+            # The rest of the line is the quoted pathname to the share.
         d.addCallback(_debug)
         return d
 
-- 
2.45.2