12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247 |
- /*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include "gsi_service.h"
- #include <errno.h>
- #include <linux/fs.h>
- #include <sys/ioctl.h>
- #include <sys/stat.h>
- #include <sys/statvfs.h>
- #include <sys/types.h>
- #include <sys/vfs.h>
- #include <unistd.h>
- #include <chrono>
- #include <string>
- #include <vector>
- #include <android-base/file.h>
- #include <android-base/logging.h>
- #include <android-base/stringprintf.h>
- #include <android-base/strings.h>
- #include <android/gsi/IGsiService.h>
- #include <ext4_utils/ext4_utils.h>
- #include <fs_mgr.h>
- #include <fs_mgr_dm_linear.h>
- #include <fstab/fstab.h>
- #include <libdm/dm.h>
- #include <libfiemap_writer/fiemap_writer.h>
- #include <logwrap/logwrap.h>
- #include <private/android_filesystem_config.h>
- #include "file_paths.h"
- #include "libgsi_private.h"
- namespace android {
- namespace gsi {
- using namespace std::literals;
- using namespace android::dm;
- using namespace android::fs_mgr;
- using namespace android::fiemap_writer;
- using android::base::StringPrintf;
- using android::base::unique_fd;
- static constexpr char kUserdataDevice[] = "/dev/block/by-name/userdata";
- // The default size of userdata.img for GSI.
- // We are looking for /data to have atleast 40% free space
- static constexpr uint32_t kMinimumFreeSpaceThreshold = 40;
- // We determine the fragmentation by making sure the files
- // we create don't have more than 16 extents.
- static constexpr uint32_t kMaximumExtents = 512;
- // Default userdata image size.
- static constexpr int64_t kDefaultUserdataSize = int64_t(8) * 1024 * 1024 * 1024;
- static constexpr std::chrono::milliseconds kDmTimeout = 5000ms;
- void GsiService::Register() {
- auto ret = android::BinderService<GsiService>::publish();
- if (ret != android::OK) {
- LOG(FATAL) << "Could not register gsi service: " << ret;
- }
- }
- GsiService::GsiService() {
- progress_ = {};
- }
- GsiService::~GsiService() {
- PostInstallCleanup();
- }
- #define ENFORCE_SYSTEM \
- do { \
- binder::Status status = CheckUid(); \
- if (!status.isOk()) return status; \
- } while (0)
- #define ENFORCE_SYSTEM_OR_SHELL \
- do { \
- binder::Status status = CheckUid(AccessLevel::SystemOrShell); \
- if (!status.isOk()) return status; \
- } while (0)
- binder::Status GsiService::startGsiInstall(int64_t gsiSize, int64_t userdataSize, bool wipeUserdata,
- int* _aidl_return) {
- GsiInstallParams params;
- params.gsiSize = gsiSize;
- params.userdataSize = userdataSize;
- params.wipeUserdata = wipeUserdata;
- return beginGsiInstall(params, _aidl_return);
- }
- binder::Status GsiService::beginGsiInstall(const GsiInstallParams& given_params, int* _aidl_return) {
- ENFORCE_SYSTEM;
- std::lock_guard<std::mutex> guard(main_lock_);
- // Make sure any interrupted installations are cleaned up.
- PostInstallCleanup();
- // Do some precursor validation on the arguments before diving into the
- // install process.
- GsiInstallParams params = given_params;
- if (int status = ValidateInstallParams(¶ms)) {
- *_aidl_return = status;
- return binder::Status::ok();
- }
- int status = StartInstall(params);
- if (status != INSTALL_OK) {
- // Perform local cleanup and delete any lingering files.
- PostInstallCleanup();
- RemoveGsiFiles(params.installDir, wipe_userdata_on_failure_);
- }
- *_aidl_return = status;
- // Clear the progress indicator.
- UpdateProgress(STATUS_NO_OPERATION, 0);
- return binder::Status::ok();
- }
- binder::Status GsiService::commitGsiChunkFromStream(const android::os::ParcelFileDescriptor& stream,
- int64_t bytes, bool* _aidl_return) {
- ENFORCE_SYSTEM;
- std::lock_guard<std::mutex> guard(main_lock_);
- *_aidl_return = CommitGsiChunk(stream.get(), bytes);
- // Clear the progress indicator.
- UpdateProgress(STATUS_NO_OPERATION, 0);
- return binder::Status::ok();
- }
- void GsiService::StartAsyncOperation(const std::string& step, int64_t total_bytes) {
- std::lock_guard<std::mutex> guard(progress_lock_);
- progress_.step = step;
- progress_.status = STATUS_WORKING;
- progress_.bytes_processed = 0;
- progress_.total_bytes = total_bytes;
- }
- void GsiService::UpdateProgress(int status, int64_t bytes_processed) {
- std::lock_guard<std::mutex> guard(progress_lock_);
- progress_.status = status;
- if (status == STATUS_COMPLETE) {
- progress_.bytes_processed = progress_.total_bytes;
- } else {
- progress_.bytes_processed = bytes_processed;
- }
- }
- binder::Status GsiService::getInstallProgress(::android::gsi::GsiProgress* _aidl_return) {
- ENFORCE_SYSTEM;
- std::lock_guard<std::mutex> guard(progress_lock_);
- *_aidl_return = progress_;
- return binder::Status::ok();
- }
- binder::Status GsiService::commitGsiChunkFromMemory(const std::vector<uint8_t>& bytes,
- bool* _aidl_return) {
- ENFORCE_SYSTEM;
- std::lock_guard<std::mutex> guard(main_lock_);
- *_aidl_return = CommitGsiChunk(bytes.data(), bytes.size());
- return binder::Status::ok();
- }
- binder::Status GsiService::setGsiBootable(bool one_shot, int* _aidl_return) {
- std::lock_guard<std::mutex> guard(main_lock_);
- if (installing_) {
- ENFORCE_SYSTEM;
- int error = SetGsiBootable(one_shot);
- PostInstallCleanup();
- if (error) {
- RemoveGsiFiles(install_dir_, wipe_userdata_on_failure_);
- *_aidl_return = error;
- } else {
- *_aidl_return = INSTALL_OK;
- }
- } else {
- ENFORCE_SYSTEM_OR_SHELL;
- *_aidl_return = ReenableGsi(one_shot);
- PostInstallCleanup();
- }
- return binder::Status::ok();
- }
- binder::Status GsiService::isGsiEnabled(bool* _aidl_return) {
- ENFORCE_SYSTEM_OR_SHELL;
- std::lock_guard<std::mutex> guard(main_lock_);
- std::string boot_key;
- if (!GetInstallStatus(&boot_key)) {
- *_aidl_return = false;
- } else {
- *_aidl_return = (boot_key == kInstallStatusOk);
- }
- return binder::Status::ok();
- }
- binder::Status GsiService::removeGsiInstall(bool* _aidl_return) {
- ENFORCE_SYSTEM_OR_SHELL;
- std::lock_guard<std::mutex> guard(main_lock_);
- // Just in case an install was left hanging.
- std::string install_dir;
- if (installing_) {
- install_dir = install_dir_;
- PostInstallCleanup();
- } else {
- install_dir = GetInstalledImageDir();
- }
- if (IsGsiRunning()) {
- // Can't remove gsi files while running.
- *_aidl_return = UninstallGsi();
- } else {
- *_aidl_return = RemoveGsiFiles(install_dir, true /* wipeUserdata */);
- }
- return binder::Status::ok();
- }
- binder::Status GsiService::disableGsiInstall(bool* _aidl_return) {
- ENFORCE_SYSTEM_OR_SHELL;
- std::lock_guard<std::mutex> guard(main_lock_);
- *_aidl_return = DisableGsiInstall();
- return binder::Status::ok();
- }
- binder::Status GsiService::isGsiRunning(bool* _aidl_return) {
- ENFORCE_SYSTEM_OR_SHELL;
- std::lock_guard<std::mutex> guard(main_lock_);
- *_aidl_return = IsGsiRunning();
- return binder::Status::ok();
- }
- binder::Status GsiService::isGsiInstalled(bool* _aidl_return) {
- ENFORCE_SYSTEM_OR_SHELL;
- std::lock_guard<std::mutex> guard(main_lock_);
- *_aidl_return = IsGsiInstalled();
- return binder::Status::ok();
- }
- binder::Status GsiService::isGsiInstallInProgress(bool* _aidl_return) {
- ENFORCE_SYSTEM_OR_SHELL;
- std::lock_guard<std::mutex> guard(main_lock_);
- *_aidl_return = installing_;
- return binder::Status::ok();
- }
- binder::Status GsiService::cancelGsiInstall(bool* _aidl_return) {
- ENFORCE_SYSTEM;
- should_abort_ = true;
- std::lock_guard<std::mutex> guard(main_lock_);
- should_abort_ = false;
- if (installing_) {
- PostInstallCleanup();
- RemoveGsiFiles(install_dir_, wipe_userdata_on_failure_);
- }
- *_aidl_return = true;
- return binder::Status::ok();
- }
- binder::Status GsiService::getGsiBootStatus(int* _aidl_return) {
- ENFORCE_SYSTEM_OR_SHELL;
- std::lock_guard<std::mutex> guard(main_lock_);
- if (!IsGsiInstalled()) {
- *_aidl_return = BOOT_STATUS_NOT_INSTALLED;
- return binder::Status::ok();
- }
- std::string boot_key;
- if (!GetInstallStatus(&boot_key)) {
- PLOG(ERROR) << "read " << kGsiInstallStatusFile;
- *_aidl_return = BOOT_STATUS_NOT_INSTALLED;
- return binder::Status::ok();
- }
- bool single_boot = !access(kGsiOneShotBootFile, F_OK);
- if (boot_key == kInstallStatusWipe) {
- // This overrides all other statuses.
- *_aidl_return = BOOT_STATUS_WILL_WIPE;
- } else if (boot_key == kInstallStatusDisabled) {
- // A single-boot GSI will have a "disabled" status, because it's
- // disabled immediately upon reading the one_shot_boot file. However,
- // we still want to return SINGLE_BOOT, because it makes the
- // transition clearer to the user.
- if (single_boot) {
- *_aidl_return = BOOT_STATUS_SINGLE_BOOT;
- } else {
- *_aidl_return = BOOT_STATUS_DISABLED;
- }
- } else if (single_boot) {
- *_aidl_return = BOOT_STATUS_SINGLE_BOOT;
- } else {
- *_aidl_return = BOOT_STATUS_ENABLED;
- }
- return binder::Status::ok();
- }
- binder::Status GsiService::getUserdataImageSize(int64_t* _aidl_return) {
- ENFORCE_SYSTEM;
- std::lock_guard<std::mutex> guard(main_lock_);
- *_aidl_return = -1;
- if (installing_) {
- // Size has already been computed.
- *_aidl_return = userdata_size_;
- } else if (IsGsiRunning()) {
- // :TODO: libdm
- unique_fd fd(open(kUserdataDevice, O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
- if (fd < 0) {
- PLOG(ERROR) << "open " << kUserdataDevice;
- return binder::Status::ok();
- }
- int64_t size;
- if (ioctl(fd, BLKGETSIZE64, &size)) {
- PLOG(ERROR) << "BLKGETSIZE64 " << kUserdataDevice;
- return binder::Status::ok();
- }
- *_aidl_return = size;
- } else {
- // Stat the size of the userdata file.
- auto userdata_gsi = GetInstalledImagePath("userdata_gsi");
- struct stat s;
- if (stat(userdata_gsi.c_str(), &s)) {
- if (errno != ENOENT) {
- PLOG(ERROR) << "open " << userdata_gsi;
- return binder::Status::ok();
- }
- *_aidl_return = 0;
- } else {
- *_aidl_return = s.st_size;
- }
- }
- return binder::Status::ok();
- }
- binder::Status GsiService::getInstalledGsiImageDir(std::string* _aidl_return) {
- ENFORCE_SYSTEM;
- std::lock_guard<std::mutex> guard(main_lock_);
- if (IsGsiInstalled()) {
- *_aidl_return = GetInstalledImageDir();
- }
- return binder::Status::ok();
- }
- binder::Status GsiService::wipeGsiUserdata(int* _aidl_return) {
- ENFORCE_SYSTEM_OR_SHELL;
- std::lock_guard<std::mutex> guard(main_lock_);
- if (IsGsiRunning() || !IsGsiInstalled()) {
- *_aidl_return = IGsiService::INSTALL_ERROR_GENERIC;
- return binder::Status::ok();
- }
- *_aidl_return = WipeUserdata();
- return binder::Status::ok();
- }
- binder::Status GsiService::CheckUid(AccessLevel level) {
- std::vector<uid_t> allowed_uids{AID_ROOT, AID_SYSTEM};
- if (level == AccessLevel::SystemOrShell) {
- allowed_uids.push_back(AID_SHELL);
- }
- uid_t uid = IPCThreadState::self()->getCallingUid();
- for (const auto& allowed_uid : allowed_uids) {
- if (allowed_uid == uid) {
- return binder::Status::ok();
- }
- }
- auto message = StringPrintf("UID %d is not allowed", uid);
- return binder::Status::fromExceptionCode(binder::Status::EX_SECURITY,
- String8(message.c_str()));
- }
- void GsiService::PostInstallCleanup() {
- // This must be closed before unmapping partitions.
- system_writer_ = nullptr;
- const auto& dm = DeviceMapper::Instance();
- if (dm.GetState("userdata_gsi") != DmDeviceState::INVALID) {
- DestroyLogicalPartition("userdata_gsi", kDmTimeout);
- }
- if (dm.GetState("system_gsi") != DmDeviceState::INVALID) {
- DestroyLogicalPartition("system_gsi", kDmTimeout);
- }
- installing_ = false;
- partitions_ .clear();
- }
- static bool IsExternalStoragePath(const std::string& path) {
- if (!android::base::StartsWith(path, "/mnt/media_rw/")) {
- return false;
- }
- unique_fd fd(open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
- if (fd < 0) {
- PLOG(ERROR) << "open failed: " << path;
- return false;
- }
- struct statfs info;
- if (fstatfs(fd, &info)) {
- PLOG(ERROR) << "statfs failed: " << path;
- return false;
- }
- LOG(ERROR) << "fs type: " << info.f_type;
- return info.f_type == MSDOS_SUPER_MAGIC;
- }
- int GsiService::ValidateInstallParams(GsiInstallParams* params) {
- // If no install path was specified, use the default path. We also allow
- // specifying the top-level folder, and then we choose the correct location
- // underneath.
- if (params->installDir.empty() || params->installDir == "/data/gsi") {
- params->installDir = kDefaultGsiImageFolder;
- }
- // Normalize the path and add a trailing slash.
- std::string origInstallDir = params->installDir;
- if (!android::base::Realpath(origInstallDir, ¶ms->installDir)) {
- PLOG(ERROR) << "realpath failed: " << origInstallDir;
- return INSTALL_ERROR_GENERIC;
- }
- // Ensure the path ends in / for consistency. Even though GetImagePath()
- // does this already, we want it to appear this way in install_dir.
- if (!android::base::EndsWith(params->installDir, "/")) {
- params->installDir += "/";
- }
- // Currently, we can only install to /data/gsi/ or external storage.
- if (IsExternalStoragePath(params->installDir)) {
- Fstab fstab;
- if (!ReadDefaultFstab(&fstab)) {
- LOG(ERROR) << "cannot read default fstab";
- return INSTALL_ERROR_GENERIC;
- }
- FstabEntry* system = GetEntryForMountPoint(&fstab, "/system");
- if (!system) {
- LOG(ERROR) << "cannot find /system fstab entry";
- return INSTALL_ERROR_GENERIC;
- }
- if (fs_mgr_verity_is_check_at_most_once(*system)) {
- LOG(ERROR) << "cannot install GSIs to external media if verity uses check_at_most_once";
- return INSTALL_ERROR_GENERIC;
- }
- } else if (params->installDir != kDefaultGsiImageFolder) {
- LOG(ERROR) << "cannot install GSI to " << params->installDir;
- return INSTALL_ERROR_GENERIC;
- }
- if (params->gsiSize % LP_SECTOR_SIZE) {
- LOG(ERROR) << "GSI size " << params->gsiSize << " is not a multiple of " << LP_SECTOR_SIZE;
- return INSTALL_ERROR_GENERIC;
- }
- if (params->userdataSize % LP_SECTOR_SIZE) {
- LOG(ERROR) << "userdata size " << params->userdataSize << " is not a multiple of "
- << LP_SECTOR_SIZE;
- return INSTALL_ERROR_GENERIC;
- }
- return INSTALL_OK;
- }
- int GsiService::StartInstall(const GsiInstallParams& params) {
- installing_ = true;
- userdata_block_size_ = 0;
- system_block_size_ = 0;
- gsi_size_ = params.gsiSize;
- userdata_size_ = (params.userdataSize) ? params.userdataSize : kDefaultUserdataSize;
- wipe_userdata_ = params.wipeUserdata;
- can_use_devicemapper_ = false;
- gsi_bytes_written_ = 0;
- install_dir_ = params.installDir;
- userdata_gsi_path_ = GetImagePath(install_dir_, "userdata_gsi");
- system_gsi_path_ = GetImagePath(install_dir_, "system_gsi");
- // Only rm userdata_gsi if one didn't already exist.
- wipe_userdata_on_failure_ = wipe_userdata_ || access(userdata_gsi_path_.c_str(), F_OK);
- if (int status = PerformSanityChecks()) {
- return status;
- }
- if (int status = PreallocateFiles()) {
- return status;
- }
- if (int status = DetermineReadWriteMethod()) {
- return status;
- }
- if (!FormatUserdata()) {
- return INSTALL_ERROR_GENERIC;
- }
- // Map system_gsi so we can write to it.
- system_writer_ = OpenPartition("system_gsi");
- if (!system_writer_) {
- return INSTALL_ERROR_GENERIC;
- }
- return INSTALL_OK;
- }
- int GsiService::DetermineReadWriteMethod() {
- // If there is a device-mapper node wrapping the block device, then we're
- // able to create another node around it; the dm layer does not carry the
- // exclusion lock down the stack when a mount occurs.
- //
- // If there is no intermediate device-mapper node, then partitions cannot be
- // opened writable due to sepolicy and exclusivity of having a mounted
- // filesystem. This should only happen on devices with no encryption, or
- // devices with FBE and no metadata encryption. For these cases it suffices
- // to perform normal file writes to /data/gsi (which is unencrypted).
- std::string block_device;
- if (!FiemapWriter::GetBlockDeviceForFile(system_gsi_path_.c_str(), &block_device,
- &can_use_devicemapper_)) {
- return INSTALL_ERROR_GENERIC;
- }
- if (install_dir_ != kDefaultGsiImageFolder && can_use_devicemapper_) {
- // Never use device-mapper on external media. We don't support adopted
- // storage yet, and accidentally using device-mapper could be dangerous
- // as we hardcode the userdata device as backing storage.
- LOG(ERROR) << "unexpected device-mapper node used to mount external media";
- return INSTALL_ERROR_GENERIC;
- }
- return INSTALL_OK;
- }
- std::string GsiService::GetImagePath(const std::string& image_dir, const std::string& name) {
- std::string dir = image_dir;
- if (!android::base::EndsWith(dir, "/")) {
- dir += "/";
- }
- return dir + name + ".img";
- }
- std::string GsiService::GetInstalledImageDir() {
- // If there's no install left, just return /data/gsi since that's where
- // installs go by default.
- std::string dir;
- if (android::base::ReadFileToString(kGsiInstallDirFile, &dir)) {
- return dir;
- }
- return kDefaultGsiImageFolder;
- }
- std::string GsiService::GetInstalledImagePath(const std::string& name) {
- return GetImagePath(GetInstalledImageDir(), name);
- }
- int GsiService::PerformSanityChecks() {
- if (gsi_size_ < 0) {
- LOG(ERROR) << "image size " << gsi_size_ << " is negative";
- return INSTALL_ERROR_GENERIC;
- }
- if (android::gsi::IsGsiRunning()) {
- LOG(ERROR) << "cannot install gsi inside a live gsi";
- return INSTALL_ERROR_GENERIC;
- }
- struct statvfs sb;
- if (statvfs(install_dir_.c_str(), &sb)) {
- PLOG(ERROR) << "failed to read file system stats";
- return INSTALL_ERROR_GENERIC;
- }
- // This is the same as android::vold::GetFreebytes() but we also
- // need the total file system size so we open code it here.
- uint64_t free_space = 1ULL * sb.f_bavail * sb.f_frsize;
- uint64_t fs_size = sb.f_blocks * sb.f_frsize;
- if (free_space <= (gsi_size_ + userdata_size_)) {
- LOG(ERROR) << "not enough free space (only " << free_space << " bytes available)";
- return INSTALL_ERROR_NO_SPACE;
- }
- // We are asking for 40% of the /data to be empty.
- // TODO: may be not hard code it like this
- double free_space_percent = ((1.0 * free_space) / fs_size) * 100;
- if (free_space_percent < kMinimumFreeSpaceThreshold) {
- LOG(ERROR) << "free space " << static_cast<uint64_t>(free_space_percent)
- << "% is below the minimum threshold of " << kMinimumFreeSpaceThreshold << "%";
- return INSTALL_ERROR_FILE_SYSTEM_CLUTTERED;
- }
- return INSTALL_OK;
- }
- int GsiService::PreallocateFiles() {
- if (wipe_userdata_) {
- SplitFiemap::RemoveSplitFiles(userdata_gsi_path_);
- }
- SplitFiemap::RemoveSplitFiles(system_gsi_path_);
- // TODO: trigger GC from fiemap writer.
- // Create fallocated files.
- if (int status = PreallocateUserdata()) {
- return status;
- }
- if (int status = PreallocateSystem()) {
- return status;
- }
- // Save the extent information in liblp.
- metadata_ = CreateMetadata();
- if (!metadata_) {
- return INSTALL_ERROR_GENERIC;
- }
- UpdateProgress(STATUS_COMPLETE, 0);
- return INSTALL_OK;
- }
- int GsiService::PreallocateUserdata() {
- int error;
- std::unique_ptr<SplitFiemap> userdata_image;
- if (wipe_userdata_ || access(userdata_gsi_path_.c_str(), F_OK)) {
- StartAsyncOperation("create userdata", userdata_size_);
- userdata_image = CreateFiemapWriter(userdata_gsi_path_, userdata_size_, &error);
- if (!userdata_image) {
- LOG(ERROR) << "Could not create userdata image: " << userdata_gsi_path_;
- return error;
- }
- // Signal that we need to reformat userdata.
- wipe_userdata_ = true;
- } else {
- userdata_image = CreateFiemapWriter(userdata_gsi_path_, 0, &error);
- if (!userdata_image) {
- LOG(ERROR) << "Could not open userdata image: " << userdata_gsi_path_;
- return error;
- }
- if (userdata_size_ && userdata_image->size() < userdata_size_) {
- // :TODO: need to fallocate more blocks and resizefs.
- }
- userdata_size_ = userdata_image->size();
- }
- userdata_block_size_ = userdata_image->block_size();
- Image image = {
- .writer = std::move(userdata_image),
- .actual_size = userdata_size_,
- };
- partitions_.emplace(std::make_pair("userdata_gsi", std::move(image)));
- return INSTALL_OK;
- }
- int GsiService::PreallocateSystem() {
- StartAsyncOperation("create system", gsi_size_);
- int error;
- auto system_image = CreateFiemapWriter(system_gsi_path_, gsi_size_, &error);
- if (!system_image) {
- return error;
- }
- system_block_size_ = system_image->block_size();
- Image image = {
- .writer = std::move(system_image),
- .actual_size = gsi_size_,
- };
- partitions_.emplace(std::make_pair("system_gsi", std::move(image)));
- return INSTALL_OK;
- }
- std::unique_ptr<SplitFiemap> GsiService::CreateFiemapWriter(const std::string& path,
- uint64_t size, int* error) {
- bool create = (size != 0);
- std::function<bool(uint64_t, uint64_t)> progress;
- if (create) {
- // TODO: allow cancelling inside cancelGsiInstall.
- progress = [this](uint64_t bytes, uint64_t /* total */) -> bool {
- UpdateProgress(STATUS_WORKING, bytes);
- if (should_abort_) return false;
- return true;
- };
- }
- std::unique_ptr<SplitFiemap> file;
- if (!size) {
- file = SplitFiemap::Open(path);
- } else {
- file = SplitFiemap::Create(path, size, 0, std::move(progress));
- }
- if (!file) {
- LOG(ERROR) << "failed to create or open " << path;
- *error = INSTALL_ERROR_GENERIC;
- return nullptr;
- }
- uint64_t extents = file->extents().size();
- if (extents > kMaximumExtents) {
- LOG(ERROR) << "file " << path << " has too many extents: " << extents;
- *error = INSTALL_ERROR_FILE_SYSTEM_CLUTTERED;
- return nullptr;
- }
- return file;
- }
- // Write data through an fd.
- class FdWriter final : public GsiService::WriteHelper {
- public:
- FdWriter(const std::string& path, unique_fd&& fd) : path_(path), fd_(std::move(fd)) {}
- bool Write(const void* data, uint64_t bytes) override {
- return android::base::WriteFully(fd_, data, bytes);
- }
- bool Flush() override {
- if (fsync(fd_)) {
- PLOG(ERROR) << "fsync failed: " << path_;
- return false;
- }
- return true;
- }
- uint64_t Size() override { return get_block_device_size(fd_); }
- private:
- std::string path_;
- unique_fd fd_;
- };
- // Write data through a SplitFiemap.
- class SplitFiemapWriter final : public GsiService::WriteHelper {
- public:
- explicit SplitFiemapWriter(SplitFiemap* writer) : writer_(writer) {}
- bool Write(const void* data, uint64_t bytes) override {
- return writer_->Write(data, bytes);
- }
- bool Flush() override {
- return writer_->Flush();
- }
- uint64_t Size() override { return writer_->size(); }
- private:
- SplitFiemap* writer_;
- };
- std::unique_ptr<GsiService::WriteHelper> GsiService::OpenPartition(const std::string& name) {
- if (can_use_devicemapper_) {
- std::string path;
- if (!CreateLogicalPartition(kUserdataDevice, *metadata_.get(), name, true, kDmTimeout,
- &path)) {
- LOG(ERROR) << "Error creating device-mapper node for " << name;
- return {};
- }
- static const int kOpenFlags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
- unique_fd fd(open(path.c_str(), kOpenFlags));
- if (fd < 0) {
- PLOG(ERROR) << "could not open " << path;
- }
- return std::make_unique<FdWriter>(GetImagePath(install_dir_, name), std::move(fd));
- }
- auto iter = partitions_.find(name);
- if (iter == partitions_.end()) {
- LOG(ERROR) << "could not find partition " << name;
- return {};
- }
- return std::make_unique<SplitFiemapWriter>(iter->second.writer.get());
- }
- bool GsiService::CommitGsiChunk(int stream_fd, int64_t bytes) {
- StartAsyncOperation("write gsi", gsi_size_);
- if (bytes < 0) {
- LOG(ERROR) << "chunk size " << bytes << " is negative";
- return false;
- }
- auto buffer = std::make_unique<char[]>(system_block_size_);
- int progress = -1;
- uint64_t remaining = bytes;
- while (remaining) {
- // :TODO: check file pin status!
- size_t max_to_read = std::min(system_block_size_, remaining);
- ssize_t rv = TEMP_FAILURE_RETRY(read(stream_fd, buffer.get(), max_to_read));
- if (rv < 0) {
- PLOG(ERROR) << "read gsi chunk";
- return false;
- }
- if (rv == 0) {
- LOG(ERROR) << "no bytes left in stream";
- return false;
- }
- if (!CommitGsiChunk(buffer.get(), rv)) {
- return false;
- }
- CHECK(static_cast<uint64_t>(rv) <= remaining);
- remaining -= rv;
- // Only update the progress when the % (or permille, in this case)
- // significantly changes.
- int new_progress = ((gsi_size_ - remaining) * 1000) / gsi_size_;
- if (new_progress != progress) {
- UpdateProgress(STATUS_WORKING, gsi_size_ - remaining);
- }
- }
- UpdateProgress(STATUS_COMPLETE, gsi_size_);
- return true;
- }
- bool GsiService::CommitGsiChunk(const void* data, size_t bytes) {
- if (!installing_) {
- LOG(ERROR) << "no gsi installation in progress";
- return false;
- }
- if (static_cast<uint64_t>(bytes) > gsi_size_ - gsi_bytes_written_) {
- // We cannot write past the end of the image file.
- LOG(ERROR) << "chunk size " << bytes << " exceeds remaining image size (" << gsi_size_
- << " expected, " << gsi_bytes_written_ << " written)";
- return false;
- }
- if (!system_writer_->Write(data, bytes)) {
- PLOG(ERROR) << "write failed";
- return false;
- }
- gsi_bytes_written_ += bytes;
- return true;
- }
- int GsiService::SetGsiBootable(bool one_shot) {
- if (gsi_bytes_written_ != gsi_size_) {
- // We cannot boot if the image is incomplete.
- LOG(ERROR) << "image incomplete; expected " << gsi_size_ << " bytes, waiting for "
- << (gsi_size_ - gsi_bytes_written_) << " bytes";
- return INSTALL_ERROR_GENERIC;
- }
- if (!system_writer_->Flush()) {
- return INSTALL_ERROR_GENERIC;
- }
- // If files moved (are no longer pinned), the metadata file will be invalid.
- for (const auto& [name, image] : partitions_) {
- if (!image.writer->HasPinnedExtents()) {
- LOG(ERROR) << name << " no longer has pinned extents";
- return INSTALL_ERROR_GENERIC;
- }
- }
- // Remember the installation directory.
- if (!android::base::WriteStringToFile(install_dir_, kGsiInstallDirFile)) {
- PLOG(ERROR) << "write failed: " << kGsiInstallDirFile;
- return INSTALL_ERROR_GENERIC;
- }
- // Note: create the install status file last, since this is the actual boot
- // indicator.
- if (!CreateMetadataFile() || !SetBootMode(one_shot) || !CreateInstallStatusFile()) {
- return INSTALL_ERROR_GENERIC;
- }
- return INSTALL_OK;
- }
- int GsiService::ReenableGsi(bool one_shot) {
- if (!android::gsi::IsGsiInstalled()) {
- LOG(ERROR) << "no gsi installed - cannot re-enable";
- return INSTALL_ERROR_GENERIC;
- }
- std::string boot_key;
- if (!GetInstallStatus(&boot_key)) {
- PLOG(ERROR) << "read " << kGsiInstallStatusFile;
- return INSTALL_ERROR_GENERIC;
- }
- if (boot_key != kInstallStatusDisabled) {
- LOG(ERROR) << "GSI is not currently disabled";
- return INSTALL_ERROR_GENERIC;
- }
- if (IsGsiRunning()) {
- if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) {
- return INSTALL_ERROR_GENERIC;
- }
- return INSTALL_OK;
- }
- // Note: this metadata is only used to recover the original partition sizes.
- // We do not trust the extent information, which will get rebuilt later.
- auto old_metadata = ReadFromImageFile(kGsiLpMetadataFile);
- if (!old_metadata) {
- LOG(ERROR) << "GSI install is incomplete";
- return INSTALL_ERROR_GENERIC;
- }
- // Set up enough installer state so that we can use various helper
- // methods.
- //
- // TODO(dvander) Extract all of the installer state into a separate
- // class so this is more manageable.
- install_dir_ = GetInstalledImageDir();
- system_gsi_path_ = GetImagePath(install_dir_, "system_gsi");
- if (int error = DetermineReadWriteMethod()) {
- return error;
- }
- // Recover parition information.
- Image userdata_image;
- if (int error = GetExistingImage(*old_metadata.get(), "userdata_gsi", &userdata_image)) {
- return error;
- }
- partitions_.emplace(std::make_pair("userdata_gsi", std::move(userdata_image)));
- Image system_image;
- if (int error = GetExistingImage(*old_metadata.get(), "system_gsi", &system_image)) {
- return error;
- }
- partitions_.emplace(std::make_pair("system_gsi", std::move(system_image)));
- metadata_ = CreateMetadata();
- if (!metadata_) {
- return INSTALL_ERROR_GENERIC;
- }
- if (!CreateMetadataFile() || !SetBootMode(one_shot) || !CreateInstallStatusFile()) {
- return INSTALL_ERROR_GENERIC;
- }
- return INSTALL_OK;
- }
- int GsiService::WipeUserdata() {
- // Note: this metadata is only used to recover the original partition sizes.
- // We do not trust the extent information, which will get rebuilt later.
- auto old_metadata = ReadFromImageFile(kGsiLpMetadataFile);
- if (!old_metadata) {
- LOG(ERROR) << "GSI install is incomplete";
- return INSTALL_ERROR_GENERIC;
- }
- install_dir_ = GetInstalledImageDir();
- system_gsi_path_ = GetImagePath(install_dir_, "system_gsi");
- if (int error = DetermineReadWriteMethod()) {
- return error;
- }
- // Recover parition information.
- Image userdata_image;
- if (int error = GetExistingImage(*old_metadata.get(), "userdata_gsi", &userdata_image)) {
- return error;
- }
- partitions_.emplace(std::make_pair("userdata_gsi", std::move(userdata_image)));
- metadata_ = CreateMetadata();
- if (!metadata_) {
- return INSTALL_ERROR_GENERIC;
- }
- auto writer = OpenPartition("userdata_gsi");
- if (!writer) {
- return IGsiService::INSTALL_ERROR_GENERIC;
- }
- // Wipe the first 1MiB of the device, ensuring both the first block and
- // the superblock are destroyed.
- static constexpr uint64_t kEraseSize = 1024 * 1024;
- std::string zeroes(4096, 0);
- uint64_t erase_size = std::min(kEraseSize, writer->Size());
- for (uint64_t i = 0; i < erase_size; i += zeroes.size()) {
- if (!writer->Write(zeroes.data(), zeroes.size())) {
- PLOG(ERROR) << "write userdata_gsi";
- return IGsiService::INSTALL_ERROR_GENERIC;
- }
- }
- return INSTALL_OK;
- }
- static uint64_t GetPartitionSize(const LpMetadata& metadata, const LpMetadataPartition& partition) {
- uint64_t total = 0;
- for (size_t i = 0; i < partition.num_extents; i++) {
- const auto& extent = metadata.extents[partition.first_extent_index + i];
- if (extent.target_type != LP_TARGET_TYPE_LINEAR) {
- LOG(ERROR) << "non-linear extent detected";
- return 0;
- }
- total += extent.num_sectors * LP_SECTOR_SIZE;
- }
- return total;
- }
- static uint64_t GetPartitionSize(const LpMetadata& metadata, const std::string& name) {
- for (const auto& partition : metadata.partitions) {
- if (GetPartitionName(partition) == name) {
- return GetPartitionSize(metadata, partition);
- }
- }
- return 0;
- }
- int GsiService::GetExistingImage(const LpMetadata& metadata, const std::string& name,
- Image* image) {
- int error;
- std::string path = GetInstalledImagePath(name);
- auto writer = CreateFiemapWriter(path.c_str(), 0, &error);
- if (!writer) {
- return error;
- }
- // Even after recovering the FIEMAP, we also need to know the exact intended
- // size of the image, since FiemapWriter may have extended the final block.
- uint64_t actual_size = GetPartitionSize(metadata, name);
- if (!actual_size) {
- LOG(ERROR) << "Could not determine the pre-existing size of " << name;
- return INSTALL_ERROR_GENERIC;
- }
- image->writer = std::move(writer);
- image->actual_size = actual_size;
- return INSTALL_OK;
- }
- bool GsiService::RemoveGsiFiles(const std::string& install_dir, bool wipeUserdata) {
- bool ok = true;
- std::string message;
- if (!SplitFiemap::RemoveSplitFiles(GetImagePath(install_dir, "system_gsi"), &message)) {
- LOG(ERROR) << message;
- ok = false;
- }
- if (wipeUserdata &&
- !SplitFiemap::RemoveSplitFiles(GetImagePath(install_dir, "userdata_gsi"), &message)) {
- LOG(ERROR) << message;
- ok = false;
- }
- std::vector<std::string> files{
- kGsiInstallStatusFile,
- kGsiLpMetadataFile,
- kGsiOneShotBootFile,
- kGsiInstallDirFile,
- };
- for (const auto& file : files) {
- if (!android::base::RemoveFileIfExists(file, &message)) {
- LOG(ERROR) << message;
- ok = false;
- }
- }
- return ok;
- }
- bool GsiService::DisableGsiInstall() {
- if (!android::gsi::IsGsiInstalled()) {
- LOG(ERROR) << "cannot disable gsi install - no install detected";
- return false;
- }
- if (installing_) {
- LOG(ERROR) << "cannot disable gsi during GSI installation";
- return false;
- }
- if (!DisableGsi()) {
- PLOG(ERROR) << "could not write gsi status";
- return false;
- }
- return true;
- }
- std::unique_ptr<LpMetadata> GsiService::CreateMetadata() {
- std::string data_device_path;
- if (install_dir_ == kDefaultGsiImageFolder && !access(kUserdataDevice, F_OK)) {
- data_device_path = kUserdataDevice;
- } else {
- auto writer = partitions_["system_gsi"].writer.get();
- data_device_path = writer->bdev_path();
- }
- auto data_device_name = android::base::Basename(data_device_path);
- PartitionOpener opener;
- BlockDeviceInfo data_device_info;
- if (!opener.GetInfo(data_device_path, &data_device_info)) {
- LOG(ERROR) << "Error reading userdata partition";
- return nullptr;
- }
- std::vector<BlockDeviceInfo> block_devices = {data_device_info};
- auto builder = MetadataBuilder::New(block_devices, data_device_name, 128 * 1024, 1);
- if (!builder) {
- LOG(ERROR) << "Error creating metadata builder";
- return nullptr;
- }
- builder->IgnoreSlotSuffixing();
- for (const auto& [name, image] : partitions_) {
- uint32_t flags = LP_PARTITION_ATTR_NONE;
- if (name == "system_gsi") {
- flags |= LP_PARTITION_ATTR_READONLY;
- }
- Partition* partition = builder->AddPartition(name, flags);
- if (!partition) {
- LOG(ERROR) << "Error adding " << name << " to partition table";
- return nullptr;
- }
- if (!AddPartitionFiemap(builder.get(), partition, image, data_device_name)) {
- return nullptr;
- }
- }
- auto metadata = builder->Export();
- if (!metadata) {
- LOG(ERROR) << "Error exporting partition table";
- return nullptr;
- }
- return metadata;
- }
- bool GsiService::CreateMetadataFile() {
- if (!WriteToImageFile(kGsiLpMetadataFile, *metadata_.get())) {
- LOG(ERROR) << "Error writing GSI partition table image";
- return false;
- }
- return true;
- }
- bool GsiService::FormatUserdata() {
- auto writer = OpenPartition("userdata_gsi");
- if (!writer) {
- return false;
- }
- // libcutils checks the first 4K, no matter the block size.
- std::string zeroes(4096, 0);
- if (!writer->Write(zeroes.data(), zeroes.size())) {
- PLOG(ERROR) << "write userdata_gsi";
- return false;
- }
- return true;
- }
- bool GsiService::AddPartitionFiemap(MetadataBuilder* builder, Partition* partition,
- const Image& image, const std::string& block_device) {
- uint64_t sectors_needed = image.actual_size / LP_SECTOR_SIZE;
- for (const auto& extent : image.writer->extents()) {
- // :TODO: block size check for length, not sector size
- if (extent.fe_length % LP_SECTOR_SIZE != 0) {
- LOG(ERROR) << "Extent is not sector-aligned: " << extent.fe_length;
- return false;
- }
- if (extent.fe_physical % LP_SECTOR_SIZE != 0) {
- LOG(ERROR) << "Extent physical sector is not sector-aligned: " << extent.fe_physical;
- return false;
- }
- uint64_t num_sectors =
- std::min(static_cast<uint64_t>(extent.fe_length / LP_SECTOR_SIZE), sectors_needed);
- if (!num_sectors || !sectors_needed) {
- // This should never happen, but we include it just in case. It would
- // indicate that the last filesystem block had multiple extents.
- LOG(WARNING) << "FiemapWriter allocated extra blocks";
- break;
- }
- uint64_t physical_sector = extent.fe_physical / LP_SECTOR_SIZE;
- if (!builder->AddLinearExtent(partition, block_device, num_sectors, physical_sector)) {
- LOG(ERROR) << "Could not add extent to lp metadata";
- return false;
- }
- sectors_needed -= num_sectors;
- }
- return true;
- }
- bool GsiService::SetBootMode(bool one_shot) {
- if (one_shot) {
- if (!android::base::WriteStringToFile("1", kGsiOneShotBootFile)) {
- PLOG(ERROR) << "write " << kGsiOneShotBootFile;
- return false;
- }
- } else if (!access(kGsiOneShotBootFile, F_OK)) {
- std::string error;
- if (!android::base::RemoveFileIfExists(kGsiOneShotBootFile, &error)) {
- LOG(ERROR) << error;
- return false;
- }
- }
- return true;
- }
- bool GsiService::CreateInstallStatusFile() {
- if (!android::base::WriteStringToFile("0", kGsiInstallStatusFile)) {
- PLOG(ERROR) << "write " << kGsiInstallStatusFile;
- return false;
- }
- return true;
- }
- void GsiService::RunStartupTasks() {
- if (!IsGsiInstalled()) {
- return;
- }
- std::string boot_key;
- if (!GetInstallStatus(&boot_key)) {
- PLOG(ERROR) << "read " << kGsiInstallStatusFile;
- return;
- }
- if (!IsGsiRunning()) {
- // Check if a wipe was requested from fastboot or adb-in-gsi.
- if (boot_key == kInstallStatusWipe) {
- RemoveGsiFiles(GetInstalledImageDir(), true /* wipeUserdata */);
- }
- } else {
- // NB: When single-boot is enabled, init will write "disabled" into the
- // install_status file, which will cause GetBootAttempts to return
- // false. Thus, we won't write "ok" here.
- int ignore;
- if (GetBootAttempts(boot_key, &ignore)) {
- // Mark the GSI as having successfully booted.
- if (!android::base::WriteStringToFile(kInstallStatusOk, kGsiInstallStatusFile)) {
- PLOG(ERROR) << "write " << kGsiInstallStatusFile;
- }
- }
- }
- }
- } // namespace gsi
- } // namespace android
|