Bug 1382883 - Pass paths and open file handles to CDM host binaries on CDM startup. r=gerald
authorChris Pearce <cpearce@mozilla.com>
Thu, 27 Jul 2017 12:50:46 +1200
changeset 420096 eb898389180abf04d7bb0c603a9e097671c3759e
parent 420095 0739667c02050d60fec1f965307a8a85f8c40b8b
child 420097 9bc216bce3c6cb524c3937a66fa923e1a8b0894e
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgerald
bugs1382883
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1382883 - Pass paths and open file handles to CDM host binaries on CDM startup. r=gerald MozReview-Commit-ID: 9IhRqlFrNJf
dom/media/gmp/ChromiumCDMAdapter.cpp
dom/media/gmp/ChromiumCDMAdapter.h
dom/media/gmp/GMPChild.cpp
dom/media/gmp/GMPChild.h
media/gmp-clearkey/0.1/gmp-clearkey.cpp
--- a/dom/media/gmp/ChromiumCDMAdapter.cpp
+++ b/dom/media/gmp/ChromiumCDMAdapter.cpp
@@ -1,28 +1,42 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ChromiumCDMAdapter.h"
 #include "content_decryption_module.h"
+#include "content_decryption_module_ext.h"
 #include "VideoUtils.h"
 #include "gmp-api/gmp-entrypoints.h"
 #include "gmp-api/gmp-decryption.h"
 #include "gmp-api/gmp-video-codec.h"
 #include "gmp-api/gmp-platform.h"
 #include "WidevineUtils.h"
 #include "GMPLog.h"
+#include "mozilla/Move.h"
+
+#if defined(XP_MACOSX) || defined(XP_LINUX)
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#endif
 
 // Declared in WidevineAdapter.cpp.
 extern const GMPPlatformAPI* sPlatform;
 
 namespace mozilla {
 
+ChromiumCDMAdapter::ChromiumCDMAdapter(nsTArray<nsCString>&& aHostFilePaths)
+{
+  PopulateHostFiles(Move(aHostFilePaths));
+}
+
 void
 ChromiumCDMAdapter::SetAdaptee(PRLibrary* aLib)
 {
   mLib = aLib;
 }
 
 void*
 ChromiumCdmHost(int aHostInterfaceVersion, void* aUserData)
@@ -32,25 +46,46 @@ ChromiumCdmHost(int aHostInterfaceVersio
     return nullptr;
   }
   return static_cast<cdm::Host_8*>(aUserData);
 }
 
 #define STRINGIFY(s) _STRINGIFY(s)
 #define _STRINGIFY(s) #s
 
