Snap for 6198741 from 9e040fc1f42ce548384ee33f519dbe8e95fe48b4 to sdk-release
Change-Id: I0054fe35d4ff8cd284f6c6a56a840561f71b14be
diff --git a/Android.bp b/Android.bp
index 421ece2..1667ffb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -79,7 +79,7 @@
srcs: [
"daemon.cpp",
"gsi_service.cpp",
- "gsi_installer.cpp",
+ "partition_installer.cpp",
],
required: [
"mke2fs",
@@ -88,19 +88,23 @@
"gsid.rc",
],
shared_libs: [
- "gsi_aidl_interface-cpp",
"libbase",
"libbinder",
+ "libcrypto",
+ "liblog",
+ ],
+ static_libs: [
+ "gsi_aidl_interface-cpp",
+ "libavb",
+ "libcutils",
+ "libdm",
"libext4_utils",
"libfs_mgr",
"libgsi",
- "liblog",
+ "libgsid",
"liblp",
"libutils",
- ],
- static_libs: [
- "libdm",
- "libfiemap_passthrough",
+ "libc++fs",
],
local_include_dirs: ["include"],
}
@@ -119,12 +123,17 @@
filegroup {
name: "gsiservice_aidl",
srcs: [
- "aidl/android/gsi/GsiInstallParams.aidl",
+ "aidl/android/gsi/AvbPublicKey.aidl",
"aidl/android/gsi/GsiProgress.aidl",
- "aidl/android/gsi/IImageService.aidl",
"aidl/android/gsi/IGsid.aidl",
"aidl/android/gsi/IGsiService.aidl",
+ "aidl/android/gsi/IImageService.aidl",
+ "aidl/android/gsi/IProgressCallback.aidl",
"aidl/android/gsi/MappedImage.aidl",
],
path: "aidl",
}
+
+vts_config {
+ name: "VtsGsiBootTest",
+}
diff --git a/Android.mk b/Android.mk
deleted file mode 100644
index 21d0b45..0000000
--- a/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := VtsGsiBootTest
--include test/vts/tools/build/Android.host_config.mk
diff --git a/OWNERS b/OWNERS
index 3ef0a70..8a0a10e 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,2 +1,3 @@
dvander@google.com
sspatil@google.com
+elsk@google.com
diff --git a/aidl/android/gsi/AvbPublicKey.aidl b/aidl/android/gsi/AvbPublicKey.aidl
new file mode 100644
index 0000000..519ec80
--- /dev/null
+++ b/aidl/android/gsi/AvbPublicKey.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+package android.gsi;
+
+/** {@hide} */
+parcelable AvbPublicKey {
+ /* Raw data bytes. */
+ byte[] bytes;
+ /* SHA-1 digest of the key. */
+ byte[] sha1;
+}
diff --git a/aidl/android/gsi/GsiInstallParams.aidl b/aidl/android/gsi/GsiInstallParams.aidl
deleted file mode 100644
index 6e46da4..0000000
--- a/aidl/android/gsi/GsiInstallParams.aidl
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.
- */
-
-package android.gsi;
-
-/** {@hide} */
-parcelable GsiInstallParams {
- /**
- * The directory to install GSI images under. This must be either an empty
- * string (which will use the default /data/gsi), "/data/gsi", or a mount
- * under /mnt/media_rw. It may end in a trailing slash.
- */
- @utf8InCpp String installDir;
-
- /* The size of the on-disk GSI image. */
- long gsiSize;
-
- /* The desired size of the userdata partition. */
- long userdataSize;
-
- /* If false, a userdata image is only created if one does not already
- * exist. If the size is zero, a default size of 8GiB is used. If there is
- * an existing image smaller than the desired size, it may be resized
- * automatically.
- */
- boolean wipeUserdata;
-}
-
diff --git a/aidl/android/gsi/IGsiService.aidl b/aidl/android/gsi/IGsiService.aidl
index 6aed1c1..65050fe 100644
--- a/aidl/android/gsi/IGsiService.aidl
+++ b/aidl/android/gsi/IGsiService.aidl
@@ -16,7 +16,7 @@
package android.gsi;
-import android.gsi.GsiInstallParams;
+import android.gsi.AvbPublicKey;
import android.gsi.GsiProgress;
import android.gsi.IImageService;
import android.os.ParcelFileDescriptor;
@@ -78,9 +78,12 @@
*
* @param oneShot If true, the GSI will boot once and then disable itself.
* It can still be re-enabled again later with setGsiBootable.
+ * @param dsuSlot The DSU slot to be enabled. Possible values are available
+ * with the getInstalledDsuSlots()
+ *
* @return INSTALL_* error code.
*/
- int enableGsi(boolean oneShot);
+ int enableGsi(boolean oneShot, @utf8InCpp String dsuSlot);
/**
* @return True if Gsi is enabled
@@ -122,29 +125,55 @@
boolean isGsiRunning();
/**
+ * Returns the active DSU slot if there is any DSU installed, empty string otherwise.
+ */
+ @utf8InCpp String getActiveDsuSlot();
+
+ /**
* If a GSI is installed, returns the directory where the installed images
* are located. Otherwise, returns an empty string.
*/
- @utf8InCpp String getInstalledGsiImageDir();
+ @utf8InCpp String getInstalledGsiImageDir();
/**
- * Begin a GSI installation.
+ * Returns all installed DSU slots.
+ */
+ @utf8InCpp List<String> getInstalledDsuSlots();
+
+ /**
+ * Open a DSU installation
*
- * This is a replacement for startGsiInstall, in order to supply additional
- * options.
+ * @param installDir The directory to install DSU images under. This must be
+ * either an empty string (which will use the default /data/gsi),
+ * "/data/gsi", or a mount under /mnt/media_rw. It may end in a trailing slash.
*
* @return 0 on success, an error code on failure.
*/
- int beginGsiInstall(in GsiInstallParams params);
+ int openInstall(in @utf8InCpp String installDir);
/**
- * Wipe the userdata of an existing GSI install. This will not work if the
- * GSI is currently running. The userdata image will not be removed, but the
- * first block will be zeroed ensuring that the next GSI boot formats /data.
+ * Close a DSU installation. An installation is complete after the close been invoked.
+ */
+ int closeInstall();
+
+ /**
+ * Create a DSU partition within the current installation
+ *
+ * @param name The DSU partition name
+ * @param size Bytes in the partition
+ * @param readOnly True if the partition is readOnly when DSU is running
+ */
+ int createPartition(in @utf8InCpp String name, long size, boolean readOnly);
+
+ /**
+ * Wipe a partition. This will not work if the GSI is currently running.
+ * The partition will not be removed, but the first block will be zeroed.
+ *
+ * @param name The DSU partition name
*
* @return 0 on success, an error code on failure.
*/
- int wipeGsiUserdata();
+ int zeroPartition(in @utf8InCpp String name);
/**
* Open a handle to an IImageService for the given metadata and data storage paths.
@@ -154,4 +183,26 @@
* /metadata/gsi/{prefix}.
*/
IImageService openImageService(@utf8InCpp String prefix);
+
+ /**
+ * Dump diagnostic information about device-mapper devices. This is intended
+ * for dumpstate.
+ */
+ @utf8InCpp String dumpDeviceMapperDevices();
+
+ /**
+ * Retrieve AVB public key from the current mapped partition.
+ * This works only while partition device is mapped and the end-of-partition
+ * AVB footer has been written.
+ * A call to createPartition() does the following things:
+ * 1. Close the previous partition installer, thus unmap the partition.
+ * 2. Open a new partition installer.
+ * 3. Create and map the new partition.
+ *
+ * In other words, getAvbPublicKey() works between two createPartition() calls.
+ *
+ * @param dst Output the AVB public key.
+ * @return 0 on success, an error code on failure.
+ */
+ int getAvbPublicKey(out AvbPublicKey dst);
}
diff --git a/aidl/android/gsi/IImageService.aidl b/aidl/android/gsi/IImageService.aidl
index 1195c00..c8c5a9d 100644
--- a/aidl/android/gsi/IImageService.aidl
+++ b/aidl/android/gsi/IImageService.aidl
@@ -16,7 +16,9 @@
package android.gsi;
+import android.gsi.AvbPublicKey;
import android.gsi.MappedImage;
+import android.gsi.IProgressCallback;
/** {@hide} */
interface IImageService {
@@ -25,6 +27,11 @@
const int CREATE_IMAGE_READONLY = 0x1;
const int CREATE_IMAGE_ZERO_FILL = 0x2;
+ /* Successfully returned */
+ const int IMAGE_OK = 0;
+ /* Generic error code */
+ const int IMAGE_ERROR = 1;
+
/**
* Create an image that can be mapped as a block device.
*
@@ -35,15 +42,20 @@
* free, the call will fail.
* @param readonly If readonly, MapBackingImage() will configure the device as
* readonly.
- * @return True on success, false otherwise.
+ * @param on_progress Progress callback. It is invoked when there is an interesting update.
+ * For each invocation, |current| is the number of bytes actually written,
+ * and |total| is set to |size|.
+ * @throws ServiceSpecificException if any error occurs. Exception code is a
+ * FiemapStatus::ErrorCode value.
*/
- void createBackingImage(@utf8InCpp String name, long size, int flags);
+ void createBackingImage(@utf8InCpp String name, long size, int flags,
+ @nullable IProgressCallback on_progress);
/**
* Delete an image created with createBackingImage.
*
* @param name Image name as passed to createBackingImage().
- * @return True on success, false otherwise.
+ * @throws ServiceSpecificException if any error occurs.
*/
void deleteBackingImage(@utf8InCpp String name);
@@ -81,6 +93,18 @@
boolean isImageMapped(@utf8InCpp String name);
/**
+ * Retrieve AVB public key from an image.
+ * If the image is already mapped then it works the same as
+ * IGsiService::getAvbPublicKey(). Otherwise this will attempt to
+ * map / unmap the partition image upon enter / return.
+ *
+ * @param name Image name as passed to createBackingImage().
+ * @param dst Output of the AVB public key.
+ * @return 0 on success, an error code on failure.
+ */
+ int getAvbPublicKey(@utf8InCpp String name, out AvbPublicKey dst);
+
+ /**
* Get all installed backing image names
*
* @return list of installed backing image names
@@ -95,7 +119,23 @@
* @param bytes Number of zeros to be written, starting from the
* beginning. If bytes is equal to 0, then the whole
* image file is filled with zeros.
- * @return True on success, false otherwise.
+ * @throws ServiceSpecificException if any error occurs. Exception code is a
+ * FiemapStatus::ErrorCode value.
*/
void zeroFillNewImage(@utf8InCpp String name, long bytes);
+
+ /**
+ * Find and remove all images in the containing folder of this instance.
+ */
+ void removeAllImages();
+
+ /**
+ * Remove all images that were marked as disabled in recovery.
+ */
+ void removeDisabledImages();
+
+ /**
+ * Return the block device path of a mapped image, or an empty string if not mapped.
+ */
+ @utf8InCpp String getMappedImageDevice(@utf8InCpp String name);
}
diff --git a/aidl/android/gsi/IProgressCallback.aidl b/aidl/android/gsi/IProgressCallback.aidl
new file mode 100644
index 0000000..b7acefc
--- /dev/null
+++ b/aidl/android/gsi/IProgressCallback.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+package android.gsi;
+
+/** {@hide} */
+interface IProgressCallback {
+ /**
+ * Report progress for a long-running task.
+ *
+ * The percentage can be computed using current / total. The task is
+ * done when current == total.
+ *
+ * Different APIs may have different meanings for the parameters; see
+ * specific APIs for details.
+ *
+ * @param current a value indicating the current progress. Should be treated as uint64.
+ * @param total a value indicating 100% progress. Should be treated as uint64.
+ */
+ void onProgress(long current, long total);
+}
diff --git a/daemon.cpp b/daemon.cpp
index 3fa7b3f..782d2ed 100644
--- a/daemon.cpp
+++ b/daemon.cpp
@@ -16,6 +16,7 @@
#include <getopt.h>
+#include <iostream>
#include <string>
#include <android-base/logging.h>
@@ -23,6 +24,7 @@
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <libgsi/libgsi.h>
+#include <libgsi/libgsid.h>
#include "gsi_service.h"
@@ -30,12 +32,34 @@
using android::sp;
using namespace std::literals;
+static int DumpDeviceMapper() {
+ auto service = android::gsi::GetGsiService();
+ if (!service) {
+ std::cerr << "Could not start IGsiService.\n";
+ return 1;
+ }
+ std::string output;
+ auto status = service->dumpDeviceMapperDevices(&output);
+ if (!status.isOk()) {
+ std::cerr << "Could not dump device-mapper devices: " << status.exceptionMessage().c_str()
+ << "\n";
+ return 1;
+ }
+ std::cout << output;
+ return 0;
+}
+
int main(int argc, char** argv) {
android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
- if (argc > 1 && argv[1] == "run-startup-tasks"s) {
- android::gsi::GsiService::RunStartupTasks();
- exit(0);
+ if (argc > 1) {
+ if (argv[1] == "run-startup-tasks"s) {
+ android::gsi::GsiService::RunStartupTasks();
+ exit(0);
+ } else if (argv[1] == "dump-device-mapper"s) {
+ int rc = DumpDeviceMapper();
+ exit(rc);
+ }
}
android::gsi::Gsid::Register();
diff --git a/file_paths.h b/file_paths.h
index b596704..1ae7817 100644
--- a/file_paths.h
+++ b/file_paths.h
@@ -16,22 +16,26 @@
#pragma once
+#include <filesystem>
+
namespace android {
namespace gsi {
static constexpr char kDefaultDsuImageFolder[] = "/data/gsi/dsu/";
static constexpr char kUserdataDevice[] = "/dev/block/by-name/userdata";
-static constexpr char kDsuMetadataDir[] = "/metadata/gsi/dsu";
-static constexpr char kDsuOneShotBootFile[] = "/metadata/gsi/dsu/one_shot_boot";
-static constexpr char kDsuInstallDirFile[] = "/metadata/gsi/dsu/install_dir";
+static inline std::string MetadataDir(const std::string& dsu_slot) {
+ return std::filesystem::path(DSU_METADATA_PREFIX) / dsu_slot;
+}
+
+static constexpr char kDsuOneShotBootFile[] = DSU_METADATA_PREFIX "one_shot_boot";
// This file can contain the following values:
// [int] - boot attempt counter, starting from 0
// "ok" - boot was marked successful
// "disabled" - boot into GSI no longer allowed
// "wipe" - boot into GSI not allowed; next reboot will delete gsi
-static constexpr char kDsuInstallStatusFile[] = "/metadata/gsi/dsu/install_status";
+static constexpr char kDsuInstallStatusFile[] = DSU_METADATA_PREFIX "install_status";
} // namespace gsi
} // namespace android
diff --git a/gsi_installer.cpp b/gsi_installer.cpp
deleted file mode 100644
index 27e6dd0..0000000
--- a/gsi_installer.cpp
+++ /dev/null
@@ -1,443 +0,0 @@
-/*
- * 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_installer.h"
-
-#include <sys/statvfs.h>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <ext4_utils/ext4_utils.h>
-#include <fs_mgr_dm_linear.h>
-#include <libdm/dm.h>
-#include <libgsi/libgsi.h>
-
-#include "file_paths.h"
-#include "gsi_service.h"
-#include "libgsi_private.h"
-
-namespace android {
-namespace gsi {
-
-using namespace std::literals;
-using namespace android::dm;
-using namespace android::fiemap;
-using namespace android::fs_mgr;
-using android::base::unique_fd;
-
-// 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;
-// Default userdata image size.
-static constexpr int64_t kDefaultUserdataSize = int64_t(2) * 1024 * 1024 * 1024;
-
-GsiInstaller::GsiInstaller(GsiService* service, const GsiInstallParams& params)
- : service_(service),
- install_dir_(params.installDir),
- gsi_size_(params.gsiSize),
- wipe_userdata_(params.wipeUserdata) {
- userdata_size_ = (params.userdataSize) ? params.userdataSize : kDefaultUserdataSize;
- images_ = ImageManager::Open(kDsuMetadataDir, install_dir_);
-
- // Only rm userdata_gsi if one didn't already exist.
- if (wipe_userdata_ || !images_->BackingImageExists("userdata_gsi")) {
- wipe_userdata_on_failure_ = true;
- }
-}
-
-GsiInstaller::GsiInstaller(GsiService* service, const std::string& install_dir)
- : service_(service), install_dir_(install_dir) {
- images_ = ImageManager::Open(kDsuMetadataDir, install_dir_);
-
- // The install already exists, so always mark it as succeeded.
- succeeded_ = true;
-}
-
-GsiInstaller::~GsiInstaller() {
- if (!succeeded_) {
- // Close open handles before we remove files.
- system_device_ = nullptr;
- PostInstallCleanup(images_.get());
-
- GsiService::RemoveGsiFiles(install_dir_, wipe_userdata_on_failure_);
- }
- if (IsAshmemMapped()) {
- UnmapAshmem();
- }
-}
-
-void GsiInstaller::PostInstallCleanup() {
- auto manager = ImageManager::Open(kDsuMetadataDir, GsiService::GetInstalledImageDir());
- if (!manager) {
- LOG(ERROR) << "Could not open image manager";
- return;
- }
- return PostInstallCleanup(manager.get());
-}
-
-void GsiInstaller::PostInstallCleanup(ImageManager* manager) {
- if (manager->IsImageMapped("userdata_gsi")) {
- manager->UnmapImageDevice("userdata_gsi");
- }
- if (manager->IsImageMapped("system_gsi")) {
- manager->UnmapImageDevice("system_gsi");
- }
-}
-
-int GsiInstaller::StartInstall() {
- if (int status = PerformSanityChecks()) {
- return status;
- }
- if (int status = PreallocateFiles()) {
- return status;
- }
- if (!FormatUserdata()) {
- return IGsiService::INSTALL_ERROR_GENERIC;
- }
-
- // Map system_gsi so we can write to it.
- system_device_ = OpenPartition("system_gsi");
- if (!system_device_) {
- return IGsiService::INSTALL_ERROR_GENERIC;
- }
-
- // Clear the progress indicator.
- service_->UpdateProgress(IGsiService::STATUS_NO_OPERATION, 0);
- return IGsiService::INSTALL_OK;
-}
-
-int GsiInstaller::PerformSanityChecks() {
- if (!images_) {
- LOG(ERROR) << "unable to create image manager";
- return IGsiService::INSTALL_ERROR_GENERIC;
- }
- if (gsi_size_ < 0) {
- LOG(ERROR) << "image size " << gsi_size_ << " is negative";
- return IGsiService::INSTALL_ERROR_GENERIC;
- }
- if (android::gsi::IsGsiRunning()) {
- LOG(ERROR) << "cannot install gsi inside a live gsi";
- return IGsiService::INSTALL_ERROR_GENERIC;
- }
-
- struct statvfs sb;
- if (statvfs(install_dir_.c_str(), &sb)) {
- PLOG(ERROR) << "failed to read file system stats";
- return IGsiService::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 IGsiService::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 IGsiService::INSTALL_ERROR_FILE_SYSTEM_CLUTTERED;
- }
- return IGsiService::INSTALL_OK;
-}
-
-int GsiInstaller::PreallocateFiles() {
- if (wipe_userdata_) {
- images_->DeleteBackingImage("userdata_gsi");
- }
- images_->DeleteBackingImage("system_gsi");
-
- // Create fallocated files.
- if (int status = PreallocateUserdata()) {
- return status;
- }
- if (int status = PreallocateSystem()) {
- return status;
- }
-
- service_->UpdateProgress(IGsiService::STATUS_COMPLETE, 0);
- return IGsiService::INSTALL_OK;
-}
-
-int GsiInstaller::PreallocateUserdata() {
- if (wipe_userdata_ || !images_->BackingImageExists("userdata_gsi")) {
- service_->StartAsyncOperation("create userdata", userdata_size_);
- if (!CreateImage("userdata_gsi", userdata_size_, false)) {
- LOG(ERROR) << "Could not create userdata image";
- return IGsiService::INSTALL_ERROR_GENERIC;
- }
-
- // Signal that we need to reformat userdata.
- wipe_userdata_ = true;
- }
- return IGsiService::INSTALL_OK;
-}
-
-int GsiInstaller::PreallocateSystem() {
- service_->StartAsyncOperation("create system", gsi_size_);
-
- if (!CreateImage("system_gsi", gsi_size_, true)) {
- return IGsiService::INSTALL_ERROR_GENERIC;
- }
- return IGsiService::INSTALL_OK;
-}
-
-bool GsiInstaller::CreateImage(const std::string& name, uint64_t size, bool readonly) {
- auto progress = [this](uint64_t bytes, uint64_t /* total */) -> bool {
- service_->UpdateProgress(IGsiService::STATUS_WORKING, bytes);
- if (service_->should_abort()) return false;
- return true;
- };
- int flags = ImageManager::CREATE_IMAGE_DEFAULT;
- if (readonly) {
- flags |= ImageManager::CREATE_IMAGE_READONLY;
- }
- return images_->CreateBackingImage(name, size, flags, std::move(progress));
-}
-
-std::unique_ptr<MappedDevice> GsiInstaller::OpenPartition(const std::string& name) {
- return MappedDevice::Open(images_.get(), 10s, name);
-}
-
-bool GsiInstaller::CommitGsiChunk(int stream_fd, int64_t bytes) {
- service_->StartAsyncOperation("write gsi", gsi_size_);
-
- if (bytes < 0) {
- LOG(ERROR) << "chunk size " << bytes << " is negative";
- return false;
- }
-
- static const size_t kBlockSize = 4096;
- auto buffer = std::make_unique<char[]>(kBlockSize);
-
- int progress = -1;
- uint64_t remaining = bytes;
- while (remaining) {
- size_t max_to_read = std::min(static_cast<uint64_t>(kBlockSize), 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) {
- service_->UpdateProgress(IGsiService::STATUS_WORKING, gsi_size_ - remaining);
- }
- }
-
- service_->UpdateProgress(IGsiService::STATUS_COMPLETE, gsi_size_);
- return true;
-}
-
-bool GsiInstaller::IsFinishedWriting() {
- return gsi_bytes_written_ == gsi_size_;
-}
-
-bool GsiInstaller::IsAshmemMapped() {
- return ashmem_data_ != MAP_FAILED;
-}
-
-bool GsiInstaller::CommitGsiChunk(const void* data, size_t bytes) {
- 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 (service_->should_abort()) {
- return false;
- }
- if (!android::base::WriteFully(system_device_->fd(), data, bytes)) {
- PLOG(ERROR) << "write failed";
- return false;
- }
- gsi_bytes_written_ += bytes;
- return true;
-}
-
-bool GsiInstaller::MapAshmem(int fd, size_t size) {
- ashmem_size_ = size;
- ashmem_data_ = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- return ashmem_data_ != MAP_FAILED;
-}
-
-void GsiInstaller::UnmapAshmem() {
- if (munmap(ashmem_data_, ashmem_size_) != 0) {
- PLOG(ERROR) << "cannot munmap";
- return;
- }
- ashmem_data_ = MAP_FAILED;
- ashmem_size_ = -1;
-}
-
-bool GsiInstaller::CommitGsiChunk(size_t bytes) {
- if (!IsAshmemMapped()) {
- PLOG(ERROR) << "ashmem is not mapped";
- return false;
- }
- bool success = CommitGsiChunk(ashmem_data_, bytes);
- if (success && IsFinishedWriting()) {
- UnmapAshmem();
- }
- return success;
-}
-
-bool GsiInstaller::SetBootMode(bool one_shot) {
- if (one_shot) {
- if (!android::base::WriteStringToFile("1", kDsuOneShotBootFile)) {
- PLOG(ERROR) << "write " << kDsuOneShotBootFile;
- return false;
- }
- } else if (!access(kDsuOneShotBootFile, F_OK)) {
- std::string error;
- if (!android::base::RemoveFileIfExists(kDsuOneShotBootFile, &error)) {
- LOG(ERROR) << error;
- return false;
- }
- }
- return true;
-}
-
-bool GsiInstaller::CreateInstallStatusFile() {
- if (!android::base::WriteStringToFile("0", kDsuInstallStatusFile)) {
- PLOG(ERROR) << "write " << kDsuInstallStatusFile;
- return false;
- }
- return true;
-}
-
-bool GsiInstaller::FormatUserdata() {
- auto device = OpenPartition("userdata_gsi");
- if (!device) {
- return false;
- }
-
- // libcutils checks the first 4K, no matter the block size.
- std::string zeroes(4096, 0);
- if (!android::base::WriteFully(device->fd(), zeroes.data(), zeroes.size())) {
- PLOG(ERROR) << "write userdata_gsi";
- return false;
- }
- return true;
-}
-
-int GsiInstaller::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 IGsiService::INSTALL_ERROR_GENERIC;
- }
-
- if (fsync(system_device_->fd())) {
- PLOG(ERROR) << "fsync failed for system_gsi";
- return IGsiService::INSTALL_ERROR_GENERIC;
- }
- system_device_ = {};
-
- // If files moved (are no longer pinned), the metadata file will be invalid.
- // This check can be removed once b/133967059 is fixed.
- if (!images_->Validate()) {
- return IGsiService::INSTALL_ERROR_GENERIC;
- }
-
- // Remember the installation directory.
- if (!android::base::WriteStringToFile(install_dir_, kDsuInstallDirFile)) {
- PLOG(ERROR) << "write failed: " << kDsuInstallDirFile;
- return IGsiService::INSTALL_ERROR_GENERIC;
- }
-
- // Note: create the install status file last, since this is the actual boot
- // indicator.
- if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) {
- return IGsiService::INSTALL_ERROR_GENERIC;
- }
-
- succeeded_ = true;
- return IGsiService::INSTALL_OK;
-}
-
-int GsiInstaller::CheckInstallState() {
- std::vector<std::string> gsi_images = {"system_gsi", "userdata_gsi"};
- for (const auto& image : gsi_images) {
- if (!images_->PartitionExists(image) || !images_->BackingImageExists(image)) {
- return IGsiService::INSTALL_ERROR_GENERIC;
- }
- }
- return IGsiService::INSTALL_OK;
-}
-
-int GsiInstaller::ReenableGsi(bool one_shot) {
- if (IsGsiRunning()) {
- if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) {
- return IGsiService::INSTALL_ERROR_GENERIC;
- }
- return IGsiService::INSTALL_OK;
- }
-
- if (int error = CheckInstallState()) {
- return error;
- }
- if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) {
- return IGsiService::INSTALL_ERROR_GENERIC;
- }
- return IGsiService::INSTALL_OK;
-}
-
-int GsiInstaller::WipeUserdata() {
- if (int error = CheckInstallState()) {
- return error;
- }
-
- auto device = OpenPartition("userdata_gsi");
- if (!device) {
- 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, get_block_device_size(device->fd()));
- for (uint64_t i = 0; i < erase_size; i += zeroes.size()) {
- if (!android::base::WriteFully(device->fd(), zeroes.data(), zeroes.size())) {
- PLOG(ERROR) << "write userdata_gsi";
- return IGsiService::INSTALL_ERROR_GENERIC;
- }
- }
- return IGsiService::INSTALL_OK;
-}
-
-} // namespace gsi
-} // namespace android
diff --git a/gsi_service.cpp b/gsi_service.cpp
index dcf2d83..3705c5b 100644
--- a/gsi_service.cpp
+++ b/gsi_service.cpp
@@ -18,16 +18,19 @@
#include <errno.h>
#include <linux/fs.h>
+#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/vfs.h>
#include <unistd.h>
+#include <array>
#include <chrono>
#include <string>
#include <vector>
+#include <android-base/errors.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
@@ -36,7 +39,10 @@
#include <android/gsi/IGsiService.h>
#include <ext4_utils/ext4_utils.h>
#include <fs_mgr.h>
+#include <libavb/libavb.h>
+#include <libdm/dm.h>
#include <libfiemap/image_manager.h>
+#include <openssl/sha.h>
#include <private/android_filesystem_config.h>
#include "file_paths.h"
@@ -48,11 +54,24 @@
using namespace std::literals;
using namespace android::fs_mgr;
using namespace android::fiemap;
+using android::base::ReadFileToString;
+using android::base::ReadFullyAtOffset;
+using android::base::RemoveFileIfExists;
using android::base::StringPrintf;
using android::base::unique_fd;
+using android::base::WriteStringToFd;
+using android::base::WriteStringToFile;
+using android::dm::DeviceMapper;
+
+static std::mutex sInstanceLock;
android::wp<GsiService> GsiService::sInstance;
+// Default userdata image size.
+static constexpr int64_t kDefaultUserdataSize = int64_t(2) * 1024 * 1024 * 1024;
+
+static bool GetAvbPublicKeyFromFd(int fd, AvbPublicKey* dst);
+
void Gsid::Register() {
auto ret = android::BinderService<Gsid>::publish();
if (ret != android::OK) {
@@ -67,11 +86,10 @@
GsiService::GsiService(Gsid* parent) : parent_(parent) {
progress_ = {};
- GsiInstaller::PostInstallCleanup();
}
GsiService::~GsiService() {
- std::lock_guard<std::mutex> guard(parent_->lock());
+ std::lock_guard<std::mutex> guard(sInstanceLock);
if (sInstance == this) {
// No more consumers, gracefully shut down gsid.
@@ -80,7 +98,7 @@
}
android::sp<IGsiService> GsiService::Get(Gsid* parent) {
- std::lock_guard<std::mutex> guard(parent->lock());
+ std::lock_guard<std::mutex> guard(sInstanceLock);
android::sp<GsiService> service = sInstance.promote();
if (!service) {
@@ -102,23 +120,88 @@
if (!status.isOk()) return status; \
} while (0)
-binder::Status GsiService::beginGsiInstall(const GsiInstallParams& given_params,
- int* _aidl_return) {
+int GsiService::SaveInstallation(const std::string& installation) {
+ auto dsu_slot = GetDsuSlot(installation);
+ auto install_dir_file = DsuInstallDirFile(dsu_slot);
+ auto metadata_dir = android::base::Dirname(install_dir_file);
+ if (access(metadata_dir.c_str(), F_OK) != 0) {
+ if (mkdir(metadata_dir.c_str(), 0777) != 0) {
+ PLOG(ERROR) << "Failed to mkdir " << metadata_dir;
+ return INSTALL_ERROR_GENERIC;
+ }
+ }
+ auto fd = android::base::unique_fd(
+ open(install_dir_file.c_str(), O_RDWR | O_SYNC | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR));
+ if (!WriteStringToFd(installation, fd)) {
+ PLOG(ERROR) << "write failed: " << DsuInstallDirFile(dsu_slot);
+ return INSTALL_ERROR_GENERIC;
+ }
+ return INSTALL_OK;
+}
+
+binder::Status GsiService::openInstall(const std::string& install_dir, int* _aidl_return) {
+ ENFORCE_SYSTEM;
+ std::lock_guard<std::mutex> guard(parent_->lock());
+ if (IsGsiRunning()) {
+ *_aidl_return = IGsiService::INSTALL_ERROR_GENERIC;
+ return binder::Status::ok();
+ }
+ install_dir_ = install_dir;
+ if (int status = ValidateInstallParams(install_dir_)) {
+ *_aidl_return = status;
+ return binder::Status::ok();
+ }
+ std::string message;
+ auto dsu_slot = GetDsuSlot(install_dir_);
+ if (!RemoveFileIfExists(GetCompleteIndication(dsu_slot), &message)) {
+ LOG(ERROR) << message;
+ }
+ // Remember the installation directory before allocate any resource
+ *_aidl_return = SaveInstallation(install_dir_);
+ return binder::Status::ok();
+}
+
+binder::Status GsiService::closeInstall(int* _aidl_return) {
+ ENFORCE_SYSTEM;
+ std::lock_guard<std::mutex> guard(parent_->lock());
+ auto dsu_slot = GetDsuSlot(install_dir_);
+ std::string file = GetCompleteIndication(dsu_slot);
+ if (!WriteStringToFile("OK", file)) {
+ PLOG(ERROR) << "write failed: " << file;
+ *_aidl_return = INSTALL_ERROR_GENERIC;
+ }
+ *_aidl_return = INSTALL_OK;
+ return binder::Status::ok();
+}
+
+binder::Status GsiService::createPartition(const ::std::string& name, int64_t size, bool readOnly,
+ int32_t* _aidl_return) {
ENFORCE_SYSTEM;
std::lock_guard<std::mutex> guard(parent_->lock());
- // Make sure any interrupted installations are cleaned up.
+ if (install_dir_.empty()) {
+ PLOG(ERROR) << "open is required for createPartition";
+ *_aidl_return = INSTALL_ERROR_GENERIC;
+ return binder::Status::ok();
+ }
+
+ // Make sure a pending interrupted installations are cleaned up.
installer_ = nullptr;
// 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;
+ if (size % LP_SECTOR_SIZE) {
+ LOG(ERROR) << " size " << size << " is not a multiple of " << LP_SECTOR_SIZE;
+ *_aidl_return = INSTALL_ERROR_GENERIC;
return binder::Status::ok();
}
- installer_ = std::make_unique<GsiInstaller>(this, params);
+ if (size == 0 && name == "userdata") {
+ size = kDefaultUserdataSize;
+ }
+ installer_ = std::make_unique<PartitionInstaller>(this, install_dir_, name,
+ GetDsuSlot(install_dir_), size, readOnly);
+ progress_ = {};
int status = installer_->StartInstall();
if (status != INSTALL_OK) {
installer_ = nullptr;
@@ -165,6 +248,9 @@
ENFORCE_SYSTEM;
std::lock_guard<std::mutex> guard(progress_lock_);
+ if (installer_ == nullptr) {
+ progress_ = {};
+ }
*_aidl_return = progress_;
return binder::Status::ok();
}
@@ -183,6 +269,7 @@
binder::Status GsiService::setGsiAshmem(const ::android::os::ParcelFileDescriptor& ashmem,
int64_t size, bool* _aidl_return) {
+ ENFORCE_SYSTEM;
if (!installer_) {
*_aidl_return = false;
return binder::Status::ok();
@@ -191,13 +278,21 @@
return binder::Status::ok();
}
-binder::Status GsiService::enableGsi(bool one_shot, int* _aidl_return) {
+binder::Status GsiService::enableGsi(bool one_shot, const std::string& dsuSlot, int* _aidl_return) {
std::lock_guard<std::mutex> guard(parent_->lock());
+ if (!WriteStringToFile(dsuSlot, kDsuActiveFile)) {
+ PLOG(ERROR) << "write failed: " << GetDsuSlot(install_dir_);
+ *_aidl_return = INSTALL_ERROR_GENERIC;
+ return binder::Status::ok();
+ }
if (installer_) {
ENFORCE_SYSTEM;
- if (int error = installer_->SetGsiBootable(one_shot)) {
- *_aidl_return = error;
+ installer_ = {};
+ // Note: create the install status file last, since this is the actual boot
+ // indicator.
+ if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) {
+ *_aidl_return = IGsiService::INSTALL_ERROR_GENERIC;
} else {
*_aidl_return = INSTALL_OK;
}
@@ -217,7 +312,7 @@
if (!GetInstallStatus(&boot_key)) {
*_aidl_return = false;
} else {
- *_aidl_return = (boot_key == kInstallStatusOk);
+ *_aidl_return = (boot_key != kInstallStatusDisabled);
}
return binder::Status::ok();
}
@@ -226,19 +321,13 @@
ENFORCE_SYSTEM_OR_SHELL;
std::lock_guard<std::mutex> guard(parent_->lock());
- // Just in case an install was left hanging.
- std::string install_dir;
- if (installer_) {
- install_dir = installer_->install_dir();
- } else {
- install_dir = GetInstalledImageDir();
- }
-
+ std::string install_dir = GetActiveInstalledImageDir();
if (IsGsiRunning()) {
// Can't remove gsi files while running.
*_aidl_return = UninstallGsi();
} else {
- *_aidl_return = RemoveGsiFiles(install_dir, true /* wipeUserdata */);
+ installer_ = {};
+ *_aidl_return = RemoveGsiFiles(install_dir);
}
return binder::Status::ok();
}
@@ -291,13 +380,26 @@
ENFORCE_SYSTEM;
std::lock_guard<std::mutex> guard(parent_->lock());
- if (IsGsiInstalled()) {
- *_aidl_return = GetInstalledImageDir();
- }
+ *_aidl_return = GetActiveInstalledImageDir();
return binder::Status::ok();
}
-binder::Status GsiService::wipeGsiUserdata(int* _aidl_return) {
+binder::Status GsiService::getActiveDsuSlot(std::string* _aidl_return) {
+ ENFORCE_SYSTEM_OR_SHELL;
+ std::lock_guard<std::mutex> guard(parent_->lock());
+
+ *_aidl_return = GetActiveDsuSlot();
+ return binder::Status::ok();
+}
+
+binder::Status GsiService::getInstalledDsuSlots(std::vector<std::string>* _aidl_return) {
+ ENFORCE_SYSTEM;
+ std::lock_guard<std::mutex> guard(parent_->lock());
+ *_aidl_return = GetInstalledDsuSlots();
+ return binder::Status::ok();
+}
+
+binder::Status GsiService::zeroPartition(const std::string& name, int* _aidl_return) {
ENFORCE_SYSTEM_OR_SHELL;
std::lock_guard<std::mutex> guard(parent_->lock());
@@ -306,15 +408,89 @@
return binder::Status::ok();
}
- auto installer = std::make_unique<GsiInstaller>(this, GetInstalledImageDir());
- *_aidl_return = installer->WipeUserdata();
+ std::string install_dir = GetActiveInstalledImageDir();
+ *_aidl_return = PartitionInstaller::WipeWritable(GetDsuSlot(install_dir), install_dir, name);
return binder::Status::ok();
}
-static binder::Status BinderError(const std::string& message) {
- return binder::Status::fromExceptionCode(binder::Status::EX_SERVICE_SPECIFIC,
- String8(message.c_str()));
+static binder::Status BinderError(const std::string& message,
+ FiemapStatus::ErrorCode status = FiemapStatus::ErrorCode::ERROR) {
+ return binder::Status::fromServiceSpecificError(static_cast<int32_t>(status), message.c_str());
+}
+
+binder::Status GsiService::dumpDeviceMapperDevices(std::string* _aidl_return) {
+ ENFORCE_SYSTEM_OR_SHELL;
+
+ auto& dm = DeviceMapper::Instance();
+
+ std::vector<DeviceMapper::DmBlockDevice> devices;
+ if (!dm.GetAvailableDevices(&devices)) {
+ return BinderError("Could not list devices");
+ }
+
+ std::stringstream text;
+ for (const auto& device : devices) {
+ text << "Device " << device.name() << " (" << device.Major() << ":" << device.Minor()
+ << ")\n";
+
+ std::vector<DeviceMapper::TargetInfo> table;
+ if (!dm.GetTableInfo(device.name(), &table)) {
+ continue;
+ }
+
+ for (const auto& target : table) {
+ const auto& spec = target.spec;
+ auto target_type = DeviceMapper::GetTargetType(spec);
+ text << " " << target_type << " " << spec.sector_start << " " << spec.length << " "
+ << target.data << "\n";
+ }
+ }
+
+ *_aidl_return = text.str();
+ return binder::Status::ok();
+}
+
+binder::Status GsiService::getAvbPublicKey(AvbPublicKey* dst, int32_t* _aidl_return) {
+ ENFORCE_SYSTEM;
+ std::lock_guard<std::mutex> guard(parent_->lock());
+
+ if (!installer_) {
+ *_aidl_return = INSTALL_ERROR_GENERIC;
+ return binder::Status::ok();
+ }
+ int fd = installer_->GetPartitionFd();
+ if (!GetAvbPublicKeyFromFd(fd, dst)) {
+ LOG(ERROR) << "Failed to extract AVB public key";
+ *_aidl_return = INSTALL_ERROR_GENERIC;
+ return binder::Status::ok();
+ }
+ *_aidl_return = INSTALL_OK;
+ return binder::Status::ok();
+}
+
+bool GsiService::CreateInstallStatusFile() {
+ if (!android::base::WriteStringToFile("0", kDsuInstallStatusFile)) {
+ PLOG(ERROR) << "write " << kDsuInstallStatusFile;
+ return false;
+ }
+ return true;
+}
+
+bool GsiService::SetBootMode(bool one_shot) {
+ if (one_shot) {
+ if (!android::base::WriteStringToFile("1", kDsuOneShotBootFile)) {
+ PLOG(ERROR) << "write " << kDsuOneShotBootFile;
+ return false;
+ }
+ } else if (!access(kDsuOneShotBootFile, F_OK)) {
+ std::string error;
+ if (!android::base::RemoveFileIfExists(kDsuOneShotBootFile, &error)) {
+ LOG(ERROR) << error;
+ return false;
+ }
+ }
+ return true;
}
static binder::Status UidSecurityError() {
@@ -327,14 +503,20 @@
public:
ImageService(GsiService* service, std::unique_ptr<ImageManager>&& impl, uid_t uid);
binder::Status getAllBackingImages(std::vector<std::string>* _aidl_return);
- binder::Status createBackingImage(const std::string& name, int64_t size, int flags) override;
+ binder::Status createBackingImage(const std::string& name, int64_t size, int flags,
+ const sp<IProgressCallback>& on_progress) override;
binder::Status deleteBackingImage(const std::string& name) override;
binder::Status mapImageDevice(const std::string& name, int32_t timeout_ms,
MappedImage* mapping) override;
binder::Status unmapImageDevice(const std::string& name) override;
binder::Status backingImageExists(const std::string& name, bool* _aidl_return) override;
binder::Status isImageMapped(const std::string& name, bool* _aidl_return) override;
+ binder::Status getAvbPublicKey(const std::string& name, AvbPublicKey* dst,
+ int32_t* _aidl_return) override;
binder::Status zeroFillNewImage(const std::string& name, int64_t bytes) override;
+ binder::Status removeAllImages() override;
+ binder::Status removeDisabledImages() override;
+ binder::Status getMappedImageDevice(const std::string& name, std::string* device) override;
private:
bool CheckUid();
@@ -353,13 +535,28 @@
return binder::Status::ok();
}
-binder::Status ImageService::createBackingImage(const std::string& name, int64_t size, int flags) {
+binder::Status ImageService::createBackingImage(const std::string& name, int64_t size, int flags,
+ const sp<IProgressCallback>& on_progress) {
if (!CheckUid()) return UidSecurityError();
std::lock_guard<std::mutex> guard(parent_->lock());
- if (!impl_->CreateBackingImage(name, size, flags, nullptr)) {
- return BinderError("Failed to create");
+ std::function<bool(uint64_t, uint64_t)> callback;
+ if (on_progress) {
+ callback = [on_progress](uint64_t current, uint64_t total) -> bool {
+ auto status = on_progress->onProgress(static_cast<int64_t>(current),
+ static_cast<int64_t>(total));
+ if (!status.isOk()) {
+ LOG(ERROR) << "progress callback returned: " << status.toString8().string();
+ return false;
+ }
+ return true;
+ };
+ }
+
+ auto res = impl_->CreateBackingImage(name, size, flags, std::move(callback));
+ if (!res.is_ok()) {
+ return BinderError("Failed to create: " + res.string(), res.error_code());
}
return binder::Status::ok();
}
@@ -416,6 +613,46 @@
return binder::Status::ok();
}
+binder::Status ImageService::getAvbPublicKey(const std::string& name, AvbPublicKey* dst,
+ int32_t* _aidl_return) {
+ if (!CheckUid()) return UidSecurityError();
+
+ std::lock_guard<std::mutex> guard(parent_->lock());
+
+ std::string device_path;
+ std::unique_ptr<MappedDevice> mapped_device;
+ if (!impl_->IsImageMapped(name)) {
+ mapped_device = MappedDevice::Open(impl_.get(), 10s, name);
+ if (!mapped_device) {
+ PLOG(ERROR) << "Fail to map image: " << name;
+ *_aidl_return = IMAGE_ERROR;
+ return binder::Status::ok();
+ }
+ device_path = mapped_device->path();
+ } else {
+ if (!impl_->GetMappedImageDevice(name, &device_path)) {
+ PLOG(ERROR) << "GetMappedImageDevice() failed";
+ *_aidl_return = IMAGE_ERROR;
+ return binder::Status::ok();
+ }
+ }
+ android::base::unique_fd fd(open(device_path.c_str(), O_RDONLY | O_CLOEXEC));
+ if (!fd.ok()) {
+ PLOG(ERROR) << "Fail to open mapped device: " << device_path;
+ *_aidl_return = IMAGE_ERROR;
+ return binder::Status::ok();
+ }
+ bool ok = GetAvbPublicKeyFromFd(fd.get(), dst);
+ fd = {};
+ if (!ok) {
+ LOG(ERROR) << "Failed to extract AVB public key";
+ *_aidl_return = IMAGE_ERROR;
+ return binder::Status::ok();
+ }
+ *_aidl_return = IMAGE_OK;
+ return binder::Status::ok();
+}
+
binder::Status ImageService::zeroFillNewImage(const std::string& name, int64_t bytes) {
if (!CheckUid()) return UidSecurityError();
@@ -424,8 +661,39 @@
if (bytes < 0) {
return BinderError("Cannot use negative values");
}
- if (!impl_->ZeroFillNewImage(name, bytes)) {
- return BinderError("Failed to fill image with zeros");
+ auto res = impl_->ZeroFillNewImage(name, bytes);
+ if (!res.is_ok()) {
+ return BinderError("Failed to fill image with zeros: " + res.string(), res.error_code());
+ }
+ return binder::Status::ok();
+}
+
+binder::Status ImageService::removeAllImages() {
+ if (!CheckUid()) return UidSecurityError();
+
+ std::lock_guard<std::mutex> guard(parent_->lock());
+ if (!impl_->RemoveAllImages()) {
+ return BinderError("Failed to remove all images");
+ }
+ return binder::Status::ok();
+}
+
+binder::Status ImageService::removeDisabledImages() {
+ if (!CheckUid()) return UidSecurityError();
+
+ std::lock_guard<std::mutex> guard(parent_->lock());
+ if (!impl_->RemoveDisabledImages()) {
+ return BinderError("Failed to remove disabled images");
+ }
+ return binder::Status::ok();
+}
+
+binder::Status ImageService::getMappedImageDevice(const std::string& name, std::string* device) {
+ if (!CheckUid()) return UidSecurityError();
+
+ std::lock_guard<std::mutex> guard(parent_->lock());
+ if (!impl_->GetMappedImageDevice(name, device)) {
+ *device = "";
}
return binder::Status::ok();
}
@@ -441,14 +709,20 @@
auto in_metadata_dir = kImageMetadataPrefix + prefix;
auto in_data_dir = kImageDataPrefix + prefix;
+ auto install_dir_file = DsuInstallDirFile(GetDsuSlot(prefix));
+ std::string in_data_dir_tmp;
+ if (android::base::ReadFileToString(install_dir_file, &in_data_dir_tmp)) {
+ in_data_dir = in_data_dir_tmp;
+ LOG(INFO) << "load " << install_dir_file << ":" << in_data_dir;
+ }
std::string metadata_dir, data_dir;
if (!android::base::Realpath(in_metadata_dir, &metadata_dir)) {
- PLOG(ERROR) << "realpath failed: " << metadata_dir;
+ PLOG(ERROR) << "realpath failed for metadata: " << in_metadata_dir;
return BinderError("Invalid path");
}
if (!android::base::Realpath(in_data_dir, &data_dir)) {
- PLOG(ERROR) << "realpath failed: " << data_dir;
+ PLOG(ERROR) << "realpath failed for data: " << in_data_dir;
return BinderError("Invalid path");
}
if (!android::base::StartsWith(metadata_dir, kImageMetadataPrefix) ||
@@ -503,27 +777,27 @@
return info.f_type == MSDOS_SUPER_MAGIC;
}
-int GsiService::ValidateInstallParams(GsiInstallParams* params) {
+int GsiService::ValidateInstallParams(std::string& install_dir) {
// 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 = kDefaultDsuImageFolder;
+ if (install_dir.empty() || install_dir == "/data/gsi") {
+ install_dir = kDefaultDsuImageFolder;
}
// Normalize the path and add a trailing slash.
- std::string origInstallDir = params->installDir;
- if (!android::base::Realpath(origInstallDir, ¶ms->installDir)) {
+ std::string origInstallDir = install_dir;
+ if (!android::base::Realpath(origInstallDir, &install_dir)) {
PLOG(ERROR) << "realpath failed: " << origInstallDir;
return INSTALL_ERROR_GENERIC;
}
// Ensure the path ends in / for consistency.
- if (!android::base::EndsWith(params->installDir, "/")) {
- params->installDir += "/";
+ if (!android::base::EndsWith(install_dir, "/")) {
+ install_dir += "/";
}
// Currently, we can only install to /data/gsi/ or external storage.
- if (IsExternalStoragePath(params->installDir)) {
+ if (IsExternalStoragePath(install_dir)) {
Fstab fstab;
if (!ReadDefaultFstab(&fstab)) {
LOG(ERROR) << "cannot read default fstab";
@@ -538,28 +812,38 @@
LOG(ERROR) << "cannot install GSIs to external media if verity uses check_at_most_once";
return INSTALL_ERROR_GENERIC;
}
- } else if (params->installDir != kDefaultDsuImageFolder) {
- 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;
+ } else if (install_dir != kDefaultDsuImageFolder) {
+ LOG(ERROR) << "cannot install DSU to " << install_dir;
return INSTALL_ERROR_GENERIC;
}
return INSTALL_OK;
}
+std::string GsiService::GetActiveDsuSlot() {
+ if (!install_dir_.empty()) {
+ return GetDsuSlot(install_dir_);
+ } else {
+ std::string active_dsu;
+ return GetActiveDsu(&active_dsu) ? active_dsu : "";
+ }
+}
+
+std::string GsiService::GetActiveInstalledImageDir() {
+ // Just in case an install was left hanging.
+ if (installer_) {
+ return installer_->install_dir();
+ } else {
+ return GetInstalledImageDir();
+ }
+}
+
std::string GsiService::GetInstalledImageDir() {
// If there's no install left, just return /data/gsi since that's where
// installs go by default.
+ std::string active_dsu;
std::string dir;
- if (android::base::ReadFileToString(kDsuInstallDirFile, &dir)) {
+ if (GetActiveDsu(&active_dsu) &&
+ android::base::ReadFileToString(DsuInstallDirFile(active_dsu), &dir)) {
return dir;
}
return kDefaultDsuImageFolder;
@@ -570,7 +854,6 @@
LOG(ERROR) << "no gsi installed - cannot re-enable";
return INSTALL_ERROR_GENERIC;
}
-
std::string boot_key;
if (!GetInstallStatus(&boot_key)) {
PLOG(ERROR) << "read " << kDsuInstallStatusFile;
@@ -580,28 +863,43 @@
LOG(ERROR) << "GSI is not currently disabled";
return INSTALL_ERROR_GENERIC;
}
-
- installer_ = std::make_unique<GsiInstaller>(this, GetInstalledImageDir());
- return installer_->ReenableGsi(one_shot);
+ if (IsGsiRunning()) {
+ if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) {
+ return IGsiService::INSTALL_ERROR_GENERIC;
+ }
+ return IGsiService::INSTALL_OK;
+ }
+ if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) {
+ return IGsiService::INSTALL_ERROR_GENERIC;
+ }
+ return IGsiService::INSTALL_OK;
}
-bool GsiService::RemoveGsiFiles(const std::string& install_dir, bool wipeUserdata) {
+bool GsiService::RemoveGsiFiles(const std::string& install_dir) {
bool ok = true;
- if (auto manager = ImageManager::Open(kDsuMetadataDir, install_dir)) {
- ok &= manager->DeleteBackingImage("system_gsi");
- if (wipeUserdata) {
- ok &= manager->DeleteBackingImage("userdata_gsi");
+ auto active_dsu = GetDsuSlot(install_dir);
+ if (auto manager = ImageManager::Open(MetadataDir(active_dsu), install_dir)) {
+ std::vector<std::string> images = manager->GetAllBackingImages();
+ for (auto&& image : images) {
+ if (!android::base::EndsWith(image, kDsuPostfix)) {
+ continue;
+ }
+ if (manager->IsImageMapped(image)) {
+ ok &= manager->UnmapImageDevice(image);
+ }
+ ok &= manager->DeleteBackingImage(image);
}
}
-
+ auto dsu_slot = GetDsuSlot(install_dir);
std::vector<std::string> files{
kDsuInstallStatusFile,
kDsuOneShotBootFile,
- kDsuInstallDirFile,
+ DsuInstallDirFile(dsu_slot),
+ GetCompleteIndication(dsu_slot),
};
for (const auto& file : files) {
std::string message;
- if (!android::base::RemoveFileIfExists(file, &message)) {
+ if (!RemoveFileIfExists(file, &message)) {
LOG(ERROR) << message;
ok = false;
}
@@ -625,11 +923,63 @@
return true;
}
+std::string GsiService::GetCompleteIndication(const std::string& dsu_slot) {
+ return DSU_METADATA_PREFIX + dsu_slot + "/complete";
+}
+
+bool GsiService::IsInstallationComplete(const std::string& dsu_slot) {
+ if (access(kDsuInstallStatusFile, F_OK) != 0) {
+ return false;
+ }
+ std::string file = GetCompleteIndication(dsu_slot);
+ std::string content;
+ if (!ReadFileToString(file, &content)) {
+ return false;
+ }
+ return content == "OK";
+}
+
+std::vector<std::string> GsiService::GetInstalledDsuSlots() {
+ std::vector<std::string> dsu_slots;
+ auto d = std::unique_ptr<DIR, decltype(&closedir)>(opendir(DSU_METADATA_PREFIX), closedir);
+ if (d != nullptr) {
+ struct dirent* de;
+ while ((de = readdir(d.get())) != nullptr) {
+ if (de->d_name[0] == '.') {
+ continue;
+ }
+ auto dsu_slot = std::string(de->d_name);
+ if (access(DsuInstallDirFile(dsu_slot).c_str(), F_OK) != 0) {
+ continue;
+ }
+ dsu_slots.push_back(dsu_slot);
+ }
+ }
+ return dsu_slots;
+}
+
+void GsiService::CleanCorruptedInstallation() {
+ for (auto&& slot : GetInstalledDsuSlots()) {
+ bool is_complete = IsInstallationComplete(slot);
+ if (!is_complete) {
+ LOG(INFO) << "CleanCorruptedInstallation for slot: " << slot;
+ std::string install_dir;
+ if (!android::base::ReadFileToString(DsuInstallDirFile(slot), &install_dir) ||
+ !RemoveGsiFiles(install_dir)) {
+ LOG(ERROR) << "Failed to CleanCorruptedInstallation on " << slot;
+ }
+ }
+ }
+}
+
void GsiService::RunStartupTasks() {
- if (!IsGsiInstalled()) {
+ CleanCorruptedInstallation();
+
+ std::string active_dsu;
+ if (!GetActiveDsu(&active_dsu)) {
+ PLOG(INFO) << "no DSU";
return;
}
-
std::string boot_key;
if (!GetInstallStatus(&boot_key)) {
PLOG(ERROR) << "read " << kDsuInstallStatusFile;
@@ -639,7 +989,7 @@
if (!IsGsiRunning()) {
// Check if a wipe was requested from fastboot or adb-in-gsi.
if (boot_key == kInstallStatusWipe) {
- RemoveGsiFiles(GetInstalledImageDir(), true /* wipeUserdata */);
+ RemoveGsiFiles(GetInstalledImageDir());
}
} else {
// NB: When single-boot is enabled, init will write "disabled" into the
@@ -655,5 +1005,47 @@
}
}
+static bool GetAvbPublicKeyFromFd(int fd, AvbPublicKey* dst) {
+ // Read the AVB footer from EOF.
+ int64_t total_size = get_block_device_size(fd);
+ int64_t footer_offset = total_size - AVB_FOOTER_SIZE;
+ std::array<uint8_t, AVB_FOOTER_SIZE> footer_bytes;
+ if (!ReadFullyAtOffset(fd, footer_bytes.data(), AVB_FOOTER_SIZE, footer_offset)) {
+ PLOG(ERROR) << "cannot read AVB footer";
+ return false;
+ }
+ // Validate the AVB footer data and byte swap to native byte order.
+ AvbFooter footer;
+ if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_bytes.data(), &footer)) {
+ LOG(ERROR) << "invalid AVB footer";
+ return false;
+ }
+ // Read the VBMeta image.
+ std::vector<uint8_t> vbmeta_bytes(footer.vbmeta_size);
+ if (!ReadFullyAtOffset(fd, vbmeta_bytes.data(), vbmeta_bytes.size(), footer.vbmeta_offset)) {
+ PLOG(ERROR) << "cannot read VBMeta image";
+ return false;
+ }
+ // Validate the VBMeta image and retrieve AVB public key.
+ // After a successful call to avb_vbmeta_image_verify(), public_key_data
+ // will point to the serialized AVB public key, in the same format generated
+ // by the `avbtool extract_public_key` command.
+ const uint8_t* public_key_data;
+ size_t public_key_size;
+ AvbVBMetaVerifyResult result = avb_vbmeta_image_verify(vbmeta_bytes.data(), vbmeta_bytes.size(),
+ &public_key_data, &public_key_size);
+ if (result != AVB_VBMETA_VERIFY_RESULT_OK) {
+ LOG(ERROR) << "invalid VBMeta image: " << avb_vbmeta_verify_result_to_string(result);
+ return false;
+ }
+ if (public_key_data != nullptr) {
+ dst->bytes.resize(public_key_size);
+ memcpy(dst->bytes.data(), public_key_data, public_key_size);
+ dst->sha1.resize(SHA_DIGEST_LENGTH);
+ SHA1(public_key_data, public_key_size, dst->sha1.data());
+ }
+ return true;
+}
+
} // namespace gsi
} // namespace android
diff --git a/gsi_service.h b/gsi_service.h
index 1df4aeb..a853e2b 100644
--- a/gsi_service.h
+++ b/gsi_service.h
@@ -30,7 +30,7 @@
#include <liblp/builder.h>
#include "libgsi/libgsi.h"
-#include "gsi_installer.h"
+#include "partition_installer.h"
namespace android {
namespace gsi {
@@ -57,7 +57,10 @@
static android::sp<IGsiService> Get(Gsid* parent);
- binder::Status beginGsiInstall(const GsiInstallParams& params, int* _aidl_return) override;
+ binder::Status openInstall(const std::string& install_dir, int* _aidl_return) override;
+ binder::Status closeInstall(int32_t* _aidl_return) override;
+ binder::Status createPartition(const ::std::string& name, int64_t size, bool readOnly,
+ int32_t* _aidl_return) override;
binder::Status commitGsiChunkFromStream(const ::android::os::ParcelFileDescriptor& stream,
int64_t bytes, bool* _aidl_return) override;
binder::Status getInstallProgress(::android::gsi::GsiProgress* _aidl_return) override;
@@ -65,7 +68,7 @@
bool* _aidl_return) override;
binder::Status commitGsiChunkFromAshmem(int64_t bytes, bool* _aidl_return) override;
binder::Status cancelGsiInstall(bool* _aidl_return) override;
- binder::Status enableGsi(bool oneShot, int* _aidl_return) override;
+ binder::Status enableGsi(bool oneShot, const std::string& dsuSlot, int* _aidl_return) override;
binder::Status isGsiEnabled(bool* _aidl_return) override;
binder::Status removeGsi(bool* _aidl_return) override;
binder::Status disableGsi(bool* _aidl_return) override;
@@ -73,9 +76,13 @@
binder::Status isGsiRunning(bool* _aidl_return) override;
binder::Status isGsiInstallInProgress(bool* _aidl_return) override;
binder::Status getInstalledGsiImageDir(std::string* _aidl_return) override;
- binder::Status wipeGsiUserdata(int* _aidl_return) override;
+ binder::Status getActiveDsuSlot(std::string* _aidl_return) override;
+ binder::Status getInstalledDsuSlots(std::vector<std::string>* _aidl_return) override;
+ binder::Status zeroPartition(const std::string& name, int* _aidl_return) override;
binder::Status openImageService(const std::string& prefix,
android::sp<IImageService>* _aidl_return) override;
+ binder::Status dumpDeviceMapperDevices(std::string* _aidl_return) override;
+ binder::Status getAvbPublicKey(AvbPublicKey* dst, int32_t* _aidl_return) override;
// This is in GsiService, rather than GsiInstaller, since we need to access
// it outside of the main lock which protects the unique_ptr.
@@ -83,26 +90,37 @@
void UpdateProgress(int status, int64_t bytes_processed);
// Helper methods for GsiInstaller.
- static bool RemoveGsiFiles(const std::string& install_dir, bool wipeUserdata);
+ static bool RemoveGsiFiles(const std::string& install_dir);
bool should_abort() const { return should_abort_; }
Gsid* parent() const { return parent_.get(); }
static void RunStartupTasks();
static std::string GetInstalledImageDir();
+ std::string GetActiveDsuSlot();
+ std::string GetActiveInstalledImageDir();
+
+ static std::vector<std::string> GetInstalledDsuSlots();
private:
GsiService(Gsid* parent);
- int ValidateInstallParams(GsiInstallParams* params);
+ static int ValidateInstallParams(std::string& install_dir);
bool DisableGsiInstall();
int ReenableGsi(bool one_shot);
+ static void CleanCorruptedInstallation();
+ static int SaveInstallation(const std::string&);
+ static bool IsInstallationComplete(const std::string&);
+ static std::string GetCompleteIndication(const std::string&);
enum class AccessLevel { System, SystemOrShell };
binder::Status CheckUid(AccessLevel level = AccessLevel::System);
+ bool CreateInstallStatusFile();
+ bool SetBootMode(bool one_shot);
static android::wp<GsiService> sInstance;
+ std::string install_dir_ = {};
android::sp<Gsid> parent_;
- std::unique_ptr<GsiInstaller> installer_;
+ std::unique_ptr<PartitionInstaller> installer_;
// These are initialized or set in StartInstall().
std::atomic<bool> should_abort_ = false;
diff --git a/gsi_tool.cpp b/gsi_tool.cpp
index e3718c7..a38bf20 100644
--- a/gsi_tool.cpp
+++ b/gsi_tool.cpp
@@ -17,6 +17,7 @@
#include <getopt.h>
#include <stdio.h>
#include <sysexits.h>
+#include <unistd.h>
#include <chrono>
#include <condition_variable>
@@ -30,6 +31,8 @@
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <android/gsi/IGsiService.h>
#include <android/gsi/IGsid.h>
@@ -42,6 +45,8 @@
using namespace std::chrono_literals;
using android::sp;
+using android::base::Split;
+using android::base::StringPrintf;
using CommandCallback = std::function<int(sp<IGsiService>, int, char**)>;
static int Disable(sp<IGsiService> gsid, int argc, char** argv);
@@ -187,21 +192,23 @@
};
static int Install(sp<IGsiService> gsid, int argc, char** argv) {
+ constexpr const char* kDefaultPartition = "system";
struct option options[] = {
{"install-dir", required_argument, nullptr, 'i'},
{"gsi-size", required_argument, nullptr, 's'},
{"no-reboot", no_argument, nullptr, 'n'},
{"userdata-size", required_argument, nullptr, 'u'},
+ {"partition-name", required_argument, nullptr, 'p'},
{"wipe", no_argument, nullptr, 'w'},
{nullptr, 0, nullptr, 0},
};
- GsiInstallParams params;
- params.gsiSize = 0;
- params.userdataSize = 0;
- params.wipeUserdata = false;
+ int64_t gsiSize = 0;
+ int64_t userdataSize = 0;
+ bool wipeUserdata = false;
bool reboot = true;
-
+ std::string installDir = "";
+ std::string partition = kDefaultPartition;
if (getuid() != 0) {
std::cerr << "must be root to install a GSI" << std::endl;
return EX_NOPERM;
@@ -210,24 +217,26 @@
int rv, index;
while ((rv = getopt_long_only(argc, argv, "", options, &index)) != -1) {
switch (rv) {
+ case 'p':
+ partition = optarg;
+ break;
case 's':
- if (!android::base::ParseInt(optarg, ¶ms.gsiSize) || params.gsiSize <= 0) {
+ if (!android::base::ParseInt(optarg, &gsiSize) || gsiSize <= 0) {
std::cerr << "Could not parse image size: " << optarg << std::endl;
return EX_USAGE;
}
break;
case 'u':
- if (!android::base::ParseInt(optarg, ¶ms.userdataSize) ||
- params.userdataSize < 0) {
+ if (!android::base::ParseInt(optarg, &userdataSize) || userdataSize < 0) {
std::cerr << "Could not parse image size: " << optarg << std::endl;
return EX_USAGE;
}
break;
case 'i':
- params.installDir = optarg;
+ installDir = optarg;
break;
case 'w':
- params.wipeUserdata = true;
+ wipeUserdata = true;
break;
case 'n':
reboot = false;
@@ -235,7 +244,7 @@
}
}
- if (params.gsiSize <= 0) {
+ if (gsiSize <= 0) {
std::cerr << "Must specify --gsi-size." << std::endl;
return EX_USAGE;
}
@@ -253,31 +262,52 @@
std::cerr << "Error duplicating descriptor: " << strerror(errno) << std::endl;
return EX_SOFTWARE;
}
-
// Note: the progress bar needs to be re-started in between each call.
ProgressBar progress(gsid);
progress.Display();
-
int error;
- auto status = gsid->beginGsiInstall(params, &error);
+ auto status = gsid->openInstall(installDir, &error);
+ if (!status.isOk() || error != IGsiService::INSTALL_OK) {
+ std::cerr << "Could not open DSU installation: " << ErrorMessage(status, error) << "\n";
+ return EX_SOFTWARE;
+ }
+ if (partition == kDefaultPartition) {
+ auto status = gsid->createPartition("userdata", userdataSize, false, &error);
+ if (!status.isOk() || error != IGsiService::INSTALL_OK) {
+ std::cerr << "Could not start live image install: " << ErrorMessage(status, error)
+ << "\n";
+ return EX_SOFTWARE;
+ }
+ }
+
+ status = gsid->createPartition(partition, gsiSize, true, &error);
if (!status.isOk() || error != IGsiService::INSTALL_OK) {
std::cerr << "Could not start live image install: " << ErrorMessage(status, error) << "\n";
return EX_SOFTWARE;
}
-
android::os::ParcelFileDescriptor stream(std::move(input));
bool ok = false;
progress.Display();
- status = gsid->commitGsiChunkFromStream(stream, params.gsiSize, &ok);
+ status = gsid->commitGsiChunkFromStream(stream, gsiSize, &ok);
if (!ok) {
std::cerr << "Could not commit live image data: " << ErrorMessage(status) << "\n";
return EX_SOFTWARE;
}
+ status = gsid->closeInstall(&error);
+ if (!status.isOk() || error != IGsiService::INSTALL_OK) {
+ std::cerr << "Could not close DSU installation: " << ErrorMessage(status, error) << "\n";
+ return EX_SOFTWARE;
+ }
progress.Finish();
-
- status = gsid->enableGsi(true, &error);
+ std::string dsuSlot;
+ status = gsid->getActiveDsuSlot(&dsuSlot);
+ if (!status.isOk()) {
+ std::cerr << "Could not get the active DSU slot: " << ErrorMessage(status) << "\n";
+ return EX_SOFTWARE;
+ }
+ status = gsid->enableGsi(true, dsuSlot, &error);
if (!status.isOk() || error != IGsiService::INSTALL_OK) {
std::cerr << "Could not make live image bootable: " << ErrorMessage(status, error) << "\n";
return EX_SOFTWARE;
@@ -344,7 +374,7 @@
}
int error;
- status = gsid->wipeGsiUserdata(&error);
+ status = gsid->zeroPartition("userdata", &error);
if (!status.isOk() || error) {
std::cerr << "Could not wipe GSI userdata: " << ErrorMessage(status, error) << "\n";
return EX_SOFTWARE;
@@ -386,21 +416,43 @@
if (getuid() != 0) {
return 0;
}
- sp<IImageService> image_service = nullptr;
- status = gsid->openImageService("dsu", &image_service);
- if (!status.isOk()) {
- std::cerr << "error: " << status.exceptionMessage().string() << std::endl;
- return EX_SOFTWARE;
- }
- std::vector<std::string> images;
- status = image_service->getAllBackingImages(&images);
- if (!status.isOk()) {
- std::cerr << "error: " << status.exceptionMessage().string() << std::endl;
- return EX_SOFTWARE;
- }
- for (auto&& image : images) {
- std::cout << "installed: " << image << std::endl;
+ std::vector<std::string> dsu_slots;
+ status = gsid->getInstalledDsuSlots(&dsu_slots);
+ if (!status.isOk()) {
+ std::cerr << status.exceptionMessage().string() << std::endl;
+ return EX_SOFTWARE;
+ }
+ int n = 0;
+ for (auto&& dsu_slot : dsu_slots) {
+ std::cout << "[" << n++ << "] " << dsu_slot << std::endl;
+ sp<IImageService> image_service = nullptr;
+ status = gsid->openImageService("dsu/" + dsu_slot + "/", &image_service);
+ if (!status.isOk()) {
+ std::cerr << "error: " << status.exceptionMessage().string() << std::endl;
+ return EX_SOFTWARE;
+ }
+ std::vector<std::string> images;
+ status = image_service->getAllBackingImages(&images);
+ if (!status.isOk()) {
+ std::cerr << "error: " << status.exceptionMessage().string() << std::endl;
+ return EX_SOFTWARE;
+ }
+ for (auto&& image : images) {
+ std::cout << "installed: " << image << std::endl;
+ AvbPublicKey public_key;
+ int err = 0;
+ status = image_service->getAvbPublicKey(image, &public_key, &err);
+ std::cout << "AVB public key (sha1): ";
+ if (!public_key.bytes.empty()) {
+ for (auto b : public_key.sha1) {
+ std::cout << StringPrintf("%02x", b & 255);
+ }
+ std::cout << std::endl;
+ } else {
+ std::cout << "[NONE]" << std::endl;
+ }
+ }
}
return 0;
}
@@ -421,9 +473,10 @@
static int Enable(sp<IGsiService> gsid, int argc, char** argv) {
bool one_shot = false;
-
+ std::string dsuSlot = {};
struct option options[] = {
{"single-boot", no_argument, nullptr, 's'},
+ {"dsuslot", required_argument, nullptr, 'd'},
{nullptr, 0, nullptr, 0},
};
int rv, index;
@@ -432,6 +485,9 @@
case 's':
one_shot = true;
break;
+ case 'd':
+ dsuSlot = optarg;
+ break;
default:
std::cerr << "Unrecognized argument to enable\n";
return EX_USAGE;
@@ -451,9 +507,15 @@
std::cerr << "Cannot enable or disable while an installation is in progress." << std::endl;
return EX_SOFTWARE;
}
-
+ if (dsuSlot.empty()) {
+ auto status = gsid->getActiveDsuSlot(&dsuSlot);
+ if (!status.isOk()) {
+ std::cerr << "Could not get the active DSU slot: " << ErrorMessage(status) << "\n";
+ return EX_SOFTWARE;
+ }
+ }
int error;
- auto status = gsid->enableGsi(one_shot, &error);
+ auto status = gsid->enableGsi(one_shot, dsuSlot, &error);
if (!status.isOk() || error != IGsiService::INSTALL_OK) {
std::cerr << "Error re-enabling GSI: " << ErrorMessage(status, error) << "\n";
return EX_SOFTWARE;
@@ -493,7 +555,8 @@
" %s <disable|install|wipe|status> [options]\n"
"\n"
" disable Disable the currently installed GSI.\n"
- " enable [-s, --single-boot]\n"
+ " enable [-s, --single-boot]\n"
+ " [-d, --dsuslot slotname]\n"
" Enable a previously disabled GSI.\n"
" install Install a new GSI. Specify the image size with\n"
" --gsi-size and the desired userdata size with\n"
diff --git a/gsid.rc b/gsid.rc
index cd69917..1640a73 100644
--- a/gsid.rc
+++ b/gsid.rc
@@ -8,11 +8,13 @@
mkdir /metadata/gsi 0771 root system
mkdir /metadata/gsi/dsu 0771 root system
mkdir /metadata/gsi/ota 0771 root system
+ mkdir /metadata/gsi/remount 0771 root system
on post-fs-data
- mkdir /data/gsi 0700 root root
+ mkdir /data/gsi 0700 root root encryption=None
mkdir /data/gsi/dsu 0700 root root
mkdir /data/gsi/ota 0700 root root
+ mkdir /data/gsi/remount 0700 root root
on boot
exec_background - root root -- /system/bin/gsid run-startup-tasks
diff --git a/include/libgsi/libgsi.h b/include/libgsi/libgsi.h
index 9b24f47..987797a 100644
--- a/include/libgsi/libgsi.h
+++ b/include/libgsi/libgsi.h
@@ -23,11 +23,25 @@
static constexpr char kGsiServiceName[] = "gsiservice";
-static constexpr char kGsiBootedIndicatorFile[] = "/metadata/gsi/dsu/booted";
+#define DSU_METADATA_PREFIX "/metadata/gsi/dsu/"
-static constexpr char kGsiLpNamesFile[] = "/metadata/gsi/dsu/lp_names";
+static constexpr char kGsiBootedIndicatorFile[] = DSU_METADATA_PREFIX "booted";
-static constexpr char kDsuLpMetadataFile[] = "/metadata/gsi/dsu/lp_metadata";
+static constexpr char kGsiLpNamesFile[] = DSU_METADATA_PREFIX "lp_names";
+
+static constexpr char kDsuActiveFile[] = DSU_METADATA_PREFIX "active";
+
+static inline std::string DsuLpMetadataFile(const std::string& dsu_slot) {
+ return DSU_METADATA_PREFIX + dsu_slot + "/lp_metadata";
+}
+
+static inline std::string DsuInstallDirFile(const std::string& dsu_slot) {
+ return DSU_METADATA_PREFIX + dsu_slot + "/install_dir";
+}
+
+// install_dir "/data/gsi/dsu/dsu" has a slot name "dsu"
+// install_dir "/data/gsi/dsu/dsu2" has a slot name "dsu2"
+std::string GetDsuSlot(const std::string& install_dir);
static constexpr char kGsiBootedProp[] = "ro.gsid.image_running";
@@ -35,6 +49,10 @@
static constexpr int kMaxBootAttempts = 1;
+// Get the currently active dsu slot
+// Return true on success
+bool GetActiveDsu(std::string* active_dsu);
+
// Returns true if the currently running system image is a live GSI.
bool IsGsiRunning();
diff --git a/libfiemap/.clang-format b/libfiemap/.clang-format
deleted file mode 120000
index 8b770a1..0000000
--- a/libfiemap/.clang-format
+++ /dev/null
@@ -1 +0,0 @@
-../../.clang-format-4
\ No newline at end of file
diff --git a/libfiemap/Android.bp b/libfiemap/Android.bp
deleted file mode 100644
index d426e27..0000000
--- a/libfiemap/Android.bp
+++ /dev/null
@@ -1,131 +0,0 @@
-//
-// Copyright (C) 2018 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.
-//
-
-cc_library_headers {
- name: "libfiemap_headers",
- recovery_available: true,
- export_include_dirs: ["include"],
-}
-
-cc_defaults {
- name: "libfiemap_defaults",
- defaults: ["fs_mgr_defaults"],
- cflags: [
- "-D_FILE_OFFSET_BITS=64",
- "-Wall",
- "-Werror",
- ],
-
- srcs: [
- "fiemap_writer.cpp",
- "image_manager.cpp",
- "metadata.cpp",
- "split_fiemap_writer.cpp",
- "utility.cpp",
- ],
-
- static_libs: [
- "libdm",
- "libext2_uuid",
- "libext4_utils",
- "liblp",
- "libfs_mgr",
- ],
-
- shared_libs: [
- "libbase",
- ],
-
- header_libs: [
- "libfiemap_headers",
- "liblog_headers",
- ],
-
- export_shared_lib_headers: [
- "libbase",
- ],
-
- export_header_lib_headers: [
- "libfiemap_headers",
- ],
-}
-
-// Open up a binder IImageManager interface.
-cc_library_static {
- name: "libfiemap_binder",
- defaults: ["libfiemap_defaults"],
- srcs: [
- "binder.cpp",
- ],
- whole_static_libs: [
- "gsi_aidl_interface-cpp",
- "libgsi",
- ],
- shared_libs: [
- "libbinder",
- ],
-}
-
-// Open up a passthrough IImageManager interface. Use libfiemap_binder whenever
-// possible. This should only be used when binder is not available.
-cc_library_static {
- name: "libfiemap_passthrough",
- defaults: ["libfiemap_defaults"],
- recovery_available: true,
- srcs: [
- "passthrough.cpp",
- ],
-}
-
-cc_test {
- name: "fiemap_writer_test",
- defaults: ["libfiemap_defaults"],
- static_libs: [
- "libbase",
- "libdm",
- "liblog",
- ],
-
- data: [
- "testdata/unaligned_file",
- "testdata/file_4k",
- "testdata/file_32k",
- ],
-
- srcs: [
- "fiemap_writer_test.cpp",
- ],
-}
-
-cc_test {
- name: "fiemap_image_test",
- defaults: ["libfiemap_defaults"],
- static_libs: [
- "libdm",
- "libext4_utils",
- "libfs_mgr",
- "liblp",
- ],
- shared_libs: [
- "libcrypto",
- "libcrypto_utils",
- "libcutils",
- "liblog",
- ],
- srcs: [
- "image_test.cpp",
- ],
-}
diff --git a/libfiemap/Android.mk b/libfiemap/Android.mk
deleted file mode 100644
index 3c07b8e..0000000
--- a/libfiemap/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := VtsFiemapWriterTest
--include test/vts/tools/build/Android.host_config.mk
diff --git a/libfiemap/AndroidTest.xml b/libfiemap/AndroidTest.xml
deleted file mode 100644
index 44c80fc..0000000
--- a/libfiemap/AndroidTest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<configuration description="Config for VTS VtsFiemapWriterTest">
- <option name="config-descriptor:metadata" key="plan" value="vts-kernel" />
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
- <option name="abort-on-push-failure" value="false"/>
- <option name="push-group" value="HostDrivenTest.push"/>
- </target_preparer>
- <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
- <option name="test-module-name" value="VtsFiemapWriterTest"/>
- <option name="binary-test-source" value="_32bit::DATA/nativetest/fiemap_writer_test/fiemap_writer_test" />
- <option name="binary-test-source" value="_64bit::DATA/nativetest64/fiemap_writer_test/fiemap_writer_test" />
- <option name="binary-test-type" value="gtest"/>
- <option name="precondition-first-api-level" value="29" />
- <option name="test-timeout" value="1m"/>
- </test>
-</configuration>
diff --git a/libfiemap/README.md b/libfiemap/README.md
deleted file mode 100644
index 62d610a..0000000
--- a/libfiemap/README.md
+++ /dev/null
@@ -1,75 +0,0 @@
-libfiemap
-=============
-
-`libfiemap` is a library for creating block-devices that are backed by
-storage in read-write partitions. It exists primary for gsid. Generally, the
-library works by using `libfiemap_writer` to allocate large files within
-filesystem, and then tracks their extents.
-
-There are three main uses for `libfiemap`:
- - Creating images that will act as block devices. For example, gsid needs to
- create a `system_gsi` image to store Dynamic System Updates.
- - Mapping the image as a block device while /data is mounted. This is fairly
- tricky and is described in more detail below.
- - Mapping the image as a block device during first-stage init. This is simple
- because it uses the same logic from dynamic partitions.
-
-Image creation is done through `SplitFiemap`. Depending on the file system,
-a large image may have to be split into multiple files. On Ext4 the limit is
-16GiB and on FAT32 it's 4GiB. Images are saved into `/data/gsi/<name>/`
-where `<name>` is chosen by the process requesting the image.
-
-At the same time, a file called `/metadata/gsi/<name>/lp_metadata` is created.
-This is a super partition header that allows first-stage init to create dynamic
-partitions from the image files. It also tracks the canonical size of the image,
-since the file size may be larger due to alignment.
-
-Mapping
--------
-
-It is easy to make block devices out of blocks on `/data` when it is not
-mounted, so first-stage init has no issues mapping dynamic partitions from
-images. After `/data` is mounted however, there are two problems:
- - `/data` is encrypted.
- - `/dev/block/by-name/data` may be marked as in-use.
-
-We break the problem down into three scenarios.
-
-### FDE and Metadata Encrypted Devices
-
-When FDE or metadata encryption is used, `/data` is not mounted from
-`/dev/block/by-name/data`. Instead, it is mounted from an intermediate
-`dm-crypt` or `dm-default-key` device. This means the underlying device is
-not marked in use, and we can create new dm-linear devices on top of it.
-
-On these devices, a block device for an image will consist of a single
-device-mapper device with a `dm-linear` table entry for each extent in the
-backing file.
-
-### Unencrypted and FBE-encrypted Devices
-
-When a device is unencrypted, or is encrypted with FBE but not metadata
-encryption, we instead use a loop device with `LOOP_SET_DIRECT_IO` enabled.
-Since `/data/gsi` has encryption disabled, this means the raw blocks will be
-unencrypted as well.
-
-### Split Images
-
-If an image was too large to store a single file on the underlying filesystem,
-on an FBE/unencrypted device we will have multiple loop devices. In this case,
-we create a device-mapper device as well. For each loop device it will have one
-`dm-linear` table entry spanning the length of the device.
-
-State Tracking
---------------
-
-It's important that we know whether or not an image is currently in-use by a
-block device. It could be catastrophic to write to a dm-linear device if the
-underlying blocks are no longer owned by the original file. Thus, when mapping
-an image, we create a property called `gsid.mapped_image.<name>` and set it to
-the path of the block device.
-
-Additionally, we create a `/metadata/gsi/<subdir>/<name>.status` file. Each
-line in this file denotes a dependency on either a device-mapper node or a loop
-device. When deleting a block device, this file is used to release all
-resources.
diff --git a/libfiemap/binder.cpp b/libfiemap/binder.cpp
deleted file mode 100644
index dcb887a..0000000
--- a/libfiemap/binder.cpp
+++ /dev/null
@@ -1,213 +0,0 @@
-//
-// 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.
-//
-
-#if !defined(__ANDROID_RECOVERY__)
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android/gsi/IGsiService.h>
-#include <android/gsi/IGsid.h>
-#include <binder/IServiceManager.h>
-#include <libfiemap/image_manager.h>
-#include <libgsi/libgsi.h>
-
-namespace android {
-namespace fiemap {
-
-using namespace android::gsi;
-using namespace std::chrono_literals;
-
-class ImageManagerBinder final : public IImageManager {
- public:
- ImageManagerBinder(android::sp<IGsiService>&& service, android::sp<IImageService>&& manager);
- bool CreateBackingImage(const std::string& name, uint64_t size, int flags) override;
- bool DeleteBackingImage(const std::string& name) override;
- bool MapImageDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,
- std::string* path) override;
- bool UnmapImageDevice(const std::string& name) override;
- bool BackingImageExists(const std::string& name) override;
- bool IsImageMapped(const std::string& name) override;
- bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
- std::string* dev) override;
- bool ZeroFillNewImage(const std::string& name, uint64_t bytes) override;
-
- std::vector<std::string> GetAllBackingImages() override;
-
- private:
- android::sp<IGsiService> service_;
- android::sp<IImageService> manager_;
-};
-
-ImageManagerBinder::ImageManagerBinder(android::sp<IGsiService>&& service,
- android::sp<IImageService>&& manager)
- : service_(std::move(service)), manager_(std::move(manager)) {}
-
-bool ImageManagerBinder::CreateBackingImage(const std::string& name, uint64_t size, int flags) {
- auto status = manager_->createBackingImage(name, size, flags);
- if (!status.isOk()) {
- LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
- return false;
- }
- return true;
-}
-
-bool ImageManagerBinder::DeleteBackingImage(const std::string& name) {
- auto status = manager_->deleteBackingImage(name);
- if (!status.isOk()) {
- LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
- return false;
- }
- return true;
-}
-
-bool ImageManagerBinder::MapImageDevice(const std::string& name,
- const std::chrono::milliseconds& timeout_ms,
- std::string* path) {
- int32_t timeout_ms_count =
- static_cast<int32_t>(std::clamp<typename std::chrono::milliseconds::rep>(
- timeout_ms.count(), INT32_MIN, INT32_MAX));
- MappedImage map;
- auto status = manager_->mapImageDevice(name, timeout_ms_count, &map);
- if (!status.isOk()) {
- LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
- return false;
- }
- *path = map.path;
- return true;
-}
-
-bool ImageManagerBinder::UnmapImageDevice(const std::string& name) {
- auto status = manager_->unmapImageDevice(name);
- if (!status.isOk()) {
- LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
- return false;
- }
- return true;
-}
-
-bool ImageManagerBinder::BackingImageExists(const std::string& name) {
- bool retval;
- auto status = manager_->backingImageExists(name, &retval);
- if (!status.isOk()) {
- LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
- return false;
- }
- return retval;
-}
-
-bool ImageManagerBinder::IsImageMapped(const std::string& name) {
- bool retval;
- auto status = manager_->isImageMapped(name, &retval);
- if (!status.isOk()) {
- LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
- return false;
- }
- return retval;
-}
-
-bool ImageManagerBinder::MapImageWithDeviceMapper(const IPartitionOpener& opener,
- const std::string& name, std::string* dev) {
- (void)opener;
- (void)name;
- (void)dev;
- LOG(ERROR) << "MapImageWithDeviceMapper is not available over binder.";
- return false;
-}
-
-std::vector<std::string> ImageManagerBinder::GetAllBackingImages() {
- std::vector<std::string> retval;
- auto status = manager_->getAllBackingImages(&retval);
- if (!status.isOk()) {
- LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
- }
- return retval;
-}
-
-bool ImageManagerBinder::ZeroFillNewImage(const std::string& name, uint64_t bytes) {
- auto status = manager_->zeroFillNewImage(name, bytes);
- if (!status.isOk()) {
- LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
- return false;
- }
- return true;
-}
-
-static android::sp<IGsid> AcquireIGsid(const std::chrono::milliseconds& timeout_ms) {
- if (android::base::GetProperty("init.svc.gsid", "") != "running") {
- if (!android::base::SetProperty("ctl.start", "gsid") ||
- !android::base::WaitForProperty("init.svc.gsid", "running", timeout_ms)) {
- LOG(ERROR) << "Could not start the gsid service";
- return nullptr;
- }
- // Sleep for 250ms to give the service time to register.
- usleep(250 * 1000);
- }
- auto sm = android::defaultServiceManager();
- auto name = android::String16(kGsiServiceName);
- auto service = sm->checkService(name);
- return android::interface_cast<IGsid>(service);
-}
-
-static android::sp<IGsid> GetGsiService(const std::chrono::milliseconds& timeout_ms) {
- auto start_time = std::chrono::steady_clock::now();
-
- std::chrono::milliseconds elapsed = std::chrono::milliseconds::zero();
- do {
- if (auto gsid = AcquireIGsid(timeout_ms - elapsed); gsid != nullptr) {
- return gsid;
- }
- auto now = std::chrono::steady_clock::now();
- elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
- } while (elapsed <= timeout_ms);
-
- LOG(ERROR) << "Timed out trying to acquire IGsid interface";
- return nullptr;
-}
-
-std::unique_ptr<IImageManager> IImageManager::Open(const std::string& dir,
- const std::chrono::milliseconds& timeout_ms) {
- auto gsid = GetGsiService(timeout_ms);
- if (!gsid) {
- return nullptr;
- }
-
- android::sp<IGsiService> service;
- auto status = gsid->getClient(&service);
- if (!status.isOk() || !service) {
- LOG(ERROR) << "Could not acquire IGsiService";
- return nullptr;
- }
-
- android::sp<IImageService> manager;
- status = service->openImageService(dir, &manager);
- if (!status.isOk() || !manager) {
- LOG(ERROR) << "Could not acquire IImageManager: " << status.exceptionMessage().string();
- return nullptr;
- }
- return std::make_unique<ImageManagerBinder>(std::move(service), std::move(manager));
-}
-
-} // namespace fiemap
-} // namespace android
-
-#endif // __ANDROID_RECOVERY__
diff --git a/libfiemap/fiemap_writer.cpp b/libfiemap/fiemap_writer.cpp
deleted file mode 100644
index 46fcb0e..0000000
--- a/libfiemap/fiemap_writer.cpp
+++ /dev/null
@@ -1,745 +0,0 @@
-/*
- * Copyright (C) 2018 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 <libfiemap/fiemap_writer.h>
-
-#include <dirent.h>
-#include <fcntl.h>
-#include <linux/fs.h>
-#include <stdio.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/sysmacros.h>
-#include <sys/types.h>
-#include <sys/vfs.h>
-#include <unistd.h>
-
-#include <limits>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <libdm/dm.h>
-
-namespace android {
-namespace fiemap {
-
-using namespace android::dm;
-
-// We are expecting no more than 512 extents in a fiemap of the file we create.
-// If we find more, then it is treated as error for now.
-static constexpr const uint32_t kMaxExtents = 512;
-
-// TODO: Fallback to using fibmap if FIEMAP_EXTENT_MERGED is set.
-static constexpr const uint32_t kUnsupportedExtentFlags =
- FIEMAP_EXTENT_UNKNOWN | FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_DELALLOC |
- FIEMAP_EXTENT_NOT_ALIGNED | FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_DATA_TAIL |
- FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_SHARED | FIEMAP_EXTENT_MERGED;
-
-// Large file support must be enabled.
-static_assert(sizeof(off_t) == sizeof(uint64_t));
-
-static inline void cleanup(const std::string& file_path, bool created) {
- if (created) {
- unlink(file_path.c_str());
- }
-}
-
-static bool BlockDeviceToName(uint32_t major, uint32_t minor, std::string* bdev_name) {
- // The symlinks in /sys/dev/block point to the block device node under /sys/device/..
- // The directory name in the target corresponds to the name of the block device. We use
- // that to extract the block device name.
- // e.g for block device name 'ram0', there exists a symlink named '1:0' in /sys/dev/block as
- // follows.
- // 1:0 -> ../../devices/virtual/block/ram0
- std::string sysfs_path = ::android::base::StringPrintf("/sys/dev/block/%u:%u", major, minor);
- std::string sysfs_bdev;
-
- if (!::android::base::Readlink(sysfs_path, &sysfs_bdev)) {
- PLOG(ERROR) << "Failed to read link at: " << sysfs_path;
- return false;
- }
-
- *bdev_name = ::android::base::Basename(sysfs_bdev);
- // Paranoid sanity check to make sure we just didn't get the
- // input in return as-is.
- if (sysfs_bdev == *bdev_name) {
- LOG(ERROR) << "Malformed symlink for block device: " << sysfs_bdev;
- return false;
- }
-
- return true;
-}
-
-static bool ValidateDmTarget(const DeviceMapper::TargetInfo& target) {
- const auto& entry = target.spec;
- if (entry.sector_start != 0) {
- LOG(INFO) << "Stopping at target with non-zero starting sector";
- return false;
- }
-
- auto target_type = DeviceMapper::GetTargetType(entry);
- if (target_type == "bow" || target_type == "default-key" || target_type == "crypt") {
- return true;
- }
- if (target_type == "linear") {
- auto pieces = android::base::Split(target.data, " ");
- if (pieces[1] != "0") {
- LOG(INFO) << "Stopping at complex linear target with non-zero starting sector: "
- << pieces[1];
- return false;
- }
- return true;
- }
-
- LOG(INFO) << "Stopping at complex target type " << target_type;
- return false;
-}
-
-static bool DeviceMapperStackPop(const std::string& bdev, std::string* bdev_raw) {
- *bdev_raw = bdev;
-
- if (!::android::base::StartsWith(bdev, "dm-")) {
- // We are at the bottom of the device mapper stack.
- return true;
- }
-
- // Get the device name.
- auto dm_name_file = "/sys/block/" + bdev + "/dm/name";
- std::string dm_name;
- if (!android::base::ReadFileToString(dm_name_file, &dm_name)) {
- PLOG(ERROR) << "Could not read file: " << dm_name_file;
- return false;
- }
- dm_name = android::base::Trim(dm_name);
-
- auto& dm = DeviceMapper::Instance();
- std::vector<DeviceMapper::TargetInfo> table;
- if (!dm.GetTableInfo(dm_name, &table)) {
- LOG(ERROR) << "Could not read device-mapper table for " << dm_name << " at " << bdev;
- return false;
- }
-
- // The purpose of libfiemap is to provide an extent-based view into
- // a file. This is difficult if devices are not layered in a 1:1 manner;
- // we would have to translate and break up extents based on the actual
- // block mapping. Since this is too complex, we simply stop processing
- // the device-mapper stack if we encounter a complex case.
- //
- // It is up to the caller to decide whether stopping at a virtual block
- // device is allowable. In most cases it is not, because we want either
- // "userdata" or an external volume. It is useful for tests however.
- // Callers can check by comparing the device number to that of userdata,
- // or by checking whether is a device-mapper node.
- if (table.size() > 1) {
- LOG(INFO) << "Stopping at complex table for " << dm_name << " at " << bdev;
- return true;
- }
- if (!ValidateDmTarget(table[0])) {
- return true;
- }
-
- auto dm_leaf_dir = "/sys/block/" + bdev + "/slaves";
- auto d = std::unique_ptr<DIR, decltype(&closedir)>(opendir(dm_leaf_dir.c_str()), closedir);
- if (d == nullptr) {
- PLOG(ERROR) << "Failed to open: " << dm_leaf_dir;
- return false;
- }
-
- struct dirent* de;
- uint32_t num_leaves = 0;
- std::string bdev_next = "";
- while ((de = readdir(d.get())) != nullptr) {
- if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
- continue;
- }
-
- // We set the first name we find here
- if (bdev_next.empty()) {
- bdev_next = de->d_name;
- }
- num_leaves++;
- }
-
- // if we have more than one leaves, we return immediately. We can't continue to create the
- // file since we don't know how to write it out using fiemap, so it will be readable via the
- // underlying block devices later. The reader will also have to construct the same device mapper
- // target in order read the file out.
- if (num_leaves > 1) {
- LOG(ERROR) << "Found " << num_leaves << " leaf block devices under device mapper device "
- << bdev;
- return false;
- }
-
- // recursively call with the block device we found in order to pop the device mapper stack.
- return DeviceMapperStackPop(bdev_next, bdev_raw);
-}
-
-bool FiemapWriter::GetBlockDeviceForFile(const std::string& file_path, std::string* bdev_path,
- bool* uses_dm) {
- struct stat sb;
- if (stat(file_path.c_str(), &sb)) {
- PLOG(ERROR) << "Failed to get stat for: " << file_path;
- return false;
- }
-
- std::string bdev;
- if (!BlockDeviceToName(major(sb.st_dev), minor(sb.st_dev), &bdev)) {
- LOG(ERROR) << "Failed to get block device name for " << major(sb.st_dev) << ":"
- << minor(sb.st_dev);
- return false;
- }
-
- std::string bdev_raw;
- if (!DeviceMapperStackPop(bdev, &bdev_raw)) {
- LOG(ERROR) << "Failed to get the bottom of the device mapper stack for device: " << bdev;
- return false;
- }
-
- if (uses_dm) {
- *uses_dm = (bdev_raw != bdev);
- }
-
- LOG(DEBUG) << "Popped device (" << bdev_raw << ") from device mapper stack starting with ("
- << bdev << ")";
-
- *bdev_path = ::android::base::StringPrintf("/dev/block/%s", bdev_raw.c_str());
-
- // Make sure we are talking to a block device before calling it a success.
- if (stat(bdev_path->c_str(), &sb)) {
- PLOG(ERROR) << "Failed to get stat for block device: " << *bdev_path;
- return false;
- }
-
- if ((sb.st_mode & S_IFMT) != S_IFBLK) {
- PLOG(ERROR) << "File: " << *bdev_path << " is not a block device";
- return false;
- }
-
- return true;
-}
-
-static bool GetBlockDeviceSize(int bdev_fd, const std::string& bdev_path, uint64_t* bdev_size) {
- uint64_t size_in_bytes = 0;
- if (ioctl(bdev_fd, BLKGETSIZE64, &size_in_bytes)) {
- PLOG(ERROR) << "Failed to get total size for: " << bdev_path;
- return false;
- }
-
- *bdev_size = size_in_bytes;
-
- return true;
-}
-
-static uint64_t GetFileSize(const std::string& file_path) {
- struct stat sb;
- if (stat(file_path.c_str(), &sb)) {
- PLOG(ERROR) << "Failed to get size for file: " << file_path;
- return 0;
- }
-
- return sb.st_size;
-}
-
-static bool PerformFileChecks(const std::string& file_path, uint64_t file_size, uint64_t* blocksz,
- uint32_t* fs_type) {
- struct statfs64 sfs;
- if (statfs64(file_path.c_str(), &sfs)) {
- PLOG(ERROR) << "Failed to read file system status at: " << file_path;
- return false;
- }
-
- if (!sfs.f_bsize) {
- LOG(ERROR) << "Unsupported block size: " << sfs.f_bsize;
- return false;
- }
-
- // Check if the filesystem is of supported types.
- // Only ext4, f2fs, and vfat are tested and supported.
- switch (sfs.f_type) {
- case EXT4_SUPER_MAGIC:
- case F2FS_SUPER_MAGIC:
- case MSDOS_SUPER_MAGIC:
- break;
- default:
- LOG(ERROR) << "Unsupported file system type: 0x" << std::hex << sfs.f_type;
- return false;
- }
-
- uint64_t available_bytes = sfs.f_bsize * sfs.f_bavail;
- if (access(file_path.c_str(), F_OK) != 0 && available_bytes <= file_size) {
- LOG(ERROR) << "Not enough free space in file system to create file of size : " << file_size;
- return false;
- }
-
- *blocksz = sfs.f_bsize;
- *fs_type = sfs.f_type;
- return true;
-}
-
-static bool FallocateFallback(int file_fd, uint64_t block_size, uint64_t file_size,
- const std::string& file_path,
- const std::function<bool(uint64_t, uint64_t)>& on_progress) {
- // Even though this is much faster than writing zeroes, it is still slow
- // enough that we need to fire the progress callback periodically. To
- // easily achieve this, we seek in chunks. We use 1000 chunks since
- // normally we only fire the callback on 1/1000th increments.
- uint64_t bytes_per_chunk = std::max(file_size / 1000, block_size);
-
- // Seek just to the end of each chunk and write a single byte, causing
- // the filesystem to allocate blocks.
- off_t cursor = 0;
- off_t end = static_cast<off_t>(file_size);
- while (cursor < end) {
- cursor = std::min(static_cast<off_t>(cursor + bytes_per_chunk), end);
- auto rv = TEMP_FAILURE_RETRY(lseek(file_fd, cursor - 1, SEEK_SET));
- if (rv < 0) {
- PLOG(ERROR) << "Failed to lseek " << file_path;
- return false;
- }
- if (rv != cursor - 1) {
- LOG(ERROR) << "Seek returned wrong offset " << rv << " for file " << file_path;
- return false;
- }
- char buffer[] = {0};
- if (!android::base::WriteFully(file_fd, buffer, 1)) {
- PLOG(ERROR) << "Write failed: " << file_path;
- return false;
- }
- if (on_progress && !on_progress(cursor, file_size)) {
- return false;
- }
- }
- return true;
-}
-
-static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz,
- uint64_t file_size, unsigned int fs_type,
- std::function<bool(uint64_t, uint64_t)> on_progress) {
- // Reserve space for the file on the file system and write it out to make sure the extents
- // don't come back unwritten. Return from this function with the kernel file offset set to 0.
- // If the filesystem is f2fs, then we also PIN the file on disk to make sure the blocks
- // aren't moved around.
- switch (fs_type) {
- case EXT4_SUPER_MAGIC:
- case F2FS_SUPER_MAGIC:
- if (fallocate(file_fd, FALLOC_FL_ZERO_RANGE, 0, file_size)) {
- PLOG(ERROR) << "Failed to allocate space for file: " << file_path
- << " size: " << file_size;
- return false;
- }
- break;
- case MSDOS_SUPER_MAGIC:
- // fallocate() is not supported, and not needed, since VFAT does not support holes.
- // Instead we can perform a much faster allocation.
- return FallocateFallback(file_fd, blocksz, file_size, file_path, on_progress);
- default:
- LOG(ERROR) << "Missing fallocate() support for file system " << fs_type;
- return false;
- }
-
- // write zeroes in 'blocksz' byte increments until we reach file_size to make sure the data
- // blocks are actually written to by the file system and thus getting rid of the holes in the
- // file.
- auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksz), free);
- if (buffer == nullptr) {
- LOG(ERROR) << "failed to allocate memory for writing file";
- return false;
- }
-
- off64_t offset = lseek64(file_fd, 0, SEEK_SET);
- if (offset < 0) {
- PLOG(ERROR) << "Failed to seek at the beginning of : " << file_path;
- return false;
- }
-
- int permille = -1;
- while (offset < file_size) {
- if (!::android::base::WriteFully(file_fd, buffer.get(), blocksz)) {
- PLOG(ERROR) << "Failed to write" << blocksz << " bytes at offset" << offset
- << " in file " << file_path;
- return false;
- }
-
- offset += blocksz;
-
- // Don't invoke the callback every iteration - wait until a significant
- // chunk (here, 1/1000th) of the data has been processed.
- int new_permille = (static_cast<uint64_t>(offset) * 1000) / file_size;
- if (new_permille != permille && static_cast<uint64_t>(offset) != file_size) {
- if (on_progress && !on_progress(offset, file_size)) {
- return false;
- }
- permille = new_permille;
- }
- }
-
- if (lseek64(file_fd, 0, SEEK_SET) < 0) {
- PLOG(ERROR) << "Failed to reset offset at the beginning of : " << file_path;
- return false;
- }
-
- // flush all writes here ..
- if (fsync(file_fd)) {
- PLOG(ERROR) << "Failed to synchronize written file:" << file_path;
- return false;
- }
-
- // Send one last progress notification.
- if (on_progress && !on_progress(file_size, file_size)) {
- return false;
- }
- return true;
-}
-
-static bool PinFile(int file_fd, const std::string& file_path, uint32_t fs_type) {
- if (fs_type != F2FS_SUPER_MAGIC) {
- // No pinning necessary for ext4/msdos. The blocks, once allocated, are
- // expected to be fixed.
- return true;
- }
-
-// F2FS-specific ioctl
-// It requires the below kernel commit merged in v4.16-rc1.
-// 1ad71a27124c ("f2fs: add an ioctl to disable GC for specific file")
-// In android-4.4,
-// 56ee1e817908 ("f2fs: updates on v4.16-rc1")
-// In android-4.9,
-// 2f17e34672a8 ("f2fs: updates on v4.16-rc1")
-// In android-4.14,
-// ce767d9a55bc ("f2fs: updates on v4.16-rc1")
-#ifndef F2FS_IOC_SET_PIN_FILE
-#ifndef F2FS_IOCTL_MAGIC
-#define F2FS_IOCTL_MAGIC 0xf5
-#endif
-#define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32)
-#endif
-
- uint32_t pin_status = 1;
- int error = ioctl(file_fd, F2FS_IOC_SET_PIN_FILE, &pin_status);
- if (error < 0) {
- if ((errno == ENOTTY) || (errno == ENOTSUP)) {
- PLOG(ERROR) << "Failed to pin file, not supported by kernel: " << file_path;
- } else {
- PLOG(ERROR) << "Failed to pin file: " << file_path;
- }
- return false;
- }
-
- return true;
-}
-
-static bool IsFilePinned(int file_fd, const std::string& file_path, uint32_t fs_type) {
- if (fs_type != F2FS_SUPER_MAGIC) {
- // No pinning necessary for ext4 or vfat. The blocks, once allocated,
- // are expected to be fixed.
- return true;
- }
-
-// F2FS-specific ioctl
-// It requires the below kernel commit merged in v4.16-rc1.
-// 1ad71a27124c ("f2fs: add an ioctl to disable GC for specific file")
-// In android-4.4,
-// 56ee1e817908 ("f2fs: updates on v4.16-rc1")
-// In android-4.9,
-// 2f17e34672a8 ("f2fs: updates on v4.16-rc1")
-// In android-4.14,
-// ce767d9a55bc ("f2fs: updates on v4.16-rc1")
-#ifndef F2FS_IOC_GET_PIN_FILE
-#ifndef F2FS_IOCTL_MAGIC
-#define F2FS_IOCTL_MAGIC 0xf5
-#endif
-#define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32)
-#endif
-
- // f2fs: export FS_NOCOW_FL flag to user
- uint32_t flags;
- int error = ioctl(file_fd, FS_IOC_GETFLAGS, &flags);
- if (error < 0) {
- if ((errno == ENOTTY) || (errno == ENOTSUP)) {
- PLOG(ERROR) << "Failed to get flags, not supported by kernel: " << file_path;
- } else {
- PLOG(ERROR) << "Failed to get flags: " << file_path;
- }
- return false;
- }
- if (!(flags & FS_NOCOW_FL)) {
- LOG(ERROR) << "It is not pinned: " << file_path;
- return false;
- }
-
- // F2FS_IOC_GET_PIN_FILE returns the number of blocks moved.
- uint32_t moved_blocks_nr;
- error = ioctl(file_fd, F2FS_IOC_GET_PIN_FILE, &moved_blocks_nr);
- if (error < 0) {
- if ((errno == ENOTTY) || (errno == ENOTSUP)) {
- PLOG(ERROR) << "Failed to get file pin status, not supported by kernel: " << file_path;
- } else {
- PLOG(ERROR) << "Failed to get file pin status: " << file_path;
- }
- return false;
- }
-
- if (moved_blocks_nr) {
- LOG(ERROR) << moved_blocks_nr << " blocks moved in file " << file_path;
- }
- return moved_blocks_nr == 0;
-}
-
-bool FiemapWriter::HasPinnedExtents(const std::string& file_path) {
- android::base::unique_fd fd(open(file_path.c_str(), O_NOFOLLOW | O_CLOEXEC | O_RDONLY));
- if (fd < 0) {
- PLOG(ERROR) << "open: " << file_path;
- return false;
- }
-
- struct statfs64 sfs;
- if (fstatfs64(fd, &sfs)) {
- PLOG(ERROR) << "fstatfs64: " << file_path;
- return false;
- }
- return IsFilePinned(fd, file_path, sfs.f_type);
-}
-
-static bool ReadFiemap(int file_fd, const std::string& file_path,
- std::vector<struct fiemap_extent>* extents) {
- uint64_t fiemap_size =
- sizeof(struct fiemap_extent) + kMaxExtents * sizeof(struct fiemap_extent);
- auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, fiemap_size), free);
- if (buffer == nullptr) {
- LOG(ERROR) << "Failed to allocate memory for fiemap";
- return false;
- }
-
- struct fiemap* fiemap = reinterpret_cast<struct fiemap*>(buffer.get());
- fiemap->fm_start = 0;
- fiemap->fm_length = UINT64_MAX;
- // make sure file is synced to disk before we read the fiemap
- fiemap->fm_flags = FIEMAP_FLAG_SYNC;
- fiemap->fm_extent_count = kMaxExtents;
-
- if (ioctl(file_fd, FS_IOC_FIEMAP, fiemap)) {
- PLOG(ERROR) << "Failed to get FIEMAP from the kernel for file: " << file_path;
- return false;
- }
-
- if (fiemap->fm_mapped_extents == 0) {
- LOG(ERROR) << "File " << file_path << " has zero extents";
- return false;
- }
-
- // Iterate through each extent read and make sure its valid before adding it to the vector
- bool last_extent_seen = false;
- struct fiemap_extent* extent = &fiemap->fm_extents[0];
- for (uint32_t i = 0; i < fiemap->fm_mapped_extents; i++, extent++) {
- // LogExtent(i + 1, *extent);
- if (extent->fe_flags & kUnsupportedExtentFlags) {
- LOG(ERROR) << "Extent " << i + 1 << " of file " << file_path
- << " has unsupported flags";
- extents->clear();
- return false;
- }
-
- if (extent->fe_flags & FIEMAP_EXTENT_LAST) {
- last_extent_seen = true;
- if (i != (fiemap->fm_mapped_extents - 1)) {
- LOG(WARNING) << "Extents are being received out-of-order";
- }
- }
- extents->emplace_back(std::move(*extent));
- }
-
- if (!last_extent_seen) {
- // The file is possibly too fragmented.
- if (fiemap->fm_mapped_extents == kMaxExtents) {
- LOG(ERROR) << "File is too fragmented, needs more than " << kMaxExtents << " extents.";
- }
- extents->clear();
- }
-
- return last_extent_seen;
-}
-
-static bool ReadFibmap(int file_fd, const std::string& file_path,
- std::vector<struct fiemap_extent>* extents) {
- struct stat s;
- if (fstat(file_fd, &s)) {
- PLOG(ERROR) << "Failed to stat " << file_path;
- return false;
- }
-
- unsigned int blksize;
- if (ioctl(file_fd, FIGETBSZ, &blksize) < 0) {
- PLOG(ERROR) << "Failed to get FIGETBSZ for " << file_path;
- return false;
- }
- if (!blksize) {
- LOG(ERROR) << "Invalid filesystem block size: " << blksize;
- return false;
- }
-
- uint64_t num_blocks = (s.st_size + blksize - 1) / blksize;
- if (num_blocks > std::numeric_limits<uint32_t>::max()) {
- LOG(ERROR) << "Too many blocks for FIBMAP (" << num_blocks << ")";
- return false;
- }
-
- for (uint32_t last_block, block_number = 0; block_number < num_blocks; block_number++) {
- uint32_t block = block_number;
- if (ioctl(file_fd, FIBMAP, &block)) {
- PLOG(ERROR) << "Failed to get FIBMAP for file " << file_path;
- return false;
- }
- if (!block) {
- LOG(ERROR) << "Logical block " << block_number << " is a hole, which is not supported";
- return false;
- }
-
- if (!extents->empty() && block == last_block + 1) {
- extents->back().fe_length += blksize;
- } else {
- extents->push_back(fiemap_extent{.fe_logical = block_number,
- .fe_physical = static_cast<uint64_t>(block) * blksize,
- .fe_length = static_cast<uint64_t>(blksize),
- .fe_flags = 0});
- }
- last_block = block;
- }
- return true;
-}
-
-FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_size, bool create,
- std::function<bool(uint64_t, uint64_t)> progress) {
- // if 'create' is false, open an existing file and do not truncate.
- int open_flags = O_RDWR | O_CLOEXEC;
- if (create) {
- if (access(file_path.c_str(), F_OK) == 0) {
- LOG(WARNING) << "File " << file_path << " already exists, truncating";
- }
- open_flags |= O_CREAT | O_TRUNC;
- }
- ::android::base::unique_fd file_fd(
- TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags, S_IRUSR | S_IWUSR)));
- if (file_fd < 0) {
- PLOG(ERROR) << "Failed to create file at: " << file_path;
- return nullptr;
- }
-
- std::string abs_path;
- if (!::android::base::Realpath(file_path, &abs_path)) {
- PLOG(ERROR) << "Invalid file path: " << file_path;
- cleanup(file_path, create);
- return nullptr;
- }
-
- std::string bdev_path;
- if (!GetBlockDeviceForFile(abs_path, &bdev_path)) {
- LOG(ERROR) << "Failed to get block dev path for file: " << file_path;
- cleanup(abs_path, create);
- return nullptr;
- }
-
- ::android::base::unique_fd bdev_fd(
- TEMP_FAILURE_RETRY(open(bdev_path.c_str(), O_RDONLY | O_CLOEXEC)));
- if (bdev_fd < 0) {
- PLOG(ERROR) << "Failed to open block device: " << bdev_path;
- cleanup(file_path, create);
- return nullptr;
- }
-
- uint64_t bdevsz;
- if (!GetBlockDeviceSize(bdev_fd, bdev_path, &bdevsz)) {
- LOG(ERROR) << "Failed to get block device size for : " << bdev_path;
- cleanup(file_path, create);
- return nullptr;
- }
-
- if (!create) {
- file_size = GetFileSize(abs_path);
- if (file_size == 0) {
- LOG(ERROR) << "Invalid file size of zero bytes for file: " << abs_path;
- return nullptr;
- }
- }
-
- uint64_t blocksz;
- uint32_t fs_type;
- if (!PerformFileChecks(abs_path, file_size, &blocksz, &fs_type)) {
- LOG(ERROR) << "Failed to validate file or file system for file:" << abs_path;
- cleanup(abs_path, create);
- return nullptr;
- }
-
- // Align up to the nearest block size.
- if (file_size % blocksz) {
- file_size += blocksz - (file_size % blocksz);
- }
-
- if (create) {
- if (!AllocateFile(file_fd, abs_path, blocksz, file_size, fs_type, std::move(progress))) {
- LOG(ERROR) << "Failed to allocate file: " << abs_path << " of size: " << file_size
- << " bytes";
- cleanup(abs_path, create);
- return nullptr;
- }
- }
-
- // f2fs may move the file blocks around.
- if (!PinFile(file_fd, abs_path, fs_type)) {
- cleanup(abs_path, create);
- LOG(ERROR) << "Failed to pin the file in storage";
- return nullptr;
- }
-
- // now allocate the FiemapWriter and start setting it up
- FiemapUniquePtr fmap(new FiemapWriter());
- switch (fs_type) {
- case EXT4_SUPER_MAGIC:
- case F2FS_SUPER_MAGIC:
- if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) {
- LOG(ERROR) << "Failed to read fiemap of file: " << abs_path;
- cleanup(abs_path, create);
- return nullptr;
- }
- break;
- case MSDOS_SUPER_MAGIC:
- if (!ReadFibmap(file_fd, abs_path, &fmap->extents_)) {
- LOG(ERROR) << "Failed to read fibmap of file: " << abs_path;
- cleanup(abs_path, create);
- return nullptr;
- }
- break;
- }
-
- fmap->file_path_ = abs_path;
- fmap->bdev_path_ = bdev_path;
- fmap->file_size_ = file_size;
- fmap->bdev_size_ = bdevsz;
- fmap->fs_type_ = fs_type;
- fmap->block_size_ = blocksz;
-
- LOG(VERBOSE) << "Successfully created FiemapWriter for file " << abs_path << " on block device "
- << bdev_path;
- return fmap;
-}
-
-} // namespace fiemap
-} // namespace android
diff --git a/libfiemap/fiemap_writer_test.cpp b/libfiemap/fiemap_writer_test.cpp
deleted file mode 100644
index 4ac7161..0000000
--- a/libfiemap/fiemap_writer_test.cpp
+++ /dev/null
@@ -1,541 +0,0 @@
-/*
- * Copyright (C) 2018 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 <fcntl.h>
-#include <inttypes.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/vfs.h>
-#include <unistd.h>
-
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <gtest/gtest.h>
-#include <libdm/loop_control.h>
-#include <libfiemap/fiemap_writer.h>
-#include <libfiemap/split_fiemap_writer.h>
-
-#include "utility.h"
-
-namespace android {
-namespace fiemap {
-
-using namespace std;
-using namespace std::string_literals;
-using namespace android::fiemap;
-using unique_fd = android::base::unique_fd;
-using LoopDevice = android::dm::LoopDevice;
-
-std::string gTestDir;
-uint64_t testfile_size = 536870912; // default of 512MiB
-size_t gBlockSize = 0;
-
-class FiemapWriterTest : public ::testing::Test {
- protected:
- void SetUp() override {
- const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
- testfile = gTestDir + "/"s + tinfo->name();
- }
-
- void TearDown() override { unlink(testfile.c_str()); }
-
- // name of the file we use for testing
- std::string testfile;
-};
-
-class SplitFiemapTest : public ::testing::Test {
- protected:
- void SetUp() override {
- const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
- testfile = gTestDir + "/"s + tinfo->name();
- }
-
- void TearDown() override {
- std::string message;
- if (!SplitFiemap::RemoveSplitFiles(testfile, &message)) {
- cerr << "Could not remove all split files: " << message;
- }
- }
-
- // name of the file we use for testing
- std::string testfile;
-};
-
-TEST_F(FiemapWriterTest, CreateImpossiblyLargeFile) {
- // Try creating a file of size ~100TB but aligned to
- // 512 byte to make sure block alignment tests don't
- // fail.
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 1099511627997184);
- EXPECT_EQ(fptr, nullptr);
- EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
- EXPECT_EQ(errno, ENOENT);
-}
-
-TEST_F(FiemapWriterTest, CreateUnalignedFile) {
- // Try creating a file of size 4097 bytes which is guaranteed
- // to be unaligned to all known block sizes.
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize + 1);
- ASSERT_NE(fptr, nullptr);
- ASSERT_EQ(fptr->size(), gBlockSize * 2);
-}
-
-TEST_F(FiemapWriterTest, CheckFilePath) {
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
- ASSERT_NE(fptr, nullptr);
- EXPECT_EQ(fptr->size(), gBlockSize);
- EXPECT_EQ(fptr->file_path(), testfile);
- EXPECT_EQ(access(testfile.c_str(), F_OK), 0);
-}
-
-TEST_F(FiemapWriterTest, CheckFileSize) {
- // Create a large-ish file and test that the expected size matches.
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 1024 * 1024 * 16);
- ASSERT_NE(fptr, nullptr);
-
- struct stat s;
- ASSERT_EQ(stat(testfile.c_str(), &s), 0);
- EXPECT_EQ(static_cast<uint64_t>(s.st_size), fptr->size());
-}
-
-TEST_F(FiemapWriterTest, CheckProgress) {
- std::vector<uint64_t> expected;
- size_t invocations = 0;
- auto callback = [&](uint64_t done, uint64_t total) -> bool {
- if (invocations >= expected.size()) {
- return false;
- }
- EXPECT_EQ(done, expected[invocations]);
- EXPECT_EQ(total, gBlockSize);
- invocations++;
- return true;
- };
-
- expected.push_back(gBlockSize);
-
- auto ptr = FiemapWriter::Open(testfile, gBlockSize, true, std::move(callback));
- EXPECT_NE(ptr, nullptr);
- EXPECT_EQ(invocations, expected.size());
-}
-
-TEST_F(FiemapWriterTest, CheckPinning) {
- auto ptr = FiemapWriter::Open(testfile, 4096);
- ASSERT_NE(ptr, nullptr);
- EXPECT_TRUE(FiemapWriter::HasPinnedExtents(testfile));
-}
-
-TEST_F(FiemapWriterTest, CheckBlockDevicePath) {
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
- EXPECT_EQ(fptr->size(), gBlockSize);
- EXPECT_EQ(fptr->bdev_path().find("/dev/block/"), size_t(0));
- EXPECT_EQ(fptr->bdev_path().find("/dev/block/dm-"), string::npos);
-}
-
-TEST_F(FiemapWriterTest, CheckFileCreated) {
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 32768);
- ASSERT_NE(fptr, nullptr);
- unique_fd fd(open(testfile.c_str(), O_RDONLY));
- EXPECT_GT(fd, -1);
-}
-
-TEST_F(FiemapWriterTest, CheckFileSizeActual) {
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
- ASSERT_NE(fptr, nullptr);
-
- struct stat sb;
- ASSERT_EQ(stat(testfile.c_str(), &sb), 0);
- EXPECT_GE(sb.st_size, testfile_size);
-}
-
-TEST_F(FiemapWriterTest, CheckFileExtents) {
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
- ASSERT_NE(fptr, nullptr);
- EXPECT_GT(fptr->extents().size(), 0);
-}
-
-TEST_F(FiemapWriterTest, ExistingFile) {
- // Create the file.
- { ASSERT_NE(FiemapWriter::Open(testfile, gBlockSize), nullptr); }
- // Test that we can still open it.
- {
- auto ptr = FiemapWriter::Open(testfile, 0, false);
- ASSERT_NE(ptr, nullptr);
- EXPECT_GT(ptr->extents().size(), 0);
- }
-}
-
-TEST_F(FiemapWriterTest, FileDeletedOnError) {
- auto callback = [](uint64_t, uint64_t) -> bool { return false; };
- auto ptr = FiemapWriter::Open(testfile, gBlockSize, true, std::move(callback));
- EXPECT_EQ(ptr, nullptr);
- EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
- EXPECT_EQ(errno, ENOENT);
-}
-
-TEST_F(FiemapWriterTest, MaxBlockSize) {
- ASSERT_GT(DetermineMaximumFileSize(testfile), 0);
-}
-
-TEST_F(FiemapWriterTest, FibmapBlockAddressing) {
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
- ASSERT_NE(fptr, nullptr);
-
- switch (fptr->fs_type()) {
- case F2FS_SUPER_MAGIC:
- case EXT4_SUPER_MAGIC:
- // Skip the test for FIEMAP supported filesystems. This is really
- // because f2fs/ext4 have caches that seem to defeat reading back
- // directly from the block device, and writing directly is too
- // dangerous.
- std::cout << "Skipping test, filesystem does not use FIBMAP\n";
- return;
- }
-
- bool uses_dm;
- std::string bdev_path;
- ASSERT_TRUE(FiemapWriter::GetBlockDeviceForFile(testfile, &bdev_path, &uses_dm));
-
- if (uses_dm) {
- // We could use a device-mapper wrapper here to bypass encryption, but
- // really this test is for FIBMAP correctness on VFAT (where encryption
- // is never used), so we don't bother.
- std::cout << "Skipping test, block device is metadata encrypted\n";
- return;
- }
-
- std::string data(fptr->size(), '\0');
- for (size_t i = 0; i < data.size(); i++) {
- data[i] = 'A' + static_cast<char>(data.size() % 26);
- }
-
- {
- unique_fd fd(open(testfile.c_str(), O_WRONLY | O_CLOEXEC));
- ASSERT_GE(fd, 0);
- ASSERT_TRUE(android::base::WriteFully(fd, data.data(), data.size()));
- ASSERT_EQ(fsync(fd), 0);
- }
-
- ASSERT_FALSE(fptr->extents().empty());
- const auto& first_extent = fptr->extents()[0];
-
- unique_fd bdev(open(fptr->bdev_path().c_str(), O_RDONLY | O_CLOEXEC));
- ASSERT_GE(bdev, 0);
-
- off_t where = first_extent.fe_physical;
- ASSERT_EQ(lseek(bdev, where, SEEK_SET), where);
-
- // Note: this will fail on encrypted folders.
- std::string actual(data.size(), '\0');
- ASSERT_GE(first_extent.fe_length, data.size());
- ASSERT_TRUE(android::base::ReadFully(bdev, actual.data(), actual.size()));
- EXPECT_EQ(memcmp(actual.data(), data.data(), data.size()), 0);
-}
-
-TEST_F(SplitFiemapTest, Create) {
- auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);
- ASSERT_NE(ptr, nullptr);
-
- auto extents = ptr->extents();
-
- // Destroy the fiemap, closing file handles. This should not delete them.
- ptr = nullptr;
-
- std::vector<std::string> files;
- ASSERT_TRUE(SplitFiemap::GetSplitFileList(testfile, &files));
- for (const auto& path : files) {
- EXPECT_EQ(access(path.c_str(), F_OK), 0);
- }
-
- ASSERT_GE(extents.size(), files.size());
-}
-
-TEST_F(SplitFiemapTest, Open) {
- {
- auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);
- ASSERT_NE(ptr, nullptr);
- }
-
- auto ptr = SplitFiemap::Open(testfile);
- ASSERT_NE(ptr, nullptr);
-
- auto extents = ptr->extents();
- ASSERT_GE(extents.size(), 24);
-}
-
-TEST_F(SplitFiemapTest, DeleteOnFail) {
- auto ptr = SplitFiemap::Create(testfile, 1024 * 1024 * 100, 1);
- ASSERT_EQ(ptr, nullptr);
-
- std::string first_file = testfile + ".0001";
- ASSERT_NE(access(first_file.c_str(), F_OK), 0);
- ASSERT_EQ(errno, ENOENT);
- ASSERT_NE(access(testfile.c_str(), F_OK), 0);
- ASSERT_EQ(errno, ENOENT);
-}
-
-static string ReadSplitFiles(const std::string& base_path, size_t num_files) {
- std::string result;
- for (int i = 0; i < num_files; i++) {
- std::string path = base_path + android::base::StringPrintf(".%04d", i);
- std::string data;
- if (!android::base::ReadFileToString(path, &data)) {
- return {};
- }
- result += data;
- }
- return result;
-}
-
-TEST_F(SplitFiemapTest, WriteWholeFile) {
- static constexpr size_t kChunkSize = 32768;
- static constexpr size_t kSize = kChunkSize * 3;
- auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
- ASSERT_NE(ptr, nullptr);
-
- auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
- for (size_t i = 0; i < kSize / sizeof(int); i++) {
- buffer[i] = i;
- }
- ASSERT_TRUE(ptr->Write(buffer.get(), kSize));
-
- std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
- auto actual = ReadSplitFiles(testfile, 3);
- ASSERT_EQ(expected.size(), actual.size());
- EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
-}
-
-TEST_F(SplitFiemapTest, WriteFileInChunks1) {
- static constexpr size_t kChunkSize = 32768;
- static constexpr size_t kSize = kChunkSize * 3;
- auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
- ASSERT_NE(ptr, nullptr);
-
- auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
- for (size_t i = 0; i < kSize / sizeof(int); i++) {
- buffer[i] = i;
- }
-
- // Write in chunks of 1000 (so some writes straddle the boundary of two
- // files).
- size_t bytes_written = 0;
- while (bytes_written < kSize) {
- size_t to_write = std::min(kSize - bytes_written, (size_t)1000);
- char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;
- ASSERT_TRUE(ptr->Write(data, to_write));
- bytes_written += to_write;
- }
-
- std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
- auto actual = ReadSplitFiles(testfile, 3);
- ASSERT_EQ(expected.size(), actual.size());
- EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
-}
-
-TEST_F(SplitFiemapTest, WriteFileInChunks2) {
- static constexpr size_t kChunkSize = 32768;
- static constexpr size_t kSize = kChunkSize * 3;
- auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
- ASSERT_NE(ptr, nullptr);
-
- auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
- for (size_t i = 0; i < kSize / sizeof(int); i++) {
- buffer[i] = i;
- }
-
- // Write in chunks of 32KiB so every write is exactly at the end of the
- // current file.
- size_t bytes_written = 0;
- while (bytes_written < kSize) {
- size_t to_write = std::min(kSize - bytes_written, kChunkSize);
- char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;
- ASSERT_TRUE(ptr->Write(data, to_write));
- bytes_written += to_write;
- }
-
- std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
- auto actual = ReadSplitFiles(testfile, 3);
- ASSERT_EQ(expected.size(), actual.size());
- EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
-}
-
-TEST_F(SplitFiemapTest, WritePastEnd) {
- static constexpr size_t kChunkSize = 32768;
- static constexpr size_t kSize = kChunkSize * 3;
- auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
- ASSERT_NE(ptr, nullptr);
-
- auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
- for (size_t i = 0; i < kSize / sizeof(int); i++) {
- buffer[i] = i;
- }
- ASSERT_TRUE(ptr->Write(buffer.get(), kSize));
- ASSERT_FALSE(ptr->Write(buffer.get(), kSize));
-}
-
-class VerifyBlockWritesExt4 : public ::testing::Test {
- // 2GB Filesystem and 4k block size by default
- static constexpr uint64_t block_size = 4096;
- static constexpr uint64_t fs_size = 2147483648;
-
- protected:
- void SetUp() override {
- fs_path = std::string(getenv("TMPDIR")) + "/ext4_2G.img";
- uint64_t count = fs_size / block_size;
- std::string dd_cmd =
- ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
- " count=%" PRIu64 " > /dev/null 2>&1",
- fs_path.c_str(), block_size, count);
- std::string mkfs_cmd =
- ::android::base::StringPrintf("/system/bin/mkfs.ext4 -q %s", fs_path.c_str());
- // create mount point
- mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
- ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
- // create file for the file system
- int ret = system(dd_cmd.c_str());
- ASSERT_EQ(ret, 0);
- // Get and attach a loop device to the filesystem we created
- LoopDevice loop_dev(fs_path, 10s);
- ASSERT_TRUE(loop_dev.valid());
- // create file system
- ret = system(mkfs_cmd.c_str());
- ASSERT_EQ(ret, 0);
-
- // mount the file system
- ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "ext4", 0, nullptr), 0);
- }
-
- void TearDown() override {
- umount(mntpoint.c_str());
- rmdir(mntpoint.c_str());
- unlink(fs_path.c_str());
- }
-
- std::string mntpoint;
- std::string fs_path;
-};
-
-class VerifyBlockWritesF2fs : public ::testing::Test {
- // 2GB Filesystem and 4k block size by default
- static constexpr uint64_t block_size = 4096;
- static constexpr uint64_t fs_size = 2147483648;
-
- protected:
- void SetUp() override {
- fs_path = std::string(getenv("TMPDIR")) + "/f2fs_2G.img";
- uint64_t count = fs_size / block_size;
- std::string dd_cmd =
- ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
- " count=%" PRIu64 " > /dev/null 2>&1",
- fs_path.c_str(), block_size, count);
- std::string mkfs_cmd =
- ::android::base::StringPrintf("/system/bin/make_f2fs -q %s", fs_path.c_str());
- // create mount point
- mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
- ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
- // create file for the file system
- int ret = system(dd_cmd.c_str());
- ASSERT_EQ(ret, 0);
- // Get and attach a loop device to the filesystem we created
- LoopDevice loop_dev(fs_path, 10s);
- ASSERT_TRUE(loop_dev.valid());
- // create file system
- ret = system(mkfs_cmd.c_str());
- ASSERT_EQ(ret, 0);
-
- // mount the file system
- ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "f2fs", 0, nullptr), 0);
- }
-
- void TearDown() override {
- umount(mntpoint.c_str());
- rmdir(mntpoint.c_str());
- unlink(fs_path.c_str());
- }
-
- std::string mntpoint;
- std::string fs_path;
-};
-
-bool DetermineBlockSize() {
- struct statfs s;
- if (statfs(gTestDir.c_str(), &s)) {
- std::cerr << "Could not call statfs: " << strerror(errno) << "\n";
- return false;
- }
- if (!s.f_bsize) {
- std::cerr << "Invalid block size: " << s.f_bsize << "\n";
- return false;
- }
-
- gBlockSize = s.f_bsize;
- return true;
-}
-
-} // namespace fiemap
-} // namespace android
-
-using namespace android::fiemap;
-
-int main(int argc, char** argv) {
- ::testing::InitGoogleTest(&argc, argv);
- if (argc > 1 && argv[1] == "-h"s) {
- cerr << "Usage: [test_dir] [file_size]\n";
- cerr << "\n";
- cerr << "Note: test_dir must be a writable, unencrypted directory.\n";
- exit(EXIT_FAILURE);
- }
- ::android::base::InitLogging(argv, ::android::base::StderrLogger);
-
- std::string root_dir = "/data/local/unencrypted";
- if (access(root_dir.c_str(), F_OK)) {
- root_dir = "/data";
- }
-
- std::string tempdir = root_dir + "/XXXXXX"s;
- if (!mkdtemp(tempdir.data())) {
- cerr << "unable to create tempdir on " << root_dir << "\n";
- exit(EXIT_FAILURE);
- }
- if (!android::base::Realpath(tempdir, &gTestDir)) {
- cerr << "unable to find realpath for " << tempdir;
- exit(EXIT_FAILURE);
- }
-
- if (argc > 2) {
- testfile_size = strtoull(argv[2], NULL, 0);
- if (testfile_size == ULLONG_MAX) {
- testfile_size = 512 * 1024 * 1024;
- }
- }
-
- if (!DetermineBlockSize()) {
- exit(EXIT_FAILURE);
- }
-
- auto result = RUN_ALL_TESTS();
-
- std::string cmd = "rm -rf " + gTestDir;
- system(cmd.c_str());
-
- return result;
-}
diff --git a/libfiemap/image_manager.cpp b/libfiemap/image_manager.cpp
deleted file mode 100644
index 3e32dcf..0000000
--- a/libfiemap/image_manager.cpp
+++ /dev/null
@@ -1,649 +0,0 @@
-//
-// 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 <libfiemap/image_manager.h>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <ext4_utils/ext4_utils.h>
-#include <fs_mgr/file_wait.h>
-#include <fs_mgr_dm_linear.h>
-#include <libdm/loop_control.h>
-#include <libfiemap/split_fiemap_writer.h>
-
-#include "metadata.h"
-#include "utility.h"
-
-namespace android {
-namespace fiemap {
-
-using namespace std::literals;
-using android::base::unique_fd;
-using android::dm::DeviceMapper;
-using android::dm::DmDeviceState;
-using android::dm::DmTable;
-using android::dm::DmTargetLinear;
-using android::dm::LoopControl;
-using android::fs_mgr::CreateLogicalPartition;
-using android::fs_mgr::CreateLogicalPartitionParams;
-using android::fs_mgr::DestroyLogicalPartition;
-using android::fs_mgr::GetPartitionName;
-
-static constexpr char kTestImageMetadataDir[] = "/metadata/gsi/test";
-
-std::unique_ptr<ImageManager> ImageManager::Open(const std::string& dir_prefix) {
- auto metadata_dir = "/metadata/gsi/" + dir_prefix;
- auto data_dir = "/data/gsi/" + dir_prefix;
- return Open(metadata_dir, data_dir);
-}
-
-std::unique_ptr<ImageManager> ImageManager::Open(const std::string& metadata_dir,
- const std::string& data_dir) {
- return std::unique_ptr<ImageManager>(new ImageManager(metadata_dir, data_dir));
-}
-
-ImageManager::ImageManager(const std::string& metadata_dir, const std::string& data_dir)
- : metadata_dir_(metadata_dir), data_dir_(data_dir) {
- partition_opener_ = std::make_unique<android::fs_mgr::PartitionOpener>();
-}
-
-std::string ImageManager::GetImageHeaderPath(const std::string& name) {
- return JoinPaths(data_dir_, name) + ".img";
-}
-
-// The status file has one entry per line, with each entry formatted as one of:
-// dm:<name>
-// loop:<path>
-//
-// This simplifies the process of tearing down a mapping, since we can simply
-// unmap each entry in the order it appears.
-std::string ImageManager::GetStatusFilePath(const std::string& image_name) {
- return JoinPaths(metadata_dir_, image_name) + ".status";
-}
-
-static std::string GetStatusPropertyName(const std::string& image_name) {
- // Note: we don't prefix |image_name|, because CreateLogicalPartition won't
- // prefix the name either. There are no plans to change this at the moment,
- // consumers of the image API must take care to use globally-unique image
- // names.
- return "gsid.mapped_image." + image_name;
-}
-
-void ImageManager::set_partition_opener(std::unique_ptr<IPartitionOpener>&& opener) {
- partition_opener_ = std::move(opener);
-}
-
-bool ImageManager::IsImageMapped(const std::string& image_name) {
- auto prop_name = GetStatusPropertyName(image_name);
- if (android::base::GetProperty(prop_name, "").empty()) {
- // If mapped in first-stage init, the dm-device will exist but not the
- // property.
- auto& dm = DeviceMapper::Instance();
- return dm.GetState(image_name) != DmDeviceState::INVALID;
- }
- return true;
-}
-
-std::vector<std::string> ImageManager::GetAllBackingImages() {
- std::vector<std::string> images;
- auto metadata = OpenMetadata(metadata_dir_);
- if (metadata) {
- for (auto&& partition : metadata->partitions) {
- images.push_back(partition.name);
- }
- }
- return images;
-}
-
-bool ImageManager::PartitionExists(const std::string& name) {
- if (!MetadataExists(metadata_dir_)) {
- return false;
- }
- auto metadata = OpenMetadata(metadata_dir_);
- if (!metadata) {
- return false;
- }
- return !!FindPartition(*metadata.get(), name);
-}
-
-bool ImageManager::BackingImageExists(const std::string& name) {
- auto header_file = GetImageHeaderPath(name);
- return access(header_file.c_str(), F_OK) == 0;
-}
-
-bool ImageManager::CreateBackingImage(const std::string& name, uint64_t size, int flags) {
- return CreateBackingImage(name, size, flags, nullptr);
-}
-
-bool ImageManager::CreateBackingImage(const std::string& name, uint64_t size, int flags,
- std::function<bool(uint64_t, uint64_t)>&& on_progress) {
- auto data_path = GetImageHeaderPath(name);
- auto fw = SplitFiemap::Create(data_path, size, 0, on_progress);
- if (!fw) {
- return false;
- }
-
- // Except for testing, we do not allow persisting metadata that references
- // device-mapper devices. It just doesn't make sense, because the device
- // numbering may change on reboot. We allow it for testing since the images
- // are not meant to survive reboot. Outside of tests, this can only happen
- // if device-mapper is stacked in some complex way not supported by
- // FiemapWriter.
- auto device_path = GetDevicePathForFile(fw.get());
- if (android::base::StartsWith(device_path, "/dev/block/dm-") &&
- !android::base::StartsWith(metadata_dir_, kTestImageMetadataDir)) {
- LOG(ERROR) << "Cannot persist images against device-mapper device: " << device_path;
-
- fw = {};
- SplitFiemap::RemoveSplitFiles(data_path);
- return false;
- }
-
- bool readonly = !!(flags & CREATE_IMAGE_READONLY);
- if (!UpdateMetadata(metadata_dir_, name, fw.get(), size, readonly)) {
- return false;
- }
-
- if (flags & CREATE_IMAGE_ZERO_FILL) {
- if (!ZeroFillNewImage(name, 0)) {
- DeleteBackingImage(name);
- return false;
- }
- }
- return true;
-}
-
-bool ImageManager::ZeroFillNewImage(const std::string& name, uint64_t bytes) {
- auto data_path = GetImageHeaderPath(name);
-
- // See the comment in MapImageDevice() about how this works.
- std::string block_device;
- bool can_use_devicemapper;
- if (!FiemapWriter::GetBlockDeviceForFile(data_path, &block_device, &can_use_devicemapper)) {
- LOG(ERROR) << "Could not determine block device for " << data_path;
- return false;
- }
-
- if (!can_use_devicemapper) {
- // We've backed with loop devices, and since we store files in an
- // unencrypted folder, the initial zeroes we wrote will suffice.
- return true;
- }
-
- // data is dm-crypt, or FBE + dm-default-key. This means the zeroes written
- // by libfiemap were encrypted, so we need to map the image in and correct
- // this.
- auto device = MappedDevice::Open(this, 10s, name);
- if (!device) {
- return false;
- }
-
- static constexpr size_t kChunkSize = 4096;
- std::string zeroes(kChunkSize, '\0');
-
- uint64_t remaining;
- if (bytes) {
- remaining = bytes;
- } else {
- remaining = get_block_device_size(device->fd());
- if (!remaining) {
- PLOG(ERROR) << "Could not get block device size for " << device->path();
- return false;
- }
- }
- while (remaining) {
- uint64_t to_write = std::min(static_cast<uint64_t>(zeroes.size()), remaining);
- if (!android::base::WriteFully(device->fd(), zeroes.data(),
- static_cast<size_t>(to_write))) {
- PLOG(ERROR) << "write failed: " << device->path();
- return false;
- }
- remaining -= to_write;
- }
- return true;
-}
-
-bool ImageManager::DeleteBackingImage(const std::string& name) {
- // For dm-linear devices sitting on top of /data, we cannot risk deleting
- // the file. The underlying blocks could be reallocated by the filesystem.
- if (IsImageMapped(name)) {
- LOG(ERROR) << "Backing image " << name << " is currently mapped to a block device";
- return false;
- }
-
- std::string message;
- auto header_file = GetImageHeaderPath(name);
- if (!SplitFiemap::RemoveSplitFiles(header_file, &message)) {
- // This is fatal, because we don't want to leave these files dangling.
- LOG(ERROR) << "Error removing image " << name << ": " << message;
- return false;
- }
-
- auto status_file = GetStatusFilePath(name);
- if (!android::base::RemoveFileIfExists(status_file)) {
- LOG(ERROR) << "Error removing " << status_file << ": " << message;
- }
- return RemoveImageMetadata(metadata_dir_, name);
-}
-
-// Create a block device for an image file, using its extents in its
-// lp_metadata.
-bool ImageManager::MapWithDmLinear(const IPartitionOpener& opener, const std::string& name,
- const std::chrono::milliseconds& timeout_ms, std::string* path) {
- // :TODO: refresh extents in metadata file until f2fs is fixed.
- auto metadata = OpenMetadata(metadata_dir_);
- if (!metadata) {
- return false;
- }
-
- auto super = android::fs_mgr::GetMetadataSuperBlockDevice(*metadata.get());
- auto block_device = android::fs_mgr::GetBlockDevicePartitionName(*super);
-
- CreateLogicalPartitionParams params = {
- .block_device = block_device,
- .metadata = metadata.get(),
- .partition_name = name,
- .force_writable = true,
- .timeout_ms = timeout_ms,
- .partition_opener = &opener,
- };
- if (!CreateLogicalPartition(params, path)) {
- LOG(ERROR) << "Error creating device-mapper node for image " << name;
- return false;
- }
-
- auto status_string = "dm:" + name;
- auto status_file = GetStatusFilePath(name);
- if (!android::base::WriteStringToFile(status_string, status_file)) {
- PLOG(ERROR) << "Could not write status file: " << status_file;
- DestroyLogicalPartition(name);
- return false;
- }
- return true;
-}
-
-// Helper to create a loop device for a file.
-static bool CreateLoopDevice(LoopControl& control, const std::string& file,
- const std::chrono::milliseconds& timeout_ms, std::string* path) {
- static constexpr int kOpenFlags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
- android::base::unique_fd file_fd(open(file.c_str(), kOpenFlags));
- if (file_fd < 0) {
- PLOG(ERROR) << "Could not open file: " << file;
- return false;
- }
- if (!control.Attach(file_fd, timeout_ms, path)) {
- LOG(ERROR) << "Could not create loop device for: " << file;
- return false;
- }
- LOG(INFO) << "Created loop device " << *path << " for file " << file;
- return true;
-}
-
-class AutoDetachLoopDevices final {
- public:
- AutoDetachLoopDevices(LoopControl& control, const std::vector<std::string>& devices)
- : control_(control), devices_(devices), commit_(false) {}
-
- ~AutoDetachLoopDevices() {
- if (commit_) return;
- for (const auto& device : devices_) {
- control_.Detach(device);
- }
- }
-
- void Commit() { commit_ = true; }
-
- private:
- LoopControl& control_;
- const std::vector<std::string>& devices_;
- bool commit_;
-};
-
-// If an image is stored across multiple files, this takes a list of loop
-// devices and joins them together using device-mapper.
-bool ImageManager::MapWithLoopDeviceList(const std::vector<std::string>& device_list,
- const std::string& name,
- const std::chrono::milliseconds& timeout_ms,
- std::string* path) {
- auto metadata = OpenMetadata(metadata_dir_);
- if (!metadata) {
- return false;
- }
- auto partition = FindPartition(*metadata.get(), name);
- if (!partition) {
- LOG(ERROR) << "Could not find image in metadata: " << name;
- return false;
- }
-
- // Since extent lengths are in sector units, the size should be a multiple
- // of the sector size.
- uint64_t partition_size = GetPartitionSize(*metadata.get(), *partition);
- if (partition_size % LP_SECTOR_SIZE != 0) {
- LOG(ERROR) << "Partition size not sector aligned: " << name << ", " << partition_size
- << " bytes";
- return false;
- }
-
- DmTable table;
-
- uint64_t start_sector = 0;
- uint64_t sectors_needed = partition_size / LP_SECTOR_SIZE;
- for (const auto& block_device : device_list) {
- // The final block device must be == partition_size, otherwise we
- // can't find the AVB footer on verified partitions.
- static constexpr int kOpenFlags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
- unique_fd fd(open(block_device.c_str(), kOpenFlags));
- if (fd < 0) {
- PLOG(ERROR) << "Open failed: " << block_device;
- return false;
- }
-
- uint64_t file_size = get_block_device_size(fd);
- uint64_t file_sectors = file_size / LP_SECTOR_SIZE;
- uint64_t segment_size = std::min(file_sectors, sectors_needed);
-
- table.Emplace<DmTargetLinear>(start_sector, segment_size, block_device, 0);
-
- start_sector += segment_size;
- sectors_needed -= segment_size;
- if (sectors_needed == 0) {
- break;
- }
- }
-
- auto& dm = DeviceMapper::Instance();
- if (!dm.CreateDevice(name, table, path, timeout_ms)) {
- LOG(ERROR) << "Could not create device-mapper device over loop set";
- return false;
- }
-
- // Build the status file.
- std::vector<std::string> lines;
- lines.emplace_back("dm:" + name);
- for (const auto& block_device : device_list) {
- lines.emplace_back("loop:" + block_device);
- }
- auto status_message = android::base::Join(lines, "\n");
- auto status_file = GetStatusFilePath(name);
- if (!android::base::WriteStringToFile(status_message, status_file)) {
- PLOG(ERROR) << "Write failed: " << status_file;
- dm.DeleteDevice(name);
- return false;
- }
- return true;
-}
-
-static bool OptimizeLoopDevices(const std::vector<std::string>& device_list) {
- for (const auto& device : device_list) {
- unique_fd fd(open(device.c_str(), O_RDWR | O_CLOEXEC | O_NOFOLLOW));
- if (fd < 0) {
- PLOG(ERROR) << "Open failed: " << device;
- return false;
- }
- if (!LoopControl::EnableDirectIo(fd)) {
- return false;
- }
- }
- return true;
-}
-
-// Helper to use one or more loop devices around image files.
-bool ImageManager::MapWithLoopDevice(const std::string& name,
- const std::chrono::milliseconds& timeout_ms,
- std::string* path) {
- auto image_header = GetImageHeaderPath(name);
-
- std::vector<std::string> file_list;
- if (!SplitFiemap::GetSplitFileList(image_header, &file_list)) {
- LOG(ERROR) << "Could not get image file list";
- return false;
- }
-
- // Map each image file as a loopback device.
- LoopControl control;
- std::vector<std::string> loop_devices;
- AutoDetachLoopDevices auto_detach(control, loop_devices);
-
- auto start_time = std::chrono::steady_clock::now();
- for (const auto& file : file_list) {
- auto now = std::chrono::steady_clock::now();
- auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
-
- std::string loop_device;
- if (!CreateLoopDevice(control, file, timeout_ms - elapsed, &loop_device)) {
- break;
- }
- loop_devices.emplace_back(loop_device);
- }
- if (loop_devices.size() != file_list.size()) {
- // The number of devices will mismatch if CreateLoopDevice() failed.
- return false;
- }
-
- // If OptimizeLoopDevices fails, we'd use double the memory.
- if (!OptimizeLoopDevices(loop_devices)) {
- return false;
- }
-
- // If there's only one loop device (by far the most common case, splits
- // will normally only happen on sdcards with FAT32), then just return that
- // as the block device. Otherwise, we need to use dm-linear to stitch
- // together all the loop devices we just created.
- if (loop_devices.size() > 1) {
- if (!MapWithLoopDeviceList(loop_devices, name, timeout_ms, path)) {
- return false;
- }
- }
-
- auto status_message = "loop:" + loop_devices.back();
- auto status_file = GetStatusFilePath(name);
- if (!android::base::WriteStringToFile(status_message, status_file)) {
- PLOG(ERROR) << "Write failed: " << status_file;
- return false;
- }
-
- auto_detach.Commit();
-
- *path = loop_devices.back();
- return true;
-}
-
-bool ImageManager::MapImageDevice(const std::string& name,
- const std::chrono::milliseconds& timeout_ms, std::string* path) {
- if (IsImageMapped(name)) {
- LOG(ERROR) << "Backing image " << name << " is already mapped";
- return false;
- }
-
- auto image_header = GetImageHeaderPath(name);
-
- // 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;
- bool can_use_devicemapper;
- if (!FiemapWriter::GetBlockDeviceForFile(image_header, &block_device, &can_use_devicemapper)) {
- LOG(ERROR) << "Could not determine block device for " << image_header;
- return false;
- }
-
- if (can_use_devicemapper) {
- if (!MapWithDmLinear(*partition_opener_.get(), name, timeout_ms, path)) {
- return false;
- }
- } else if (!MapWithLoopDevice(name, timeout_ms, path)) {
- return false;
- }
-
- // Set a property so we remember this is mapped.
- auto prop_name = GetStatusPropertyName(name);
- if (!android::base::SetProperty(prop_name, *path)) {
- UnmapImageDevice(name, true);
- return false;
- }
- return true;
-}
-
-bool ImageManager::MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
- std::string* dev) {
- std::string ignore_path;
- if (!MapWithDmLinear(opener, name, {}, &ignore_path)) {
- return false;
- }
-
- auto& dm = DeviceMapper::Instance();
- if (!dm.GetDeviceString(name, dev)) {
- return false;
- }
- return true;
-}
-
-bool ImageManager::UnmapImageDevice(const std::string& name) {
- return UnmapImageDevice(name, false);
-}
-
-bool ImageManager::UnmapImageDevice(const std::string& name, bool force) {
- if (!force && !IsImageMapped(name)) {
- LOG(ERROR) << "Backing image " << name << " is not mapped";
- return false;
- }
- auto& dm = DeviceMapper::Instance();
- LoopControl loop;
-
- std::string status;
- auto status_file = GetStatusFilePath(name);
- if (!android::base::ReadFileToString(status_file, &status)) {
- PLOG(ERROR) << "Read failed: " << status_file;
- return false;
- }
-
- auto lines = android::base::Split(status, "\n");
- for (const auto& line : lines) {
- auto pieces = android::base::Split(line, ":");
- if (pieces.size() != 2) {
- LOG(ERROR) << "Unknown status line";
- continue;
- }
- if (pieces[0] == "dm") {
- // Failure to remove a dm node is fatal, since we can't safely
- // remove the file or loop devices.
- const auto& name = pieces[1];
- if (!dm.DeleteDeviceIfExists(name)) {
- return false;
- }
- } else if (pieces[0] == "loop") {
- // Failure to remove a loop device is not fatal, since we can still
- // remove the backing file if we want.
- loop.Detach(pieces[1]);
- } else {
- LOG(ERROR) << "Unknown status: " << pieces[0];
- }
- }
-
- std::string message;
- if (!android::base::RemoveFileIfExists(status_file, &message)) {
- LOG(ERROR) << "Could not remove " << status_file << ": " << message;
- }
-
- auto status_prop = GetStatusPropertyName(name);
- android::base::SetProperty(status_prop, "");
- return true;
-}
-
-bool ImageManager::RemoveAllImages() {
- if (!MetadataExists(metadata_dir_)) {
- return true;
- }
- auto metadata = OpenMetadata(metadata_dir_);
- if (!metadata) {
- return RemoveAllMetadata(metadata_dir_);
- }
-
- bool ok = true;
- for (const auto& partition : metadata->partitions) {
- auto partition_name = GetPartitionName(partition);
- ok &= DeleteBackingImage(partition_name);
- }
- return ok && RemoveAllMetadata(metadata_dir_);
-}
-
-bool ImageManager::Validate() {
- auto metadata = OpenMetadata(metadata_dir_);
- if (!metadata) {
- return false;
- }
-
- for (const auto& partition : metadata->partitions) {
- auto name = GetPartitionName(partition);
- auto image_path = GetImageHeaderPath(name);
- auto fiemap = SplitFiemap::Open(image_path);
- if (!fiemap || !fiemap->HasPinnedExtents()) {
- LOG(ERROR) << "Image is missing or was moved: " << image_path;
- return false;
- }
- }
- return true;
-}
-
-std::unique_ptr<MappedDevice> MappedDevice::Open(IImageManager* manager,
- const std::chrono::milliseconds& timeout_ms,
- const std::string& name) {
- std::string path;
- if (!manager->MapImageDevice(name, timeout_ms, &path)) {
- return nullptr;
- }
-
- auto device = std::unique_ptr<MappedDevice>(new MappedDevice(manager, name, path));
- if (device->fd() < 0) {
- return nullptr;
- }
- return device;
-}
-
-MappedDevice::MappedDevice(IImageManager* manager, const std::string& name, const std::string& path)
- : manager_(manager), name_(name), path_(path) {
- // The device is already mapped; try and open it.
- fd_.reset(open(path.c_str(), O_RDWR | O_CLOEXEC));
-}
-
-MappedDevice::~MappedDevice() {
- fd_ = {};
- manager_->UnmapImageDevice(name_);
-}
-
-bool IImageManager::UnmapImageIfExists(const std::string& name) {
- // No lock is needed even though this seems to be vulnerable to TOCTOU. If process A
- // calls MapImageDevice() while process B calls UnmapImageIfExists(), and MapImageDevice()
- // happens after process B checks IsImageMapped(), it would be as if MapImageDevice() is called
- // after process B finishes calling UnmapImageIfExists(), resulting the image to be mapped,
- // which is a reasonable sequence.
- if (!IsImageMapped(name)) {
- return true;
- }
- return UnmapImageDevice(name);
-}
-
-} // namespace fiemap
-} // namespace android
diff --git a/libfiemap/image_test.cpp b/libfiemap/image_test.cpp
deleted file mode 100644
index f05825c..0000000
--- a/libfiemap/image_test.cpp
+++ /dev/null
@@ -1,251 +0,0 @@
-//
-// 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 <stdlib.h>
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <chrono>
-#include <iostream>
-#include <thread>
-
-#include <android-base/file.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <ext4_utils/ext4_utils.h>
-#include <fs_mgr/file_wait.h>
-#include <gtest/gtest.h>
-#include <libdm/dm.h>
-#include <libfiemap/image_manager.h>
-
-using namespace android::dm;
-using namespace std::literals;
-using android::base::unique_fd;
-using android::fiemap::ImageManager;
-using android::fs_mgr::BlockDeviceInfo;
-using android::fs_mgr::PartitionOpener;
-using android::fs_mgr::WaitForFile;
-
-static std::string gDataPath;
-static std::string gDataMountPath;
-static constexpr char kMetadataPath[] = "/metadata/gsi/test";
-
-static constexpr uint64_t kTestImageSize = 1024 * 1024;
-
-class TestPartitionOpener final : public PartitionOpener {
- public:
- android::base::unique_fd Open(const std::string& partition_name, int flags) const override {
- return PartitionOpener::Open(GetPathForBlockDeviceName(partition_name), flags);
- }
- bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const override {
- return PartitionOpener::GetInfo(GetPathForBlockDeviceName(partition_name), info);
- }
- std::string GetDeviceString(const std::string& partition_name) const override {
- return PartitionOpener::GetDeviceString(GetPathForBlockDeviceName(partition_name));
- }
-
- private:
- static std::string GetPathForBlockDeviceName(const std::string& name) {
- if (android::base::StartsWith(name, "loop") || android::base::StartsWith(name, "dm-")) {
- return "/dev/block/"s + name;
- }
- return name;
- }
-};
-
-// This fixture is for tests against the device's native configuration.
-class NativeTest : public ::testing::Test {
- protected:
- void SetUp() override {
- manager_ = ImageManager::Open(kMetadataPath, gDataPath);
- ASSERT_NE(manager_, nullptr);
-
- manager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
-
- const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
- base_name_ = tinfo->name();
- }
-
- void TearDown() override {
- manager_->UnmapImageDevice(base_name_);
- manager_->DeleteBackingImage(base_name_);
- }
-
- std::string PropertyName() { return "gsid.mapped_image." + base_name_; }
-
- std::unique_ptr<ImageManager> manager_;
- std::string base_name_;
-};
-
-TEST_F(NativeTest, CreateAndMap) {
- ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize, false, nullptr));
-
- std::string path;
- ASSERT_TRUE(manager_->MapImageDevice(base_name_, 5s, &path));
- ASSERT_TRUE(manager_->IsImageMapped(base_name_));
- ASSERT_EQ(android::base::GetProperty(PropertyName(), ""), path);
-
- {
- unique_fd fd(open(path.c_str(), O_RDWR | O_NOFOLLOW | O_CLOEXEC));
- ASSERT_GE(fd, 0);
- ASSERT_EQ(get_block_device_size(fd), kTestImageSize);
- }
-
- ASSERT_TRUE(manager_->UnmapImageDevice(base_name_));
- ASSERT_FALSE(manager_->IsImageMapped(base_name_));
- ASSERT_EQ(android::base::GetProperty(PropertyName(), ""), "");
-}
-
-// This fixture is for tests against a simulated device environment. Rather
-// than use /data, we create an image and then layer a new filesystem within
-// it. Each test then decides how to mount and create layered images. This
-// allows us to test FBE vs FDE configurations.
-class ImageTest : public ::testing::Test {
- public:
- ImageTest() : dm_(DeviceMapper::Instance()) {}
-
- void SetUp() override {
- manager_ = ImageManager::Open(kMetadataPath, gDataPath);
- ASSERT_NE(manager_, nullptr);
-
- manager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
-
- submanager_ = ImageManager::Open(kMetadataPath + "/mnt"s, gDataPath + "/mnt"s);
- ASSERT_NE(submanager_, nullptr);
-
- submanager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
-
- // Ensure that metadata is cleared in between runs.
- submanager_->RemoveAllImages();
- manager_->RemoveAllImages();
-
- const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
- base_name_ = tinfo->name();
- test_image_name_ = base_name_ + "-base";
- wrapper_device_name_ = base_name_ + "-wrapper";
-
- ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize * 16, false, nullptr));
- ASSERT_TRUE(manager_->MapImageDevice(base_name_, 5s, &base_device_));
- }
-
- void TearDown() override {
- submanager_->UnmapImageDevice(test_image_name_);
- umount(gDataMountPath.c_str());
- dm_.DeleteDeviceIfExists(wrapper_device_name_);
- manager_->UnmapImageDevice(base_name_);
- manager_->DeleteBackingImage(base_name_);
- }
-
- protected:
- bool DoFormat(const std::string& device) {
- // clang-format off
- std::vector<std::string> mkfs_args = {
- "/system/bin/mke2fs",
- "-F",
- "-b 4096",
- "-t ext4",
- "-m 0",
- "-O has_journal",
- device,
- ">/dev/null",
- "2>/dev/null",
- "</dev/null",
- };
- // clang-format on
- auto command = android::base::Join(mkfs_args, " ");
- return system(command.c_str()) == 0;
- }
-
- std::unique_ptr<ImageManager> manager_;
- std::unique_ptr<ImageManager> submanager_;
-
- DeviceMapper& dm_;
- std::string base_name_;
- std::string base_device_;
- std::string test_image_name_;
- std::string wrapper_device_name_;
-};
-
-TEST_F(ImageTest, DirectMount) {
- ASSERT_TRUE(DoFormat(base_device_));
- ASSERT_EQ(mount(base_device_.c_str(), gDataMountPath.c_str(), "ext4", 0, nullptr), 0);
- ASSERT_TRUE(submanager_->CreateBackingImage(test_image_name_, kTestImageSize, false, nullptr));
-
- std::string path;
- ASSERT_TRUE(submanager_->MapImageDevice(test_image_name_, 5s, &path));
- ASSERT_TRUE(android::base::StartsWith(path, "/dev/block/loop"));
-}
-
-TEST_F(ImageTest, IndirectMount) {
- // Create a simple wrapper around the base device that we'll mount from
- // instead. This will simulate the code paths for dm-crypt/default-key/bow
- // and force us to use device-mapper rather than loop devices.
- uint64_t device_size = 0;
- {
- unique_fd fd(open(base_device_.c_str(), O_RDWR | O_CLOEXEC));
- ASSERT_GE(fd, 0);
- device_size = get_block_device_size(fd);
- ASSERT_EQ(device_size, kTestImageSize * 16);
- }
- uint64_t num_sectors = device_size / 512;
-
- auto& dm = DeviceMapper::Instance();
-
- DmTable table;
- table.Emplace<DmTargetLinear>(0, num_sectors, base_device_, 0);
- ASSERT_TRUE(dm.CreateDevice(wrapper_device_name_, table));
-
- // Format and mount.
- std::string wrapper_device;
- ASSERT_TRUE(dm.GetDmDevicePathByName(wrapper_device_name_, &wrapper_device));
- ASSERT_TRUE(WaitForFile(wrapper_device, 5s));
- ASSERT_TRUE(DoFormat(wrapper_device));
- ASSERT_EQ(mount(wrapper_device.c_str(), gDataMountPath.c_str(), "ext4", 0, nullptr), 0);
-
- ASSERT_TRUE(submanager_->CreateBackingImage(test_image_name_, kTestImageSize, false, nullptr));
-
- std::string path;
- ASSERT_TRUE(submanager_->MapImageDevice(test_image_name_, 5s, &path));
- ASSERT_TRUE(android::base::StartsWith(path, "/dev/block/dm-"));
-}
-
-bool Mkdir(const std::string& path) {
- if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
- std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
- return false;
- }
- return true;
-}
-
-int main(int argc, char** argv) {
- ::testing::InitGoogleTest(&argc, argv);
-
- if (argc >= 2) {
- gDataPath = argv[1];
- } else {
- gDataPath = "/data/gsi/test";
- }
- gDataMountPath = gDataPath + "/mnt"s;
-
- if (!Mkdir(gDataPath) || !Mkdir(kMetadataPath) || !Mkdir(gDataMountPath) ||
- !Mkdir(kMetadataPath + "/mnt"s)) {
- return 1;
- }
- return RUN_ALL_TESTS();
-}
diff --git a/libfiemap/include/libfiemap/fiemap_writer.h b/libfiemap/include/libfiemap/fiemap_writer.h
deleted file mode 100644
index c692265..0000000
--- a/libfiemap/include/libfiemap/fiemap_writer.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#pragma once
-
-#include <linux/fiemap.h>
-#include <stdint.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <functional>
-#include <string>
-#include <vector>
-
-#include <android-base/unique_fd.h>
-
-namespace android {
-namespace fiemap {
-
-class FiemapWriter;
-using FiemapUniquePtr = std::unique_ptr<FiemapWriter>;
-
-class FiemapWriter final {
- public:
- // Factory method for FiemapWriter.
- // The method returns FiemapUniquePtr that contains all the data necessary to be able to write
- // to the given file directly using raw block i/o. The optional progress callback will be
- // invoked, if create is true, while the file is being initialized. It receives the bytes
- // written and the number of total bytes. If the callback returns false, the operation will
- // fail.
- //
- // Note: when create is true, the file size will be aligned up to the nearest file system
- // block.
- static FiemapUniquePtr Open(const std::string& file_path, uint64_t file_size,
- bool create = true,
- std::function<bool(uint64_t, uint64_t)> progress = {});
-
- // Check that a file still has the same extents since it was last opened with FiemapWriter,
- // assuming the file was not resized outside of FiemapWriter. Returns false either on error
- // or if the file was not pinned.
- //
- // This will always return true on Ext4. On F2FS, it will return true if either of the
- // following cases are true:
- // - The file was never pinned.
- // - The file is pinned and has not been moved by the GC.
- // Thus, this method should only be called for pinned files (such as those returned by
- // FiemapWriter::Open).
- static bool HasPinnedExtents(const std::string& file_path);
-
- // Returns the underlying block device of a file. This will look past device-mapper layers
- // as long as each layer would not change block mappings (i.e., dm-crypt, dm-bow, and dm-
- // default-key tables are okay; dm-linear is not). If a mapping such as dm-linear is found,
- // it will be returned in place of any physical block device.
- //
- // It is the caller's responsibility to check whether the returned block device is acceptable.
- // Gsid, for example, will only accept /dev/block/by-name/userdata as the bottom device.
- // Callers can check the device name (dm- or loop prefix), inspect sysfs, or compare the major
- // number against a boot device.
- //
- // If device-mapper nodes were encountered, then |uses_dm| will be set to true.
- static bool GetBlockDeviceForFile(const std::string& file_path, std::string* bdev_path,
- bool* uses_dm = nullptr);
-
- ~FiemapWriter() = default;
-
- const std::string& file_path() const { return file_path_; };
- uint64_t size() const { return file_size_; };
- const std::string& bdev_path() const { return bdev_path_; };
- uint64_t block_size() const { return block_size_; };
- const std::vector<struct fiemap_extent>& extents() { return extents_; };
- uint32_t fs_type() const { return fs_type_; }
-
- // Non-copyable & Non-movable
- FiemapWriter(const FiemapWriter&) = delete;
- FiemapWriter& operator=(const FiemapWriter&) = delete;
- FiemapWriter& operator=(FiemapWriter&&) = delete;
- FiemapWriter(FiemapWriter&&) = delete;
-
- private:
- // Name of the file managed by this class.
- std::string file_path_;
- // Block device on which we have created the file.
- std::string bdev_path_;
-
- // Size in bytes of the file this class is writing
- uint64_t file_size_;
-
- // total size in bytes of the block device
- uint64_t bdev_size_;
-
- // Filesystem type where the file is being created.
- // See: <uapi/linux/magic.h> for filesystem magic numbers
- uint32_t fs_type_;
-
- // block size as reported by the kernel of the underlying block device;
- uint64_t block_size_;
-
- // This file's fiemap
- std::vector<struct fiemap_extent> extents_;
-
- FiemapWriter() = default;
-};
-
-} // namespace fiemap
-} // namespace android
diff --git a/libfiemap/include/libfiemap/image_manager.h b/libfiemap/include/libfiemap/image_manager.h
deleted file mode 100644
index efbe9bd..0000000
--- a/libfiemap/include/libfiemap/image_manager.h
+++ /dev/null
@@ -1,184 +0,0 @@
-//
-// 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.
-//
-
-#pragma once
-
-#include <stdint.h>
-
-#include <chrono>
-#include <functional>
-#include <memory>
-#include <string>
-
-#include <android-base/unique_fd.h>
-#include <liblp/partition_opener.h>
-
-namespace android {
-namespace fiemap {
-
-class IImageManager {
- public:
- using IPartitionOpener = android::fs_mgr::IPartitionOpener;
-
- virtual ~IImageManager() {}
-
- // When linking to libfiemap_binder, the Open() call will use binder.
- // Otherwise, the Open() call will use the ImageManager implementation
- // below.
- static std::unique_ptr<IImageManager> Open(const std::string& dir_prefix,
- const std::chrono::milliseconds& timeout_ms);
-
- // Flags for CreateBackingImage().
- static constexpr int CREATE_IMAGE_DEFAULT = 0x0;
- static constexpr int CREATE_IMAGE_READONLY = 0x1;
- static constexpr int CREATE_IMAGE_ZERO_FILL = 0x2;
-
- // Create an image that can be mapped as a block-device. If |force_zero_fill|
- // is true, the image will be zero-filled. Otherwise, the initial content
- // of the image is undefined. If zero-fill is requested, and the operation
- // cannot be completed, the image will be deleted and this function will
- // return false.
- virtual bool CreateBackingImage(const std::string& name, uint64_t size, int flags) = 0;
-
- // Delete an image created with CreateBackingImage. Its entry will be
- // removed from the associated lp_metadata file.
- virtual bool DeleteBackingImage(const std::string& name) = 0;
-
- // Create a block device for an image previously created with
- // CreateBackingImage. This will wait for at most |timeout_ms| milliseconds
- // for |path| to be available, and will return false if not available in
- // the requested time. If |timeout_ms| is zero, this is NOT guaranteed to
- // return true. A timeout of 10s is recommended.
- //
- // Note that snapshots created with a readonly flag are always mapped
- // writable. The flag is persisted in the lp_metadata file however, so if
- // fs_mgr::CreateLogicalPartition(s) is used, the flag will be respected.
- virtual bool MapImageDevice(const std::string& name,
- const std::chrono::milliseconds& timeout_ms, std::string* path) = 0;
-
- // Unmap a block device previously mapped with mapBackingImage.
- virtual bool UnmapImageDevice(const std::string& name) = 0;
-
- // Returns true whether the named backing image exists.
- virtual bool BackingImageExists(const std::string& name) = 0;
-
- // Returns true if the specified image is mapped to a device.
- virtual bool IsImageMapped(const std::string& name) = 0;
-
- // Map an image using device-mapper. This is not available over binder, and
- // is intended only for first-stage init. The returned device is a major:minor
- // device string.
- virtual bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
- std::string* dev) = 0;
-
- // Get all backing image names.
- virtual std::vector<std::string> GetAllBackingImages() = 0;
-
- // Writes |bytes| zeros to |name| file. If |bytes| is 0, then the
- // whole file if filled with zeros.
- virtual bool ZeroFillNewImage(const std::string& name, uint64_t bytes) = 0;
-
- virtual bool UnmapImageIfExists(const std::string& name);
-};
-
-class ImageManager final : public IImageManager {
- public:
- // Return an ImageManager for the given metadata and data directories. Both
- // directories must already exist.
- static std::unique_ptr<ImageManager> Open(const std::string& metadata_dir,
- const std::string& data_dir);
-
- // Helper function that derives the metadata and data dirs given a single
- // prefix.
- static std::unique_ptr<ImageManager> Open(const std::string& dir_prefix);
-
- // Methods that must be implemented from IImageManager.
- bool CreateBackingImage(const std::string& name, uint64_t size, int flags) override;
- bool DeleteBackingImage(const std::string& name) override;
- bool MapImageDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,
- std::string* path) override;
- bool UnmapImageDevice(const std::string& name) override;
- bool BackingImageExists(const std::string& name) override;
- bool IsImageMapped(const std::string& name) override;
- bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
- std::string* dev) override;
-
- std::vector<std::string> GetAllBackingImages();
- // Same as CreateBackingImage, but provides a progress notification.
- bool CreateBackingImage(const std::string& name, uint64_t size, int flags,
- std::function<bool(uint64_t, uint64_t)>&& on_progress);
-
- // Find and remove all images and metadata for a given image dir.
- bool RemoveAllImages();
-
- // Returns true if the named partition exists. This does not check the
- // consistency of the backing image/data file.
- bool PartitionExists(const std::string& name);
-
- // Validates that all images still have pinned extents. This will be removed
- // once b/134588268 is fixed.
- bool Validate();
-
- void set_partition_opener(std::unique_ptr<IPartitionOpener>&& opener);
-
- // Writes |bytes| zeros at the beginning of the passed image
- bool ZeroFillNewImage(const std::string& name, uint64_t bytes);
-
- private:
- ImageManager(const std::string& metadata_dir, const std::string& data_dir);
- std::string GetImageHeaderPath(const std::string& name);
- std::string GetStatusFilePath(const std::string& image_name);
- bool MapWithLoopDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,
- std::string* path);
- bool MapWithLoopDeviceList(const std::vector<std::string>& device_list, const std::string& name,
- const std::chrono::milliseconds& timeout_ms, std::string* path);
- bool MapWithDmLinear(const IPartitionOpener& opener, const std::string& name,
- const std::chrono::milliseconds& timeout_ms, std::string* path);
- bool UnmapImageDevice(const std::string& name, bool force);
-
- ImageManager(const ImageManager&) = delete;
- ImageManager& operator=(const ImageManager&) = delete;
- ImageManager& operator=(ImageManager&&) = delete;
- ImageManager(ImageManager&&) = delete;
-
- std::string metadata_dir_;
- std::string data_dir_;
- std::unique_ptr<IPartitionOpener> partition_opener_;
-};
-
-// RAII helper class for mapping and opening devices with an ImageManager.
-class MappedDevice final {
- public:
- static std::unique_ptr<MappedDevice> Open(IImageManager* manager,
- const std::chrono::milliseconds& timeout_ms,
- const std::string& name);
-
- ~MappedDevice();
-
- int fd() const { return fd_; }
- const std::string& path() const { return path_; }
-
- protected:
- MappedDevice(IImageManager* manager, const std::string& name, const std::string& path);
-
- IImageManager* manager_;
- std::string name_;
- std::string path_;
- android::base::unique_fd fd_;
-};
-
-} // namespace fiemap
-} // namespace android
diff --git a/libfiemap/include/libfiemap/split_fiemap_writer.h b/libfiemap/include/libfiemap/split_fiemap_writer.h
deleted file mode 100644
index feffb3d..0000000
--- a/libfiemap/include/libfiemap/split_fiemap_writer.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include <stdint.h>
-
-#include <functional>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android-base/unique_fd.h>
-
-#include "fiemap_writer.h"
-
-namespace android {
-namespace fiemap {
-
-// Wrapper around FiemapWriter that is able to split images across files if
-// necessary.
-class SplitFiemap final {
- public:
- using ProgressCallback = std::function<bool(uint64_t, uint64_t)>;
-
- // Create a new split fiemap file. If |max_piece_size| is 0, the number of
- // pieces will be determined automatically by detecting the filesystem.
- // Otherwise, the file will be split evenly (with the remainder in the
- // final file).
- static std::unique_ptr<SplitFiemap> Create(const std::string& file_path, uint64_t file_size,
- uint64_t max_piece_size,
- ProgressCallback progress = {});
-
- // Open an existing split fiemap file.
- static std::unique_ptr<SplitFiemap> Open(const std::string& file_path);
-
- ~SplitFiemap();
-
- // Return a list of all files created for a split file.
- static bool GetSplitFileList(const std::string& file_path, std::vector<std::string>* list);
-
- // Destroy all components of a split file. If the root file does not exist,
- // this returns true and does not report an error.
- static bool RemoveSplitFiles(const std::string& file_path, std::string* message = nullptr);
-
- // Return whether all components of a split file still have pinned extents.
- bool HasPinnedExtents() const;
-
- // Helper method for writing data that spans files. Note there is no seek
- // method (yet); this starts at 0 and increments the position by |bytes|.
- bool Write(const void* data, uint64_t bytes);
-
- // Flush all writes to all split files.
- bool Flush();
-
- const std::vector<struct fiemap_extent>& extents();
- uint32_t block_size() const;
- uint64_t size() const { return total_size_; }
- const std::string& bdev_path() const;
-
- // Non-copyable & Non-movable
- SplitFiemap(const SplitFiemap&) = delete;
- SplitFiemap& operator=(const SplitFiemap&) = delete;
- SplitFiemap& operator=(SplitFiemap&&) = delete;
- SplitFiemap(SplitFiemap&&) = delete;
-
- private:
- SplitFiemap() = default;
- void AddFile(FiemapUniquePtr&& file);
-
- bool creating_ = false;
- std::string list_file_;
- std::vector<FiemapUniquePtr> files_;
- std::vector<struct fiemap_extent> extents_;
- uint64_t total_size_ = 0;
-
- // Most recently open file and position for Write().
- size_t cursor_index_ = 0;
- uint64_t cursor_file_pos_ = 0;
- android::base::unique_fd cursor_fd_;
-};
-
-} // namespace fiemap
-} // namespace android
diff --git a/libfiemap/metadata.cpp b/libfiemap/metadata.cpp
deleted file mode 100644
index 597efe9..0000000
--- a/libfiemap/metadata.cpp
+++ /dev/null
@@ -1,196 +0,0 @@
-//
-// 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 "metadata.h"
-
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <liblp/builder.h>
-
-#include "utility.h"
-
-namespace android {
-namespace fiemap {
-
-using namespace android::fs_mgr;
-
-static constexpr uint32_t kMaxMetadataSize = 256 * 1024;
-
-std::string GetMetadataFile(const std::string& metadata_dir) {
- return JoinPaths(metadata_dir, "lp_metadata");
-}
-
-bool MetadataExists(const std::string& metadata_dir) {
- auto metadata_file = GetMetadataFile(metadata_dir);
- return access(metadata_file.c_str(), F_OK) == 0;
-}
-
-std::unique_ptr<LpMetadata> OpenMetadata(const std::string& metadata_dir) {
- auto metadata_file = GetMetadataFile(metadata_dir);
- auto metadata = ReadFromImageFile(metadata_file);
- if (!metadata) {
- LOG(ERROR) << "Could not read metadata file " << metadata_file;
- return nullptr;
- }
- return metadata;
-}
-
-// :TODO: overwrite on create if open fails
-std::unique_ptr<MetadataBuilder> OpenOrCreateMetadata(const std::string& metadata_dir,
- SplitFiemap* file) {
- auto metadata_file = GetMetadataFile(metadata_dir);
-
- PartitionOpener opener;
- std::unique_ptr<MetadataBuilder> builder;
- if (access(metadata_file.c_str(), R_OK)) {
- if (errno != ENOENT) {
- PLOG(ERROR) << "access " << metadata_file << " failed:";
- return nullptr;
- }
-
- auto data_device = GetDevicePathForFile(file);
-
- BlockDeviceInfo device_info;
- if (!opener.GetInfo(data_device, &device_info)) {
- LOG(ERROR) << "Could not read partition: " << data_device;
- return nullptr;
- }
-
- std::vector<BlockDeviceInfo> block_devices = {device_info};
- auto super_name = android::base::Basename(data_device);
- builder = MetadataBuilder::New(block_devices, super_name, kMaxMetadataSize, 1);
- } else {
- auto metadata = OpenMetadata(metadata_dir);
- if (!metadata) {
- return nullptr;
- }
- builder = MetadataBuilder::New(*metadata.get(), &opener);
- }
-
- if (!builder) {
- LOG(ERROR) << "Could not create metadata builder";
- return nullptr;
- }
- return builder;
-}
-
-bool SaveMetadata(MetadataBuilder* builder, const std::string& metadata_dir) {
- auto exported = builder->Export();
- if (!exported) {
- LOG(ERROR) << "Unable to export new metadata";
- return false;
- }
-
- // If there are no more partitions in the metadata, just delete the file.
- auto metadata_file = GetMetadataFile(metadata_dir);
- if (exported->partitions.empty() && android::base::RemoveFileIfExists(metadata_file)) {
- return true;
- }
- if (!WriteToImageFile(metadata_file, *exported.get())) {
- LOG(ERROR) << "Unable to save new metadata";
- return false;
- }
- return true;
-}
-
-bool RemoveAllMetadata(const std::string& dir) {
- auto metadata_file = GetMetadataFile(dir);
- return android::base::RemoveFileIfExists(metadata_file);
-}
-
-bool FillPartitionExtents(MetadataBuilder* builder, Partition* partition, SplitFiemap* file,
- uint64_t partition_size) {
- auto block_device = android::base::Basename(GetDevicePathForFile(file));
-
- uint64_t sectors_needed = partition_size / LP_SECTOR_SIZE;
- for (const auto& extent : file->extents()) {
- 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 RemoveImageMetadata(const std::string& metadata_dir, const std::string& partition_name) {
- if (!MetadataExists(metadata_dir)) {
- return true;
- }
- auto metadata = OpenMetadata(metadata_dir);
- if (!metadata) {
- return false;
- }
-
- PartitionOpener opener;
- auto builder = MetadataBuilder::New(*metadata.get(), &opener);
- if (!builder) {
- return false;
- }
- builder->RemovePartition(partition_name);
- return SaveMetadata(builder.get(), metadata_dir);
-}
-
-bool UpdateMetadata(const std::string& metadata_dir, const std::string& partition_name,
- SplitFiemap* file, uint64_t partition_size, bool readonly) {
- auto builder = OpenOrCreateMetadata(metadata_dir, file);
- if (!builder) {
- return false;
- }
- auto partition = builder->FindPartition(partition_name);
- if (!partition) {
- int attrs = 0;
- if (readonly) attrs |= LP_PARTITION_ATTR_READONLY;
-
- if ((partition = builder->AddPartition(partition_name, attrs)) == nullptr) {
- LOG(ERROR) << "Could not add partition " << partition_name << " to metadata";
- return false;
- }
- }
- partition->RemoveExtents();
-
- if (!FillPartitionExtents(builder.get(), partition, file, partition_size)) {
- return false;
- }
- return SaveMetadata(builder.get(), metadata_dir);
-}
-
-} // namespace fiemap
-} // namespace android
diff --git a/libfiemap/metadata.h b/libfiemap/metadata.h
deleted file mode 100644
index f0ce23e..0000000
--- a/libfiemap/metadata.h
+++ /dev/null
@@ -1,36 +0,0 @@
-//
-// 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 <stdint.h>
-
-#include <memory>
-#include <string>
-
-#include <libfiemap/split_fiemap_writer.h>
-#include <liblp/liblp.h>
-
-namespace android {
-namespace fiemap {
-
-bool MetadataExists(const std::string& metadata_dir);
-std::unique_ptr<android::fs_mgr::LpMetadata> OpenMetadata(const std::string& metadata_dir);
-bool UpdateMetadata(const std::string& metadata_dir, const std::string& partition_name,
- SplitFiemap* file, uint64_t partition_size, bool readonly);
-bool RemoveImageMetadata(const std::string& metadata_dir, const std::string& partition_name);
-bool RemoveAllMetadata(const std::string& dir);
-
-} // namespace fiemap
-} // namespace android
diff --git a/libfiemap/passthrough.cpp b/libfiemap/passthrough.cpp
deleted file mode 100644
index 1ccd9a0..0000000
--- a/libfiemap/passthrough.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-//
-// 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 <libfiemap/image_manager.h>
-
-namespace android {
-namespace fiemap {
-
-std::unique_ptr<IImageManager> IImageManager::Open(const std::string& dir_prefix,
- const std::chrono::milliseconds& timeout_ms) {
- (void)timeout_ms;
- return ImageManager::Open(dir_prefix);
-}
-
-} // namespace fiemap
-} // namespace android
diff --git a/libfiemap/split_fiemap_writer.cpp b/libfiemap/split_fiemap_writer.cpp
deleted file mode 100644
index cc54f20..0000000
--- a/libfiemap/split_fiemap_writer.cpp
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- * 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 <libfiemap/split_fiemap_writer.h>
-
-#include <fcntl.h>
-#include <stdint.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <memory>
-#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-base/unique_fd.h>
-
-#include "utility.h"
-
-namespace android {
-namespace fiemap {
-
-using android::base::unique_fd;
-
-// We use a four-digit suffix at the end of filenames.
-static const size_t kMaxFilePieces = 500;
-
-std::unique_ptr<SplitFiemap> SplitFiemap::Create(const std::string& file_path, uint64_t file_size,
- uint64_t max_piece_size,
- ProgressCallback progress) {
- if (!file_size) {
- LOG(ERROR) << "Cannot create a fiemap for a 0-length file: " << file_path;
- return nullptr;
- }
-
- if (!max_piece_size) {
- max_piece_size = DetermineMaximumFileSize(file_path);
- if (!max_piece_size) {
- LOG(ERROR) << "Could not determine maximum file size for " << file_path;
- return nullptr;
- }
- }
-
- // Remove any existing file.
- RemoveSplitFiles(file_path);
-
- // Call |progress| only when the total percentage would significantly change.
- int permille = -1;
- uint64_t total_bytes_written = 0;
- auto on_progress = [&](uint64_t written, uint64_t) -> bool {
- uint64_t actual_written = total_bytes_written + written;
- int new_permille = (actual_written * 1000) / file_size;
- if (new_permille != permille && actual_written < file_size) {
- if (progress && !progress(actual_written, file_size)) {
- return false;
- }
- permille = new_permille;
- }
- return true;
- };
-
- std::unique_ptr<SplitFiemap> out(new SplitFiemap());
- out->creating_ = true;
- out->list_file_ = file_path;
-
- // Create the split files.
- uint64_t remaining_bytes = file_size;
- while (remaining_bytes) {
- if (out->files_.size() >= kMaxFilePieces) {
- LOG(ERROR) << "Requested size " << file_size << " created too many split files";
- return nullptr;
- }
- std::string chunk_path =
- android::base::StringPrintf("%s.%04d", file_path.c_str(), (int)out->files_.size());
- uint64_t chunk_size = std::min(max_piece_size, remaining_bytes);
- auto writer = FiemapWriter::Open(chunk_path, chunk_size, true, on_progress);
- if (!writer) {
- return nullptr;
- }
-
- // To make sure the alignment doesn't create too much inconsistency, we
- // account the *actual* size, not the requested size.
- total_bytes_written += writer->size();
-
- // writer->size() is block size aligned and could be bigger than remaining_bytes
- // If remaining_bytes is bigger, set remaining_bytes to 0 to avoid underflow error.
- remaining_bytes = remaining_bytes > writer->size() ? (remaining_bytes - writer->size()) : 0;
-
- out->AddFile(std::move(writer));
- }
-
- // Create the split file list.
- unique_fd fd(open(out->list_file_.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC, 0660));
- if (fd < 0) {
- PLOG(ERROR) << "Failed to open " << file_path;
- return nullptr;
- }
-
- for (const auto& writer : out->files_) {
- std::string line = android::base::Basename(writer->file_path()) + "\n";
- if (!android::base::WriteFully(fd, line.data(), line.size())) {
- PLOG(ERROR) << "Write failed " << file_path;
- return nullptr;
- }
- }
-
- // Unset this bit, so we don't unlink on destruction.
- out->creating_ = false;
- return out;
-}
-
-std::unique_ptr<SplitFiemap> SplitFiemap::Open(const std::string& file_path) {
- std::vector<std::string> files;
- if (!GetSplitFileList(file_path, &files)) {
- return nullptr;
- }
-
- std::unique_ptr<SplitFiemap> out(new SplitFiemap());
- out->list_file_ = file_path;
-
- for (const auto& file : files) {
- auto writer = FiemapWriter::Open(file, 0, false);
- if (!writer) {
- // Error was logged in Open().
- return nullptr;
- }
- out->AddFile(std::move(writer));
- }
- return out;
-}
-
-bool SplitFiemap::GetSplitFileList(const std::string& file_path, std::vector<std::string>* list) {
- // This is not the most efficient thing, but it is simple and recovering
- // the fiemap/fibmap is much more expensive.
- std::string contents;
- if (!android::base::ReadFileToString(file_path, &contents, true)) {
- PLOG(ERROR) << "Error reading file: " << file_path;
- return false;
- }
-
- std::vector<std::string> names = android::base::Split(contents, "\n");
- std::string dir = android::base::Dirname(file_path);
- for (const auto& name : names) {
- if (!name.empty()) {
- list->emplace_back(dir + "/" + name);
- }
- }
- return true;
-}
-
-bool SplitFiemap::RemoveSplitFiles(const std::string& file_path, std::string* message) {
- // Early exit if this does not exist, and do not report an error.
- if (access(file_path.c_str(), F_OK) && errno == ENOENT) {
- return true;
- }
-
- bool ok = true;
- std::vector<std::string> files;
- if (GetSplitFileList(file_path, &files)) {
- for (const auto& file : files) {
- ok &= android::base::RemoveFileIfExists(file, message);
- }
- }
- ok &= android::base::RemoveFileIfExists(file_path, message);
- return ok;
-}
-
-bool SplitFiemap::HasPinnedExtents() const {
- for (const auto& file : files_) {
- if (!FiemapWriter::HasPinnedExtents(file->file_path())) {
- return false;
- }
- }
- return true;
-}
-
-const std::vector<struct fiemap_extent>& SplitFiemap::extents() {
- if (extents_.empty()) {
- for (const auto& file : files_) {
- const auto& extents = file->extents();
- extents_.insert(extents_.end(), extents.begin(), extents.end());
- }
- }
- return extents_;
-}
-
-bool SplitFiemap::Write(const void* data, uint64_t bytes) {
- // Open the current file.
- FiemapWriter* file = files_[cursor_index_].get();
-
- const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(data);
- uint64_t bytes_remaining = bytes;
- while (bytes_remaining) {
- // How many bytes can we write into the current file?
- uint64_t file_bytes_left = file->size() - cursor_file_pos_;
- if (!file_bytes_left) {
- if (cursor_index_ == files_.size() - 1) {
- LOG(ERROR) << "write past end of file requested";
- return false;
- }
-
- // No space left in the current file, but we have more files to
- // use, so prep the next one.
- cursor_fd_ = {};
- cursor_file_pos_ = 0;
- file = files_[++cursor_index_].get();
- file_bytes_left = file->size();
- }
-
- // Open the current file if it's not open.
- if (cursor_fd_ < 0) {
- cursor_fd_.reset(open(file->file_path().c_str(), O_CLOEXEC | O_WRONLY));
- if (cursor_fd_ < 0) {
- PLOG(ERROR) << "open failed: " << file->file_path();
- return false;
- }
- CHECK(cursor_file_pos_ == 0);
- }
-
- if (!FiemapWriter::HasPinnedExtents(file->file_path())) {
- LOG(ERROR) << "file is no longer pinned: " << file->file_path();
- return false;
- }
-
- uint64_t bytes_to_write = std::min(file_bytes_left, bytes_remaining);
- if (!android::base::WriteFully(cursor_fd_, data_ptr, bytes_to_write)) {
- PLOG(ERROR) << "write failed: " << file->file_path();
- return false;
- }
- data_ptr += bytes_to_write;
- bytes_remaining -= bytes_to_write;
- cursor_file_pos_ += bytes_to_write;
- }
-
- // If we've reached the end of the current file, close it for sanity.
- if (cursor_file_pos_ == file->size()) {
- cursor_fd_ = {};
- }
- return true;
-}
-
-bool SplitFiemap::Flush() {
- for (const auto& file : files_) {
- unique_fd fd(open(file->file_path().c_str(), O_RDONLY | O_CLOEXEC));
- if (fd < 0) {
- PLOG(ERROR) << "open failed: " << file->file_path();
- return false;
- }
- if (fsync(fd)) {
- PLOG(ERROR) << "fsync failed: " << file->file_path();
- return false;
- }
- }
- return true;
-}
-
-SplitFiemap::~SplitFiemap() {
- if (!creating_) {
- return;
- }
-
- // We failed to finish creating, so unlink everything.
- unlink(list_file_.c_str());
- for (auto&& file : files_) {
- std::string path = file->file_path();
- file = nullptr;
-
- unlink(path.c_str());
- }
-}
-
-void SplitFiemap::AddFile(FiemapUniquePtr&& file) {
- total_size_ += file->size();
- files_.emplace_back(std::move(file));
-}
-
-uint32_t SplitFiemap::block_size() const {
- return files_[0]->block_size();
-}
-
-const std::string& SplitFiemap::bdev_path() const {
- return files_[0]->bdev_path();
-}
-
-} // namespace fiemap
-} // namespace android
diff --git a/libfiemap/testdata/file_32k b/libfiemap/testdata/file_32k
deleted file mode 100644
index 12f3be4..0000000
--- a/libfiemap/testdata/file_32k
+++ /dev/null
Binary files differ
diff --git a/libfiemap/testdata/file_4k b/libfiemap/testdata/file_4k
deleted file mode 100644
index 08e7df1..0000000
--- a/libfiemap/testdata/file_4k
+++ /dev/null
Binary files differ
diff --git a/libfiemap/testdata/unaligned_file b/libfiemap/testdata/unaligned_file
deleted file mode 100644
index c107c26..0000000
--- a/libfiemap/testdata/unaligned_file
+++ /dev/null
Binary files differ
diff --git a/libfiemap/utility.cpp b/libfiemap/utility.cpp
deleted file mode 100644
index 1f6378f..0000000
--- a/libfiemap/utility.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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 "utility.h"
-
-#include <stdint.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/vfs.h>
-#include <unistd.h>
-
-#include <android-base/logging.h>
-#include <android-base/strings.h>
-#include <libfiemap/fiemap_writer.h>
-
-namespace android {
-namespace fiemap {
-
-static constexpr char kUserdataDevice[] = "/dev/block/by-name/userdata";
-
-uint64_t DetermineMaximumFileSize(const std::string& file_path) {
- // Create the smallest file possible (one block).
- auto writer = FiemapWriter::Open(file_path, 1);
- if (!writer) {
- return 0;
- }
-
- uint64_t result = 0;
- switch (writer->fs_type()) {
- case EXT4_SUPER_MAGIC:
- // The minimum is 16GiB, so just report that. If we wanted we could parse the
- // superblock and figure out if 64-bit support is enabled.
- result = 17179869184ULL;
- break;
- case F2FS_SUPER_MAGIC:
- // Formula is from https://www.kernel.org/doc/Documentation/filesystems/f2fs.txt
- // 4KB * (923 + 2 * 1018 + 2 * 1018 * 1018 + 1018 * 1018 * 1018) := 3.94TB.
- result = 4329690886144ULL;
- break;
- case MSDOS_SUPER_MAGIC:
- // 4GB-1, which we want aligned to the block size.
- result = 4294967295;
- result -= (result % writer->block_size());
- break;
- default:
- LOG(ERROR) << "Unknown file system type: " << writer->fs_type();
- break;
- }
-
- // Close and delete the temporary file.
- writer = nullptr;
- unlink(file_path.c_str());
-
- return result;
-}
-
-// Given a SplitFiemap, this returns a device path that will work during first-
-// stage init (i.e., its path can be found by InitRequiredDevices).
-std::string GetDevicePathForFile(SplitFiemap* file) {
- auto bdev_path = file->bdev_path();
-
- struct stat userdata, given;
- if (!stat(bdev_path.c_str(), &given) && !stat(kUserdataDevice, &userdata)) {
- if (S_ISBLK(given.st_mode) && S_ISBLK(userdata.st_mode) &&
- given.st_rdev == userdata.st_rdev) {
- return kUserdataDevice;
- }
- }
- return bdev_path;
-}
-
-std::string JoinPaths(const std::string& dir, const std::string& file) {
- if (android::base::EndsWith(dir, "/")) {
- return dir + file;
- }
- return dir + "/" + file;
-}
-
-} // namespace fiemap
-} // namespace android
diff --git a/libfiemap/utility.h b/libfiemap/utility.h
deleted file mode 100644
index 19b632d..0000000
--- a/libfiemap/utility.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include <stdint.h>
-
-#include <string>
-
-#include <libfiemap/split_fiemap_writer.h>
-
-namespace android {
-namespace fiemap {
-
-// Given a file that will be created, determine the maximum size its containing
-// filesystem allows. Note this is a theoretical maximum size; free space is
-// ignored entirely.
-uint64_t DetermineMaximumFileSize(const std::string& file_path);
-
-// Given a SplitFiemap, this returns a device path that will work during first-
-// stage init (i.e., its path can be found by InitRequiredDevices).
-std::string GetDevicePathForFile(android::fiemap::SplitFiemap* file);
-
-// Combine two path components into a single path.
-std::string JoinPaths(const std::string& dir, const std::string& file);
-
-} // namespace fiemap
-} // namespace android
diff --git a/libgsi.cpp b/libgsi.cpp
index 2c8deb0..b342cc5 100644
--- a/libgsi.cpp
+++ b/libgsi.cpp
@@ -23,6 +23,7 @@
#include <android-base/file.h>
#include <android-base/parseint.h>
+#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include "file_paths.h"
@@ -32,8 +33,14 @@
namespace gsi {
using namespace std::literals;
+using android::base::ReadFileToString;
+using android::base::Split;
using android::base::unique_fd;
+bool GetActiveDsu(std::string* active_dsu) {
+ return android::base::ReadFileToString(kDsuActiveFile, active_dsu);
+}
+
bool IsGsiRunning() {
return !access(kGsiBootedIndicatorFile, F_OK);
}
@@ -53,6 +60,10 @@
return fsync(fd) == 0;
}
+std::string GetDsuSlot(const std::string& install_dir) {
+ return android::base::Basename(install_dir);
+}
+
bool CanBootIntoGsi(std::string* error) {
// Always delete this as a safety precaution, so we can return to the
// original system image. If we're confident GSI will boot, this will
diff --git a/partition_installer.cpp b/partition_installer.cpp
new file mode 100644
index 0000000..2ac313f
--- /dev/null
+++ b/partition_installer.cpp
@@ -0,0 +1,359 @@
+/*
+ * 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 "partition_installer.h"
+
+#include <sys/statvfs.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <ext4_utils/ext4_utils.h>
+#include <fs_mgr_dm_linear.h>
+#include <libdm/dm.h>
+#include <libgsi/libgsi.h>
+
+#include "file_paths.h"
+#include "gsi_service.h"
+#include "libgsi_private.h"
+
+namespace android {
+namespace gsi {
+
+using namespace std::literals;
+using namespace android::dm;
+using namespace android::fiemap;
+using namespace android::fs_mgr;
+using android::base::unique_fd;
+
+// 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;
+
+PartitionInstaller::PartitionInstaller(GsiService* service, const std::string& install_dir,
+ const std::string& name, const std::string& active_dsu,
+ int64_t size, bool read_only)
+ : service_(service),
+ install_dir_(install_dir),
+ name_(name),
+ active_dsu_(active_dsu),
+ size_(size),
+ readOnly_(read_only) {
+ images_ = ImageManager::Open(MetadataDir(active_dsu), install_dir_);
+}
+
+PartitionInstaller::~PartitionInstaller() {
+ Finish();
+ if (!succeeded_) {
+ // Close open handles before we remove files.
+ system_device_ = nullptr;
+ PostInstallCleanup(images_.get());
+ }
+ if (IsAshmemMapped()) {
+ UnmapAshmem();
+ }
+}
+
+void PartitionInstaller::PostInstallCleanup() {
+ auto manager = ImageManager::Open(MetadataDir(active_dsu_), install_dir_);
+ if (!manager) {
+ LOG(ERROR) << "Could not open image manager";
+ return;
+ }
+ return PostInstallCleanup(manager.get());
+}
+
+void PartitionInstaller::PostInstallCleanup(ImageManager* manager) {
+ std::string file = GetBackingFile(name_);
+ if (manager->IsImageMapped(file)) {
+ LOG(ERROR) << "unmap " << file;
+ manager->UnmapImageDevice(file);
+ }
+ manager->DeleteBackingImage(file);
+}
+
+int PartitionInstaller::StartInstall() {
+ if (int status = PerformSanityChecks()) {
+ return status;
+ }
+ if (int status = Preallocate()) {
+ return status;
+ }
+ if (!readOnly_) {
+ if (!Format()) {
+ return IGsiService::INSTALL_ERROR_GENERIC;
+ }
+ succeeded_ = true;
+ } else {
+ // Map ${name}_gsi so we can write to it.
+ system_device_ = OpenPartition(GetBackingFile(name_));
+ if (!system_device_) {
+ return IGsiService::INSTALL_ERROR_GENERIC;
+ }
+
+ // Clear the progress indicator.
+ service_->UpdateProgress(IGsiService::STATUS_NO_OPERATION, 0);
+ }
+ return IGsiService::INSTALL_OK;
+}
+
+int PartitionInstaller::PerformSanityChecks() {
+ if (!images_) {
+ LOG(ERROR) << "unable to create image manager";
+ return IGsiService::INSTALL_ERROR_GENERIC;
+ }
+ if (size_ < 0) {
+ LOG(ERROR) << "image size " << size_ << " is negative";
+ return IGsiService::INSTALL_ERROR_GENERIC;
+ }
+ if (android::gsi::IsGsiRunning()) {
+ LOG(ERROR) << "cannot install gsi inside a live gsi";
+ return IGsiService::INSTALL_ERROR_GENERIC;
+ }
+
+ struct statvfs sb;
+ if (statvfs(install_dir_.c_str(), &sb)) {
+ PLOG(ERROR) << "failed to read file system stats";
+ return IGsiService::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 <= (size_)) {
+ LOG(ERROR) << "not enough free space (only " << free_space << " bytes available)";
+ return IGsiService::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 IGsiService::INSTALL_ERROR_FILE_SYSTEM_CLUTTERED;
+ }
+ return IGsiService::INSTALL_OK;
+}
+
+int PartitionInstaller::Preallocate() {
+ std::string file = GetBackingFile(name_);
+ if (!images_->UnmapImageIfExists(file)) {
+ LOG(ERROR) << "failed to UnmapImageIfExists " << file;
+ return IGsiService::INSTALL_ERROR_GENERIC;
+ }
+ // always delete the old one when it presents in case there might a partition
+ // with same name but different size.
+ if (images_->BackingImageExists(file)) {
+ if (!images_->DeleteBackingImage(file)) {
+ LOG(ERROR) << "failed to DeleteBackingImage " << file;
+ return IGsiService::INSTALL_ERROR_GENERIC;
+ }
+ }
+ service_->StartAsyncOperation("create " + name_, size_);
+ if (!CreateImage(file, size_)) {
+ LOG(ERROR) << "Could not create userdata image";
+ return IGsiService::INSTALL_ERROR_GENERIC;
+ }
+ service_->UpdateProgress(IGsiService::STATUS_COMPLETE, 0);
+ return IGsiService::INSTALL_OK;
+}
+
+bool PartitionInstaller::CreateImage(const std::string& name, uint64_t size) {
+ auto progress = [this](uint64_t bytes, uint64_t /* total */) -> bool {
+ service_->UpdateProgress(IGsiService::STATUS_WORKING, bytes);
+ if (service_->should_abort()) return false;
+ return true;
+ };
+ int flags = ImageManager::CREATE_IMAGE_DEFAULT;
+ if (readOnly_) {
+ flags |= ImageManager::CREATE_IMAGE_READONLY;
+ }
+ return images_->CreateBackingImage(name, size, flags, std::move(progress));
+}
+
+std::unique_ptr<MappedDevice> PartitionInstaller::OpenPartition(const std::string& name) {
+ return MappedDevice::Open(images_.get(), 10s, name);
+}
+
+bool PartitionInstaller::CommitGsiChunk(int stream_fd, int64_t bytes) {
+ service_->StartAsyncOperation("write " + name_, size_);
+
+ if (bytes < 0) {
+ LOG(ERROR) << "chunk size " << bytes << " is negative";
+ return false;
+ }
+
+ static const size_t kBlockSize = 4096;
+ auto buffer = std::make_unique<char[]>(kBlockSize);
+
+ int progress = -1;
+ uint64_t remaining = bytes;
+ while (remaining) {
+ size_t max_to_read = std::min(static_cast<uint64_t>(kBlockSize), 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 = ((size_ - remaining) * 1000) / size_;
+ if (new_progress != progress) {
+ service_->UpdateProgress(IGsiService::STATUS_WORKING, size_ - remaining);
+ }
+ }
+
+ service_->UpdateProgress(IGsiService::STATUS_COMPLETE, size_);
+ return true;
+}
+
+bool PartitionInstaller::IsFinishedWriting() {
+ return gsi_bytes_written_ == size_;
+}
+
+bool PartitionInstaller::IsAshmemMapped() {
+ return ashmem_data_ != MAP_FAILED;
+}
+
+bool PartitionInstaller::CommitGsiChunk(const void* data, size_t bytes) {
+ if (static_cast<uint64_t>(bytes) > size_ - gsi_bytes_written_) {
+ // We cannot write past the end of the image file.
+ LOG(ERROR) << "chunk size " << bytes << " exceeds remaining image size (" << size_
+ << " expected, " << gsi_bytes_written_ << " written)";
+ return false;
+ }
+ if (service_->should_abort()) {
+ return false;
+ }
+ if (!android::base::WriteFully(system_device_->fd(), data, bytes)) {
+ PLOG(ERROR) << "write failed";
+ return false;
+ }
+ gsi_bytes_written_ += bytes;
+ return true;
+}
+
+int PartitionInstaller::GetPartitionFd() {
+ return system_device_->fd();
+}
+
+bool PartitionInstaller::MapAshmem(int fd, size_t size) {
+ ashmem_size_ = size;
+ ashmem_data_ = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ return ashmem_data_ != MAP_FAILED;
+}
+
+void PartitionInstaller::UnmapAshmem() {
+ if (munmap(ashmem_data_, ashmem_size_) != 0) {
+ PLOG(ERROR) << "cannot munmap";
+ return;
+ }
+ ashmem_data_ = MAP_FAILED;
+ ashmem_size_ = -1;
+}
+
+bool PartitionInstaller::CommitGsiChunk(size_t bytes) {
+ if (!IsAshmemMapped()) {
+ PLOG(ERROR) << "ashmem is not mapped";
+ return false;
+ }
+ bool success = CommitGsiChunk(ashmem_data_, bytes);
+ if (success && IsFinishedWriting()) {
+ UnmapAshmem();
+ }
+ return success;
+}
+
+const std::string PartitionInstaller::GetBackingFile(std::string name) {
+ return name + "_gsi";
+}
+
+bool PartitionInstaller::Format() {
+ auto file = GetBackingFile(name_);
+ auto device = OpenPartition(file);
+ if (!device) {
+ return false;
+ }
+
+ // libcutils checks the first 4K, no matter the block size.
+ std::string zeroes(4096, 0);
+ if (!android::base::WriteFully(device->fd(), zeroes.data(), zeroes.size())) {
+ PLOG(ERROR) << "write " << file;
+ return false;
+ }
+ return true;
+}
+
+int PartitionInstaller::Finish() {
+ if (readOnly_ && gsi_bytes_written_ != size_) {
+ // We cannot boot if the image is incomplete.
+ LOG(ERROR) << "image incomplete; expected " << size_ << " bytes, waiting for "
+ << (size_ - gsi_bytes_written_) << " bytes";
+ return IGsiService::INSTALL_ERROR_GENERIC;
+ }
+ if (system_device_ != nullptr && fsync(system_device_->fd())) {
+ PLOG(ERROR) << "fsync failed for " << name_ << "_gsi";
+ return IGsiService::INSTALL_ERROR_GENERIC;
+ }
+ system_device_ = {};
+
+ // If files moved (are no longer pinned), the metadata file will be invalid.
+ // This check can be removed once b/133967059 is fixed.
+ if (!images_->Validate()) {
+ return IGsiService::INSTALL_ERROR_GENERIC;
+ }
+
+ succeeded_ = true;
+ return IGsiService::INSTALL_OK;
+}
+
+int PartitionInstaller::WipeWritable(const std::string& active_dsu, const std::string& install_dir,
+ const std::string& name) {
+ auto image = ImageManager::Open(MetadataDir(active_dsu), install_dir);
+ // The device object has to be destroyed before the image object
+ auto device = MappedDevice::Open(image.get(), 10s, name);
+ if (!device) {
+ 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, get_block_device_size(device->fd()));
+ for (uint64_t i = 0; i < erase_size; i += zeroes.size()) {
+ if (!android::base::WriteFully(device->fd(), zeroes.data(), zeroes.size())) {
+ PLOG(ERROR) << "write userdata_gsi";
+ return IGsiService::INSTALL_ERROR_GENERIC;
+ }
+ }
+ return IGsiService::INSTALL_OK;
+}
+
+} // namespace gsi
+} // namespace android
diff --git a/gsi_installer.h b/partition_installer.h
similarity index 68%
rename from gsi_installer.h
rename to partition_installer.h
index 33bebfb..1503648 100644
--- a/gsi_installer.h
+++ b/partition_installer.h
@@ -32,16 +32,15 @@
class GsiService;
-class GsiInstaller final {
+class PartitionInstaller final {
using ImageManager = android::fiemap::ImageManager;
using MappedDevice = android::fiemap::MappedDevice;
public:
// Constructor for a new GSI installation.
- GsiInstaller(GsiService* service, const GsiInstallParams& params);
- // Constructor for re-enabling a previous GSI installation.
- GsiInstaller(GsiService* service, const std::string& install_dir);
- ~GsiInstaller();
+ PartitionInstaller(GsiService* service, const std::string& installDir, const std::string& name,
+ const std::string& active_dsu, int64_t size, bool read_only);
+ ~PartitionInstaller();
// Methods for a clean GSI install.
int StartInstall();
@@ -49,30 +48,26 @@
bool CommitGsiChunk(const void* data, size_t bytes);
bool MapAshmem(int fd, size_t size);
bool CommitGsiChunk(size_t bytes);
- int SetGsiBootable(bool one_shot);
+ int GetPartitionFd();
- // Methods for interacting with an existing install.
- int ReenableGsi(bool one_shot);
- int WipeUserdata();
+ static int WipeWritable(const std::string& active_dsu, const std::string& install_dir,
+ const std::string& name);
// Clean up install state if gsid crashed and restarted.
- static void PostInstallCleanup();
- static void PostInstallCleanup(ImageManager* manager);
+ void PostInstallCleanup();
+ void PostInstallCleanup(ImageManager* manager);
const std::string& install_dir() const { return install_dir_; }
- uint64_t userdata_size() const { return userdata_size_; }
private:
+ int Finish();
int PerformSanityChecks();
- int PreallocateFiles();
- int PreallocateUserdata();
- int PreallocateSystem();
- bool FormatUserdata();
- bool CreateImage(const std::string& name, uint64_t size, bool readonly);
+ int Preallocate();
+ bool Format();
+ bool CreateImage(const std::string& name, uint64_t size);
std::unique_ptr<MappedDevice> OpenPartition(const std::string& name);
int CheckInstallState();
- bool CreateInstallStatusFile();
- bool SetBootMode(bool one_shot);
+ static const std::string GetBackingFile(std::string name);
bool IsFinishedWriting();
bool IsAshmemMapped();
void UnmapAshmem();
@@ -80,11 +75,11 @@
GsiService* service_;
std::string install_dir_;
+ std::string name_;
+ std::string active_dsu_;
std::unique_ptr<ImageManager> images_;
- uint64_t gsi_size_ = 0;
- uint64_t userdata_size_ = 0;
- bool wipe_userdata_ = false;
- bool wipe_userdata_on_failure_ = false;
+ uint64_t size_ = 0;
+ bool readOnly_;
// Remaining data we're waiting to receive for the GSI image.
uint64_t gsi_bytes_written_ = 0;
bool succeeded_ = false;
diff --git a/tests/Android.bp b/tests/Android.bp
index e162dfb..6718eb9 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -42,9 +42,13 @@
cc_test {
name: "vts_gsi_boot_test",
defaults: ["gsi_boot_defaults"],
- test_suites: ["vts-core"],
+ test_suites: [
+ "general-tests",
+ "vts-core",
+ ],
auto_gen_config: true,
test_min_api_level: 29,
+ require_root: true,
}
java_test_host {
diff --git a/tests/DSUEndtoEndTest.java b/tests/DSUEndtoEndTest.java
index 175ca54..69d40ad 100644
--- a/tests/DSUEndtoEndTest.java
+++ b/tests/DSUEndtoEndTest.java
@@ -34,6 +34,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.lang.Process;
@@ -51,6 +52,7 @@
private static final String UI_AUTOMATOR_INSTRUMENTATION_RUNNER =
"androidx.test.uiautomator.UiAutomatorInstrumentationTestRunner";
private static final String CLASS = "LockScreenAutomation";
+ private static final String LPUNPACK_PATH = "bin/lpunpack";
private static final String SIMG2IMG_PATH = "bin/simg2img";
// Example: atest -v DSUEndtoEndTest -- --test-arg \
@@ -62,6 +64,12 @@
importance=Importance.ALWAYS)
private String mSystemImagePath;
+ @Option(name="userdata_size",
+ shortName='u',
+ description="size in bytes of the new userdata partition",
+ importance=Importance.ALWAYS)
+ private long mUserdataSize = kDefaultUserdataSize;
+
private File mUnsparseSystemImage;
@After
@@ -77,11 +85,32 @@
String simg2imgPath = "simg2img";
if (mSystemImagePath == null) {
IBuildInfo buildInfo = getBuild();
- File system = ((IDeviceBuildInfo) buildInfo).getDeviceImageFile();
- Assert.assertNotEquals("Failed to fetch system image. See system_image_path parameter", null, system);
- mSystemImagePath = ZipUtil2.extractFileFromZip(new ZipFile(system), "system.img").getAbsolutePath();
+ File imgs = ((IDeviceBuildInfo) buildInfo).getDeviceImageFile();
+ Assert.assertNotEquals("Failed to fetch system image. See system_image_path parameter", null, imgs);
File otaTools = buildInfo.getFile("otatools.zip");
File tempdir = ZipUtil2.extractZipToTemp(otaTools, "otatools");
+ File system = ZipUtil2.extractFileFromZip(new ZipFile(imgs), "system.img");
+ if (system == null) {
+ File superImg = ZipUtil2.extractFileFromZip(new ZipFile(imgs), "super.img");
+ String lpunpackPath = new File(tempdir, LPUNPACK_PATH).getAbsolutePath();
+ String outputDir = superImg.getParentFile().getAbsolutePath();
+ String[] cmd = {lpunpackPath, "-p", "system_a", superImg.getAbsolutePath(), outputDir};
+ Process p = Runtime.getRuntime().exec(cmd);
+ p.waitFor();
+ if (p.exitValue() == 0) {
+ mSystemImagePath = new File(outputDir, "system_a.img").getAbsolutePath();
+ } else {
+ ByteArrayOutputStream stderr = new ByteArrayOutputStream();
+ int len;
+ byte[] buf = new byte[1024];
+ while ((len = p.getErrorStream().read(buf)) != -1) {
+ stderr.write(buf, 0, len);
+ }
+ Assert.assertEquals("non-zero exit value (" + stderr.toString("UTF-8") + ")", 0, p.exitValue());
+ }
+ } else {
+ mSystemImagePath = system.getAbsolutePath();
+ }
simg2imgPath = new File(tempdir, SIMG2IMG_PATH).getAbsolutePath();
}
File gsi = new File(mSystemImagePath);
@@ -109,7 +138,7 @@
// Sleep after installing to allow time for gsi_tool to reboot. This prevents a race between
// the device rebooting and waitForDeviceAvailable() returning.
- getDevice().executeShellV2Command("gsi_tool install --userdata-size " + kDefaultUserdataSize +
+ getDevice().executeShellV2Command("gsi_tool install --userdata-size " + mUserdataSize +
" --gsi-size " + gsi.length() + " && sleep 10000000", gsi, null, 10, TimeUnit.MINUTES, 1);
getDevice().waitForDeviceAvailable();
getDevice().enableAdbRoot();