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