+static cdm::HostFile
+TakeToCDMHostFile(HostFileData& aHostFileData)
+{
+  return cdm::HostFile(aHostFileData.mBinary.Path().get(),
+                       aHostFileData.mBinary.TakePlatformFile(),
+                       aHostFileData.mSig.TakePlatformFile());
+}
+
 GMPErr
 ChromiumCDMAdapter::GMPInit(const GMPPlatformAPI* aPlatformAPI)
 {
   CDM_LOG("ChromiumCDMAdapter::GMPInit");
   sPlatform = aPlatformAPI;
   if (!mLib) {
     return GMPGenericErr;
   }
 
+  // Note: we must call the VerifyCdmHost_0 function if it's present before
+  // we call the initialize function.
+  auto verify = reinterpret_cast<decltype(::VerifyCdmHost_0)*>(
+    PR_FindFunctionSymbol(mLib, STRINGIFY(VerifyCdmHost_0)));
+  if (verify) {
+    nsTArray<cdm::HostFile> files;
+    for (HostFileData& hostFile : mHostFiles) {
+      files.AppendElement(TakeToCDMHostFile(hostFile));
+    }
+    bool result = verify(files.Elements(), files.Length());
+    GMP_LOG("%s VerifyCdmHost_0 returned %d", __func__, result);
+  }
+
   auto init = reinterpret_cast<decltype(::INITIALIZE_CDM_MODULE)*>(
     PR_FindFunctionSymbol(mLib, STRINGIFY(INITIALIZE_CDM_MODULE)));
   if (!init) {
     return GMPGenericErr;
   }
 
   CDM_LOG(STRINGIFY(INITIALIZE_CDM_MODULE)"()");
   init();
@@ -125,9 +160,69 @@ ChromiumCDMAdapter::Supports(int32_t aMo
                              int32_t aInterfaceVersion,
                              int32_t aHostVersion)
 {
   return aModuleVersion == CDM_MODULE_VERSION &&
          aInterfaceVersion == cdm::ContentDecryptionModule_8::kVersion &&
          aHostVersion == cdm::Host_8::kVersion;
 }
 
+HostFile::HostFile(HostFile&& aOther)
+  : mPath(aOther.mPath)
+  , mFile(aOther.TakePlatformFile())
+{
+}
+
+HostFile::~HostFile()
+{
+  if (mFile != cdm::kInvalidPlatformFile) {
+#ifdef XP_WIN
+    CloseHandle(mFile);
+#else
+    close(mFile);
+#endif
+    mFile = cdm::kInvalidPlatformFile;
+  }
+}
+
+#ifdef XP_WIN
+HostFile::HostFile(const nsCString& aPath)
+  : mPath(NS_ConvertUTF8toUTF16(aPath))
+{
+  HANDLE handle = CreateFileW(mPath.get(),
+                              GENERIC_READ,
+                              FILE_SHARE_READ | FILE_SHARE_WRITE,
+                              NULL,
+                              OPEN_EXISTING,
+                              0,
+                              NULL);
+  mFile = (handle == INVALID_HANDLE_VALUE) ? cdm::kInvalidPlatformFile : handle;
+}
+#endif
+
+#if defined(XP_MACOSX) || defined(XP_LINUX)
+HostFile::HostFile(const nsCString& aPath)
+  : mPath(aPath)
+{
+  // Note: open() returns -1 on failure; i.e. kInvalidPlatformFile.
+  mFile = open(aPath.get(), O_RDONLY);
+}
+#endif
+
+cdm::PlatformFile
+HostFile::TakePlatformFile()
+{
+  cdm::PlatformFile f = mFile;
+  mFile = cdm::kInvalidPlatformFile;
+  return f;
+}
+
+void
+ChromiumCDMAdapter::PopulateHostFiles(nsTArray<nsCString>&& aHostFilePaths)
+{
+  for (const nsCString& path : aHostFilePaths) {
+    mHostFiles.AppendElement(
+      HostFileData(mozilla::HostFile(path),
+                   mozilla::HostFile(path + NS_LITERAL_CSTRING(".sig"))));
+  }
+}
+
 } // namespace mozilla
--- a/dom/media/gmp/ChromiumCDMAdapter.h
+++ b/dom/media/gmp/ChromiumCDMAdapter.h
@@ -4,38 +4,86 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef ChromiumAdapter_h_
 #define ChromiumAdapter_h_
 
 #include "GMPLoader.h"
 #include "prlink.h"
 #include "GMPUtils.h"
