From: 胡玮文 Date: Thu, 16 Dec 2021 14:31:17 +0000 (+0800) Subject: mds: fix crash when exporting unlinked dir X-Git-Tag: v18.0.0~749^2~1 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=fcdee486697ca98a44960e6150bfd36dc34e5fe5;p=ceph.git mds: fix crash when exporting unlinked dir When fetch() an unlinked dir, we set fnode->version = 1, but leave projected_version = 0. This will trigger the assert `dir->get_projected_version() == dir->get_version()` in Migrator::encode_export_dir(). projected_version should equal to `fnode->version` unless this dir is projected. Fix this by introducing a new helper CDir::set_fresh_fnode(), which will ensure versions are correctly set. Fixes: https://tracker.ceph.com/issues/53597 Signed-off-by: 胡玮文 --- diff --git a/src/mds/CDir.cc b/src/mds/CDir.cc index aee737f82b69..f53e44e6f189 100644 --- a/src/mds/CDir.cc +++ b/src/mds/CDir.cc @@ -1475,6 +1475,20 @@ void CDir::mark_new(LogSegment *ls) mdcache->mds->queue_waiters(waiters); } +void CDir::set_fresh_fnode(fnode_const_ptr&& ptr) { + ceph_assert(inode->is_auth()); + ceph_assert(!is_projected()); + ceph_assert(!state_test(STATE_COMMITTING)); + reset_fnode(std::move(ptr)); + projected_version = committing_version = committed_version = get_version(); + + if (state_test(STATE_REJOINUNDEF)) { + ceph_assert(mdcache->mds->is_rejoin()); + state_clear(STATE_REJOINUNDEF); + mdcache->opened_undef_dirfrag(this); + } +} + void CDir::mark_clean() { dout(10) << __func__ << " " << *this << " version " << get_version() << dendl; @@ -1547,16 +1561,9 @@ void CDir::fetch(std::string_view dname, snapid_t last, pdir && pdir->inode->is_stray() && !inode->snaprealm) { dout(7) << "fetch dirfrag for unlinked directory, mark complete" << dendl; if (get_version() == 0) { - ceph_assert(inode->is_auth()); auto _fnode = allocate_fnode(); _fnode->version = 1; - reset_fnode(std::move(_fnode)); - - if (state_test(STATE_REJOINUNDEF)) { - ceph_assert(mdcache->mds->is_rejoin()); - state_clear(STATE_REJOINUNDEF); - mdcache->opened_undef_dirfrag(this); - } + set_fresh_fnode(std::move(_fnode)); } mark_complete(); @@ -2043,17 +2050,7 @@ void CDir::_omap_fetched(bufferlist& hdrbl, map& omap, // take the loaded fnode? // only if we are a fresh CDir* with no prior state. if (get_version() == 0) { - ceph_assert(!is_projected()); - ceph_assert(!state_test(STATE_COMMITTING)); - auto _fnode = allocate_fnode(got_fnode); - reset_fnode(std::move(_fnode)); - projected_version = committing_version = committed_version = get_version(); - - if (state_test(STATE_REJOINUNDEF)) { - ceph_assert(mdcache->mds->is_rejoin()); - state_clear(STATE_REJOINUNDEF); - mdcache->opened_undef_dirfrag(this); - } + set_fresh_fnode(allocate_fnode(got_fnode)); } list undef_inodes; diff --git a/src/mds/CDir.h b/src/mds/CDir.h index 1d87f314dcb7..852f86ba826e 100644 --- a/src/mds/CDir.h +++ b/src/mds/CDir.h @@ -247,6 +247,7 @@ public: void reset_fnode(fnode_const_ptr&& ptr) { fnode = std::move(ptr); } + void set_fresh_fnode(fnode_const_ptr&& ptr); const fnode_const_ptr& get_fnode() const { return fnode;