Use privileged helper process to mount images.

Uses a privileged helper process to mount images, so that the untrusted
data is only read by the non-root, sandboxed main process.

BUG=chromium:682968
CQ-DEPEND=CL:434201
TEST=test_that -b ${BOARD} ${DUT_ip} platform_ImageLoaderServer

Change-Id: I7aa12d998065c0c2c81628f23ed52505333b4e8c
Reviewed-on: https://chromium-review.googlesource.com/434126
Commit-Ready: Greg Kerr <kerrnel@chromium.org>
Tested-by: Greg Kerr <kerrnel@chromium.org>
Reviewed-by: Jorge Lucangeli Obes <jorgelo@chromium.org>
diff --git a/.presubmitignore b/.presubmitignore
index d102f14..a901455 100644
--- a/.presubmitignore
+++ b/.presubmitignore
@@ -5,3 +5,4 @@
 imageloader.conf
 dbus_permissions/org.chromium.ImageLoader.conf
 seccomp/imageloader-seccomp-x86.policy
+seccomp/imageloader-helper-seccomp-x86.policy
diff --git a/component.cc b/component.cc
index c71ec36..d2e7b56 100644
--- a/component.cc
+++ b/component.cc
@@ -24,6 +24,8 @@
 #include <crypto/sha2.h>
 #include <crypto/signature_verifier.h>
 
+#include "helper_process.h"
+
 namespace imageloader {
 
 namespace {
@@ -130,7 +132,7 @@
   return manifest_;
 }
 
-bool Component::Mount(VerityMounter* mounter, const base::FilePath& dest_dir) {
+bool Component::Mount(HelperProcess* mounter, const base::FilePath& dest_dir) {
   if (!initialized_) {
     NOTREACHED() << "Component not initialized";
     return false;
@@ -151,12 +153,7 @@
   }
   base::ScopedFD image_fd(image.TakePlatformFile());
 
-  // The mount point is not yet taken, so go ahead.
-  if (!mounter->Mount(image_fd, dest_dir, table)) {
-    LOG(ERROR) << "Failed to mount image.";
-    return false;
-  }
-  return true;
+  return mounter->SendMountCommand(image_fd.get(), dest_dir.value(), table);
 }
 
 bool Component::ParseManifest() {
diff --git a/component.h b/component.h
index 03524a2..33ad211 100644
--- a/component.h
+++ b/component.h
@@ -24,7 +24,7 @@
 #include <base/macros.h>
 #include <crypto/secure_hash.h>
 
-#include "verity_mounter.h"
+#include "helper_process.h"
 
 namespace imageloader {
 
@@ -56,7 +56,7 @@
   bool CopyTo(const base::FilePath& dest_dir);
 
   // Mounts the component into |mount_point|. |mount_point| must already exist.
-  bool Mount(VerityMounter* mounter, const base::FilePath& mount_point);
+  bool Mount(HelperProcess* mounter, const base::FilePath& mount_point);
 
   // Return a reference to the parsed manifest object, which is stored in
   // memory.
diff --git a/component_unittest.cc b/component_unittest.cc
index 8080458..d194038 100644
--- a/component_unittest.cc
+++ b/component_unittest.cc
@@ -10,9 +10,8 @@
 #include <string>
 #include <vector>
 
-#include "mock_verity_mounter.h"
+#include "mock_helper_process.h"
 #include "test_utilities.h"
-#include "verity_mounter.h"
 
 #include <base/files/file_path.h>
 #include <base/files/file_util.h>
@@ -143,10 +142,14 @@
   ASSERT_TRUE(base::CreateDirectory(copied_dir));
   ASSERT_TRUE(base::SetPosixFilePermissions(copied_dir, kComponentDirPerms));
 
-  auto mount_mock = base::MakeUnique<MockVerityMounter>();
-  ON_CALL(*mount_mock, Mount(_, _, _)).WillByDefault(testing::Return(true));
-  EXPECT_CALL(*mount_mock, Mount(_, _, _)).Times(1);
-  ASSERT_TRUE(copied_component.Mount(mount_mock.get(), mount_dir));
+  // Note: this fails to test the actual mounting process since it is just a
+  // mock here. The platform_ImageLoader autotest tests the real helper
+  // process running as a dbus service.
+  auto helper_mock = base::MakeUnique<MockHelperProcess>();
+  EXPECT_CALL(*helper_mock, SendMountCommand(_, _, _)).Times(1);
+  ON_CALL(*helper_mock, SendMountCommand(_, _, _))
+      .WillByDefault(testing::Return(true));
+  ASSERT_TRUE(copied_component.Mount(helper_mock.get(), mount_dir));
 }
 
 TEST_F(ComponentTest, CheckFilesAfterCopy) {
diff --git a/dbus_adaptors/org.chromium.ImageLoaderInterface.xml b/dbus_adaptors/org.chromium.ImageLoaderInterface.xml
index 68dc448..6b0c9e5 100644
--- a/dbus_adaptors/org.chromium.ImageLoaderInterface.xml
+++ b/dbus_adaptors/org.chromium.ImageLoaderInterface.xml
@@ -50,5 +50,21 @@
     </tp:docstring>
     </arg>
   </method>
+  <method name="LoadComponent">
+    <tp:docstring>
+      Loads the component, if and only if the component verifies the
+      signature check, and returns the mount point.
+    </tp:docstring>
+    <arg name="name" type="s" direction="in">
+    <tp:docstring>
+      The name of the component.
+    </tp:docstring>
+    </arg>
+    <arg name="mount_point" type="s" direction="out">
+      <tp:docstring>
+        The mount point of the verified and mounted component.
+      </tp:docstring>
+    </arg>
+  </method>
 </interface>
 </node>
diff --git a/dbus_permissions/org.chromium.ImageLoader.conf b/dbus_permissions/org.chromium.ImageLoader.conf
index de66092..c2fdbbd 100644
--- a/dbus_permissions/org.chromium.ImageLoader.conf
+++ b/dbus_permissions/org.chromium.ImageLoader.conf
@@ -22,6 +22,9 @@
     <allow send_destination="org.chromium.ImageLoader"
       send_interface="org.chromium.ImageLoaderInterface"
       send_member="GetComponentVersion" />
+    <allow send_destination="org.chromium.ImageLoader"
+      send_interface="org.chromium.ImageLoaderInterface"
+      send_member="LoadComponent" />
   </policy>
   <policy context="default">
     <deny send_destination="org.chromium.ImageLoader"/>
diff --git a/helper_process.cc b/helper_process.cc
new file mode 100644
index 0000000..76db008
--- /dev/null
+++ b/helper_process.cc
@@ -0,0 +1,113 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "helper_process.h"
+
+#include <poll.h>
+#include <signal.h>
+#include <sys/socket.h>
+
+#include <utility>
+#include <vector>
+
+#include <base/process/launch.h>
+
+#include "ipc.pb.h"
+
+namespace imageloader {
+
+void HelperProcess::Start(int argc, char* argv[], const std::string& fd_arg) {
+  int control[2];
+
+  if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, control) != 0)
+    PLOG(FATAL) << "socketpair failed";
+
+  control_fd_.reset(control[0]);
+  const int subprocess_fd = control[1];
+
+  CHECK_GE(argc, 1);
+  std::vector<std::string> child_argv;
+  for (int i = 0; i < argc; i++)
+    child_argv.push_back(argv[i]);
+
+  child_argv.push_back(fd_arg + "=" + std::to_string(subprocess_fd));
+
+  base::FileHandleMappingVector fd_mapping;
+  fd_mapping.push_back({subprocess_fd, subprocess_fd});
+
+  base::LaunchOptions options;
+  options.fds_to_remap = &fd_mapping;
+
+  base::Process p = base::LaunchProcess(child_argv, options);
+  CHECK(p.IsValid());
+  pid_ = p.Pid();
+}
+
+bool HelperProcess::SendMountCommand(int fd, const std::string& path,
+                                     const std::string& table) {
+  struct msghdr msg = {0};
+  char fds[CMSG_SPACE(sizeof(fd))];
+  memset(fds, '\0', sizeof(fds));
+
+  MountImage msg_proto;
+  msg_proto.set_mount_path(path);
+  msg_proto.set_table(table);
+
+  std::string msg_str;
+  if (!msg_proto.SerializeToString(&msg_str))
+    LOG(FATAL) << "error serializing protobuf";
+
+  // iov takes a non-const pointer.
+  char buffer[msg_str.size() + 1];
+  memcpy(buffer, msg_str.c_str(), sizeof(buffer));
+
+  struct iovec iov[1];
+  iov[0].iov_base = buffer;
+  iov[0].iov_len = sizeof(buffer);
+
+  msg.msg_iov = iov;
+  msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]);
+
+  msg.msg_control = fds;
+  msg.msg_controllen = sizeof(fds);
+
+  struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+  cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+
+  // Move the file descriptor into the payload.
+  memmove(CMSG_DATA(cmsg), &fd, sizeof(fd));
+  msg.msg_controllen = cmsg->cmsg_len;
+
+  if (sendmsg(control_fd_.get(), &msg, 0) < 0) PLOG(FATAL) << "sendmsg failed";
+
+  return WaitForResponse();
+}
+
+bool HelperProcess::WaitForResponse() {
+  struct pollfd pfd;
+  pfd.fd = control_fd_.get();
+  pfd.events = POLLIN;
+
+  int rc = poll(&pfd, 1, /*timeout=*/ 2000);
+  PCHECK(rc >= 0 || errno == EINTR);
+
+  if (pfd.revents & POLLIN) {
+    char buffer[4096];
+    memset(buffer, '\0', sizeof(buffer));
+    ssize_t bytes = read(control_fd_.get(), buffer, sizeof(buffer));
+    PCHECK(bytes != -1);
+
+    MountResponse response;
+    if (!response.ParseFromArray(buffer, bytes)) {
+      LOG(FATAL) << "could not deserialize protobuf: " << buffer;
+    }
+    return response.success();
+  }
+
+  return false;
+}
+
+}  // namespace imageloader
diff --git a/helper_process.h b/helper_process.h
new file mode 100644
index 0000000..91a9627
--- /dev/null
+++ b/helper_process.h
@@ -0,0 +1,48 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IMAGELOADER_HELPER_PROCESS_H_
+#define IMAGELOADER_HELPER_PROCESS_H_
+
+#include <sys/types.h>
+
+#include <base/files/scoped_file.h>
+#include <base/macros.h>
+
+namespace imageloader {
+
+// Tracks a helper subprocess. Handles forking, cleaning up on termination, and
+// IPC.
+class HelperProcess {
+ public:
+  HelperProcess() = default;
+  virtual ~HelperProcess() = default;
+
+  // Re-execs imageloader with a new argument: "|fd_arg|=N", where N is the side
+  // of |control_fd|. This tells the subprocess to start up a different
+  // mainloop.
+  void Start(int argc, char* argv[], const std::string& fd_arg);
+
+  // Sends a message telling the helper process to mount the file backed by |fd|
+  // at the |path|.
+  virtual bool SendMountCommand(int fd, const std::string& path,
+                                const std::string& table);
+
+  const pid_t pid() { return pid_; }
+
+ protected:
+  // Waits for a reply from the helper process indicating if the mount succeeded
+  // or failed.
+  virtual bool WaitForResponse();
+
+  pid_t pid_{0};
+  base::ScopedFD control_fd_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HelperProcess);
+};
+
+}  // namespace imageloader
+
+#endif  // IMAGELOADER_HELPER_PROCESS_H_
diff --git a/imageloader.cc b/imageloader.cc
index 50e2944..bebd31b 100644
--- a/imageloader.cc
+++ b/imageloader.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium OS Authors. All rights reserved.
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -23,15 +23,17 @@
 const char ImageLoader::kImageLoaderUserName[] = "imageloaderd";
 const int ImageLoader::kShutdownTimeoutMilliseconds = 20000;
 
