summaryrefslogtreecommitdiffstats
path: root/src/os/FuseStore.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/os/FuseStore.cc')
-rw-r--r--src/os/FuseStore.cc1287
1 files changed, 1287 insertions, 0 deletions
diff --git a/src/os/FuseStore.cc b/src/os/FuseStore.cc
new file mode 100644
index 000000000..a1a9aa6d0
--- /dev/null
+++ b/src/os/FuseStore.cc
@@ -0,0 +1,1287 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/compat.h"
+#include "include/ceph_fuse.h"
+#include "FuseStore.h"
+#include "os/ObjectStore.h"
+#include "include/stringify.h"
+#include "common/errno.h"
+
+#include <fuse_lowlevel.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h> /* Definition of AT_* constants */
+#include <sys/stat.h>
+
+#if defined(__APPLE__) || defined(__FreeBSD__)
+#include <sys/param.h>
+#include <sys/mount.h>
+#endif
+
+#define dout_context store->cct
+#define dout_subsys ceph_subsys_fuse
+#include "common/debug.h"
+#undef dout_prefix
+#define dout_prefix *_dout << "fuse "
+
+using std::less;
+using std::list;
+using std::map;
+using std::set;
+using std::string;
+using std::vector;
+
+using ceph::bufferlist;
+using ceph::bufferptr;
+
+// some fuse-y bits of state
+struct fs_info {
+ struct fuse_args args;
+ struct fuse *f;
+#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
+ struct fuse_chan *ch;
+#endif
+ char *mountpoint;
+};
+
+int FuseStore::open_file(string p, struct fuse_file_info *fi,
+ std::function<int(bufferlist *bl)> f)
+{
+ if (open_files.count(p)) {
+ OpenFile *o = open_files[p];
+ fi->fh = reinterpret_cast<uint64_t>(o);
+ ++o->ref;
+ return 0;
+ }
+ bufferlist bl;
+ int r = f(&bl);
+ if (r < 0) {
+ return r;
+ }
+ OpenFile *o = new OpenFile;
+ o->path = p;
+ o->bl = std::move(bl);
+ open_files[p] = o;
+ fi->fh = reinterpret_cast<uint64_t>(o);
+ ++o->ref;
+ return 0;
+}
+
+FuseStore::FuseStore(ObjectStore *s, string p)
+ : store(s),
+ mount_point(p),
+ fuse_thread(this)
+{
+ info = new fs_info();
+}
+
+FuseStore::~FuseStore()
+{
+ delete info;
+}
+
+/*
+ * / - root directory
+ * $cid/
+ * $cid/type - objectstore type
+ * $cid/bitwise_hash_start = lowest hash value
+ * $cid/bitwise_hash_end = highest hash value
+ * $cid/bitwise_hash_bits - how many bits are significant
+ * $cid/pgmeta/ - pgmeta object
+ * $cid/all/ - all objects
+ * $cid/all/$obj/
+ * $cid/all/$obj/bitwise_hash
+ * $cid/all/$obj/data
+ * $cid/all/$obj/omap/$key
+ * $cid/all/$obj/attr/$name
+ * $cid/by_bitwise_hash/$hash/$bits/$obj - all objects with this (bitwise) hash (prefix)
+ */
+enum {
+ FN_ROOT = 1,
+ FN_TYPE,
+ FN_COLLECTION,
+ FN_HASH_START,
+ FN_HASH_END,
+ FN_HASH_BITS,
+ FN_OBJECT,
+ FN_OBJECT_HASH,
+ FN_OBJECT_DATA,
+ FN_OBJECT_OMAP_HEADER,
+ FN_OBJECT_OMAP,
+ FN_OBJECT_OMAP_VAL,
+ FN_OBJECT_ATTR,
+ FN_OBJECT_ATTR_VAL,
+ FN_ALL,
+ FN_HASH_DIR,
+ FN_HASH_VAL,
+};
+
+static int parse_fn(CephContext* cct, const char *path, coll_t *cid,
+ ghobject_t *oid, string *key,
+ uint32_t *hash, uint32_t *hash_bits)
+{
+ list<string> v;
+ for (const char *p = path; *p; ++p) {
+ if (*p == '/')
+ continue;
+ const char *e;
+ for (e = p + 1; *e && *e != '/'; e++) ;
+ string c(p, e-p);
+ v.push_back(c);
+ p = e;
+ if (!*p)
+ break;
+ }
+ ldout(cct, 10) << __func__ << " path " << path << " -> " << v << dendl;
+
+ if (v.empty())
+ return FN_ROOT;
+
+ if (v.front() == "type")
+ return FN_TYPE;
+
+ if (!cid->parse(v.front())) {
+ return -ENOENT;
+ }
+ if (v.size() == 1)
+ return FN_COLLECTION;
+ v.pop_front();
+
+ if (v.front() == "bitwise_hash_start")
+ return FN_HASH_START;
+ if (v.front() == "bitwise_hash_end")
+ return FN_HASH_END;
+ if (v.front() == "bitwise_hash_bits")
+ return FN_HASH_BITS;
+ if (v.front() == "pgmeta") {
+ spg_t pgid;
+ if (cid->is_pg(&pgid)) {
+ *oid = pgid.make_pgmeta_oid();
+ v.pop_front();
+ if (v.empty())
+ return FN_OBJECT;
+ goto do_object;
+ }
+ return -ENOENT;
+ }
+ if (v.front() == "all") {
+ v.pop_front();
+ if (v.empty())
+ return FN_ALL;
+ goto do_dir;
+ }
+ if (v.front() == "by_bitwise_hash") {
+ v.pop_front();
+ if (v.empty())
+ return FN_HASH_DIR;
+ unsigned long hv, hm;
+ int r = sscanf(v.front().c_str(), "%lx", &hv);
+ if (r != 1)
+ return -ENOENT;
+ int shift = 32 - v.front().length() * 4;
+ v.pop_front();
+ if (v.empty())
+ return FN_HASH_DIR;
+ r = sscanf(v.front().c_str(), "%ld", &hm);
+ if (r != 1)
+ return -ENOENT;
+ if (hm < 1 || hm > 32)
+ return -ENOENT;
+ v.pop_front();
+ *hash = hv << shift;//hobject_t::_reverse_bits(hv << shift);
+ *hash_bits = hm;
+ if (v.empty())
+ return FN_HASH_VAL;
+ goto do_dir;
+ }
+ return -ENOENT;
+
+ do_dir:
+ {
+ string o = v.front();
+ if (!oid->parse(o)) {
+ return -ENOENT;
+ }
+ v.pop_front();
+ if (v.empty())
+ return FN_OBJECT;
+ }
+
+ do_object:
+ if (v.front() == "data")
+ return FN_OBJECT_DATA;
+ if (v.front() == "omap_header")
+ return FN_OBJECT_OMAP_HEADER;
+ if (v.front() == "omap") {
+ v.pop_front();
+ if (v.empty())
+ return FN_OBJECT_OMAP;
+ *key = v.front();
+ v.pop_front();
+ if (v.empty())
+ return FN_OBJECT_OMAP_VAL;
+ return -ENOENT;
+ }
+ if (v.front() == "attr") {
+ v.pop_front();
+ if (v.empty())
+ return FN_OBJECT_ATTR;
+ *key = v.front();
+ v.pop_front();
+ if (v.empty())
+ return FN_OBJECT_ATTR_VAL;
+ return -ENOENT;
+ }
+ if (v.front() == "bitwise_hash")
+ return FN_OBJECT_HASH;
+ return -ENOENT;
+}
+
+
+static int os_getattr(const char *path, struct stat *stbuf
+#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
+ , struct fuse_file_info *fi
+#endif
+ )
+{
+ fuse_context *fc = fuse_get_context();
+ FuseStore *fs = static_cast<FuseStore*>(fc->private_data);
+ ldout(fs->store->cct, 10) << __func__ << " " << path << dendl;
+ coll_t cid;
+ ghobject_t oid;
+ string key;
+ uint32_t hash_value, hash_bits;
+ int t = parse_fn(fs->store->cct, path, &cid, &oid, &key, &hash_value,
+ &hash_bits);
+ if (t < 0)
+ return t;
+
+ std::lock_guard<std::mutex> l(fs->lock);
+
+ stbuf->st_size = 0;
+ stbuf->st_uid = 0;
+ stbuf->st_gid = 0;
+ stbuf->st_mode = S_IFREG | 0700;
+
+ auto ch = fs->store->open_collection(cid);
+
+ switch (t) {
+ case FN_OBJECT_OMAP:
+ case FN_OBJECT_ATTR:
+ case FN_OBJECT:
+ case FN_OBJECT_DATA:
+ case FN_OBJECT_OMAP_HEADER:
+ case FN_OBJECT_OMAP_VAL:
+ {
+ spg_t pgid;
+ if (cid.is_pg(&pgid)) {
+ if (!ch) {
+ return -ENOENT;
+ }
+ int bits = fs->store->collection_bits(ch);
+ if (bits >= 0 && !oid.match(bits, pgid.ps())) {
+ // sorry, not part of this PG
+ return -ENOENT;
+ }
+ }
+ }
+ break;
+ }
+
+ switch (t) {
+ case FN_OBJECT_OMAP:
+ case FN_OBJECT_ATTR:
+ case FN_OBJECT:
+ if (!fs->store->exists(ch, oid))
+ return -ENOENT;
+ // fall-thru
+ case FN_ALL:
+ case FN_HASH_DIR:
+ case FN_HASH_VAL:
+ case FN_COLLECTION:
+ if (!fs->store->collection_exists(cid))
+ return -ENOENT;
+ // fall-thru
+ case FN_ROOT:
+ stbuf->st_mode = S_IFDIR | 0700;
+ return 0;
+
+ case FN_TYPE:
+ stbuf->st_size = fs->store->get_type().length() + 1;
+ break;
+
+ case FN_OBJECT_HASH:
+ if (!fs->store->exists(ch, oid))
+ return -ENOENT;
+ stbuf->st_size = 9;
+ return 0;
+
+ case FN_HASH_END:
+ if (!ch)
+ return -ENOENT;
+ if (fs->store->collection_bits(ch) < 0)
+ return -ENOENT;
+ // fall-thru
+ case FN_HASH_START:
+ stbuf->st_size = 9;
+ return 0;
+
+ case FN_HASH_BITS:
+ {
+ if (!ch)
+ return -ENOENT;
+ int bits = fs->store->collection_bits(ch);
+ if (bits < 0)
+ return -ENOENT;
+ char buf[12];
+ snprintf(buf, sizeof(buf), "%d\n", bits);
+ stbuf->st_size = strlen(buf);
+ }
+ return 0;
+
+ case FN_OBJECT_DATA:
+ {
+ if (!fs->store->exists(ch, oid))
+ return -ENOENT;
+ int r = fs->store->stat(ch, oid, stbuf);
+ if (r < 0)
+ return r;
+ }
+ break;
+
+ case FN_OBJECT_OMAP_HEADER:
+ {
+ if (!fs->store->exists(ch, oid))
+ return -ENOENT;
+ bufferlist bl;
+ fs->store->omap_get_header(ch, oid, &bl);
+ stbuf->st_size = bl.length();
+ }
+ break;
+
+ case FN_OBJECT_OMAP_VAL:
+ {
+ if (!fs->store->exists(ch, oid))
+ return -ENOENT;
+ set<string> k;
+ k.insert(key);
+ map<string,bufferlist> v;
+ fs->store->omap_get_values(ch, oid, k, &v);
+ if (!v.count(key)) {
+ return -ENOENT;
+ }
+ stbuf->st_size = v[key].length();
+ }
+ break;
+
+ case FN_OBJECT_ATTR_VAL:
+ {
+ if (!fs->store->exists(ch, oid))
+ return -ENOENT;
+ bufferptr v;
+ int r = fs->store->getattr(ch, oid, key.c_str(), v);
+ if (r == -ENODATA)
+ r = -ENOENT;
+ if (r < 0)
+ return r;
+ stbuf->st_size = v.length();
+ }
+ break;
+
+ default:
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+static int os_readdir(const char *path,
+ void *buf,
+ fuse_fill_dir_t filler,
+ off_t offset,
+ struct fuse_file_info *fi
+#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
+ , enum fuse_readdir_flags
+#endif
+ )
+{
+ fuse_context *fc = fuse_get_context();
+ FuseStore *fs = static_cast<FuseStore*>(fc->private_data);
+ ldout(fs->store->cct, 10) << __func__ << " " << path << " offset " << offset
+ << dendl;
+ coll_t cid;
+ ghobject_t oid;
+ string key;
+ uint32_t hash_value, hash_bits;
+ int t = parse_fn(fs->store->cct, path, &cid, &oid, &key, &hash_value,
+ &hash_bits);
+ if (t < 0)
+ return t;
+
+ std::lock_guard<std::mutex> l(fs->lock);
+
+ auto ch = fs->store->open_collection(cid);
+
+ // we can't shift 32 bits or else off_t will go negative
+ const int hash_shift = 31;
+
+ switch (t) {
+ case FN_ROOT:
+ {
+ filler_compat(filler, buf, "type", NULL, 0);
+ vector<coll_t> cls;
+ fs->store->list_collections(cls);
+ for (auto c : cls) {
+ int r = filler_compat(filler, buf, stringify(c).c_str(), NULL, 0);
+ if (r > 0)
+ break;
+ }
+ }
+ break;
+
+ case FN_COLLECTION:
+ {
+ if (!ch) {
+ return -ENOENT;
+ }
+ filler_compat(filler, buf, "bitwise_hash_start", NULL, 0);
+ if (fs->store->collection_bits(ch) >= 0) {
+ filler_compat(filler, buf, "bitwise_hash_end", NULL, 0);
+ filler_compat(filler, buf, "bitwise_hash_bits", NULL, 0);
+ }
+ filler_compat(filler, buf, "all", NULL, 0);
+ filler_compat(filler, buf, "by_bitwise_hash", NULL, 0);
+ spg_t pgid;
+ if (cid.is_pg(&pgid) &&
+ fs->store->exists(ch, pgid.make_pgmeta_oid())) {
+ filler_compat(filler, buf, "pgmeta", NULL, 0);
+ }
+ }
+ break;
+
+ case FN_OBJECT:
+ {
+ filler_compat(filler, buf, "bitwise_hash", NULL, 0);
+ filler_compat(filler, buf, "data", NULL, 0);
+ filler_compat(filler, buf, "omap", NULL, 0);
+ filler_compat(filler, buf, "attr", NULL, 0);
+ filler_compat(filler, buf, "omap_header", NULL, 0);
+ }
+ break;
+
+ case FN_HASH_VAL:
+ case FN_ALL:
+ {
+ uint32_t bitwise_hash = (offset >> hash_shift) & 0xffffffff;
+ uint32_t hashoff = offset - (bitwise_hash << hash_shift);
+ int skip = hashoff;
+ ghobject_t next = cid.get_min_hobj();
+ if (offset) {
+ // obey the offset
+ next.hobj.set_hash(hobject_t::_reverse_bits(bitwise_hash));
+ } else if (t == FN_HASH_VAL) {
+ next.hobj.set_hash(hobject_t::_reverse_bits(hash_value));
+ }
+ ghobject_t last;
+ if (t == FN_HASH_VAL) {
+ last = next;
+ uint64_t rev_end = (hash_value | (0xffffffff >> hash_bits)) + 1;
+ if (rev_end >= 0x100000000)
+ last = ghobject_t::get_max();
+ else
+ last.hobj.set_hash(hobject_t::_reverse_bits(rev_end));
+ } else {
+ last = ghobject_t::get_max();
+ }
+ ldout(fs->store->cct, 10) << __func__ << std::hex
+ << " offset " << offset << " hash "
+ << hobject_t::_reverse_bits(hash_value)
+ << std::dec
+ << "/" << hash_bits
+ << " first " << next << " last " << last
+ << dendl;
+ while (true) {
+ vector<ghobject_t> ls;
+ int r = fs->store->collection_list(
+ ch, next, last, 1000, &ls, &next);
+ if (r < 0)
+ return r;
+ for (auto p : ls) {
+ if (skip) {
+ --skip;
+ continue;
+ }
+ uint32_t cur_bitwise_hash = p.hobj.get_bitwise_key_u32();
+ if (cur_bitwise_hash != bitwise_hash) {
+ bitwise_hash = cur_bitwise_hash;
+ hashoff = 0;
+ }
+ ++hashoff;
+ uint64_t cur_off = ((uint64_t)bitwise_hash << hash_shift) |
+ (uint64_t)hashoff;
+ string s = stringify(p);
+ r = filler_compat(filler, buf, s.c_str(), NULL, cur_off);
+ if (r)
+ break;
+ }
+ if (r)
+ break;
+ if (next == ghobject_t::get_max() || next == last)
+ break;
+ }
+ }
+ break;
+
+ case FN_OBJECT_OMAP:
+ {
+ set<string> keys;
+ fs->store->omap_get_keys(ch, oid, &keys);
+ unsigned skip = offset;
+ for (auto k : keys) {
+ if (skip) {
+ --skip;
+ continue;
+ }
+ ++offset;
+ int r = filler_compat(filler, buf, k.c_str(), NULL, offset);
+ if (r)
+ break;
+ }
+ }
+ break;
+
+ case FN_OBJECT_ATTR:
+ {
+ map<string,bufferptr,less<>> aset;
+ fs->store->getattrs(ch, oid, aset);
+ unsigned skip = offset;
+ for (auto a : aset) {
+ if (skip) {
+ --skip;
+ continue;
+ }
+ ++offset;
+ int r = filler_compat(filler, buf, a.first.c_str(), NULL, offset);
+ if (r)
+ break;
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+static int os_open(const char *path, struct fuse_file_info *fi)
+{
+ fuse_context *fc = fuse_get_context();
+ FuseStore *fs = static_cast<FuseStore*>(fc->private_data);
+ ldout(fs->store->cct, 10) << __func__ << " " << path << dendl;
+ coll_t cid;
+ ghobject_t oid;
+ string key;
+ uint32_t hash_value, hash_bits;
+ int t = parse_fn(fs->store->cct, path, &cid, &oid, &key, &hash_value,
+ &hash_bits);
+ if (t < 0)
+ return t;
+
+ std::lock_guard<std::mutex> l(fs->lock);
+
+ auto ch = fs->store->open_collection(cid);
+
+ bufferlist *pbl = 0;
+ switch (t) {
+ case FN_TYPE:
+ pbl = new bufferlist;
+ pbl->append(fs->store->get_type());
+ pbl->append("\n");
+ break;
+
+ case FN_HASH_START:
+ {
+ pbl = new bufferlist;
+ spg_t pgid;
+ if (cid.is_pg(&pgid)) {
+ unsigned long h;
+ h = hobject_t::_reverse_bits(pgid.ps());
+ char buf[10];
+ snprintf(buf, sizeof(buf), "%08lx\n", h);
+ pbl->append(buf);
+ } else {
+ pbl->append("00000000\n");
+ }
+ }
+ break;
+
+ case FN_HASH_END:
+ {
+ if (!ch) {
+ return -ENOENT;
+ }
+ spg_t pgid;
+ unsigned long h;
+ if (cid.is_pg(&pgid)) {
+ int hash_bits = fs->store->collection_bits(ch);
+ if (hash_bits >= 0) {
+ uint64_t rev_start = hobject_t::_reverse_bits(pgid.ps());
+ uint64_t rev_end = (rev_start | (0xffffffff >> hash_bits));
+ h = rev_end;
+ } else {
+ return -ENOENT;
+ }
+ } else {
+ h = 0xffffffff;
+ }
+ char buf[10];
+ snprintf(buf, sizeof(buf), "%08lx\n", h);
+ pbl = new bufferlist;
+ pbl->append(buf);
+ }
+ break;
+
+ case FN_HASH_BITS:
+ {
+ if (!ch) {
+ return -ENOENT;
+ }
+ int r = fs->store->collection_bits(ch);
+ if (r < 0)
+ return r;
+ char buf[12];
+ snprintf(buf, sizeof(buf), "%d\n", r);
+ pbl = new bufferlist;
+ pbl->append(buf);
+ }
+ break;
+
+ case FN_OBJECT_HASH:
+ {
+ pbl = new bufferlist;
+ char buf[10];
+ snprintf(buf, sizeof(buf), "%08x\n",
+ (unsigned)oid.hobj.get_bitwise_key_u32());
+ pbl->append(buf);
+ }
+ break;
+
+ case FN_OBJECT_DATA:
+ {
+ int r = fs->open_file(
+ path, fi,
+ [&](bufferlist *pbl) {
+ return fs->store->read(ch, oid, 0, 0, *pbl);
+ });
+ if (r < 0) {
+ return r;
+ }
+ }
+ break;
+
+ case FN_OBJECT_ATTR_VAL:
+ {
+ int r = fs->open_file(
+ path, fi,
+ [&](bufferlist *pbl) {
+ bufferptr bp;
+ int r = fs->store->getattr(ch, oid, key.c_str(), bp);
+ if (r < 0)
+ return r;
+ pbl->append(bp);
+ return 0;
+ });
+ if (r < 0)
+ return r;
+ }
+ break;
+
+ case FN_OBJECT_OMAP_VAL:
+ {
+ int r = fs->open_file(
+ path, fi,
+ [&](bufferlist *pbl) {
+ set<string> k;
+ k.insert(key);
+ map<string,bufferlist> v;
+ int r = fs->store->omap_get_values(ch, oid, k, &v);
+ if (r < 0)
+ return r;
+ *pbl = v[key];
+ return 0;
+ });
+ if (r < 0)
+ return r;
+ }
+ break;
+
+ case FN_OBJECT_OMAP_HEADER:
+ {
+ int r = fs->open_file(
+ path, fi,
+ [&](bufferlist *pbl) {
+ return fs->store->omap_get_header(ch, oid, pbl);
+ });
+ if (r < 0)
+ return r;
+ }
+ break;
+ }
+
+ if (pbl) {
+ FuseStore::OpenFile *o = new FuseStore::OpenFile;
+ o->bl = std::move(*pbl);
+ fi->fh = reinterpret_cast<uint64_t>(o);
+ }
+ return 0;
+}
+
+static int os_mkdir(const char *path, mode_t mode)
+{
+ fuse_context *fc = fuse_get_context();
+ FuseStore *fs = static_cast<FuseStore*>(fc->private_data);
+ ldout(fs->store->cct, 10) << __func__ << " " << path << dendl;
+ coll_t cid;
+ ghobject_t oid;
+ string key;
+ uint32_t hash_value, hash_bits;
+ int f = parse_fn(fs->store->cct, path, &cid, &oid, &key, &hash_value,
+ &hash_bits);
+ if (f < 0)
+ return f;
+
+ std::lock_guard<std::mutex> l(fs->lock);
+
+ ObjectStore::CollectionHandle ch;
+
+ ObjectStore::Transaction t;
+ switch (f) {
+ case FN_OBJECT:
+ {
+ ch = fs->store->open_collection(cid);
+ if (!ch) {
+ return -ENOENT;
+ }
+ spg_t pgid;
+ if (cid.is_pg(&pgid)) {
+ int bits = fs->store->collection_bits(ch);
+ if (bits >= 0 && !oid.match(bits, pgid.ps())) {
+ // sorry, not part of this PG
+ return -EINVAL;
+ }
+ }
+ t.touch(cid, oid);
+ ch = fs->store->open_collection(cid);
+ }
+ break;
+
+ case FN_COLLECTION:
+ if (cid.is_pg()) {
+ // use the mode for the bit count. e.g., mkdir --mode=0003
+ // mnt/0.7_head will create 0.7 with bits = 3.
+ mode &= 0777;
+ if (mode >= 32)
+ return -EINVAL;
+ } else {
+ mode = 0;
+ }
+ t.create_collection(cid, mode);
+ ch = fs->store->create_new_collection(cid);
+ break;
+
+ default:
+ return -EPERM;
+ }
+
+ if (!t.empty()) {
+ fs->store->queue_transaction(ch, std::move(t));
+ }
+
+ return 0;
+}
+
+static int os_chmod(const char *path, mode_t mode
+#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
+ , struct fuse_file_info *fi
+#endif
+ )
+{
+ fuse_context *fc = fuse_get_context();
+ FuseStore *fs = static_cast<FuseStore*>(fc->private_data);
+ ldout(fs->store->cct, 10) << __func__ << " " << path << dendl;
+ return 0;
+}
+
+static int os_create(const char *path, mode_t mode, struct fuse_file_info *fi)
+{
+ fuse_context *fc = fuse_get_context();
+ FuseStore *fs = static_cast<FuseStore*>(fc->private_data);
+ ldout(fs->store->cct, 10) << __func__ << " " << path << dendl;
+ coll_t cid;
+ ghobject_t oid;
+ string key;
+ uint32_t hash_value, hash_bits;
+ int f = parse_fn(fs->store->cct, path, &cid, &oid, &key, &hash_value,
+ &hash_bits);
+ if (f < 0)
+ return f;
+
+ std::lock_guard<std::mutex> l(fs->lock);
+
+ ObjectStore::CollectionHandle ch = fs->store->open_collection(cid);
+
+ ObjectStore::Transaction t;
+ bufferlist *pbl = 0;
+ switch (f) {
+ case FN_OBJECT_DATA:
+ {
+ pbl = new bufferlist;
+ fs->store->read(ch, oid, 0, 0, *pbl);
+ }
+ break;
+
+ case FN_OBJECT_ATTR_VAL:
+ {
+ pbl = new bufferlist;
+ bufferptr bp;
+ int r = fs->store->getattr(ch, oid, key.c_str(), bp);
+ if (r == -ENODATA) {
+ bufferlist empty;
+ t.setattr(cid, oid, key.c_str(), empty);
+ }
+ pbl->append(bp);
+ }
+ break;
+
+ case FN_OBJECT_OMAP_VAL:
+ {
+ pbl = new bufferlist;
+ set<string> k;
+ k.insert(key);
+ map<string,bufferlist> v;
+ fs->store->omap_get_values(ch, oid, k, &v);
+ if (v.count(key) == 0) {
+ map<string,bufferlist> aset;
+ aset[key] = bufferlist();
+ t.omap_setkeys(cid, oid, aset);
+ } else {
+ *pbl = v[key];
+ }
+ }
+ break;
+ }
+
+ if (!t.empty()) {
+ fs->store->queue_transaction(ch, std::move(t));
+ }
+
+ if (pbl) {
+ FuseStore::OpenFile *o = new FuseStore::OpenFile;
+ o->bl = std::move(*pbl);
+ o->dirty = true;
+ fi->fh = reinterpret_cast<uint64_t>(o);
+ }
+ return 0;
+}
+
+static int os_release(const char *path, struct fuse_file_info *fi)
+{
+ fuse_context *fc = fuse_get_context();
+ FuseStore *fs = static_cast<FuseStore*>(fc->private_data);
+ ldout(fs->store->cct, 10) << __func__ << " " << path << dendl;
+ std::lock_guard<std::mutex> l(fs->lock);
+ FuseStore::OpenFile *o = reinterpret_cast<FuseStore::OpenFile*>(fi->fh);
+ if (--o->ref == 0) {
+ ldout(fs->store->cct, 10) << __func__ << " closing last " << o->path << dendl;
+ fs->open_files.erase(o->path);
+ delete o;
+ }
+ return 0;
+}
+
+static int os_read(const char *path, char *buf, size_t size, off_t offset,
+ struct fuse_file_info *fi)
+{
+ fuse_context *fc = fuse_get_context();
+ FuseStore *fs = static_cast<FuseStore*>(fc->private_data);
+ ldout(fs->store->cct, 10) << __func__ << " " << path << " offset " << offset
+ << " size " << size << dendl;
+ std::lock_guard<std::mutex> l(fs->lock);
+ FuseStore::OpenFile *o = reinterpret_cast<FuseStore::OpenFile*>(fi->fh);
+ if (!o)
+ return 0;
+ if (offset >= o->bl.length())
+ return 0;
+ if (offset + size > o->bl.length())
+ size = o->bl.length() - offset;
+ bufferlist r;
+ r.substr_of(o->bl, offset, size);
+ memcpy(buf, r.c_str(), r.length());
+ return r.length();
+}
+
+static int os_write(const char *path, const char *buf, size_t size,
+ off_t offset, struct fuse_file_info *fi)
+{
+ fuse_context *fc = fuse_get_context();
+ FuseStore *fs = static_cast<FuseStore*>(fc->private_data);
+ ldout(fs->store->cct, 10) << __func__ << " " << path << " offset " << offset
+ << " size " << size << dendl;
+ std::lock_guard<std::mutex> l(fs->lock);
+ FuseStore::OpenFile *o = reinterpret_cast<FuseStore::OpenFile*>(fi->fh);
+ if (!o)
+ return 0;
+
+ bufferlist final;
+ if (offset) {
+ if (offset > o->bl.length()) {
+ final.substr_of(o->bl, 0, offset);
+ } else {
+ final.claim_append(o->bl);
+ size_t zlen = offset - final.length();
+ final.append_zero(zlen);
+ }
+ }
+ final.append(buf, size);
+ if (offset + size < o->bl.length()) {
+ bufferlist rest;
+ rest.substr_of(o->bl, offset + size, o->bl.length() - offset - size);
+ final.claim_append(rest);
+ }
+ o->bl = final;
+ o->dirty = true;
+ return size;
+}
+
+int os_flush(const char *path, struct fuse_file_info *fi)
+{
+ fuse_context *fc = fuse_get_context();
+ FuseStore *fs = static_cast<FuseStore*>(fc->private_data);
+ ldout(fs->store->cct, 10) << __func__ << " " << path << dendl;
+ coll_t cid;
+ ghobject_t oid;
+ string key;
+ uint32_t hash_value, hash_bits;
+ int f = parse_fn(fs->store->cct, path, &cid, &oid, &key, &hash_value,
+ &hash_bits);
+ if (f < 0)
+ return f;
+
+ std::lock_guard<std::mutex> l(fs->lock);
+
+ FuseStore::OpenFile *o = reinterpret_cast<FuseStore::OpenFile*>(fi->fh);
+ if (!o)
+ return 0;
+ if (!o->dirty)
+ return 0;
+
+ ObjectStore::CollectionHandle ch = fs->store->open_collection(cid);
+
+ ObjectStore::Transaction t;
+
+ switch (f) {
+ case FN_OBJECT_DATA:
+ t.write(cid, oid, 0, o->bl.length(), o->bl);
+ break;
+
+ case FN_OBJECT_ATTR_VAL:
+ t.setattr(cid, oid, key.c_str(), o->bl);
+ break;
+
+ case FN_OBJECT_OMAP_VAL:
+ {
+ map<string,bufferlist> aset;
+ aset[key] = o->bl;
+ t.omap_setkeys(cid, oid, aset);
+ break;
+ }
+
+ case FN_OBJECT_OMAP_HEADER:
+ t.omap_setheader(cid, oid, o->bl);
+ break;
+
+ default:
+ return 0;
+ }
+
+ fs->store->queue_transaction(ch, std::move(t));
+
+ return 0;
+}
+
+static int os_unlink(const char *path)
+{
+ fuse_context *fc = fuse_get_context();
+ FuseStore *fs = static_cast<FuseStore*>(fc->private_data);
+ ldout(fs->store->cct, 10) << __func__ << " " << path << dendl;
+ coll_t cid;
+ ghobject_t oid;
+ string key;
+ uint32_t hash_value, hash_bits;
+ int f = parse_fn(fs->store->cct, path, &cid, &oid, &key, &hash_value,
+ &hash_bits);
+ if (f < 0)
+ return f;
+
+ std::lock_guard<std::mutex> l(fs->lock);
+
+ ObjectStore::CollectionHandle ch = fs->store->open_collection(cid);
+ ObjectStore::Transaction t;
+
+ switch (f) {
+ case FN_OBJECT_OMAP_VAL:
+ {
+ t.omap_rmkey(cid, oid, key);
+ }
+ break;
+
+ case FN_OBJECT_ATTR_VAL:
+ t.rmattr(cid, oid, key.c_str());
+ break;
+
+ case FN_OBJECT_OMAP_HEADER:
+ {
+ bufferlist empty;
+ t.omap_setheader(cid, oid, empty);
+ }
+ break;
+
+ case FN_OBJECT:
+ t.remove(cid, oid);
+ break;
+
+ case FN_COLLECTION:
+ {
+ bool empty;
+ int r = fs->store->collection_empty(ch, &empty);
+ if (r < 0)
+ return r;
+ if (!empty)
+ return -ENOTEMPTY;
+ t.remove_collection(cid);
+ }
+ break;
+
+ case FN_OBJECT_DATA:
+ t.truncate(cid, oid, 0);
+ break;
+
+ default:
+ return -EPERM;
+ }
+
+ fs->store->queue_transaction(ch, std::move(t));
+
+ return 0;
+}
+
+static int os_truncate(const char *path, off_t size
+#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
+ , struct fuse_file_info *fi
+#endif
+ )
+{
+ fuse_context *fc = fuse_get_context();
+ FuseStore *fs = static_cast<FuseStore*>(fc->private_data);
+ ldout(fs->store->cct, 10) << __func__ << " " << path << " size " << size << dendl;
+ coll_t cid;
+ ghobject_t oid;
+ string key;
+ uint32_t hash_value, hash_bits;
+ int f = parse_fn(fs->store->cct, path, &cid, &oid, &key, &hash_value,
+ &hash_bits);
+ if (f < 0)
+ return f;
+
+ if (f == FN_OBJECT_OMAP_VAL ||
+ f == FN_OBJECT_ATTR_VAL ||
+ f == FN_OBJECT_OMAP_HEADER) {
+ if (size)
+ return -EPERM;
+ return 0;
+ }
+ if (f != FN_OBJECT_DATA)
+ return -EPERM;
+
+ std::lock_guard<std::mutex> l(fs->lock);
+
+ if (fs->open_files.count(path)) {
+ FuseStore::OpenFile *o = fs->open_files[path];
+ if (o->bl.length() > size) {
+ bufferlist t;
+ t.substr_of(o->bl, 0, size);
+ o->bl.swap(t);
+ }
+ }
+
+ ObjectStore::CollectionHandle ch = fs->store->open_collection(cid);
+ ObjectStore::Transaction t;
+ t.truncate(cid, oid, size);
+ fs->store->queue_transaction(ch, std::move(t));
+ return 0;
+}
+
+static int os_statfs(const char *path, struct statvfs *stbuf)
+{
+ fuse_context *fc = fuse_get_context();
+ FuseStore *fs = static_cast<FuseStore*>(fc->private_data);
+ ldout(fs->store->cct, 10) << __func__ << " " << path << dendl;
+ std::lock_guard<std::mutex> l(fs->lock);
+
+ struct store_statfs_t s;
+ int r = fs->store->statfs(&s);
+ if (r < 0)
+ return r;
+ stbuf->f_bsize = 4096; // LIES!
+ stbuf->f_blocks = s.total / 4096;
+ stbuf->f_bavail = s.available / 4096;
+ stbuf->f_bfree = stbuf->f_bavail;
+
+ ldout(fs->store->cct, 10) << __func__ << " " << path << ": "
+ << stbuf->f_bavail << "/" << stbuf->f_blocks << dendl;
+ return 0;
+}
+
+static struct fuse_operations fs_oper = {
+ getattr: os_getattr,
+ readlink: 0,
+#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
+ getdir: 0,
+#endif
+ mknod: 0,
+ mkdir: os_mkdir,
+ unlink: os_unlink,
+ rmdir: os_unlink,
+ symlink: 0,
+ rename: 0,
+ link: 0,
+ chmod: os_chmod,
+ chown: 0,
+ truncate: os_truncate,
+#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
+ utime: 0,
+#endif
+ open: os_open,
+ read: os_read,
+ write: os_write,
+ statfs: os_statfs,
+ flush: os_flush,
+ release: os_release,
+ fsync: 0,
+ setxattr: 0,
+ getxattr: 0,
+ listxattr: 0,
+ removexattr: 0,
+ opendir: 0,
+ readdir: os_readdir,
+ releasedir: 0,
+ fsyncdir: 0,
+ init: 0,
+ destroy: 0,
+ access: 0,
+ create: os_create,
+};
+
+int FuseStore::main()
+{
+ const char *v[] = {
+ "foo",
+ mount_point.c_str(),
+ "-f",
+ "-d", // debug
+ };
+ int c = 3;
+ auto fuse_debug = store->cct->_conf.get_val<bool>("fuse_debug");
+ if (fuse_debug)
+ ++c;
+ return fuse_main(c, (char**)v, &fs_oper, (void*)this);
+}
+
+int FuseStore::start()
+{
+ dout(10) << __func__ << dendl;
+
+ memset(&info->args, 0, sizeof(info->args));
+ const char *v[] = {
+ "foo",
+ mount_point.c_str(),
+ "-f", // foreground
+ "-d", // debug
+ };
+ int c = 3;
+#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
+ int rc;
+ struct fuse_cmdline_opts opts = {};
+#endif
+ auto fuse_debug = store->cct->_conf.get_val<bool>("fuse_debug");
+ if (fuse_debug)
+ ++c;
+ fuse_args a = FUSE_ARGS_INIT(c, (char**)v);
+ info->args = a;
+#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
+ if (fuse_parse_cmdline(&info->args, &opts) == -1) {
+#else
+ if (fuse_parse_cmdline(&info->args, &info->mountpoint, NULL, NULL) == -1) {
+#endif
+ derr << __func__ << " failed to parse args" << dendl;
+ return -EINVAL;
+ }
+
+#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
+ info->mountpoint = opts.mountpoint;
+ info->f = fuse_new(&info->args, &fs_oper, sizeof(fs_oper), (void*)this);
+ if (!info->f) {
+ derr << __func__ << " fuse_new failed" << dendl;
+ return -EIO;
+ }
+
+ rc = fuse_mount(info->f, info->mountpoint);
+ if (rc != 0) {
+ derr << __func__ << " fuse_mount failed" << dendl;
+ return -EIO;
+ }
+#else
+ info->ch = fuse_mount(info->mountpoint, &info->args);
+ if (!info->ch) {
+ derr << __func__ << " fuse_mount failed" << dendl;
+ return -EIO;
+ }
+
+ info->f = fuse_new(info->ch, &info->args, &fs_oper, sizeof(fs_oper),
+ (void*)this);
+ if (!info->f) {
+ fuse_unmount(info->mountpoint, info->ch);
+ derr << __func__ << " fuse_new failed" << dendl;
+ return -EIO;
+ }
+#endif
+
+ fuse_thread.create("fusestore");
+ dout(10) << __func__ << " done" << dendl;
+ return 0;
+}
+
+int FuseStore::loop()
+{
+ dout(10) << __func__ << " enter" << dendl;
+ int r = fuse_loop(info->f);
+ if (r)
+ derr << __func__ << " got " << cpp_strerror(r) << dendl;
+ dout(10) << __func__ << " exit" << dendl;
+ return r;
+}
+
+int FuseStore::stop()
+{
+ dout(10) << __func__ << " enter" << dendl;
+#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
+ fuse_unmount(info->f);
+#else
+ fuse_unmount(info->mountpoint, info->ch);
+#endif
+ fuse_thread.join();
+ fuse_destroy(info->f);
+ dout(10) << __func__ << " exit" << dendl;
+ return 0;
+}