// -*- 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 #include #include #include #include /* Definition of AT_* constants */ #include #if defined(__APPLE__) || defined(__FreeBSD__) #include #include #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 f) { if (open_files.count(p)) { OpenFile *o = open_files[p]; fi->fh = reinterpret_cast(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(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 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(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 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 k; k.insert(key); map 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(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 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 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 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 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> 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(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 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 k; k.insert(key); map 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(o); } return 0; } static int os_mkdir(const char *path, mode_t mode) { fuse_context *fc = fuse_get_context(); FuseStore *fs = static_cast(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 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(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(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 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 k; k.insert(key); map v; fs->store->omap_get_values(ch, oid, k, &v); if (v.count(key) == 0) { map 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(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(fc->private_data); ldout(fs->store->cct, 10) << __func__ << " " << path << dendl; std::lock_guard l(fs->lock); FuseStore::OpenFile *o = reinterpret_cast(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(fc->private_data); ldout(fs->store->cct, 10) << __func__ << " " << path << " offset " << offset << " size " << size << dendl; std::lock_guard l(fs->lock); FuseStore::OpenFile *o = reinterpret_cast(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(fc->private_data); ldout(fs->store->cct, 10) << __func__ << " " << path << " offset " << offset << " size " << size << dendl; std::lock_guard l(fs->lock); FuseStore::OpenFile *o = reinterpret_cast(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(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 l(fs->lock); FuseStore::OpenFile *o = reinterpret_cast(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 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(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 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(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 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(fc->private_data); ldout(fs->store->cct, 10) << __func__ << " " << path << dendl; std::lock_guard 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("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("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; }