From: David Zafman Date: Wed, 30 Jul 2014 18:22:29 +0000 (-0700) Subject: Renames and removal towards a unified ceph_objectstore_tool X-Git-Tag: v0.80.10~69^2~70 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=a42273ecd955470105cba1cc4ac7eb782ac46833;p=ceph.git Renames and removal towards a unified ceph_objectstore_tool Rename ceph_filestore_dump.cc and ceph_filestore_dump.py Remove ceph_filestore_tool.cc Signed-off-by: David Zafman (cherry picked from commit 77864193a1162393ade783480aee39a232934377) --- diff --git a/src/test/ceph_objectstore_tool.py b/src/test/ceph_objectstore_tool.py new file mode 100755 index 00000000000..3718a0ee037 --- /dev/null +++ b/src/test/ceph_objectstore_tool.py @@ -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 "" + + +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 index 244ac620804..00000000000 --- a/src/tools/ceph_filestore_dump.cc +++ /dev/null @@ -1,2075 +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 -#include - -#include - -#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; - -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 = ghobject_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 = ghobject_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 data; - attr_section(const map &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 omap; - omap_section(const map &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 -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 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 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::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 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::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 " << coll_t::META_COLL << " " << log_oid.oid << std::endl; - rmt->remove(coll_t::META_COLL, log_oid); - cout << "remove " << coll_t::META_COLL << " " << biginfo_oid.oid << std::endl; - rmt->remove(coll_t::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 snap_collections; // obsolete - map 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 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 &oset) -{ - oset.clear(); - for (int count = OMAP_BATCH_SIZE; count && iter->valid(); --count, iter->next()) { - oset.insert(pair(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 aset; - ret = store->getattrs(cid, obj, aset, false); - 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 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 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::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::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 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 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::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 aset; - int r = store->getattrs(coll, ghobj, aset); - if (r < 0) { - cerr << "getattrs: " << cpp_strerror(-r) << std::endl; - return r; - } - - for (map::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 oset; - while(iter->valid()) { - get_omap_batch(iter, oset); - - for (map::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 keys; - map 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 attrset; - bufferlist valbl; - - if (debug) - cerr << "Set_omap " << ghobj << std::endl; - - if (get_fd_data(fd, valbl)) - return 1; - - attrset.insert(pair(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 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 ... (get|set)-bytes [file]" << std::endl; - cerr << "ceph-filestore-dump ... (set-(attr|omap) [file]" << std::endl; - cerr << "ceph-filestore-dump ... (set-omaphdr) [file]" << std::endl; - cerr << "ceph-filestore-dump ... (get|rm)-(attr|omap) " << std::endl; - cerr << "ceph-filestore-dump ... (get-omaphdr)" << std::endl; - cerr << "ceph-filestore-dump ... list-attrs" << std::endl; - cerr << "ceph-filestore-dump ... list-omap" << std::endl; - cerr << "ceph-filestore-dump ... 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(&fspath), - "path to filestore directory, mandatory") - ("journal-path", po::value(&jpath), - "path to journal, mandatory") - ("pgid", po::value(&pgidstr), - "PG id, mandatory except for import") - ("type", po::value(&type), - "Arg is one of [info, log, remove, export, import, list]") - ("file", po::value(&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(&object), "ghobject in json") - ("objcmd", po::value(&objcmd), "command [(get|set)-bytes, (get|set|rm)-(attr|omap), list-attrs, list-omap, remove]") - ("arg1", po::value(&arg1), "arg1 based on cmd") - ("arg2", po::value(&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 ceph_options, def_args; - vector ceph_option_strings = po::collect_unrecognized( - parsed.options, po::include_positional); - ceph_options.reserve(ceph_option_strings.size()); - for (vector::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 ls; - vector::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(coll_t::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 past_intervals; - hobject_t biginfo_oid = OSD::make_pg_biginfo_oid(pgid); - interval_set 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 index eb9f8dac36d..00000000000 --- a/src/tools/ceph_filestore_tool.cc +++ /dev/null @@ -1,260 +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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common/Formatter.h" - -#include "global/global_init.h" -#include "os/ObjectStore.h" -#include "os/FileStore.h" -#include "common/perf_counters.h" -#include "common/errno.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(&fspath), - "path to filestore directory, mandatory") - ("journal-path", po::value(&jpath), - "path to journal, mandatory") - ("pgid", po::value(&pgidstr), - "PG id") - ("list-lost-objects", po::value( - &list_lost_objects)->default_value(false), - "list lost objects") - ("fix-lost-objects", po::value( - &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 ceph_options, def_args; - vector ceph_option_strings = po::collect_unrecognized( - parsed.options, po::include_positional); - ceph_options.reserve(ceph_option_strings.size()); - for (vector::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 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 candidates; - r = fs->list_collections(candidates); - if (r < 0) { - cerr << "Error listing collections: " << cpp_strerror(r) << std::endl; - goto UMOUNT; - } - for (vector::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::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 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::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 index 00000000000..65be6d9cb96 --- /dev/null +++ b/src/tools/ceph_objectstore_tool.cc @@ -0,0 +1,2074 @@ +// -*- 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 +#include + +#include + +#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; + +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 = ghobject_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 = ghobject_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 data; + attr_section(const map &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 omap; + omap_section(const map &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 +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 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 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::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 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::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 " << coll_t::META_COLL << " " << log_oid.oid << std::endl; + rmt->remove(coll_t::META_COLL, log_oid); + cout << "remove " << coll_t::META_COLL << " " << biginfo_oid.oid << std::endl; + rmt->remove(coll_t::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 snap_collections; // obsolete + map 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 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 &oset) +{ + oset.clear(); + for (int count = OMAP_BATCH_SIZE; count && iter->valid(); --count, iter->next()) { + oset.insert(pair(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 aset; + ret = store->getattrs(cid, obj, aset, false); + 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 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 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::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::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 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 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::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 aset; + int r = store->getattrs(coll, ghobj, aset); + if (r < 0) { + cerr << "getattrs: " << cpp_strerror(-r) << std::endl; + return r; + } + + for (map::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 oset; + while(iter->valid()) { + get_omap_batch(iter, oset); + + for (map::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 keys; + map 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 attrset; + bufferlist valbl; + + if (debug) + cerr << "Set_omap " << ghobj << std::endl; + + if (get_fd_data(fd, valbl)) + return 1; + + attrset.insert(pair(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 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 ... (get|set)-bytes [file]" << std::endl; + cerr << "ceph-filestore-dump ... (set-(attr|omap) [file]" << std::endl; + cerr << "ceph-filestore-dump ... (set-omaphdr) [file]" << std::endl; + cerr << "ceph-filestore-dump ... (get|rm)-(attr|omap) " << std::endl; + cerr << "ceph-filestore-dump ... (get-omaphdr)" << std::endl; + cerr << "ceph-filestore-dump ... list-attrs" << std::endl; + cerr << "ceph-filestore-dump ... list-omap" << std::endl; + cerr << "ceph-filestore-dump ... 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(&fspath), + "path to filestore directory, mandatory") + ("journal-path", po::value(&jpath), + "path to journal, mandatory") + ("pgid", po::value(&pgidstr), + "PG id, mandatory except for import") + ("type", po::value(&type), + "Arg is one of [info, log, remove, export, import, list]") + ("file", po::value(&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(&object), "ghobject in json") + ("objcmd", po::value(&objcmd), "command [(get|set)-bytes, (get|set|rm)-(attr|omap), list-attrs, list-omap, remove]") + ("arg1", po::value(&arg1), "arg1 based on cmd") + ("arg2", po::value(&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 ceph_options, def_args; + vector ceph_option_strings = po::collect_unrecognized( + parsed.options, po::include_positional); + ceph_options.reserve(ceph_option_strings.size()); + for (vector::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 ls; + vector::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(coll_t::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 past_intervals; + hobject_t biginfo_oid = OSD::make_pg_biginfo_oid(pgid); + interval_set 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); +}