1 /*
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 <errno.h>
18 #include <getopt.h>
19 #include <stdio.h>
20 #include <sys/mount.h>
21 #include <sys/types.h>
22 #include <sys/vfs.h>
23 #include <unistd.h>
24
25 #include <string>
26 #include <thread>
27 #include <utility>
28 #include <vector>
29
30 #include <android-base/file.h>
31 #include <android-base/logging.h>
32 #include <android-base/properties.h>
33 #include <android-base/strings.h>
34 #include <android/os/IVold.h>
35 #include <binder/IServiceManager.h>
36 #include <bootloader_message/bootloader_message.h>
37 #include <cutils/android_reboot.h>
38 #include <fec/io.h>
39 #include <fs_mgr_overlayfs.h>
40 #include <fs_mgr_priv.h>
41 #include <fstab/fstab.h>
42 #include <libavb_user/libavb_user.h>
43 #include <libgsi/libgsid.h>
44
45 namespace {
46
usage(int exit_status)47 [[noreturn]] void usage(int exit_status) {
48 LOG(INFO) << getprogname()
49 << " [-h] [-R] [-T fstab_file] [partition]...\n"
50 "\t-h --help\tthis help\n"
51 "\t-R --reboot\tdisable verity & reboot to facilitate remount\n"
52 "\t-T --fstab\tcustom fstab file location\n"
53 "\tpartition\tspecific partition(s) (empty does all)\n"
54 "\n"
55 "Remount specified partition(s) read-write, by name or mount point.\n"
56 "-R notwithstanding, verity must be disabled on partition(s).\n"
57 "-R within a DSU guest system reboots into the DSU instead of the host system,\n"
58 "this command would enable DSU (one-shot) if not already enabled.";
59
60 ::exit(exit_status);
61 }
62
remountable_partition(const android::fs_mgr::FstabEntry & entry)63 bool remountable_partition(const android::fs_mgr::FstabEntry& entry) {
64 if (entry.fs_mgr_flags.vold_managed) return false;
65 if (entry.fs_mgr_flags.recovery_only) return false;
66 if (entry.fs_mgr_flags.slot_select_other) return false;
67 if (!(entry.flags & MS_RDONLY)) return false;
68 if (entry.fs_type == "vfat") return false;
69 return true;
70 }
71
system_mount_point(const android::fs_mgr::FstabEntry & entry)72 const std::string system_mount_point(const android::fs_mgr::FstabEntry& entry) {
73 if (entry.mount_point == "/") return "/system";
74 return entry.mount_point;
75 }
76
is_wrapped(const android::fs_mgr::Fstab & overlayfs_candidates,const android::fs_mgr::FstabEntry & entry)77 const android::fs_mgr::FstabEntry* is_wrapped(const android::fs_mgr::Fstab& overlayfs_candidates,
78 const android::fs_mgr::FstabEntry& entry) {
79 auto mount_point = system_mount_point(entry);
80 auto it = std::find_if(overlayfs_candidates.begin(), overlayfs_candidates.end(),
81 [&mount_point](const auto& entry) {
82 return android::base::StartsWith(mount_point,
83 system_mount_point(entry) + "/");
84 });
85 if (it == overlayfs_candidates.end()) return nullptr;
86 return &(*it);
87 }
88
89 auto verbose = false;
90
MyLogger(android::base::LogId id,android::base::LogSeverity severity,const char * tag,const char * file,unsigned int line,const char * message)91 void MyLogger(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
92 const char* file, unsigned int line, const char* message) {
93 if (verbose || severity == android::base::ERROR || message[0] != '[') {
94 fprintf(stderr, "%s\n", message);
95 }
96 static auto logd = android::base::LogdLogger();
97 logd(id, severity, tag, file, line, message);
98 }
99
reboot(bool overlayfs=false)100 [[noreturn]] void reboot(bool overlayfs = false) {
101 if (overlayfs) {
102 LOG(INFO) << "Successfully setup overlayfs\nrebooting device";
103 } else {
104 LOG(INFO) << "Successfully disabled verity\nrebooting device";
105 }
106 ::sync();
107 android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,remount");
108 ::sleep(60);
109 ::exit(0); // SUCCESS
110 }
111
GetVold()112 static android::sp<android::os::IVold> GetVold() {
113 while (true) {
114 if (auto sm = android::defaultServiceManager()) {
115 if (auto binder = sm->getService(android::String16("vold"))) {
116 if (auto vold = android::interface_cast<android::os::IVold>(binder)) {
117 return vold;
118 }
119 }
120 }
121 std::this_thread::sleep_for(2s);
122 }
123 }
124
125 } // namespace
126
127 using namespace std::chrono_literals;
128
129 enum RemountStatus {
130 REMOUNT_SUCCESS = 0,
131 NOT_USERDEBUG,
132 BADARG,
133 NOT_ROOT,
134 NO_FSTAB,
135 UNKNOWN_PARTITION,
136 INVALID_PARTITION,
137 VERITY_PARTITION,
138 BAD_OVERLAY,
139 NO_MOUNTS,
140 REMOUNT_FAILED,
141 MUST_REBOOT,
142 BINDER_ERROR,
143 CHECKPOINTING,
144 GSID_ERROR,
145 };
146
do_remount(int argc,char * argv[])147 static int do_remount(int argc, char* argv[]) {
148 RemountStatus retval = REMOUNT_SUCCESS;
149
150 // If somehow this executable is delivered on a "user" build, it can
151 // not function, so providing a clear message to the caller rather than
152 // letting if fall through and provide a lot of confusing failure messages.
153 if (!ALLOW_ADBD_DISABLE_VERITY || (android::base::GetProperty("ro.debuggable", "0") != "1")) {
154 LOG(ERROR) << "only functions on userdebug or eng builds";
155 return NOT_USERDEBUG;
156 }
157
158 const char* fstab_file = nullptr;
159 auto can_reboot = false;
160
161 struct option longopts[] = {
162 {"fstab", required_argument, nullptr, 'T'},
163 {"help", no_argument, nullptr, 'h'},
164 {"reboot", no_argument, nullptr, 'R'},
165 {"verbose", no_argument, nullptr, 'v'},
166 {0, 0, nullptr, 0},
167 };
168 for (int opt; (opt = ::getopt_long(argc, argv, "hRT:v", longopts, nullptr)) != -1;) {
169 switch (opt) {
170 case 'h':
171 usage(SUCCESS);
172 break;
173 case 'R':
174 can_reboot = true;
175 break;
176 case 'T':
177 if (fstab_file) {
178 LOG(ERROR) << "Cannot supply two fstabs: -T " << fstab_file << " -T" << optarg;
179 usage(BADARG);
180 }
181 fstab_file = optarg;
182 break;
183 case 'v':
184 verbose = true;
185 break;
186 default:
187 LOG(ERROR) << "Bad Argument -" << char(opt);
188 usage(BADARG);
189 break;
190 }
191 }
192
193 // Make sure we are root.
194 if (::getuid() != 0) {
195 LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
196 return NOT_ROOT;
197 }
198
199 // Read the selected fstab.
200 android::fs_mgr::Fstab fstab;
201 auto fstab_read = false;
202 if (fstab_file) {
203 fstab_read = android::fs_mgr::ReadFstabFromFile(fstab_file, &fstab);
204 } else {
205 fstab_read = android::fs_mgr::ReadDefaultFstab(&fstab);
206 // Manufacture a / entry from /proc/mounts if missing.
207 if (!GetEntryForMountPoint(&fstab, "/system") && !GetEntryForMountPoint(&fstab, "/")) {
208 android::fs_mgr::Fstab mounts;
209 if (android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts)) {
210 if (auto entry = GetEntryForMountPoint(&mounts, "/")) {
211 if (entry->fs_type != "rootfs") fstab.emplace_back(*entry);
212 }
213 }
214 }
215 }
216 if (!fstab_read || fstab.empty()) {
217 PLOG(ERROR) << "Failed to read fstab";
218 return NO_FSTAB;
219 }
220
221 if (android::base::GetBoolProperty("ro.virtual_ab.enabled", false) &&
222 !android::base::GetBoolProperty("ro.virtual_ab.retrofit", false)) {
223 // Virtual A/B devices can use /data as backing storage; make sure we're
224 // not checkpointing.
225 auto vold = GetVold();
226 bool checkpointing = false;
227 if (!vold->isCheckpointing(&checkpointing).isOk()) {
228 LOG(ERROR) << "Could not determine checkpointing status.";
229 return BINDER_ERROR;
230 }
231 if (checkpointing) {
232 LOG(ERROR) << "Cannot use remount when a checkpoint is in progress.";
233 return CHECKPOINTING;
234 }
235 }
236
237 // Generate the list of supported overlayfs mount points.
238 auto overlayfs_candidates = fs_mgr_overlayfs_candidate_list(fstab);
239
240 // Generate the all remountable partitions sub-list
241 android::fs_mgr::Fstab all;
242 for (auto const& entry : fstab) {
243 if (!remountable_partition(entry)) continue;
244 if (overlayfs_candidates.empty() ||
245 GetEntryForMountPoint(&overlayfs_candidates, entry.mount_point) ||
246 (is_wrapped(overlayfs_candidates, entry) == nullptr)) {
247 all.emplace_back(entry);
248 }
249 }
250
251 // Parse the unique list of valid partition arguments.
252 android::fs_mgr::Fstab partitions;
253 for (; argc > optind; ++optind) {
254 auto partition = std::string(argv[optind]);
255 if (partition.empty()) continue;
256 if (partition == "/") partition = "/system";
257 auto find_part = [&partition](const auto& entry) {
258 const auto mount_point = system_mount_point(entry);
259 if (partition == mount_point) return true;
260 if (partition == android::base::Basename(mount_point)) return true;
261 return false;
262 };
263 // Do we know about the partition?
264 auto it = std::find_if(fstab.begin(), fstab.end(), find_part);
265 if (it == fstab.end()) {
266 LOG(ERROR) << "Unknown partition " << argv[optind] << ", skipping";
267 retval = UNKNOWN_PARTITION;
268 continue;
269 }
270 // Is that one covered by an existing overlayfs?
271 auto wrap = is_wrapped(overlayfs_candidates, *it);
272 if (wrap) {
273 LOG(INFO) << "partition " << argv[optind] << " covered by overlayfs for "
274 << wrap->mount_point << ", switching";
275 partition = system_mount_point(*wrap);
276 }
277 // Is it a remountable partition?
278 it = std::find_if(all.begin(), all.end(), find_part);
279 if (it == all.end()) {
280 LOG(ERROR) << "Invalid partition " << argv[optind] << ", skipping";
281 retval = INVALID_PARTITION;
282 continue;
283 }
284 if (GetEntryForMountPoint(&partitions, it->mount_point) == nullptr) {
285 partitions.emplace_back(*it);
286 }
287 }
288
289 if (partitions.empty() && !retval) {
290 partitions = all;
291 }
292
293 // Check verity and optionally setup overlayfs backing.
294 auto reboot_later = false;
295 auto user_please_reboot_later = false;
296 auto setup_overlayfs = false;
297 auto just_disabled_verity = false;
298 for (auto it = partitions.begin(); it != partitions.end();) {
299 auto& entry = *it;
300 auto& mount_point = entry.mount_point;
301 if (fs_mgr_is_verity_enabled(entry)) {
302 retval = VERITY_PARTITION;
303 auto ret = false;
304 if (android::base::GetProperty("ro.boot.vbmeta.device_state", "") != "locked") {
305 if (AvbOps* ops = avb_ops_user_new()) {
306 ret = avb_user_verity_set(
307 ops, android::base::GetProperty("ro.boot.slot_suffix", "").c_str(),
308 false);
309 avb_ops_user_free(ops);
310 }
311 if (!ret && fs_mgr_set_blk_ro(entry.blk_device, false)) {
312 fec::io fh(entry.blk_device.c_str(), O_RDWR);
313 ret = fh && fh.set_verity_status(false);
314 }
315 if (ret) {
316 LOG(WARNING) << "Disabling verity for " << mount_point;
317 just_disabled_verity = true;
318 reboot_later = can_reboot;
319 user_please_reboot_later = true;
320 }
321 }
322 if (!ret) {
323 LOG(ERROR) << "Skipping " << mount_point << " for remount";
324 it = partitions.erase(it);
325 continue;
326 }
327 }
328
329 auto change = false;
330 errno = 0;
331 if (fs_mgr_overlayfs_setup(nullptr, mount_point.c_str(), &change, just_disabled_verity)) {
332 if (change) {
333 LOG(INFO) << "Using overlayfs for " << mount_point;
334 reboot_later = can_reboot;
335 user_please_reboot_later = true;
336 setup_overlayfs = true;
337 }
338 } else if (errno) {
339 PLOG(ERROR) << "Overlayfs setup for " << mount_point << " failed, skipping";
340 retval = BAD_OVERLAY;
341 it = partitions.erase(it);
342 continue;
343 }
344 ++it;
345 }
346
347 // If (1) remount requires a reboot to take effect, (2) system is currently
348 // running a DSU guest and (3) DSU is disabled, then enable DSU so that the
349 // next reboot would not take us back to the host system but stay within
350 // the guest system.
351 if (reboot_later) {
352 if (auto gsid = android::gsi::GetGsiService()) {
353 auto dsu_running = false;
354 if (auto status = gsid->isGsiRunning(&dsu_running); !status.isOk()) {
355 LOG(ERROR) << "Failed to get DSU running state: " << status;
356 return BINDER_ERROR;
357 }
358 auto dsu_enabled = false;
359 if (auto status = gsid->isGsiEnabled(&dsu_enabled); !status.isOk()) {
360 LOG(ERROR) << "Failed to get DSU enabled state: " << status;
361 return BINDER_ERROR;
362 }
363 if (dsu_running && !dsu_enabled) {
364 std::string dsu_slot;
365 if (auto status = gsid->getActiveDsuSlot(&dsu_slot); !status.isOk()) {
366 LOG(ERROR) << "Failed to get active DSU slot: " << status;
367 return BINDER_ERROR;
368 }
369 LOG(INFO) << "DSU is running but disabled, enable DSU so that we stay within the "
370 "DSU guest system after reboot";
371 int error = 0;
372 if (auto status = gsid->enableGsi(/* oneShot = */ true, dsu_slot, &error);
373 !status.isOk() || error != android::gsi::IGsiService::INSTALL_OK) {
374 LOG(ERROR) << "Failed to enable DSU: " << status << ", error code: " << error;
375 return !status.isOk() ? BINDER_ERROR : GSID_ERROR;
376 }
377 LOG(INFO) << "Successfully enabled DSU (one-shot mode)";
378 }
379 }
380 }
381
382 if (partitions.empty() || just_disabled_verity) {
383 if (reboot_later) reboot(setup_overlayfs);
384 if (user_please_reboot_later) {
385 return MUST_REBOOT;
386 }
387 LOG(WARNING) << "No partitions to remount";
388 return retval;
389 }
390
391 // Mount overlayfs.
392 errno = 0;
393 if (!fs_mgr_overlayfs_mount_all(&partitions) && errno) {
394 retval = BAD_OVERLAY;
395 PLOG(ERROR) << "Can not mount overlayfs for partitions";
396 }
397
398 // Get actual mounts _after_ overlayfs has been added.
399 android::fs_mgr::Fstab mounts;
400 if (!android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts) || mounts.empty()) {
401 PLOG(ERROR) << "Failed to read /proc/mounts";
402 retval = NO_MOUNTS;
403 }
404
405 // Remount selected partitions.
406 for (auto& entry : partitions) {
407 // unlock the r/o key for the mount point device
408 if (entry.fs_mgr_flags.logical) {
409 fs_mgr_update_logical_partition(&entry);
410 }
411 auto blk_device = entry.blk_device;
412 auto mount_point = entry.mount_point;
413
414 auto found = false;
415 for (auto it = mounts.rbegin(); it != mounts.rend(); ++it) {
416 auto& rentry = *it;
417 if (mount_point == rentry.mount_point) {
418 blk_device = rentry.blk_device;
419 found = true;
420 break;
421 }
422 // Find overlayfs mount point?
423 if ((mount_point == "/") && (rentry.mount_point == "/system")) {
424 blk_device = rentry.blk_device;
425 mount_point = "/system";
426 found = true;
427 break;
428 }
429 }
430 if (!found) {
431 PLOG(INFO) << "skip unmounted partition dev:" << blk_device << " mnt:" << mount_point;
432 continue;
433 }
434 if (blk_device == "/dev/root") {
435 auto from_fstab = GetEntryForMountPoint(&fstab, mount_point);
436 if (from_fstab) blk_device = from_fstab->blk_device;
437 }
438 fs_mgr_set_blk_ro(blk_device, false);
439
440 // Find system-as-root mount point?
441 if ((mount_point == "/system") && !GetEntryForMountPoint(&mounts, mount_point) &&
442 GetEntryForMountPoint(&mounts, "/")) {
443 mount_point = "/";
444 }
445
446 // Now remount!
447 if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
448 nullptr) == 0) {
449 continue;
450 }
451 if ((errno == EINVAL) && (mount_point != entry.mount_point)) {
452 mount_point = entry.mount_point;
453 if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
454 nullptr) == 0) {
455 continue;
456 }
457 }
458 PLOG(ERROR) << "failed to remount partition dev:" << blk_device << " mnt:" << mount_point;
459 // If errno is EROFS at this point, we are dealing with r/o
460 // filesystem types like squashfs, erofs or ext4 dedupe. We will
461 // consider such a device that does not have CONFIG_OVERLAY_FS
462 // in the kernel as a misconfigured.
463 if (errno == EROFS) {
464 LOG(ERROR) << "Consider providing all the dependencies to enable overlayfs";
465 }
466 retval = REMOUNT_FAILED;
467 }
468
469 if (reboot_later) reboot(setup_overlayfs);
470 if (user_please_reboot_later) {
471 LOG(INFO) << "Now reboot your device for settings to take effect";
472 return 0;
473 }
474
475 return retval;
476 }
477
main(int argc,char * argv[])478 int main(int argc, char* argv[]) {
479 android::base::InitLogging(argv, MyLogger);
480 int result = do_remount(argc, argv);
481 if (result == MUST_REBOOT) {
482 LOG(INFO) << "Now reboot your device for settings to take effect";
483 } else if (result == REMOUNT_SUCCESS) {
484 printf("remount succeeded\n");
485 } else {
486 printf("remount failed\n");
487 }
488 return result;
489 }
490