From c70c0665fc83450112f1a7256fc57f52999c8b51 Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Fri, 10 Jan 2020 15:42:15 +0100 Subject: [PATCH] init: Bind mount /mnt/installer early for scoped storage. Scoped storage has some unique requirements that are hard to implement with the two mount namespaces, because the daemon that does the mounting (vold) lives in a different namespace than the processes using those mounts. In particular, /mnt/installer is a special bind mount that should receive mount events under /mnt/user, but at the same time only only propagate mount events under /mnt/installer to /mnt/installer in the other namespace. More details in the code. Bug: 134706060 Test: /mnt/installer shows up and is setup correctly. Change-Id: I6dab5ace5a345d9d684a9f1ae94c833fc294d49e --- init/mount_namespace.cpp | 58 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp index 93eb2440c..b811622c5 100644 --- a/init/mount_namespace.cpp +++ b/init/mount_namespace.cpp @@ -35,6 +35,19 @@ namespace android { namespace init { namespace { +static bool BindMount(const std::string& source, const std::string& mount_point, + bool recursive = false) { + unsigned long mountflags = MS_BIND; + if (recursive) { + mountflags |= MS_REC; + } + if (mount(source.c_str(), mount_point.c_str(), nullptr, mountflags, nullptr) == -1) { + PLOG(ERROR) << "Failed to bind mount " << source; + return false; + } + return true; +} + static bool MakeShared(const std::string& mount_point, bool recursive = false) { unsigned long mountflags = MS_SHARED; if (recursive) { @@ -47,6 +60,18 @@ static bool MakeShared(const std::string& mount_point, bool recursive = false) { return true; } +static bool MakeSlave(const std::string& mount_point, bool recursive = false) { + unsigned long mountflags = MS_SLAVE; + if (recursive) { + mountflags |= MS_REC; + } + if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) { + PLOG(ERROR) << "Failed to change propagation type to slave"; + return false; + } + return true; +} + static bool MakePrivate(const std::string& mount_point, bool recursive = false) { unsigned long mountflags = MS_PRIVATE; if (recursive) { @@ -191,6 +216,39 @@ bool SetupMountNamespaces() { // namespace if (!(MakePrivate("/linkerconfig"))) return false; + // The two mount namespaces present challenges for scoped storage, because + // vold, which is responsible for most of the mounting, lives in the + // bootstrap mount namespace, whereas most other daemons and all apps live + // in the default namespace. Scoped storage has a need for a + // /mnt/installer view that is a slave bind mount of /mnt/user - in other + // words, all mounts under /mnt/user should automatically show up under + // /mnt/installer. However, additional mounts done under /mnt/installer + // should not propagate back to /mnt/user. In a single mount namespace + // this is easy to achieve, by simply marking the /mnt/installer a slave + // bind mount. Unfortunately, if /mnt/installer is only created and + // bind mounted after the two namespaces are created below, we end up + // with the following situation: + // /mnt/user and /mnt/installer share the same peer group in both the + // bootstrap and default namespaces. Marking /mnt/installer slave in either + // namespace means that it won't propagate events to the /mnt/installer in + // the other namespace, which is still something we require - vold is the + // one doing the mounting under /mnt/installer, and those mounts should + // show up in the default namespace as well. + // + // The simplest solution is to do the bind mount before the two namespaces + // are created: the effect is that in both namespaces, /mnt/installer is a + // slave to the /mnt/user mount, and at the same time /mnt/installer in the + // bootstrap namespace shares a peer group with /mnt/installer in the + // default namespace. + if (!mkdir_recursive("/mnt/user", 0755)) return false; + if (!mkdir_recursive("/mnt/installer", 0755)) return false; + if (!(BindMount("/mnt/user", "/mnt/installer", true))) return false; + // First, make /mnt/installer a slave bind mount + if (!(MakeSlave("/mnt/installer"))) return false; + // Then, make it shared again - effectively creating a new peer group, that + // will be inherited by new mount namespaces. + if (!(MakeShared("/mnt/installer"))) return false; + bootstrap_ns_fd.reset(OpenMountNamespace()); bootstrap_ns_id = GetMountNamespaceId();