+#include "nsTArray.h"
+#include "content_decryption_module_ext.h"
+#include "nsString.h"
 
 struct GMPPlatformAPI;
 
 namespace mozilla {
 
+#if defined(OS_WIN)
+typedef nsString HostFileString;
+#else
+typedef nsCString HostFileString;
+#endif
+
+class HostFile
+{
+public:
+  explicit HostFile(const nsCString& aPath);
+  HostFile(HostFile&& aOther);
+  ~HostFile();
+
+  const HostFileString& Path() const { return mPath; }
+  cdm::PlatformFile TakePlatformFile();
+
+private:
+  const HostFileString mPath;
+  cdm::PlatformFile mFile = cdm::kInvalidPlatformFile;
+};
+
+struct HostFileData
+{
+  HostFileData(HostFile&& aBinary, HostFile&& aSig)
+    : mBinary(Move(aBinary))
+    , mSig(Move(aSig))
+  {
+  }
+
+  HostFileData(HostFileData&& aOther)
+    : mBinary(Move(aOther.mBinary))
+    , mSig(Move(aOther.mSig))
+  {
+  }
+
+  ~HostFileData() {}
+
+  HostFile mBinary;
+  HostFile mSig;
+};
+
 class ChromiumCDMAdapter : public gmp::GMPAdapter
 {
 public:
+  explicit ChromiumCDMAdapter(nsTArray<nsCString>&& aHostFilePaths);
 
   void SetAdaptee(PRLibrary* aLib) override;
 
   // These are called in place of the corresponding GMP API functions.
   GMPErr GMPInit(const GMPPlatformAPI* aPlatformAPI) override;
   GMPErr GMPGetAPI(const char* aAPIName,
                    void* aHostAPI,
                    void** aPluginAPI,
                    uint32_t aDecryptorId) override;
   void GMPShutdown() override;
 
   static bool Supports(int32_t aModuleVersion,
                        int32_t aInterfaceVersion,
                        int32_t aHostVersion);
 
 private:
+  void PopulateHostFiles(nsTArray<nsCString>&& aHostFilePaths);
+
   PRLibrary* mLib = nullptr;
+  nsTArray<HostFileData> mHostFiles;
 };
 
 } // namespace mozilla
 
 #endif // ChromiumAdapter_h_
--- a/dom/media/gmp/GMPChild.cpp
+++ b/dom/media/gmp/GMPChild.cpp
@@ -17,23 +17,26 @@
 #include "gmp-video-decode.h"
 #include "gmp-video-encode.h"
 #include "GMPPlatform.h"
 #include "mozilla/ipc/CrashReporterClient.h"
 #include "mozilla/ipc/ProcessChild.h"
 #include "GMPUtils.h"
 #include "prio.h"
 #include "base/task.h"
+#include "base/command_line.h"
 #include "widevine-adapter/WidevineAdapter.h"
 #include "ChromiumCDMAdapter.h"
+#include "GMPLog.h"
 
 using namespace mozilla::ipc;
 
 #ifdef XP_WIN
 #include <stdlib.h> // for _exit()
+#include "WinUtils.h"
 #else
 #include <unistd.h> // for _exit()
 #endif
 
 #if defined(MOZ_GMP_SANDBOX)
 #if defined(XP_MACOSX)
 #include "mozilla/Sandbox.h"
 #endif
@@ -113,25 +116,23 @@ GetPluginFile(const nsAString& aPluginPa
   nsAutoString binaryName =                            baseName + NS_LITERAL_STRING(".dll");
 #else
 #error not defined
 #endif
   aLibFile->AppendRelativePath(binaryName);
   return true;
 }
 
-#if !defined(XP_MACOSX) || !defined(MOZ_GMP_SANDBOX)
 static bool
 GetPluginFile(const nsAString& aPluginPath,
               nsCOMPtr<nsIFile>& aLibFile)
 {
   nsCOMPtr<nsIFile> unusedlibDir;
   return GetPluginFile(aPluginPath, unusedlibDir, aLibFile);
 }
-#endif
 
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
 static nsCString
 GetNativeTarget(nsIFile* aFile)
 {
   bool isLink;
   nsCString path;
   aFile->IsSymlink(&isLink);
@@ -292,16 +293,35 @@ GMPChild::RecvPreloadLibs(const nsCStrin
       }
     }
   }
 #endif
   return IPC_OK();
 }
 
 bool
