]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
ceph: do link/rename semantic checks after srcdn is readable
authorYan, Zheng <zyan@redhat.com>
Sun, 17 Sep 2017 08:51:04 +0000 (16:51 +0800)
committerYan, Zheng <zyan@redhat.com>
Sun, 17 Sep 2017 08:59:08 +0000 (16:59 +0800)
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" <zyan@redhat.com>
src/mds/Server.cc

index a5ea0ca09bb73c1eee8703c8e63d8751d695400c..f187954382344166c7281d5515cab7b0fe7c112b 100644 (file)
@@ -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<CDentry*>& 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;
+      }
     }
   }