blob: 0745148bd8403ac0954766a47f72a254c815607e [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
Jiyong Park8502ed32019-02-25 22:18:37 +090024#include <ApexProperties.sysprop.h>
Jiyong Park68660412019-01-16 23:00:59 +090025#include <android-base/file.h>
26#include <android-base/logging.h>
27#include <android-base/properties.h>
28#include <android-base/unique_fd.h>
29
30#include "util.h"
31
32namespace android {
33namespace init {
34namespace {
35
Jiyong Park68660412019-01-16 23:00:59 +090036static bool MakeShared(const std::string& mount_point, bool recursive = false) {
37 unsigned long mountflags = MS_SHARED;
38 if (recursive) {
39 mountflags |= MS_REC;
40 }
41 if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
42 PLOG(ERROR) << "Failed to change propagation type to shared";
43 return false;
44 }
45 return true;
46}
47
48static bool MakePrivate(const std::string& mount_point, bool recursive = false) {
49 unsigned long mountflags = MS_PRIVATE;
50 if (recursive) {
51 mountflags |= MS_REC;
52 }
53 if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
54 PLOG(ERROR) << "Failed to change propagation type to private";
55 return false;
56 }
57 return true;
58}
59
60static int OpenMountNamespace() {
61 int fd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
62 if (fd < 0) {
63 PLOG(ERROR) << "Cannot open fd for current mount namespace";
64 }
65 return fd;
66}
67
68static std::string GetMountNamespaceId() {
69 std::string ret;
70 if (!android::base::Readlink("/proc/self/ns/mnt", &ret)) {
71 PLOG(ERROR) << "Failed to read namespace ID";
72 return "";
73 }
74 return ret;
75}
76
Jiyong Park8502ed32019-02-25 22:18:37 +090077static bool IsApexUpdatable() {
78 static bool updatable = android::sysprop::ApexProperties::updatable().value_or(false);
79 return updatable;
Jiyong Park68660412019-01-16 23:00:59 +090080}
81
Jiyong Parkd7f7c202019-05-10 21:12:15 +090082static bool ActivateFlattenedApexesIfPossible() {
83 if (IsRecoveryMode() || IsApexUpdatable()) {
84 return true;
85 }
86
87 constexpr const char kSystemApex[] = "/system/apex";
88 constexpr const char kApexTop[] = "/apex";
89 if (mount(kSystemApex, kApexTop, nullptr, MS_BIND, nullptr) != 0) {
90 PLOG(ERROR) << "Could not bind mount " << kSystemApex << " to " << kApexTop;
91 return false;
92 }
93
Martin Stjernholmdf96e1f2019-07-17 22:17:58 +010094 // Special casing for the ART APEX
95 constexpr const char kArtApexMountPath[] = "/system/apex/com.android.art";
96 static const std::vector<std::string> kArtApexDirNames = {"com.android.art.release",
97 "com.android.art.debug"};
Jiyong Parkd7f7c202019-05-10 21:12:15 +090098 bool success = false;
Martin Stjernholmdf96e1f2019-07-17 22:17:58 +010099 for (const auto& name : kArtApexDirNames) {
Jiyong Parkd7f7c202019-05-10 21:12:15 +0900100 std::string path = std::string(kSystemApex) + "/" + name;
101 if (access(path.c_str(), F_OK) == 0) {
Martin Stjernholmdf96e1f2019-07-17 22:17:58 +0100102 if (mount(path.c_str(), kArtApexMountPath, nullptr, MS_BIND, nullptr) == 0) {
Jiyong Parkd7f7c202019-05-10 21:12:15 +0900103 success = true;
104 break;
105 }
106 }
107 }
108 if (!success) {
Martin Stjernholmdf96e1f2019-07-17 22:17:58 +0100109 PLOG(ERROR) << "Failed to bind mount the ART APEX to " << kArtApexMountPath;
Jiyong Parkd7f7c202019-05-10 21:12:15 +0900110 }
111 return success;
112}
113
Jiyong Park68660412019-01-16 23:00:59 +0900114static android::base::unique_fd bootstrap_ns_fd;
115static android::base::unique_fd default_ns_fd;
116
117static std::string bootstrap_ns_id;
118static std::string default_ns_id;
119
120} // namespace
121
122bool SetupMountNamespaces() {
123 // Set the propagation type of / as shared so that any mounting event (e.g.
124 // /data) is by default visible to all processes. When private mounting is
125 // needed for /foo/bar, then we will make /foo/bar as a mount point (by
126 // bind-mounting by to itself) and set the propagation type of the mount
127 // point to private.
128 if (!MakeShared("/", true /*recursive*/)) return false;
129
Jiyong Park7b4801a2019-02-25 16:41:38 +0900130 // /apex is a private mountpoint to give different sets of APEXes for
Jiyong Parkdcbaf9f2019-02-22 22:15:25 +0900131 // the bootstrap and default mount namespaces. The processes running with
132 // the bootstrap namespace get APEXes from the read-only partition.
133 if (!(MakePrivate("/apex"))) return false;
134
Jiyong Park68660412019-01-16 23:00:59 +0900135 bootstrap_ns_fd.reset(OpenMountNamespace());
136 bootstrap_ns_id = GetMountNamespaceId();
137
Jiyong Park7b4801a2019-02-25 16:41:38 +0900138 // When APEXes are updatable (e.g. not-flattened), we create separate mount
Jiyong Park68660412019-01-16 23:00:59 +0900139 // namespaces for processes that are started before and after the APEX is
Jiyong Park7b4801a2019-02-25 16:41:38 +0900140 // activated by apexd. In the namespace for pre-apexd processes, small
141 // number of essential APEXes (e.g. com.android.runtime) are activated.
142 // In the namespace for post-apexd processes, all APEXes are activated.
Jiyong Park68660412019-01-16 23:00:59 +0900143 bool success = true;
Jiyong Park8502ed32019-02-25 22:18:37 +0900144 if (IsApexUpdatable() && !IsRecoveryMode()) {
Jiyong Park68660412019-01-16 23:00:59 +0900145 // Creating a new namespace by cloning, saving, and switching back to
146 // the original namespace.
147 if (unshare(CLONE_NEWNS) == -1) {
148 PLOG(ERROR) << "Cannot create mount namespace";
149 return false;
150 }
151 default_ns_fd.reset(OpenMountNamespace());
152 default_ns_id = GetMountNamespaceId();
153
Jiyong Park68660412019-01-16 23:00:59 +0900154 if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) {
155 PLOG(ERROR) << "Cannot switch back to bootstrap mount namespace";
156 return false;
157 }
158 } else {
159 // Otherwise, default == bootstrap
160 default_ns_fd.reset(OpenMountNamespace());
161 default_ns_id = GetMountNamespaceId();
162 }
163
Jiyong Parkd7f7c202019-05-10 21:12:15 +0900164 success &= ActivateFlattenedApexesIfPossible();
165
Jiyong Park68660412019-01-16 23:00:59 +0900166 LOG(INFO) << "SetupMountNamespaces done";
167 return success;
168}
169
170bool SwitchToDefaultMountNamespace() {
171 if (IsRecoveryMode()) {
172 // we don't have multiple namespaces in recovery mode
173 return true;
174 }
175 if (default_ns_id != GetMountNamespaceId()) {
176 if (setns(default_ns_fd.get(), CLONE_NEWNS) == -1) {
177 PLOG(ERROR) << "Failed to switch back to the default mount namespace.";
178 return false;
179 }
180 }
181
Jiyong Parkdcbaf9f2019-02-22 22:15:25 +0900182 LOG(INFO) << "Switched to default mount namespace";
183 return true;
184}
185
Jiyong Park68660412019-01-16 23:00:59 +0900186bool SwitchToBootstrapMountNamespaceIfNeeded() {
187 if (IsRecoveryMode()) {
188 // we don't have multiple namespaces in recovery mode
189 return true;
190 }
191 if (bootstrap_ns_id != GetMountNamespaceId() && bootstrap_ns_fd.get() != -1 &&
Jiyong Park8502ed32019-02-25 22:18:37 +0900192 IsApexUpdatable()) {
Jiyong Park68660412019-01-16 23:00:59 +0900193 if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) {
194 PLOG(ERROR) << "Failed to switch to bootstrap mount namespace.";
195 return false;
196 }
197 }
198 return true;
199}
200
201} // namespace init
202} // namespace android