blob: e069a5d3e3dbe7007658c928554fc4c309eb8ab7 [file] [log] [blame]
Jiyong Park68660412019-01-16 23:00:59 +09001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "mount_namespace.h"
18
19#include <sys/mount.h>
20
21#include <string>
22#include <vector>
23
24#include <android-base/file.h>
25#include <android-base/logging.h>
26#include <android-base/properties.h>
Jooyung Han5bb9d212019-11-25 13:50:44 +090027#include <android-base/result.h>
Jiyong Park68660412019-01-16 23:00:59 +090028#include <android-base/unique_fd.h>
29
30#include "util.h"
31
32namespace android {
33namespace init {
34namespace {
35
Elliott Hughese79b8c22020-07-28 11:09:03 -070036static bool BindMount(const std::string& source, const std::string& mount_point) {
37 if (mount(source.c_str(), mount_point.c_str(), nullptr, MS_BIND | MS_REC, nullptr) == -1) {
Martijn Coenenc70c0662020-01-10 15:42:15 +010038 PLOG(ERROR) << "Failed to bind mount " << source;
39 return false;
40 }
41 return true;
42}
43
Elliott Hughese79b8c22020-07-28 11:09:03 -070044static bool ChangeMount(const std::string& mount_point, unsigned long mountflags) {
Jiyong Park68660412019-01-16 23:00:59 +090045 if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
Elliott Hughese79b8c22020-07-28 11:09:03 -070046 PLOG(ERROR) << "Failed to remount " << mount_point << " as " << std::hex << mountflags;
Jiyong Park68660412019-01-16 23:00:59 +090047 return false;
48 }
49 return true;
50}
51
52static int OpenMountNamespace() {
53 int fd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
54 if (fd < 0) {
55 PLOG(ERROR) << "Cannot open fd for current mount namespace";
56 }
57 return fd;
58}
59
60static std::string GetMountNamespaceId() {
61 std::string ret;
62 if (!android::base::Readlink("/proc/self/ns/mnt", &ret)) {
63 PLOG(ERROR) << "Failed to read namespace ID";
64 return "";
65 }
66 return ret;
67}
68
Jiyong Park68660412019-01-16 23:00:59 +090069static android::base::unique_fd bootstrap_ns_fd;
70static android::base::unique_fd default_ns_fd;
71
72static std::string bootstrap_ns_id;
73static std::string default_ns_id;
74
75} // namespace
76
Jooyung Han201801c2023-07-20 17:25:47 +090077// In case we have two sets of APEXes (non-updatable, updatable), we need two separate mount
78// namespaces.
79bool NeedsTwoMountNamespaces() {
80 if (IsRecoveryMode()) return false;
81 // In microdroid, there's only one set of APEXes in built-in directories include block devices.
82 if (IsMicrodroid()) return false;
83 return true;
84}
85
Jiyong Park68660412019-01-16 23:00:59 +090086bool SetupMountNamespaces() {
87 // Set the propagation type of / as shared so that any mounting event (e.g.
88 // /data) is by default visible to all processes. When private mounting is
89 // needed for /foo/bar, then we will make /foo/bar as a mount point (by
90 // bind-mounting by to itself) and set the propagation type of the mount
91 // point to private.
Elliott Hughese79b8c22020-07-28 11:09:03 -070092 if (!ChangeMount("/", MS_SHARED | MS_REC)) return false;
Jiyong Park68660412019-01-16 23:00:59 +090093
Jiyong Park7b4801a2019-02-25 16:41:38 +090094 // /apex is a private mountpoint to give different sets of APEXes for
Jiyong Parkdcbaf9f2019-02-22 22:15:25 +090095 // the bootstrap and default mount namespaces. The processes running with
96 // the bootstrap namespace get APEXes from the read-only partition.
Elliott Hughese79b8c22020-07-28 11:09:03 -070097 if (!(ChangeMount("/apex", MS_PRIVATE))) return false;
Jiyong Parkdcbaf9f2019-02-22 22:15:25 +090098
Jooyung Han201801c2023-07-20 17:25:47 +090099 // However, some components (e.g. servicemanager) need to access bootstrap
100 // APEXes from the default mount namespace. To achieve that, we bind-mount
101 // /apex with /bootstrap-apex (not private) in the bootstrap mount namespace.
102 // Bootstrap APEXes are mounted in /apex and also visible in /bootstrap-apex.
103 // In the default mount namespace, we detach /bootstrap-apex from /apex and
104 // bootstrap APEXes are still be visible in /bootstrap-apex.
105 //
106 // The end result will look like:
107 // in the bootstrap mount namespace:
108 // /apex (== /bootstrap-apex)
109 // {bootstrap APEXes from the read-only partition}
110 //
111 // in the default mount namespace:
112 // /bootstrap-apex
113 // {bootstrap APEXes from the read-only partition}
114 // /apex
115 // {APEXes, can be from /data partition}
116 if (NeedsTwoMountNamespaces()) {
117 if (!(BindMount("/bootstrap-apex", "/apex"))) return false;
118 }
119
Kiyoung Kim99df54b2019-11-22 16:14:10 +0900120 // /linkerconfig is a private mountpoint to give a different linker configuration
121 // based on the mount namespace. Subdirectory will be bind-mounted based on current mount
122 // namespace
Elliott Hughese79b8c22020-07-28 11:09:03 -0700123 if (!(ChangeMount("/linkerconfig", MS_PRIVATE))) return false;
Kiyoung Kim99df54b2019-11-22 16:14:10 +0900124
Martijn Coenenc70c0662020-01-10 15:42:15 +0100125 // The two mount namespaces present challenges for scoped storage, because
126 // vold, which is responsible for most of the mounting, lives in the
127 // bootstrap mount namespace, whereas most other daemons and all apps live
128 // in the default namespace. Scoped storage has a need for a
129 // /mnt/installer view that is a slave bind mount of /mnt/user - in other
130 // words, all mounts under /mnt/user should automatically show up under
131 // /mnt/installer. However, additional mounts done under /mnt/installer
132 // should not propagate back to /mnt/user. In a single mount namespace
133 // this is easy to achieve, by simply marking the /mnt/installer a slave
134 // bind mount. Unfortunately, if /mnt/installer is only created and
135 // bind mounted after the two namespaces are created below, we end up
136 // with the following situation:
137 // /mnt/user and /mnt/installer share the same peer group in both the
138 // bootstrap and default namespaces. Marking /mnt/installer slave in either
139 // namespace means that it won't propagate events to the /mnt/installer in
140 // the other namespace, which is still something we require - vold is the
141 // one doing the mounting under /mnt/installer, and those mounts should
142 // show up in the default namespace as well.
143 //
144 // The simplest solution is to do the bind mount before the two namespaces
145 // are created: the effect is that in both namespaces, /mnt/installer is a
146 // slave to the /mnt/user mount, and at the same time /mnt/installer in the
147 // bootstrap namespace shares a peer group with /mnt/installer in the
148 // default namespace.
Ricky Waia4c163d2020-04-21 12:16:43 +0100149 // /mnt/androidwritable is similar to /mnt/installer but serves for
150 // MOUNT_EXTERNAL_ANDROID_WRITABLE apps.
Martijn Coenenc70c0662020-01-10 15:42:15 +0100151 if (!mkdir_recursive("/mnt/user", 0755)) return false;
152 if (!mkdir_recursive("/mnt/installer", 0755)) return false;
Ricky Waia4c163d2020-04-21 12:16:43 +0100153 if (!mkdir_recursive("/mnt/androidwritable", 0755)) return false;
Elliott Hughese79b8c22020-07-28 11:09:03 -0700154 if (!(BindMount("/mnt/user", "/mnt/installer"))) return false;
155 if (!(BindMount("/mnt/user", "/mnt/androidwritable"))) return false;
Ricky Waia4c163d2020-04-21 12:16:43 +0100156 // First, make /mnt/installer and /mnt/androidwritable a slave bind mount
Elliott Hughese79b8c22020-07-28 11:09:03 -0700157 if (!(ChangeMount("/mnt/installer", MS_SLAVE))) return false;
158 if (!(ChangeMount("/mnt/androidwritable", MS_SLAVE))) return false;
Martijn Coenenc70c0662020-01-10 15:42:15 +0100159 // Then, make it shared again - effectively creating a new peer group, that
160 // will be inherited by new mount namespaces.
Elliott Hughese79b8c22020-07-28 11:09:03 -0700161 if (!(ChangeMount("/mnt/installer", MS_SHARED))) return false;
162 if (!(ChangeMount("/mnt/androidwritable", MS_SHARED))) return false;
Martijn Coenenc70c0662020-01-10 15:42:15 +0100163
Jiyong Park68660412019-01-16 23:00:59 +0900164 bootstrap_ns_fd.reset(OpenMountNamespace());
165 bootstrap_ns_id = GetMountNamespaceId();
166
Jiyong Park7b4801a2019-02-25 16:41:38 +0900167 // When APEXes are updatable (e.g. not-flattened), we create separate mount
Jiyong Park68660412019-01-16 23:00:59 +0900168 // namespaces for processes that are started before and after the APEX is
Jiyong Park7b4801a2019-02-25 16:41:38 +0900169 // activated by apexd. In the namespace for pre-apexd processes, small
170 // number of essential APEXes (e.g. com.android.runtime) are activated.
171 // In the namespace for post-apexd processes, all APEXes are activated.
Jiyong Park68660412019-01-16 23:00:59 +0900172 bool success = true;
Jooyung Han653b0632021-07-29 17:11:23 +0900173 if (NeedsTwoMountNamespaces()) {
Jiyong Park68660412019-01-16 23:00:59 +0900174 // Creating a new namespace by cloning, saving, and switching back to
175 // the original namespace.
176 if (unshare(CLONE_NEWNS) == -1) {
177 PLOG(ERROR) << "Cannot create mount namespace";
178 return false;
179 }
180 default_ns_fd.reset(OpenMountNamespace());
181 default_ns_id = GetMountNamespaceId();
182
Jiyong Park68660412019-01-16 23:00:59 +0900183 if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) {
184 PLOG(ERROR) << "Cannot switch back to bootstrap mount namespace";
185 return false;
186 }
187 } else {
188 // Otherwise, default == bootstrap
189 default_ns_fd.reset(OpenMountNamespace());
190 default_ns_id = GetMountNamespaceId();
191 }
Shikha Malhotra720694d2021-07-30 12:35:27 +0000192
Jiyong Park68660412019-01-16 23:00:59 +0900193 LOG(INFO) << "SetupMountNamespaces done";
194 return success;
195}
196
Jooyung Han5eb441c2022-07-23 01:41:18 +0900197// Switch the mount namespace of the current process from bootstrap to default OR from default to
198// bootstrap. If the current mount namespace is neither bootstrap nor default, keep it that way.
Jooyung Han4f23d5a2020-06-09 13:44:17 +0900199Result<void> SwitchToMountNamespaceIfNeeded(MountNamespace target_mount_namespace) {
Jooyung Han918971c2023-06-23 14:16:31 +0900200 if (IsRecoveryMode()) {
Jooyung Han4f23d5a2020-06-09 13:44:17 +0900201 // we don't have multiple namespaces in recovery mode or if apex is not updatable
202 return {};
Jiyong Park68660412019-01-16 23:00:59 +0900203 }
Jooyung Han5eb441c2022-07-23 01:41:18 +0900204
205 const std::string current_namespace_id = GetMountNamespaceId();
206 MountNamespace current_mount_namespace;
207 if (current_namespace_id == bootstrap_ns_id) {
208 current_mount_namespace = NS_BOOTSTRAP;
209 } else if (current_namespace_id == default_ns_id) {
210 current_mount_namespace = NS_DEFAULT;
211 } else {
212 // services with `namespace mnt` start in its own mount namespace. So we need to keep it.
213 return {};
214 }
215
216 // We're already in the target mount namespace.
217 if (current_mount_namespace == target_mount_namespace) {
218 return {};
219 }
220
Jooyung Han4f23d5a2020-06-09 13:44:17 +0900221 const auto& ns_fd = target_mount_namespace == NS_BOOTSTRAP ? bootstrap_ns_fd : default_ns_fd;
222 const auto& ns_name = target_mount_namespace == NS_BOOTSTRAP ? "bootstrap" : "default";
Jooyung Han5eb441c2022-07-23 01:41:18 +0900223 if (ns_fd.get() != -1) {
Jooyung Han4f23d5a2020-06-09 13:44:17 +0900224 if (setns(ns_fd.get(), CLONE_NEWNS) == -1) {
225 return ErrnoError() << "Failed to switch to " << ns_name << " mount namespace.";
Kiyoung Kime4d3f212019-12-16 14:31:04 +0900226 }
Jiyong Park68660412019-01-16 23:00:59 +0900227 }
Jooyung Han4f23d5a2020-06-09 13:44:17 +0900228 return {};
Jiyong Park68660412019-01-16 23:00:59 +0900229}
230
Kiyoung Kim0cbee0d2021-03-02 16:45:27 +0900231base::Result<MountNamespace> GetCurrentMountNamespace() {
232 std::string current_namespace_id = GetMountNamespaceId();
233 if (current_namespace_id == "") {
234 return Error() << "Failed to get current mount namespace ID";
235 }
236
237 if (current_namespace_id == bootstrap_ns_id) {
238 return NS_BOOTSTRAP;
239 } else if (current_namespace_id == default_ns_id) {
240 return NS_DEFAULT;
241 }
242
243 return Error() << "Failed to find current mount namespace";
244}
245
Jiyong Park68660412019-01-16 23:00:59 +0900246} // namespace init
247} // namespace android