+GMPChild::ResolveLinks(nsCOMPtr<nsIFile>& aPath)
+{
+#if defined(XP_WIN)
+  return widget::WinUtils::ResolveJunctionPointsAndSymLinks(aPath);
+#elif defined(XP_MACOSX)
+  nsCString targetPath = GetNativeTarget(aPath);
+  nsCOMPtr<nsIFile> newFile;
+  if (NS_FAILED(
+        NS_NewNativeLocalFile(targetPath, true, getter_AddRefs(newFile)))) {
+    return false;
+  }
+  aPath = newFile;
+  return true;
+#else
+  return true;
+#endif
+}
+
+bool
 GMPChild::GetUTF8LibPath(nsACString& aOutLibPath)
 {
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   nsAutoCString pluginDirectoryPath, pluginFilePath;
   if (!GetPluginPaths(mPluginPath, pluginDirectoryPath, pluginFilePath)) {
     MOZ_CRASH("Error scanning plugin path");
   }
   aOutLibPath.Assign(pluginFilePath);
@@ -320,16 +340,127 @@ GMPChild::GetUTF8LibPath(nsACString& aOu
   nsAutoString path;
   libFile->GetPath(path);
   aOutLibPath = NS_ConvertUTF16toUTF8(path);
 
   return true;
 #endif
 }
 
+#if defined(XP_MACOSX)
+#define FIREFOX_FILE NS_LITERAL_STRING("firefox")
+#define XUL_LIB_FILE NS_LITERAL_STRING("XUL")
+#elif defined(XP_LINUX)
+#define FIREFOX_FILE NS_LITERAL_STRING("firefox")
+#define XUL_LIB_FILE NS_LITERAL_STRING("libxul.so")
+#elif defined(OS_WIN)
+#define FIREFOX_FILE NS_LITERAL_STRING("firefox.exe")
+#define XUL_LIB_FILE NS_LITERAL_STRING("xul.dll")
+#endif
+
+#if defined(XP_MACOSX)
+static bool
+GetFirefoxAppPath(nsCOMPtr<nsIFile> aPluginContainerPath,
+                  nsCOMPtr<nsIFile>& aOutFirefoxAppPath)
+{
+  // aPluginContainerPath will end with something like:
+  // xxxx/NightlyDebug.app/Contents/MacOS/plugin-container.app/Contents/MacOS/plugin-container
+  MOZ_ASSERT(aPluginContainerPath);
+  nsCOMPtr<nsIFile> path = aPluginContainerPath;
+  for (int i = 0; i < 4; i++) {
+    nsCOMPtr<nsIFile> parent;
+    if (NS_FAILED(path->GetParent(getter_AddRefs(parent)))) {
+      return false;
+    }
+    path = parent;
+  }
+  MOZ_ASSERT(path);
+  aOutFirefoxAppPath = path;
+  return true;
+}
+#endif
+
+nsTArray<nsCString>
+GMPChild::MakeCDMHostVerificationPaths()
+{
+  nsTArray<nsCString> paths;
+
+  // Plugin binary path.
+  nsCOMPtr<nsIFile> path;
+  nsString str;
+  if (GetPluginFile(mPluginPath, path) && FileExists(path) &&
+      ResolveLinks(path) && NS_SUCCEEDED(path->GetPath(str))) {
+    paths.AppendElement(NS_ConvertUTF16toUTF8(str));
+  }
+
+  // Plugin-container binary path.
+  // Note: clang won't let us initialize an nsString from a wstring, so we
+  // need to go through UTF8 to get to an nsString.
+  const std::string pluginContainer =
+    WideToUTF8(CommandLine::ForCurrentProcess()->program());
+  path = nullptr;
+  str = NS_ConvertUTF8toUTF16(nsDependentCString(pluginContainer.c_str()));
+  if (NS_SUCCEEDED(NS_NewLocalFile(str,
+                                   true, /* aFollowLinks */
+                                   getter_AddRefs(path))) &&
+      FileExists(path) && ResolveLinks(path) &&
+      NS_SUCCEEDED(path->GetPath(str))) {
+    paths.AppendElement(nsCString(NS_ConvertUTF16toUTF8(str)));
+  } else {
+    // Without successfully determining plugin-container's path, we can't
+    // determine libxul's or Firefox's. So give up.
+    return paths;
+  }
+
+  // Firefox application binary path.
+  nsCOMPtr<nsIFile> appDir;
+#if defined(XP_WIN) || defined(XP_LINUX)
+  // Note: re-using 'path' var here, as on Windows/Linux we assume Firefox
+  // executable is in the same directory as plugin-container.
+  if (NS_SUCCEEDED(path->GetParent(getter_AddRefs(appDir))) &&
+      NS_SUCCEEDED(appDir->Clone(getter_AddRefs(path))) &&
+      NS_SUCCEEDED(path->Append(FIREFOX_FILE)) && FileExists(path) &&
+      ResolveLinks(path) && NS_SUCCEEDED(path->GetPath(str))) {
+    paths.AppendElement(NS_ConvertUTF16toUTF8(str));
+  }
+#elif defined(XP_MACOSX)
+  // On MacOS the firefox binary is a few parent directories up from
+  // plugin-container.
+  if (GetFirefoxAppPath(path, appDir) &&
+      NS_SUCCEEDED(appDir->Clone(getter_AddRefs(path))) &&
+      NS_SUCCEEDED(path->Append(FIREFOX_FILE)) && FileExists(path) &&
+      ResolveLinks(path) && NS_SUCCEEDED(path->GetPath(str))) {
+    paths.AppendElement(NS_ConvertUTF16toUTF8(str));
+  }
+#endif
+  // Libxul path. Note: re-using 'path' var here, as we assume libxul is in
+  // the same directory as Firefox executable.
+  appDir->GetPath(str);
+  if (NS_SUCCEEDED(appDir->Clone(getter_AddRefs(path))) &&
+      NS_SUCCEEDED(path->Append(XUL_LIB_FILE)) && FileExists(path) &&
+      ResolveLinks(path) && NS_SUCCEEDED(path->GetPath(str))) {
+    paths.AppendElement(NS_ConvertUTF16toUTF8(str));
+  }
+
+  return paths;
+}
+
+static nsCString
+ToCString(const nsTArray<nsCString>& aStrings)
+{
+  nsCString result;
+  for (const nsCString& s : aStrings) {
+    if (!result.IsEmpty()) {
+      result.AppendLiteral(",");
+    }
+    result.Append(s);
+  }
+  return result;
+}
+
 mozilla::ipc::IPCResult
 GMPChild::AnswerStartPlugin(const nsString& aAdapter)
 {
   LOGD("%s", __FUNCTION__);
 
   nsCString libPath;
   if (!GetUTF8LibPath(libPath)) {
     return IPC_FAIL_NO_REASON(this);
@@ -360,17 +491,19 @@ GMPChild::AnswerStartPlugin(const nsStri
     return IPC_FAIL_NO_REASON(this);
   }
 #endif
 
   GMPAdapter* adapter = nullptr;
   if (isWidevine) {
     adapter = new WidevineAdapter();
   } else if (isChromium) {
-    adapter = new ChromiumCDMAdapter();
+    nsTArray<nsCString> paths(MakeCDMHostVerificationPaths());
+    GMP_LOG("%s CDM host paths=%s", __func__, ToCString(paths).get());
+    adapter = new ChromiumCDMAdapter(Move(paths));
   }
 
   if (!mGMPLoader->Load(libPath.get(),
                         libPath.Length(),
                         platformAPI,
                         adapter)) {
     NS_WARNING("Failed to load GMP");
     delete platformAPI;
--- a/dom/media/gmp/GMPChild.h
+++ b/dom/media/gmp/GMPChild.h
@@ -36,16 +36,18 @@ public:
 
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   bool SetMacSandboxInfo(MacSandboxPluginType aPluginType);
 #endif
 
 private:
   friend class GMPContentChild;
 
+  bool ResolveLinks(nsCOMPtr<nsIFile>& aPath);
+
   bool GetUTF8LibPath(nsACString& aOutLibPath);
 
   mozilla::ipc::IPCResult AnswerStartPlugin(const nsString& aAdapter) override;
   mozilla::ipc::IPCResult RecvPreloadLibs(const nsCString& aLibs) override;
 
   PGMPTimerChild* AllocPGMPTimerChild() override;
   bool DeallocPGMPTimerChild(PGMPTimerChild* aActor) override;
 
@@ -59,16 +61,18 @@ private:
 
   mozilla::ipc::IPCResult RecvInitGMPContentChild(Endpoint<PGMPContentChild>&& aEndpoint) override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
   void ProcessingError(Result aCode, const char* aReason) override;
 
   GMPErr GetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI, uint32_t aDecryptorId = 0);
 
+  nsTArray<nsCString> MakeCDMHostVerificationPaths();
+
   nsTArray<UniquePtr<GMPContentChild>> mGMPContentChildren;
 
   RefPtr<GMPTimerChild> mTimerChild;
   RefPtr<GMPStorageChild> mStorage;
 
   MessageLoop* mGMPMessageLoop;
   nsString mPluginPath;
   UniquePtr<GMPLoader> mGMPLoader;
--- a/media/gmp-clearkey/0.1/gmp-clearkey.cpp
+++ b/media/gmp-clearkey/0.1/gmp-clearkey.cpp
@@ -12,35 +12,45 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include <assert.h>
 #include <stdio.h>
 #include <string.h>
+#include <vector>
 
 #include "ClearKeyCDM.h"
 #include "ClearKeySessionManager.h"
 // This include is required in order for content_decryption_module to work
 // on Unix systems.
 #include "stddef.h"
 #include "content_decryption_module.h"
+#include "content_decryption_module_ext.h"
+
+#if defined(XP_MACOSX) || defined(XP_LINUX)
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#endif
 
 #ifdef ENABLE_WMF
 #include "WMFUtils.h"
 #endif // ENABLE_WMF
 
 extern "C" {
 
 CDM_API
 void INITIALIZE_CDM_MODULE() {
 
 }
 
+static bool sCanReadHostVerificationFiles = false;
+
 CDM_API
 void* CreateCdmInstance(int cdm_interface_version,
                         const char* key_system,
                         uint32_t key_system_size,
                         GetCdmHostFunc get_cdm_host_func,
                         void* user_data)
 {
 
@@ -48,17 +58,75 @@ void* CreateCdmInstance(int cdm_interfac
 
 #ifdef ENABLE_WMF
   if (!wmf::EnsureLibs()) {
     CK_LOGE("Required libraries were not found");
     return nullptr;
   }
 #endif
 
+  // Test that we're able to read the host files.
+  if (!sCanReadHostVerificationFiles) {
+    return nullptr;
+  }
+
   cdm::Host_8* host = static_cast<cdm::Host_8*>(
     get_cdm_host_func(cdm_interface_version, user_data));
   ClearKeyCDM* clearKey = new ClearKeyCDM(host);
 
   CK_LOGE("Created ClearKeyCDM instance!");
 
   return clearKey;
 }
+
+const size_t TEST_READ_SIZE = 16 * 1024;
+
+bool
+CanReadSome(cdm::PlatformFile aFile)
+{
+  vector<uint8_t> data;
+  data.resize(TEST_READ_SIZE);
+#ifdef XP_WIN
+  DWORD bytesRead = 0;
+  return ReadFile(aFile, &data.front(), TEST_READ_SIZE, &bytesRead, nullptr) &&
+         bytesRead > 0;
+#elif defined(XP_MACOSX) || defined(XP_LINUX)
+  return read(aFile, &data.front(), TEST_READ_SIZE) > 0;
+#else
+  return false;
+#endif
 }
+
+void
+ClosePlatformFile(cdm::PlatformFile aFile)
+{
+#ifdef XP_WIN
+  CloseHandle(aFile);
+#elif defined(XP_MACOSX) || defined(XP_LINUX)
+  close(aFile);
+#endif
+}
+
+CDM_API
+bool
+VerifyCdmHost_0(const cdm::HostFile* aHostFiles, uint32_t aNumFiles)
+{
+  // We expect 4 binaries: clearkey, libxul, plugin-container, and Firefox.
+  bool rv = (aNumFiles == 4);
+  // Verify that each binary is readable inside the sandbox,
+  // and close the handle.
+  for (uint32_t i = 0; i < aNumFiles; i++) {
+    const cdm::HostFile& hostFile = aHostFiles[i];
+    if (hostFile.file != cdm::kInvalidPlatformFile) {
+      if (!CanReadSome(hostFile.file)) {
+        rv = false;
+      }
+      ClosePlatformFile(hostFile.file);
+    }
+    if (hostFile.sig_file != cdm::kInvalidPlatformFile) {
+      ClosePlatformFile(hostFile.sig_file);
+    }
+  }
+  sCanReadHostVerificationFiles = rv;
+  return rv;
+}
+
+} // extern "C".