1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/mozapps/update/updater/automounter_gonk.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,224 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include <android/log.h> 1.11 +#include <cutils/android_reboot.h> 1.12 +#include <errno.h> 1.13 +#include <stdlib.h> 1.14 +#include <sys/mount.h> 1.15 +#include <sys/reboot.h> 1.16 +#include <sys/types.h> 1.17 +#include <unistd.h> 1.18 + 1.19 +#include "automounter_gonk.h" 1.20 +#include "updatedefines.h" 1.21 +#include "updatelogging.h" 1.22 + 1.23 +#define LOG_TAG "GonkAutoMounter" 1.24 + 1.25 +#define GONK_LOG(level, format, ...) \ 1.26 + LOG((LOG_TAG ": " format "\n", ##__VA_ARGS__)); \ 1.27 + __android_log_print(level, LOG_TAG, format, ##__VA_ARGS__) 1.28 + 1.29 +#define LOGI(format, ...) GONK_LOG(ANDROID_LOG_INFO, format, ##__VA_ARGS__) 1.30 +#define LOGE(format, ...) GONK_LOG(ANDROID_LOG_ERROR, format, ##__VA_ARGS__) 1.31 + 1.32 +const char *kGonkMountsPath = "/proc/mounts"; 1.33 +const char *kGonkSystemPath = "/system"; 1.34 + 1.35 +GonkAutoMounter::GonkAutoMounter() : mDevice(nullptr), mAccess(Unknown) 1.36 +{ 1.37 + if (!RemountSystem(ReadWrite)) { 1.38 + LOGE("Could not remount %s as read-write.", kGonkSystemPath); 1.39 + } 1.40 +} 1.41 + 1.42 +GonkAutoMounter::~GonkAutoMounter() 1.43 +{ 1.44 + bool result = RemountSystem(ReadOnly); 1.45 + free(mDevice); 1.46 + 1.47 + if (!result) { 1.48 + // Don't take any chances when remounting as read-only fails, just reboot. 1.49 + Reboot(); 1.50 + } 1.51 +} 1.52 + 1.53 +void 1.54 +GonkAutoMounter::Reboot() 1.55 +{ 1.56 + // The android_reboot wrapper provides more safety, doing fancier read-only 1.57 + // remounting and attempting to sync() the filesystem first. If this fails 1.58 + // our only hope is to force a reboot directly without these protections. 1.59 + // For more, see system/core/libcutils/android_reboot.c 1.60 + LOGE("Could not remount %s as read-only, forcing a system reboot.", 1.61 + kGonkSystemPath); 1.62 + LogFlush(); 1.63 + 1.64 + if (android_reboot(ANDROID_RB_RESTART, 0, nullptr) != 0) { 1.65 + LOGE("Safe system reboot failed, attempting to force"); 1.66 + LogFlush(); 1.67 + 1.68 + if (reboot(RB_AUTOBOOT) != 0) { 1.69 + LOGE("CRITICAL: Failed to force restart"); 1.70 + } 1.71 + } 1.72 +} 1.73 + 1.74 +static const char * 1.75 +MountAccessToString(MountAccess access) 1.76 +{ 1.77 + switch (access) { 1.78 + case ReadOnly: return "read-only"; 1.79 + case ReadWrite: return "read-write"; 1.80 + default: return "unknown"; 1.81 + } 1.82 +} 1.83 + 1.84 +bool 1.85 +GonkAutoMounter::RemountSystem(MountAccess access) 1.86 +{ 1.87 + if (!UpdateMountStatus()) { 1.88 + return false; 1.89 + } 1.90 + 1.91 + if (mAccess == access) { 1.92 + return true; 1.93 + } 1.94 + 1.95 + unsigned long flags = MS_REMOUNT; 1.96 + if (access == ReadOnly) { 1.97 + flags |= MS_RDONLY; 1.98 + // Give the system a chance to flush file buffers 1.99 + sync(); 1.100 + } 1.101 + 1.102 + if (!MountSystem(flags)) { 1.103 + return false; 1.104 + } 1.105 + 1.106 + // Check status again to verify /system has been properly remounted 1.107 + if (!UpdateMountStatus()) { 1.108 + return false; 1.109 + } 1.110 + 1.111 + if (mAccess != access) { 1.112 + LOGE("Updated mount status %s should be %s", 1.113 + MountAccessToString(mAccess), 1.114 + MountAccessToString(access)); 1.115 + return false; 1.116 + } 1.117 + 1.118 + return true; 1.119 +} 1.120 + 1.121 +bool 1.122 +GonkAutoMounter::UpdateMountStatus() 1.123 +{ 1.124 + FILE *mountsFile = NS_tfopen(kGonkMountsPath, "r"); 1.125 + 1.126 + if (mountsFile == nullptr) { 1.127 + LOGE("Error opening %s: %s", kGonkMountsPath, strerror(errno)); 1.128 + return false; 1.129 + } 1.130 + 1.131 + // /proc/mounts returns a 0 size from fstat, so we use the same 1.132 + // pre-allocated buffer size that ADB does here 1.133 + const int mountsMaxSize = 4096; 1.134 + char mountData[mountsMaxSize]; 1.135 + size_t read = fread(mountData, 1, mountsMaxSize - 1, mountsFile); 1.136 + mountData[read + 1] = '\0'; 1.137 + 1.138 + if (ferror(mountsFile)) { 1.139 + LOGE("Error reading %s, %s", kGonkMountsPath, strerror(errno)); 1.140 + fclose(mountsFile); 1.141 + return false; 1.142 + } 1.143 + 1.144 + char *token, *tokenContext; 1.145 + bool foundSystem = false; 1.146 + 1.147 + for (token = strtok_r(mountData, "\n", &tokenContext); 1.148 + token; 1.149 + token = strtok_r(nullptr, "\n", &tokenContext)) 1.150 + { 1.151 + if (ProcessMount(token)) { 1.152 + foundSystem = true; 1.153 + break; 1.154 + } 1.155 + } 1.156 + 1.157 + fclose(mountsFile); 1.158 + 1.159 + if (!foundSystem) { 1.160 + LOGE("Couldn't find %s mount in %s", kGonkSystemPath, kGonkMountsPath); 1.161 + } 1.162 + return foundSystem; 1.163 +} 1.164 + 1.165 +bool 1.166 +GonkAutoMounter::ProcessMount(const char *mount) 1.167 +{ 1.168 + const int strSize = 256; 1.169 + char mountDev[strSize]; 1.170 + char mountDir[strSize]; 1.171 + char mountAccess[strSize]; 1.172 + 1.173 + int rv = sscanf(mount, "%255s %255s %*s %255s %*d %*d\n", 1.174 + mountDev, mountDir, mountAccess); 1.175 + mountDev[strSize - 1] = '\0'; 1.176 + mountDir[strSize - 1] = '\0'; 1.177 + mountAccess[strSize - 1] = '\0'; 1.178 + 1.179 + if (rv != 3) { 1.180 + return false; 1.181 + } 1.182 + 1.183 + if (strcmp(kGonkSystemPath, mountDir) != 0) { 1.184 + return false; 1.185 + } 1.186 + 1.187 + free(mDevice); 1.188 + mDevice = strdup(mountDev); 1.189 + mAccess = Unknown; 1.190 + 1.191 + char *option, *optionContext; 1.192 + for (option = strtok_r(mountAccess, ",", &optionContext); 1.193 + option; 1.194 + option = strtok_r(nullptr, ",", &optionContext)) 1.195 + { 1.196 + if (strcmp("ro", option) == 0) { 1.197 + mAccess = ReadOnly; 1.198 + break; 1.199 + } else if (strcmp("rw", option) == 0) { 1.200 + mAccess = ReadWrite; 1.201 + break; 1.202 + } 1.203 + } 1.204 + 1.205 + return true; 1.206 +} 1.207 + 1.208 +bool 1.209 +GonkAutoMounter::MountSystem(unsigned long flags) 1.210 +{ 1.211 + if (!mDevice) { 1.212 + LOGE("No device was found for %s", kGonkSystemPath); 1.213 + return false; 1.214 + } 1.215 + 1.216 + const char *readOnly = flags & MS_RDONLY ? "read-only" : "read-write"; 1.217 + int result = mount(mDevice, kGonkSystemPath, "none", flags, nullptr); 1.218 + 1.219 + if (result != 0) { 1.220 + LOGE("Error mounting %s as %s: %s", kGonkSystemPath, readOnly, 1.221 + strerror(errno)); 1.222 + return false; 1.223 + } 1.224 + 1.225 + LOGI("Mounted %s partition as %s", kGonkSystemPath, readOnly); 1.226 + return true; 1.227 +}