-ImageLoader::ImageLoader(ImageLoaderConfig config)
+ImageLoader::ImageLoader(ImageLoaderConfig config,
+                         std::unique_ptr<HelperProcess> process)
     : DBusServiceDaemon(kImageLoaderServiceName,
                         "/org/chromium/ImageLoader/Manager"),
-      impl_(std::move(config)) {}
+      impl_(std::move(config)),
+      helper_process_(std::move(process)) {}
 
 ImageLoader::~ImageLoader() {}
 
-int ImageLoader::OnInit() {
-  // Run with minimal privileges.
+// static
+void ImageLoader::EnterSandbox() {
   struct minijail* jail = minijail_new();
   minijail_no_new_privs(jail);
   minijail_use_seccomp_filter(jail);
@@ -39,16 +41,26 @@
   minijail_reset_signal_mask(jail);
   minijail_namespace_ipc(jail);
   minijail_namespace_net(jail);
-  minijail_namespace_user(jail);
   minijail_remount_proc_readonly(jail);
   CHECK_EQ(0, minijail_change_user(jail, kImageLoaderUserName));
   CHECK_EQ(0, minijail_change_group(jail, kImageLoaderGroupName));
   minijail_enter(jail);
+}
+
+int ImageLoader::OnInit() {
+  // Run with minimal privileges.
+  EnterSandbox();
 
   int return_code = brillo::DBusServiceDaemon::OnInit();
   if (return_code != EX_OK)
     return return_code;
 
+  process_reaper_.Register(this);
+  process_reaper_.WatchForChild(
+      FROM_HERE, helper_process_->pid(),
+      base::Bind(&ImageLoader::OnSubprocessExited, weak_factory_.GetWeakPtr(),
+                 helper_process_->pid()));
+
   PostponeShutdown();
 
   return EX_OK;
@@ -68,6 +80,10 @@
   brillo::DBusServiceDaemon::OnShutdown(return_code);
 }
 
+void ImageLoader::OnSubprocessExited(pid_t pid, const siginfo_t& info) {
+  LOG(FATAL) << "Subprocess " << pid << " exited unexpectedly.";
+}
+
 void ImageLoader::PostponeShutdown() {
   shutdown_callback_.Reset(base::Bind(&brillo::Daemon::Quit,
                                       base::Unretained(this)));
@@ -95,7 +111,7 @@
 
 bool ImageLoader::LoadComponent(brillo::ErrorPtr* err, const std::string& name,
                                 std::string* out_mount_point) {
-  *out_mount_point = impl_.LoadComponent(name);
+  *out_mount_point = impl_.LoadComponent(name, helper_process_.get());
   PostponeShutdown();
   return true;
 }
diff --git a/imageloader.gyp b/imageloader.gyp
index 157aa02..2750d7e 100644
--- a/imageloader.gyp
+++ b/imageloader.gyp
@@ -8,11 +8,22 @@
         'libbrillo-<(libbase_ver)',
         'libchrome-<(libbase_ver)',
         'libminijail',
+        'protobuf-lite',
       ],
     },
   },
   'targets': [
     {
+      'target_name': 'protos',
+      'type': 'static_library',
+      'variables': {
+        'proto_in_dir': '.',
+        'proto_out_dir': 'include',
+      },
+      'sources': ['<(proto_in_dir)/ipc.proto'],
+      'includes': ['../../platform2/common-mk/protoc.gypi'],
+    },
+    {
       'target_name': 'imageloader-adaptors',
       'type': 'none',
       'variables': {
@@ -46,13 +57,18 @@
       'type': 'static_library',
       'dependencies': [
         'imageloader-adaptors',
+        'protos',
       ],
       'sources': [
         'component.cc',
         'component.h',
+        'helper_process.cc',
+        'helper_process.h',
         'imageloader.cc',
         'imageloader.h',
         'imageloader_impl.cc',
+        'mount_helper.cc',
+        'mount_helper.h',
         'verity_mounter.h',
         'verity_mounter.cc',
       ],
@@ -93,6 +109,7 @@
             'imageloader_unittest.cc',
             'imageloader.cc',
             'imageloader.h',
+            'mock_helper_process.h',
             'test_utilities.cc',
             'test_utilities.h',
           ],
diff --git a/imageloader.h b/imageloader.h
index 0b81b17..fe34794 100644
--- a/imageloader.h
+++ b/imageloader.h
@@ -6,13 +6,17 @@
 
 #include <string>
 
+#include <signal.h>
+
 #include <base/callback.h>
 #include <base/cancelable_callback.h>
 #include <base/memory/weak_ptr.h>
 #include <brillo/daemons/dbus_daemon.h>
 #include <brillo/errors/error.h>
+#include <brillo/process_reaper.h>
 
 #include "dbus_adaptors/org.chromium.ImageLoaderInterface.h"
+#include "helper_process.h"
 #include "imageloader_impl.h"
 
 namespace imageloader {
@@ -27,7 +31,7 @@
   static const char kImageLoaderGroupName[];
   static const char kImageLoaderUserName[];
 
-  ImageLoader(ImageLoaderConfig config);
+  ImageLoader(ImageLoaderConfig config, std::unique_ptr<HelperProcess> helper);
   ~ImageLoader();
 
   // Implementations of the public methods interface.
@@ -44,7 +48,11 @@
 
   // Load and mount a component.
   bool LoadComponent(brillo::ErrorPtr* err, const std::string& name,
-                     std::string* out_mount_point);
+                     std::string* out_mount_point) override;
+
+  // Sandboxes the runtime environment, using minijail. This is publicly exposed
+  // so that imageloader_main.cc can sandbox when not running as a daemon.
+  static void EnterSandbox();
 
  protected:
   int OnInit() override;
@@ -53,6 +61,11 @@
   void OnShutdown(int* return_code) override;
 
  private:
+  // Callback from ProcessReaper to notify ImageLoader that one of the
+  // subprocesses died.
+  void OnSubprocessExited(pid_t pid, const siginfo_t& info);
+  // ImageLoader exits after 20 seconds of inactivity. This function restarts
+  // the timer.
   void PostponeShutdown();
 
   // Daemon will automatically shutdown after this length of idle time.
@@ -60,6 +73,8 @@
 
   std::unique_ptr<brillo::dbus_utils::DBusObject> dbus_object_;
   ImageLoaderImpl impl_;
+  std::unique_ptr<HelperProcess> helper_process_;
+  brillo::ProcessReaper process_reaper_;
   base::CancelableClosure shutdown_callback_;
   org::chromium::ImageLoaderInterfaceAdaptor dbus_adaptor_{this};
 
diff --git a/imageloader_impl.cc b/imageloader_impl.cc
index e49cdbd..6eb6a2c 100644
--- a/imageloader_impl.cc
+++ b/imageloader_impl.cc
@@ -45,77 +45,11 @@
   return mode == kComponentDirPerms;
 }
 
-bool CreateDirectoryWithMode(const base::FilePath& full_path, int mode) {
-  std::vector<base::FilePath> subpaths;
-
-  // Collect a list of all parent directories.
-  base::FilePath last_path = full_path;
-  subpaths.push_back(full_path);
-  for (base::FilePath path = full_path.DirName();
-       path.value() != last_path.value(); path = path.DirName()) {
-    subpaths.push_back(path);
-    last_path = path;
-  }
-
-  // Iterate through the parents and create the missing ones.
-  for (const auto& subpath : base::Reversed(subpaths)) {
-    if (base::DirectoryExists(subpath)) continue;
-    if (mkdir(subpath.value().c_str(), mode) == 0) continue;
-    // Mkdir failed, but it might have failed with EEXIST, or some other error
-    // due to the the directory appearing out of thin air. This can occur if
-    // two processes are trying to create the same file system tree at the same
-    // time. Check to see if it exists and make sure it is a directory.
-    if (!base::DirectoryExists(subpath)) {
-      PLOG(ERROR) << "Failed to create directory";
-      return false;
-    }
-  }
-  return true;
-}
-
-bool CreateMountPointIfNeeded(const base::FilePath& mount_point,
-                              bool* already_mounted) {
-  *already_mounted = false;
-  // Is this mount point somehow already taken?
-  struct stat st;
-  if (lstat(mount_point.value().c_str(), &st) == 0) {
-    if (!S_ISDIR(st.st_mode)) {
-      LOG(ERROR) << "Mount point exists but is not a directory.";
-      return false;
-    }
-
-    base::FilePath mount_parent = mount_point.DirName();
-    struct stat st2;
-    if (stat(mount_parent.value().c_str(), &st2) != 0) {
-      PLOG(ERROR) << "Could not stat the mount point parent";
-      return false;
-    }
-    if (st.st_dev != st2.st_dev) {
-      struct statfs st_fs;
-      if (statfs(mount_point.value().c_str(), &st_fs) != 0) {
-        PLOG(ERROR) << "statfs";
-        return false;
-      }
-      if (st_fs.f_type != SQUASHFS_MAGIC || !(st_fs.f_flags & ST_NODEV) ||
-          !(st_fs.f_flags & ST_NOSUID) || !(st_fs.f_flags & ST_RDONLY)) {
-        LOG(ERROR) << "File system is not the expected type.";
-        return false;
-      }
-      LOG(INFO) << "The mount point already exists: " << mount_point.value();
-      *already_mounted = true;
-      return true;
-    }
-  } else if (!CreateDirectoryWithMode(mount_point, kComponentDirPerms)) {
-    LOG(ERROR) << "Failed to create mount point: " << mount_point.value();
-    return false;
-  }
-  return true;
-}
-
 }  // namespace {}
 
 bool ImageLoaderImpl::LoadComponent(const std::string& name,
-                                    const std::string& mount_point_str) {
+                                    const std::string& mount_point_str,
+                                    HelperProcess* process) {
   base::FilePath component_path;
   if (!GetPathToCurrentComponentVersion(name, &component_path)) {
     return false;
@@ -128,15 +62,11 @@
   }
 
   base::FilePath mount_point(mount_point_str);
-  // First check if the component is already mounted and avoid unnecessary work.
-  bool already_mounted = false;
-  if (!CreateMountPointIfNeeded(mount_point, &already_mounted)) return false;
-  if (already_mounted) return true;
-
-  return component.Mount(config_.verity_mounter.get(), mount_point);
+  return component.Mount(process, mount_point);
 }
 
