]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
Renames and removal towards a unified ceph_objectstore_tool
authorDavid Zafman <david.zafman@inktank.com>
Wed, 30 Jul 2014 18:22:29 +0000 (11:22 -0700)
committerDavid Zafman <dzafman@redhat.com>
Thu, 28 Aug 2014 23:22:15 +0000 (16:22 -0700)
Rename ceph_filestore_dump.cc and ceph_filestore_dump.py
Remove ceph_filestore_tool.cc

Signed-off-by: David Zafman <david.zafman@inktank.com>
src/test/ceph_filestore_dump.py [deleted file]
src/test/ceph_objectstore_tool.py [new file with mode: 0755]
src/tools/ceph_filestore_dump.cc [deleted file]
src/tools/ceph_filestore_tool.cc [deleted file]
src/tools/ceph_objectstore_tool.cc [new file with mode: 0644]

diff --git a/src/test/ceph_filestore_dump.py b/src/test/ceph_filestore_dump.py
deleted file mode 100755 (executable)
index 3718a0e..0000000
+++ /dev/null
@@ -1,637 +0,0 @@
-#! /usr/bin/python
-
-from subprocess import call
-from subprocess import check_output
-import os
-import time
-import sys
-import re
-import string
-import logging
-
-logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.WARNING)
-
-
-def wait_for_health():
-    print "Wait for health_ok...",
-    while call("./ceph health 2> /dev/null | grep -v HEALTH_OK > /dev/null", shell=True) == 0:
-        time.sleep(5)
-    print "DONE"
-
-
-def get_pool_id(name, nullfd):
-    cmd = "./ceph osd pool stats {pool}".format(pool=name).split()
-    # pool {pool} id # .... grab the 4 field
-    return check_output(cmd, stderr=nullfd).split()[3]
-
-
-# return a sorted list of unique PGs given a directory
-def get_pgs(DIR, ID):
-    OSDS = [f for f in os.listdir(DIR) if os.path.isdir(os.path.join(DIR, f)) and string.find(f, "osd") == 0]
-    PGS = []
-    endhead = re.compile("{id}.*_head$".format(id=ID))
-    for d in OSDS:
-        DIRL2 = os.path.join(DIR, d)
-        SUBDIR = os.path.join(DIRL2, "current")
-        PGS += [f for f in os.listdir(SUBDIR) if os.path.isdir(os.path.join(SUBDIR, f)) and endhead.match(f)]
-    PGS = [re.sub("_head", "", p) for p in PGS]
-    return sorted(set(PGS))
-
-
-# return a sorted list of PGS a subset of ALLPGS that contain objects with prefix specified
-def get_objs(ALLPGS, prefix, DIR, ID):
-    OSDS = [f for f in os.listdir(DIR) if os.path.isdir(os.path.join(DIR, f)) and string.find(f, "osd") == 0]
-    PGS = []
-    for d in OSDS:
-        DIRL2 = os.path.join(DIR, d)
-        SUBDIR = os.path.join(DIRL2, "current")
-        for p in ALLPGS:
-            PGDIR = p + "_head"
-            if not os.path.isdir(os.path.join(SUBDIR, PGDIR)):
-                continue
-            FINALDIR = os.path.join(SUBDIR, PGDIR)
-            # See if there are any objects there
-            if [f for f in os.listdir(FINALDIR) if os.path.isfile(os.path.join(FINALDIR, f)) and string.find(f, prefix) == 0]:
-                PGS += [p]
-    return sorted(set(PGS))
-
-
-# return a sorted list of OSDS which have data from a given PG
-def get_osds(PG, DIR):
-    ALLOSDS = [f for f in os.listdir(DIR) if os.path.isdir(os.path.join(DIR, f)) and string.find(f, "osd") == 0]
-    OSDS = []
-    for d in ALLOSDS:
-        DIRL2 = os.path.join(DIR, d)
-        SUBDIR = os.path.join(DIRL2, "current")
-        PGDIR = PG + "_head"
-        if not os.path.isdir(os.path.join(SUBDIR, PGDIR)):
-            continue
-        OSDS += [d]
-    return sorted(OSDS)
-
-
-def get_lines(filename):
-    tmpfd = open(filename, "r")
-    line = True
-    lines = []
-    while line:
-        line = tmpfd.readline().rstrip('\n')
-        if line:
-            lines += [line]
-    tmpfd.close()
-    os.unlink(filename)
-    return lines
-
-
-def cat_file(level, filename):
-    if level < logging.getLogger().getEffectiveLevel():
-        return
-    print "File: " + filename
-    with open(filename, "r") as f:
-        while True:
-            line = f.readline().rstrip('\n')
-            if not line:
-                break
-            print line
-    print "<EOF>"
-
-
-def vstart(new):
-    print "vstarting....",
-    OPT = new and "-n" or ""
-    call("OSD=4 ./vstart.sh -l {opt} -d > /dev/null 2>&1".format(opt=OPT), shell=True)
-    print "DONE"
-
-
-def test_failure(cmd, errmsg):
-    ttyfd = open("/dev/tty", "rw")
-    TMPFILE = r"/tmp/tmp.{pid}".format(pid=os.getpid())
-    tmpfd = open(TMPFILE, "w")
-
-    logging.debug(cmd)
-    ret = call(cmd, shell=True, stdin=ttyfd, stdout=ttyfd, stderr=tmpfd)
-    ttyfd.close()
-    tmpfd.close()
-    if ret == 0:
-        logging.error("Should have failed, but got exit 0")
-        return 1
-    lines = get_lines(TMPFILE)
-    line = lines[0]
-    if line == errmsg:
-        logging.info("Correctly failed with message \"" + line + "\"")
-        return 0
-    else:
-        logging.error("Bad message to stderr \"" + line + "\"")
-        return 1
-
-
-def main():
-    sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
-    nullfd = open(os.devnull, "w")
-
-    OSDDIR = "dev"
-    REP_POOL = "rep_pool"
-    REP_NAME = "REPobject"
-    EC_POOL = "ec_pool"
-    EC_NAME = "ECobject"
-    NUM_OBJECTS = 40
-    ERRORS = 0
-    pid = os.getpid()
-    TESTDIR = "/tmp/test.{pid}".format(pid=pid)
-    DATADIR = "/tmp/data.{pid}".format(pid=pid)
-    CFSD_PREFIX = "./ceph_filestore_dump --filestore-path dev/{osd} --journal-path dev/{osd}.journal "
-    DATALINECOUNT = 10000
-    PROFNAME = "testecprofile"
-
-    vstart(new=True)
-    wait_for_health()
-
-    cmd = "./ceph osd pool create {pool} 12 12 replicated".format(pool=REP_POOL)
-    logging.debug(cmd)
-    call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
-    REPID = get_pool_id(REP_POOL, nullfd)
-
-    print "Created Replicated pool #{repid}".format(repid=REPID)
-
-    cmd = "./ceph osd erasure-code-profile set {prof} ruleset-failure-domain=osd".format(prof=PROFNAME)
-    logging.debug(cmd)
-    call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
-    cmd = "./ceph osd erasure-code-profile get {prof}".format(prof=PROFNAME)
-    logging.debug(cmd)
-    call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
-    cmd = "./ceph osd pool create {pool} 12 12 erasure {prof}".format(pool=EC_POOL, prof=PROFNAME)
-    logging.debug(cmd)
-    call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
-    ECID = get_pool_id(EC_POOL, nullfd)
-
-    print "Created Erasure coded pool #{ecid}".format(ecid=ECID)
-
-    print "Creating {objs} objects in replicated pool".format(objs=NUM_OBJECTS)
-    cmd = "mkdir -p {datadir}".format(datadir=DATADIR)
-    logging.debug(cmd)
-    call(cmd, shell=True)
-
-    db = {}
-
-    objects = range(1, NUM_OBJECTS + 1)
-    for i in objects:
-        NAME = REP_NAME + "{num}".format(num=i)
-        DDNAME = os.path.join(DATADIR, NAME)
-
-        cmd = "rm -f " + DDNAME
-        logging.debug(cmd)
-        call(cmd, shell=True)
-
-        dataline = range(DATALINECOUNT)
-        fd = open(DDNAME, "w")
-        data = "This is the replicated data for " + NAME + "\n"
-        for _ in dataline:
-            fd.write(data)
-        fd.close()
-
-        cmd = "./rados -p {pool} put {name} {ddname}".format(pool=REP_POOL, name=NAME, ddname=DDNAME)
-        logging.debug(cmd)
-        ret = call(cmd, shell=True, stderr=nullfd)
-        if ret != 0:
-            logging.critical("Replicated pool creation failed with {ret}".format(ret=ret))
-            sys.exit(1)
-
-        db[NAME] = {}
-
-        keys = range(i)
-        db[NAME]["xattr"] = {}
-        for k in keys:
-            if k == 0:
-                continue
-            mykey = "key{i}-{k}".format(i=i, k=k)
-            myval = "val{i}-{k}".format(i=i, k=k)
-            cmd = "./rados -p {pool} setxattr {name} {key} {val}".format(pool=REP_POOL, name=NAME, key=mykey, val=myval)
-            logging.debug(cmd)
-            ret = call(cmd, shell=True)
-            if ret != 0:
-                logging.error("setxattr failed with {ret}".format(ret=ret))
-                ERRORS += 1
-            db[NAME]["xattr"][mykey] = myval
-
-        # Create omap header in all objects but REPobject1
-        if i != 1:
-            myhdr = "hdr{i}".format(i=i)
-            cmd = "./rados -p {pool} setomapheader {name} {hdr}".format(pool=REP_POOL, name=NAME, hdr=myhdr)
-            logging.debug(cmd)
-            ret = call(cmd, shell=True)
-            if ret != 0:
-                logging.critical("setomapheader failed with {ret}".format(ret=ret))
-                ERRORS += 1
-            db[NAME]["omapheader"] = myhdr
-
-        db[NAME]["omap"] = {}
-        for k in keys:
-            if k == 0:
-                continue
-            mykey = "okey{i}-{k}".format(i=i, k=k)
-            myval = "oval{i}-{k}".format(i=i, k=k)
-            cmd = "./rados -p {pool} setomapval {name} {key} {val}".format(pool=REP_POOL, name=NAME, key=mykey, val=myval)
-            logging.debug(cmd)
-            ret = call(cmd, shell=True)
-            if ret != 0:
-                logging.critical("setomapval failed with {ret}".format(ret=ret))
-            db[NAME]["omap"][mykey] = myval
-
-    print "Creating {objs} objects in erasure coded pool".format(objs=NUM_OBJECTS)
-
-    for i in objects:
-        NAME = EC_NAME + "{num}".format(num=i)
-        DDNAME = os.path.join(DATADIR, NAME)
-
-        cmd = "rm -f " + DDNAME
-        logging.debug(cmd)
-        call(cmd, shell=True)
-
-        fd = open(DDNAME, "w")
-        data = "This is the erasure coded data for " + NAME + "\n"
-        for j in dataline:
-            fd.write(data)
-        fd.close()
-
-        cmd = "./rados -p {pool} put {name} {ddname}".format(pool=EC_POOL, name=NAME, ddname=DDNAME)
-        logging.debug(cmd)
-        ret = call(cmd, shell=True, stderr=nullfd)
-        if ret != 0:
-            logging.critical("Erasure coded pool creation failed with {ret}".format(ret=ret))
-            sys.exit(1)
-
-        db[NAME] = {}
-
-        db[NAME]["xattr"] = {}
-        keys = range(i)
-        for k in keys:
-            if k == 0:
-                continue
-            mykey = "key{i}-{k}".format(i=i, k=k)
-            myval = "val{i}-{k}".format(i=i, k=k)
-            cmd = "./rados -p {pool} setxattr {name} {key} {val}".format(pool=EC_POOL, name=NAME, key=mykey, val=myval)
-            logging.debug(cmd)
-            ret = call(cmd, shell=True)
-            if ret != 0:
-                logging.error("setxattr failed with {ret}".format(ret=ret))
-                ERRORS += 1
-            db[NAME]["xattr"][mykey] = myval
-
-        # Omap isn't supported in EC pools
-        db[NAME]["omap"] = {}
-
-    logging.debug(db)
-
-
-    call("./stop.sh", stderr=nullfd)
-
-    if ERRORS:
-        logging.critical("Unable to set up test")
-        sys.exit(1)
-
-    ALLREPPGS = get_pgs(OSDDIR, REPID)
-    logging.debug(ALLREPPGS)
-    ALLECPGS = get_pgs(OSDDIR, ECID)
-    logging.debug(ALLECPGS)
-
-    OBJREPPGS = get_objs(ALLREPPGS, REP_NAME, OSDDIR, REPID)
-    logging.debug(OBJREPPGS)
-    OBJECPGS = get_objs(ALLECPGS, EC_NAME, OSDDIR, ECID)
-    logging.debug(OBJECPGS)
-
-    ONEPG = ALLREPPGS[0]
-    logging.debug(ONEPG)
-    osds = get_osds(ONEPG, OSDDIR)
-    ONEOSD = osds[0]
-    logging.debug(ONEOSD)
-
-    print "Test invalid parameters"
-    # On export can't use stdout to a terminal
-    cmd = (CFSD_PREFIX + "--type export --pgid {pg}").format(osd=ONEOSD, pg=ONEPG)
-    ERRORS += test_failure(cmd, "stdout is a tty and no --file option specified")
-
-    OTHERFILE = "/tmp/foo.{pid}".format(pid=pid)
-    foofd = open(OTHERFILE, "w")
-    foofd.close()
-
-    # On import can't specify a PG
-    cmd = (CFSD_PREFIX + "--type import --pgid {pg} --file {FOO}").format(osd=ONEOSD, pg=ONEPG, FOO=OTHERFILE)
-    ERRORS += test_failure(cmd, "--pgid option invalid with import")
-
-    os.unlink(OTHERFILE)
-    cmd = (CFSD_PREFIX + "--type import --file {FOO}").format(osd=ONEOSD, FOO=OTHERFILE)
-    ERRORS += test_failure(cmd, "open: No such file or directory")
-
-    # On import can't use stdin from a terminal
-    cmd = (CFSD_PREFIX + "--type import --pgid {pg}").format(osd=ONEOSD, pg=ONEPG)
-    ERRORS += test_failure(cmd, "stdin is a tty and no --file option specified")
-
-    # Test --type list and generate json for all objects
-    print "Test --type list by generating json for all objects"
-    TMPFILE = r"/tmp/tmp.{pid}".format(pid=pid)
-    ALLPGS = OBJREPPGS + OBJECPGS
-    for pg in ALLPGS:
-        OSDS = get_osds(pg, OSDDIR)
-        for osd in OSDS:
-            cmd = (CFSD_PREFIX + "--type list --pgid {pg}").format(osd=osd, pg=pg)
-            tmpfd = open(TMPFILE, "a")
-            logging.debug(cmd)
-            ret = call(cmd, shell=True, stdout=tmpfd)
-            if ret != 0:
-                logging.error("Bad exit status {ret} from --type list request".format(ret=ret))
-                ERRORS += 1
-
-    tmpfd.close()
-    lines = get_lines(TMPFILE)
-    JSONOBJ = sorted(set(lines))
-
-    # Test get-bytes
-    print "Test get-bytes and set-bytes"
-    for basename in db.keys():
-        file = os.path.join(DATADIR, basename)
-        JSON = [l for l in JSONOBJ if l.find("\"" + basename + "\"") != -1]
-        JSON = JSON[0]
-        GETNAME = "/tmp/getbytes.{pid}".format(pid=pid)
-        TESTNAME = "/tmp/testbytes.{pid}".format(pid=pid)
-        SETNAME = "/tmp/setbytes.{pid}".format(pid=pid)
-        for pg in OBJREPPGS:
-            OSDS = get_osds(pg, OSDDIR)
-            for osd in OSDS:
-                DIR = os.path.join(OSDDIR, os.path.join(osd, os.path.join("current", "{pg}_head".format(pg=pg))))
-                fname = [f for f in os.listdir(DIR) if os.path.isfile(os.path.join(DIR, f)) and string.find(f, basename + "_") == 0]
-                if not fname:
-                    continue
-                fname = fname[0]
-                try:
-                    os.unlink(GETNAME)
-                except:
-                    pass
-                cmd = (CFSD_PREFIX + " --pgid {pg} '{json}' get-bytes {fname}").format(osd=osd, pg=pg, json=JSON, fname=GETNAME)
-                logging.debug(cmd)
-                ret = call(cmd, shell=True)
-                if ret != 0:
-                    logging.error("Bad exit status {ret}".format(ret=ret))
-                    ERRORS += 1
-                    continue
-                cmd = "diff -q {file} {getfile}".format(file=file, getfile=GETNAME)
-                ret = call(cmd, shell=True)
-                if ret != 0:
-                    logging.error("Data from get-bytes differ")
-                    logging.debug("Got:")
-                    cat_file(logging.DEBUG, GETNAME)
-                    logging.debug("Expected:")
-                    cat_file(logging.DEBUG, file)
-                    ERRORS += 1
-                fd = open(SETNAME, "w")
-                data = "put-bytes going into {file}\n".format(file=file)
-                fd.write(data)
-                fd.close()
-                cmd = (CFSD_PREFIX + "--pgid {pg} '{json}' set-bytes {sname}").format(osd=osd, pg=pg, json=JSON, sname=SETNAME)
-                logging.debug(cmd)
-                ret = call(cmd, shell=True)
-                if ret != 0:
-                    logging.error("Bad exit status {ret} from set-bytes".format(ret=ret))
-                    ERRORS += 1
-                fd = open(TESTNAME, "w")
-                cmd = (CFSD_PREFIX + "--pgid {pg} '{json}' get-bytes -").format(osd=osd, pg=pg, json=JSON)
-                logging.debug(cmd)
-                ret = call(cmd, shell=True, stdout=fd)
-                fd.close()
-                if ret != 0:
-                    logging.error("Bad exit status {ret} from get-bytes".format(ret=ret))
-                    ERRORS += 1
-                cmd = "diff -q {setfile} {testfile}".format(setfile=SETNAME, testfile=TESTNAME)
-                logging.debug(cmd)
-                ret = call(cmd, shell=True)
-                if ret != 0:
-                    logging.error("Data after set-bytes differ")
-                    logging.debug("Got:")
-                    cat_file(logging.DEBUG, TESTNAME)
-                    logging.debug("Expected:")
-                    cat_file(logging.DEBUG, SETNAME)
-                    ERRORS += 1
-                fd = open(file, "r")
-                cmd = (CFSD_PREFIX + "--pgid {pg} '{json}' set-bytes").format(osd=osd, pg=pg, json=JSON)
-                logging.debug(cmd)
-                ret = call(cmd, shell=True, stdin=fd)
-                if ret != 0:
-                    logging.error("Bad exit status {ret} from set-bytes to restore object".format(ret=ret))
-                    ERRORS += 1
-
-    try:
-        os.unlink(GETNAME)
-    except:
-        pass
-    try:
-        os.unlink(TESTNAME)
-    except:
-        pass
-    try:
-        os.unlink(SETNAME)
-    except:
-        pass
-
-    print "Test list-attrs get-attr"
-    ATTRFILE = r"/tmp/attrs.{pid}".format(pid=pid)
-    VALFILE = r"/tmp/val.{pid}".format(pid=pid)
-    for basename in db.keys():
-        file = os.path.join(DATADIR, basename)
-        JSON = [l for l in JSONOBJ if l.find("\"" + basename + "\"") != -1]
-        JSON = JSON[0]
-        for pg in OBJREPPGS:
-            OSDS = get_osds(pg, OSDDIR)
-            for osd in OSDS:
-                DIR = os.path.join(OSDDIR, os.path.join(osd, os.path.join("current", "{pg}_head".format(pg=pg))))
-                fname = [f for f in os.listdir(DIR) if os.path.isfile(os.path.join(DIR, f)) and string.find(f, basename + "_") == 0]
-                if not fname:
-                    continue
-                fname = fname[0]
-                afd = open(ATTRFILE, "w")
-                cmd = (CFSD_PREFIX + "--pgid {pg} '{json}' list-attrs").format(osd=osd, pg=pg, json=JSON)
-                logging.debug(cmd)
-                ret = call(cmd, shell=True, stdout=afd)
-                afd.close()
-                if ret != 0:
-                    logging.error("list-attrs failed with {ret}".format(ret=ret))
-                    ERRORS += 1
-                    continue
-                keys = get_lines(ATTRFILE)
-                values = dict(db[basename]["xattr"])
-                for key in keys:
-                    if key == "_" or key == "snapset":
-                        continue
-                    key = key.strip("_")
-                    if key not in values:
-                        logging.error("The key {key} should be present".format(key=key))
-                        ERRORS += 1
-                        continue
-                    exp = values.pop(key)
-                    vfd = open(VALFILE, "w")
-                    cmd = (CFSD_PREFIX + "--pgid {pg} '{json}' get-attr {key}").format(osd=osd, pg=pg, json=JSON, key="_" + key)
-                    logging.debug(cmd)
-                    ret = call(cmd, shell=True, stdout=vfd)
-                    vfd.close()
-                    if ret != 0:
-                        logging.error("get-attr failed with {ret}".format(ret=ret))
-                        ERRORS += 1
-                        continue
-                    lines = get_lines(VALFILE)
-                    val = lines[0]
-                    if exp != val:
-                        logging.error("For key {key} got value {got} instead of {expected}".format(key=key, got=val, expected=exp))
-                        ERRORS += 1
-                if len(values) != 0:
-                    logging.error("Not all keys found, remaining keys:")
-                    print values
-
-    print "Test pg info"
-    for pg in ALLREPPGS + ALLECPGS:
-        for osd in get_osds(pg, OSDDIR):
-            cmd = (CFSD_PREFIX + "--type info --pgid {pg} | grep '\"pgid\": \"{pg}\"'").format(osd=osd, pg=pg)
-            logging.debug(cmd)
-            ret = call(cmd, shell=True, stdout=nullfd)
-            if ret != 0:
-                logging.error("Getting info failed for pg {pg} from {osd} with {ret}".format(pg=pg, osd=osd, ret=ret))
-                ERRORS += 1
-
-    print "Test pg logging"
-    for pg in ALLREPPGS + ALLECPGS:
-        for osd in get_osds(pg, OSDDIR):
-            tmpfd = open(TMPFILE, "w")
-            cmd = (CFSD_PREFIX + "--type log --pgid {pg}").format(osd=osd, pg=pg)
-            logging.debug(cmd)
-            ret = call(cmd, shell=True, stdout=tmpfd)
-            if ret != 0:
-                logging.error("Getting log failed for pg {pg} from {osd} with {ret}".format(pg=pg, osd=osd, ret=ret))
-                ERRORS += 1
-            HASOBJ = pg in OBJREPPGS + OBJECPGS
-            MODOBJ = False
-            for line in get_lines(TMPFILE):
-                if line.find("modify") != -1:
-                    MODOBJ = True
-                    break
-            if HASOBJ != MODOBJ:
-                logging.error("Bad log for pg {pg} from {osd}".format(pg=pg, osd=osd))
-                MSG = (HASOBJ and [""] or ["NOT "])[0]
-                print "Log should {msg}have a modify entry".format(msg=MSG)
-                ERRORS += 1
-
-    try:
-        os.unlink(TMPFILE)
-    except:
-        pass
-
-    print "Test pg export"
-    EXP_ERRORS = 0
-    os.mkdir(TESTDIR)
-    for osd in [f for f in os.listdir(OSDDIR) if os.path.isdir(os.path.join(OSDDIR, f)) and string.find(f, "osd") == 0]:
-        os.mkdir(os.path.join(TESTDIR, osd))
-    for pg in ALLREPPGS + ALLECPGS:
-        for osd in get_osds(pg, OSDDIR):
-            mydir = os.path.join(TESTDIR, osd)
-            fname = os.path.join(mydir, pg)
-            cmd = (CFSD_PREFIX + "--type export --pgid {pg} --file {file}").format(osd=osd, pg=pg, file=fname)
-            logging.debug(cmd)
-            ret = call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
-            if ret != 0:
-                logging.error("Exporting failed for pg {pg} on {osd} with {ret}".format(pg=pg, osd=osd, ret=ret))
-                EXP_ERRORS += 1
-
-    ERRORS += EXP_ERRORS
-
-    print "Test pg removal"
-    RM_ERRORS = 0
-    for pg in ALLREPPGS + ALLECPGS:
-        for osd in get_osds(pg, OSDDIR):
-            cmd = (CFSD_PREFIX + "--type remove --pgid {pg}").format(pg=pg, osd=osd)
-            logging.debug(cmd)
-            ret = call(cmd, shell=True, stdout=nullfd)
-            if ret != 0:
-                logging.error("Removing failed for pg {pg} on {osd} with {ret}".format(pg=pg, osd=osd, ret=ret))
-                RM_ERRORS += 1
-
-    ERRORS += RM_ERRORS
-
-    IMP_ERRORS = 0
-    if EXP_ERRORS == 0 and RM_ERRORS == 0:
-        print "Test pg import"
-        for osd in [f for f in os.listdir(OSDDIR) if os.path.isdir(os.path.join(OSDDIR, f)) and string.find(f, "osd") == 0]:
-            dir = os.path.join(TESTDIR, osd)
-            for pg in [f for f in os.listdir(dir) if os.path.isfile(os.path.join(dir, f))]:
-                file = os.path.join(dir, pg)
-                cmd = (CFSD_PREFIX + "--type import --file {file}").format(osd=osd, file=file)
-                logging.debug(cmd)
-                ret = call(cmd, shell=True, stdout=nullfd)
-                if ret != 0:
-                    logging.error("Import failed from {file} with {ret}".format(file=file, ret=ret))
-                    IMP_ERRORS += 1
-    else:
-        logging.warning("SKIPPING IMPORT TESTS DUE TO PREVIOUS FAILURES")
-
-    ERRORS += IMP_ERRORS
-    logging.debug(cmd)
-    call("/bin/rm -rf {dir}".format(dir=TESTDIR), shell=True)
-
-    if EXP_ERRORS == 0 and RM_ERRORS == 0 and IMP_ERRORS == 0:
-        print "Verify replicated import data"
-        for file in [f for f in os.listdir(DATADIR) if f.find(REP_NAME) == 0]:
-            path = os.path.join(DATADIR, file)
-            tmpfd = open(TMPFILE, "w")
-            cmd = "find {dir} -name '{file}_*'".format(dir=OSDDIR, file=file)
-            logging.debug(cmd)
-            ret = call(cmd, shell=True, stdout=tmpfd)
-            if ret:
-                logging.critical("INTERNAL ERROR")
-                sys.exit(1)
-            tmpfd.close()
-            obj_locs = get_lines(TMPFILE)
-            if len(obj_locs) == 0:
-                logging.error("Can't find imported object {name}".format(name=file))
-                ERRORS += 1
-            for obj_loc in obj_locs:
-                cmd = "diff -q {src} {obj_loc}".format(src=path, obj_loc=obj_loc)
-                logging.debug(cmd)
-                ret = call(cmd, shell=True)
-                if ret != 0:
-                    logging.error("{file} data not imported properly into {obj}".format(file=file, obj=obj_loc))
-                    ERRORS += 1
-
-        vstart(new=False)
-        wait_for_health()
-
-        print "Verify erasure coded import data"
-        for file in [f for f in os.listdir(DATADIR) if f.find(EC_NAME) == 0]:
-            path = os.path.join(DATADIR, file)
-            try:
-                os.unlink(TMPFILE)
-            except:
-                pass
-            cmd = "./rados -p {pool} get {file} {out}".format(pool=EC_POOL, file=file, out=TMPFILE)
-            logging.debug(cmd)
-            call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
-            cmd = "diff -q {src} {result}".format(src=path, result=TMPFILE)
-            logging.debug(cmd)
-            ret = call(cmd, shell=True)
-            if ret != 0:
-                logging.error("{file} data not imported properly".format(file=file))
-                ERRORS += 1
-            try:
-                os.unlink(TMPFILE)
-            except:
-                pass
-
-        call("./stop.sh", stderr=nullfd)
-    else:
-        logging.warning("SKIPPING CHECKING IMPORT DATA DUE TO PREVIOUS FAILURES")
-
-    call("/bin/rm -rf {dir}".format(dir=DATADIR), shell=True)
-
-    if ERRORS == 0:
-        print "TEST PASSED"
-        sys.exit(0)
-    else:
-        print "TEST FAILED WITH {errcount} ERRORS".format(errcount=ERRORS)
-        sys.exit(1)
-
-if __name__ == "__main__":
-    main()
diff --git a/src/test/ceph_objectstore_tool.py b/src/test/ceph_objectstore_tool.py
new file mode 100755 (executable)
index 0000000..3718a0e
--- /dev/null
@@ -0,0 +1,637 @@
+#! /usr/bin/python
+
+from subprocess import call
+from subprocess import check_output
+import os
+import time
+import sys
+import re
+import string
+import logging
+
+logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.WARNING)
+
+
+def wait_for_health():
+    print "Wait for health_ok...",
+    while call("./ceph health 2> /dev/null | grep -v HEALTH_OK > /dev/null", shell=True) == 0:
+        time.sleep(5)
+    print "DONE"
+
+
+def get_pool_id(name, nullfd):
+    cmd = "./ceph osd pool stats {pool}".format(pool=name).split()
+    # pool {pool} id # .... grab the 4 field
+    return check_output(cmd, stderr=nullfd).split()[3]
+
+
+# return a sorted list of unique PGs given a directory
+def get_pgs(DIR, ID):
+    OSDS = [f for f in os.listdir(DIR) if os.path.isdir(os.path.join(DIR, f)) and string.find(f, "osd") == 0]
+    PGS = []
+    endhead = re.compile("{id}.*_head$".format(id=ID))
+    for d in OSDS:
+        DIRL2 = os.path.join(DIR, d)
+        SUBDIR = os.path.join(DIRL2, "current")
+        PGS += [f for f in os.listdir(SUBDIR) if os.path.isdir(os.path.join(SUBDIR, f)) and endhead.match(f)]
+    PGS = [re.sub("_head", "", p) for p in PGS]
+    return sorted(set(PGS))
+
+
+# return a sorted list of PGS a subset of ALLPGS that contain objects with prefix specified
+def get_objs(ALLPGS, prefix, DIR, ID):
+    OSDS = [f for f in os.listdir(DIR) if os.path.isdir(os.path.join(DIR, f)) and string.find(f, "osd") == 0]
+    PGS = []
+    for d in OSDS:
+        DIRL2 = os.path.join(DIR, d)
+        SUBDIR = os.path.join(DIRL2, "current")
+        for p in ALLPGS:
+            PGDIR = p + "_head"
+            if not os.path.isdir(os.path.join(SUBDIR, PGDIR)):
+                continue
+            FINALDIR = os.path.join(SUBDIR, PGDIR)
+            # See if there are any objects there
+            if [f for f in os.listdir(FINALDIR) if os.path.isfile(os.path.join(FINALDIR, f)) and string.find(f, prefix) == 0]:
+                PGS += [p]
+    return sorted(set(PGS))
+
+
+# return a sorted list of OSDS which have data from a given PG
+def get_osds(PG, DIR):
+    ALLOSDS = [f for f in os.listdir(DIR) if os.path.isdir(os.path.join(DIR, f)) and string.find(f, "osd") == 0]
+    OSDS = []
+    for d in ALLOSDS:
+        DIRL2 = os.path.join(DIR, d)
+        SUBDIR = os.path.join(DIRL2, "current")
+        PGDIR = PG + "_head"
+        if not os.path.isdir(os.path.join(SUBDIR, PGDIR)):
+            continue
+        OSDS += [d]
+    return sorted(OSDS)
+
+
+def get_lines(filename):
+    tmpfd = open(filename, "r")
+    line = True
+    lines = []
+    while line:
+        line = tmpfd.readline().rstrip('\n')
+        if line:
+            lines += [line]
+    tmpfd.close()
+    os.unlink(filename)
+    return lines
+
+
+def cat_file(level, filename):
+    if level < logging.getLogger().getEffectiveLevel():
+        return
+    print "File: " + filename
+    with open(filename, "r") as f:
+        while True:
+            line = f.readline().rstrip('\n')
+            if not line:
+                break
+            print line
+    print "<EOF>"
+
+
+def vstart(new):
+    print "vstarting....",
+    OPT = new and "-n" or ""
+    call("OSD=4 ./vstart.sh -l {opt} -d > /dev/null 2>&1".format(opt=OPT), shell=True)
+    print "DONE"
+
+
+def test_failure(cmd, errmsg):
+    ttyfd = open("/dev/tty", "rw")
+    TMPFILE = r"/tmp/tmp.{pid}".format(pid=os.getpid())
+    tmpfd = open(TMPFILE, "w")
+
+    logging.debug(cmd)
+    ret = call(cmd, shell=True, stdin=ttyfd, stdout=ttyfd, stderr=tmpfd)
+    ttyfd.close()
+    tmpfd.close()
+    if ret == 0:
+        logging.error("Should have failed, but got exit 0")
+        return 1
+    lines = get_lines(TMPFILE)
+    line = lines[0]
+    if line == errmsg:
+        logging.info("Correctly failed with message \"" + line + "\"")
+        return 0
+    else:
+        logging.error("Bad message to stderr \"" + line + "\"")
+        return 1
+
+
+def main():
+    sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
+    nullfd = open(os.devnull, "w")
+
+    OSDDIR = "dev"
+    REP_POOL = "rep_pool"
+    REP_NAME = "REPobject"
+    EC_POOL = "ec_pool"
+    EC_NAME = "ECobject"
+    NUM_OBJECTS = 40
+    ERRORS = 0
+    pid = os.getpid()
+    TESTDIR = "/tmp/test.{pid}".format(pid=pid)
+    DATADIR = "/tmp/data.{pid}".format(pid=pid)
+    CFSD_PREFIX = "./ceph_filestore_dump --filestore-path dev/{osd} --journal-path dev/{osd}.journal "
+    DATALINECOUNT = 10000
+    PROFNAME = "testecprofile"
+
+    vstart(new=True)
+    wait_for_health()
+
+    cmd = "./ceph osd pool create {pool} 12 12 replicated".format(pool=REP_POOL)
+    logging.debug(cmd)
+    call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
+    REPID = get_pool_id(REP_POOL, nullfd)
+
+    print "Created Replicated pool #{repid}".format(repid=REPID)
+
+    cmd = "./ceph osd erasure-code-profile set {prof} ruleset-failure-domain=osd".format(prof=PROFNAME)
+    logging.debug(cmd)
+    call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
+    cmd = "./ceph osd erasure-code-profile get {prof}".format(prof=PROFNAME)
+    logging.debug(cmd)
+    call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
+    cmd = "./ceph osd pool create {pool} 12 12 erasure {prof}".format(pool=EC_POOL, prof=PROFNAME)
+    logging.debug(cmd)
+    call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
+    ECID = get_pool_id(EC_POOL, nullfd)
+
+    print "Created Erasure coded pool #{ecid}".format(ecid=ECID)
+
+    print "Creating {objs} objects in replicated pool".format(objs=NUM_OBJECTS)
+    cmd = "mkdir -p {datadir}".format(datadir=DATADIR)
+    logging.debug(cmd)
+    call(cmd, shell=True)
+
+    db = {}
+
+    objects = range(1, NUM_OBJECTS + 1)
+    for i in objects:
+        NAME = REP_NAME + "{num}".format(num=i)
+        DDNAME = os.path.join(DATADIR, NAME)
+
+        cmd = "rm -f " + DDNAME
+        logging.debug(cmd)
+        call(cmd, shell=True)
+
+        dataline = range(DATALINECOUNT)
+        fd = open(DDNAME, "w")
+        data = "This is the replicated data for " + NAME + "\n"
+        for _ in dataline:
+            fd.write(data)
+        fd.close()
+
+        cmd = "./rados -p {pool} put {name} {ddname}".format(pool=REP_POOL, name=NAME, ddname=DDNAME)
+        logging.debug(cmd)
+        ret = call(cmd, shell=True, stderr=nullfd)
+        if ret != 0:
+            logging.critical("Replicated pool creation failed with {ret}".format(ret=ret))
+            sys.exit(1)
+
+        db[NAME] = {}
+
+        keys = range(i)
+        db[NAME]["xattr"] = {}
+        for k in keys:
+            if k == 0:
+                continue
+            mykey = "key{i}-{k}".format(i=i, k=k)
+            myval = "val{i}-{k}".format(i=i, k=k)
+            cmd = "./rados -p {pool} setxattr {name} {key} {val}".format(pool=REP_POOL, name=NAME, key=mykey, val=myval)
+            logging.debug(cmd)
+            ret = call(cmd, shell=True)
+            if ret != 0:
+                logging.error("setxattr failed with {ret}".format(ret=ret))
+                ERRORS += 1
+            db[NAME]["xattr"][mykey] = myval
+
+        # Create omap header in all objects but REPobject1
+        if i != 1:
+            myhdr = "hdr{i}".format(i=i)
+            cmd = "./rados -p {pool} setomapheader {name} {hdr}".format(pool=REP_POOL, name=NAME, hdr=myhdr)
+            logging.debug(cmd)
+            ret = call(cmd, shell=True)
+            if ret != 0:
+                logging.critical("setomapheader failed with {ret}".format(ret=ret))
+                ERRORS += 1
+            db[NAME]["omapheader"] = myhdr
+
+        db[NAME]["omap"] = {}
+        for k in keys:
+            if k == 0:
+                continue
+            mykey = "okey{i}-{k}".format(i=i, k=k)
+            myval = "oval{i}-{k}".format(i=i, k=k)
+            cmd = "./rados -p {pool} setomapval {name} {key} {val}".format(pool=REP_POOL, name=NAME, key=mykey, val=myval)
+            logging.debug(cmd)
+            ret = call(cmd, shell=True)
+            if ret != 0:
+                logging.critical("setomapval failed with {ret}".format(ret=ret))
+            db[NAME]["omap"][mykey] = myval
+
+    print "Creating {objs} objects in erasure coded pool".format(objs=NUM_OBJECTS)
+
+    for i in objects:
+        NAME = EC_NAME + "{num}".format(num=i)
+        DDNAME = os.path.join(DATADIR, NAME)
+
+        cmd = "rm -f " + DDNAME
+        logging.debug(cmd)
+        call(cmd, shell=True)
+
+        fd = open(DDNAME, "w")
+        data = "This is the erasure coded data for " + NAME + "\n"
+        for j in dataline:
+            fd.write(data)
+        fd.close()
+
+        cmd = "./rados -p {pool} put {name} {ddname}".format(pool=EC_POOL, name=NAME, ddname=DDNAME)
+        logging.debug(cmd)
+        ret = call(cmd, shell=True, stderr=nullfd)
+        if ret != 0:
+            logging.critical("Erasure coded pool creation failed with {ret}".format(ret=ret))
+            sys.exit(1)
+
+        db[NAME] = {}
+
+        db[NAME]["xattr"] = {}
+        keys = range(i)
+        for k in keys:
+            if k == 0:
+                continue
+            mykey = "key{i}-{k}".format(i=i, k=k)
+            myval = "val{i}-{k}".format(i=i, k=k)
+            cmd = "./rados -p {pool} setxattr {name} {key} {val}".format(pool=EC_POOL, name=NAME, key=mykey, val=myval)
+            logging.debug(cmd)
+            ret = call(cmd, shell=True)
+            if ret != 0:
+                logging.error("setxattr failed with {ret}".format(ret=ret))
+                ERRORS += 1
+            db[NAME]["xattr"][mykey] = myval
+
+        # Omap isn't supported in EC pools
+        db[NAME]["omap"] = {}
+
+    logging.debug(db)
+
+
+    call("./stop.sh", stderr=nullfd)
+
+    if ERRORS:
+        logging.critical("Unable to set up test")
+        sys.exit(1)
+
+    ALLREPPGS = get_pgs(OSDDIR, REPID)
+    logging.debug(ALLREPPGS)
+    ALLECPGS = get_pgs(OSDDIR, ECID)
+    logging.debug(ALLECPGS)
+
+    OBJREPPGS = get_objs(ALLREPPGS, REP_NAME, OSDDIR, REPID)
+    logging.debug(OBJREPPGS)
+    OBJECPGS = get_objs(ALLECPGS, EC_NAME, OSDDIR, ECID)
+    logging.debug(OBJECPGS)
+
+    ONEPG = ALLREPPGS[0]
+    logging.debug(ONEPG)
+    osds = get_osds(ONEPG, OSDDIR)
+    ONEOSD = osds[0]
+    logging.debug(ONEOSD)
+
+    print "Test invalid parameters"
+    # On export can't use stdout to a terminal
+    cmd = (CFSD_PREFIX + "--type export --pgid {pg}").format(osd=ONEOSD, pg=ONEPG)
+    ERRORS += test_failure(cmd, "stdout is a tty and no --file option specified")
+
+    OTHERFILE = "/tmp/foo.{pid}".format(pid=pid)
+    foofd = open(OTHERFILE, "w")
+    foofd.close()
+
+    # On import can't specify a PG
+    cmd = (CFSD_PREFIX + "--type import --pgid {pg} --file {FOO}").format(osd=ONEOSD, pg=ONEPG, FOO=OTHERFILE)
+    ERRORS += test_failure(cmd, "--pgid option invalid with import")
+
+    os.unlink(OTHERFILE)
+    cmd = (CFSD_PREFIX + "--type import --file {FOO}").format(osd=ONEOSD, FOO=OTHERFILE)
+    ERRORS += test_failure(cmd, "open: No such file or directory")
+
+    # On import can't use stdin from a terminal
+    cmd = (CFSD_PREFIX + "--type import --pgid {pg}").format(osd=ONEOSD, pg=ONEPG)
+    ERRORS += test_failure(cmd, "stdin is a tty and no --file option specified")
+
+    # Test --type list and generate json for all objects
+    print "Test --type list by generating json for all objects"
+    TMPFILE = r"/tmp/tmp.{pid}".format(pid=pid)
+    ALLPGS = OBJREPPGS + OBJECPGS
+    for pg in ALLPGS:
+        OSDS = get_osds(pg, OSDDIR)
+        for osd in OSDS:
+            cmd = (CFSD_PREFIX + "--type list --pgid {pg}").format(osd=osd, pg=pg)
+            tmpfd = open(TMPFILE, "a")
+            logging.debug(cmd)
+            ret = call(cmd, shell=True, stdout=tmpfd)
+            if ret != 0:
+                logging.error("Bad exit status {ret} from --type list request".format(ret=ret))
+                ERRORS += 1
+
+    tmpfd.close()
+    lines = get_lines(TMPFILE)
+    JSONOBJ = sorted(set(lines))
+
+    # Test get-bytes
+    print "Test get-bytes and set-bytes"
+    for basename in db.keys():
+        file = os.path.join(DATADIR, basename)
+        JSON = [l for l in JSONOBJ if l.find("\"" + basename + "\"") != -1]
+        JSON = JSON[0]
+        GETNAME = "/tmp/getbytes.{pid}".format(pid=pid)
+        TESTNAME = "/tmp/testbytes.{pid}".format(pid=pid)
+        SETNAME = "/tmp/setbytes.{pid}".format(pid=pid)
+        for pg in OBJREPPGS:
+            OSDS = get_osds(pg, OSDDIR)
+            for osd in OSDS:
+                DIR = os.path.join(OSDDIR, os.path.join(osd, os.path.join("current", "{pg}_head".format(pg=pg))))
+                fname = [f for f in os.listdir(DIR) if os.path.isfile(os.path.join(DIR, f)) and string.find(f, basename + "_") == 0]
+                if not fname:
+                    continue
+                fname = fname[0]
+                try:
+                    os.unlink(GETNAME)
+                except:
+                    pass
+                cmd = (CFSD_PREFIX + " --pgid {pg} '{json}' get-bytes {fname}").format(osd=osd, pg=pg, json=JSON, fname=GETNAME)
+                logging.debug(cmd)
+                ret = call(cmd, shell=True)
+                if ret != 0:
+                    logging.error("Bad exit status {ret}".format(ret=ret))
+                    ERRORS += 1
+                    continue
+                cmd = "diff -q {file} {getfile}".format(file=file, getfile=GETNAME)
+                ret = call(cmd, shell=True)
+                if ret != 0:
+                    logging.error("Data from get-bytes differ")
+                    logging.debug("Got:")
+                    cat_file(logging.DEBUG, GETNAME)
+                    logging.debug("Expected:")
+                    cat_file(logging.DEBUG, file)
+                    ERRORS += 1
+                fd = open(SETNAME, "w")
+                data = "put-bytes going into {file}\n".format(file=file)
+                fd.write(data)
+                fd.close()
+                cmd = (CFSD_PREFIX + "--pgid {pg} '{json}' set-bytes {sname}").format(osd=osd, pg=pg, json=JSON, sname=SETNAME)
+                logging.debug(cmd)
+                ret = call(cmd, shell=True)
+                if ret != 0:
+                    logging.error("Bad exit status {ret} from set-bytes".format(ret=ret))
+                    ERRORS += 1
+                fd = open(TESTNAME, "w")
+                cmd = (CFSD_PREFIX + "--pgid {pg} '{json}' get-bytes -").format(osd=osd, pg=pg, json=JSON)
+                logging.debug(cmd)
+                ret = call(cmd, shell=True, stdout=fd)
+                fd.close()
+                if ret != 0:
+                    logging.error("Bad exit status {ret} from get-bytes".format(ret=ret))
+                    ERRORS += 1
+                cmd = "diff -q {setfile} {testfile}".format(setfile=SETNAME, testfile=TESTNAME)
+                logging.debug(cmd)
+                ret = call(cmd, shell=True)
+                if ret != 0:
+                    logging.error("Data after set-bytes differ")
+                    logging.debug("Got:")
+                    cat_file(logging.DEBUG, TESTNAME)
+                    logging.debug("Expected:")
+                    cat_file(logging.DEBUG, SETNAME)
+                    ERRORS += 1
+                fd = open(file, "r")
+                cmd = (CFSD_PREFIX + "--pgid {pg} '{json}' set-bytes").format(osd=osd, pg=pg, json=JSON)
+                logging.debug(cmd)
+                ret = call(cmd, shell=True, stdin=fd)
+                if ret != 0:
+                    logging.error("Bad exit status {ret} from set-bytes to restore object".format(ret=ret))
+                    ERRORS += 1
+
+    try:
+        os.unlink(GETNAME)
+    except:
+        pass
+    try:
+        os.unlink(TESTNAME)
+    except:
+        pass
+    try:
+        os.unlink(SETNAME)
+    except:
+        pass
+
+    print "Test list-attrs get-attr"
+    ATTRFILE = r"/tmp/attrs.{pid}".format(pid=pid)
+    VALFILE = r"/tmp/val.{pid}".format(pid=pid)
+    for basename in db.keys():
+        file = os.path.join(DATADIR, basename)
+        JSON = [l for l in JSONOBJ if l.find("\"" + basename + "\"") != -1]
+        JSON = JSON[0]
+        for pg in OBJREPPGS:
+            OSDS = get_osds(pg, OSDDIR)
+            for osd in OSDS:
+                DIR = os.path.join(OSDDIR, os.path.join(osd, os.path.join("current", "{pg}_head".format(pg=pg))))
+                fname = [f for f in os.listdir(DIR) if os.path.isfile(os.path.join(DIR, f)) and string.find(f, basename + "_") == 0]
+                if not fname:
+                    continue
+                fname = fname[0]
+                afd = open(ATTRFILE, "w")
+                cmd = (CFSD_PREFIX + "--pgid {pg} '{json}' list-attrs").format(osd=osd, pg=pg, json=JSON)
+                logging.debug(cmd)
+                ret = call(cmd, shell=True, stdout=afd)
+                afd.close()
+                if ret != 0:
+                    logging.error("list-attrs failed with {ret}".format(ret=ret))
+                    ERRORS += 1
+                    continue
+                keys = get_lines(ATTRFILE)
+                values = dict(db[basename]["xattr"])
+                for key in keys:
+                    if key == "_" or key == "snapset":
+                        continue
+                    key = key.strip("_")
+                    if key not in values:
+                        logging.error("The key {key} should be present".format(key=key))
+                        ERRORS += 1
+                        continue
+                    exp = values.pop(key)
+                    vfd = open(VALFILE, "w")
+                    cmd = (CFSD_PREFIX + "--pgid {pg} '{json}' get-attr {key}").format(osd=osd, pg=pg, json=JSON, key="_" + key)
+                    logging.debug(cmd)
+                    ret = call(cmd, shell=True, stdout=vfd)
+                    vfd.close()
+                    if ret != 0:
+                        logging.error("get-attr failed with {ret}".format(ret=ret))
+                        ERRORS += 1
+                        continue
+                    lines = get_lines(VALFILE)
+                    val = lines[0]
+                    if exp != val:
+                        logging.error("For key {key} got value {got} instead of {expected}".format(key=key, got=val, expected=exp))
+                        ERRORS += 1
+                if len(values) != 0:
+                    logging.error("Not all keys found, remaining keys:")
+                    print values
+
+    print "Test pg info"
+    for pg in ALLREPPGS + ALLECPGS:
+        for osd in get_osds(pg, OSDDIR):
+            cmd = (CFSD_PREFIX + "--type info --pgid {pg} | grep '\"pgid\": \"{pg}\"'").format(osd=osd, pg=pg)
+            logging.debug(cmd)
+            ret = call(cmd, shell=True, stdout=nullfd)
+            if ret != 0:
+                logging.error("Getting info failed for pg {pg} from {osd} with {ret}".format(pg=pg, osd=osd, ret=ret))
+                ERRORS += 1
+
+    print "Test pg logging"
+    for pg in ALLREPPGS + ALLECPGS:
+        for osd in get_osds(pg, OSDDIR):
+            tmpfd = open(TMPFILE, "w")
+            cmd = (CFSD_PREFIX + "--type log --pgid {pg}").format(osd=osd, pg=pg)
+            logging.debug(cmd)
+            ret = call(cmd, shell=True, stdout=tmpfd)
+            if ret != 0:
+                logging.error("Getting log failed for pg {pg} from {osd} with {ret}".format(pg=pg, osd=osd, ret=ret))
+                ERRORS += 1
+            HASOBJ = pg in OBJREPPGS + OBJECPGS
+            MODOBJ = False
+            for line in get_lines(TMPFILE):
+                if line.find("modify") != -1:
+                    MODOBJ = True
+                    break
+            if HASOBJ != MODOBJ:
+                logging.error("Bad log for pg {pg} from {osd}".format(pg=pg, osd=osd))
+                MSG = (HASOBJ and [""] or ["NOT "])[0]
+                print "Log should {msg}have a modify entry".format(msg=MSG)
+                ERRORS += 1
+
+    try:
+        os.unlink(TMPFILE)
+    except:
+        pass
+
+    print "Test pg export"
+    EXP_ERRORS = 0
+    os.mkdir(TESTDIR)
+    for osd in [f for f in os.listdir(OSDDIR) if os.path.isdir(os.path.join(OSDDIR, f)) and string.find(f, "osd") == 0]:
+        os.mkdir(os.path.join(TESTDIR, osd))
+    for pg in ALLREPPGS + ALLECPGS:
+        for osd in get_osds(pg, OSDDIR):
+            mydir = os.path.join(TESTDIR, osd)
+            fname = os.path.join(mydir, pg)
+            cmd = (CFSD_PREFIX + "--type export --pgid {pg} --file {file}").format(osd=osd, pg=pg, file=fname)
+            logging.debug(cmd)
+            ret = call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
+            if ret != 0:
+                logging.error("Exporting failed for pg {pg} on {osd} with {ret}".format(pg=pg, osd=osd, ret=ret))
+                EXP_ERRORS += 1
+
+    ERRORS += EXP_ERRORS
+
+    print "Test pg removal"
+    RM_ERRORS = 0
+    for pg in ALLREPPGS + ALLECPGS:
+        for osd in get_osds(pg, OSDDIR):
+            cmd = (CFSD_PREFIX + "--type remove --pgid {pg}").format(pg=pg, osd=osd)
+            logging.debug(cmd)
+            ret = call(cmd, shell=True, stdout=nullfd)
+            if ret != 0:
+                logging.error("Removing failed for pg {pg} on {osd} with {ret}".format(pg=pg, osd=osd, ret=ret))
+                RM_ERRORS += 1
+
+    ERRORS += RM_ERRORS
+
+    IMP_ERRORS = 0
+    if EXP_ERRORS == 0 and RM_ERRORS == 0:
+        print "Test pg import"
+        for osd in [f for f in os.listdir(OSDDIR) if os.path.isdir(os.path.join(OSDDIR, f)) and string.find(f, "osd") == 0]:
+            dir = os.path.join(TESTDIR, osd)
+            for pg in [f for f in os.listdir(dir) if os.path.isfile(os.path.join(dir, f))]:
+                file = os.path.join(dir, pg)
+                cmd = (CFSD_PREFIX + "--type import --file {file}").format(osd=osd, file=file)
+                logging.debug(cmd)
+                ret = call(cmd, shell=True, stdout=nullfd)
+                if ret != 0:
+                    logging.error("Import failed from {file} with {ret}".format(file=file, ret=ret))
+                    IMP_ERRORS += 1
+    else:
+        logging.warning("SKIPPING IMPORT TESTS DUE TO PREVIOUS FAILURES")
+
+    ERRORS += IMP_ERRORS
+    logging.debug(cmd)
+    call("/bin/rm -rf {dir}".format(dir=TESTDIR), shell=True)
+
+    if EXP_ERRORS == 0 and RM_ERRORS == 0 and IMP_ERRORS == 0:
+        print "Verify replicated import data"
+        for file in [f for f in os.listdir(DATADIR) if f.find(REP_NAME) == 0]:
+            path = os.path.join(DATADIR, file)
+            tmpfd = open(TMPFILE, "w")
+            cmd = "find {dir} -name '{file}_*'".format(dir=OSDDIR, file=file)
+            logging.debug(cmd)
+            ret = call(cmd, shell=True, stdout=tmpfd)
+            if ret:
+                logging.critical("INTERNAL ERROR")
+                sys.exit(1)
+            tmpfd.close()
+            obj_locs = get_lines(TMPFILE)
+            if len(obj_locs) == 0:
+                logging.error("Can't find imported object {name}".format(name=file))
+                ERRORS += 1
+            for obj_loc in obj_locs:
+                cmd = "diff -q {src} {obj_loc}".format(src=path, obj_loc=obj_loc)
+                logging.debug(cmd)
+                ret = call(cmd, shell=True)
+                if ret != 0:
+                    logging.error("{file} data not imported properly into {obj}".format(file=file, obj=obj_loc))
+                    ERRORS += 1
+
+        vstart(new=False)
+        wait_for_health()
+
+        print "Verify erasure coded import data"
+        for file in [f for f in os.listdir(DATADIR) if f.find(EC_NAME) == 0]:
+            path = os.path.join(DATADIR, file)
+            try:
+                os.unlink(TMPFILE)
+            except:
+                pass
+            cmd = "./rados -p {pool} get {file} {out}".format(pool=EC_POOL, file=file, out=TMPFILE)
+            logging.debug(cmd)
+            call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
+            cmd = "diff -q {src} {result}".format(src=path, result=TMPFILE)
+            logging.debug(cmd)
+            ret = call(cmd, shell=True)
+            if ret != 0:
+                logging.error("{file} data not imported properly".format(file=file))
+                ERRORS += 1
+            try:
+                os.unlink(TMPFILE)
+            except:
+                pass
+
+        call("./stop.sh", stderr=nullfd)
+    else:
+        logging.warning("SKIPPING CHECKING IMPORT DATA DUE TO PREVIOUS FAILURES")
+
+    call("/bin/rm -rf {dir}".format(dir=DATADIR), shell=True)
+
+    if ERRORS == 0:
+        print "TEST PASSED"
+        sys.exit(0)
+    else:
+        print "TEST FAILED WITH {errcount} ERRORS".format(errcount=ERRORS)
+        sys.exit(1)
+
+if __name__ == "__main__":
+    main()
diff --git a/src/tools/ceph_filestore_dump.cc b/src/tools/ceph_filestore_dump.cc
deleted file mode 100644 (file)
index 0ff018c..0000000
+++ /dev/null
@@ -1,2077 +0,0 @@
-// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
-// vim: ts=8 sw=2 smarttab
-/*
- * Ceph - scalable distributed file system
- *
- * Copyright (C) 2013 Inktank
- *
- * This is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2.1, as published by the Free Software
- * Foundation.  See file COPYING.
- *
- */
-
-#include <boost/program_options/variables_map.hpp>
-#include <boost/program_options/parsers.hpp>
-
-#include <stdlib.h>
-
-#include "common/Formatter.h"
-#include "common/errno.h"
-
-#include "global/global_init.h"
-
-#include "os/ObjectStore.h"
-#include "os/FileStore.h"
-
-#include "osd/PGLog.h"
-#include "osd/OSD.h"
-
-#include "json_spirit/json_spirit_value.h"
-#include "json_spirit/json_spirit_reader.h"
-
-namespace po = boost::program_options;
-using namespace std;
-
-static coll_t META_COLL("meta");
-
-enum {
-    TYPE_NONE = 0,
-    TYPE_PG_BEGIN,
-    TYPE_PG_END,
-    TYPE_OBJECT_BEGIN,
-    TYPE_OBJECT_END,
-    TYPE_DATA,
-    TYPE_ATTRS,
-    TYPE_OMAP_HDR,
-    TYPE_OMAP,
-    TYPE_PG_METADATA,
-    END_OF_TYPES,      //Keep at the end
-};
-
-//#define INTERNAL_TEST
-//#define INTERNAL_TEST2
-
-#ifdef INTERNAL_TEST
-CompatSet get_test_compat_set() {
-  CompatSet::FeatureSet ceph_osd_feature_compat;
-  CompatSet::FeatureSet ceph_osd_feature_ro_compat;
-  CompatSet::FeatureSet ceph_osd_feature_incompat;
-  ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_BASE);
-  ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_PGINFO);
-  ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_OLOC);
-  ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_LEC);
-  ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_CATEGORIES);
-  ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_HOBJECTPOOL);
-  ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_BIGINFO);
-  ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_LEVELDBINFO);
-  ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_LEVELDBLOG);
-#ifdef INTERNAL_TEST2
-  ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_SNAPMAPPER);
-  ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_SHARDS);
-#endif
-  return CompatSet(ceph_osd_feature_compat, ceph_osd_feature_ro_compat,
-                  ceph_osd_feature_incompat);
-}
-#endif
-
-typedef uint8_t sectiontype_t;
-typedef uint32_t mymagic_t;
-typedef int64_t mysize_t;
-const ssize_t max_read = 1024 * 1024;
-const uint16_t shortmagic = 0xffce;    //goes into stream as "ceff"
-//endmagic goes into stream as "ceff ffec"
-const mymagic_t endmagic = (0xecff << 16) | shortmagic;
-const int fd_none = INT_MIN;
-bool outistty;
-
-//The first FIXED_LENGTH bytes are a fixed
-//portion of the export output.  This includes the overall
-//version number, and size of header and footer.
-//THIS STRUCTURE CAN ONLY BE APPENDED TO.  If it needs to expand,
-//the version can be bumped and then anything
-//can be added to the export format.
-struct super_header {
-  static const uint32_t super_magic = (shortmagic << 16) | shortmagic;
-  static const uint32_t super_ver = 2;
-  static const uint32_t FIXED_LENGTH = 16;
-  uint32_t magic;
-  uint32_t version;
-  uint32_t header_size;
-  uint32_t footer_size;
-
-  super_header() : magic(0), version(0), header_size(0), footer_size(0) { }
-  int read_super();
-
-  void encode(bufferlist& bl) const {
-    ::encode(magic, bl);
-    ::encode(version, bl);
-    ::encode(header_size, bl);
-    ::encode(footer_size, bl);
-  }
-  void decode(bufferlist::iterator& bl) {
-    ::decode(magic, bl);
-    ::decode(version, bl);
-    ::decode(header_size, bl);
-    ::decode(footer_size, bl);
-  }
-};
-
-struct header {
-  sectiontype_t type;
-  mysize_t size;
-  header(sectiontype_t type, mysize_t size) :
-    type(type), size(size) { }
-  header(): type(0), size(0) { }
-
-  int get_header();
-
-  void encode(bufferlist& bl) const {
-    uint32_t debug_type = (type << 24) | (type << 16) | shortmagic;
-    ENCODE_START(1, 1, bl);
-    ::encode(debug_type, bl);
-    ::encode(size, bl);
-    ENCODE_FINISH(bl);
-  }
-  void decode(bufferlist::iterator& bl) {
-    uint32_t debug_type;
-    DECODE_START(1, bl);
-    ::decode(debug_type, bl);
-    type = debug_type >> 24;
-    ::decode(size, bl);
-    DECODE_FINISH(bl);
-  }
-};
-
-struct footer {
-  mymagic_t magic;
-  footer() : magic(endmagic) { }
-
-  int get_footer();
-
-  void encode(bufferlist& bl) const {
-    ENCODE_START(1, 1, bl);
-    ::encode(magic, bl);
-    ENCODE_FINISH(bl);
-  }
-  void decode(bufferlist::iterator& bl) {
-    DECODE_START(1, bl);
-    ::decode(magic, bl);
-    DECODE_FINISH(bl);
-  }
-};
-
-struct pg_begin {
-  spg_t pgid;
-  OSDSuperblock superblock;
-
-  pg_begin(spg_t pg, const OSDSuperblock& sb):
-    pgid(pg), superblock(sb) { }
-  pg_begin() { }
-
-  void encode(bufferlist& bl) const {
-    // If superblock doesn't include CEPH_FS_FEATURE_INCOMPAT_SHARDS then
-    // shard will be NO_SHARD for a replicated pool.  This means
-    // that we allow the decode by struct_v 2.
-    ENCODE_START(3, 2, bl);
-    ::encode(pgid.pgid, bl);
-    ::encode(superblock, bl);
-    ::encode(pgid.shard, bl);
-    ENCODE_FINISH(bl);
-  }
-  // NOTE: New super_ver prevents decode from ver 1
-  void decode(bufferlist::iterator& bl) {
-    DECODE_START(3, bl);
-    ::decode(pgid.pgid, bl);
-    if (struct_v > 1) {
-      ::decode(superblock, bl);
-    }
-    if (struct_v > 2) {
-      ::decode(pgid.shard, bl);
-    } else {
-      pgid.shard = shard_id_t::NO_SHARD;
-    }
-    DECODE_FINISH(bl);
-  }
-};
-
-struct object_begin {
-  ghobject_t hoid;
-  object_begin(const ghobject_t &hoid): hoid(hoid) { }
-  object_begin() { }
-
-  // If superblock doesn't include CEPH_FS_FEATURE_INCOMPAT_SHARDS then
-  // generation will be NO_GEN, shard_id will be NO_SHARD for a replicated
-  // pool.  This means we will allow the decode by struct_v 1.
-  void encode(bufferlist& bl) const {
-    ENCODE_START(2, 1, bl);
-    ::encode(hoid.hobj, bl);
-    ::encode(hoid.generation, bl);
-    ::encode(hoid.shard_id, bl);
-    ENCODE_FINISH(bl);
-  }
-  void decode(bufferlist::iterator& bl) {
-    DECODE_START(2, bl);
-    ::decode(hoid.hobj, bl);
-    if (struct_v > 1) {
-      ::decode(hoid.generation, bl);
-      ::decode(hoid.shard_id, bl);
-    } else {
-      hoid.generation = ghobject_t::NO_GEN;
-      hoid.shard_id = shard_id_t::NO_SHARD;
-    }
-    DECODE_FINISH(bl);
-  }
-};
-
-struct data_section {
-  uint64_t offset;
-  uint64_t len;
-  bufferlist databl;
-  data_section(uint64_t offset, uint64_t len, bufferlist bl):
-     offset(offset), len(len), databl(bl) { }
-  data_section(): offset(0), len(0) { }
-
-  void encode(bufferlist& bl) const {
-    ENCODE_START(1, 1, bl);
-    ::encode(offset, bl);
-    ::encode(len, bl);
-    ::encode(databl, bl);
-    ENCODE_FINISH(bl);
-  }
-  void decode(bufferlist::iterator& bl) {
-    DECODE_START(1, bl);
-    ::decode(offset, bl);
-    ::decode(len, bl);
-    ::decode(databl, bl);
-    DECODE_FINISH(bl);
-  }
-};
-
-struct attr_section {
-  map<string,bufferptr> data;
-  attr_section(const map<string,bufferptr> &data) : data(data) { }
-  attr_section() { }
-
-  void encode(bufferlist& bl) const {
-    ENCODE_START(1, 1, bl);
-    ::encode(data, bl);
-    ENCODE_FINISH(bl);
-  }
-  void decode(bufferlist::iterator& bl) {
-    DECODE_START(1, bl);
-    ::decode(data, bl);
-    DECODE_FINISH(bl);
-  }
-};
-
-struct omap_hdr_section {
-  bufferlist hdr;
-  omap_hdr_section(bufferlist hdr) : hdr(hdr) { }
-  omap_hdr_section() { }
-
-  void encode(bufferlist& bl) const {
-    ENCODE_START(1, 1, bl);
-    ::encode(hdr, bl);
-    ENCODE_FINISH(bl);
-  }
-  void decode(bufferlist::iterator& bl) {
-    DECODE_START(1, bl);
-    ::decode(hdr, bl);
-    DECODE_FINISH(bl);
-  }
-};
-
-struct omap_section {
-  map<string, bufferlist> omap;
-  omap_section(const map<string, bufferlist> &omap) :
-    omap(omap) { }
-  omap_section() { }
-
-  void encode(bufferlist& bl) const {
-    ENCODE_START(1, 1, bl);
-    ::encode(omap, bl);
-    ENCODE_FINISH(bl);
-  }
-  void decode(bufferlist::iterator& bl) {
-    DECODE_START(1, bl);
-    ::decode(omap, bl);
-    DECODE_FINISH(bl);
-  }
-};
-
-struct metadata_section {
-  __u8 struct_ver;
-  epoch_t map_epoch;
-  pg_info_t info;
-  pg_log_t log;
-
-  metadata_section(__u8 struct_ver, epoch_t map_epoch, const pg_info_t &info,
-                  const pg_log_t &log)
-    : struct_ver(struct_ver),
-      map_epoch(map_epoch),
-      info(info),
-      log(log) { }
-  metadata_section()
-    : struct_ver(0),
-      map_epoch(0) { }
-
-  void encode(bufferlist& bl) const {
-    ENCODE_START(1, 1, bl);
-    ::encode(struct_ver, bl);
-    ::encode(map_epoch, bl);
-    ::encode(info, bl);
-    ::encode(log, bl);
-    ENCODE_FINISH(bl);
-  }
-  void decode(bufferlist::iterator& bl) {
-    DECODE_START(1, bl);
-    ::decode(struct_ver, bl);
-    ::decode(map_epoch, bl);
-    ::decode(info, bl);
-    ::decode(log, bl);
-    DECODE_FINISH(bl);
-  }
-};
-
-hobject_t infos_oid = OSD::make_infos_oid();
-hobject_t biginfo_oid, log_oid;
-
-int file_fd = fd_none;
-bool debug = false;
-super_header sh;
-
-template <typename T>
-int write_section(sectiontype_t type, const T& obj, int fd) {
-  bufferlist blhdr, bl, blftr;
-  obj.encode(bl);
-  header hdr(type, bl.length());
-  hdr.encode(blhdr);
-  footer ft;
-  ft.encode(blftr);
-
-  int ret = blhdr.write_fd(fd);
-  if (ret) return ret;
-  ret = bl.write_fd(fd);
-  if (ret) return ret;
-  ret = blftr.write_fd(fd);
-  return ret;
-}
-
-// Convert non-printable characters to '\###'
-static void cleanbin(string &str)
-{
-  bool cleaned = false;
-  string clean;
-
-  for (string::iterator it = str.begin(); it != str.end(); ++it) {
-    if (!isprint(*it)) {
-      clean.push_back('\\');
-      clean.push_back('0' + ((*it >> 6) & 7));
-      clean.push_back('0' + ((*it >> 3) & 7));
-      clean.push_back('0' + (*it & 7));
-      cleaned = true;
-    } else {
-      clean.push_back(*it);
-    }
-  }
-
-  if (cleaned)
-    str = clean;
-  return;
-}
-
-int write_simple(sectiontype_t type, int fd)
-{
-  bufferlist hbl;
-
-  header hdr(type, 0);
-  hdr.encode(hbl);
-  return hbl.write_fd(fd);
-}
-
-static int get_fd_data(int fd, bufferlist &bl)
-{
-  uint64_t total = 0;
-  do {
-    ssize_t bytes = bl.read_fd(fd, max_read);
-    if (bytes < 0) {
-      cerr << "read_fd error " << cpp_strerror(-bytes) << std::endl;
-      return 1;
-    }
-
-    if (bytes == 0)
-      break;
-
-    total += bytes;
-  } while(true);
-
-  assert(bl.length() == total);
-  return 0;
-}
-
-static void invalid_path(string &path)
-{
-  cerr << "Invalid path to osd store specified: " << path << "\n";
-  exit(1);
-}
-
-int get_log(ObjectStore *fs, coll_t coll, spg_t pgid, const pg_info_t &info,
-   PGLog::IndexedLog &log, pg_missing_t &missing)
-{ 
-  map<eversion_t, hobject_t> divergent_priors;
-  try {
-    ostringstream oss;
-    PGLog::read_log(fs, coll, log_oid, info, divergent_priors, log, missing, oss);
-    if (debug && oss.str().size())
-      cerr << oss.str() << std::endl;
-  }
-  catch (const buffer::error &e) {
-    cerr << "read_log threw exception error " << e.what() << std::endl;
-    return 1;
-  }
-  return 0;
-}
-
-//Based on RemoveWQ::_process()
-void remove_coll(ObjectStore *store, const coll_t &coll)
-{
-  spg_t pg;
-  coll.is_pg_prefix(pg);
-  OSDriver driver(
-    store,
-    coll_t(),
-    OSD::make_snapmapper_oid());
-  SnapMapper mapper(&driver, 0, 0, 0, pg.shard);
-
-  vector<ghobject_t> objects;
-  ghobject_t next;
-  int r = 0;
-  int64_t num = 0;
-  ObjectStore::Transaction *t = new ObjectStore::Transaction;
-  cout << "remove_coll " << coll << std::endl;
-  while (!next.is_max()) {
-    r = store->collection_list_partial(coll, next, 200, 300, 0,
-      &objects, &next);
-    if (r < 0)
-      goto out;
-    for (vector<ghobject_t>::iterator i = objects.begin();
-        i != objects.end();
-        ++i, ++num) {
-
-      OSDriver::OSTransaction _t(driver.get_transaction(t));
-      cout << "remove " << *i << std::endl;
-      int r = mapper.remove_oid(i->hobj, &_t);
-      if (r != 0 && r != -ENOENT) {
-        assert(0);
-      }
-
-      t->remove(coll, *i);
-      if (num >= 30) {
-        store->apply_transaction(*t);
-        delete t;
-        t = new ObjectStore::Transaction;
-        num = 0;
-      }
-    }
-  }
-  t->remove_collection(coll);
-  store->apply_transaction(*t);
-out:
-  delete t;
-}
-
-//Based on part of OSD::load_pgs()
-int finish_remove_pgs(ObjectStore *store, uint64_t *next_removal_seq)
-{
-  vector<coll_t> ls;
-  int r = store->list_collections(ls);
-  if (r < 0) {
-    cerr << "finish_remove_pgs: failed to list pgs: " << cpp_strerror(-r)
-      << std::endl;
-    return r;
-  }
-
-  for (vector<coll_t>::iterator it = ls.begin();
-       it != ls.end();
-       ++it) {
-    spg_t pgid;
-    snapid_t snap;
-
-    if (it->is_temp(pgid)) {
-      cout << "finish_remove_pgs " << *it << " clearing temp" << std::endl;
-      OSD::recursive_remove_collection(store, *it);
-      continue;
-    }
-
-    if (it->is_pg(pgid, snap)) {
-      continue;
-    }
-
-    uint64_t seq;
-    if (it->is_removal(&seq, &pgid)) {
-      if (seq >= *next_removal_seq)
-       *next_removal_seq = seq + 1;
-      cout << "finish_remove_pgs removing " << *it << ", seq is "
-              << seq << " pgid is " << pgid << std::endl;
-      remove_coll(store, *it);
-      continue;
-    }
-
-    //cout << "finish_remove_pgs ignoring unrecognized " << *it << std::endl;
-  }
-  return 0;
-}
-
-int initiate_new_remove_pg(ObjectStore *store, spg_t r_pgid,
-    uint64_t *next_removal_seq)
-{
-  ObjectStore::Transaction *rmt = new ObjectStore::Transaction;
-
-  if (store->collection_exists(coll_t(r_pgid))) {
-      coll_t to_remove = coll_t::make_removal_coll((*next_removal_seq)++, r_pgid);
-      cout << "collection rename " << coll_t(r_pgid)
-          << " to " << to_remove
-        << std::endl;
-      rmt->collection_rename(coll_t(r_pgid), to_remove);
-  } else {
-    delete rmt;
-    return ENOENT;
-  }
-
-  cout << "remove " << META_COLL << " " << log_oid.oid << std::endl;
-  rmt->remove(META_COLL, log_oid);
-  cout << "remove " << META_COLL << " " << biginfo_oid.oid << std::endl;
-  rmt->remove(META_COLL, biginfo_oid);
-
-  store->apply_transaction(*rmt);
-
-  return 0;
-}
-
-int header::get_header()
-{
-  bufferlist ebl;
-  bufferlist::iterator ebliter = ebl.begin();
-  ssize_t bytes;
-
-  bytes = ebl.read_fd(file_fd, sh.header_size);
-  if ((size_t)bytes != sh.header_size) {
-    cerr << "Unexpected EOF" << std::endl;
-    return EFAULT;
-  }
-
-  decode(ebliter);
-
-  return 0;
-}
-
-int footer::get_footer()
-{
-  bufferlist ebl;
-  bufferlist::iterator ebliter = ebl.begin();
-  ssize_t bytes;
-
-  bytes = ebl.read_fd(file_fd, sh.footer_size);
-  if ((size_t)bytes != sh.footer_size) {
-    cerr << "Unexpected EOF" << std::endl;
-    return EFAULT;
-  }
-
-  decode(ebliter);
-
-  if (magic != endmagic) {
-    cerr << "Bad footer magic" << std::endl;
-    return EFAULT;
-  }
-
-  return 0;
-}
-
-int write_info(ObjectStore::Transaction &t, epoch_t epoch, pg_info_t &info,
-    __u8 struct_ver)
-{
-  //Empty for this
-  interval_set<snapid_t> snap_collections; // obsolete
-  map<epoch_t,pg_interval_t> past_intervals;
-  coll_t coll(info.pgid);
-
-  int ret = PG::_write_info(t, epoch,
-    info, coll,
-    past_intervals,
-    snap_collections,
-    infos_oid,
-    struct_ver,
-    true, true);
-  if (ret < 0) ret = -ret;
-  if (ret) cerr << "Failed to write info" << std::endl;
-  return ret;
-}
-
-void write_log(ObjectStore::Transaction &t, pg_log_t &log)
-{
-  map<eversion_t, hobject_t> divergent_priors;
-  PGLog::write_log(t, log, log_oid, divergent_priors);
-}
-
-int write_pg(ObjectStore::Transaction &t, epoch_t epoch, pg_info_t &info,
-    pg_log_t &log, __u8 struct_ver)
-{
-  int ret = write_info(t, epoch, info, struct_ver);
-  if (ret) return ret;
-  write_log(t, log);
-  return 0;
-}
-
-const int OMAP_BATCH_SIZE = 25;
-void get_omap_batch(ObjectMap::ObjectMapIterator &iter, map<string, bufferlist> &oset)
-{
-  oset.clear();
-  for (int count = OMAP_BATCH_SIZE; count && iter->valid(); --count, iter->next()) {
-    oset.insert(pair<string, bufferlist>(iter->key(), iter->value()));
-  }
-}
-
-int export_file(ObjectStore *store, coll_t cid, ghobject_t &obj)
-{
-  struct stat st;
-  mysize_t total;
-  footer ft;
-
-  int ret = store->stat(cid, obj, &st);
-  if (ret < 0)
-    return ret;
-
-  cerr << "read " << obj << std::endl;
-
-  total = st.st_size;
-  if (debug)
-    cerr << "size=" << total << std::endl;
-
-  object_begin objb(obj);
-  ret = write_section(TYPE_OBJECT_BEGIN, objb, file_fd);
-  if (ret < 0)
-    return ret;
-
-  uint64_t offset = 0;
-  bufferlist rawdatabl;
-  while(total > 0) {
-    rawdatabl.clear();
-    mysize_t len = max_read;
-    if (len > total)
-      len = total;
-
-    ret = store->read(cid, obj, offset, len, rawdatabl);
-    if (ret < 0)
-      return ret;
-    if (ret == 0)
-      return -EINVAL;
-
-    data_section dblock(offset, len, rawdatabl);
-    if (debug)
-      cerr << "data section offset=" << offset << " len=" << len << std::endl;
-
-    total -= ret;
-    offset += ret;
-
-    ret = write_section(TYPE_DATA, dblock, file_fd);
-    if (ret) return ret;
-  }
-
-  //Handle attrs for this object
-  map<string,bufferptr> aset;
-  ret = store->getattrs(cid, obj, aset);
-  if (ret) return ret;
-  attr_section as(aset);
-  ret = write_section(TYPE_ATTRS, as, file_fd);
-  if (ret)
-    return ret;
-
-  if (debug) {
-    cerr << "attrs size " << aset.size() << std::endl;
-  }
-
-  //Handle omap information
-  bufferlist hdrbuf;
-  ret = store->omap_get_header(cid, obj, &hdrbuf, true);
-  if (ret < 0) {
-    cerr << "omap_get_header: " << cpp_strerror(-ret) << std::endl;
-    return ret;
-  }
-
-  omap_hdr_section ohs(hdrbuf);
-  ret = write_section(TYPE_OMAP_HDR, ohs, file_fd);
-  if (ret)
-    return ret;
-
-  ObjectMap::ObjectMapIterator iter = store->get_omap_iterator(cid, obj);
-  if (!iter) {
-    ret = -ENOENT;
-    cerr << "omap_get_iterator: " << cpp_strerror(-ret) << std::endl;
-    return ret;
-  }
-  iter->seek_to_first();
-  int mapcount = 0;
-  map<string, bufferlist> out;
-  while(iter->valid()) {
-    get_omap_batch(iter, out);
-
-    if (out.empty()) break;
-
-    mapcount += out.size();
-    omap_section oms(out);
-    ret = write_section(TYPE_OMAP, oms, file_fd);
-    if (ret)
-      return ret;
-  }
-  if (debug)
-    cerr << "omap map size " << mapcount << std::endl;
-
-  ret = write_simple(TYPE_OBJECT_END, file_fd);
-  if (ret)
-    return ret;
-
-  return 0;
-}
-
-int export_files(ObjectStore *store, coll_t coll)
-{
-  vector<ghobject_t> objects;
-  ghobject_t next;
-
-  while (!next.is_max()) {
-    int r = store->collection_list_partial(coll, next, 200, 300, 0,
-      &objects, &next);
-    if (r < 0)
-      return r;
-    for (vector<ghobject_t>::iterator i = objects.begin();
-        i != objects.end();
-        ++i) {
-      r = export_file(store, coll, *i);
-      if (r < 0)
-        return r;
-    }
-  }
-  return 0;
-}
-
-//Write super_header with its fixed 16 byte length
-void write_super()
-{
-  bufferlist superbl;
-  super_header sh;
-  footer ft;
-
-  header hdr(TYPE_NONE, 0);
-  hdr.encode(superbl);
-
-  sh.magic = super_header::super_magic;
-  sh.version = super_header::super_ver;
-  sh.header_size = superbl.length();
-  superbl.clear();
-  ft.encode(superbl);
-  sh.footer_size = superbl.length();
-  superbl.clear();
-
-  sh.encode(superbl);
-  assert(super_header::FIXED_LENGTH == superbl.length());
-  superbl.write_fd(file_fd);
-}
-
-int do_export(ObjectStore *fs, coll_t coll, spg_t pgid, pg_info_t &info,
-    epoch_t map_epoch, __u8 struct_ver, const OSDSuperblock& superblock)
-{
-  PGLog::IndexedLog log;
-  pg_missing_t missing;
-
-  cerr << "Exporting " << pgid << std::endl;
-
-  int ret = get_log(fs, coll, pgid, info, log, missing);
-  if (ret > 0)
-      return ret;
-
-  write_super();
-
-  pg_begin pgb(pgid, superblock);
-  ret = write_section(TYPE_PG_BEGIN, pgb, file_fd);
-  if (ret)
-    return ret;
-
-  ret = export_files(fs, coll);
-  if (ret) {
-    cerr << "export_files error " << ret << std::endl;
-    return ret;
-  }
-
-  metadata_section ms(struct_ver, map_epoch, info, log);
-  ret = write_section(TYPE_PG_METADATA, ms, file_fd);
-  if (ret)
-    return ret;
-
-  ret = write_simple(TYPE_PG_END, file_fd);
-  if (ret)
-    return ret;
-
-  return 0;
-}
-
-int super_header::read_super()
-{
-  bufferlist ebl;
-  bufferlist::iterator ebliter = ebl.begin();
-  ssize_t bytes;
-
-  bytes = ebl.read_fd(file_fd, super_header::FIXED_LENGTH);
-  if ((size_t)bytes != super_header::FIXED_LENGTH) {
-    cerr << "Unexpected EOF" << std::endl;
-    return EFAULT;
-  }
-
-  decode(ebliter);
-
-  return 0;
-}
-
-int read_section(int fd, sectiontype_t *type, bufferlist *bl)
-{
-  header hdr;
-  ssize_t bytes;
-
-  int ret = hdr.get_header();
-  if (ret)
-    return ret;
-
-  *type = hdr.type;
-
-  bl->clear();
-  bytes = bl->read_fd(fd, hdr.size);
-  if (bytes != hdr.size) {
-    cerr << "Unexpected EOF" << std::endl;
-    return EFAULT;
-  }
-
-  if (hdr.size > 0) {
-    footer ft;
-    ret = ft.get_footer();
-    if (ret)
-      return ret;
-  }
-
-  return 0;
-}
-
-int get_data(ObjectStore *store, coll_t coll, ghobject_t hoid,
-    ObjectStore::Transaction *t, bufferlist &bl)
-{
-  bufferlist::iterator ebliter = bl.begin();
-  data_section ds;
-  ds.decode(ebliter);
-
-  if (debug)
-    cerr << "\tdata: offset " << ds.offset << " len " << ds.len << std::endl;
-  t->write(coll, hoid, ds.offset, ds.len,  ds.databl);
-  return 0;
-}
-
-int get_attrs(ObjectStore *store, coll_t coll, ghobject_t hoid,
-    ObjectStore::Transaction *t, bufferlist &bl,
-    OSDriver &driver, SnapMapper &snap_mapper)
-{
-  bufferlist::iterator ebliter = bl.begin();
-  attr_section as;
-  as.decode(ebliter);
-
-  if (debug)
-    cerr << "\tattrs: len " << as.data.size() << std::endl;
-  t->setattrs(coll, hoid, as.data);
-
-  if (hoid.hobj.snap < CEPH_MAXSNAP && hoid.generation == ghobject_t::NO_GEN) {
-    map<string,bufferptr>::iterator mi = as.data.find(OI_ATTR);
-    if (mi != as.data.end()) {
-      bufferlist attr_bl;
-      attr_bl.push_back(mi->second);
-      object_info_t oi(attr_bl);
-  
-      if (debug)
-        cerr << "object_info " << oi << std::endl;
-  
-      OSDriver::OSTransaction _t(driver.get_transaction(t));
-      set<snapid_t> oi_snaps(oi.snaps.begin(), oi.snaps.end());
-      snap_mapper.add_oid(hoid.hobj, oi_snaps, &_t);
-    }
-  }
-
-  return 0;
-}
-
-int get_omap_hdr(ObjectStore *store, coll_t coll, ghobject_t hoid,
-    ObjectStore::Transaction *t, bufferlist &bl)
-{
-  bufferlist::iterator ebliter = bl.begin();
-  omap_hdr_section oh;
-  oh.decode(ebliter);
-
-  if (debug)
-    cerr << "\tomap header: " << string(oh.hdr.c_str(), oh.hdr.length())
-      << std::endl;
-  t->omap_setheader(coll, hoid, oh.hdr);
-  return 0;
-}
-
-int get_omap(ObjectStore *store, coll_t coll, ghobject_t hoid,
-    ObjectStore::Transaction *t, bufferlist &bl)
-{
-  bufferlist::iterator ebliter = bl.begin();
-  omap_section os;
-  os.decode(ebliter);
-
-  if (debug)
-    cerr << "\tomap: size " << os.omap.size() << std::endl;
-  t->omap_setkeys(coll, hoid, os.omap);
-  return 0;
-}
-
-int get_object(ObjectStore *store, coll_t coll, bufferlist &bl)
-{
-  ObjectStore::Transaction tran;
-  ObjectStore::Transaction *t = &tran;
-  bufferlist::iterator ebliter = bl.begin();
-  object_begin ob;
-  ob.decode(ebliter);
-  OSDriver driver(
-    store,
-    coll_t(),
-    OSD::make_snapmapper_oid());
-  spg_t pg;
-  coll.is_pg_prefix(pg);
-  SnapMapper mapper(&driver, 0, 0, 0, pg.shard);
-
-  t->touch(coll, ob.hoid);
-
-  cout << "Write " << ob.hoid << std::endl;
-
-  bufferlist ebl;
-  bool done = false;
-  while(!done) {
-    sectiontype_t type;
-    int ret = read_section(file_fd, &type, &ebl);
-    if (ret)
-      return ret;
-
-    //cout << "\tdo_object: Section type " << hex << type << dec << std::endl;
-    //cout << "\t\tsection size " << ebl.length() << std::endl;
-    if (type >= END_OF_TYPES) {
-      cout << "Skipping unknown object section type" << std::endl;
-      continue;
-    }
-    switch(type) {
-    case TYPE_DATA:
-      ret = get_data(store, coll, ob.hoid, t, ebl);
-      if (ret) return ret;
-      break;
-    case TYPE_ATTRS:
-      ret = get_attrs(store, coll, ob.hoid, t, ebl, driver, mapper);
-      if (ret) return ret;
-      break;
-    case TYPE_OMAP_HDR:
-      ret = get_omap_hdr(store, coll, ob.hoid, t, ebl);
-      if (ret) return ret;
-      break;
-    case TYPE_OMAP:
-      ret = get_omap(store, coll, ob.hoid, t, ebl);
-      if (ret) return ret;
-      break;
-    case TYPE_OBJECT_END:
-      done = true;
-      break;
-    default:
-      return EFAULT;
-    }
-  }
-  store->apply_transaction(*t);
-  return 0;
-}
-
-int get_pg_metadata(ObjectStore *store, coll_t coll, bufferlist &bl)
-{
-  ObjectStore::Transaction tran;
-  ObjectStore::Transaction *t = &tran;
-  bufferlist::iterator ebliter = bl.begin();
-  metadata_section ms;
-  ms.decode(ebliter);
-
-#if DIAGNOSTIC
-  Formatter *formatter = new JSONFormatter(true);
-  cout << "struct_v " << (int)ms.struct_ver << std::endl;
-  cout << "epoch " << ms.map_epoch << std::endl;
-  formatter->open_object_section("info");
-  ms.info.dump(formatter);
-  formatter->close_section();
-  formatter->flush(cout);
-  cout << std::endl;
-  
-  formatter->open_object_section("log");
-  ms.log.dump(formatter);
-  formatter->close_section();
-  formatter->flush(cout);
-  cout << std::endl;
-#endif
-
-  coll_t newcoll(ms.info.pgid);
-  t->collection_rename(coll, newcoll);
-
-  int ret = write_pg(*t, ms.map_epoch, ms.info, ms.log, ms.struct_ver);
-  if (ret) return ret;
-
-  store->apply_transaction(*t);
-
-  return 0;
-}
-
-int do_import(ObjectStore *store, OSDSuperblock& sb)
-{
-  bufferlist ebl;
-  pg_info_t info;
-  PGLog::IndexedLog log;
-
-  uint64_t next_removal_seq = 0;       //My local seq
-  finish_remove_pgs(store, &next_removal_seq);
-
-  int ret = sh.read_super();
-  if (ret)
-    return ret;
-
-  if (sh.magic != super_header::super_magic) {
-    cerr << "Invalid magic number" << std::endl;
-    return EFAULT;
-  }
-
-  if (sh.version > super_header::super_ver) {
-    cerr << "Can't handle export format version=" << sh.version << std::endl;
-    return EINVAL;
-  }
-
-  //First section must be TYPE_PG_BEGIN
-  sectiontype_t type;
-  ret = read_section(file_fd, &type, &ebl);
-  if (ret)
-    return ret;
-  if (type != TYPE_PG_BEGIN) {
-    return EFAULT;
-  }
-
-  bufferlist::iterator ebliter = ebl.begin();
-  pg_begin pgb;
-  pgb.decode(ebliter);
-  spg_t pgid = pgb.pgid;
-
-  if (debug) {
-    cerr << "Exported features: " << pgb.superblock.compat_features << std::endl;
-  }
-  if (sb.compat_features.compare(pgb.superblock.compat_features) == -1) {
-    cerr << "Export has incompatible features set "
-      << pgb.superblock.compat_features << std::endl;
-    return 1;
-  }
-
-  log_oid = OSD::make_pg_log_oid(pgid);
-  biginfo_oid = OSD::make_pg_biginfo_oid(pgid);
-
-  //Check for PG already present.
-  coll_t coll(pgid);
-  if (store->collection_exists(coll)) {
-    cerr << "pgid " << pgid << " already exists" << std::endl;
-    return 1;
-  }
-
-  //Switch to collection which will be removed automatically if
-  //this program is interupted.
-  coll_t rmcoll = coll_t::make_removal_coll(next_removal_seq, pgid);
-  ObjectStore::Transaction *t = new ObjectStore::Transaction;
-  t->create_collection(rmcoll);
-  store->apply_transaction(*t);
-  delete t;
-
-  cout << "Importing pgid " << pgid << std::endl;
-
-  bool done = false;
-  bool found_metadata = false;
-  while(!done) {
-    ret = read_section(file_fd, &type, &ebl);
-    if (ret)
-      return ret;
-
-    //cout << "do_import: Section type " << hex << type << dec << std::endl;
-    if (type >= END_OF_TYPES) {
-      cout << "Skipping unknown section type" << std::endl;
-      continue;
-    }
-    switch(type) {
-    case TYPE_OBJECT_BEGIN:
-      ret = get_object(store, rmcoll, ebl);
-      if (ret) return ret;
-      break;
-    case TYPE_PG_METADATA:
-      ret = get_pg_metadata(store, rmcoll, ebl);
-      if (ret) return ret;
-      found_metadata = true;
-      break;
-    case TYPE_PG_END:
-      done = true;
-      break;
-    default:
-      return EFAULT;
-    }
-  }
-
-  if (!found_metadata) {
-    cerr << "Missing metadata section" << std::endl;
-    return EFAULT;
-  }
-
-  return 0;
-}
-
-int do_list(ObjectStore *store, coll_t coll, Formatter *formatter)
-{
-  vector<ghobject_t> objects;
-  ghobject_t next;
-  while (!next.is_max()) {
-    int r = store->collection_list_partial(coll, next, 200, 300, 0,
-      &objects, &next);
-    if (r < 0)
-      return r;
-    for (vector<ghobject_t>::iterator i = objects.begin();
-        i != objects.end(); ++i) {
-
-      formatter->open_object_section("list");
-      i->dump(formatter);
-      formatter->close_section();
-      formatter->flush(cout);
-      cout << std::endl;
-    }
-  }
-  return 0;
-}
-
-int do_remove_object(ObjectStore *store, coll_t coll, ghobject_t &ghobj)
-{
-  spg_t pg;
-  coll.is_pg_prefix(pg);
-  OSDriver driver(
-    store,
-    coll_t(),
-    OSD::make_snapmapper_oid());
-  SnapMapper mapper(&driver, 0, 0, 0, pg.shard);
-  struct stat st;
-
-  int r = store->stat(coll, ghobj, &st);
-  if (r < 0) {
-    cerr << "remove: " << cpp_strerror(-r) << std::endl;
-    return r;
-  }
-
-  ObjectStore::Transaction *t = new ObjectStore::Transaction;
-  OSDriver::OSTransaction _t(driver.get_transaction(t));
-  cout << "remove " << ghobj << std::endl;
-  r = mapper.remove_oid(ghobj.hobj, &_t);
-  if (r != 0 && r != -ENOENT) {
-    cerr << "remove_oid returned " << cpp_strerror(-r) << std::endl;
-    return r;
-  }
-
-  t->remove(coll, ghobj);
-
-  store->apply_transaction(*t);
-  delete t;
-  return 0;
-}
-
-int do_list_attrs(ObjectStore *store, coll_t coll, ghobject_t &ghobj)
-{
-  map<string,bufferptr> aset;
-  int r = store->getattrs(coll, ghobj, aset);
-  if (r < 0) {
-    cerr << "getattrs: " << cpp_strerror(-r) << std::endl;
-    return r;
-  }
-
-  for (map<string,bufferptr>::iterator i = aset.begin();i != aset.end(); ++i) {
-    string key(i->first);
-    if (outistty)
-      cleanbin(key);
-    cout << key << std::endl;
-  }
-  return 0;
-}
-
-int do_list_omap(ObjectStore *store, coll_t coll, ghobject_t &ghobj)
-{
-  ObjectMap::ObjectMapIterator iter = store->get_omap_iterator(coll, ghobj);
-  if (!iter) {
-    cerr << "omap_get_iterator: " << cpp_strerror(ENOENT) << std::endl;
-    return -ENOENT;
-  }
-  iter->seek_to_first();
-  map<string, bufferlist> oset;
-  while(iter->valid()) {
-    get_omap_batch(iter, oset);
-
-    for (map<string,bufferlist>::iterator i = oset.begin();i != oset.end(); ++i) {
-      string key(i->first);
-      if (outistty)
-        cleanbin(key);
-      cout << key << std::endl;
-    }
-  }
-  return 0;
-}
-
-int do_get_bytes(ObjectStore *store, coll_t coll, ghobject_t &ghobj, int fd)
-{
-  struct stat st;
-  mysize_t total;
-
-  int ret = store->stat(coll, ghobj, &st);
-  if (ret < 0) {
-    cerr << "get-bytes: " << cpp_strerror(-ret) << std::endl;
-    return 1;
-  }
-
-  total = st.st_size;
-  if (debug)
-    cerr << "size=" << total << std::endl;
-
-  uint64_t offset = 0;
-  bufferlist rawdatabl;
-  while(total > 0) {
-    rawdatabl.clear();
-    mysize_t len = max_read;
-    if (len > total)
-      len = total;
-
-    ret = store->read(coll, ghobj, offset, len, rawdatabl);
-    if (ret < 0)
-      return ret;
-    if (ret == 0)
-      return -EINVAL;
-
-    if (debug)
-      cerr << "data section offset=" << offset << " len=" << len << std::endl;
-
-    total -= ret;
-    offset += ret;
-
-    ret = write(fd, rawdatabl.c_str(), ret);
-    if (ret == -1) {
-      perror("write");
-      return 1;
-    }
-  }
-
-  return 0;
-}
-
-int do_set_bytes(ObjectStore *store, coll_t coll, ghobject_t &ghobj, int fd)
-{
-  ObjectStore::Transaction tran;
-  ObjectStore::Transaction *t = &tran;
-
-  if (debug)
-    cerr << "Write " << ghobj << std::endl;
-
-  t->touch(coll, ghobj);
-  t->truncate(coll, ghobj, 0);
-
-  uint64_t offset = 0;
-  bufferlist rawdatabl;
-  do {
-    rawdatabl.clear();
-    ssize_t bytes = rawdatabl.read_fd(fd, max_read);
-    if (bytes < 0) {
-      cerr << "read_fd error " << cpp_strerror(-bytes) << std::endl;
-      return 1;
-    }
-
-    if (bytes == 0)
-      break;
-
-    if (debug)
-      cerr << "\tdata: offset " << offset << " bytes " << bytes << std::endl;
-    t->write(coll, ghobj, offset, bytes,  rawdatabl);
-
-    offset += bytes;
-    // XXX: Should we apply_transaction() every once in a while for very large files
-  } while(true);
-
-  store->apply_transaction(*t);
-  return 0;
-}
-
-int do_get_attr(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key)
-{
-  bufferptr bp;
-
-  int r = store->getattr(coll, ghobj, key.c_str(), bp);
-  if (r < 0) {
-    cerr << "getattr: " << cpp_strerror(-r) << std::endl;
-    return r;
-  }
-
-  string value(bp.c_str(), bp.length());
-  if (outistty) {
-    cleanbin(value);
-    value.push_back('\n');
-  }
-  cout << value;
-
-  return 0;
-}
-
-int do_set_attr(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key, int fd)
-{
-  ObjectStore::Transaction tran;
-  ObjectStore::Transaction *t = &tran;
-  bufferlist bl;
-
-  if (debug)
-    cerr << "Setattr " << ghobj << std::endl;
-
-  if (get_fd_data(fd, bl))
-    return 1;
-
-  t->touch(coll, ghobj);
-
-  t->setattr(coll, ghobj, key,  bl);
-
-  store->apply_transaction(*t);
-  return 0;
-}
-
-int do_rm_attr(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key)
-{
-  ObjectStore::Transaction tran;
-  ObjectStore::Transaction *t = &tran;
-
-  if (debug)
-    cerr << "Rmattr " << ghobj << std::endl;
-
-  t->rmattr(coll, ghobj, key);
-
-  store->apply_transaction(*t);
-  return 0;
-}
-
-int do_get_omap(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key)
-{
-  set<string> keys;
-  map<string, bufferlist> out;
-
-  keys.insert(key);
-
-  int r = store->omap_get_values(coll, ghobj, keys, &out);
-  if (r < 0) {
-    cerr << "omap_get_values: " << cpp_strerror(-r) << std::endl;
-    return r;
-  }
-
-  if (out.empty()) {
-    cerr << "Key not found" << std::endl;
-    return -ENOENT;
-  }
-
-  assert(out.size() == 1);
-
-  bufferlist bl = out.begin()->second;
-  string value(bl.c_str(), bl.length());
-  if (outistty) {
-    cleanbin(value);
-    value.push_back('\n');
-  }
-  cout << value;
-
-  return 0;
-}
-
-int do_set_omap(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key, int fd)
-{
-  ObjectStore::Transaction tran;
-  ObjectStore::Transaction *t = &tran;
-  map<string, bufferlist> attrset;
-  bufferlist valbl;
-
-  if (debug)
-    cerr << "Set_omap " << ghobj << std::endl;
-
-  if (get_fd_data(fd, valbl))
-    return 1;
-
-  attrset.insert(pair<string, bufferlist>(key, valbl));
-
-  t->touch(coll, ghobj);
-
-  t->omap_setkeys(coll, ghobj, attrset);
-
-  store->apply_transaction(*t);
-  return 0;
-}
-
-int do_rm_omap(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key)
-{
-  ObjectStore::Transaction tran;
-  ObjectStore::Transaction *t = &tran;
-  set<string> keys;
-
-  keys.insert(key);
-
-  if (debug)
-    cerr << "Rm_omap " << ghobj << std::endl;
-
-  t->omap_rmkeys(coll, ghobj, keys);
-
-  store->apply_transaction(*t);
-  return 0;
-}
-
-int do_get_omaphdr(ObjectStore *store, coll_t coll, ghobject_t &ghobj)
-{
-  bufferlist hdrbl;
-
-  int r = store->omap_get_header(coll, ghobj, &hdrbl, true);
-  if (r < 0) {
-    cerr << "omap_get_header: " << cpp_strerror(-r) << std::endl;
-    return r;
-  }
-
-  string header(hdrbl.c_str(), hdrbl.length());
-  if (outistty) {
-    cleanbin(header);
-    header.push_back('\n');
-  }
-  cout << header;
-
-  return 0;
-}
-
-int do_set_omaphdr(ObjectStore *store, coll_t coll, ghobject_t &ghobj, int fd)
-{
-  ObjectStore::Transaction tran;
-  ObjectStore::Transaction *t = &tran;
-  bufferlist hdrbl;
-
-  if (debug)
-    cerr << "Omap_setheader " << ghobj << std::endl;
-
-  if (get_fd_data(fd, hdrbl))
-    return 1;
-
-  t->touch(coll, ghobj);
-
-  t->omap_setheader(coll, ghobj, hdrbl);
-
-  store->apply_transaction(*t);
-  return 0;
-}
-
-void usage(po::options_description &desc)
-{
-    cerr << std::endl;
-    cerr << desc << std::endl;
-    cerr << std::endl;
-    cerr << "Positional syntax:" << std::endl;
-    cerr << std::endl;
-    cerr << "(requires --filestore-path, --journal-path and --pgid to be specified)" << std::endl;
-    cerr << "(optional [file] argument will read stdin or write stdout if not specified or if '-' specified)" << std::endl;
-    cerr << "ceph-filestore-dump ... <object> (get|set)-bytes [file]" << std::endl;
-    cerr << "ceph-filestore-dump ... <object> (set-(attr|omap) <key> [file]" << std::endl;
-    cerr << "ceph-filestore-dump ... <object> (set-omaphdr) [file]" << std::endl;
-    cerr << "ceph-filestore-dump ... <object> (get|rm)-(attr|omap) <key>" << std::endl;
-    cerr << "ceph-filestore-dump ... <object> (get-omaphdr)" << std::endl;
-    cerr << "ceph-filestore-dump ... <object> list-attrs" << std::endl;
-    cerr << "ceph-filestore-dump ... <object> list-omap" << std::endl;
-    cerr << "ceph-filestore-dump ... <object> remove" << std::endl;
-    cerr << std::endl;
-    exit(1);
-}
-
-int main(int argc, char **argv)
-{
-  string fspath, jpath, pgidstr, type, file, object, objcmd, arg1, arg2;
-  ghobject_t ghobj;
-
-  po::options_description desc("Allowed options");
-  desc.add_options()
-    ("help", "produce help message")
-    ("filestore-path", po::value<string>(&fspath),
-     "path to filestore directory, mandatory")
-    ("journal-path", po::value<string>(&jpath),
-     "path to journal, mandatory")
-    ("pgid", po::value<string>(&pgidstr),
-     "PG id, mandatory except for import")
-    ("type", po::value<string>(&type),
-     "Arg is one of [info, log, remove, export, import, list]")
-    ("file", po::value<string>(&file),
-     "path of file to export or import")
-    ("debug", "Enable diagnostic output to stderr")
-    ("skip-journal-replay", "Disable journal replay")
-    ("skip-mount-omap", "Disable mounting of omap")
-    ;
-
-  po::options_description positional("Positional options");
-  positional.add_options()
-    ("object", po::value<string>(&object), "ghobject in json")
-    ("objcmd", po::value<string>(&objcmd), "command [(get|set)-bytes, (get|set|rm)-(attr|omap), list-attrs, list-omap, remove]")
-    ("arg1", po::value<string>(&arg1), "arg1 based on cmd")
-    ("arg2", po::value<string>(&arg2), "arg2 based on cmd")
-    ;
-
-  po::options_description all("All options");
-  all.add(desc).add(positional);
-
-  po::positional_options_description pd;
-  pd.add("object", 1).add("objcmd", 1).add("arg1", 1).add("arg2", 1);
-
-  po::variables_map vm;
-  po::parsed_options parsed =
-   po::command_line_parser(argc, argv).options(all).allow_unregistered().positional(pd).run();
-  po::store( parsed, vm);
-  try {
-    po::notify(vm);
-  }
-  catch(...) {
-    usage(desc);
-  }
-
-  if (vm.count("help")) {
-    usage(desc);
-  }
-
-  if (!vm.count("filestore-path")) {
-    cerr << "Must provide --filestore-path" << std::endl;
-    usage(desc);
-  } 
-  if (!vm.count("journal-path")) {
-    cerr << "Must provide --journal-path" << std::endl;
-    usage(desc);
-  } 
-  if (vm.count("object") && !vm.count("objcmd")) {
-    cerr << "Invalid syntax, missing command" << std::endl;
-    usage(desc);
-  }
-  if (!vm.count("type") && !(vm.count("object") && vm.count("objcmd"))) {
-    cerr << "Must provide --type or object command..."
-      << std::endl;
-    usage(desc);
-  } 
-  if (vm.count("type") && vm.count("object")) {
-    cerr << "Can't specify both --type and object command syntax" << std::endl;
-    usage(desc);
-  }
-  if (type != "import" && !vm.count("pgid")) {
-    cerr << "Must provide pgid" << std::endl;
-    usage(desc);
-  } 
-
-  if (vm.count("object")) {
-    json_spirit::Value v;
-    try {
-      if (!json_spirit::read(object, v))
-        throw std::runtime_error("bad json");
-      ghobj.decode(v);
-    } catch (std::runtime_error& e) {
-      cerr << "error parsing offset: " << e.what() << std::endl;
-      exit(1);
-    }
-  }
-
-  outistty = isatty(STDOUT_FILENO);
-
-  file_fd = fd_none;
-  if (type == "export") {
-    if (!vm.count("file")) {
-      if (outistty) {
-        cerr << "stdout is a tty and no --file option specified" << std::endl;
-        exit(1);
-      }
-      file_fd = STDOUT_FILENO;
-    } else {
-      file_fd = open(file.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0666);
-    }
-  } else if (type == "import") {
-    if (!vm.count("file")) {
-      if (isatty(STDIN_FILENO)) {
-        cerr << "stdin is a tty and no --file option specified" << std::endl;
-        exit(1);
-      }
-      file_fd = STDIN_FILENO;
-    } else {
-      file_fd = open(file.c_str(), O_RDONLY);
-    }
-  }
-
-  if (vm.count("file") && file_fd == fd_none) {
-    cerr << "--file option only applies to import or export" << std::endl;
-    return 1;
-  }
-
-  if (file_fd != fd_none && file_fd < 0) {
-    perror("open");
-    return 1;
-  }
-  
-  if ((fspath.length() == 0 || jpath.length() == 0) ||
-      (type != "import" && pgidstr.length() == 0)) {
-    cerr << "Invalid params" << std::endl;
-    return 1;
-  }
-
-  if (type == "import" && pgidstr.length()) {
-    cerr << "--pgid option invalid with import" << std::endl;
-    return 1;
-  }
-
-  vector<const char *> ceph_options, def_args;
-  vector<string> ceph_option_strings = po::collect_unrecognized(
-    parsed.options, po::include_positional);
-  ceph_options.reserve(ceph_option_strings.size());
-  for (vector<string>::iterator i = ceph_option_strings.begin();
-       i != ceph_option_strings.end();
-       ++i) {
-    ceph_options.push_back(i->c_str());
-  }
-
-  if (!vm.count("debug")) {
-    debug = false;
-  } else {
-    debug = true;
-  }
-
-  osflagbits_t flags = 0;
-  if (vm.count("skip-journal-replay"))
-    flags |= SKIP_JOURNAL_REPLAY;
-  if (vm.count("skip-mount-omap"))
-    flags |= SKIP_MOUNT_OMAP;
-
-  global_init(
-    &def_args, ceph_options, CEPH_ENTITY_TYPE_OSD,
-    CODE_ENVIRONMENT_UTILITY_NODOUT, 0);
-    //CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
-  common_init_finish(g_ceph_context);
-  g_conf = g_ceph_context->_conf;
-  if (debug) {
-    g_conf->set_val_or_die("log_to_stderr", "true");
-    g_conf->set_val_or_die("err_to_stderr", "true");
-  }
-  g_conf->apply_changes(NULL);
-
-  //Verify that fspath really is an osd store
-  struct stat st;
-  if (::stat(fspath.c_str(), &st) == -1) {
-     perror("fspath");
-     invalid_path(fspath);
-  }
-  if (!S_ISDIR(st.st_mode)) {
-    invalid_path(fspath);
-  }
-  string check = fspath + "/whoami";
-  if (::stat(check.c_str(), &st) == -1) {
-     perror("whoami");
-     invalid_path(fspath);
-  }
-  if (!S_ISREG(st.st_mode)) {
-    invalid_path(fspath);
-  }
-  check = fspath + "/current";
-  if (::stat(check.c_str(), &st) == -1) {
-     perror("current");
-     invalid_path(fspath);
-  }
-  if (!S_ISDIR(st.st_mode)) {
-    invalid_path(fspath);
-  }
-
-  spg_t pgid;
-  if (pgidstr.length() && !pgid.parse(pgidstr.c_str())) {
-    cerr << "Invalid pgid '" << pgidstr << "' specified" << std::endl;
-    return 1;
-  }
-
-  ObjectStore *fs = ObjectStore::create(NULL, "filestore", fspath, jpath, flags);
-  
-  int r = fs->mount();
-  if (r < 0) {
-    if (r == -EBUSY) {
-      cerr << "OSD has the store locked" << std::endl;
-    } else {
-      cerr << "Mount failed with '" << cpp_strerror(-r) << "'" << std::endl;
-    }
-    return 1;
-  }
-
-  bool fs_sharded_objects = fs->get_allow_sharded_objects();
-
-  int ret = 0;
-  vector<coll_t> ls;
-  vector<coll_t>::iterator it;
-  CompatSet supported;
-
-#ifdef INTERNAL_TEST
-  supported = get_test_compat_set();
-#else
-  supported = OSD::get_osd_compat_set();
-#endif
-
-  bufferlist bl;
-  OSDSuperblock superblock;
-  bufferlist::iterator p;
-  r = fs->read(META_COLL, OSD_SUPERBLOCK_POBJECT, 0, 0, bl);
-  if (r < 0) {
-    cerr << "Failure to read OSD superblock error= " << r << std::endl;
-    goto out;
-  }
-
-  p = bl.begin();
-  ::decode(superblock, p);
-
-#ifdef INTERNAL_TEST2
-  fs->set_allow_sharded_objects();
-  assert(fs->get_allow_sharded_objects());
-  fs_sharded_objects = true;
-  superblock.compat_features.incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_SHARDS);
-#endif
-
-  if (debug) {
-    cerr << "Supported features: " << supported << std::endl;
-    cerr << "On-disk features: " << superblock.compat_features << std::endl;
-  }
-  if (supported.compare(superblock.compat_features) == -1) {
-    cerr << "On-disk OSD incompatible features set "
-      << superblock.compat_features << std::endl;
-    ret = EINVAL;
-    goto out;
-  }
-
-  // If there was a crash as an OSD was transitioning to sharded objects
-  // and hadn't completed a set_allow_sharded_objects().
-  // This utility does not want to attempt to finish that transition.
-  if (superblock.compat_features.incompat.contains(CEPH_OSD_FEATURE_INCOMPAT_SHARDS) != fs_sharded_objects) {
-    // An OSD should never have call set_allow_sharded_objects() before
-    // updating its own OSD features.
-    if (fs_sharded_objects)
-      cerr << "FileStore sharded but OSD not set, Corruption?" << std::endl;
-    else
-      cerr << "Found incomplete transition to sharded objects" << std::endl;
-    ret = EINVAL;
-    goto out;
-  }
-
-  if (type == "import") {
-
-    try {
-      ret = do_import(fs, superblock);
-    }
-    catch (const buffer::error &e) {
-      cerr << "do_import threw exception error " << e.what() << std::endl;
-      ret = EFAULT;
-    }
-    if (ret == EFAULT) {
-      cerr << "Corrupt input for import" << std::endl;
-    }
-    if (ret == 0)
-      cout << "Import successful" << std::endl;
-    goto out;
-  }
-
-  log_oid = OSD::make_pg_log_oid(pgid);
-  biginfo_oid = OSD::make_pg_biginfo_oid(pgid);
-
-  if (type == "remove") {
-    uint64_t next_removal_seq = 0;     //My local seq
-    finish_remove_pgs(fs, &next_removal_seq);
-    int r = initiate_new_remove_pg(fs, pgid, &next_removal_seq);
-    if (r) {
-      cerr << "PG '" << pgid << "' not found" << std::endl;
-      ret = 1;
-      goto out;
-    }
-    finish_remove_pgs(fs, &next_removal_seq);
-    cout << "Remove successful" << std::endl;
-    goto out;
-  }
-
-  r = fs->list_collections(ls);
-  if (r < 0) {
-    cerr << "failed to list pgs: " << cpp_strerror(-r) << std::endl;
-    ret = 1;
-    goto out;
-  }
-
-  for (it = ls.begin(); it != ls.end(); ++it) {
-    snapid_t snap;
-    spg_t tmppgid;
-
-    if (!it->is_pg(tmppgid, snap)) {
-      continue;
-    }
-
-    if (it->is_temp(tmppgid)) {
-      continue;
-    }
-
-    if (tmppgid != pgid) {
-      continue;
-    }
-    if (snap != CEPH_NOSNAP && debug) {
-      cerr << "skipping snapped dir " << *it
-              << " (pg " << pgid << " snap " << snap << ")" << std::endl;
-      continue;
-    }
-
-    //Found!
-    break;
-  }
-
-  epoch_t map_epoch;
-// The following code for export, info, log require omap or !skip-mount-omap
-  if (it != ls.end()) {
-  
-    coll_t coll = *it;
-  
-    if (vm.count("objcmd")) {
-      ret = 0;
-      if (objcmd == "remove") {
-        int r = do_remove_object(fs, coll, ghobj);
-        if (r) {
-          ret = 1;
-        }
-        goto out;
-      } else if (objcmd == "list-attrs") {
-        int r = do_list_attrs(fs, coll, ghobj);
-        if (r) {
-          ret = 1;
-        }
-        goto out;
-      } else if (objcmd == "list-omap") {
-        int r = do_list_omap(fs, coll, ghobj);
-        if (r) {
-          ret = 1;
-        }
-        goto out;
-      } else if (objcmd == "get-bytes" || objcmd == "set-bytes") {
-        int r;
-        if (objcmd == "get-bytes") {
-          int fd;
-          if (vm.count("arg1") == 0 || arg1 == "-") {
-            fd = STDOUT_FILENO;
-         } else {
-            fd = open(arg1.c_str(), O_WRONLY|O_TRUNC|O_CREAT|O_EXCL|O_LARGEFILE, 0666);
-            if (fd == -1) {
-              cerr << "open " << arg1 << " " << cpp_strerror(errno) << std::endl;
-              ret = 1;
-              goto out;
-            }
-          }
-          r = do_get_bytes(fs, coll, ghobj, fd);
-          if (fd != STDOUT_FILENO)
-            close(fd);
-        } else {
-          int fd;
-          if (vm.count("arg1") == 0 || arg1 == "-") {
-            fd = STDIN_FILENO;
-         } else {
-            fd = open(arg1.c_str(), O_RDONLY|O_LARGEFILE, 0666);
-            if (fd == -1) {
-              cerr << "open " << arg1 << " " << cpp_strerror(errno) << std::endl;
-              ret = 1;
-              goto out;
-            }
-          }
-          r = do_set_bytes(fs, coll, ghobj, fd);
-          if (fd != STDIN_FILENO)
-            close(fd);
-        }
-        if (r)
-          ret = 1;
-        goto out;
-      } else if (objcmd == "get-attr") {
-       if (vm.count("arg1") == 0)
-         usage(desc);
-       r = do_get_attr(fs, coll, ghobj, arg1);
-       if (r)
-         ret = 1;
-        goto out;
-      } else if (objcmd == "set-attr") {
-       if (vm.count("arg1") == 0)
-         usage(desc);
-
-       int fd;
-       if (vm.count("arg2") == 0 || arg2 == "-") {
-         fd = STDIN_FILENO;
-       } else {
-         fd = open(arg2.c_str(), O_RDONLY|O_LARGEFILE, 0666);
-         if (fd == -1) {
-           cerr << "open " << arg2 << " " << cpp_strerror(errno) << std::endl;
-           ret = 1;
-           goto out;
-         }
-       }
-       r = do_set_attr(fs, coll, ghobj, arg1, fd);
-       if (fd != STDIN_FILENO)
-         close(fd);
-       if (r)
-         ret = 1;
-        goto out;
-      } else if (objcmd == "rm-attr") {
-       if (vm.count("arg1") == 0)
-         usage(desc);
-       r = do_rm_attr(fs, coll, ghobj, arg1);
-       if (r)
-         ret = 1;
-        goto out;
-      } else if (objcmd == "get-omap") {
-       if (vm.count("arg1") == 0)
-         usage(desc);
-       r = do_get_omap(fs, coll, ghobj, arg1);
-       if (r)
-         ret = 1;
-        goto out;
-      } else if (objcmd == "set-omap") {
-       if (vm.count("arg1") == 0)
-         usage(desc);
-
-       int fd;
-       if (vm.count("arg2") == 0 || arg2 == "-") {
-         fd = STDIN_FILENO;
-       } else {
-         fd = open(arg2.c_str(), O_RDONLY|O_LARGEFILE, 0666);
-         if (fd == -1) {
-           cerr << "open " << arg2 << " " << cpp_strerror(errno) << std::endl;
-           ret = 1;
-           goto out;
-         }
-       }
-       r = do_set_omap(fs, coll, ghobj, arg1, fd);
-       if (fd != STDIN_FILENO)
-         close(fd);
-       if (r)
-         ret = 1;
-        goto out;
-      } else if (objcmd == "rm-omap") {
-       if (vm.count("arg1") == 0)
-         usage(desc);
-       r = do_rm_omap(fs, coll, ghobj, arg1);
-       if (r)
-         ret = 1;
-        goto out;
-      } else if (objcmd == "get-omaphdr") {
-       if (vm.count("arg1"))
-         usage(desc);
-       r = do_get_omaphdr(fs, coll, ghobj);
-       if (r)
-         ret = 1;
-        goto out;
-      } else if (objcmd == "set-omaphdr") {
-        // Extra arg
-       if (vm.count("arg2"))
-         usage(desc);
-       int fd;
-       if (vm.count("arg1") == 0 || arg1 == "-") {
-         fd = STDIN_FILENO;
-       } else {
-         fd = open(arg1.c_str(), O_RDONLY|O_LARGEFILE, 0666);
-         if (fd == -1) {
-           cerr << "open " << arg1 << " " << cpp_strerror(errno) << std::endl;
-           ret = 1;
-           goto out;
-         }
-       }
-       r = do_set_omaphdr(fs, coll, ghobj, fd);
-       if (fd != STDIN_FILENO)
-         close(fd);
-       if (r)
-         ret = 1;
-        goto out;
-      }
-      cerr << "Unknown object command '" << objcmd << "'" << std::endl;
-      usage(desc);
-    }
-
-    if (type == "list") {
-      Formatter *formatter = new JSONFormatter(false);
-      r = do_list(fs, coll, formatter);
-      if (r) {
-        cerr << "do_list failed with " << r << std::endl;
-        ret = 1;
-      }
-      goto out;
-    }
-
-    Formatter *formatter = new JSONFormatter(true);
-    bufferlist bl;
-    map_epoch = PG::peek_map_epoch(fs, coll, infos_oid, &bl);
-    if (debug)
-      cerr << "map_epoch " << map_epoch << std::endl;
-
-    pg_info_t info(pgid);
-    map<epoch_t,pg_interval_t> past_intervals;
-    hobject_t biginfo_oid = OSD::make_pg_biginfo_oid(pgid);
-    interval_set<snapid_t> snap_collections;
-  
-    __u8 struct_ver;
-    r = PG::read_info(fs, coll, bl, info, past_intervals, biginfo_oid,
-      infos_oid, snap_collections, struct_ver);
-    if (r < 0) {
-      cerr << "read_info error " << cpp_strerror(-r) << std::endl;
-      ret = 1;
-      goto out;
-    }
-    if (debug)
-      cerr << "struct_v " << (int)struct_ver << std::endl;
-
-    if (type == "export") {
-      ret = do_export(fs, coll, pgid, info, map_epoch, struct_ver, superblock);
-      if (ret == 0 && file_fd != STDOUT_FILENO)
-        cout << "Export successful" << std::endl;
-    } else if (type == "info") {
-      formatter->open_object_section("info");
-      info.dump(formatter);
-      formatter->close_section();
-      formatter->flush(cout);
-      cout << std::endl;
-    } else if (type == "log") {
-      PGLog::IndexedLog log;
-      pg_missing_t missing;
-      ret = get_log(fs, coll, pgid, info, log, missing);
-      if (ret > 0)
-          goto out;
-  
-      formatter->open_object_section("log");
-      log.dump(formatter);
-      formatter->close_section();
-      formatter->flush(cout);
-      cout << std::endl;
-      formatter->open_object_section("missing");
-      missing.dump(formatter);
-      formatter->close_section();
-      formatter->flush(cout);
-      cout << std::endl;
-    } else {
-      cerr << "Must provide --type (info, log, remove, export, import, list)"
-       << std::endl;
-      usage(desc);
-    }
-  } else {
-    cerr << "PG '" << pgid << "' not found" << std::endl;
-    ret = 1;
-  }
-
-out:
-  if (fs->umount() < 0) {
-    cerr << "umount failed" << std::endl;
-    return 1;
-  }
-
-  return (ret != 0);
-}
-
diff --git a/src/tools/ceph_filestore_tool.cc b/src/tools/ceph_filestore_tool.cc
deleted file mode 100644 (file)
index e461ebe..0000000
+++ /dev/null
@@ -1,253 +0,0 @@
-// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
-// vim: ts=8 sw=2 smarttab
-/*
- * Ceph - scalable distributed file system
- *
- * Copyright (C) 2013 Inktank
- *
- * This is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2.1, as published by the Free Software
- * Foundation.  See file COPYING.
- *
- */
-
-#include <boost/program_options/variables_map.hpp>
-#include <boost/program_options/parsers.hpp>
-
-#include <stdlib.h>
-
-#include "common/Formatter.h"
-#include "common/errno.h"
-
-#include "global/global_init.h"
-
-#include "os/ObjectStore.h"
-#include "os/FileStore.h"
-
-#include "osd/PGLog.h"
-#include "osd/osd_types.h"
-#include "osd/OSD.h"
-
-namespace po = boost::program_options;
-using namespace std;
-
-static void invalid_path(string &path)
-{
-  cout << "Invalid path to osd store specified: " << path << "\n";
-  exit(1);
-}
-
-int main(int argc, char **argv)
-{
-  string fspath, jpath, pgidstr;
-  bool list_lost_objects = false;
-  bool fix_lost_objects = false;
-  unsigned LIST_AT_A_TIME = 100;
-  unsigned scanned = 0;
-  
-  po::options_description desc("Allowed options");
-  desc.add_options()
-    ("help", "produce help message")
-    ("filestore-path", po::value<string>(&fspath),
-     "path to filestore directory, mandatory")
-    ("journal-path", po::value<string>(&jpath),
-     "path to journal, mandatory")
-    ("pgid", po::value<string>(&pgidstr),
-     "PG id")
-    ("list-lost-objects", po::value<bool>(
-      &list_lost_objects)->default_value(false),
-     "list lost objects")
-    ("fix-lost-objects", po::value<bool>(
-      &fix_lost_objects)->default_value(false),
-     "fix lost objects")
-    ;
-
-  po::variables_map vm;
-  po::parsed_options parsed =
-   po::command_line_parser(argc, argv).options(desc).
-    allow_unregistered().run();
-  po::store( parsed, vm);
-  try {
-    po::notify(vm);
-  }
-  catch(...) {
-    cout << desc << std::endl;
-    exit(1);
-  }
-     
-  if (vm.count("help")) {
-    cout << desc << std::endl;
-    return 1;
-  }
-
-  if (!vm.count("filestore-path")) {
-    cerr << "Must provide filestore-path" << std::endl
-        << desc << std::endl;
-    return 1;
-  } 
-  if (!vm.count("journal-path")) {
-    cerr << "Must provide journal-path" << std::endl
-        << desc << std::endl;
-    return 1;
-  } 
-
-  if ((fspath.length() == 0 || jpath.length() == 0)) {
-    cerr << "Invalid params" << desc << std::endl;
-    exit(1);
-  }
-
-  vector<const char *> ceph_options, def_args;
-  vector<string> ceph_option_strings = po::collect_unrecognized(
-    parsed.options, po::include_positional);
-  ceph_options.reserve(ceph_option_strings.size());
-  for (vector<string>::iterator i = ceph_option_strings.begin();
-       i != ceph_option_strings.end();
-       ++i) {
-    ceph_options.push_back(i->c_str());
-  }
-
-  global_init(
-    &def_args, ceph_options, CEPH_ENTITY_TYPE_OSD,
-    CODE_ENVIRONMENT_UTILITY, 0);
-    //CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
-  common_init_finish(g_ceph_context);
-  g_ceph_context->_conf->apply_changes(NULL);
-  g_conf = g_ceph_context->_conf;
-
-  //Verify that fspath really is an osd store
-  struct stat st;
-  if (::stat(fspath.c_str(), &st) == -1) {
-     perror("fspath");
-     invalid_path(fspath);
-  }
-  if (!S_ISDIR(st.st_mode)) {
-    invalid_path(fspath);
-  }
-  string check = fspath + "/whoami";
-  if (::stat(check.c_str(), &st) == -1) {
-     perror("whoami");
-     invalid_path(fspath);
-  }
-  if (!S_ISREG(st.st_mode)) {
-    invalid_path(fspath);
-  }
-  check = fspath + "/current";
-  if (::stat(check.c_str(), &st) == -1) {
-     perror("current");
-     invalid_path(fspath);
-  }
-  if (!S_ISDIR(st.st_mode)) {
-    invalid_path(fspath);
-  }
-
-  ObjectStore *fs = new FileStore(fspath, jpath);
-
-  int r = fs->mount();
-  if (r < 0) {
-    if (r == -EBUSY) {
-      cout << "OSD has the store locked" << std::endl;
-    } else {
-      cout << "Mount failed with '" << cpp_strerror(-r) << "'" << std::endl;
-    }
-    return 1;
-  }
-
-  vector<coll_t> colls_to_check;
-  if (pgidstr.length()) {
-    spg_t pgid;
-    if (!pgid.parse(pgidstr.c_str())) {
-      cout << "Invalid pgid '" << pgidstr << "' specified" << std::endl;
-      exit(1);
-    }
-    colls_to_check.push_back(coll_t(pgid));
-  } else {
-    vector<coll_t> candidates;
-    r = fs->list_collections(candidates);
-    if (r < 0) {
-      cerr << "Error listing collections: " << cpp_strerror(r) << std::endl;
-      goto UMOUNT;
-    }
-    for (vector<coll_t>::iterator i = candidates.begin();
-        i != candidates.end();
-        ++i) {
-      spg_t pgid;
-      snapid_t snap;
-      if (i->is_pg(pgid, snap)) {
-       colls_to_check.push_back(*i);
-      }
-    }
-  }
-
-  cerr << colls_to_check.size() << " pgs to scan" << std::endl;
-  for (vector<coll_t>::iterator i = colls_to_check.begin();
-       i != colls_to_check.end();
-       ++i, ++scanned) {
-    cerr << "Scanning " << *i << ", " << scanned << "/"
-        << colls_to_check.size() << " completed" << std::endl;
-    ghobject_t next;
-    while (!next.is_max()) {
-      vector<ghobject_t> list;
-      r = fs->collection_list_partial(
-       *i,
-       next,
-       LIST_AT_A_TIME,
-       LIST_AT_A_TIME,
-       CEPH_NOSNAP,
-       &list,
-       &next);
-      if (r < 0) {
-       cerr << "Error listing collection: " << *i << ", "
-            << cpp_strerror(r) << std::endl;
-       goto UMOUNT;
-      }
-      for (vector<ghobject_t>::iterator obj = list.begin();
-          obj != list.end();
-          ++obj) {
-       bufferlist attr;
-       r = fs->getattr(*i, *obj, OI_ATTR, attr);
-       if (r < 0) {
-         cerr << "Error getting attr on : " << make_pair(*i, *obj) << ", "
-              << cpp_strerror(r) << std::endl;
-         goto UMOUNT;
-       }
-       object_info_t oi;
-       bufferlist::iterator bp = attr.begin();
-       try {
-         ::decode(oi, bp);
-       } catch (...) {
-         r = -EINVAL;
-         cerr << "Error getting attr on : " << make_pair(*i, *obj) << ", "
-              << cpp_strerror(r) << std::endl;
-         goto UMOUNT;
-       }
-       if (oi.is_lost()) {
-         if (list_lost_objects) {
-           cout << *i << "/" << *obj << " is lost" << std::endl;
-         }
-         if (fix_lost_objects) {
-           cerr << *i << "/" << *obj << " is lost, fixing" << std::endl;
-           oi.clear_flag(object_info_t::FLAG_LOST);
-           bufferlist bl2;
-           ::encode(oi, bl2);
-           ObjectStore::Transaction t;
-           t.setattr(*i, *obj, OI_ATTR, bl2);
-           r = fs->apply_transaction(t);
-           if (r < 0) {
-             cerr << "Error getting fixing attr on : " << make_pair(*i, *obj)
-                  << ", "
-                  << cpp_strerror(r) << std::endl;
-             goto UMOUNT;
-           }
-         }
-       }
-      }
-    }
-  }
-  cerr << "Completed" << std::endl;
-
- UMOUNT:
-  fs->sync_and_flush();
-  fs->umount();
-  return r;
-}
diff --git a/src/tools/ceph_objectstore_tool.cc b/src/tools/ceph_objectstore_tool.cc
new file mode 100644 (file)
index 0000000..6655497
--- /dev/null
@@ -0,0 +1,2076 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 Inktank
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation.  See file COPYING.
+ *
+ */
+
+#include <boost/program_options/variables_map.hpp>
+#include <boost/program_options/parsers.hpp>
+
+#include <stdlib.h>
+
+#include "common/Formatter.h"
+#include "common/errno.h"
+
+#include "global/global_init.h"
+
+#include "os/ObjectStore.h"
+#include "os/FileStore.h"
+
+#include "osd/PGLog.h"
+#include "osd/OSD.h"
+
+#include "json_spirit/json_spirit_value.h"
+#include "json_spirit/json_spirit_reader.h"
+
+namespace po = boost::program_options;
+using namespace std;
+
+static coll_t META_COLL("meta");
+
+enum {
+    TYPE_NONE = 0,
+    TYPE_PG_BEGIN,
+    TYPE_PG_END,
+    TYPE_OBJECT_BEGIN,
+    TYPE_OBJECT_END,
+    TYPE_DATA,
+    TYPE_ATTRS,
+    TYPE_OMAP_HDR,
+    TYPE_OMAP,
+    TYPE_PG_METADATA,
+    END_OF_TYPES,      //Keep at the end
+};
+
+//#define INTERNAL_TEST
+//#define INTERNAL_TEST2
+
+#ifdef INTERNAL_TEST
+CompatSet get_test_compat_set() {
+  CompatSet::FeatureSet ceph_osd_feature_compat;
+  CompatSet::FeatureSet ceph_osd_feature_ro_compat;
+  CompatSet::FeatureSet ceph_osd_feature_incompat;
+  ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_BASE);
+  ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_PGINFO);
+  ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_OLOC);
+  ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_LEC);
+  ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_CATEGORIES);
+  ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_HOBJECTPOOL);
+  ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_BIGINFO);
+  ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_LEVELDBINFO);
+  ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_LEVELDBLOG);
+#ifdef INTERNAL_TEST2
+  ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_SNAPMAPPER);
+  ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_SHARDS);
+#endif
+  return CompatSet(ceph_osd_feature_compat, ceph_osd_feature_ro_compat,
+                  ceph_osd_feature_incompat);
+}
+#endif
+
+typedef uint8_t sectiontype_t;
+typedef uint32_t mymagic_t;
+typedef int64_t mysize_t;
+const ssize_t max_read = 1024 * 1024;
+const uint16_t shortmagic = 0xffce;    //goes into stream as "ceff"
+//endmagic goes into stream as "ceff ffec"
+const mymagic_t endmagic = (0xecff << 16) | shortmagic;
+const int fd_none = INT_MIN;
+bool outistty;
+
+//The first FIXED_LENGTH bytes are a fixed
+//portion of the export output.  This includes the overall
+//version number, and size of header and footer.
+//THIS STRUCTURE CAN ONLY BE APPENDED TO.  If it needs to expand,
+//the version can be bumped and then anything
+//can be added to the export format.
+struct super_header {
+  static const uint32_t super_magic = (shortmagic << 16) | shortmagic;
+  static const uint32_t super_ver = 2;
+  static const uint32_t FIXED_LENGTH = 16;
+  uint32_t magic;
+  uint32_t version;
+  uint32_t header_size;
+  uint32_t footer_size;
+
+  super_header() : magic(0), version(0), header_size(0), footer_size(0) { }
+  int read_super();
+
+  void encode(bufferlist& bl) const {
+    ::encode(magic, bl);
+    ::encode(version, bl);
+    ::encode(header_size, bl);
+    ::encode(footer_size, bl);
+  }
+  void decode(bufferlist::iterator& bl) {
+    ::decode(magic, bl);
+    ::decode(version, bl);
+    ::decode(header_size, bl);
+    ::decode(footer_size, bl);
+  }
+};
+
+struct header {
+  sectiontype_t type;
+  mysize_t size;
+  header(sectiontype_t type, mysize_t size) :
+    type(type), size(size) { }
+  header(): type(0), size(0) { }
+
+  int get_header();
+
+  void encode(bufferlist& bl) const {
+    uint32_t debug_type = (type << 24) | (type << 16) | shortmagic;
+    ENCODE_START(1, 1, bl);
+    ::encode(debug_type, bl);
+    ::encode(size, bl);
+    ENCODE_FINISH(bl);
+  }
+  void decode(bufferlist::iterator& bl) {
+    uint32_t debug_type;
+    DECODE_START(1, bl);
+    ::decode(debug_type, bl);
+    type = debug_type >> 24;
+    ::decode(size, bl);
+    DECODE_FINISH(bl);
+  }
+};
+
+struct footer {
+  mymagic_t magic;
+  footer() : magic(endmagic) { }
+
+  int get_footer();
+
+  void encode(bufferlist& bl) const {
+    ENCODE_START(1, 1, bl);
+    ::encode(magic, bl);
+    ENCODE_FINISH(bl);
+  }
+  void decode(bufferlist::iterator& bl) {
+    DECODE_START(1, bl);
+    ::decode(magic, bl);
+    DECODE_FINISH(bl);
+  }
+};
+
+struct pg_begin {
+  spg_t pgid;
+  OSDSuperblock superblock;
+
+  pg_begin(spg_t pg, const OSDSuperblock& sb):
+    pgid(pg), superblock(sb) { }
+  pg_begin() { }
+
+  void encode(bufferlist& bl) const {
+    // If superblock doesn't include CEPH_FS_FEATURE_INCOMPAT_SHARDS then
+    // shard will be NO_SHARD for a replicated pool.  This means
+    // that we allow the decode by struct_v 2.
+    ENCODE_START(3, 2, bl);
+    ::encode(pgid.pgid, bl);
+    ::encode(superblock, bl);
+    ::encode(pgid.shard, bl);
+    ENCODE_FINISH(bl);
+  }
+  // NOTE: New super_ver prevents decode from ver 1
+  void decode(bufferlist::iterator& bl) {
+    DECODE_START(3, bl);
+    ::decode(pgid.pgid, bl);
+    if (struct_v > 1) {
+      ::decode(superblock, bl);
+    }
+    if (struct_v > 2) {
+      ::decode(pgid.shard, bl);
+    } else {
+      pgid.shard = shard_id_t::NO_SHARD;
+    }
+    DECODE_FINISH(bl);
+  }
+};
+
+struct object_begin {
+  ghobject_t hoid;
+  object_begin(const ghobject_t &hoid): hoid(hoid) { }
+  object_begin() { }
+
+  // If superblock doesn't include CEPH_FS_FEATURE_INCOMPAT_SHARDS then
+  // generation will be NO_GEN, shard_id will be NO_SHARD for a replicated
+  // pool.  This means we will allow the decode by struct_v 1.
+  void encode(bufferlist& bl) const {
+    ENCODE_START(2, 1, bl);
+    ::encode(hoid.hobj, bl);
+    ::encode(hoid.generation, bl);
+    ::encode(hoid.shard_id, bl);
+    ENCODE_FINISH(bl);
+  }
+  void decode(bufferlist::iterator& bl) {
+    DECODE_START(2, bl);
+    ::decode(hoid.hobj, bl);
+    if (struct_v > 1) {
+      ::decode(hoid.generation, bl);
+      ::decode(hoid.shard_id, bl);
+    } else {
+      hoid.generation = ghobject_t::NO_GEN;
+      hoid.shard_id = shard_id_t::NO_SHARD;
+    }
+    DECODE_FINISH(bl);
+  }
+};
+
+struct data_section {
+  uint64_t offset;
+  uint64_t len;
+  bufferlist databl;
+  data_section(uint64_t offset, uint64_t len, bufferlist bl):
+     offset(offset), len(len), databl(bl) { }
+  data_section(): offset(0), len(0) { }
+
+  void encode(bufferlist& bl) const {
+    ENCODE_START(1, 1, bl);
+    ::encode(offset, bl);
+    ::encode(len, bl);
+    ::encode(databl, bl);
+    ENCODE_FINISH(bl);
+  }
+  void decode(bufferlist::iterator& bl) {
+    DECODE_START(1, bl);
+    ::decode(offset, bl);
+    ::decode(len, bl);
+    ::decode(databl, bl);
+    DECODE_FINISH(bl);
+  }
+};
+
+struct attr_section {
+  map<string,bufferptr> data;
+  attr_section(const map<string,bufferptr> &data) : data(data) { }
+  attr_section() { }
+
+  void encode(bufferlist& bl) const {
+    ENCODE_START(1, 1, bl);
+    ::encode(data, bl);
+    ENCODE_FINISH(bl);
+  }
+  void decode(bufferlist::iterator& bl) {
+    DECODE_START(1, bl);
+    ::decode(data, bl);
+    DECODE_FINISH(bl);
+  }
+};
+
+struct omap_hdr_section {
+  bufferlist hdr;
+  omap_hdr_section(bufferlist hdr) : hdr(hdr) { }
+  omap_hdr_section() { }
+
+  void encode(bufferlist& bl) const {
+    ENCODE_START(1, 1, bl);
+    ::encode(hdr, bl);
+    ENCODE_FINISH(bl);
+  }
+  void decode(bufferlist::iterator& bl) {
+    DECODE_START(1, bl);
+    ::decode(hdr, bl);
+    DECODE_FINISH(bl);
+  }
+};
+
+struct omap_section {
+  map<string, bufferlist> omap;
+  omap_section(const map<string, bufferlist> &omap) :
+    omap(omap) { }
+  omap_section() { }
+
+  void encode(bufferlist& bl) const {
+    ENCODE_START(1, 1, bl);
+    ::encode(omap, bl);
+    ENCODE_FINISH(bl);
+  }
+  void decode(bufferlist::iterator& bl) {
+    DECODE_START(1, bl);
+    ::decode(omap, bl);
+    DECODE_FINISH(bl);
+  }
+};
+
+struct metadata_section {
+  __u8 struct_ver;
+  epoch_t map_epoch;
+  pg_info_t info;
+  pg_log_t log;
+
+  metadata_section(__u8 struct_ver, epoch_t map_epoch, const pg_info_t &info,
+                  const pg_log_t &log)
+    : struct_ver(struct_ver),
+      map_epoch(map_epoch),
+      info(info),
+      log(log) { }
+  metadata_section()
+    : struct_ver(0),
+      map_epoch(0) { }
+
+  void encode(bufferlist& bl) const {
+    ENCODE_START(1, 1, bl);
+    ::encode(struct_ver, bl);
+    ::encode(map_epoch, bl);
+    ::encode(info, bl);
+    ::encode(log, bl);
+    ENCODE_FINISH(bl);
+  }
+  void decode(bufferlist::iterator& bl) {
+    DECODE_START(1, bl);
+    ::decode(struct_ver, bl);
+    ::decode(map_epoch, bl);
+    ::decode(info, bl);
+    ::decode(log, bl);
+    DECODE_FINISH(bl);
+  }
+};
+
+hobject_t infos_oid = OSD::make_infos_oid();
+hobject_t biginfo_oid, log_oid;
+
+int file_fd = fd_none;
+bool debug = false;
+super_header sh;
+
+template <typename T>
+int write_section(sectiontype_t type, const T& obj, int fd) {
+  bufferlist blhdr, bl, blftr;
+  obj.encode(bl);
+  header hdr(type, bl.length());
+  hdr.encode(blhdr);
+  footer ft;
+  ft.encode(blftr);
+
+  int ret = blhdr.write_fd(fd);
+  if (ret) return ret;
+  ret = bl.write_fd(fd);
+  if (ret) return ret;
+  ret = blftr.write_fd(fd);
+  return ret;
+}
+
+// Convert non-printable characters to '\###'
+static void cleanbin(string &str)
+{
+  bool cleaned = false;
+  string clean;
+
+  for (string::iterator it = str.begin(); it != str.end(); ++it) {
+    if (!isprint(*it)) {
+      clean.push_back('\\');
+      clean.push_back('0' + ((*it >> 6) & 7));
+      clean.push_back('0' + ((*it >> 3) & 7));
+      clean.push_back('0' + (*it & 7));
+      cleaned = true;
+    } else {
+      clean.push_back(*it);
+    }
+  }
+
+  if (cleaned)
+    str = clean;
+  return;
+}
+
+int write_simple(sectiontype_t type, int fd)
+{
+  bufferlist hbl;
+
+  header hdr(type, 0);
+  hdr.encode(hbl);
+  return hbl.write_fd(fd);
+}
+
+static int get_fd_data(int fd, bufferlist &bl)
+{
+  uint64_t total = 0;
+  do {
+    ssize_t bytes = bl.read_fd(fd, max_read);
+    if (bytes < 0) {
+      cerr << "read_fd error " << cpp_strerror(-bytes) << std::endl;
+      return 1;
+    }
+
+    if (bytes == 0)
+      break;
+
+    total += bytes;
+  } while(true);
+
+  assert(bl.length() == total);
+  return 0;
+}
+
+static void invalid_path(string &path)
+{
+  cerr << "Invalid path to osd store specified: " << path << "\n";
+  exit(1);
+}
+
+int get_log(ObjectStore *fs, coll_t coll, spg_t pgid, const pg_info_t &info,
+   PGLog::IndexedLog &log, pg_missing_t &missing)
+{
+  map<eversion_t, hobject_t> divergent_priors;
+  try {
+    ostringstream oss;
+    PGLog::read_log(fs, coll, log_oid, info, divergent_priors, log, missing, oss);
+    if (debug && oss.str().size())
+      cerr << oss.str() << std::endl;
+  }
+  catch (const buffer::error &e) {
+    cerr << "read_log threw exception error " << e.what() << std::endl;
+    return 1;
+  }
+  return 0;
+}
+
+//Based on RemoveWQ::_process()
+void remove_coll(ObjectStore *store, const coll_t &coll)
+{
+  spg_t pg;
+  coll.is_pg_prefix(pg);
+  OSDriver driver(
+    store,
+    coll_t(),
+    OSD::make_snapmapper_oid());
+  SnapMapper mapper(&driver, 0, 0, 0, pg.shard);
+
+  vector<ghobject_t> objects;
+  ghobject_t next;
+  int r = 0;
+  int64_t num = 0;
+  ObjectStore::Transaction *t = new ObjectStore::Transaction;
+  cout << "remove_coll " << coll << std::endl;
+  while (!next.is_max()) {
+    r = store->collection_list_partial(coll, next, 200, 300, 0,
+      &objects, &next);
+    if (r < 0)
+      goto out;
+    for (vector<ghobject_t>::iterator i = objects.begin();
+        i != objects.end();
+        ++i, ++num) {
+
+      OSDriver::OSTransaction _t(driver.get_transaction(t));
+      cout << "remove " << *i << std::endl;
+      int r = mapper.remove_oid(i->hobj, &_t);
+      if (r != 0 && r != -ENOENT) {
+        assert(0);
+      }
+
+      t->remove(coll, *i);
+      if (num >= 30) {
+        store->apply_transaction(*t);
+        delete t;
+        t = new ObjectStore::Transaction;
+        num = 0;
+      }
+    }
+  }
+  t->remove_collection(coll);
+  store->apply_transaction(*t);
+out:
+  delete t;
+}
+
+//Based on part of OSD::load_pgs()
+int finish_remove_pgs(ObjectStore *store, uint64_t *next_removal_seq)
+{
+  vector<coll_t> ls;
+  int r = store->list_collections(ls);
+  if (r < 0) {
+    cerr << "finish_remove_pgs: failed to list pgs: " << cpp_strerror(-r)
+      << std::endl;
+    return r;
+  }
+
+  for (vector<coll_t>::iterator it = ls.begin();
+       it != ls.end();
+       ++it) {
+    spg_t pgid;
+    snapid_t snap;
+
+    if (it->is_temp(pgid)) {
+      cout << "finish_remove_pgs " << *it << " clearing temp" << std::endl;
+      OSD::recursive_remove_collection(store, *it);
+      continue;
+    }
+
+    if (it->is_pg(pgid, snap)) {
+      continue;
+    }
+
+    uint64_t seq;
+    if (it->is_removal(&seq, &pgid)) {
+      if (seq >= *next_removal_seq)
+       *next_removal_seq = seq + 1;
+      cout << "finish_remove_pgs removing " << *it << ", seq is "
+              << seq << " pgid is " << pgid << std::endl;
+      remove_coll(store, *it);
+      continue;
+    }
+
+    //cout << "finish_remove_pgs ignoring unrecognized " << *it << std::endl;
+  }
+  return 0;
+}
+
+int initiate_new_remove_pg(ObjectStore *store, spg_t r_pgid,
+    uint64_t *next_removal_seq)
+{
+  ObjectStore::Transaction *rmt = new ObjectStore::Transaction;
+
+  if (store->collection_exists(coll_t(r_pgid))) {
+      coll_t to_remove = coll_t::make_removal_coll((*next_removal_seq)++, r_pgid);
+      cout << "collection rename " << coll_t(r_pgid)
+          << " to " << to_remove
+        << std::endl;
+      rmt->collection_rename(coll_t(r_pgid), to_remove);
+  } else {
+    delete rmt;
+    return ENOENT;
+  }
+
+  cout << "remove " << META_COLL << " " << log_oid.oid << std::endl;
+  rmt->remove(META_COLL, log_oid);
+  cout << "remove " << META_COLL << " " << biginfo_oid.oid << std::endl;
+  rmt->remove(META_COLL, biginfo_oid);
+
+  store->apply_transaction(*rmt);
+
+  return 0;
+}
+
+int header::get_header()
+{
+  bufferlist ebl;
+  bufferlist::iterator ebliter = ebl.begin();
+  ssize_t bytes;
+
+  bytes = ebl.read_fd(file_fd, sh.header_size);
+  if ((size_t)bytes != sh.header_size) {
+    cerr << "Unexpected EOF" << std::endl;
+    return EFAULT;
+  }
+
+  decode(ebliter);
+
+  return 0;
+}
+
+int footer::get_footer()
+{
+  bufferlist ebl;
+  bufferlist::iterator ebliter = ebl.begin();
+  ssize_t bytes;
+
+  bytes = ebl.read_fd(file_fd, sh.footer_size);
+  if ((size_t)bytes != sh.footer_size) {
+    cerr << "Unexpected EOF" << std::endl;
+    return EFAULT;
+  }
+
+  decode(ebliter);
+
+  if (magic != endmagic) {
+    cerr << "Bad footer magic" << std::endl;
+    return EFAULT;
+  }
+
+  return 0;
+}
+
+int write_info(ObjectStore::Transaction &t, epoch_t epoch, pg_info_t &info,
+    __u8 struct_ver)
+{
+  //Empty for this
+  interval_set<snapid_t> snap_collections; // obsolete
+  map<epoch_t,pg_interval_t> past_intervals;
+  coll_t coll(info.pgid);
+
+  int ret = PG::_write_info(t, epoch,
+    info, coll,
+    past_intervals,
+    snap_collections,
+    infos_oid,
+    struct_ver,
+    true, true);
+  if (ret < 0) ret = -ret;
+  if (ret) cerr << "Failed to write info" << std::endl;
+  return ret;
+}
+
+void write_log(ObjectStore::Transaction &t, pg_log_t &log)
+{
+  map<eversion_t, hobject_t> divergent_priors;
+  PGLog::write_log(t, log, log_oid, divergent_priors);
+}
+
+int write_pg(ObjectStore::Transaction &t, epoch_t epoch, pg_info_t &info,
+    pg_log_t &log, __u8 struct_ver)
+{
+  int ret = write_info(t, epoch, info, struct_ver);
+  if (ret) return ret;
+  write_log(t, log);
+  return 0;
+}
+
+const int OMAP_BATCH_SIZE = 25;
+void get_omap_batch(ObjectMap::ObjectMapIterator &iter, map<string, bufferlist> &oset)
+{
+  oset.clear();
+  for (int count = OMAP_BATCH_SIZE; count && iter->valid(); --count, iter->next()) {
+    oset.insert(pair<string, bufferlist>(iter->key(), iter->value()));
+  }
+}
+
+int export_file(ObjectStore *store, coll_t cid, ghobject_t &obj)
+{
+  struct stat st;
+  mysize_t total;
+  footer ft;
+
+  int ret = store->stat(cid, obj, &st);
+  if (ret < 0)
+    return ret;
+
+  cerr << "read " << obj << std::endl;
+
+  total = st.st_size;
+  if (debug)
+    cerr << "size=" << total << std::endl;
+
+  object_begin objb(obj);
+  ret = write_section(TYPE_OBJECT_BEGIN, objb, file_fd);
+  if (ret < 0)
+    return ret;
+
+  uint64_t offset = 0;
+  bufferlist rawdatabl;
+  while(total > 0) {
+    rawdatabl.clear();
+    mysize_t len = max_read;
+    if (len > total)
+      len = total;
+
+    ret = store->read(cid, obj, offset, len, rawdatabl);
+    if (ret < 0)
+      return ret;
+    if (ret == 0)
+      return -EINVAL;
+
+    data_section dblock(offset, len, rawdatabl);
+    if (debug)
+      cerr << "data section offset=" << offset << " len=" << len << std::endl;
+
+    total -= ret;
+    offset += ret;
+
+    ret = write_section(TYPE_DATA, dblock, file_fd);
+    if (ret) return ret;
+  }
+
+  //Handle attrs for this object
+  map<string,bufferptr> aset;
+  ret = store->getattrs(cid, obj, aset);
+  if (ret) return ret;
+  attr_section as(aset);
+  ret = write_section(TYPE_ATTRS, as, file_fd);
+  if (ret)
+    return ret;
+
+  if (debug) {
+    cerr << "attrs size " << aset.size() << std::endl;
+  }
+
+  //Handle omap information
+  bufferlist hdrbuf;
+  ret = store->omap_get_header(cid, obj, &hdrbuf, true);
+  if (ret < 0) {
+    cerr << "omap_get_header: " << cpp_strerror(-ret) << std::endl;
+    return ret;
+  }
+
+  omap_hdr_section ohs(hdrbuf);
+  ret = write_section(TYPE_OMAP_HDR, ohs, file_fd);
+  if (ret)
+    return ret;
+
+  ObjectMap::ObjectMapIterator iter = store->get_omap_iterator(cid, obj);
+  if (!iter) {
+    ret = -ENOENT;
+    cerr << "omap_get_iterator: " << cpp_strerror(-ret) << std::endl;
+    return ret;
+  }
+  iter->seek_to_first();
+  int mapcount = 0;
+  map<string, bufferlist> out;
+  while(iter->valid()) {
+    get_omap_batch(iter, out);
+
+    if (out.empty()) break;
+
+    mapcount += out.size();
+    omap_section oms(out);
+    ret = write_section(TYPE_OMAP, oms, file_fd);
+    if (ret)
+      return ret;
+  }
+  if (debug)
+    cerr << "omap map size " << mapcount << std::endl;
+
+  ret = write_simple(TYPE_OBJECT_END, file_fd);
+  if (ret)
+    return ret;
+
+  return 0;
+}
+
+int export_files(ObjectStore *store, coll_t coll)
+{
+  vector<ghobject_t> objects;
+  ghobject_t next;
+
+  while (!next.is_max()) {
+    int r = store->collection_list_partial(coll, next, 200, 300, 0,
+      &objects, &next);
+    if (r < 0)
+      return r;
+    for (vector<ghobject_t>::iterator i = objects.begin();
+        i != objects.end();
+        ++i) {
+      r = export_file(store, coll, *i);
+      if (r < 0)
+        return r;
+    }
+  }
+  return 0;
+}
+
+//Write super_header with its fixed 16 byte length
+void write_super()
+{
+  bufferlist superbl;
+  super_header sh;
+  footer ft;
+
+  header hdr(TYPE_NONE, 0);
+  hdr.encode(superbl);
+
+  sh.magic = super_header::super_magic;
+  sh.version = super_header::super_ver;
+  sh.header_size = superbl.length();
+  superbl.clear();
+  ft.encode(superbl);
+  sh.footer_size = superbl.length();
+  superbl.clear();
+
+  sh.encode(superbl);
+  assert(super_header::FIXED_LENGTH == superbl.length());
+  superbl.write_fd(file_fd);
+}
+
+int do_export(ObjectStore *fs, coll_t coll, spg_t pgid, pg_info_t &info,
+    epoch_t map_epoch, __u8 struct_ver, const OSDSuperblock& superblock)
+{
+  PGLog::IndexedLog log;
+  pg_missing_t missing;
+
+  cerr << "Exporting " << pgid << std::endl;
+
+  int ret = get_log(fs, coll, pgid, info, log, missing);
+  if (ret > 0)
+      return ret;
+
+  write_super();
+
+  pg_begin pgb(pgid, superblock);
+  ret = write_section(TYPE_PG_BEGIN, pgb, file_fd);
+  if (ret)
+    return ret;
+
+  ret = export_files(fs, coll);
+  if (ret) {
+    cerr << "export_files error " << ret << std::endl;
+    return ret;
+  }
+
+  metadata_section ms(struct_ver, map_epoch, info, log);
+  ret = write_section(TYPE_PG_METADATA, ms, file_fd);
+  if (ret)
+    return ret;
+
+  ret = write_simple(TYPE_PG_END, file_fd);
+  if (ret)
+    return ret;
+
+  return 0;
+}
+
+int super_header::read_super()
+{
+  bufferlist ebl;
+  bufferlist::iterator ebliter = ebl.begin();
+  ssize_t bytes;
+
+  bytes = ebl.read_fd(file_fd, super_header::FIXED_LENGTH);
+  if ((size_t)bytes != super_header::FIXED_LENGTH) {
+    cerr << "Unexpected EOF" << std::endl;
+    return EFAULT;
+  }
+
+  decode(ebliter);
+
+  return 0;
+}
+
+int read_section(int fd, sectiontype_t *type, bufferlist *bl)
+{
+  header hdr;
+  ssize_t bytes;
+
+  int ret = hdr.get_header();
+  if (ret)
+    return ret;
+
+  *type = hdr.type;
+
+  bl->clear();
+  bytes = bl->read_fd(fd, hdr.size);
+  if (bytes != hdr.size) {
+    cerr << "Unexpected EOF" << std::endl;
+    return EFAULT;
+  }
+
+  if (hdr.size > 0) {
+    footer ft;
+    ret = ft.get_footer();
+    if (ret)
+      return ret;
+  }
+
+  return 0;
+}
+
+int get_data(ObjectStore *store, coll_t coll, ghobject_t hoid,
+    ObjectStore::Transaction *t, bufferlist &bl)
+{
+  bufferlist::iterator ebliter = bl.begin();
+  data_section ds;
+  ds.decode(ebliter);
+
+  if (debug)
+    cerr << "\tdata: offset " << ds.offset << " len " << ds.len << std::endl;
+  t->write(coll, hoid, ds.offset, ds.len,  ds.databl);
+  return 0;
+}
+
+int get_attrs(ObjectStore *store, coll_t coll, ghobject_t hoid,
+    ObjectStore::Transaction *t, bufferlist &bl,
+    OSDriver &driver, SnapMapper &snap_mapper)
+{
+  bufferlist::iterator ebliter = bl.begin();
+  attr_section as;
+  as.decode(ebliter);
+
+  if (debug)
+    cerr << "\tattrs: len " << as.data.size() << std::endl;
+  t->setattrs(coll, hoid, as.data);
+
+  if (hoid.hobj.snap < CEPH_MAXSNAP && hoid.generation == ghobject_t::NO_GEN) {
+    map<string,bufferptr>::iterator mi = as.data.find(OI_ATTR);
+    if (mi != as.data.end()) {
+      bufferlist attr_bl;
+      attr_bl.push_back(mi->second);
+      object_info_t oi(attr_bl);
+
+      if (debug)
+        cerr << "object_info " << oi << std::endl;
+
+      OSDriver::OSTransaction _t(driver.get_transaction(t));
+      set<snapid_t> oi_snaps(oi.snaps.begin(), oi.snaps.end());
+      snap_mapper.add_oid(hoid.hobj, oi_snaps, &_t);
+    }
+  }
+
+  return 0;
+}
+
+int get_omap_hdr(ObjectStore *store, coll_t coll, ghobject_t hoid,
+    ObjectStore::Transaction *t, bufferlist &bl)
+{
+  bufferlist::iterator ebliter = bl.begin();
+  omap_hdr_section oh;
+  oh.decode(ebliter);
+
+  if (debug)
+    cerr << "\tomap header: " << string(oh.hdr.c_str(), oh.hdr.length())
+      << std::endl;
+  t->omap_setheader(coll, hoid, oh.hdr);
+  return 0;
+}
+
+int get_omap(ObjectStore *store, coll_t coll, ghobject_t hoid,
+    ObjectStore::Transaction *t, bufferlist &bl)
+{
+  bufferlist::iterator ebliter = bl.begin();
+  omap_section os;
+  os.decode(ebliter);
+
+  if (debug)
+    cerr << "\tomap: size " << os.omap.size() << std::endl;
+  t->omap_setkeys(coll, hoid, os.omap);
+  return 0;
+}
+
+int get_object(ObjectStore *store, coll_t coll, bufferlist &bl)
+{
+  ObjectStore::Transaction tran;
+  ObjectStore::Transaction *t = &tran;
+  bufferlist::iterator ebliter = bl.begin();
+  object_begin ob;
+  ob.decode(ebliter);
+  OSDriver driver(
+    store,
+    coll_t(),
+    OSD::make_snapmapper_oid());
+  spg_t pg;
+  coll.is_pg_prefix(pg);
+  SnapMapper mapper(&driver, 0, 0, 0, pg.shard);
+
+  t->touch(coll, ob.hoid);
+
+  cout << "Write " << ob.hoid << std::endl;
+
+  bufferlist ebl;
+  bool done = false;
+  while(!done) {
+    sectiontype_t type;
+    int ret = read_section(file_fd, &type, &ebl);
+    if (ret)
+      return ret;
+
+    //cout << "\tdo_object: Section type " << hex << type << dec << std::endl;
+    //cout << "\t\tsection size " << ebl.length() << std::endl;
+    if (type >= END_OF_TYPES) {
+      cout << "Skipping unknown object section type" << std::endl;
+      continue;
+    }
+    switch(type) {
+    case TYPE_DATA:
+      ret = get_data(store, coll, ob.hoid, t, ebl);
+      if (ret) return ret;
+      break;
+    case TYPE_ATTRS:
+      ret = get_attrs(store, coll, ob.hoid, t, ebl, driver, mapper);
+      if (ret) return ret;
+      break;
+    case TYPE_OMAP_HDR:
+      ret = get_omap_hdr(store, coll, ob.hoid, t, ebl);
+      if (ret) return ret;
+      break;
+    case TYPE_OMAP:
+      ret = get_omap(store, coll, ob.hoid, t, ebl);
+      if (ret) return ret;
+      break;
+    case TYPE_OBJECT_END:
+      done = true;
+      break;
+    default:
+      return EFAULT;
+    }
+  }
+  store->apply_transaction(*t);
+  return 0;
+}
+
+int get_pg_metadata(ObjectStore *store, coll_t coll, bufferlist &bl)
+{
+  ObjectStore::Transaction tran;
+  ObjectStore::Transaction *t = &tran;
+  bufferlist::iterator ebliter = bl.begin();
+  metadata_section ms;
+  ms.decode(ebliter);
+
+#if DIAGNOSTIC
+  Formatter *formatter = new JSONFormatter(true);
+  cout << "struct_v " << (int)ms.struct_ver << std::endl;
+  cout << "epoch " << ms.map_epoch << std::endl;
+  formatter->open_object_section("info");
+  ms.info.dump(formatter);
+  formatter->close_section();
+  formatter->flush(cout);
+  cout << std::endl;
+
+  formatter->open_object_section("log");
+  ms.log.dump(formatter);
+  formatter->close_section();
+  formatter->flush(cout);
+  cout << std::endl;
+#endif
+
+  coll_t newcoll(ms.info.pgid);
+  t->collection_rename(coll, newcoll);
+
+  int ret = write_pg(*t, ms.map_epoch, ms.info, ms.log, ms.struct_ver);
+  if (ret) return ret;
+
+  store->apply_transaction(*t);
+
+  return 0;
+}
+
+int do_import(ObjectStore *store, OSDSuperblock& sb)
+{
+  bufferlist ebl;
+  pg_info_t info;
+  PGLog::IndexedLog log;
+
+  uint64_t next_removal_seq = 0;       //My local seq
+  finish_remove_pgs(store, &next_removal_seq);
+
+  int ret = sh.read_super();
+  if (ret)
+    return ret;
+
+  if (sh.magic != super_header::super_magic) {
+    cerr << "Invalid magic number" << std::endl;
+    return EFAULT;
+  }
+
+  if (sh.version > super_header::super_ver) {
+    cerr << "Can't handle export format version=" << sh.version << std::endl;
+    return EINVAL;
+  }
+
+  //First section must be TYPE_PG_BEGIN
+  sectiontype_t type;
+  ret = read_section(file_fd, &type, &ebl);
+  if (ret)
+    return ret;
+  if (type != TYPE_PG_BEGIN) {
+    return EFAULT;
+  }
+
+  bufferlist::iterator ebliter = ebl.begin();
+  pg_begin pgb;
+  pgb.decode(ebliter);
+  spg_t pgid = pgb.pgid;
+
+  if (debug) {
+    cerr << "Exported features: " << pgb.superblock.compat_features << std::endl;
+  }
+  if (sb.compat_features.compare(pgb.superblock.compat_features) == -1) {
+    cerr << "Export has incompatible features set "
+      << pgb.superblock.compat_features << std::endl;
+    return 1;
+  }
+
+  log_oid = OSD::make_pg_log_oid(pgid);
+  biginfo_oid = OSD::make_pg_biginfo_oid(pgid);
+
+  //Check for PG already present.
+  coll_t coll(pgid);
+  if (store->collection_exists(coll)) {
+    cerr << "pgid " << pgid << " already exists" << std::endl;
+    return 1;
+  }
+
+  //Switch to collection which will be removed automatically if
+  //this program is interupted.
+  coll_t rmcoll = coll_t::make_removal_coll(next_removal_seq, pgid);
+  ObjectStore::Transaction *t = new ObjectStore::Transaction;
+  t->create_collection(rmcoll);
+  store->apply_transaction(*t);
+  delete t;
+
+  cout << "Importing pgid " << pgid << std::endl;
+
+  bool done = false;
+  bool found_metadata = false;
+  while(!done) {
+    ret = read_section(file_fd, &type, &ebl);
+    if (ret)
+      return ret;
+
+    //cout << "do_import: Section type " << hex << type << dec << std::endl;
+    if (type >= END_OF_TYPES) {
+      cout << "Skipping unknown section type" << std::endl;
+      continue;
+    }
+    switch(type) {
+    case TYPE_OBJECT_BEGIN:
+      ret = get_object(store, rmcoll, ebl);
+      if (ret) return ret;
+      break;
+    case TYPE_PG_METADATA:
+      ret = get_pg_metadata(store, rmcoll, ebl);
+      if (ret) return ret;
+      found_metadata = true;
+      break;
+    case TYPE_PG_END:
+      done = true;
+      break;
+    default:
+      return EFAULT;
+    }
+  }
+
+  if (!found_metadata) {
+    cerr << "Missing metadata section" << std::endl;
+    return EFAULT;
+  }
+
+  return 0;
+}
+
+int do_list(ObjectStore *store, coll_t coll, Formatter *formatter)
+{
+  vector<ghobject_t> objects;
+  ghobject_t next;
+  while (!next.is_max()) {
+    int r = store->collection_list_partial(coll, next, 200, 300, 0,
+      &objects, &next);
+    if (r < 0)
+      return r;
+    for (vector<ghobject_t>::iterator i = objects.begin();
+        i != objects.end(); ++i) {
+
+      formatter->open_object_section("list");
+      i->dump(formatter);
+      formatter->close_section();
+      formatter->flush(cout);
+      cout << std::endl;
+    }
+  }
+  return 0;
+}
+
+int do_remove_object(ObjectStore *store, coll_t coll, ghobject_t &ghobj)
+{
+  spg_t pg;
+  coll.is_pg_prefix(pg);
+  OSDriver driver(
+    store,
+    coll_t(),
+    OSD::make_snapmapper_oid());
+  SnapMapper mapper(&driver, 0, 0, 0, pg.shard);
+  struct stat st;
+
+  int r = store->stat(coll, ghobj, &st);
+  if (r < 0) {
+    cerr << "remove: " << cpp_strerror(-r) << std::endl;
+    return r;
+  }
+
+  ObjectStore::Transaction *t = new ObjectStore::Transaction;
+  OSDriver::OSTransaction _t(driver.get_transaction(t));
+  cout << "remove " << ghobj << std::endl;
+  r = mapper.remove_oid(ghobj.hobj, &_t);
+  if (r != 0 && r != -ENOENT) {
+    cerr << "remove_oid returned " << cpp_strerror(-r) << std::endl;
+    return r;
+  }
+
+  t->remove(coll, ghobj);
+
+  store->apply_transaction(*t);
+  delete t;
+  return 0;
+}
+
+int do_list_attrs(ObjectStore *store, coll_t coll, ghobject_t &ghobj)
+{
+  map<string,bufferptr> aset;
+  int r = store->getattrs(coll, ghobj, aset);
+  if (r < 0) {
+    cerr << "getattrs: " << cpp_strerror(-r) << std::endl;
+    return r;
+  }
+
+  for (map<string,bufferptr>::iterator i = aset.begin();i != aset.end(); ++i) {
+    string key(i->first);
+    if (outistty)
+      cleanbin(key);
+    cout << key << std::endl;
+  }
+  return 0;
+}
+
+int do_list_omap(ObjectStore *store, coll_t coll, ghobject_t &ghobj)
+{
+  ObjectMap::ObjectMapIterator iter = store->get_omap_iterator(coll, ghobj);
+  if (!iter) {
+    cerr << "omap_get_iterator: " << cpp_strerror(ENOENT) << std::endl;
+    return -ENOENT;
+  }
+  iter->seek_to_first();
+  map<string, bufferlist> oset;
+  while(iter->valid()) {
+    get_omap_batch(iter, oset);
+
+    for (map<string,bufferlist>::iterator i = oset.begin();i != oset.end(); ++i) {
+      string key(i->first);
+      if (outistty)
+        cleanbin(key);
+      cout << key << std::endl;
+    }
+  }
+  return 0;
+}
+
+int do_get_bytes(ObjectStore *store, coll_t coll, ghobject_t &ghobj, int fd)
+{
+  struct stat st;
+  mysize_t total;
+
+  int ret = store->stat(coll, ghobj, &st);
+  if (ret < 0) {
+    cerr << "get-bytes: " << cpp_strerror(-ret) << std::endl;
+    return 1;
+  }
+
+  total = st.st_size;
+  if (debug)
+    cerr << "size=" << total << std::endl;
+
+  uint64_t offset = 0;
+  bufferlist rawdatabl;
+  while(total > 0) {
+    rawdatabl.clear();
+    mysize_t len = max_read;
+    if (len > total)
+      len = total;
+
+    ret = store->read(coll, ghobj, offset, len, rawdatabl);
+    if (ret < 0)
+      return ret;
+    if (ret == 0)
+      return -EINVAL;
+
+    if (debug)
+      cerr << "data section offset=" << offset << " len=" << len << std::endl;
+
+    total -= ret;
+    offset += ret;
+
+    ret = write(fd, rawdatabl.c_str(), ret);
+    if (ret == -1) {
+      perror("write");
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+int do_set_bytes(ObjectStore *store, coll_t coll, ghobject_t &ghobj, int fd)
+{
+  ObjectStore::Transaction tran;
+  ObjectStore::Transaction *t = &tran;
+
+  if (debug)
+    cerr << "Write " << ghobj << std::endl;
+
+  t->touch(coll, ghobj);
+  t->truncate(coll, ghobj, 0);
+
+  uint64_t offset = 0;
+  bufferlist rawdatabl;
+  do {
+    rawdatabl.clear();
+    ssize_t bytes = rawdatabl.read_fd(fd, max_read);
+    if (bytes < 0) {
+      cerr << "read_fd error " << cpp_strerror(-bytes) << std::endl;
+      return 1;
+    }
+
+    if (bytes == 0)
+      break;
+
+    if (debug)
+      cerr << "\tdata: offset " << offset << " bytes " << bytes << std::endl;
+    t->write(coll, ghobj, offset, bytes,  rawdatabl);
+
+    offset += bytes;
+    // XXX: Should we apply_transaction() every once in a while for very large files
+  } while(true);
+
+  store->apply_transaction(*t);
+  return 0;
+}
+
+int do_get_attr(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key)
+{
+  bufferptr bp;
+
+  int r = store->getattr(coll, ghobj, key.c_str(), bp);
+  if (r < 0) {
+    cerr << "getattr: " << cpp_strerror(-r) << std::endl;
+    return r;
+  }
+
+  string value(bp.c_str(), bp.length());
+  if (outistty) {
+    cleanbin(value);
+    value.push_back('\n');
+  }
+  cout << value;
+
+  return 0;
+}
+
+int do_set_attr(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key, int fd)
+{
+  ObjectStore::Transaction tran;
+  ObjectStore::Transaction *t = &tran;
+  bufferlist bl;
+
+  if (debug)
+    cerr << "Setattr " << ghobj << std::endl;
+
+  if (get_fd_data(fd, bl))
+    return 1;
+
+  t->touch(coll, ghobj);
+
+  t->setattr(coll, ghobj, key,  bl);
+
+  store->apply_transaction(*t);
+  return 0;
+}
+
+int do_rm_attr(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key)
+{
+  ObjectStore::Transaction tran;
+  ObjectStore::Transaction *t = &tran;
+
+  if (debug)
+    cerr << "Rmattr " << ghobj << std::endl;
+
+  t->rmattr(coll, ghobj, key);
+
+  store->apply_transaction(*t);
+  return 0;
+}
+
+int do_get_omap(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key)
+{
+  set<string> keys;
+  map<string, bufferlist> out;
+
+  keys.insert(key);
+
+  int r = store->omap_get_values(coll, ghobj, keys, &out);
+  if (r < 0) {
+    cerr << "omap_get_values: " << cpp_strerror(-r) << std::endl;
+    return r;
+  }
+
+  if (out.empty()) {
+    cerr << "Key not found" << std::endl;
+    return -ENOENT;
+  }
+
+  assert(out.size() == 1);
+
+  bufferlist bl = out.begin()->second;
+  string value(bl.c_str(), bl.length());
+  if (outistty) {
+    cleanbin(value);
+    value.push_back('\n');
+  }
+  cout << value;
+
+  return 0;
+}
+
+int do_set_omap(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key, int fd)
+{
+  ObjectStore::Transaction tran;
+  ObjectStore::Transaction *t = &tran;
+  map<string, bufferlist> attrset;
+  bufferlist valbl;
+
+  if (debug)
+    cerr << "Set_omap " << ghobj << std::endl;
+
+  if (get_fd_data(fd, valbl))
+    return 1;
+
+  attrset.insert(pair<string, bufferlist>(key, valbl));
+
+  t->touch(coll, ghobj);
+
+  t->omap_setkeys(coll, ghobj, attrset);
+
+  store->apply_transaction(*t);
+  return 0;
+}
+
+int do_rm_omap(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key)
+{
+  ObjectStore::Transaction tran;
+  ObjectStore::Transaction *t = &tran;
+  set<string> keys;
+
+  keys.insert(key);
+
+  if (debug)
+    cerr << "Rm_omap " << ghobj << std::endl;
+
+  t->omap_rmkeys(coll, ghobj, keys);
+
+  store->apply_transaction(*t);
+  return 0;
+}
+
+int do_get_omaphdr(ObjectStore *store, coll_t coll, ghobject_t &ghobj)
+{
+  bufferlist hdrbl;
+
+  int r = store->omap_get_header(coll, ghobj, &hdrbl, true);
+  if (r < 0) {
+    cerr << "omap_get_header: " << cpp_strerror(-r) << std::endl;
+    return r;
+  }
+
+  string header(hdrbl.c_str(), hdrbl.length());
+  if (outistty) {
+    cleanbin(header);
+    header.push_back('\n');
+  }
+  cout << header;
+
+  return 0;
+}
+
+int do_set_omaphdr(ObjectStore *store, coll_t coll, ghobject_t &ghobj, int fd)
+{
+  ObjectStore::Transaction tran;
+  ObjectStore::Transaction *t = &tran;
+  bufferlist hdrbl;
+
+  if (debug)
+    cerr << "Omap_setheader " << ghobj << std::endl;
+
+  if (get_fd_data(fd, hdrbl))
+    return 1;
+
+  t->touch(coll, ghobj);
+
+  t->omap_setheader(coll, ghobj, hdrbl);
+
+  store->apply_transaction(*t);
+  return 0;
+}
+
+void usage(po::options_description &desc)
+{
+    cerr << std::endl;
+    cerr << desc << std::endl;
+    cerr << std::endl;
+    cerr << "Positional syntax:" << std::endl;
+    cerr << std::endl;
+    cerr << "(requires --filestore-path, --journal-path and --pgid to be specified)" << std::endl;
+    cerr << "(optional [file] argument will read stdin or write stdout if not specified or if '-' specified)" << std::endl;
+    cerr << "ceph-filestore-dump ... <object> (get|set)-bytes [file]" << std::endl;
+    cerr << "ceph-filestore-dump ... <object> (set-(attr|omap) <key> [file]" << std::endl;
+    cerr << "ceph-filestore-dump ... <object> (set-omaphdr) [file]" << std::endl;
+    cerr << "ceph-filestore-dump ... <object> (get|rm)-(attr|omap) <key>" << std::endl;
+    cerr << "ceph-filestore-dump ... <object> (get-omaphdr)" << std::endl;
+    cerr << "ceph-filestore-dump ... <object> list-attrs" << std::endl;
+    cerr << "ceph-filestore-dump ... <object> list-omap" << std::endl;
+    cerr << "ceph-filestore-dump ... <object> remove" << std::endl;
+    cerr << std::endl;
+    exit(1);
+}
+
+int main(int argc, char **argv)
+{
+  string fspath, jpath, pgidstr, type, file, object, objcmd, arg1, arg2;
+  ghobject_t ghobj;
+
+  po::options_description desc("Allowed options");
+  desc.add_options()
+    ("help", "produce help message")
+    ("filestore-path", po::value<string>(&fspath),
+     "path to filestore directory, mandatory")
+    ("journal-path", po::value<string>(&jpath),
+     "path to journal, mandatory")
+    ("pgid", po::value<string>(&pgidstr),
+     "PG id, mandatory except for import")
+    ("type", po::value<string>(&type),
+     "Arg is one of [info, log, remove, export, import, list]")
+    ("file", po::value<string>(&file),
+     "path of file to export or import")
+    ("debug", "Enable diagnostic output to stderr")
+    ("skip-journal-replay", "Disable journal replay")
+    ("skip-mount-omap", "Disable mounting of omap")
+    ;
+
+  po::options_description positional("Positional options");
+  positional.add_options()
+    ("object", po::value<string>(&object), "ghobject in json")
+    ("objcmd", po::value<string>(&objcmd), "command [(get|set)-bytes, (get|set|rm)-(attr|omap), list-attrs, list-omap, remove]")
+    ("arg1", po::value<string>(&arg1), "arg1 based on cmd")
+    ("arg2", po::value<string>(&arg2), "arg2 based on cmd")
+    ;
+
+  po::options_description all("All options");
+  all.add(desc).add(positional);
+
+  po::positional_options_description pd;
+  pd.add("object", 1).add("objcmd", 1).add("arg1", 1).add("arg2", 1);
+
+  po::variables_map vm;
+  po::parsed_options parsed =
+   po::command_line_parser(argc, argv).options(all).allow_unregistered().positional(pd).run();
+  po::store( parsed, vm);
+  try {
+    po::notify(vm);
+  }
+  catch(...) {
+    usage(desc);
+  }
+
+  if (vm.count("help")) {
+    usage(desc);
+  }
+
+  if (!vm.count("filestore-path")) {
+    cerr << "Must provide --filestore-path" << std::endl;
+    usage(desc);
+  }
+  if (!vm.count("journal-path")) {
+    cerr << "Must provide --journal-path" << std::endl;
+    usage(desc);
+  }
+  if (vm.count("object") && !vm.count("objcmd")) {
+    cerr << "Invalid syntax, missing command" << std::endl;
+    usage(desc);
+  }
+  if (!vm.count("type") && !(vm.count("object") && vm.count("objcmd"))) {
+    cerr << "Must provide --type or object command..."
+      << std::endl;
+    usage(desc);
+  }
+  if (vm.count("type") && vm.count("object")) {
+    cerr << "Can't specify both --type and object command syntax" << std::endl;
+    usage(desc);
+  }
+  if (type != "import" && !vm.count("pgid")) {
+    cerr << "Must provide pgid" << std::endl;
+    usage(desc);
+  }
+
+  if (vm.count("object")) {
+    json_spirit::Value v;
+    try {
+      if (!json_spirit::read(object, v))
+        throw std::runtime_error("bad json");
+      ghobj.decode(v);
+    } catch (std::runtime_error& e) {
+      cerr << "error parsing offset: " << e.what() << std::endl;
+      exit(1);
+    }
+  }
+
+  outistty = isatty(STDOUT_FILENO);
+
+  file_fd = fd_none;
+  if (type == "export") {
+    if (!vm.count("file")) {
+      if (outistty) {
+        cerr << "stdout is a tty and no --file option specified" << std::endl;
+        exit(1);
+      }
+      file_fd = STDOUT_FILENO;
+    } else {
+      file_fd = open(file.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0666);
+    }
+  } else if (type == "import") {
+    if (!vm.count("file")) {
+      if (isatty(STDIN_FILENO)) {
+        cerr << "stdin is a tty and no --file option specified" << std::endl;
+        exit(1);
+      }
+      file_fd = STDIN_FILENO;
+    } else {
+      file_fd = open(file.c_str(), O_RDONLY);
+    }
+  }
+
+  if (vm.count("file") && file_fd == fd_none) {
+    cerr << "--file option only applies to import or export" << std::endl;
+    return 1;
+  }
+
+  if (file_fd != fd_none && file_fd < 0) {
+    perror("open");
+    return 1;
+  }
+
+  if ((fspath.length() == 0 || jpath.length() == 0) ||
+      (type != "import" && pgidstr.length() == 0)) {
+    cerr << "Invalid params" << std::endl;
+    return 1;
+  }
+
+  if (type == "import" && pgidstr.length()) {
+    cerr << "--pgid option invalid with import" << std::endl;
+    return 1;
+  }
+
+  vector<const char *> ceph_options, def_args;
+  vector<string> ceph_option_strings = po::collect_unrecognized(
+    parsed.options, po::include_positional);
+  ceph_options.reserve(ceph_option_strings.size());
+  for (vector<string>::iterator i = ceph_option_strings.begin();
+       i != ceph_option_strings.end();
+       ++i) {
+    ceph_options.push_back(i->c_str());
+  }
+
+  if (!vm.count("debug")) {
+    debug = false;
+  } else {
+    debug = true;
+  }
+
+  osflagbits_t flags = 0;
+  if (vm.count("skip-journal-replay"))
+    flags |= SKIP_JOURNAL_REPLAY;
+  if (vm.count("skip-mount-omap"))
+    flags |= SKIP_MOUNT_OMAP;
+
+  global_init(
+    &def_args, ceph_options, CEPH_ENTITY_TYPE_OSD,
+    CODE_ENVIRONMENT_UTILITY_NODOUT, 0);
+    //CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+  common_init_finish(g_ceph_context);
+  g_conf = g_ceph_context->_conf;
+  if (debug) {
+    g_conf->set_val_or_die("log_to_stderr", "true");
+    g_conf->set_val_or_die("err_to_stderr", "true");
+  }
+  g_conf->apply_changes(NULL);
+
+  //Verify that fspath really is an osd store
+  struct stat st;
+  if (::stat(fspath.c_str(), &st) == -1) {
+     perror("fspath");
+     invalid_path(fspath);
+  }
+  if (!S_ISDIR(st.st_mode)) {
+    invalid_path(fspath);
+  }
+  string check = fspath + "/whoami";
+  if (::stat(check.c_str(), &st) == -1) {
+     perror("whoami");
+     invalid_path(fspath);
+  }
+  if (!S_ISREG(st.st_mode)) {
+    invalid_path(fspath);
+  }
+  check = fspath + "/current";
+  if (::stat(check.c_str(), &st) == -1) {
+     perror("current");
+     invalid_path(fspath);
+  }
+  if (!S_ISDIR(st.st_mode)) {
+    invalid_path(fspath);
+  }
+
+  spg_t pgid;
+  if (pgidstr.length() && !pgid.parse(pgidstr.c_str())) {
+    cerr << "Invalid pgid '" << pgidstr << "' specified" << std::endl;
+    return 1;
+  }
+
+  ObjectStore *fs = ObjectStore::create(NULL, "filestore", fspath, jpath, flags);
+
+  int r = fs->mount();
+  if (r < 0) {
+    if (r == -EBUSY) {
+      cerr << "OSD has the store locked" << std::endl;
+    } else {
+      cerr << "Mount failed with '" << cpp_strerror(-r) << "'" << std::endl;
+    }
+    return 1;
+  }
+
+  bool fs_sharded_objects = fs->get_allow_sharded_objects();
+
+  int ret = 0;
+  vector<coll_t> ls;
+  vector<coll_t>::iterator it;
+  CompatSet supported;
+
+#ifdef INTERNAL_TEST
+  supported = get_test_compat_set();
+#else
+  supported = OSD::get_osd_compat_set();
+#endif
+
+  bufferlist bl;
+  OSDSuperblock superblock;
+  bufferlist::iterator p;
+  r = fs->read(META_COLL, OSD_SUPERBLOCK_POBJECT, 0, 0, bl);
+  if (r < 0) {
+    cerr << "Failure to read OSD superblock error= " << r << std::endl;
+    goto out;
+  }
+
+  p = bl.begin();
+  ::decode(superblock, p);
+
+#ifdef INTERNAL_TEST2
+  fs->set_allow_sharded_objects();
+  assert(fs->get_allow_sharded_objects());
+  fs_sharded_objects = true;
+  superblock.compat_features.incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_SHARDS);
+#endif
+
+  if (debug) {
+    cerr << "Supported features: " << supported << std::endl;
+    cerr << "On-disk features: " << superblock.compat_features << std::endl;
+  }
+  if (supported.compare(superblock.compat_features) == -1) {
+    cerr << "On-disk OSD incompatible features set "
+      << superblock.compat_features << std::endl;
+    ret = EINVAL;
+    goto out;
+  }
+
+  // If there was a crash as an OSD was transitioning to sharded objects
+  // and hadn't completed a set_allow_sharded_objects().
+  // This utility does not want to attempt to finish that transition.
+  if (superblock.compat_features.incompat.contains(CEPH_OSD_FEATURE_INCOMPAT_SHARDS) != fs_sharded_objects) {
+    // An OSD should never have call set_allow_sharded_objects() before
+    // updating its own OSD features.
+    if (fs_sharded_objects)
+      cerr << "FileStore sharded but OSD not set, Corruption?" << std::endl;
+    else
+      cerr << "Found incomplete transition to sharded objects" << std::endl;
+    ret = EINVAL;
+    goto out;
+  }
+
+  if (type == "import") {
+
+    try {
+      ret = do_import(fs, superblock);
+    }
+    catch (const buffer::error &e) {
+      cerr << "do_import threw exception error " << e.what() << std::endl;
+      ret = EFAULT;
+    }
+    if (ret == EFAULT) {
+      cerr << "Corrupt input for import" << std::endl;
+    }
+    if (ret == 0)
+      cout << "Import successful" << std::endl;
+    goto out;
+  }
+
+  log_oid = OSD::make_pg_log_oid(pgid);
+  biginfo_oid = OSD::make_pg_biginfo_oid(pgid);
+
+  if (type == "remove") {
+    uint64_t next_removal_seq = 0;     //My local seq
+    finish_remove_pgs(fs, &next_removal_seq);
+    int r = initiate_new_remove_pg(fs, pgid, &next_removal_seq);
+    if (r) {
+      cerr << "PG '" << pgid << "' not found" << std::endl;
+      ret = 1;
+      goto out;
+    }
+    finish_remove_pgs(fs, &next_removal_seq);
+    cout << "Remove successful" << std::endl;
+    goto out;
+  }
+
+  r = fs->list_collections(ls);
+  if (r < 0) {
+    cerr << "failed to list pgs: " << cpp_strerror(-r) << std::endl;
+    ret = 1;
+    goto out;
+  }
+
+  for (it = ls.begin(); it != ls.end(); ++it) {
+    snapid_t snap;
+    spg_t tmppgid;
+
+    if (!it->is_pg(tmppgid, snap)) {
+      continue;
+    }
+
+    if (it->is_temp(tmppgid)) {
+      continue;
+    }
+
+    if (tmppgid != pgid) {
+      continue;
+    }
+    if (snap != CEPH_NOSNAP && debug) {
+      cerr << "skipping snapped dir " << *it
+              << " (pg " << pgid << " snap " << snap << ")" << std::endl;
+      continue;
+    }
+
+    //Found!
+    break;
+  }
+
+  epoch_t map_epoch;
+// The following code for export, info, log require omap or !skip-mount-omap
+  if (it != ls.end()) {
+
+    coll_t coll = *it;
+
+    if (vm.count("objcmd")) {
+      ret = 0;
+      if (objcmd == "remove") {
+        int r = do_remove_object(fs, coll, ghobj);
+        if (r) {
+          ret = 1;
+        }
+        goto out;
+      } else if (objcmd == "list-attrs") {
+        int r = do_list_attrs(fs, coll, ghobj);
+        if (r) {
+          ret = 1;
+        }
+        goto out;
+      } else if (objcmd == "list-omap") {
+        int r = do_list_omap(fs, coll, ghobj);
+        if (r) {
+          ret = 1;
+        }
+        goto out;
+      } else if (objcmd == "get-bytes" || objcmd == "set-bytes") {
+        int r;
+        if (objcmd == "get-bytes") {
+          int fd;
+          if (vm.count("arg1") == 0 || arg1 == "-") {
+            fd = STDOUT_FILENO;
+         } else {
+            fd = open(arg1.c_str(), O_WRONLY|O_TRUNC|O_CREAT|O_EXCL|O_LARGEFILE, 0666);
+            if (fd == -1) {
+              cerr << "open " << arg1 << " " << cpp_strerror(errno) << std::endl;
+              ret = 1;
+              goto out;
+            }
+          }
+          r = do_get_bytes(fs, coll, ghobj, fd);
+          if (fd != STDOUT_FILENO)
+            close(fd);
+        } else {
+          int fd;
+          if (vm.count("arg1") == 0 || arg1 == "-") {
+            fd = STDIN_FILENO;
+         } else {
+            fd = open(arg1.c_str(), O_RDONLY|O_LARGEFILE, 0666);
+            if (fd == -1) {
+              cerr << "open " << arg1 << " " << cpp_strerror(errno) << std::endl;
+              ret = 1;
+              goto out;
+            }
+          }
+          r = do_set_bytes(fs, coll, ghobj, fd);
+          if (fd != STDIN_FILENO)
+            close(fd);
+        }
+        if (r)
+          ret = 1;
+        goto out;
+      } else if (objcmd == "get-attr") {
+       if (vm.count("arg1") == 0)
+         usage(desc);
+       r = do_get_attr(fs, coll, ghobj, arg1);
+       if (r)
+         ret = 1;
+        goto out;
+      } else if (objcmd == "set-attr") {
+       if (vm.count("arg1") == 0)
+         usage(desc);
+
+       int fd;
+       if (vm.count("arg2") == 0 || arg2 == "-") {
+         fd = STDIN_FILENO;
+       } else {
+         fd = open(arg2.c_str(), O_RDONLY|O_LARGEFILE, 0666);
+         if (fd == -1) {
+           cerr << "open " << arg2 << " " << cpp_strerror(errno) << std::endl;
+           ret = 1;
+           goto out;
+         }
+       }
+       r = do_set_attr(fs, coll, ghobj, arg1, fd);
+       if (fd != STDIN_FILENO)
+         close(fd);
+       if (r)
+         ret = 1;
+        goto out;
+      } else if (objcmd == "rm-attr") {
+       if (vm.count("arg1") == 0)
+         usage(desc);
+       r = do_rm_attr(fs, coll, ghobj, arg1);
+       if (r)
+         ret = 1;
+        goto out;
+      } else if (objcmd == "get-omap") {
+       if (vm.count("arg1") == 0)
+         usage(desc);
+       r = do_get_omap(fs, coll, ghobj, arg1);
+       if (r)
+         ret = 1;
+        goto out;
+      } else if (objcmd == "set-omap") {
+       if (vm.count("arg1") == 0)
+         usage(desc);
+
+       int fd;
+       if (vm.count("arg2") == 0 || arg2 == "-") {
+         fd = STDIN_FILENO;
+       } else {
+         fd = open(arg2.c_str(), O_RDONLY|O_LARGEFILE, 0666);
+         if (fd == -1) {
+           cerr << "open " << arg2 << " " << cpp_strerror(errno) << std::endl;
+           ret = 1;
+           goto out;
+         }
+       }
+       r = do_set_omap(fs, coll, ghobj, arg1, fd);
+       if (fd != STDIN_FILENO)
+         close(fd);
+       if (r)
+         ret = 1;
+        goto out;
+      } else if (objcmd == "rm-omap") {
+       if (vm.count("arg1") == 0)
+         usage(desc);
+       r = do_rm_omap(fs, coll, ghobj, arg1);
+       if (r)
+         ret = 1;
+        goto out;
+      } else if (objcmd == "get-omaphdr") {
+       if (vm.count("arg1"))
+         usage(desc);
+       r = do_get_omaphdr(fs, coll, ghobj);
+       if (r)
+         ret = 1;
+        goto out;
+      } else if (objcmd == "set-omaphdr") {
+        // Extra arg
+       if (vm.count("arg2"))
+         usage(desc);
+       int fd;
+       if (vm.count("arg1") == 0 || arg1 == "-") {
+         fd = STDIN_FILENO;
+       } else {
+         fd = open(arg1.c_str(), O_RDONLY|O_LARGEFILE, 0666);
+         if (fd == -1) {
+           cerr << "open " << arg1 << " " << cpp_strerror(errno) << std::endl;
+           ret = 1;
+           goto out;
+         }
+       }
+       r = do_set_omaphdr(fs, coll, ghobj, fd);
+       if (fd != STDIN_FILENO)
+         close(fd);
+       if (r)
+         ret = 1;
+        goto out;
+      }
+      cerr << "Unknown object command '" << objcmd << "'" << std::endl;
+      usage(desc);
+    }
+
+    if (type == "list") {
+      Formatter *formatter = new JSONFormatter(false);
+      r = do_list(fs, coll, formatter);
+      if (r) {
+        cerr << "do_list failed with " << r << std::endl;
+        ret = 1;
+      }
+      goto out;
+    }
+
+    Formatter *formatter = new JSONFormatter(true);
+    bufferlist bl;
+    map_epoch = PG::peek_map_epoch(fs, coll, infos_oid, &bl);
+    if (debug)
+      cerr << "map_epoch " << map_epoch << std::endl;
+
+    pg_info_t info(pgid);
+    map<epoch_t,pg_interval_t> past_intervals;
+    hobject_t biginfo_oid = OSD::make_pg_biginfo_oid(pgid);
+    interval_set<snapid_t> snap_collections;
+
+    __u8 struct_ver;
+    r = PG::read_info(fs, coll, bl, info, past_intervals, biginfo_oid,
+      infos_oid, snap_collections, struct_ver);
+    if (r < 0) {
+      cerr << "read_info error " << cpp_strerror(-r) << std::endl;
+      ret = 1;
+      goto out;
+    }
+    if (debug)
+      cerr << "struct_v " << (int)struct_ver << std::endl;
+
+    if (type == "export") {
+      ret = do_export(fs, coll, pgid, info, map_epoch, struct_ver, superblock);
+      if (ret == 0 && file_fd != STDOUT_FILENO)
+        cout << "Export successful" << std::endl;
+    } else if (type == "info") {
+      formatter->open_object_section("info");
+      info.dump(formatter);
+      formatter->close_section();
+      formatter->flush(cout);
+      cout << std::endl;
+    } else if (type == "log") {
+      PGLog::IndexedLog log;
+      pg_missing_t missing;
+      ret = get_log(fs, coll, pgid, info, log, missing);
+      if (ret > 0)
+          goto out;
+
+      formatter->open_object_section("log");
+      log.dump(formatter);
+      formatter->close_section();
+      formatter->flush(cout);
+      cout << std::endl;
+      formatter->open_object_section("missing");
+      missing.dump(formatter);
+      formatter->close_section();
+      formatter->flush(cout);
+      cout << std::endl;
+    } else {
+      cerr << "Must provide --type (info, log, remove, export, import, list)"
+       << std::endl;
+      usage(desc);
+    }
+  } else {
+    cerr << "PG '" << pgid << "' not found" << std::endl;
+    ret = 1;
+  }
+
+out:
+  if (fs->umount() < 0) {
+    cerr << "umount failed" << std::endl;
+    return 1;
+  }
+
+  return (ret != 0);
+}