diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index eaa080232880ab..09029b2aa4df7c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1244,6 +1244,7 @@ add_custom_target(cephfs_testing DEPENDS vstart rados cephfs + cls_cephfs ceph-fuse ceph-dencoder cephfs-journal-tool diff --git a/src/include/rados/librados.h b/src/include/rados/librados.h index e9b5c91c0f502d..e86ee5d3ebb560 100644 --- a/src/include/rados/librados.h +++ b/src/include/rados/librados.h @@ -994,6 +994,8 @@ CEPH_RADOS_API int rados_object_list(rados_ioctx_t io, const rados_object_list_cursor start, const rados_object_list_cursor finish, const size_t result_size, + const char *filter_buf, + const size_t filter_buf_len, rados_object_list_item *results, rados_object_list_cursor *next); diff --git a/src/include/rados/librados.hpp b/src/include/rados/librados.hpp index 1287c77de9c423..135730cd3ffca0 100644 --- a/src/include/rados/librados.hpp +++ b/src/include/rados/librados.hpp @@ -818,6 +818,7 @@ namespace librados bool object_list_is_end(const ObjectCursor &oc); int object_list(const ObjectCursor &start, const ObjectCursor &finish, const size_t result_count, + const bufferlist &filter, std::vector *result, ObjectCursor *next); void object_list_slice( diff --git a/src/librados/librados.cc b/src/librados/librados.cc index e66d17b638aca3..eb8d2053862e8d 100644 --- a/src/librados/librados.cc +++ b/src/librados/librados.cc @@ -3678,6 +3678,8 @@ extern "C" int rados_object_list(rados_ioctx_t io, const rados_object_list_cursor start, const rados_object_list_cursor finish, const size_t result_item_count, + const char *filter_buf, + const size_t filter_buf_len, rados_object_list_item *result_items, rados_object_list_cursor *next) { @@ -3691,6 +3693,11 @@ extern "C" int rados_object_list(rados_ioctx_t io, std::list result; hobject_t next_hash; + bufferlist filter_bl; + if (filter_buf != nullptr) { + filter_bl.append(filter_buf, filter_buf_len); + } + C_SaferCond cond; ctx->objecter->enumerate_objects( ctx->poolid, @@ -3698,6 +3705,7 @@ extern "C" int rados_object_list(rados_ioctx_t io, *((hobject_t*)start), *((hobject_t*)finish), result_item_count, + filter_bl, &result, &next_hash, &cond); @@ -5223,6 +5231,7 @@ bool librados::IoCtx::object_list_is_end(const ObjectCursor &oc) int librados::IoCtx::object_list(const ObjectCursor &start, const ObjectCursor &finish, const size_t result_item_count, + const bufferlist &filter, std::vector *result, ObjectCursor *next) { @@ -5239,6 +5248,7 @@ int librados::IoCtx::object_list(const ObjectCursor &start, *((hobject_t*)start.c_cursor), *((hobject_t*)finish.c_cursor), result_item_count, + filter, &obj_result, &next_hash, &cond); diff --git a/src/osdc/Objecter.cc b/src/osdc/Objecter.cc index cff49d48877313..8a941026b5e958 100644 --- a/src/osdc/Objecter.cc +++ b/src/osdc/Objecter.cc @@ -4861,6 +4861,7 @@ void Objecter::enumerate_objects( const hobject_t &start, const hobject_t &end, const uint32_t max, + const bufferlist &filter_bl, std::list *result, hobject_t *next, Context *on_finish) @@ -4909,11 +4910,8 @@ void Objecter::enumerate_objects( C_EnumerateReply *on_ack = new C_EnumerateReply( this, next, result, end, pool_id, on_finish); - // Construct pgls operation - bufferlist filter; // FIXME pass in? - ObjectOperation op; - op.pg_nls(max, filter, start, 0); + op.pg_nls(max, filter_bl, start, 0); // Issue. See you later in _enumerate_reply object_locator_t oloc(pool_id, ns); diff --git a/src/osdc/Objecter.h b/src/osdc/Objecter.h index a8bfbdb031a9c3..1f882f9391ca09 100644 --- a/src/osdc/Objecter.h +++ b/src/osdc/Objecter.h @@ -187,7 +187,7 @@ struct ObjectOperation { osd_op.op.pgls.start_epoch = start_epoch; ::encode(cookie, osd_op.indata); } - void add_pgls_filter(int op, uint64_t count, bufferlist& filter, + void add_pgls_filter(int op, uint64_t count, const bufferlist& filter, collection_list_handle_t cookie, epoch_t start_epoch) { OSDOp& osd_op = add_op(op); osd_op.op.pgls.count = count; @@ -219,7 +219,7 @@ struct ObjectOperation { flags |= CEPH_OSD_FLAG_PGOP; } - void pg_nls(uint64_t count, bufferlist& filter, + void pg_nls(uint64_t count, const bufferlist& filter, collection_list_handle_t cookie, epoch_t start_epoch) { if (filter.length() == 0) add_pgls(CEPH_OSD_OP_PGNLS, count, cookie, start_epoch); @@ -2579,6 +2579,7 @@ class Objecter : public md_config_obs_t, public Dispatcher { const hobject_t &start, const hobject_t &end, const uint32_t max, + const bufferlist &filter_bl, std::list *result, hobject_t *next, Context *on_finish); diff --git a/src/test/librados/list.cc b/src/test/librados/list.cc index 39b02b5b0c5983..17304dd6c26839 100644 --- a/src/test/librados/list.cc +++ b/src/test/librados/list.cc @@ -693,7 +693,7 @@ TEST_F(LibRadosList, EnumerateObjects) { memset(results, 0, sizeof(rados_object_list_item) * 12); int r = rados_object_list(ioctx, c, rados_object_list_end(ioctx), - 12, results, &c); + 12, NULL, 0, results, &c); ASSERT_GE(r, 0); for (int i = 0; i < r; ++i) { std::string oid(results[i].oid, results[i].oid_length); @@ -761,7 +761,7 @@ TEST_F(LibRadosList, EnumerateObjectsSplit) { memset(results, 0, sizeof(rados_object_list_item) * 12); int r = rados_object_list(ioctx, c, shard_end, - 12, results, &c); + 12, NULL, 0, results, &c); ASSERT_GE(r, 0); for (int i = 0; i < r; ++i) { std::string oid(results[i].oid, results[i].oid_length); @@ -806,7 +806,7 @@ TEST_F(LibRadosListPP, EnumerateObjectsPP) { while(!ioctx.object_list_is_end(c)) { std::vector result; - int r = ioctx.object_list(c, end, 12, &result, &c); + int r = ioctx.object_list(c, end, 12, {}, &result, &c); ASSERT_GE(r, 0); ASSERT_EQ(r, (int)result.size()); for (int i = 0; i < r; ++i) { @@ -861,7 +861,7 @@ TEST_F(LibRadosListPP, EnumerateObjectsSplitPP) { while(c < shard_end) { std::vector result; - int r = ioctx.object_list(c, shard_end, 12, &result, &c); + int r = ioctx.object_list(c, shard_end, 12, {}, &result, &c); ASSERT_GE(r, 0); for (const auto & i : result) { @@ -906,5 +906,53 @@ TEST_F(LibRadosListNP, ListObjectsError) { rados_shutdown(cluster); } +TEST_F(LibRadosListPP, EnumerateObjectsFilterPP) { + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + bufferlist obj_content; + obj_content.append(buf, sizeof(buf)); + + std::string target_str = "content"; + + // Write xattr bare, no ::encod'ing + bufferlist target_val; + target_val.append(target_str); + bufferlist nontarget_val; + nontarget_val.append("rhubarb"); + + ASSERT_EQ(0, ioctx.write("has_xattr", obj_content, obj_content.length(), 0)); + ASSERT_EQ(0, ioctx.write("has_wrong_xattr", obj_content, obj_content.length(), 0)); + ASSERT_EQ(0, ioctx.write("no_xattr", obj_content, obj_content.length(), 0)); + + ASSERT_EQ(0, ioctx.setxattr("has_xattr", "theattr", target_val)); + ASSERT_EQ(0, ioctx.setxattr("has_wrong_xattr", "theattr", nontarget_val)); + + bufferlist filter_bl; + std::string filter_name = "plain"; + ::encode(filter_name, filter_bl); + ::encode("_theattr", filter_bl); + ::encode(target_str, filter_bl); + + ObjectCursor c = ioctx.object_list_begin(); + ObjectCursor end = ioctx.object_list_end(); + bool foundit = false; + while(!ioctx.object_list_is_end(c)) + { + std::vector result; + int r = ioctx.object_list(c, end, 12, filter_bl, &result, &c); + ASSERT_GE(r, 0); + ASSERT_EQ(r, result.size()); + for (int i = 0; i < r; ++i) { + auto oid = result[i].oid; + // We should only see the object that matches the filter + ASSERT_EQ(oid, "has_xattr"); + // We should only see it once + ASSERT_FALSE(foundit); + foundit = true; + } + } + ASSERT_TRUE(foundit); +} + #pragma GCC diagnostic pop #pragma GCC diagnostic warning "-Wpragmas" diff --git a/src/tools/cephfs/DataScan.cc b/src/tools/cephfs/DataScan.cc index 066c0189129b54..ce0b6ec4bae5f4 100644 --- a/src/tools/cephfs/DataScan.cc +++ b/src/tools/cephfs/DataScan.cc @@ -67,7 +67,7 @@ bool DataScan::parse_kwarg( dout(4) << "Using local file output to '" << val << "'" << dendl; driver = new LocalFileDriver(val, data_io); return true; - } else if (arg == std::string("-n")) { + } else if (arg == std::string("--worker_n")) { std::string err; n = strict_strtoll(val.c_str(), 10, &err); if (!err.empty()) { @@ -76,7 +76,7 @@ bool DataScan::parse_kwarg( return false; } return true; - } else if (arg == std::string("-m")) { + } else if (arg == std::string("--worker_m")) { std::string err; m = strict_strtoll(val.c_str(), 10, &err); if (!err.empty()) { @@ -436,39 +436,21 @@ int parse_oid(const std::string &oid, uint64_t *inode_no, uint64_t *obj_id) return 0; } -// Pending sharded pgls & add in progress mechanism for that -#undef SHARDEDPGLS int DataScan::scan_extents() { -#ifdef SHARDED_PGLS - float progress = 0.0; - librados::NObjectIterator i = data_io.nobjects_begin(n, m); -#else - librados::NObjectIterator i = data_io.nobjects_begin(); -#endif - - librados::NObjectIterator i_end = data_io.nobjects_end(); - int r = 0; - - for (; i != i_end; ++i) { - const std::string oid = i->get_oid(); -#ifdef SHARDED_PGLS - if (i.get_progress() != progress) { - if (int(i.get_progress() * 100) / 5 != int(progress * 100) / 5) { - std::cerr << percentify(i.get_progress()) << "%" << std::endl; - } - progress = i.get_progress(); - } -#endif - + return forall_objects(data_io, false, [this]( + std::string const &oid, + uint64_t obj_name_ino, + uint64_t obj_name_offset) -> int + { // Read size uint64_t size; time_t mtime; - r = data_io.stat(oid, &size, &mtime); + int r = data_io.stat(oid, &size, &mtime); if (r != 0) { dout(4) << "Cannot stat '" << oid << "': skipping" << dendl; - continue; + return r; } // I need to keep track of @@ -481,61 +463,132 @@ int DataScan::scan_extents() // and the actual size (offset of last object + size of highest ID seen) // // This logic doesn't take account of striping. - uint64_t inode_no = 0; - uint64_t obj_id = 0; - r = parse_oid(oid, &inode_no, &obj_id); - if (r != 0) { - dout(4) << "Bad object name '" << oid << "' skipping" << dendl; - continue; - } - - int r = ClsCephFSClient::accumulate_inode_metadata( + r = ClsCephFSClient::accumulate_inode_metadata( data_io, - inode_no, - obj_id, + obj_name_ino, + obj_name_offset, size, mtime); if (r < 0) { derr << "Failed to accumulate metadata data from '" << oid << "': " << cpp_strerror(r) << dendl; - continue; + return r; } - } - return 0; + return r; + }); } -int DataScan::scan_inodes() +int DataScan::probe_filter(librados::IoCtx &ioctx) { -#ifdef SHARDED_PGLS - float progress = 0.0; - librados::NObjectIterator i = data_io.nobjects_begin(n, m); -#else - librados::NObjectIterator i; - bool legacy_filtering = false; + bufferlist filter_bl; + ClsCephFSClient::build_tag_filter("test", &filter_bl); + librados::ObjectCursor range_i; + librados::ObjectCursor range_end; + + std::vector tmp_result; + librados::ObjectCursor tmp_next; + int r = ioctx.object_list(ioctx.object_list_begin(), ioctx.object_list_end(), + 1, filter_bl, &tmp_result, &tmp_next); + + return r >= 0; +} + +int DataScan::forall_objects( + librados::IoCtx &ioctx, + bool untagged_only, + std::function handler + ) +{ + librados::ObjectCursor range_i; + librados::ObjectCursor range_end; + ioctx.object_list_slice( + ioctx.object_list_begin(), + ioctx.object_list_end(), + n, + m, + &range_i, + &range_end); + bufferlist filter_bl; - ClsCephFSClient::build_tag_filter(filter_tag, &filter_bl); - // try/catch to deal with older OSDs that don't support - // the cephfs pgls filtering mode - try { - i = data_io.nobjects_begin(filter_bl); - dout(4) << "OSDs accepted cephfs object filtering" << dendl; - } catch (const std::runtime_error &e) { - // A little unfriendly, librados raises std::runtime_error - // on pretty much any unhandled I/O return value, such as - // the OSD saying -EINVAL because of our use of a filter - // mode that it doesn't know about. - std::cerr << "OSDs do not support cephfs object filtering: using " - "(slower) fallback mode" << std::endl; - legacy_filtering = true; - i = data_io.nobjects_begin(); + bool legacy_filtering = false; + if (untagged_only) { + // probe to deal with older OSDs that don't support + // the cephfs pgls filtering mode + legacy_filtering = !probe_filter(ioctx); + if (!legacy_filtering) { + ClsCephFSClient::build_tag_filter(filter_tag, &filter_bl); + } + } + + int r = 0; + while(range_i < range_end) { + std::vector result; + int r = ioctx.object_list(range_i, range_end, 1, + filter_bl, &result, &range_i); + if (r < 0) { + derr << "Unexpected error listing objects: " << cpp_strerror(r) << dendl; + return r; + } + + for (const auto &i : result) { + const std::string &oid = i.oid; + uint64_t obj_name_ino = 0; + uint64_t obj_name_offset = 0; + r = parse_oid(oid, &obj_name_ino, &obj_name_offset); + if (r != 0) { + dout(4) << "Bad object name '" << oid << "', skipping" << dendl; + continue; + } + + if (untagged_only && legacy_filtering) { + dout(20) << "Applying filter to " << oid << dendl; + + // We are only interested in 0th objects during this phase: we touched + // the other objects during scan_extents + if (obj_name_offset != 0) { + dout(20) << "Non-zeroth object" << dendl; + continue; + } + + bufferlist scrub_tag_bl; + int r = ioctx.getxattr(oid, "scrub_tag", scrub_tag_bl); + if (r >= 0) { + std::string read_tag; + bufferlist::iterator q = scrub_tag_bl.begin(); + try { + ::decode(read_tag, q); + if (read_tag == filter_tag) { + dout(20) << "skipping " << oid << " because it has the filter_tag" + << dendl; + continue; + } + } catch (const buffer::error &err) { + } + dout(20) << "read non-matching tag '" << read_tag << "'" << dendl; + } else { + dout(20) << "no tag read (" << r << ")" << dendl; + } + + } else if (untagged_only) { + assert(obj_name_offset == 0); + dout(20) << "OSD matched oid " << oid << dendl; + } + + int this_oid_r = handler(oid, obj_name_ino, obj_name_offset); + if (r == 0 && this_oid_r < 0) { + r = this_oid_r; + } + } } -#endif - librados::NObjectIterator i_end = data_io.nobjects_end(); + return r; +} +int DataScan::scan_inodes() +{ bool roots_present; int r = driver->check_roots(&roots_present); if (r != 0) { @@ -550,77 +603,31 @@ int DataScan::scan_inodes() return -EIO; } - for (; i != i_end; ++i) { - const std::string oid = i->get_oid(); -#ifdef SHARDED_PGLS - if (i.get_progress() != progress) { - if (int(i.get_progress() * 100) / 5 != int(progress * 100) / 5) { - std::cerr << percentify(i.get_progress()) << "%" << std::endl; - } - progress = i.get_progress(); - } -#endif - - uint64_t obj_name_ino = 0; - uint64_t obj_name_offset = 0; - r = parse_oid(oid, &obj_name_ino, &obj_name_offset); - if (r != 0) { - dout(4) << "Bad object name '" << oid << "', skipping" << dendl; - continue; - } - - if (legacy_filtering) { - dout(20) << "Applying filter to " << oid << dendl; - - // We are only interested in 0th objects during this phase: we touched - // the other objects during scan_extents - if (obj_name_offset != 0) { - dout(20) << "Non-zeroth object" << dendl; - continue; - } - - bufferlist scrub_tag_bl; - int r = data_io.getxattr(oid, "scrub_tag", scrub_tag_bl); - if (r >= 0) { - std::string read_tag; - bufferlist::iterator q = scrub_tag_bl.begin(); - try { - ::decode(read_tag, q); - if (read_tag == filter_tag) { - dout(20) << "skipping " << oid << " because it has the filter_tag" - << dendl; - continue; - } - } catch (const buffer::error &err) { - } - dout(20) << "read non-matching tag '" << read_tag << "'" << dendl; - } else { - dout(20) << "no tag read (" << r << ")" << dendl; - } - - } else { - assert(obj_name_offset == 0); - dout(20) << "OSD matched oid " << oid << dendl; - } + return forall_objects(data_io, true, [this]( + std::string const &oid, + uint64_t obj_name_ino, + uint64_t obj_name_offset) -> int + { + int r = 0; AccumulateResult accum_res; inode_backtrace_t backtrace; ceph_file_layout loaded_layout = g_default_file_layout; - int r = ClsCephFSClient::fetch_inode_accumulate_result( + r = ClsCephFSClient::fetch_inode_accumulate_result( data_io, oid, &backtrace, &loaded_layout, &accum_res); if (r == -EINVAL) { dout(4) << "Accumulated metadata missing from '" << oid << ", did you run scan_extents?" << dendl; - continue; - } else if (r < 0) { + return r; + } else if (r < 0) { dout(4) << "Unexpected error loading accumulated metadata from '" << oid << "': " << cpp_strerror(r) << dendl; // FIXME: this creates situation where if a client has a corrupt // backtrace/layout, we will fail to inject it. We should (optionally) // proceed if the backtrace/layout is corrupt but we have valid // accumulated metadata. - continue; + return r; } const time_t file_mtime = accum_res.max_mtime; @@ -708,15 +715,15 @@ int DataScan::scan_inodes() time_t omtime(0); r = data_io.stat(std::string(buf), &osize, &omtime); if (r == 0) { - if (osize > 0) { - // Upper bound within this object - uint64_t upper_size = (osize - 1) / guessed_layout.fl_stripe_unit - * (guessed_layout.fl_stripe_unit * guessed_layout.fl_stripe_count) - + (i % guessed_layout.fl_stripe_count) - * guessed_layout.fl_stripe_unit + (osize - 1) - % guessed_layout.fl_stripe_unit + 1; - incomplete_size = MAX(incomplete_size, upper_size); - } + if (osize > 0) { + // Upper bound within this object + uint64_t upper_size = (osize - 1) / guessed_layout.fl_stripe_unit + * (guessed_layout.fl_stripe_unit * guessed_layout.fl_stripe_count) + + (i % guessed_layout.fl_stripe_count) + * guessed_layout.fl_stripe_unit + (osize - 1) + % guessed_layout.fl_stripe_unit + 1; + incomplete_size = MAX(incomplete_size, upper_size); + } } else if (r == -ENOENT) { // Absent object, treat as size 0 and ignore. } else { @@ -727,7 +734,7 @@ int DataScan::scan_inodes() if (r != 0 && r != -ENOENT) { derr << "Unexpected error checking size of ino 0x" << std::hex << obj_name_ino << std::dec << ": " << cpp_strerror(r) << dendl; - continue; + return r; } file_size = complete_objs * guessed_layout.fl_object_size + incomplete_size; @@ -796,9 +803,9 @@ int DataScan::scan_inodes() } } } - } - return 0; + return r; + }); } bool DataScan::valid_ino(inodeno_t ino) const @@ -871,8 +878,9 @@ int DataScan::scan_frags() i = metadata_io.nobjects_begin(); } - librados::NObjectIterator i_end = metadata_io.nobjects_end(); +int DataScan::scan_frags() +{ bool roots_present; int r = driver->check_roots(&roots_present); if (r != 0) { @@ -887,14 +895,16 @@ int DataScan::scan_frags() return -EIO; } - for (; i != i_end; ++i) { - const std::string oid = i->get_oid(); - uint64_t obj_name_ino = 0; - uint64_t obj_name_offset = 0; + return forall_objects(metadata_io, true, [this]( + std::string const &oid, + uint64_t obj_name_ino, + uint64_t obj_name_offset) -> int + { + int r = 0; r = parse_oid(oid, &obj_name_ino, &obj_name_offset); if (r != 0) { dout(4) << "Bad object name '" << oid << "', skipping" << dendl; - continue; + return r; } if (obj_name_ino < (1ULL << 40)) { @@ -902,39 +912,7 @@ int DataScan::scan_frags() // orphaned then we should be resetting them some other // way dout(10) << "Skipping system ino " << obj_name_ino << dendl; - continue; - } - - if (legacy_filtering) { - dout(20) << "Applying filter to " << oid << dendl; - - // We are only interested in 0th objects during this phase: we touched - // the other objects during scan_extents - if (obj_name_offset != 0) { - dout(20) << "Non-zeroth object" << dendl; - continue; - } - - bufferlist scrub_tag_bl; - int r = metadata_io.getxattr(oid, "scrub_tag", scrub_tag_bl); - if (r >= 0) { - std::string read_tag; - bufferlist::iterator q = scrub_tag_bl.begin(); - ::decode(read_tag, q); - if (read_tag == filter_tag) { - dout(20) << "skipping " << oid << " because it has the filter_tag" - << dendl; - continue; - } else { - dout(20) << "read non-matching tag '" << read_tag << "'" << dendl; - } - } else { - dout(20) << "no tag read (" << r << ")" << dendl; - } - - } else { - assert(obj_name_offset == 0); - dout(20) << "OSD matched oid " << oid << dendl; + return 0; } AccumulateResult accum_res; @@ -954,10 +932,10 @@ int DataScan::scan_frags() librados::ObjectReadOperation op; op.getxattr("parent", &parent_bl, &parent_r); op.getxattr("layout", &layout_bl, &layout_r); - int r = metadata_io.operate(oid, &op, &op_bl); + r = metadata_io.operate(oid, &op, &op_bl); if (r != 0 && r != -ENODATA) { derr << "Unexpected error reading backtrace: " << cpp_strerror(parent_r) << dendl; - continue; + return r; } if (parent_r != -ENODATA) { @@ -967,7 +945,7 @@ int DataScan::scan_frags() } catch (buffer::error &e) { dout(4) << "Corrupt backtrace on '" << oid << "': " << e << dendl; if (!force_corrupt) { - continue; + return -EINVAL; } else { // Treat backtrace as absent: we'll inject into lost+found backtrace = inode_backtrace_t(); @@ -982,7 +960,7 @@ int DataScan::scan_frags() } catch (buffer::error &e) { dout(4) << "Corrupt layout on '" << oid << "': " << e << dendl; if (!force_corrupt) { - continue; + return -EINVAL; } } } @@ -1007,7 +985,7 @@ int DataScan::scan_frags() fnode.fragstat.nfiles = 1; fnode.fragstat.nsubdirs = 0; } else { - continue; + return r; } } @@ -1056,9 +1034,9 @@ int DataScan::scan_frags() } } } - } - return 0; + return r; + }); } int MetadataTool::read_fnode( @@ -1511,7 +1489,7 @@ int MetadataDriver::find_or_create_dirfrag( object_t frag_oid = InodeStore::get_object_name(ino, fragment, ""); op.omap_set_header(fnode_bl); r = metadata_io.operate(frag_oid.name, &op); - if (r == -EOVERFLOW) { + if (r == -EOVERFLOW || r == -EEXIST) { // Someone else wrote it (see case A above) dout(10) << "Dirfrag creation race: 0x" << std::hex << ino << " " << fragment << std::dec << dendl; diff --git a/src/tools/cephfs/DataScan.h b/src/tools/cephfs/DataScan.h index 1b9c580ce26897..00c1658237804e 100644 --- a/src/tools/cephfs/DataScan.h +++ b/src/tools/cephfs/DataScan.h @@ -284,7 +284,17 @@ class DataScan : public MDSUtility, public MetadataTool const std::vector &arg, std::vector::const_iterator &i); + int probe_filter(librados::IoCtx &ioctx); + /** + * Apply a function to all objects in an ioctx's pool, optionally + * restricted to only those objects with a 00000000 offset and + * no tag matching DataScan::scrub_tag. + */ + int forall_objects( + librados::IoCtx &ioctx, + bool untagged_only, + std::function handler); public: void usage();