EDK: Add Core::DuplicateHandleWithReducedRights().

This will support both MojoDuplicateHandleWithReducedRights() and
MojoDuplicateHandle().

(No plumbing done yet -- to be done later.)

R=azani@chromium.org

Review URL: https://codereview.chromium.org/2001673003 .
diff --git a/mojo/edk/system/core_unittest.cc b/mojo/edk/system/core_unittest.cc
index c91ef7b..005dede 100644
--- a/mojo/edk/system/core_unittest.cc
+++ b/mojo/edk/system/core_unittest.cc
@@ -59,6 +59,31 @@
   EXPECT_EQ(MOJO_RESULT_OK, core()->GetRights(h, MakeUserPointer(&rights)));
   EXPECT_EQ(kDefaultMockHandleRights, rights);
 
+  MojoHandle h_dup = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            core()->DuplicateHandleWithReducedRights(
+                h, MOJO_HANDLE_RIGHT_DUPLICATE, MakeUserPointer(&h_dup)));
+  EXPECT_EQ(1u, info.GetDuplicateDispatcherCallCount());
+  EXPECT_NE(h_dup, MOJO_HANDLE_INVALID);
+  EXPECT_NE(h_dup, h);
+  rights = MOJO_HANDLE_RIGHT_NONE;
+  EXPECT_EQ(MOJO_RESULT_OK, core()->GetRights(h_dup, MakeUserPointer(&rights)));
+  EXPECT_EQ(kDefaultMockHandleRights & ~MOJO_HANDLE_RIGHT_DUPLICATE, rights);
+  MojoHandle h_denied = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_PERMISSION_DENIED,
+            core()->DuplicateHandleWithReducedRights(
+                h_dup, MOJO_HANDLE_RIGHT_NONE, MakeUserPointer(&h_denied)));
+  EXPECT_EQ(1u, info.GetDuplicateDispatcherCallCount());
+  EXPECT_EQ(MOJO_HANDLE_INVALID, h_denied);
+
+  EXPECT_EQ(0u, info.GetDtorCallCount());
+  EXPECT_EQ(0u, info.GetCloseCallCount());
+  EXPECT_EQ(0u, info.GetCancelAllAwakablesCallCount());
+  EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_dup));
+  EXPECT_EQ(1u, info.GetDtorCallCount());
+  EXPECT_EQ(1u, info.GetCloseCallCount());
+  EXPECT_EQ(1u, info.GetCancelAllAwakablesCallCount());
+
   EXPECT_EQ(0u, info.GetWriteMessageCallCount());
   EXPECT_EQ(MOJO_RESULT_OK,
             core()->WriteMessage(h, NullUserPointer(), 0, NullUserPointer(), 0,
@@ -191,13 +216,14 @@
   EXPECT_EQ(0u, hss.satisfied_signals);
   EXPECT_EQ(0u, hss.satisfiable_signals);
 
-  EXPECT_EQ(0u, info.GetDtorCallCount());
-  EXPECT_EQ(0u, info.GetCloseCallCount());
-  EXPECT_EQ(0u, info.GetCancelAllAwakablesCallCount());
-  EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h));
-  EXPECT_EQ(1u, info.GetCancelAllAwakablesCallCount());
-  EXPECT_EQ(1u, info.GetCloseCallCount());
+  // |h| shares |info| with |h_dup|, which was closed above.
   EXPECT_EQ(1u, info.GetDtorCallCount());
+  EXPECT_EQ(1u, info.GetCloseCallCount());
+  EXPECT_EQ(1u, info.GetCancelAllAwakablesCallCount());
+  EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h));
+  EXPECT_EQ(2u, info.GetDtorCallCount());
+  EXPECT_EQ(2u, info.GetCloseCallCount());
+  EXPECT_EQ(2u, info.GetCancelAllAwakablesCallCount());
 
   // No awakables should ever have ever been added.
   EXPECT_EQ(0u, info.GetRemoveAwakableCallCount());
@@ -230,6 +256,20 @@
     EXPECT_EQ(0u, rights);
   }
 
+  // |DuplicateHandleWithReducedRights()|:
+  {
+    MojoHandle h = MOJO_HANDLE_INVALID;
+    EXPECT_EQ(
+        MOJO_RESULT_INVALID_ARGUMENT,
+        core()->DuplicateHandleWithReducedRights(
+            MOJO_HANDLE_INVALID, MOJO_HANDLE_RIGHT_NONE, MakeUserPointer(&h)));
+    EXPECT_EQ(MOJO_HANDLE_INVALID, h);
+    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->DuplicateHandleWithReducedRights(
+                  10, MOJO_HANDLE_RIGHT_NONE, MakeUserPointer(&h)));
+    EXPECT_EQ(MOJO_HANDLE_INVALID, h);
+  }
+
   // |Wait()|:
   {
     EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
@@ -592,6 +632,17 @@
     EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h));
   }
 
+  // |DuplicateHandleWithReducedRights()|:
+  {
+    MockHandleInfo info;
+    MojoHandle h = CreateMockHandle(&info);
+    EXPECT_DEATH_IF_SUPPORTED(core()->DuplicateHandleWithReducedRights(
+                                  h, MOJO_HANDLE_RIGHT_NONE, NullUserPointer()),
+                              kMemoryCheckFailedRegex);
+
+    EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h));
+  }
+
   // |WaitMany()|:
   {
     MojoHandle handle = MOJO_HANDLE_INVALID;
@@ -688,6 +739,16 @@
   EXPECT_EQ(MOJO_RESULT_OK, core()->GetRights(h[1], MakeUserPointer(&rights)));
   EXPECT_EQ(kDefaultMessagePipeHandleRights, rights);
 
+  // Neither should be duplicatable.
+  MojoHandle h_denied = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_PERMISSION_DENIED,
+            core()->DuplicateHandleWithReducedRights(
+                h[0], MOJO_HANDLE_RIGHT_NONE, MakeUserPointer(&h_denied)));
+  EXPECT_EQ(MOJO_RESULT_PERMISSION_DENIED,
+            core()->DuplicateHandleWithReducedRights(
+                h[1], MOJO_HANDLE_RIGHT_NONE, MakeUserPointer(&h_denied)));
+  EXPECT_EQ(MOJO_HANDLE_INVALID, h_denied);
+
   // Neither should be readable.
   MojoHandleSignals signals[2] = {MOJO_HANDLE_SIGNAL_READABLE,
                                   MOJO_HANDLE_SIGNAL_READABLE};
@@ -1047,6 +1108,16 @@
   EXPECT_EQ(MOJO_RESULT_OK, core()->GetRights(ch, MakeUserPointer(&rights)));
   EXPECT_EQ(kDefaultDataPipeConsumerHandleRights, rights);
 
+  // Neither should be duplicatable.
+  MojoHandle h_denied = MOJO_HANDLE_INVALID;
+  EXPECT_EQ(MOJO_RESULT_PERMISSION_DENIED,
+            core()->DuplicateHandleWithReducedRights(
+                ph, MOJO_HANDLE_RIGHT_NONE, MakeUserPointer(&h_denied)));
+  EXPECT_EQ(MOJO_RESULT_PERMISSION_DENIED,
+            core()->DuplicateHandleWithReducedRights(
+                ch, MOJO_HANDLE_RIGHT_NONE, MakeUserPointer(&h_denied)));
+  EXPECT_EQ(MOJO_HANDLE_INVALID, h_denied);
+
   // Producer should be never-readable, but already writable.
   hss = kEmptyMojoHandleSignalsState;
   EXPECT_EQ(