-std::string ImageLoaderImpl::LoadComponent(const std::string& name) {
+std::string ImageLoaderImpl::LoadComponent(const std::string& name,
+                                           HelperProcess* process) {
   base::FilePath component_path;
   if (!GetPathToCurrentComponentVersion(name, &component_path)) {
     return kBadResult;
@@ -150,15 +80,8 @@
 
   base::FilePath mount_point(
       GetMountPoint(config_.mount_path, name, component.manifest().version));
-  // First check if the component is already mounted and avoid unnecessary work.
-  bool already_mounted = false;
-  if (!CreateMountPointIfNeeded(mount_point, &already_mounted))
-    return kBadResult;
-  if (already_mounted) return name;
-
-  return component.Mount(config_.verity_mounter.get(), mount_point)
-             ? mount_point.value()
-             : kBadResult;
+  return component.Mount(process, mount_point) ? mount_point.value()
+                                               : kBadResult;
 }
 
 bool ImageLoaderImpl::RegisterComponent(
diff --git a/imageloader_impl.h b/imageloader_impl.h
index 63bc475..3fc0d23 100644
--- a/imageloader_impl.h
+++ b/imageloader_impl.h
@@ -11,22 +11,20 @@
 #include <base/gtest_prod_util.h>
 #include <base/macros.h>
 
-#include "verity_mounter.h"
+#include "helper_process.h"
 
 namespace imageloader {
 
 struct ImageLoaderConfig {
   ImageLoaderConfig(const std::vector<uint8_t> key, const char* storage_path,
-                    const char* mount_path, std::unique_ptr<VerityMounter> ops)
+                    const char* mount_path)
       : key(key),
         storage_dir(storage_path),
-        mount_path(mount_path),
-        verity_mounter(std::move(ops)) {}
+        mount_path(mount_path) {}
 
   std::vector<uint8_t> key;
   base::FilePath storage_dir;
   base::FilePath mount_path;
-  std::unique_ptr<VerityMounter> verity_mounter;
 };
 
 class ImageLoaderImpl {
@@ -44,10 +42,11 @@
 
   // Load the specified component. This returns the mount point or an empty
   // string on failure.
-  std::string LoadComponent(const std::string& name);
+  std::string LoadComponent(const std::string& name, HelperProcess* process);
 
   // Load the specified component at a set mount point.
-  bool LoadComponent(const std::string& name, const std::string& mount_point);
+  bool LoadComponent(const std::string& name, const std::string& mount_point,
+                     HelperProcess* process);
 
   // The directory hierarchy for a component consists of the storage_root (i.e.
   // `/var/lib/imageloader`), the component_root
diff --git a/imageloader_main.cc b/imageloader_main.cc
index b5f5f1e..9d073e4 100644
--- a/imageloader_main.cc
+++ b/imageloader_main.cc
@@ -8,9 +8,10 @@
 #include <brillo/flag_helper.h>
 #include <brillo/syslog_logging.h>
 
+#include "helper_process.h"
 #include "imageloader.h"
 #include "imageloader_impl.h"
-#include "verity_mounter.h"
+#include "mount_helper.h"
 
 constexpr uint8_t kProdPublicKey[] = {
     0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
@@ -35,18 +36,33 @@
                 "Specifies the name of the component when using --mount.");
   DEFINE_string(mount_point, "",
                 "Specifies the mountpoint when using --mount.");
+  DEFINE_int32(mount_helper_fd, -1,
+               "Control socket for starting an ImageLoader subprocess. Used "
+               "internally.");
   brillo::FlagHelper::Init(argc, argv, "imageloader");
 
   brillo::OpenLog("imageloader", true);
   brillo::InitLog(brillo::kLogToSyslog);
 
+  // Executing this as the helper process if specified.
+  if (FLAGS_mount_helper_fd >= 0) {
+    CHECK_GT(FLAGS_mount_helper_fd, -1);
+    base::ScopedFD fd(FLAGS_mount_helper_fd);
+    imageloader::MountHelper mount_helper(std::move(fd));
+    return mount_helper.Run();
+  }
+
   std::vector<uint8_t> key(std::begin(kProdPublicKey),
                            std::end(kProdPublicKey));
-  auto verity_mounter = base::MakeUnique<imageloader::VerityMounter>();
-  imageloader::ImageLoaderConfig config(key, kComponentsPath, kMountPath,
-                                        std::move(verity_mounter));
+  imageloader::ImageLoaderConfig config(key, kComponentsPath, kMountPath);
+  auto helper_process = base::MakeUnique<imageloader::HelperProcess>();
+  helper_process->Start(argc, argv, "--mount_helper_fd");
 
+  // Load and mount the specified component and exit.
   if (FLAGS_mount) {
+    // Run with minimal privilege.
+    imageloader::ImageLoader::EnterSandbox();
+
     if (FLAGS_mount_point == "" || FLAGS_mount_component == "") {
       LOG(ERROR) << "--mount_component=name and --mount_point=path must be set "
                     "with --mount";
@@ -55,14 +71,16 @@
     // Access the ImageLoaderImpl directly to avoid needless dbus dependencies,
     // which may not be available at early boot.
     imageloader::ImageLoaderImpl loader(std::move(config));
-    if (!loader.LoadComponent(FLAGS_mount_component, FLAGS_mount_point)) {
+    if (!loader.LoadComponent(FLAGS_mount_component, FLAGS_mount_point,
+                              helper_process.get())) {
       LOG(ERROR) << "Failed to verify and mount component.";
       return 1;
     }
     return 0;
   }
 
-  imageloader::ImageLoader daemon(std::move(config));
+  // Run as a daemon and wait for dbus requests.
+  imageloader::ImageLoader daemon(std::move(config), std::move(helper_process));
   daemon.Run();
 
   return 0;
diff --git a/imageloader_unittest.cc b/imageloader_unittest.cc
index 575cadc..30aa2cc 100644
--- a/imageloader_unittest.cc
+++ b/imageloader_unittest.cc
@@ -11,7 +11,7 @@
 #include <vector>
 
 #include "component.h"
-#include "mock_verity_mounter.h"
+#include "mock_helper_process.h"
 #include "test_utilities.h"
 #include "verity_mounter.h"
 
@@ -37,8 +37,7 @@
   ImageLoaderConfig GetConfig(const char* path) {
     std::vector<uint8_t> key(std::begin(kDevPublicKey),
                              std::end(kDevPublicKey));
-    auto ops = base::MakeUnique<MockVerityMounter>();
-    ImageLoaderConfig config(key, path, "/foo", std::move(ops));
+    ImageLoaderConfig config(key, path, "/foo");
     return config;
   }
 
@@ -112,16 +111,16 @@
 
 TEST_F(ImageLoaderTest, MountValidImage) {
   std::vector<uint8_t> key(std::begin(kDevPublicKey), std::end(kDevPublicKey));
-  auto mount_mock = base::MakeUnique<MockVerityMounter>();
-  ON_CALL(*mount_mock, Mount(_, _, _)).WillByDefault(testing::Return(true));
-  EXPECT_CALL(*mount_mock, Mount(_, _, _)).Times(2);
+  auto helper_mock = base::MakeUnique<MockHelperProcess>();
+  EXPECT_CALL(*helper_mock, SendMountCommand(_, _, _)).Times(2);
+  ON_CALL(*helper_mock, SendMountCommand(_, _, _))
+      .WillByDefault(testing::Return(true));
 
   base::ScopedTempDir scoped_mount_dir;
   ASSERT_TRUE(scoped_mount_dir.CreateUniqueTempDir());
 
   ImageLoaderConfig config(key, temp_dir_.value().c_str(),
-                           scoped_mount_dir.path().value().c_str(),
-                           std::move(mount_mock));
+                           scoped_mount_dir.path().value().c_str());
   ImageLoaderImpl loader(std::move(config));
 
   // We previously tested RegisterComponent, so assume this works if it reports
@@ -131,26 +130,28 @@
 
   const std::string expected_path =
       scoped_mount_dir.path().value() + "/PepperFlashPlayer/22.0.0.158";
-  EXPECT_EQ(expected_path, loader.LoadComponent(kTestComponentName));
+  EXPECT_EQ(expected_path,
+            loader.LoadComponent(kTestComponentName, helper_mock.get()));
 
   // Let's also test mounting the component at a fixed point.
   const std::string expected_path2 =
       scoped_mount_dir.path().value() + "/FixedMountPoint";
-  EXPECT_TRUE(loader.LoadComponent(kTestComponentName, expected_path2));
+  EXPECT_TRUE(loader.LoadComponent(kTestComponentName, expected_path2,
+                                   helper_mock.get()));
 }
 
 TEST_F(ImageLoaderTest, MountInvalidImage) {
   std::vector<uint8_t> key(std::begin(kDevPublicKey), std::end(kDevPublicKey));
-  auto mount_mock = base::MakeUnique<MockVerityMounter>();
-  ON_CALL(*mount_mock, Mount(_, _, _)).WillByDefault(testing::Return(true));
-  EXPECT_CALL(*mount_mock, Mount(_, _, _)).Times(0);
+  auto helper_mock = base::MakeUnique<MockHelperProcess>();
+  EXPECT_CALL(*helper_mock, SendMountCommand(_, _, _)).Times(0);
+  ON_CALL(*helper_mock, SendMountCommand(_, _, _))
+      .WillByDefault(testing::Return(true));
 
   base::ScopedTempDir scoped_mount_dir;
   ASSERT_TRUE(scoped_mount_dir.CreateUniqueTempDir());
 
   ImageLoaderConfig config(key, temp_dir_.value().c_str(),
-                           scoped_mount_dir.path().value().c_str(),
-                           std::move(mount_mock));
+                           scoped_mount_dir.path().value().c_str());
   ImageLoaderImpl loader(std::move(config));
 
   // We previously tested RegisterComponent, so assume this works if it reports
@@ -164,7 +165,7 @@
   std::string contents = "corrupt";
   ASSERT_EQ(static_cast<int>(contents.size()),
             base::WriteFile(table, contents.data(), contents.size()));
-  ASSERT_EQ("", loader.LoadComponent(kTestComponentName));
+  ASSERT_EQ("", loader.LoadComponent(kTestComponentName, helper_mock.get()));
 }
 
 TEST_F(ImageLoaderTest, SetupTable) {
diff --git a/ipc.proto b/ipc.proto
new file mode 100644
index 0000000..c3f6e96
--- /dev/null
+++ b/ipc.proto
@@ -0,0 +1,21 @@
+// Copyright 2016 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+package imageloader;
+
+// Optional fields are considered best practice, but since the client and server
+// come from the same code base, this uses required fields to save on
+// validation.
+
+message MountImage {
+  required string mount_path = 1;
+  required string table = 2;
+}
+
+message MountResponse {
+  required bool success = 1;
+}
diff --git a/mock_helper_process.h b/mock_helper_process.h
new file mode 100644
index 0000000..4116b9e
--- /dev/null
+++ b/mock_helper_process.h
@@ -0,0 +1,30 @@
+// Copyright 2016 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IMAGELOADER_MOCK_HELPER_PROCESS_H_
+#define IMAGELOADER_MOCK_HELPER_PROCESS_H_
+
+#include "gmock/gmock.h"
+#include "helper_process.h"
+
+namespace imageloader {
+
+// Mock helper process used for unit testing.
+class MockHelperProcess : public HelperProcess {
+ public:
+  MockHelperProcess() {}
+  ~MockHelperProcess() {}
+
+  // Sends a message telling the helper process to mount the file backed by |fd|
+  // at the |path|.
+  MOCK_METHOD3(SendMountCommand,
+               bool(int, const std::string&, const std::string&));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockHelperProcess);
+};
+
+}  // namespace imageloader
+
+#endif // IMAGELOADER_MOCK_HELPER_PROCESS_H_
diff --git a/mount_helper.cc b/mount_helper.cc
new file mode 100644
index 0000000..4f94800
--- /dev/null
+++ b/mount_helper.cc
@@ -0,0 +1,121 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mount_helper.h"
+
+#include <fcntl.h>
+#include <libminijail.h>
+#include <sys/capability.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <base/files/file_path.h>
+#include <base/bind.h>
+#include <base/logging.h>
+#include <base/time/time.h>
+
+#include "imageloader.h"
+#include "ipc.pb.h"
+#include "verity_mounter.h"
+
+namespace imageloader {
+
+namespace {
+constexpr char kSeccompFilterPath[] =
+    "/opt/google/imageloader/imageloader-helper-seccomp.policy";
+}  // namespace
+
+MountHelper::MountHelper(base::ScopedFD control_fd)
+    : control_fd_(std::move(control_fd)),
+      control_watcher_(),
+      pending_fd_(-1),
+      mounter_() {}
+
+int MountHelper::OnInit() {
+  // Prevent the main process from sending us any signals.
+  // errno can be EPERM if the process is already the group leader.
+  if (setsid() < 0 && errno != EPERM) PLOG(FATAL) << "setsid failed";
+
+  // Run with minimal privileges.
+  struct minijail* jail = minijail_new();
+  minijail_no_new_privs(jail);
+  minijail_use_seccomp_filter(jail);
+  minijail_parse_seccomp_filters(jail, kSeccompFilterPath);
+  minijail_reset_signal_mask(jail);
+  minijail_namespace_net(jail);
+  minijail_skip_remount_private(jail);
+  minijail_enter(jail);
+
+  MessageLoopForIO::current()->WatchFileDescriptor(control_fd_.get(), true,
+                                                   MessageLoopForIO::WATCH_READ,
+                                                   &control_watcher_, this);
+  return Daemon::OnInit();
+}
+
+void MountHelper::OnFileCanReadWithoutBlocking(int fd) {
+  CHECK_EQ(fd, control_fd_.get());
+  char buffer[4096 * 4];
+  memset(buffer, '\0', sizeof(buffer));
+
+  struct msghdr msg = {0};
+  struct iovec iov[1];
+
+  iov[0].iov_base = buffer;
+  iov[0].iov_len = sizeof(buffer);
+
+  msg.msg_iov = iov;
+  msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]);
+
+  char c_buffer[256];
+  msg.msg_control = c_buffer;
+  msg.msg_controllen = sizeof(c_buffer);
+
+  ssize_t bytes = recvmsg(fd, &msg, 0);
+  if (bytes < 0) PLOG(FATAL) << "recvmsg failed";
+  // Per recvmsg(2), the return value will be 0 when the peer has performed an
+  // orderly shutdown.
+  if (bytes == 0) _exit(0);
+
+  struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+
+  if (cmsg == nullptr) LOG(FATAL) << "no cmsg";
+
+  if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
+    LOG(FATAL) << "cmsg is wrong type";
+
+  memmove(&pending_fd_, CMSG_DATA(cmsg), sizeof(pending_fd_));
+
+  MountImage command;
+  if (!command.ParseFromArray(buffer, strlen(buffer)))
+    LOG(FATAL) << "error parsing protobuf";
+
+  // Handle the command to mount the image.
+  MountResponse response = HandleCommand(command);
+  // Reply to the parent process with the success or failure.
+  SendResponse(response);
+}
+
+MountResponse MountHelper::HandleCommand(MountImage& command) {
+  bool status = mounter_.Mount(base::ScopedFD(pending_fd_),
+                               base::FilePath(command.mount_path()),
+                               command.table());
+  if (!status) LOG(ERROR) << "mount failed";
+
+  MountResponse response;
+  response.set_success(status);
+  return response;
+}
+
+void MountHelper::SendResponse(MountResponse& response) {
+  std::string response_str;
+  if (!response.SerializeToString(&response_str))
+    LOG(FATAL) << "failed to serialize protobuf";
+
+  if (write(control_fd_.get(), response_str.data(), response_str.size()) !=
+      static_cast<ssize_t>(response_str.size())) {
+    LOG(FATAL) << "short write on protobuf";
+  }
+}
+
+}  // namespace imageloader
diff --git a/mount_helper.h b/mount_helper.h
new file mode 100644
index 0000000..499a5b3
--- /dev/null
+++ b/mount_helper.h
@@ -0,0 +1,48 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IMAGELOADER_MOUNT_HELPER_H_
+#define IMAGELOADER_MOUNT_HELPER_H_
+
+#include <base/files/scoped_file.h>
+#include <base/message_loop/message_loop.h>
+#include <brillo/daemons/daemon.h>
+
+#include "ipc.pb.h"
+#include "verity_mounter.h"
+
+using MessageLoopForIO = base::MessageLoopForIO;
+
+namespace imageloader {
+
+// Main loop for the Mount helper process.
+// This object is used in the subprocess.
+class MountHelper : public brillo::Daemon,
+                    public base::MessageLoopForIO::Watcher {
+ public:
+  explicit MountHelper(base::ScopedFD control_fd);
+
+ protected:
+  // OVerrides Daemon init callback.
+  int OnInit() override;
+
+  // Overrides MessageLoopForIO callbacks for new data on |control_fd_|.
+  void OnFileCanReadWithoutBlocking(int fd) override;
+  void OnFileCanWriteWithoutBlocking(int fd) override {}
+
+ private:
+  MountResponse HandleCommand(MountImage& command);
+  void SendResponse(MountResponse& response);
+
+  base::ScopedFD control_fd_;
+  MessageLoopForIO::FileDescriptorWatcher control_watcher_;
+  int pending_fd_;
+  VerityMounter mounter_;
+
+  DISALLOW_COPY_AND_ASSIGN(MountHelper);
+};
+
+}  // namespace imageloader
+
+#endif  // IMAGELOADER_MOUNT_HELPER_H_
diff --git a/seccomp/imageloader-helper-seccomp-amd64.policy b/seccomp/imageloader-helper-seccomp-amd64.policy
new file mode 100644
index 0000000..4142276
--- /dev/null
+++ b/seccomp/imageloader-helper-seccomp-amd64.policy
@@ -0,0 +1,75 @@
+# Copyright 2017 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+access:1
+arch_prctl: 1
+brk: 1
+capget: 1
+capset: 1
+chown: 1
+clock_getres: 1
+clone: 1
+close: 1
+connect: 1
+dup2: 1
+epoll_ctl: 1
+epoll_wait: 1
+execve: 1
+exit: 1
+exit_group: 1
+fcntl: 1
+fdatasync: 1
+fstat: 1
+futex: 1
+getdents: 1
+getdents64: 1
+geteuid: 1
+getresgid: 1
+getresuid: 1
+getrlimit: 1
+getsockname: 1
+gettimeofday: 1
+gettid: 1
+ioctl: 1
+lseek: 1
+lstat: 1
+mkdir: 1
+mmap: 1
+mount: 1
+mprotect: 1
+munmap: 1
+nanosleep: 1
+open: 1
+openat: 1
+pipe: 1
+poll: 1
+prctl: 1
+pwrite64: 1
+read: 1
+recvmsg: 1
+rename: 1
+restart_syscall: 1
+rmdir: 1
+rt_sigaction: 1
+rt_sigreturn: 1
+rt_sigprocmask: 1
+semctl: 1
+semget: 1
+semop: 1
+sendmsg: 1
+sendto: 1
+set_robust_list: 1
+set_tid_address: 1
+setgroups: 1
+setresgid: 1
+setresuid: 1
+signalfd4: 1
+socket: 1
+stat: 1
+statfs: 1
+sysinfo: 1
+tgkill: 1
+uname: 1
+unlink: 1
+wait4: 1
+write: 1
diff --git a/seccomp/imageloader-helper-seccomp-arm.policy b/seccomp/imageloader-helper-seccomp-arm.policy
new file mode 100644
index 0000000..53f7da2
--- /dev/null
+++ b/seccomp/imageloader-helper-seccomp-arm.policy
@@ -0,0 +1,76 @@
+# Copyright 2017 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+_llseek: 1
+ARM_set_tls: 1
+access:1
+brk: 1
+capget: 1
+capset: 1
+chown: 1
+clock_getres: 1
+clock_gettime: 1
+clone: 1
+close: 1
+connect: 1
+dup2: 1
+execve: 1
+epoll_ctl: 1
+epoll_wait: 1
+exit: 1
+exit_group: 1
+fcntl64: 1
+fdatasync: 1
+fstat64: 1
+futex: 1
+getdents64: 1
+geteuid32: 1
+getresgid32: 1
+getresuid32: 1
+getsockname: 1
+gettimeofday: 1
+gettid: 1
+ioctl: 1
+lstat64: 1
+mkdir: 1
+mmap2: 1
+mount: 1
+mprotect: 1
+munmap: 1
+nanosleep: 1
+open: 1
+openat: 1
+pipe: 1
+poll: 1
+prctl: 1
+prlimit64: 1
+pwrite64: 1
+read: 1
+recvmsg: 1
+rename: 1
+restart_syscall: 1
+rmdir: 1
+rt_sigaction: 1
+rt_sigprocmask: 1
+semctl: 1
+semget: 1
+semop: 1
+send: 1
+sendmsg: 1
+sendto: 1
+set_robust_list: 1
+set_tid_address: 1
+setgroups32: 1
+setresgid32: 1
+setresuid32: 1
+sigreturn: 1
+signalfd4: 1
+socket: 1
+stat64: 1
+statfs64: 1
+sysinfo: 1
+tgkill: 1
+ugetrlimit: 1
+uname: 1
+wait4: 1
+write: 1
diff --git a/seccomp/imageloader-helper-seccomp-x86.policy b/seccomp/imageloader-helper-seccomp-x86.policy
new file mode 100644
index 0000000..cfcba52
--- /dev/null
+++ b/seccomp/imageloader-helper-seccomp-x86.policy
@@ -0,0 +1,79 @@
+# Copyright 2016 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+_llseek: 1
+access:1
+brk: 1
+capget: 1
+capset: 1
+chown: 1
+clock_getres: 1
+clock_gettime: 1
+clone: 1
+close: 1
+dup2: 1
+epoll_ctl: 1
+epoll_wait: 1
+execve: 1
+exit: 1
+exit_group: 1
+fcntl64: 1
+fdatasync: 1
+fstat64: 1
+futex: 1
+getdents: 1
+getdents64: 1
+geteuid32: 1
+getresgid32: 1
+getresuid32: 1
+getrlimit: 1
+gettimeofday: 1
+gettid: 1
+ioctl: 1
+ipc: 1
+lseek: 1
+lstat64: 1
+mkdir: 1
+mmap2: 1
+mount: 1
+mprotect: 1
+munmap: 1
+nanosleep: 1
+open: 1
+openat: 1
+pipe: 1
+poll: 1
+prctl: 1
+prlimit64: 1
+pwrite64: 1
+read: 1
+rename: 1
+restart_syscall: 1
+rmdir: 1
+rt_sigaction: 1
+rt_sigreturn: 1
+rt_sigprocmask: 1
+set_robust_list: 1
+set_thread_area: 1
+set_tid_address: 1
+setgroups: 1
+setresgid: 1
+setresuid: 1
+signalfd4: 1
+sigreturn: 1
+# On 32-bit x86, the networking operatons such as connect() and
+# sendmsg() are multiplexed through the socketcall() system call. arg0
+# is the operation, such as SYS_SOCKETPAIR (8). See <linux/net.h> for the
+# symbolic names.
+socketcall: arg0 == 1 || arg0 == 3 || arg0 == 6 || arg0 == 8 || arg0 == 9 || arg0 == 16 || arg0 == 17
+stat64: 1
+statfs64: 1
+sysinfo: 1
+tgkill: 1
+time: 1
+ugetrlimit: 1
+uname: 1
+unlink: 1
+wait4: 1
+waitpid: 1
+write: 1
diff --git a/seccomp/imageloader-seccomp-amd64.policy b/seccomp/imageloader-seccomp-amd64.policy
index 2f600d9..6dac10c 100644
--- a/seccomp/imageloader-seccomp-amd64.policy
+++ b/seccomp/imageloader-seccomp-amd64.policy
@@ -55,4 +55,5 @@
 tgkill: 1
 uname: 1
 unlink: 1
+waitid: 1
 write: 1
diff --git a/verity_mounter.cc b/verity_mounter.cc
index 79659b8..220ed50 100644
--- a/verity_mounter.cc
+++ b/verity_mounter.cc
@@ -7,21 +7,28 @@
 #include <algorithm>
 #include <string>
 
+#include </usr/include/linux/magic.h>
 #include <fcntl.h>
 #include <linux/loop.h>
 #include <sys/ioctl.h>
-#include <sys/stat.h>
 #include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/vfs.h>
 
 #include <base/command_line.h>
+#include <base/containers/adapters.h>
+#include <base/files/file_path.h>
 #include <base/files/file_util.h>
 #include <base/process/launch.h>
 #include <base/rand_util.h>
 #include <base/strings/string_number_conversions.h>
-#include <base/strings/stringprintf.h>
 #include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
 #include <base/time/time.h>
 
+#include "component.h"
+
 namespace imageloader {
 
 namespace {
@@ -81,8 +88,96 @@
 void ClearLoopDevice(const std::string& device_path) {
   base::ScopedFD loop_device_fd(
       open(device_path.c_str(), O_RDONLY | O_CLOEXEC));
-  if (loop_device_fd.is_valid())
-    ioctl(loop_device_fd.get(), LOOP_CLR_FD, 0);
+  if (loop_device_fd.is_valid()) ioctl(loop_device_fd.get(), LOOP_CLR_FD, 0);
+}
+
+bool SetupDeviceMapper(const std::string& device_path, const std::string& table,
+                       std::string* dev_name) {
+  // Now setup the dmsetup table.
+  std::string final_table = table;
+  if (!VerityMounter::SetupTable(&final_table, device_path)) return false;
+
+  // Generate a name with a random string of 32 characters: we consider this to
+  // have sufficiently low chance of collision to assume the name isn't taken.
+  std::vector<uint8_t> rand_bytes(32);
+  base::RandBytes(rand_bytes.data(), rand_bytes.size());
+  std::string name = base::HexEncode(rand_bytes.data(), rand_bytes.size());
+
+  if (!LaunchDMCreate(name, final_table)) {
+    LOG(ERROR) << "Failed to run dmsetup.";
+    return false;
+  }
+
+  dev_name->assign("/dev/mapper/" + name);
+  return true;
+}
+
+bool CreateDirectoryWithMode(const base::FilePath& full_path, int mode) {
+  std::vector<base::FilePath> subpaths;
+
+  // Collect a list of all parent directories.
+  base::FilePath last_path = full_path;
+  subpaths.push_back(full_path);
+  for (base::FilePath path = full_path.DirName();
+       path.value() != last_path.value(); path = path.DirName()) {
+    subpaths.push_back(path);
+    last_path = path;
+  }
+
+  // Iterate through the parents and create the missing ones.
+  for (const auto& subpath : base::Reversed(subpaths)) {
+    if (base::DirectoryExists(subpath)) continue;
+    if (mkdir(subpath.value().c_str(), mode) == 0) continue;
+    // Mkdir failed, but it might have failed with EEXIST, or some other error
+    // due to the the directory appearing out of thin air. This can occur if
+    // two processes are trying to create the same file system tree at the same
+    // time. Check to see if it exists and make sure it is a directory.
+    if (!base::DirectoryExists(subpath)) {
+      PLOG(ERROR) << "Failed to create directory: " << subpath.value();
+      return false;
+    }
+  }
+  return true;
+}
+
+bool CreateMountPointIfNeeded(const base::FilePath& mount_point,
+                              bool* already_mounted) {
+  LOG(INFO) << "In CreateMountPoint if needed";
+  *already_mounted = false;
+  // Is this mount point somehow already taken?
+  struct stat st;
+  if (lstat(mount_point.value().c_str(), &st) == 0) {
+    if (!S_ISDIR(st.st_mode)) {
+      LOG(ERROR) << "Mount point exists but is not a directory.";
+      return false;
+    }
+
+    base::FilePath mount_parent = mount_point.DirName();
+    struct stat st2;
+    if (stat(mount_parent.value().c_str(), &st2) != 0) {
+      PLOG(ERROR) << "Could not stat the mount point parent";
+      return false;
+    }
+    if (st.st_dev != st2.st_dev) {
+      struct statfs st_fs;
+      if (statfs(mount_point.value().c_str(), &st_fs) != 0) {
+        PLOG(ERROR) << "statfs";
+        return false;
+      }
+      if (st_fs.f_type != SQUASHFS_MAGIC || !(st_fs.f_flags & ST_NODEV) ||
+          !(st_fs.f_flags & ST_NOSUID) || !(st_fs.f_flags & ST_RDONLY)) {
+        LOG(ERROR) << "File system is not the expected type.";
+        return false;
+      }
+      LOG(INFO) << "The mount point already exists: " << mount_point.value();
+      *already_mounted = true;
+      return true;
+    }
+  } else if (!CreateDirectoryWithMode(mount_point, kComponentDirPerms)) {
+    LOG(ERROR) << "Failed to create mount point: " << mount_point.value();
+    return false;
+  }
+  return true;
 }
 
 // Reserves a loop device and associates it with |image_fd|. The path to the
@@ -123,29 +218,7 @@
   return MountStatus::SUCCESS;
 }
 
-bool SetupDeviceMapper(const std::string& device_path, const std::string& table,
-                       std::string* dev_name) {
-  // Now setup the dmsetup table.
-  std::string final_table = table;
-  if (!VerityMounter::SetupTable(&final_table, device_path))
-    return false;
-
-  // Generate a name with a random string of 32 characters: we consider this to
-  // have sufficiently low chance of collision to assume the name isn't taken.
-  std::vector<uint8_t> rand_bytes(32);
-  base::RandBytes(rand_bytes.data(), rand_bytes.size());
-  std::string name = base::HexEncode(rand_bytes.data(), rand_bytes.size());
-
-  if (!LaunchDMCreate(name, final_table)) {
-    LOG(ERROR) << "Failed to run dmsetup.";
-    return false;
-  }
-
-  dev_name->assign("/dev/mapper/" + name);
-  return true;
-}
-
-} // namespace
+}  // namespace
 
 // static
 bool VerityMounter::SetupTable(std::string* table,
@@ -171,6 +244,11 @@
 bool VerityMounter::Mount(const base::ScopedFD& image_fd,
                           const base::FilePath& mount_point,
                           const std::string& table) {
+  // First check if the component is already mounted and avoid unnecessary work.
+  bool already_mounted = false;
+  if (!CreateMountPointIfNeeded(mount_point, &already_mounted)) return false;
+  if (already_mounted) return true;
+
   std::string loop_device_path;
   // We need to retry because another program could grap the loop device,
   // resulting in an EBUSY error. If that happens, run again and grab a new