}
}
+bool CDentry::scrub(snapid_t next_seq)
+{
+ dout(20) << "scrubbing " << *this << " next_seq = " << next_seq << dendl;
+
+ /* attempt to locate damage in first of CDentry, see:
+ * https://tracker.ceph.com/issues/56140
+ */
+ /* skip projected dentries as first/last may have placeholder values */
+ if (!is_projected()) {
+ CDir* dir = get_dir();
+
+ if (first > next_seq) {
+ derr << __func__ << ": first > next_seq (" << next_seq << ") " << *this << dendl;
+ dir->go_bad_dentry(last, get_name());
+ return true;
+ } else if (first > last) {
+ derr << __func__ << ": first > last " << *this << dendl;
+ dir->go_bad_dentry(last, get_name());
+ return true;
+ }
+
+ auto&& realm = dir->get_inode()->find_snaprealm();
+ if (realm) {
+ auto&& snaps = realm->get_snaps();
+ auto it = snaps.lower_bound(first);
+ bool stale = last != CEPH_NOSNAP && (it == snaps.end() || *it > last);
+ if (stale) {
+ dout(20) << "is stale" << dendl;
+ /* TODO: maybe trim? */
+ }
+ }
+ }
+ return false;
+}
+
MEMPOOL_DEFINE_OBJECT_FACTORY(CDentry, co_dentry, mds_co);
void mark_auth();
void clear_auth();
+ bool scrub(snapid_t next_seq);
+
// -- exporting
// note: this assumes the dentry already exists.
// i.e., the name is already extracted... so we just need the other state.
bool CDir::try_trim_snap_dentry(CDentry *dn, const set<snapid_t>& snaps)
{
- ceph_assert(dn->last != CEPH_NOSNAP);
+ if (dn->last == CEPH_NOSNAP) {
+ return false;
+ }
set<snapid_t>::const_iterator p = snaps.lower_bound(dn->first);
CDentry::linkage_t *dnl= dn->get_linkage();
CInode *in = 0;
string key;
dn->key().encode(key);
- if (dn->last != CEPH_NOSNAP &&
- snaps && try_trim_snap_dentry(dn, *snaps)) {
+ if (snaps && try_trim_snap_dentry(dn, *snaps)) {
dout(10) << " rm " << key << dendl;
to_remove.emplace_back(std::move(key));
return;
*/
bool scrub_local();
+ /**
+ * Go bad due to a damaged dentry (register with damagetable and go BADFRAG)
+ */
+ void go_bad_dentry(snapid_t last, std::string_view dname);
+
const scrub_info_t *scrub_info() const {
if (!scrub_infop)
scrub_info_create();
double rand_threshold,
bool *force_dirty);
- /**
- * Go bad due to a damaged dentry (register with damagetable and go BADFRAG)
- */
- void go_bad_dentry(snapid_t last, std::string_view dname);
-
/**
* Go bad due to a damaged header (register with damagetable and go BADFRAG)
*/
ScrubHeaderRef header = dir->get_scrub_header();
version_t last_scrub = dir->scrub_info()->last_recursive.version;
if (header->get_recursive()) {
- for (auto it = dir->begin(); it != dir->end(); ++it) {
- if (it->first.snapid != CEPH_NOSNAP)
+ auto next_seq = mdcache->get_global_snaprealm()->get_newest_seq()+1;
+ for (auto it = dir->begin(); it != dir->end(); /* nop */) {
+ auto [dnk, dn] = *it;
+ ++it; /* trim (in the future) may remove dentry */
+
+ if (dn->scrub(next_seq)) {
+ std::string path;
+ dir->get_inode()->make_path_string(path, true);
+ clog->warn() << "Scrub error on dentry " << *dn
+ << " see " << g_conf()->name
+ << " log and `damage ls` output for details";
+ }
+
+ if (dnk.snapid != CEPH_NOSNAP) {
continue;
- CDentry *dn = it->second;
+ }
+
CDentry::linkage_t *dnl = dn->get_linkage();
if (dn->get_version() <= last_scrub &&
dnl->get_remote_d_type() != DT_DIR &&
!header->get_force()) {
- dout(15) << __func__ << " skip dentry " << it->first
+ dout(15) << __func__ << " skip dentry " << dnk
<< ", no change since last scrub" << dendl;
continue;
}