From: Yan, Zheng Date: Sun, 17 Sep 2017 08:51:04 +0000 (+0800) Subject: ceph: do link/rename semantic checks after srcdn is readable X-Git-Tag: v13.0.1~849^2 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=46962b253563a867707e7c5d7887abf2060cc4d7;p=ceph-ci.git ceph: do link/rename semantic checks after srcdn is readable For hard link, source inode must not be directory. For rename, types of source/destination inodes must match. If srcdn is replica and we do these checks while it's not readble, it's possible that wrong source inode is used in these checks. Fixes: http://tracker.ceph.com/issues/21383 Signed-off-by: "Yan, Zheng" --- diff --git a/src/mds/Server.cc b/src/mds/Server.cc index a5ea0ca09bb..f1879543823 100644 --- a/src/mds/Server.cc +++ b/src/mds/Server.cc @@ -3167,7 +3167,8 @@ void Server::handle_client_open(MDRequestRef& mdr) return; } - bool need_auth = !file_mode_is_readonly(cmode) || (flags & CEPH_O_TRUNC); + bool need_auth = !file_mode_is_readonly(cmode) || + (flags & (CEPH_O_TRUNC | CEPH_O_DIRECTORY)); if ((cmode & CEPH_FILE_MODE_WR) && mdcache->is_readonly()) { dout(7) << "read-only FS" << dendl; @@ -5213,9 +5214,15 @@ void Server::handle_client_link(MDRequestRef& mdr) dout(7) << "handle_client_link link " << dn->get_name() << " in " << *dir << dendl; dout(7) << "target is " << *targeti << dendl; if (targeti->is_dir()) { - dout(7) << "target is a dir, failing..." << dendl; - respond_to_request(mdr, -EINVAL); - return; + // if srcdn is replica, need to make sure its linkage is correct + vector& trace = mdr->dn[1]; + if (trace.empty() || + trace.back()->is_auth() || + trace.back()->lock.can_read(mdr->get_client())) { + dout(7) << "target is a dir, failing..." << dendl; + respond_to_request(mdr, -EINVAL); + return; + } } xlocks.insert(&targeti->linklock); @@ -6501,25 +6508,30 @@ void Server::handle_client_rename(MDRequestRef& mdr) oldin = mdcache->get_dentry_inode(destdn, mdr, true); if (!oldin) return; dout(10) << " oldin " << *oldin << dendl; - - // mv /some/thing /to/some/existing_other_thing - if (oldin->is_dir() && !srci->is_dir()) { - respond_to_request(mdr, -EISDIR); - return; - } - if (!oldin->is_dir() && srci->is_dir()) { - respond_to_request(mdr, -ENOTDIR); - return; - } // non-empty dir? do trivial fast unlocked check, do another check later with read locks if (oldin->is_dir() && _dir_is_nonempty_unlocked(mdr, oldin)) { respond_to_request(mdr, -ENOTEMPTY); return; } - if (srci == oldin && !srcdn->get_dir()->inode->is_stray()) { - respond_to_request(mdr, 0); // no-op. POSIX makes no sense. - return; + + // if srcdn is replica, need to make sure its linkage is correct + if (srcdn->is_auth() || + srcdn->lock.can_read(mdr->get_client()) || + (srcdn->lock.is_xlocked() && srcdn->lock.get_xlock_by() == mdr)) { + // mv /some/thing /to/some/existing_other_thing + if (oldin->is_dir() && !srci->is_dir()) { + respond_to_request(mdr, -EISDIR); + return; + } + if (!oldin->is_dir() && srci->is_dir()) { + respond_to_request(mdr, -ENOTDIR); + return; + } + if (srci == oldin && !srcdn->get_dir()->inode->is_stray()) { + respond_to_request(mdr, 0); // no-op. POSIX makes no sense. + return; + } } }