// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #include "include/compat.h" #include "FuseStore.h" #include "os/ObjectStore.h" #include "include/stringify.h" #include "common/errno.h" #define FUSE_USE_VERSION 30 #include #include #include #include #include /* Definition of AT_* constants */ #include #if defined(DARWIN) || 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 " // some fuse-y bits of state struct fs_info { struct fuse_args args; struct fuse *f; struct fuse_chan *ch; 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.claim(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) { 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; 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)) { int bits = fs->store->collection_bits(cid); 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(cid, 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(cid, oid)) return -ENOENT; stbuf->st_size = 9; return 0; case FN_HASH_END: if (!fs->store->collection_exists(cid)) return -ENOENT; if (fs->store->collection_bits(cid) < 0) return -ENOENT; // fall-thru case FN_HASH_START: stbuf->st_size = 9; return 0; case FN_HASH_BITS: { if (!fs->store->collection_exists(cid)) return -ENOENT; int bits = fs->store->collection_bits(cid); if (bits < 0) return -ENOENT; char buf[8]; snprintf(buf, sizeof(buf), "%d\n", bits); stbuf->st_size = strlen(buf); } return 0; case FN_OBJECT_DATA: { if (!fs->store->exists(cid, oid)) return -ENOENT; int r = fs->store->stat(cid, oid, stbuf); if (r < 0) return r; } break; case FN_OBJECT_OMAP_HEADER: { if (!fs->store->exists(cid, oid)) return -ENOENT; bufferlist bl; fs->store->omap_get_header(cid, oid, &bl); stbuf->st_size = bl.length(); } break; case FN_OBJECT_OMAP_VAL: { if (!fs->store->exists(cid, oid)) return -ENOENT; set k; k.insert(key); map v; fs->store->omap_get_values(cid, 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(cid, oid)) return -ENOENT; bufferptr v; int r = fs->store->getattr(cid, 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) { 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); // we can't shift 32 bits or else off_t will go negative const int hash_shift = 31; switch (t) { case FN_ROOT: { filler(buf, "type", NULL, 0); vector cls; fs->store->list_collections(cls); for (auto c : cls) { int r = filler(buf, stringify(c).c_str(), NULL, 0); if (r > 0) break; } } break; case FN_COLLECTION: { filler(buf, "bitwise_hash_start", NULL, 0); if (fs->store->collection_bits(cid) >= 0) { filler(buf, "bitwise_hash_end", NULL, 0); filler(buf, "bitwise_hash_bits", NULL, 0); } filler(buf, "all", NULL, 0); filler(buf, "by_bitwise_hash", NULL, 0); spg_t pgid; if (cid.is_pg(&pgid) && fs->store->exists(cid, pgid.make_pgmeta_oid())) { filler(buf, "pgmeta", NULL, 0); } } break; case FN_OBJECT: { filler(buf, "bitwise_hash", NULL, 0); filler(buf, "data", NULL, 0); filler(buf, "omap", NULL, 0); filler(buf, "attr", NULL, 0); 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( cid, 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(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(cid, oid, &keys); unsigned skip = offset; for (auto k : keys) { if (skip) { --skip; continue; } ++offset; int r = filler(buf, k.c_str(), NULL, offset); if (r) break; } } break; case FN_OBJECT_ATTR: { map aset; fs->store->getattrs(cid, oid, aset); unsigned skip = offset; for (auto a : aset) { if (skip) { --skip; continue; } ++offset; int r = 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); 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: { spg_t pgid; unsigned long h; if (cid.is_pg(&pgid)) { int hash_bits = fs->store->collection_bits(cid); 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: { int r = fs->store->collection_bits(cid); if (r < 0) return r; char buf[8]; 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(cid, 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(cid, 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(cid, 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(cid, oid, pbl); }); if (r < 0) return r; } break; } if (pbl) { FuseStore::OpenFile *o = new FuseStore::OpenFile; o->bl.claim(*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::Transaction t; switch (f) { case FN_OBJECT: { spg_t pgid; if (cid.is_pg(&pgid)) { int bits = fs->store->collection_bits(cid); if (bits >= 0 && !oid.match(bits, pgid.ps())) { // sorry, not part of this PG return -EINVAL; } } t.touch(cid, oid); } 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); break; default: return -EPERM; } if (!t.empty()) { ceph::shared_ptr osr( new ObjectStore::Sequencer("fuse")); fs->store->apply_transaction(&*osr, std::move(t)); C_SaferCond waiter; if (!osr->flush_commit(&waiter)) waiter.wait(); } return 0; } static int os_chmod(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; 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::Transaction t; bufferlist *pbl = 0; switch (f) { case FN_OBJECT_DATA: { pbl = new bufferlist; fs->store->read(cid, oid, 0, 0, *pbl); } break; case FN_OBJECT_ATTR_VAL: { pbl = new bufferlist; bufferptr bp; int r = fs->store->getattr(cid, 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(cid, 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()) { ceph::shared_ptr osr( new ObjectStore::Sequencer("fuse")); fs->store->apply_transaction(&*osr, std::move(t)); C_SaferCond waiter; if (!osr->flush_commit(&waiter)) waiter.wait(); } if (pbl) { FuseStore::OpenFile *o = new FuseStore::OpenFile; o->bl.claim(*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::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; } ceph::shared_ptr osr( new ObjectStore::Sequencer("fuse")); fs->store->apply_transaction(&*osr, std::move(t)); C_SaferCond waiter; if (!osr->flush_commit(&waiter)) waiter.wait(); 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::Transaction t; switch (f) { case FN_OBJECT_OMAP_VAL: { set keys; keys.insert(key); t.omap_rmkeys(cid, oid, keys); } 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(cid, &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; } ceph::shared_ptr osr( new ObjectStore::Sequencer("fuse")); fs->store->apply_transaction(&*osr, std::move(t)); C_SaferCond waiter; if (!osr->flush_commit(&waiter)) waiter.wait(); return 0; } static int os_truncate(const char *path, off_t size) { 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::Transaction t; t.truncate(cid, oid, size); ceph::shared_ptr osr( new ObjectStore::Sequencer("fuse")); fs->store->apply_transaction(&*osr, std::move(t)); C_SaferCond waiter; if (!osr->flush_commit(&waiter)) waiter.wait(); 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; return 0; } static struct fuse_operations fs_oper = { getattr: os_getattr, readlink: 0, getdir: 0, 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, utime: 0, 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; if (store->cct->_conf->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 (store->cct->_conf->fuse_debug) ++c; fuse_args a = FUSE_ARGS_INIT(c, (char**)v); info->args = a; if (fuse_parse_cmdline(&info->args, &info->mountpoint, NULL, NULL) == -1) { derr << __func__ << " failed to parse args" << dendl; return -EINVAL; } 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; } 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; fuse_unmount(info->mountpoint, info->ch); fuse_thread.join(); fuse_destroy(info->f); dout(10) << __func__ << " exit" << dendl; return 0; }