michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "automounter_gonk.h" michael@0: #include "updatedefines.h" michael@0: #include "updatelogging.h" michael@0: michael@0: #define LOG_TAG "GonkAutoMounter" michael@0: michael@0: #define GONK_LOG(level, format, ...) \ michael@0: LOG((LOG_TAG ": " format "\n", ##__VA_ARGS__)); \ michael@0: __android_log_print(level, LOG_TAG, format, ##__VA_ARGS__) michael@0: michael@0: #define LOGI(format, ...) GONK_LOG(ANDROID_LOG_INFO, format, ##__VA_ARGS__) michael@0: #define LOGE(format, ...) GONK_LOG(ANDROID_LOG_ERROR, format, ##__VA_ARGS__) michael@0: michael@0: const char *kGonkMountsPath = "/proc/mounts"; michael@0: const char *kGonkSystemPath = "/system"; michael@0: michael@0: GonkAutoMounter::GonkAutoMounter() : mDevice(nullptr), mAccess(Unknown) michael@0: { michael@0: if (!RemountSystem(ReadWrite)) { michael@0: LOGE("Could not remount %s as read-write.", kGonkSystemPath); michael@0: } michael@0: } michael@0: michael@0: GonkAutoMounter::~GonkAutoMounter() michael@0: { michael@0: bool result = RemountSystem(ReadOnly); michael@0: free(mDevice); michael@0: michael@0: if (!result) { michael@0: // Don't take any chances when remounting as read-only fails, just reboot. michael@0: Reboot(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: GonkAutoMounter::Reboot() michael@0: { michael@0: // The android_reboot wrapper provides more safety, doing fancier read-only michael@0: // remounting and attempting to sync() the filesystem first. If this fails michael@0: // our only hope is to force a reboot directly without these protections. michael@0: // For more, see system/core/libcutils/android_reboot.c michael@0: LOGE("Could not remount %s as read-only, forcing a system reboot.", michael@0: kGonkSystemPath); michael@0: LogFlush(); michael@0: michael@0: if (android_reboot(ANDROID_RB_RESTART, 0, nullptr) != 0) { michael@0: LOGE("Safe system reboot failed, attempting to force"); michael@0: LogFlush(); michael@0: michael@0: if (reboot(RB_AUTOBOOT) != 0) { michael@0: LOGE("CRITICAL: Failed to force restart"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: static const char * michael@0: MountAccessToString(MountAccess access) michael@0: { michael@0: switch (access) { michael@0: case ReadOnly: return "read-only"; michael@0: case ReadWrite: return "read-write"; michael@0: default: return "unknown"; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: GonkAutoMounter::RemountSystem(MountAccess access) michael@0: { michael@0: if (!UpdateMountStatus()) { michael@0: return false; michael@0: } michael@0: michael@0: if (mAccess == access) { michael@0: return true; michael@0: } michael@0: michael@0: unsigned long flags = MS_REMOUNT; michael@0: if (access == ReadOnly) { michael@0: flags |= MS_RDONLY; michael@0: // Give the system a chance to flush file buffers michael@0: sync(); michael@0: } michael@0: michael@0: if (!MountSystem(flags)) { michael@0: return false; michael@0: } michael@0: michael@0: // Check status again to verify /system has been properly remounted michael@0: if (!UpdateMountStatus()) { michael@0: return false; michael@0: } michael@0: michael@0: if (mAccess != access) { michael@0: LOGE("Updated mount status %s should be %s", michael@0: MountAccessToString(mAccess), michael@0: MountAccessToString(access)); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: GonkAutoMounter::UpdateMountStatus() michael@0: { michael@0: FILE *mountsFile = NS_tfopen(kGonkMountsPath, "r"); michael@0: michael@0: if (mountsFile == nullptr) { michael@0: LOGE("Error opening %s: %s", kGonkMountsPath, strerror(errno)); michael@0: return false; michael@0: } michael@0: michael@0: // /proc/mounts returns a 0 size from fstat, so we use the same michael@0: // pre-allocated buffer size that ADB does here michael@0: const int mountsMaxSize = 4096; michael@0: char mountData[mountsMaxSize]; michael@0: size_t read = fread(mountData, 1, mountsMaxSize - 1, mountsFile); michael@0: mountData[read + 1] = '\0'; michael@0: michael@0: if (ferror(mountsFile)) { michael@0: LOGE("Error reading %s, %s", kGonkMountsPath, strerror(errno)); michael@0: fclose(mountsFile); michael@0: return false; michael@0: } michael@0: michael@0: char *token, *tokenContext; michael@0: bool foundSystem = false; michael@0: michael@0: for (token = strtok_r(mountData, "\n", &tokenContext); michael@0: token; michael@0: token = strtok_r(nullptr, "\n", &tokenContext)) michael@0: { michael@0: if (ProcessMount(token)) { michael@0: foundSystem = true; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: fclose(mountsFile); michael@0: michael@0: if (!foundSystem) { michael@0: LOGE("Couldn't find %s mount in %s", kGonkSystemPath, kGonkMountsPath); michael@0: } michael@0: return foundSystem; michael@0: } michael@0: michael@0: bool michael@0: GonkAutoMounter::ProcessMount(const char *mount) michael@0: { michael@0: const int strSize = 256; michael@0: char mountDev[strSize]; michael@0: char mountDir[strSize]; michael@0: char mountAccess[strSize]; michael@0: michael@0: int rv = sscanf(mount, "%255s %255s %*s %255s %*d %*d\n", michael@0: mountDev, mountDir, mountAccess); michael@0: mountDev[strSize - 1] = '\0'; michael@0: mountDir[strSize - 1] = '\0'; michael@0: mountAccess[strSize - 1] = '\0'; michael@0: michael@0: if (rv != 3) { michael@0: return false; michael@0: } michael@0: michael@0: if (strcmp(kGonkSystemPath, mountDir) != 0) { michael@0: return false; michael@0: } michael@0: michael@0: free(mDevice); michael@0: mDevice = strdup(mountDev); michael@0: mAccess = Unknown; michael@0: michael@0: char *option, *optionContext; michael@0: for (option = strtok_r(mountAccess, ",", &optionContext); michael@0: option; michael@0: option = strtok_r(nullptr, ",", &optionContext)) michael@0: { michael@0: if (strcmp("ro", option) == 0) { michael@0: mAccess = ReadOnly; michael@0: break; michael@0: } else if (strcmp("rw", option) == 0) { michael@0: mAccess = ReadWrite; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: GonkAutoMounter::MountSystem(unsigned long flags) michael@0: { michael@0: if (!mDevice) { michael@0: LOGE("No device was found for %s", kGonkSystemPath); michael@0: return false; michael@0: } michael@0: michael@0: const char *readOnly = flags & MS_RDONLY ? "read-only" : "read-write"; michael@0: int result = mount(mDevice, kGonkSystemPath, "none", flags, nullptr); michael@0: michael@0: if (result != 0) { michael@0: LOGE("Error mounting %s as %s: %s", kGonkSystemPath, readOnly, michael@0: strerror(errno)); michael@0: return false; michael@0: } michael@0: michael@0: LOGI("Mounted %s partition as %s", kGonkSystemPath, readOnly); michael@0: return true; michael@0: }