summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2020-04-28 20:27:22 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2020-04-28 20:27:22 +0000
commit1fc176f3f4cf79967329e0c8731a63b459561e44 (patch)
tree54b0313105d531de45e5ed94a70317b063097662
parent1a0326594859a8f5624408c63584ab377889261b (diff)
parent4d267077aa2a169b410185b1a5f5a78212fb4745 (diff)
downloadplatform_system_gsid-android10-mainline-tzdata-release.tar.gz
platform_system_gsid-android10-mainline-tzdata-release.tar.bz2
platform_system_gsid-android10-mainline-tzdata-release.zip
Snap for 6439596 from 4d267077aa2a169b410185b1a5f5a78212fb4745 to qt-aml-tzdata-releaseandroid-mainline-10.0.0_r11android10-mainline-tzdata-release
Change-Id: Ic2a201d3658a3e99884397a228862bcefaa40419
-rw-r--r--.clang-format1
-rw-r--r--Android.bp78
-rw-r--r--Android.mk22
-rw-r--r--PREUPLOAD.cfg5
-rw-r--r--aidl/android/gsi/GsiInstallParams.aidl19
-rw-r--r--aidl/android/gsi/IGsiService.aidl89
-rw-r--r--aidl/android/gsi/IGsid.aidl30
-rw-r--r--aidl/android/gsi/IImageService.aidl101
-rw-r--r--aidl/android/gsi/MappedImage.aidl23
-rw-r--r--daemon.cpp38
-rw-r--r--file_paths.h11
-rw-r--r--gsi_installer.cpp403
-rw-r--r--gsi_installer.h95
-rw-r--r--gsi_service.cpp1164
-rw-r--r--gsi_service.h146
-rw-r--r--gsi_tool.cpp125
-rw-r--r--gsid.rc5
-rw-r--r--include/libgsi/libgsi.h11
-rw-r--r--include/libgsi/libgsid.h27
l---------libfiemap/.clang-format1
-rw-r--r--libfiemap/Android.bp135
-rw-r--r--libfiemap/AndroidTest.xml30
-rw-r--r--libfiemap/README.md75
-rw-r--r--libfiemap/binder.cpp213
-rw-r--r--libfiemap/fiemap_writer.cpp745
-rw-r--r--libfiemap/fiemap_writer_test.cpp541
-rw-r--r--libfiemap/image_manager.cpp649
-rw-r--r--libfiemap/image_test.cpp251
-rw-r--r--libfiemap/include/libfiemap/fiemap_writer.h118
-rw-r--r--libfiemap/include/libfiemap/image_manager.h184
-rw-r--r--libfiemap/include/libfiemap/split_fiemap_writer.h97
-rw-r--r--libfiemap/metadata.cpp196
-rw-r--r--libfiemap/metadata.h36
-rw-r--r--libfiemap/passthrough.cpp29
-rw-r--r--libfiemap/split_fiemap_writer.cpp303
-rw-r--r--libfiemap/testdata/file_32kbin32768 -> 0 bytes
-rw-r--r--libfiemap/testdata/file_4kbin4096 -> 0 bytes
-rw-r--r--libfiemap/testdata/unaligned_filebin4097 -> 0 bytes
-rw-r--r--libfiemap/utility.cpp93
-rw-r--r--libfiemap/utility.h41
-rw-r--r--libgsi.cpp33
-rw-r--r--libgsid.cpp71
-rw-r--r--tests/Android.bp66
-rw-r--r--tests/AndroidManifest.xml30
-rw-r--r--tests/DSUEndtoEndTest.java168
-rw-r--r--tests/LockScreenAutomation.java151
-rw-r--r--tests/dsu-test.xml22
47 files changed, 1154 insertions, 5517 deletions
diff --git a/.clang-format b/.clang-format
index d72ca25..55773a2 100644
--- a/.clang-format
+++ b/.clang-format
@@ -9,4 +9,3 @@ ContinuationIndentWidth: 8
PointerAlignment: Left
TabWidth: 4
UseTab: Never
-IncludeBlocks: Preserve
diff --git a/Android.bp b/Android.bp
index ff1c488..e5cedd8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -18,16 +18,14 @@ cc_binary {
name: "gsi_tool",
shared_libs: [
"gsi_aidl_interface-cpp",
- "libbase",
"libbinder",
+ "libbase",
"libcutils",
"libgsi",
"liblog",
+ "libservices",
"libutils",
],
- static_libs: [
- "libgsid",
- ],
srcs: [
"gsi_tool.cpp",
],
@@ -46,26 +44,6 @@ cc_library {
export_include_dirs: ["include"],
}
-cc_library_static {
- name: "libgsid",
- srcs: [
- "libgsid.cpp",
- ],
- shared_libs: [
- "gsi_aidl_interface-cpp",
- "libbase",
- "libbinder",
- "libcutils",
- "liblog",
- "libservices",
- "libutils",
- ],
- static_libs: [
- "libgsi",
- ],
- export_include_dirs: ["include"],
-}
-
cc_library_headers {
name: "libgsi_headers",
host_supported: true,
@@ -79,7 +57,6 @@ cc_binary {
srcs: [
"daemon.cpp",
"gsi_service.cpp",
- "gsi_installer.cpp",
],
required: [
"mke2fs",
@@ -88,29 +65,52 @@ cc_binary {
"gsid.rc",
],
shared_libs: [
+ "gsi_aidl_interface-cpp",
"libbase",
"libbinder",
- "libcrypto",
- "liblog",
- ],
- static_libs: [
- "gsi_aidl_interface-cpp",
- "libcutils",
- "libdm",
"libext4_utils",
- "libfiemap_passthrough",
"libfs_mgr",
"libgsi",
- "libgsid",
+ "liblog",
"liblp",
"libutils",
],
+ static_libs: [
+ "libdm",
+ "libfiemap_writer",
+ ],
local_include_dirs: ["include"],
}
+cc_test {
+ name: "gsi_boot_test",
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libhardware",
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "liblog",
+ "libutils",
+ ],
+ static_libs: [
+ "libext4_utils",
+ "libfstab",
+ "android.hardware.weaver@1.0",
+ ],
+ srcs: [
+ "tests/boot_tests.cpp",
+ ],
+}
+
aidl_interface {
name: "gsi_aidl_interface",
- srcs: [":gsiservice_aidl"],
+ srcs: [
+ "aidl/android/gsi/GsiInstallParams.aidl",
+ "aidl/android/gsi/GsiProgress.aidl",
+ "aidl/android/gsi/IGsiService.aidl",
+ ],
local_include_dir: "aidl",
backend: {
ndk: {
@@ -124,14 +124,6 @@ filegroup {
srcs: [
"aidl/android/gsi/GsiInstallParams.aidl",
"aidl/android/gsi/GsiProgress.aidl",
- "aidl/android/gsi/IImageService.aidl",
- "aidl/android/gsi/IGsid.aidl",
"aidl/android/gsi/IGsiService.aidl",
- "aidl/android/gsi/MappedImage.aidl",
],
- path: "aidl",
-}
-
-vts_config {
- name: "VtsGsiBootTest",
}
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..21d0b45
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,22 @@
+#
+# 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/PREUPLOAD.cfg b/PREUPLOAD.cfg
deleted file mode 100644
index c8dbf77..0000000
--- a/PREUPLOAD.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-[Builtin Hooks]
-clang_format = true
-
-[Builtin Hooks Options]
-clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
diff --git a/aidl/android/gsi/GsiInstallParams.aidl b/aidl/android/gsi/GsiInstallParams.aidl
index a9307c7..6e46da4 100644
--- a/aidl/android/gsi/GsiInstallParams.aidl
+++ b/aidl/android/gsi/GsiInstallParams.aidl
@@ -25,24 +25,17 @@ parcelable GsiInstallParams {
*/
@utf8InCpp String installDir;
- /**
- * The DSU partition name
- */
- @utf8InCpp String name;
+ /* The size of the on-disk GSI image. */
+ long gsiSize;
- /* The size of the DSU image. */
- long size;
+ /* The desired size of the userdata partition. */
+ long userdataSize;
- /* If false, an image is only created if one does not already
+ /* 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 wipe;
-
- /**
- * Is this partition read only?
- */
- boolean readOnly;
+ boolean wipeUserdata;
}
diff --git a/aidl/android/gsi/IGsiService.aidl b/aidl/android/gsi/IGsiService.aidl
index c480ef4..4ffdf62 100644
--- a/aidl/android/gsi/IGsiService.aidl
+++ b/aidl/android/gsi/IGsiService.aidl
@@ -18,7 +18,6 @@ package android.gsi;
import android.gsi.GsiInstallParams;
import android.gsi.GsiProgress;
-import android.gsi.IImageService;
import android.os.ParcelFileDescriptor;
/** {@hide} */
@@ -41,6 +40,25 @@ interface IGsiService {
const int INSTALL_ERROR_FILE_SYSTEM_CLUTTERED = 3;
/**
+ * Starts a GSI installation. Use beginGsiInstall() to target external
+ * media.
+ *
+ * If wipeUserData is true, a clean userdata image is always created to the
+ * desired size.
+ *
+ * If wipeUserData is 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 is
+ * resized automatically.
+ *
+ * @param gsiSize The size of the on-disk GSI image.
+ * @param userdataSize The desired size of the userdata partition.
+ * @param wipeUserdata True to wipe destination userdata.
+ * @return 0 on success, an error code on failure.
+ */
+ int startGsiInstall(long gsiSize, long userdataSize, boolean wipeUserdata);
+
+ /**
* Write bytes from a stream to the on-disk GSI.
*
* @param stream Stream descriptor.
@@ -56,21 +74,12 @@ interface IGsiService {
GsiProgress getInstallProgress();
/**
- * Set the file descriptor that points to a ashmem which will be used
- * to fetch data during the commitGsiChunkFromAshmem.
- *
- * @param stream fd that points to a ashmem
- * @param size size of the ashmem file
- */
- boolean setGsiAshmem(in ParcelFileDescriptor stream, long size);
-
- /**
- * Write bytes from ashmem previously set with setGsiAshmem to GSI partition
+ * Write bytes from memory to the on-disk GSI.
*
- * @param bytes Number of bytes to submit
+ * @param bytes Byte array.
* @return true on success, false otherwise.
*/
- boolean commitGsiChunkFromAshmem(long bytes);
+ boolean commitGsiChunkFromMemory(in byte[] bytes);
/**
* Complete a GSI installation and mark it as bootable. The caller is
@@ -80,7 +89,7 @@ interface IGsiService {
* It can still be re-enabled again later with setGsiBootable.
* @return INSTALL_* error code.
*/
- int enableGsi(boolean oneShot);
+ int setGsiBootable(boolean oneShot);
/**
* @return True if Gsi is enabled
@@ -104,28 +113,55 @@ interface IGsiService {
*
* @return true on success, false otherwise.
*/
- boolean removeGsi();
+ boolean removeGsiInstall();
/**
* Disables a GSI install. The image and userdata will be retained, but can
* be re-enabled at any time with setGsiBootable.
*/
- boolean disableGsi();
+ boolean disableGsiInstall();
/**
- * Returns true if a gsi is installed.
+ * Return the size of the userdata partition for an installed GSI. If there
+ * is no image, 0 is returned. On error, -1 is returned.
*/
- boolean isGsiInstalled();
+ long getUserdataImageSize();
+
/**
* Returns true if the gsi is currently running, false otherwise.
*/
boolean isGsiRunning();
/**
+ * Returns true if a gsi is installed.
+ */
+ boolean isGsiInstalled();
+
+ /* No GSI is installed. */
+ const int BOOT_STATUS_NOT_INSTALLED = 0;
+ /* GSI is installed, but booting is disabled. */
+ const int BOOT_STATUS_DISABLED = 1;
+ /* GSI is installed, but will only boot once. */
+ const int BOOT_STATUS_SINGLE_BOOT = 2;
+ /* GSI is installed and bootable. */
+ const int BOOT_STATUS_ENABLED = 3;
+ /* GSI will be wiped next boot. */
+ const int BOOT_STATUS_WILL_WIPE = 4;
+
+ /**
+ * Returns the boot status of a GSI. See the BOOT_STATUS constants in IGsiService.
+ *
+ * GSI_STATE_NOT_INSTALLED will be returned if no GSI installation has been
+ * fully completed. Any other value indicates a GSI is installed. If a GSI
+ * currently running, DISABLED or SINGLE_BOOT can still be returned.
+ */
+ int getGsiBootStatus();
+
+ /**
* 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.
@@ -145,19 +181,4 @@ interface IGsiService {
* @return 0 on success, an error code on failure.
*/
int wipeGsiUserdata();
-
- /**
- * Open a handle to an IImageService for the given metadata and data storage paths.
- *
- * @param prefix A prefix used to organize images. The data path will become
- * /data/gsi/{prefix} and the metadata path will become
- * /metadata/gsi/{prefix}.
- */
- IImageService openImageService(@utf8InCpp String prefix);
-
- /**
- * Dump diagnostic information about device-mapper devices. This is intended
- * for dumpstate.
- */
- @utf8InCpp String dumpDeviceMapperDevices();
}
diff --git a/aidl/android/gsi/IGsid.aidl b/aidl/android/gsi/IGsid.aidl
deleted file mode 100644
index 0c1a7b2..0000000
--- a/aidl/android/gsi/IGsid.aidl
+++ /dev/null
@@ -1,30 +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;
-
-import android.gsi.IGsiService;
-
-/** {@hide} */
-interface IGsid {
- // Acquire an IGsiService client. gsid automatically shuts down when the
- // last client is dropped. To start the daemon:
- //
- // 1. Check if the "init.svc.gsid" property is "running". If not, continue.
- // 2. Set the "ctl.start" property to "gsid".
- // 3. Wait for "init.svc.gsid" to be "running".
- IGsiService getClient();
-}
diff --git a/aidl/android/gsi/IImageService.aidl b/aidl/android/gsi/IImageService.aidl
deleted file mode 100644
index 1195c00..0000000
--- a/aidl/android/gsi/IImageService.aidl
+++ /dev/null
@@ -1,101 +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;
-
-import android.gsi.MappedImage;
-
-/** {@hide} */
-interface IImageService {
- /* These flags match fiemap::ImageManager::CreateBackingImage. */
- const int CREATE_IMAGE_DEFAULT = 0x0;
- const int CREATE_IMAGE_READONLY = 0x1;
- const int CREATE_IMAGE_ZERO_FILL = 0x2;
-
- /**
- * Create an image that can be mapped as a block device.
- *
- * This call will fail if running a GSI.
- *
- * @param name Image name. If the image already exists, the call will fail.
- * @param size Image size, in bytes. If too large, or not enough space is
- * free, the call will fail.
- * @param readonly If readonly, MapBackingImage() will configure the device as
- * readonly.
- * @return True on success, false otherwise.
- */
- void createBackingImage(@utf8InCpp String name, long size, int flags);
-
- /**
- * Delete an image created with createBackingImage.
- *
- * @param name Image name as passed to createBackingImage().
- * @return True on success, false otherwise.
- */
- void deleteBackingImage(@utf8InCpp String name);
-
- /**
- * Map an image, created with createBackingImage, such that it is accessible as a
- * block device.
- *
- * @param name Image name as passed to createBackingImage().
- * @param timeout_ms Time to wait for a valid mapping, in milliseconds. This must be more
- * than zero; 10 seconds is recommended.
- * @param mapping Information about the newly mapped block device.
- */
- void mapImageDevice(@utf8InCpp String name, int timeout_ms, out MappedImage mapping);
-
- /**
- * Unmap a block device previously mapped with mapBackingImage. This step is necessary before
- * calling deleteBackingImage.
- *
- * @param name Image name as passed to createBackingImage().
- */
- void unmapImageDevice(@utf8InCpp String name);
-
- /**
- * Returns whether or not a backing image exists.
- *
- * @param name Image name as passed to createBackingImage().
- */
- boolean backingImageExists(@utf8InCpp String name);
-
- /**
- * Returns whether or not the named image is mapped.
- *
- * @param name Image name as passed to createBackingImage().
- */
- boolean isImageMapped(@utf8InCpp String name);
-
- /**
- * Get all installed backing image names
- *
- * @return list of installed backing image names
- */
- @utf8InCpp List<String> getAllBackingImages();
-
- /**
- * Writes a given amount of zeros in image file.
- *
- * @param name Image name. If the image does not exist, the call
- * will fail.
- * @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.
- */
- void zeroFillNewImage(@utf8InCpp String name, long bytes);
-}
diff --git a/aidl/android/gsi/MappedImage.aidl b/aidl/android/gsi/MappedImage.aidl
deleted file mode 100644
index aa5af0d..0000000
--- a/aidl/android/gsi/MappedImage.aidl
+++ /dev/null
@@ -1,23 +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 MappedImage {
- /* Path to the block device. */
- @utf8InCpp String path;
-}
diff --git a/daemon.cpp b/daemon.cpp
index 782d2ed..0ea1cb5 100644
--- a/daemon.cpp
+++ b/daemon.cpp
@@ -14,55 +14,29 @@
// limitations under the License.
//
+#include "gsi_service.h"
+
#include <getopt.h>
-#include <iostream>
#include <string>
#include <android-base/logging.h>
-#include <binder/BinderService.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
-#include <libgsi/libgsi.h>
-#include <libgsi/libgsid.h>
-
-#include "gsi_service.h"
using android::ProcessState;
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) {
- 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);
- }
+ if (argc > 1 && argv[1] == "run-startup-tasks"s) {
+ android::gsi::GsiService::RunStartupTasks();
+ exit(0);
}
- android::gsi::Gsid::Register();
+ android::gsi::GsiService::Register();
{
sp<ProcessState> ps(ProcessState::self());
ps->startThreadPool();
diff --git a/file_paths.h b/file_paths.h
index b596704..3335195 100644
--- a/file_paths.h
+++ b/file_paths.h
@@ -19,19 +19,18 @@
namespace android {
namespace gsi {
-static constexpr char kDefaultDsuImageFolder[] = "/data/gsi/dsu/";
-static constexpr char kUserdataDevice[] = "/dev/block/by-name/userdata";
+static constexpr char kDefaultGsiImageFolder[] = "/data/gsi/dsu/";
-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 constexpr char kGsiLpMetadataFile[] = "/metadata/gsi/dsu/lp_metadata";
+static constexpr char kGsiOneShotBootFile[] = "/metadata/gsi/dsu/one_shot_boot";
+static constexpr char kGsiInstallDirFile[] = "/metadata/gsi/dsu/install_dir";
// 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 kGsiInstallStatusFile[] = "/metadata/gsi/dsu/install_status";
} // namespace gsi
} // namespace android
diff --git a/gsi_installer.cpp b/gsi_installer.cpp
deleted file mode 100644
index fe6b837..0000000
--- a/gsi_installer.cpp
+++ /dev/null
@@ -1,403 +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),
- name_(params.name),
- size_(params.size),
- readOnly_(params.readOnly),
- wipe_(params.wipe) {
- size_ = (params.size) ? params.size : kDefaultUserdataSize;
- images_ = ImageManager::Open(kDsuMetadataDir, install_dir_);
-
- // Only rm backing file if one didn't already exist.
- if (wipe_ || !images_->BackingImageExists(GetBackingFile(name_))) {
- wipe_on_failure_ = true;
- }
-
- // Remember the installation directory before allocate any resource
- if (!android::base::WriteStringToFile(install_dir_, kDsuInstallDirFile)) {
- PLOG(ERROR) << "write failed: " << kDsuInstallDirFile;
- }
-}
-
-GsiInstaller::~GsiInstaller() {
- if (!succeeded_) {
- // Close open handles before we remove files.
- system_device_ = nullptr;
- PostInstallCleanup(images_.get());
-
- GsiService::RemoveGsiFiles(install_dir_, wipe_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) {
- std::string file = GetBackingFile(name_);
- if (manager->IsImageMapped(file)) {
- LOG(ERROR) << "unmap " << file;
- manager->UnmapImageDevice(file);
- }
-}
-
-int GsiInstaller::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 GsiInstaller::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 GsiInstaller::Preallocate() {
- std::string file = GetBackingFile(name_);
- if (wipe_) {
- images_->DeleteBackingImage(file);
- }
- LOG(ERROR) << "is exists:" << name_ << " " << images_->BackingImageExists(file);
- if (!images_->BackingImageExists(file)) {
- 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 GsiInstaller::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> 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 " + 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 GsiInstaller::IsFinishedWriting() {
- return gsi_bytes_written_ == size_;
-}
-
-bool GsiInstaller::IsAshmemMapped() {
- return ashmem_data_ != MAP_FAILED;
-}
-
-bool GsiInstaller::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;
-}
-
-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;
-}
-
-const std::string GsiInstaller::GetBackingFile(std::string name) {
- return name + "_gsi";
-}
-
-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::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 GsiInstaller::SetGsiBootable(bool one_shot) {
- if (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 (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;
- }
-
- // 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::ReenableGsi(bool 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;
-}
-
-int GsiInstaller::WipeWritable(const std::string& install_dir, const std::string& name) {
- auto image = ImageManager::Open(kDsuMetadataDir, 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/gsi_installer.h
deleted file mode 100644
index 33f1937..0000000
--- a/gsi_installer.h
+++ /dev/null
@@ -1,95 +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 <sys/mman.h>
-
-#include <memory>
-#include <string>
-
-#include <android-base/unique_fd.h>
-#include <android/gsi/IGsiService.h>
-#include <android/gsi/MappedImage.h>
-#include <libfiemap/image_manager.h>
-#include <liblp/builder.h>
-
-namespace android {
-namespace gsi {
-
-class GsiService;
-
-class GsiInstaller final {
- using ImageManager = android::fiemap::ImageManager;
- using MappedDevice = android::fiemap::MappedDevice;
-
- public:
- // Constructor for a new GSI installation.
- GsiInstaller(GsiService* service, const GsiInstallParams& params);
- ~GsiInstaller();
-
- // Methods for a clean GSI install.
- int StartInstall();
- bool CommitGsiChunk(int stream_fd, int64_t bytes);
- 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);
-
- // Methods for interacting with an existing install.
- static int ReenableGsi(bool one_shot);
- static int WipeWritable(const std::string& install_dir, const std::string& name);
-
- // Clean up install state if gsid crashed and restarted.
- void PostInstallCleanup();
- void PostInstallCleanup(ImageManager* manager);
-
- const std::string& install_dir() const { return install_dir_; }
-
- private:
- int PerformSanityChecks();
- int Preallocate();
- bool Format();
- bool CreateImage(const std::string& name, uint64_t size);
- std::unique_ptr<MappedDevice> OpenPartition(const std::string& name);
- int CheckInstallState();
- static bool CreateInstallStatusFile();
- static bool SetBootMode(bool one_shot);
- static const std::string GetBackingFile(std::string name);
- bool IsFinishedWriting();
- bool IsAshmemMapped();
- void UnmapAshmem();
-
- GsiService* service_;
-
- std::string install_dir_;
- std::string name_;
- std::unique_ptr<ImageManager> images_;
- uint64_t size_ = 0;
- bool readOnly_;
- bool wipe_ = false;
- bool wipe_on_failure_ = false;
- // Remaining data we're waiting to receive for the GSI image.
- uint64_t gsi_bytes_written_ = 0;
- bool succeeded_ = false;
- uint64_t ashmem_size_ = -1;
- void* ashmem_data_ = MAP_FAILED;
-
- std::unique_ptr<MappedDevice> system_device_;
-};
-
-} // namespace gsi
-} // namespace android
diff --git a/gsi_service.cpp b/gsi_service.cpp
index dbf0ea6..3efec40 100644
--- a/gsi_service.cpp
+++ b/gsi_service.cpp
@@ -20,6 +20,7 @@
#include <linux/fs.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
+#include <sys/statvfs.h>
#include <sys/types.h>
#include <sys/vfs.h>
#include <unistd.h>
@@ -32,12 +33,14 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <android/gsi/BnImageService.h>
#include <android/gsi/IGsiService.h>
#include <ext4_utils/ext4_utils.h>
#include <fs_mgr.h>
+#include <fs_mgr_dm_linear.h>
+#include <fstab/fstab.h>
#include <libdm/dm.h>
-#include <libfiemap/image_manager.h>
+#include <libfiemap_writer/fiemap_writer.h>
+#include <logwrap/logwrap.h>
#include <private/android_filesystem_config.h>
#include "file_paths.h"
@@ -47,69 +50,66 @@ namespace android {
namespace gsi {
using namespace std::literals;
+using namespace android::dm;
using namespace android::fs_mgr;
-using namespace android::fiemap;
+using namespace android::fiemap_writer;
using android::base::StringPrintf;
using android::base::unique_fd;
-using android::dm::DeviceMapper;
-android::wp<GsiService> GsiService::sInstance;
+static constexpr char kUserdataDevice[] = "/dev/block/by-name/userdata";
-void Gsid::Register() {
- auto ret = android::BinderService<Gsid>::publish();
+// The default size of userdata.img for GSI.
+// We are looking for /data to have atleast 40% free space
+static constexpr uint32_t kMinimumFreeSpaceThreshold = 40;
+// We determine the fragmentation by making sure the files
+// we create don't have more than 16 extents.
+static constexpr uint32_t kMaximumExtents = 512;
+// Default userdata image size.
+static constexpr int64_t kDefaultUserdataSize = int64_t(8) * 1024 * 1024 * 1024;
+static constexpr std::chrono::milliseconds kDmTimeout = 5000ms;
+
+void GsiService::Register() {
+ auto ret = android::BinderService<GsiService>::publish();
if (ret != android::OK) {
LOG(FATAL) << "Could not register gsi service: " << ret;
}
}
-binder::Status Gsid::getClient(android::sp<IGsiService>* _aidl_return) {
- *_aidl_return = GsiService::Get(this);
- return binder::Status::ok();
-}
-
-GsiService::GsiService(Gsid* parent) : parent_(parent) {
+GsiService::GsiService() {
progress_ = {};
}
GsiService::~GsiService() {
- std::lock_guard<std::mutex> guard(parent_->lock());
-
- if (sInstance == this) {
- // No more consumers, gracefully shut down gsid.
- exit(0);
- }
+ PostInstallCleanup();
}
-android::sp<IGsiService> GsiService::Get(Gsid* parent) {
- std::lock_guard<std::mutex> guard(parent->lock());
-
- android::sp<GsiService> service = sInstance.promote();
- if (!service) {
- service = new GsiService(parent);
- sInstance = service.get();
- }
- return service.get();
-}
-
-#define ENFORCE_SYSTEM \
- do { \
- binder::Status status = CheckUid(); \
- if (!status.isOk()) return status; \
+#define ENFORCE_SYSTEM \
+ do { \
+ binder::Status status = CheckUid(); \
+ if (!status.isOk()) return status; \
} while (0)
-#define ENFORCE_SYSTEM_OR_SHELL \
- do { \
- binder::Status status = CheckUid(AccessLevel::SystemOrShell); \
- if (!status.isOk()) return status; \
+#define ENFORCE_SYSTEM_OR_SHELL \
+ do { \
+ binder::Status status = CheckUid(AccessLevel::SystemOrShell); \
+ if (!status.isOk()) return status; \
} while (0)
-binder::Status GsiService::beginGsiInstall(const GsiInstallParams& given_params,
+binder::Status GsiService::startGsiInstall(int64_t gsiSize, int64_t userdataSize, bool wipeUserdata,
int* _aidl_return) {
+ GsiInstallParams params;
+ params.gsiSize = gsiSize;
+ params.userdataSize = userdataSize;
+ params.wipeUserdata = wipeUserdata;
+ return beginGsiInstall(params, _aidl_return);
+}
+
+binder::Status GsiService::beginGsiInstall(const GsiInstallParams& given_params, int* _aidl_return) {
ENFORCE_SYSTEM;
- std::lock_guard<std::mutex> guard(parent_->lock());
+ std::lock_guard<std::mutex> guard(main_lock_);
// Make sure any interrupted installations are cleaned up.
- installer_ = nullptr;
+ PostInstallCleanup();
// Do some precursor validation on the arguments before diving into the
// install process.
@@ -119,26 +119,28 @@ binder::Status GsiService::beginGsiInstall(const GsiInstallParams& given_params,
return binder::Status::ok();
}
- installer_ = std::make_unique<GsiInstaller>(this, params);
- int status = installer_->StartInstall();
+ int status = StartInstall(params);
if (status != INSTALL_OK) {
- installer_ = nullptr;
+ // Perform local cleanup and delete any lingering files.
+ PostInstallCleanup();
+ RemoveGsiFiles(params.installDir, wipe_userdata_on_failure_);
}
*_aidl_return = status;
+
+ // Clear the progress indicator.
+ UpdateProgress(STATUS_NO_OPERATION, 0);
return binder::Status::ok();
}
binder::Status GsiService::commitGsiChunkFromStream(const android::os::ParcelFileDescriptor& stream,
int64_t bytes, bool* _aidl_return) {
ENFORCE_SYSTEM;
- std::lock_guard<std::mutex> guard(parent_->lock());
+ std::lock_guard<std::mutex> guard(main_lock_);
- if (!installer_) {
- *_aidl_return = false;
- return binder::Status::ok();
- }
+ *_aidl_return = CommitGsiChunk(stream.get(), bytes);
- *_aidl_return = installer_->CommitGsiChunk(stream.get(), bytes);
+ // Clear the progress indicator.
+ UpdateProgress(STATUS_NO_OPERATION, 0);
return binder::Status::ok();
}
@@ -170,34 +172,24 @@ binder::Status GsiService::getInstallProgress(::android::gsi::GsiProgress* _aidl
return binder::Status::ok();
}
-binder::Status GsiService::commitGsiChunkFromAshmem(int64_t bytes, bool* _aidl_return) {
+binder::Status GsiService::commitGsiChunkFromMemory(const std::vector<uint8_t>& bytes,
+ bool* _aidl_return) {
ENFORCE_SYSTEM;
- std::lock_guard<std::mutex> guard(parent_->lock());
-
- if (!installer_) {
- *_aidl_return = false;
- return binder::Status::ok();
- }
- *_aidl_return = installer_->CommitGsiChunk(bytes);
- return binder::Status::ok();
-}
+ std::lock_guard<std::mutex> guard(main_lock_);
-binder::Status GsiService::setGsiAshmem(const ::android::os::ParcelFileDescriptor& ashmem,
- int64_t size, bool* _aidl_return) {
- if (!installer_) {
- *_aidl_return = false;
- return binder::Status::ok();
- }
- *_aidl_return = installer_->MapAshmem(ashmem.get(), size);
+ *_aidl_return = CommitGsiChunk(bytes.data(), bytes.size());
return binder::Status::ok();
}
-binder::Status GsiService::enableGsi(bool one_shot, int* _aidl_return) {
- std::lock_guard<std::mutex> guard(parent_->lock());
+binder::Status GsiService::setGsiBootable(bool one_shot, int* _aidl_return) {
+ std::lock_guard<std::mutex> guard(main_lock_);
- if (installer_) {
+ if (installing_) {
ENFORCE_SYSTEM;
- if (int error = installer_->SetGsiBootable(one_shot)) {
+ int error = SetGsiBootable(one_shot);
+ PostInstallCleanup();
+ if (error) {
+ RemoveGsiFiles(install_dir_, wipe_userdata_on_failure_);
*_aidl_return = error;
} else {
*_aidl_return = INSTALL_OK;
@@ -205,15 +197,15 @@ binder::Status GsiService::enableGsi(bool one_shot, int* _aidl_return) {
} else {
ENFORCE_SYSTEM_OR_SHELL;
*_aidl_return = ReenableGsi(one_shot);
+ PostInstallCleanup();
}
- installer_ = nullptr;
return binder::Status::ok();
}
binder::Status GsiService::isGsiEnabled(bool* _aidl_return) {
ENFORCE_SYSTEM_OR_SHELL;
- std::lock_guard<std::mutex> guard(parent_->lock());
+ std::lock_guard<std::mutex> guard(main_lock_);
std::string boot_key;
if (!GetInstallStatus(&boot_key)) {
*_aidl_return = false;
@@ -223,11 +215,19 @@ binder::Status GsiService::isGsiEnabled(bool* _aidl_return) {
return binder::Status::ok();
}
-binder::Status GsiService::removeGsi(bool* _aidl_return) {
+binder::Status GsiService::removeGsiInstall(bool* _aidl_return) {
ENFORCE_SYSTEM_OR_SHELL;
- std::lock_guard<std::mutex> guard(parent_->lock());
+ std::lock_guard<std::mutex> guard(main_lock_);
+
+ // Just in case an install was left hanging.
+ std::string install_dir;
+ if (installing_) {
+ install_dir = install_dir_;
+ PostInstallCleanup();
+ } else {
+ install_dir = GetInstalledImageDir();
+ }
- std::string install_dir = GetActiveInstalledImageDir();
if (IsGsiRunning()) {
// Can't remove gsi files while running.
*_aidl_return = UninstallGsi();
@@ -237,9 +237,9 @@ binder::Status GsiService::removeGsi(bool* _aidl_return) {
return binder::Status::ok();
}
-binder::Status GsiService::disableGsi(bool* _aidl_return) {
+binder::Status GsiService::disableGsiInstall(bool* _aidl_return) {
ENFORCE_SYSTEM_OR_SHELL;
- std::lock_guard<std::mutex> guard(parent_->lock());
+ std::lock_guard<std::mutex> guard(main_lock_);
*_aidl_return = DisableGsiInstall();
return binder::Status::ok();
@@ -247,7 +247,7 @@ binder::Status GsiService::disableGsi(bool* _aidl_return) {
binder::Status GsiService::isGsiRunning(bool* _aidl_return) {
ENFORCE_SYSTEM_OR_SHELL;
- std::lock_guard<std::mutex> guard(parent_->lock());
+ std::lock_guard<std::mutex> guard(main_lock_);
*_aidl_return = IsGsiRunning();
return binder::Status::ok();
@@ -255,7 +255,7 @@ binder::Status GsiService::isGsiRunning(bool* _aidl_return) {
binder::Status GsiService::isGsiInstalled(bool* _aidl_return) {
ENFORCE_SYSTEM_OR_SHELL;
- std::lock_guard<std::mutex> guard(parent_->lock());
+ std::lock_guard<std::mutex> guard(main_lock_);
*_aidl_return = IsGsiInstalled();
return binder::Status::ok();
@@ -263,234 +263,127 @@ binder::Status GsiService::isGsiInstalled(bool* _aidl_return) {
binder::Status GsiService::isGsiInstallInProgress(bool* _aidl_return) {
ENFORCE_SYSTEM_OR_SHELL;
- std::lock_guard<std::mutex> guard(parent_->lock());
+ std::lock_guard<std::mutex> guard(main_lock_);
- *_aidl_return = !!installer_;
+ *_aidl_return = installing_;
return binder::Status::ok();
}
binder::Status GsiService::cancelGsiInstall(bool* _aidl_return) {
ENFORCE_SYSTEM;
should_abort_ = true;
- std::lock_guard<std::mutex> guard(parent_->lock());
+ std::lock_guard<std::mutex> guard(main_lock_);
should_abort_ = false;
- installer_ = nullptr;
+ if (installing_) {
+ PostInstallCleanup();
+ RemoveGsiFiles(install_dir_, wipe_userdata_on_failure_);
+ }
*_aidl_return = true;
return binder::Status::ok();
}
-binder::Status GsiService::getInstalledGsiImageDir(std::string* _aidl_return) {
- ENFORCE_SYSTEM;
- std::lock_guard<std::mutex> guard(parent_->lock());
-
- *_aidl_return = GetActiveInstalledImageDir();
- return binder::Status::ok();
-}
-
-binder::Status GsiService::wipeGsiUserdata(int* _aidl_return) {
+binder::Status GsiService::getGsiBootStatus(int* _aidl_return) {
ENFORCE_SYSTEM_OR_SHELL;
- std::lock_guard<std::mutex> guard(parent_->lock());
+ std::lock_guard<std::mutex> guard(main_lock_);
- if (IsGsiRunning() || !IsGsiInstalled()) {
- *_aidl_return = IGsiService::INSTALL_ERROR_GENERIC;
+ if (!IsGsiInstalled()) {
+ *_aidl_return = BOOT_STATUS_NOT_INSTALLED;
return binder::Status::ok();
}
- std::string install_dir = GetActiveInstalledImageDir();
- *_aidl_return = GsiInstaller::WipeWritable(install_dir, "userdata");
-
- 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()));
-}
-
-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::string boot_key;
+ if (!GetInstallStatus(&boot_key)) {
+ PLOG(ERROR) << "read " << kGsiInstallStatusFile;
+ *_aidl_return = BOOT_STATUS_NOT_INSTALLED;
+ return binder::Status::ok();
}
- 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";
+ bool single_boot = !access(kGsiOneShotBootFile, F_OK);
+
+ if (boot_key == kInstallStatusWipe) {
+ // This overrides all other statuses.
+ *_aidl_return = BOOT_STATUS_WILL_WIPE;
+ } else if (boot_key == kInstallStatusDisabled) {
+ // A single-boot GSI will have a "disabled" status, because it's
+ // disabled immediately upon reading the one_shot_boot file. However,
+ // we still want to return SINGLE_BOOT, because it makes the
+ // transition clearer to the user.
+ if (single_boot) {
+ *_aidl_return = BOOT_STATUS_SINGLE_BOOT;
+ } else {
+ *_aidl_return = BOOT_STATUS_DISABLED;
}
- }
-
- *_aidl_return = text.str();
- return binder::Status::ok();
-}
-
-static binder::Status UidSecurityError() {
- uid_t uid = IPCThreadState::self()->getCallingUid();
- auto message = StringPrintf("UID %d is not allowed", uid);
- return binder::Status::fromExceptionCode(binder::Status::EX_SECURITY, String8(message.c_str()));
-}
-
-class ImageService : public BinderService<ImageService>, public BnImageService {
- 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 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 zeroFillNewImage(const std::string& name, int64_t bytes) override;
-
- private:
- bool CheckUid();
-
- android::sp<GsiService> service_;
- android::sp<Gsid> parent_;
- std::unique_ptr<ImageManager> impl_;
- uid_t uid_;
-};
-
-ImageService::ImageService(GsiService* service, std::unique_ptr<ImageManager>&& impl, uid_t uid)
- : service_(service), parent_(service->parent()), impl_(std::move(impl)), uid_(uid) {}
-
-binder::Status ImageService::getAllBackingImages(std::vector<std::string>* _aidl_return) {
- *_aidl_return = impl_->GetAllBackingImages();
- return binder::Status::ok();
-}
-
-binder::Status ImageService::createBackingImage(const std::string& name, int64_t size, int flags) {
- if (!CheckUid()) return UidSecurityError();
-
- std::lock_guard<std::mutex> guard(parent_->lock());
-
- if (!impl_->CreateBackingImage(name, size, flags, nullptr)) {
- return BinderError("Failed to create");
- }
- return binder::Status::ok();
-}
-
-binder::Status ImageService::deleteBackingImage(const std::string& name) {
- if (!CheckUid()) return UidSecurityError();
-
- std::lock_guard<std::mutex> guard(parent_->lock());
-
- if (!impl_->DeleteBackingImage(name)) {
- return BinderError("Failed to delete");
- }
- return binder::Status::ok();
-}
-
-binder::Status ImageService::mapImageDevice(const std::string& name, int32_t timeout_ms,
- MappedImage* mapping) {
- if (!CheckUid()) return UidSecurityError();
-
- std::lock_guard<std::mutex> guard(parent_->lock());
-
- if (!impl_->MapImageDevice(name, std::chrono::milliseconds(timeout_ms), &mapping->path)) {
- return BinderError("Failed to map");
+ } else if (single_boot) {
+ *_aidl_return = BOOT_STATUS_SINGLE_BOOT;
+ } else {
+ *_aidl_return = BOOT_STATUS_ENABLED;
}
return binder::Status::ok();
}
-binder::Status ImageService::unmapImageDevice(const std::string& name) {
- if (!CheckUid()) return UidSecurityError();
-
- std::lock_guard<std::mutex> guard(parent_->lock());
+binder::Status GsiService::getUserdataImageSize(int64_t* _aidl_return) {
+ ENFORCE_SYSTEM;
+ std::lock_guard<std::mutex> guard(main_lock_);
+
+ *_aidl_return = -1;
+
+ if (installing_) {
+ // Size has already been computed.
+ *_aidl_return = userdata_size_;
+ } else if (IsGsiRunning()) {
+ // :TODO: libdm
+ unique_fd fd(open(kUserdataDevice, O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
+ if (fd < 0) {
+ PLOG(ERROR) << "open " << kUserdataDevice;
+ return binder::Status::ok();
+ }
- if (!impl_->UnmapImageDevice(name)) {
- return BinderError("Failed to unmap");
+ int64_t size;
+ if (ioctl(fd, BLKGETSIZE64, &size)) {
+ PLOG(ERROR) << "BLKGETSIZE64 " << kUserdataDevice;
+ return binder::Status::ok();
+ }
+ *_aidl_return = size;
+ } else {
+ // Stat the size of the userdata file.
+ auto userdata_gsi = GetInstalledImagePath("userdata_gsi");
+ struct stat s;
+ if (stat(userdata_gsi.c_str(), &s)) {
+ if (errno != ENOENT) {
+ PLOG(ERROR) << "open " << userdata_gsi;
+ return binder::Status::ok();
+ }
+ *_aidl_return = 0;
+ } else {
+ *_aidl_return = s.st_size;
+ }
}
return binder::Status::ok();
}
-binder::Status ImageService::backingImageExists(const std::string& name, bool* _aidl_return) {
- if (!CheckUid()) return UidSecurityError();
-
- std::lock_guard<std::mutex> guard(parent_->lock());
-
- *_aidl_return = impl_->BackingImageExists(name);
- return binder::Status::ok();
-}
-
-binder::Status ImageService::isImageMapped(const std::string& name, bool* _aidl_return) {
- if (!CheckUid()) return UidSecurityError();
-
- std::lock_guard<std::mutex> guard(parent_->lock());
-
- *_aidl_return = impl_->IsImageMapped(name);
- return binder::Status::ok();
-}
-
-binder::Status ImageService::zeroFillNewImage(const std::string& name, int64_t bytes) {
- if (!CheckUid()) return UidSecurityError();
-
- std::lock_guard<std::mutex> guard(parent_->lock());
+binder::Status GsiService::getInstalledGsiImageDir(std::string* _aidl_return) {
+ ENFORCE_SYSTEM;
+ std::lock_guard<std::mutex> guard(main_lock_);
- if (bytes < 0) {
- return BinderError("Cannot use negative values");
- }
- if (!impl_->ZeroFillNewImage(name, bytes)) {
- return BinderError("Failed to fill image with zeros");
+ if (IsGsiInstalled()) {
+ *_aidl_return = GetInstalledImageDir();
}
return binder::Status::ok();
}
-bool ImageService::CheckUid() {
- return uid_ == IPCThreadState::self()->getCallingUid();
-}
-
-binder::Status GsiService::openImageService(const std::string& prefix,
- android::sp<IImageService>* _aidl_return) {
- static constexpr char kImageMetadataPrefix[] = "/metadata/gsi/";
- static constexpr char kImageDataPrefix[] = "/data/gsi/";
-
- auto in_metadata_dir = kImageMetadataPrefix + prefix;
- auto in_data_dir = kImageDataPrefix + prefix;
-
- std::string metadata_dir, data_dir;
- if (!android::base::Realpath(in_metadata_dir, &metadata_dir)) {
- PLOG(ERROR) << "realpath failed: " << metadata_dir;
- return BinderError("Invalid path");
- }
- if (!android::base::Realpath(in_data_dir, &data_dir)) {
- PLOG(ERROR) << "realpath failed: " << data_dir;
- return BinderError("Invalid path");
- }
- if (!android::base::StartsWith(metadata_dir, kImageMetadataPrefix) ||
- !android::base::StartsWith(data_dir, kImageDataPrefix)) {
- return BinderError("Invalid path");
- }
+binder::Status GsiService::wipeGsiUserdata(int* _aidl_return) {
+ ENFORCE_SYSTEM_OR_SHELL;
+ std::lock_guard<std::mutex> guard(main_lock_);
- uid_t uid = IPCThreadState::self()->getCallingUid();
- if (uid != AID_ROOT) {
- return UidSecurityError();
+ if (IsGsiRunning() || !IsGsiInstalled()) {
+ *_aidl_return = IGsiService::INSTALL_ERROR_GENERIC;
+ return binder::Status::ok();
}
- auto impl = ImageManager::Open(metadata_dir, data_dir);
- if (!impl) {
- return BinderError("Unknown error");
- }
+ *_aidl_return = WipeUserdata();
- *_aidl_return = new ImageService(this, std::move(impl), uid);
return binder::Status::ok();
}
@@ -506,7 +399,26 @@ binder::Status GsiService::CheckUid(AccessLevel level) {
return binder::Status::ok();
}
}
- return UidSecurityError();
+
+ auto message = StringPrintf("UID %d is not allowed", uid);
+ return binder::Status::fromExceptionCode(binder::Status::EX_SECURITY,
+ String8(message.c_str()));
+}
+
+void GsiService::PostInstallCleanup() {
+ // This must be closed before unmapping partitions.
+ system_writer_ = nullptr;
+
+ const auto& dm = DeviceMapper::Instance();
+ if (dm.GetState("userdata_gsi") != DmDeviceState::INVALID) {
+ DestroyLogicalPartition("userdata_gsi", kDmTimeout);
+ }
+ if (dm.GetState("system_gsi") != DmDeviceState::INVALID) {
+ DestroyLogicalPartition("system_gsi", kDmTimeout);
+ }
+
+ installing_ = false;
+ partitions_ .clear();
}
static bool IsExternalStoragePath(const std::string& path) {
@@ -532,7 +444,7 @@ int GsiService::ValidateInstallParams(GsiInstallParams* params) {
// specifying the top-level folder, and then we choose the correct location
// underneath.
if (params->installDir.empty() || params->installDir == "/data/gsi") {
- params->installDir = kDefaultDsuImageFolder;
+ params->installDir = kDefaultGsiImageFolder;
}
// Normalize the path and add a trailing slash.
@@ -541,7 +453,8 @@ int GsiService::ValidateInstallParams(GsiInstallParams* params) {
PLOG(ERROR) << "realpath failed: " << origInstallDir;
return INSTALL_ERROR_GENERIC;
}
- // Ensure the path ends in / for consistency.
+ // Ensure the path ends in / for consistency. Even though GetImagePath()
+ // does this already, we want it to appear this way in install_dir.
if (!android::base::EndsWith(params->installDir, "/")) {
params->installDir += "/";
}
@@ -562,36 +475,415 @@ int GsiService::ValidateInstallParams(GsiInstallParams* params) {
LOG(ERROR) << "cannot install GSIs to external media if verity uses check_at_most_once";
return INSTALL_ERROR_GENERIC;
}
- } else if (params->installDir != kDefaultDsuImageFolder) {
+ } else if (params->installDir != kDefaultGsiImageFolder) {
LOG(ERROR) << "cannot install GSI to " << params->installDir;
return INSTALL_ERROR_GENERIC;
}
- if (params->size % LP_SECTOR_SIZE) {
- LOG(ERROR) << params->name << " size " << params->size << " is not a multiple of "
+ if (params->gsiSize % LP_SECTOR_SIZE) {
+ LOG(ERROR) << "GSI size " << params->gsiSize << " is not a multiple of " << LP_SECTOR_SIZE;
+ return INSTALL_ERROR_GENERIC;
+ }
+ if (params->userdataSize % LP_SECTOR_SIZE) {
+ LOG(ERROR) << "userdata size " << params->userdataSize << " is not a multiple of "
<< LP_SECTOR_SIZE;
return INSTALL_ERROR_GENERIC;
}
return INSTALL_OK;
}
-std::string GsiService::GetActiveInstalledImageDir() {
- // Just in case an install was left hanging.
- if (installer_) {
- return installer_->install_dir();
- } else {
- return GetInstalledImageDir();
+int GsiService::StartInstall(const GsiInstallParams& params) {
+ installing_ = true;
+ userdata_block_size_ = 0;
+ system_block_size_ = 0;
+ gsi_size_ = params.gsiSize;
+ userdata_size_ = (params.userdataSize) ? params.userdataSize : kDefaultUserdataSize;
+ wipe_userdata_ = params.wipeUserdata;
+ can_use_devicemapper_ = false;
+ gsi_bytes_written_ = 0;
+ install_dir_ = params.installDir;
+
+ userdata_gsi_path_ = GetImagePath(install_dir_, "userdata_gsi");
+ system_gsi_path_ = GetImagePath(install_dir_, "system_gsi");
+
+ // Only rm userdata_gsi if one didn't already exist.
+ wipe_userdata_on_failure_ = wipe_userdata_ || access(userdata_gsi_path_.c_str(), F_OK);
+
+ if (int status = PerformSanityChecks()) {
+ return status;
+ }
+ if (int status = PreallocateFiles()) {
+ return status;
+ }
+ if (int status = DetermineReadWriteMethod()) {
+ return status;
}
+ if (!FormatUserdata()) {
+ return INSTALL_ERROR_GENERIC;
+ }
+
+ // Map system_gsi so we can write to it.
+ system_writer_ = OpenPartition("system_gsi");
+ if (!system_writer_) {
+ return INSTALL_ERROR_GENERIC;
+ }
+ return INSTALL_OK;
+}
+
+int GsiService::DetermineReadWriteMethod() {
+ // If there is a device-mapper node wrapping the block device, then we're
+ // able to create another node around it; the dm layer does not carry the
+ // exclusion lock down the stack when a mount occurs.
+ //
+ // If there is no intermediate device-mapper node, then partitions cannot be
+ // opened writable due to sepolicy and exclusivity of having a mounted
+ // filesystem. This should only happen on devices with no encryption, or
+ // devices with FBE and no metadata encryption. For these cases it suffices
+ // to perform normal file writes to /data/gsi (which is unencrypted).
+ std::string block_device;
+ if (!FiemapWriter::GetBlockDeviceForFile(system_gsi_path_.c_str(), &block_device,
+ &can_use_devicemapper_)) {
+ return INSTALL_ERROR_GENERIC;
+ }
+ if (install_dir_ != kDefaultGsiImageFolder && can_use_devicemapper_) {
+ // Never use device-mapper on external media. We don't support adopted
+ // storage yet, and accidentally using device-mapper could be dangerous
+ // as we hardcode the userdata device as backing storage.
+ LOG(ERROR) << "unexpected device-mapper node used to mount external media";
+ return INSTALL_ERROR_GENERIC;
+ }
+ return INSTALL_OK;
+}
+
+std::string GsiService::GetImagePath(const std::string& image_dir, const std::string& name) {
+ std::string dir = image_dir;
+ if (!android::base::EndsWith(dir, "/")) {
+ dir += "/";
+ }
+ return dir + name + ".img";
}
std::string GsiService::GetInstalledImageDir() {
// If there's no install left, just return /data/gsi since that's where
// installs go by default.
std::string dir;
- if (android::base::ReadFileToString(kDsuInstallDirFile, &dir)) {
+ if (android::base::ReadFileToString(kGsiInstallDirFile, &dir)) {
return dir;
}
- return kDefaultDsuImageFolder;
+ return kDefaultGsiImageFolder;
+}
+
+std::string GsiService::GetInstalledImagePath(const std::string& name) {
+ return GetImagePath(GetInstalledImageDir(), name);
+}
+
+int GsiService::PerformSanityChecks() {
+ if (gsi_size_ < 0) {
+ LOG(ERROR) << "image size " << gsi_size_ << " is negative";
+ return INSTALL_ERROR_GENERIC;
+ }
+ if (android::gsi::IsGsiRunning()) {
+ LOG(ERROR) << "cannot install gsi inside a live gsi";
+ return INSTALL_ERROR_GENERIC;
+ }
+
+ struct statvfs sb;
+ if (statvfs(install_dir_.c_str(), &sb)) {
+ PLOG(ERROR) << "failed to read file system stats";
+ return INSTALL_ERROR_GENERIC;
+ }
+
+ // This is the same as android::vold::GetFreebytes() but we also
+ // need the total file system size so we open code it here.
+ uint64_t free_space = 1ULL * sb.f_bavail * sb.f_frsize;
+ uint64_t fs_size = sb.f_blocks * sb.f_frsize;
+ if (free_space <= (gsi_size_ + userdata_size_)) {
+ LOG(ERROR) << "not enough free space (only " << free_space << " bytes available)";
+ return INSTALL_ERROR_NO_SPACE;
+ }
+ // We are asking for 40% of the /data to be empty.
+ // TODO: may be not hard code it like this
+ double free_space_percent = ((1.0 * free_space) / fs_size) * 100;
+ if (free_space_percent < kMinimumFreeSpaceThreshold) {
+ LOG(ERROR) << "free space " << static_cast<uint64_t>(free_space_percent)
+ << "% is below the minimum threshold of " << kMinimumFreeSpaceThreshold << "%";
+ return INSTALL_ERROR_FILE_SYSTEM_CLUTTERED;
+ }
+ return INSTALL_OK;
+}
+
+int GsiService::PreallocateFiles() {
+ if (wipe_userdata_) {
+ SplitFiemap::RemoveSplitFiles(userdata_gsi_path_);
+ }
+ SplitFiemap::RemoveSplitFiles(system_gsi_path_);
+
+ // TODO: trigger GC from fiemap writer.
+
+ // Create fallocated files.
+ if (int status = PreallocateUserdata()) {
+ return status;
+ }
+ if (int status = PreallocateSystem()) {
+ return status;
+ }
+
+ // Save the extent information in liblp.
+ metadata_ = CreateMetadata();
+ if (!metadata_) {
+ return INSTALL_ERROR_GENERIC;
+ }
+
+ UpdateProgress(STATUS_COMPLETE, 0);
+ return INSTALL_OK;
+}
+
+int GsiService::PreallocateUserdata() {
+ int error;
+ std::unique_ptr<SplitFiemap> userdata_image;
+ if (wipe_userdata_ || access(userdata_gsi_path_.c_str(), F_OK)) {
+ StartAsyncOperation("create userdata", userdata_size_);
+ userdata_image = CreateFiemapWriter(userdata_gsi_path_, userdata_size_, &error);
+ if (!userdata_image) {
+ LOG(ERROR) << "Could not create userdata image: " << userdata_gsi_path_;
+ return error;
+ }
+ // Signal that we need to reformat userdata.
+ wipe_userdata_ = true;
+ } else {
+ userdata_image = CreateFiemapWriter(userdata_gsi_path_, 0, &error);
+ if (!userdata_image) {
+ LOG(ERROR) << "Could not open userdata image: " << userdata_gsi_path_;
+ return error;
+ }
+ if (userdata_size_ && userdata_image->size() < userdata_size_) {
+ // :TODO: need to fallocate more blocks and resizefs.
+ }
+ userdata_size_ = userdata_image->size();
+ }
+
+ userdata_block_size_ = userdata_image->block_size();
+
+ Image image = {
+ .writer = std::move(userdata_image),
+ .actual_size = userdata_size_,
+ };
+ partitions_.emplace(std::make_pair("userdata_gsi", std::move(image)));
+ return INSTALL_OK;
+}
+
+int GsiService::PreallocateSystem() {
+ StartAsyncOperation("create system", gsi_size_);
+
+ int error;
+ auto system_image = CreateFiemapWriter(system_gsi_path_, gsi_size_, &error);
+ if (!system_image) {
+ return error;
+ }
+
+ system_block_size_ = system_image->block_size();
+
+ Image image = {
+ .writer = std::move(system_image),
+ .actual_size = gsi_size_,
+ };
+ partitions_.emplace(std::make_pair("system_gsi", std::move(image)));
+ return INSTALL_OK;
+}
+
+std::unique_ptr<SplitFiemap> GsiService::CreateFiemapWriter(const std::string& path,
+ uint64_t size, int* error) {
+ bool create = (size != 0);
+
+ std::function<bool(uint64_t, uint64_t)> progress;
+ if (create) {
+ // TODO: allow cancelling inside cancelGsiInstall.
+ progress = [this](uint64_t bytes, uint64_t /* total */) -> bool {
+ UpdateProgress(STATUS_WORKING, bytes);
+ if (should_abort_) return false;
+ return true;
+ };
+ }
+
+ std::unique_ptr<SplitFiemap> file;
+ if (!size) {
+ file = SplitFiemap::Open(path);
+ } else {
+ file = SplitFiemap::Create(path, size, 0, std::move(progress));
+ }
+ if (!file) {
+ LOG(ERROR) << "failed to create or open " << path;
+ *error = INSTALL_ERROR_GENERIC;
+ return nullptr;
+ }
+
+ uint64_t extents = file->extents().size();
+ if (extents > kMaximumExtents) {
+ LOG(ERROR) << "file " << path << " has too many extents: " << extents;
+ *error = INSTALL_ERROR_FILE_SYSTEM_CLUTTERED;
+ return nullptr;
+ }
+ return file;
+}
+
+// Write data through an fd.
+class FdWriter final : public GsiService::WriteHelper {
+ public:
+ FdWriter(const std::string& path, unique_fd&& fd) : path_(path), fd_(std::move(fd)) {}
+
+ bool Write(const void* data, uint64_t bytes) override {
+ return android::base::WriteFully(fd_, data, bytes);
+ }
+ bool Flush() override {
+ if (fsync(fd_)) {
+ PLOG(ERROR) << "fsync failed: " << path_;
+ return false;
+ }
+ return true;
+ }
+ uint64_t Size() override { return get_block_device_size(fd_); }
+
+ private:
+ std::string path_;
+ unique_fd fd_;
+};
+
+// Write data through a SplitFiemap.
+class SplitFiemapWriter final : public GsiService::WriteHelper {
+ public:
+ explicit SplitFiemapWriter(SplitFiemap* writer) : writer_(writer) {}
+
+ bool Write(const void* data, uint64_t bytes) override {
+ return writer_->Write(data, bytes);
+ }
+ bool Flush() override {
+ return writer_->Flush();
+ }
+ uint64_t Size() override { return writer_->size(); }
+
+ private:
+ SplitFiemap* writer_;
+};
+
+std::unique_ptr<GsiService::WriteHelper> GsiService::OpenPartition(const std::string& name) {
+ if (can_use_devicemapper_) {
+ std::string path;
+ if (!CreateLogicalPartition(kUserdataDevice, *metadata_.get(), name, true, kDmTimeout,
+ &path)) {
+ LOG(ERROR) << "Error creating device-mapper node for " << name;
+ return {};
+ }
+
+ static const int kOpenFlags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
+ unique_fd fd(open(path.c_str(), kOpenFlags));
+ if (fd < 0) {
+ PLOG(ERROR) << "could not open " << path;
+ }
+ return std::make_unique<FdWriter>(GetImagePath(install_dir_, name), std::move(fd));
+ }
+
+ auto iter = partitions_.find(name);
+ if (iter == partitions_.end()) {
+ LOG(ERROR) << "could not find partition " << name;
+ return {};
+ }
+ return std::make_unique<SplitFiemapWriter>(iter->second.writer.get());
+}
+
+bool GsiService::CommitGsiChunk(int stream_fd, int64_t bytes) {
+ StartAsyncOperation("write gsi", gsi_size_);
+
+ if (bytes < 0) {
+ LOG(ERROR) << "chunk size " << bytes << " is negative";
+ return false;
+ }
+
+ auto buffer = std::make_unique<char[]>(system_block_size_);
+
+ int progress = -1;
+ uint64_t remaining = bytes;
+ while (remaining) {
+ // :TODO: check file pin status!
+ size_t max_to_read = std::min(system_block_size_, remaining);
+ ssize_t rv = TEMP_FAILURE_RETRY(read(stream_fd, buffer.get(), max_to_read));
+ if (rv < 0) {
+ PLOG(ERROR) << "read gsi chunk";
+ return false;
+ }
+ if (rv == 0) {
+ LOG(ERROR) << "no bytes left in stream";
+ return false;
+ }
+ if (!CommitGsiChunk(buffer.get(), rv)) {
+ return false;
+ }
+ CHECK(static_cast<uint64_t>(rv) <= remaining);
+ remaining -= rv;
+
+ // Only update the progress when the % (or permille, in this case)
+ // significantly changes.
+ int new_progress = ((gsi_size_ - remaining) * 1000) / gsi_size_;
+ if (new_progress != progress) {
+ UpdateProgress(STATUS_WORKING, gsi_size_ - remaining);
+ }
+ }
+
+ UpdateProgress(STATUS_COMPLETE, gsi_size_);
+ return true;
+}
+
+bool GsiService::CommitGsiChunk(const void* data, size_t bytes) {
+ if (!installing_) {
+ LOG(ERROR) << "no gsi installation in progress";
+ return false;
+ }
+ if (static_cast<uint64_t>(bytes) > gsi_size_ - gsi_bytes_written_) {
+ // We cannot write past the end of the image file.
+ LOG(ERROR) << "chunk size " << bytes << " exceeds remaining image size (" << gsi_size_
+ << " expected, " << gsi_bytes_written_ << " written)";
+ return false;
+ }
+
+ if (!system_writer_->Write(data, bytes)) {
+ PLOG(ERROR) << "write failed";
+ return false;
+ }
+ gsi_bytes_written_ += bytes;
+ return true;
+}
+
+int GsiService::SetGsiBootable(bool one_shot) {
+ if (gsi_bytes_written_ != gsi_size_) {
+ // We cannot boot if the image is incomplete.
+ LOG(ERROR) << "image incomplete; expected " << gsi_size_ << " bytes, waiting for "
+ << (gsi_size_ - gsi_bytes_written_) << " bytes";
+ return INSTALL_ERROR_GENERIC;
+ }
+
+ if (!system_writer_->Flush()) {
+ return INSTALL_ERROR_GENERIC;
+ }
+
+ // If files moved (are no longer pinned), the metadata file will be invalid.
+ for (const auto& [name, image] : partitions_) {
+ if (!image.writer->HasPinnedExtents()) {
+ LOG(ERROR) << name << " no longer has pinned extents";
+ return INSTALL_ERROR_GENERIC;
+ }
+ }
+
+ // Remember the installation directory.
+ if (!android::base::WriteStringToFile(install_dir_, kGsiInstallDirFile)) {
+ PLOG(ERROR) << "write failed: " << kGsiInstallDirFile;
+ return INSTALL_ERROR_GENERIC;
+ }
+
+ // Note: create the install status file last, since this is the actual boot
+ // indicator.
+ if (!CreateMetadataFile() || !SetBootMode(one_shot) || !CreateInstallStatusFile()) {
+ return INSTALL_ERROR_GENERIC;
+ }
+ return INSTALL_OK;
}
int GsiService::ReenableGsi(bool one_shot) {
@@ -602,7 +894,7 @@ int GsiService::ReenableGsi(bool one_shot) {
std::string boot_key;
if (!GetInstallStatus(&boot_key)) {
- PLOG(ERROR) << "read " << kDsuInstallStatusFile;
+ PLOG(ERROR) << "read " << kGsiInstallStatusFile;
return INSTALL_ERROR_GENERIC;
}
if (boot_key != kInstallStatusDisabled) {
@@ -610,33 +902,164 @@ int GsiService::ReenableGsi(bool one_shot) {
return INSTALL_ERROR_GENERIC;
}
- return GsiInstaller::ReenableGsi(one_shot);
+ if (IsGsiRunning()) {
+ if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) {
+ return INSTALL_ERROR_GENERIC;
+ }
+ return INSTALL_OK;
+ }
+ // Note: this metadata is only used to recover the original partition sizes.
+ // We do not trust the extent information, which will get rebuilt later.
+ auto old_metadata = ReadFromImageFile(kGsiLpMetadataFile);
+ if (!old_metadata) {
+ LOG(ERROR) << "GSI install is incomplete";
+ return INSTALL_ERROR_GENERIC;
+ }
+
+ // Set up enough installer state so that we can use various helper
+ // methods.
+ //
+ // TODO(dvander) Extract all of the installer state into a separate
+ // class so this is more manageable.
+ install_dir_ = GetInstalledImageDir();
+ system_gsi_path_ = GetImagePath(install_dir_, "system_gsi");
+ if (int error = DetermineReadWriteMethod()) {
+ return error;
+ }
+
+ // Recover parition information.
+ Image userdata_image;
+ if (int error = GetExistingImage(*old_metadata.get(), "userdata_gsi", &userdata_image)) {
+ return error;
+ }
+ partitions_.emplace(std::make_pair("userdata_gsi", std::move(userdata_image)));
+
+ Image system_image;
+ if (int error = GetExistingImage(*old_metadata.get(), "system_gsi", &system_image)) {
+ return error;
+ }
+ partitions_.emplace(std::make_pair("system_gsi", std::move(system_image)));
+
+ metadata_ = CreateMetadata();
+ if (!metadata_) {
+ return INSTALL_ERROR_GENERIC;
+ }
+ if (!CreateMetadataFile() || !SetBootMode(one_shot) || !CreateInstallStatusFile()) {
+ return INSTALL_ERROR_GENERIC;
+ }
+ return INSTALL_OK;
+}
+
+int GsiService::WipeUserdata() {
+ // Note: this metadata is only used to recover the original partition sizes.
+ // We do not trust the extent information, which will get rebuilt later.
+ auto old_metadata = ReadFromImageFile(kGsiLpMetadataFile);
+ if (!old_metadata) {
+ LOG(ERROR) << "GSI install is incomplete";
+ return INSTALL_ERROR_GENERIC;
+ }
+
+ install_dir_ = GetInstalledImageDir();
+ system_gsi_path_ = GetImagePath(install_dir_, "system_gsi");
+ if (int error = DetermineReadWriteMethod()) {
+ return error;
+ }
+
+ // Recover parition information.
+ Image userdata_image;
+ if (int error = GetExistingImage(*old_metadata.get(), "userdata_gsi", &userdata_image)) {
+ return error;
+ }
+ partitions_.emplace(std::make_pair("userdata_gsi", std::move(userdata_image)));
+
+ metadata_ = CreateMetadata();
+ if (!metadata_) {
+ return INSTALL_ERROR_GENERIC;
+ }
+
+ auto writer = OpenPartition("userdata_gsi");
+ if (!writer) {
+ return IGsiService::INSTALL_ERROR_GENERIC;
+ }
+
+ // Wipe the first 1MiB of the device, ensuring both the first block and
+ // the superblock are destroyed.
+ static constexpr uint64_t kEraseSize = 1024 * 1024;
+
+ std::string zeroes(4096, 0);
+ uint64_t erase_size = std::min(kEraseSize, writer->Size());
+ for (uint64_t i = 0; i < erase_size; i += zeroes.size()) {
+ if (!writer->Write(zeroes.data(), zeroes.size())) {
+ PLOG(ERROR) << "write userdata_gsi";
+ return IGsiService::INSTALL_ERROR_GENERIC;
+ }
+ }
+ return INSTALL_OK;
+}
+
+static uint64_t GetPartitionSize(const LpMetadata& metadata, const LpMetadataPartition& partition) {
+ uint64_t total = 0;
+ for (size_t i = 0; i < partition.num_extents; i++) {
+ const auto& extent = metadata.extents[partition.first_extent_index + i];
+ if (extent.target_type != LP_TARGET_TYPE_LINEAR) {
+ LOG(ERROR) << "non-linear extent detected";
+ return 0;
+ }
+ total += extent.num_sectors * LP_SECTOR_SIZE;
+ }
+ return total;
+}
+
+static uint64_t GetPartitionSize(const LpMetadata& metadata, const std::string& name) {
+ for (const auto& partition : metadata.partitions) {
+ if (GetPartitionName(partition) == name) {
+ return GetPartitionSize(metadata, partition);
+ }
+ }
+ return 0;
+}
+
+int GsiService::GetExistingImage(const LpMetadata& metadata, const std::string& name,
+ Image* image) {
+ int error;
+ std::string path = GetInstalledImagePath(name);
+ auto writer = CreateFiemapWriter(path.c_str(), 0, &error);
+ if (!writer) {
+ return error;
+ }
+
+ // Even after recovering the FIEMAP, we also need to know the exact intended
+ // size of the image, since FiemapWriter may have extended the final block.
+ uint64_t actual_size = GetPartitionSize(metadata, name);
+ if (!actual_size) {
+ LOG(ERROR) << "Could not determine the pre-existing size of " << name;
+ return INSTALL_ERROR_GENERIC;
+ }
+ image->writer = std::move(writer);
+ image->actual_size = actual_size;
+ return INSTALL_OK;
}
bool GsiService::RemoveGsiFiles(const std::string& install_dir, bool wipeUserdata) {
bool ok = true;
- if (auto manager = ImageManager::Open(kDsuMetadataDir, install_dir)) {
- std::vector<std::string> images = manager->GetAllBackingImages();
- for (auto&& image : images) {
- if (!android::base::EndsWith(image, "_gsi")) {
- continue;
- }
- if (manager->IsImageMapped(image)) {
- ok &= manager->UnmapImageDevice(image);
- }
- if (!android::base::StartsWith(image, "userdata") || wipeUserdata) {
- ok &= manager->DeleteBackingImage(image);
- }
- }
+ std::string message;
+ if (!SplitFiemap::RemoveSplitFiles(GetImagePath(install_dir, "system_gsi"), &message)) {
+ LOG(ERROR) << message;
+ ok = false;
+ }
+ if (wipeUserdata &&
+ !SplitFiemap::RemoveSplitFiles(GetImagePath(install_dir, "userdata_gsi"), &message)) {
+ LOG(ERROR) << message;
+ ok = false;
}
std::vector<std::string> files{
- kDsuInstallStatusFile,
- kDsuOneShotBootFile,
- kDsuInstallDirFile,
+ kGsiInstallStatusFile,
+ kGsiLpMetadataFile,
+ kGsiOneShotBootFile,
+ kGsiInstallDirFile,
};
for (const auto& file : files) {
- std::string message;
if (!android::base::RemoveFileIfExists(file, &message)) {
LOG(ERROR) << message;
ok = false;
@@ -650,7 +1073,7 @@ bool GsiService::DisableGsiInstall() {
LOG(ERROR) << "cannot disable gsi install - no install detected";
return false;
}
- if (installer_) {
+ if (installing_) {
LOG(ERROR) << "cannot disable gsi during GSI installation";
return false;
}
@@ -661,22 +1084,143 @@ bool GsiService::DisableGsiInstall() {
return true;
}
-void GsiService::CleanCorruptedInstallation() {
- auto install_dir = GetInstalledImageDir();
- if (!RemoveGsiFiles(install_dir, true)) {
- LOG(ERROR) << "Failed to CleanCorruptedInstallation on " << install_dir;
+std::unique_ptr<LpMetadata> GsiService::CreateMetadata() {
+ std::string data_device_path;
+ if (install_dir_ == kDefaultGsiImageFolder && !access(kUserdataDevice, F_OK)) {
+ data_device_path = kUserdataDevice;
+ } else {
+ auto writer = partitions_["system_gsi"].writer.get();
+ data_device_path = writer->bdev_path();
+ }
+ auto data_device_name = android::base::Basename(data_device_path);
+
+ PartitionOpener opener;
+ BlockDeviceInfo data_device_info;
+ if (!opener.GetInfo(data_device_path, &data_device_info)) {
+ LOG(ERROR) << "Error reading userdata partition";
+ return nullptr;
+ }
+
+ std::vector<BlockDeviceInfo> block_devices = {data_device_info};
+ auto builder = MetadataBuilder::New(block_devices, data_device_name, 128 * 1024, 1);
+ if (!builder) {
+ LOG(ERROR) << "Error creating metadata builder";
+ return nullptr;
+ }
+ builder->IgnoreSlotSuffixing();
+
+ for (const auto& [name, image] : partitions_) {
+ uint32_t flags = LP_PARTITION_ATTR_NONE;
+ if (name == "system_gsi") {
+ flags |= LP_PARTITION_ATTR_READONLY;
+ }
+ Partition* partition = builder->AddPartition(name, flags);
+ if (!partition) {
+ LOG(ERROR) << "Error adding " << name << " to partition table";
+ return nullptr;
+ }
+ if (!AddPartitionFiemap(builder.get(), partition, image, data_device_name)) {
+ return nullptr;
+ }
+ }
+
+ auto metadata = builder->Export();
+ if (!metadata) {
+ LOG(ERROR) << "Error exporting partition table";
+ return nullptr;
+ }
+ return metadata;
+}
+
+bool GsiService::CreateMetadataFile() {
+ if (!WriteToImageFile(kGsiLpMetadataFile, *metadata_.get())) {
+ LOG(ERROR) << "Error writing GSI partition table image";
+ return false;
+ }
+ return true;
+}
+
+bool GsiService::FormatUserdata() {
+ auto writer = OpenPartition("userdata_gsi");
+ if (!writer) {
+ return false;
+ }
+
+ // libcutils checks the first 4K, no matter the block size.
+ std::string zeroes(4096, 0);
+ if (!writer->Write(zeroes.data(), zeroes.size())) {
+ PLOG(ERROR) << "write userdata_gsi";
+ return false;
+ }
+ return true;
+}
+
+bool GsiService::AddPartitionFiemap(MetadataBuilder* builder, Partition* partition,
+ const Image& image, const std::string& block_device) {
+ uint64_t sectors_needed = image.actual_size / LP_SECTOR_SIZE;
+ for (const auto& extent : image.writer->extents()) {
+ // :TODO: block size check for length, not sector size
+ if (extent.fe_length % LP_SECTOR_SIZE != 0) {
+ LOG(ERROR) << "Extent is not sector-aligned: " << extent.fe_length;
+ return false;
+ }
+ if (extent.fe_physical % LP_SECTOR_SIZE != 0) {
+ LOG(ERROR) << "Extent physical sector is not sector-aligned: " << extent.fe_physical;
+ return false;
+ }
+
+ uint64_t num_sectors =
+ std::min(static_cast<uint64_t>(extent.fe_length / LP_SECTOR_SIZE), sectors_needed);
+ if (!num_sectors || !sectors_needed) {
+ // This should never happen, but we include it just in case. It would
+ // indicate that the last filesystem block had multiple extents.
+ LOG(WARNING) << "FiemapWriter allocated extra blocks";
+ break;
+ }
+
+ uint64_t physical_sector = extent.fe_physical / LP_SECTOR_SIZE;
+ if (!builder->AddLinearExtent(partition, block_device, num_sectors, physical_sector)) {
+ LOG(ERROR) << "Could not add extent to lp metadata";
+ return false;
+ }
+
+ sectors_needed -= num_sectors;
+ }
+ return true;
+}
+
+bool GsiService::SetBootMode(bool one_shot) {
+ if (one_shot) {
+ if (!android::base::WriteStringToFile("1", kGsiOneShotBootFile)) {
+ PLOG(ERROR) << "write " << kGsiOneShotBootFile;
+ return false;
+ }
+ } else if (!access(kGsiOneShotBootFile, F_OK)) {
+ std::string error;
+ if (!android::base::RemoveFileIfExists(kGsiOneShotBootFile, &error)) {
+ LOG(ERROR) << error;
+ return false;
+ }
+ }
+ return true;
+}
+
+bool GsiService::CreateInstallStatusFile() {
+ if (!android::base::WriteStringToFile("0", kGsiInstallStatusFile)) {
+ PLOG(ERROR) << "write " << kGsiInstallStatusFile;
+ return false;
}
+ return true;
}
void GsiService::RunStartupTasks() {
if (!IsGsiInstalled()) {
- CleanCorruptedInstallation();
return;
}
std::string boot_key;
if (!GetInstallStatus(&boot_key)) {
- PLOG(ERROR) << "read " << kDsuInstallStatusFile;
+ PLOG(ERROR) << "read " << kGsiInstallStatusFile;
return;
}
@@ -692,8 +1236,8 @@ void GsiService::RunStartupTasks() {
int ignore;
if (GetBootAttempts(boot_key, &ignore)) {
// Mark the GSI as having successfully booted.
- if (!android::base::WriteStringToFile(kInstallStatusOk, kDsuInstallStatusFile)) {
- PLOG(ERROR) << "write " << kDsuInstallStatusFile;
+ if (!android::base::WriteStringToFile(kInstallStatusOk, kGsiInstallStatusFile)) {
+ PLOG(ERROR) << "write " << kGsiInstallStatusFile;
}
}
}
diff --git a/gsi_service.h b/gsi_service.h
index 0f06b0e..30b5782 100644
--- a/gsi_service.h
+++ b/gsi_service.h
@@ -24,95 +24,143 @@
#include <android-base/unique_fd.h>
#include <android/gsi/BnGsiService.h>
-#include <android/gsi/BnGsid.h>
#include <binder/BinderService.h>
-#include <libfiemap/split_fiemap_writer.h>
+#include <libfiemap_writer/split_fiemap_writer.h>
#include <liblp/builder.h>
#include "libgsi/libgsi.h"
-#include "gsi_installer.h"
-
namespace android {
namespace gsi {
-class Gsid : public BinderService<Gsid>, public BnGsid {
+class GsiService : public BinderService<GsiService>, public BnGsiService {
public:
static void Register();
- static char const* getServiceName() { return kGsiServiceName; }
-
- binder::Status getClient(android::sp<IGsiService>* _aidl_return) override;
-
- private:
- friend class GsiService;
- friend class ImageService;
-
- std::mutex& lock() { return lock_; }
-
- std::mutex lock_;
-};
-class GsiService : public BinderService<GsiService>, public BnGsiService {
- public:
+ GsiService();
~GsiService() override;
- static android::sp<IGsiService> Get(Gsid* parent);
-
+ binder::Status startGsiInstall(int64_t gsiSize, int64_t userdataSize, bool wipeUserdata,
+ int* _aidl_return) override;
binder::Status beginGsiInstall(const GsiInstallParams& params, int* _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;
- binder::Status setGsiAshmem(const ::android::os::ParcelFileDescriptor& ashmem, int64_t size,
- bool* _aidl_return) override;
- binder::Status commitGsiChunkFromAshmem(int64_t bytes, bool* _aidl_return) override;
+ binder::Status commitGsiChunkFromMemory(const ::std::vector<uint8_t>& bytes,
+ bool* _aidl_return) override;
binder::Status cancelGsiInstall(bool* _aidl_return) override;
- binder::Status enableGsi(bool oneShot, int* _aidl_return) override;
+ binder::Status setGsiBootable(bool oneShot, 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;
- binder::Status isGsiInstalled(bool* _aidl_return) override;
+ binder::Status removeGsiInstall(bool* _aidl_return) override;
+ binder::Status disableGsiInstall(bool* _aidl_return) override;
binder::Status isGsiRunning(bool* _aidl_return) override;
+ binder::Status isGsiInstalled(bool* _aidl_return) override;
binder::Status isGsiInstallInProgress(bool* _aidl_return) override;
+ binder::Status getUserdataImageSize(int64_t* _aidl_return) override;
+ binder::Status getGsiBootStatus(int* _aidl_return) override;
binder::Status getInstalledGsiImageDir(std::string* _aidl_return) override;
binder::Status wipeGsiUserdata(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;
-
- // This is in GsiService, rather than GsiInstaller, since we need to access
- // it outside of the main lock which protects the unique_ptr.
- void StartAsyncOperation(const std::string& step, int64_t total_bytes);
- void UpdateProgress(int status, int64_t bytes_processed);
- // Helper methods for GsiInstaller.
- static bool RemoveGsiFiles(const std::string& install_dir, bool wipeUserdata);
- bool should_abort() const { return should_abort_; }
- Gsid* parent() const { return parent_.get(); }
+ static char const* getServiceName() { return kGsiServiceName; }
static void RunStartupTasks();
- static std::string GetInstalledImageDir();
- std::string GetActiveInstalledImageDir();
+
+ // This helper class will redirect writes to either a SplitFiemap or
+ // device-mapper.
+ class WriteHelper {
+ public:
+ virtual ~WriteHelper() {};
+ virtual bool Write(const void* data, uint64_t bytes) = 0;
+ virtual bool Flush() = 0;
+ virtual uint64_t Size() = 0;
+
+ WriteHelper() = default;
+ WriteHelper(const WriteHelper&) = delete;
+ WriteHelper& operator=(const WriteHelper&) = delete;
+ WriteHelper& operator=(WriteHelper&&) = delete;
+ WriteHelper(WriteHelper&&) = delete;
+ };
private:
- GsiService(Gsid* parent);
+ using LpMetadata = android::fs_mgr::LpMetadata;
+ using MetadataBuilder = android::fs_mgr::MetadataBuilder;
+ using SplitFiemap = android::fiemap_writer::SplitFiemap;
+
+ struct Image {
+ std::unique_ptr<SplitFiemap> writer;
+ uint64_t actual_size;
+ };
+
int ValidateInstallParams(GsiInstallParams* params);
- bool DisableGsiInstall();
+ int StartInstall(const GsiInstallParams& params);
+ int PerformSanityChecks();
+ int PreallocateFiles();
+ int PreallocateUserdata();
+ int PreallocateSystem();
+ int DetermineReadWriteMethod();
+ bool FormatUserdata();
+ bool CommitGsiChunk(int stream_fd, int64_t bytes);
+ bool CommitGsiChunk(const void* data, size_t bytes);
+ int SetGsiBootable(bool one_shot);
int ReenableGsi(bool one_shot);
- static void CleanCorruptedInstallation();
+ int WipeUserdata();
+ bool DisableGsiInstall();
+ bool AddPartitionFiemap(android::fs_mgr::MetadataBuilder* builder,
+ android::fs_mgr::Partition* partition, const Image& image,
+ const std::string& block_device);
+ std::unique_ptr<LpMetadata> CreateMetadata();
+ std::unique_ptr<SplitFiemap> CreateFiemapWriter(const std::string& path, uint64_t size,
+ int* error);
+ bool CreateInstallStatusFile();
+ bool CreateMetadataFile();
+ bool SetBootMode(bool one_shot);
+ void PostInstallCleanup();
- enum class AccessLevel { System, SystemOrShell };
+ void StartAsyncOperation(const std::string& step, int64_t total_bytes);
+ void UpdateProgress(int status, int64_t bytes_processed);
+ int GetExistingImage(const LpMetadata& metadata, const std::string& name, Image* image);
+ std::unique_ptr<WriteHelper> OpenPartition(const std::string& name);
+
+ enum class AccessLevel {
+ System,
+ SystemOrShell
+ };
binder::Status CheckUid(AccessLevel level = AccessLevel::System);
- static android::wp<GsiService> sInstance;
+ static bool RemoveGsiFiles(const std::string& install_dir, bool wipeUserdata);
+ static std::string GetImagePath(const std::string& image_dir, const std::string& name);
+ static std::string GetInstalledImagePath(const std::string& name);
+ static std::string GetInstalledImageDir();
+
+ std::mutex main_lock_;
- android::sp<Gsid> parent_;
- std::unique_ptr<GsiInstaller> installer_;
+ // Set before installation starts, to determine whether or not to delete
+ // the userdata image if installation fails.
+ bool wipe_userdata_on_failure_;
// These are initialized or set in StartInstall().
+ bool installing_ = false;
std::atomic<bool> should_abort_ = false;
+ std::string install_dir_;
+ std::string userdata_gsi_path_;
+ std::string system_gsi_path_;
+ uint64_t userdata_block_size_;
+ uint64_t system_block_size_;
+ uint64_t gsi_size_;
+ uint64_t userdata_size_;
+ bool can_use_devicemapper_;
+ bool wipe_userdata_;
+ // Remaining data we're waiting to receive for the GSI image.
+ uint64_t gsi_bytes_written_;
// Progress bar state.
std::mutex progress_lock_;
GsiProgress progress_;
+
+ std::unique_ptr<WriteHelper> system_writer_;
+
+ // This is used to track which GSI partitions have been created.
+ std::map<std::string, Image> partitions_;
+ std::unique_ptr<LpMetadata> metadata_;
};
} // namespace gsi
diff --git a/gsi_tool.cpp b/gsi_tool.cpp
index 0841e61..ee6094b 100644
--- a/gsi_tool.cpp
+++ b/gsi_tool.cpp
@@ -17,7 +17,6 @@
#include <getopt.h>
#include <stdio.h>
#include <sysexits.h>
-#include <unistd.h>
#include <chrono>
#include <condition_variable>
@@ -28,16 +27,13 @@
#include <string>
#include <thread>
-#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/unique_fd.h>
#include <android/gsi/IGsiService.h>
-#include <android/gsi/IGsid.h>
#include <binder/IServiceManager.h>
#include <cutils/android_reboot.h>
#include <libgsi/libgsi.h>
-#include <libgsi/libgsid.h>
using namespace android::gsi;
using namespace std::chrono_literals;
@@ -54,7 +50,6 @@ static int Status(sp<IGsiService> gsid, int argc, char** argv);
static int Cancel(sp<IGsiService> gsid, int argc, char** argv);
static const std::map<std::string, CommandCallback> kCommandMap = {
- // clang-format off
{"disable", Disable},
{"enable", Enable},
{"install", Install},
@@ -62,11 +57,32 @@ static const std::map<std::string, CommandCallback> kCommandMap = {
{"wipe-data", WipeData},
{"status", Status},
{"cancel", Cancel},
- // clang-format on
};
-static std::string ErrorMessage(const android::binder::Status& status,
- int error_code = IGsiService::INSTALL_ERROR_GENERIC) {
+static sp<IGsiService> GetGsiService() {
+ if (android::base::GetProperty("init.svc.gsid", "") != "running") {
+ if (!android::base::SetProperty("ctl.start", "gsid") ||
+ !android::base::WaitForProperty("init.svc.gsid", "running", 5s)) {
+ std::cerr << "Unable to start gsid\n";
+ return nullptr;
+ }
+ }
+
+ static const int kSleepTimeMs = 50;
+ static const int kTotalWaitTimeMs = 3000;
+ for (int i = 0; i < kTotalWaitTimeMs / kSleepTimeMs; i++) {
+ auto sm = android::defaultServiceManager();
+ auto name = android::String16(kGsiServiceName);
+ android::sp<android::IBinder> res = sm->checkService(name);
+ if (res) {
+ return android::interface_cast<IGsiService>(res);
+ }
+ usleep(kSleepTimeMs * 1000);
+ }
+ return nullptr;
+}
+
+static std::string ErrorMessage(const android::binder::Status& status, int error_code = IGsiService::INSTALL_ERROR_GENERIC) {
if (!status.isOk()) {
return status.exceptionMessage().string();
}
@@ -188,23 +204,21 @@ class ProgressBar {
};
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},
};
- long gsiSize = 0;
- long userdataSize = 0;
- bool wipeUserdata = false;
+ GsiInstallParams params;
+ params.gsiSize = 0;
+ params.userdataSize = 0;
+ params.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;
@@ -213,26 +227,24 @@ static int Install(sp<IGsiService> gsid, int argc, char** argv) {
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, &gsiSize) || gsiSize <= 0) {
+ if (!android::base::ParseInt(optarg, &params.gsiSize) || params.gsiSize <= 0) {
std::cerr << "Could not parse image size: " << optarg << std::endl;
return EX_USAGE;
}
break;
case 'u':
- if (!android::base::ParseInt(optarg, &userdataSize) || userdataSize < 0) {
+ if (!android::base::ParseInt(optarg, &params.userdataSize) ||
+ params.userdataSize < 0) {
std::cerr << "Could not parse image size: " << optarg << std::endl;
return EX_USAGE;
}
break;
case 'i':
- installDir = optarg;
+ params.installDir = optarg;
break;
case 'w':
- wipeUserdata = true;
+ params.wipeUserdata = true;
break;
case 'n':
reboot = false;
@@ -240,7 +252,7 @@ static int Install(sp<IGsiService> gsid, int argc, char** argv) {
}
}
- if (gsiSize <= 0) {
+ if (params.gsiSize <= 0) {
std::cerr << "Must specify --gsi-size." << std::endl;
return EX_USAGE;
}
@@ -258,42 +270,23 @@ static int Install(sp<IGsiService> gsid, int argc, char** argv) {
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;
- if (partition == kDefaultPartition) {
- GsiInstallParams userdataParams;
- userdataParams.installDir = installDir;
- userdataParams.name = "userdata";
- userdataParams.size = userdataSize;
- userdataParams.wipe = wipeUserdata;
- userdataParams.readOnly = false;
-
- auto status = gsid->beginGsiInstall(userdataParams, &error);
- if (!status.isOk() || error != IGsiService::INSTALL_OK) {
- std::cerr << "Could not start live image install: " << ErrorMessage(status, error)
- << "\n";
- return EX_SOFTWARE;
- }
- }
- GsiInstallParams systemParams;
- systemParams.installDir = installDir;
- systemParams.name = partition;
- systemParams.size = gsiSize;
- systemParams.wipe = true;
- systemParams.readOnly = true;
- auto status = gsid->beginGsiInstall(systemParams, &error);
+ int error;
+ auto status = gsid->beginGsiInstall(params, &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, systemParams.size, &ok);
+ status = gsid->commitGsiChunkFromStream(stream, params.gsiSize, &ok);
if (!ok) {
std::cerr << "Could not commit live image data: " << ErrorMessage(status) << "\n";
return EX_SOFTWARE;
@@ -301,7 +294,7 @@ static int Install(sp<IGsiService> gsid, int argc, char** argv) {
progress.Finish();
- status = gsid->enableGsi(true, &error);
+ status = gsid->setGsiBootable(true, &error);
if (!status.isOk() || error != IGsiService::INSTALL_OK) {
std::cerr << "Could not make live image bootable: " << ErrorMessage(status, error) << "\n";
return EX_SOFTWARE;
@@ -324,7 +317,7 @@ static int Wipe(sp<IGsiService> gsid, int argc, char** /* argv */) {
return EX_USAGE;
}
bool ok;
- auto status = gsid->removeGsi(&ok);
+ auto status = gsid->removeGsiInstall(&ok);
if (!status.isOk() || !ok) {
std::cerr << "Could not remove GSI install: " << ErrorMessage(status) << "\n";
return EX_SOFTWARE;
@@ -407,25 +400,6 @@ static int Status(sp<IGsiService> gsid, int argc, char** /* argv */) {
} else {
std::cout << "normal" << std::endl;
}
- 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;
- }
return 0;
}
@@ -477,7 +451,7 @@ static int Enable(sp<IGsiService> gsid, int argc, char** argv) {
}
int error;
- auto status = gsid->enableGsi(one_shot, &error);
+ auto status = gsid->setGsiBootable(one_shot, &error);
if (!status.isOk() || error != IGsiService::INSTALL_OK) {
std::cerr << "Error re-enabling GSI: " << ErrorMessage(status, error) << "\n";
return EX_SOFTWARE;
@@ -500,7 +474,7 @@ static int Disable(sp<IGsiService> gsid, int argc, char** /* argv */) {
}
bool ok = false;
- gsid->disableGsi(&ok);
+ gsid->disableGsiInstall(&ok);
if (!ok) {
std::cerr << "Error disabling GSI" << std::endl;
return EX_SOFTWARE;
@@ -532,11 +506,10 @@ static int usage(int /* argc */, char* argv[]) {
}
int main(int argc, char** argv) {
- android::base::InitLogging(argv, android::base::StdioLogger, android::base::DefaultAborter);
-
- android::sp<IGsiService> service = GetGsiService();
- if (!service) {
- return EX_SOFTWARE;
+ auto gsid = GetGsiService();
+ if (!gsid) {
+ std::cerr << "Could not connect to the gsid service." << std::endl;
+ return EX_NOPERM;
}
if (1 >= argc) {
@@ -552,6 +525,6 @@ int main(int argc, char** argv) {
return usage(argc, argv);
}
- int rc = iter->second(service, argc - 1, argv + 1);
+ int rc = iter->second(gsid, argc - 1, argv + 1);
return rc;
}
diff --git a/gsid.rc b/gsid.rc
index 7591c35..b0acab1 100644
--- a/gsid.rc
+++ b/gsid.rc
@@ -1,5 +1,4 @@
service gsid /system/bin/gsid
- oneshot
disabled
user root
group root system media_rw
@@ -7,12 +6,10 @@ service gsid /system/bin/gsid
on post-fs
mkdir /metadata/gsi 0771 root system
mkdir /metadata/gsi/dsu 0771 root system
- mkdir /metadata/gsi/ota 0771 root system
on post-fs-data
- mkdir /data/gsi 0700 root root encryption=None
+ mkdir /data/gsi 0700 root root
mkdir /data/gsi/dsu 0700 root root
- mkdir /data/gsi/ota 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..94e6f57 100644
--- a/include/libgsi/libgsi.h
+++ b/include/libgsi/libgsi.h
@@ -25,14 +25,8 @@ static constexpr char kGsiServiceName[] = "gsiservice";
static constexpr char kGsiBootedIndicatorFile[] = "/metadata/gsi/dsu/booted";
-static constexpr char kGsiLpNamesFile[] = "/metadata/gsi/dsu/lp_names";
-
-static constexpr char kDsuLpMetadataFile[] = "/metadata/gsi/dsu/lp_metadata";
-
static constexpr char kGsiBootedProp[] = "ro.gsid.image_running";
-static constexpr char kDsuPostfix[] = "_gsi";
-
static constexpr int kMaxBootAttempts = 1;
// Returns true if the currently running system image is a live GSI.
@@ -49,10 +43,11 @@ bool UninstallGsi();
bool DisableGsi();
// Returns true if init should attempt to boot into a live GSI image, false
-// otherwise. If false, an error message is set.
+// otherwise. If true, then the path to the liblp metadata file is set. If
+// false, an error message is set instead.
//
// This is only called by first-stage init.
-bool CanBootIntoGsi(std::string* error);
+bool CanBootIntoGsi(std::string* metadata_file, std::string* error);
// Called by first-stage init to indicate that we're about to boot into a
// GSI.
diff --git a/include/libgsi/libgsid.h b/include/libgsi/libgsid.h
deleted file mode 100644
index 46f8ef5..0000000
--- a/include/libgsi/libgsid.h
+++ /dev/null
@@ -1,27 +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 <android/gsi/IGsiService.h>
-
-namespace android {
-namespace gsi {
-
-android::sp<IGsiService> GetGsiService();
-
-} // namespace gsi
-} // namespace android
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 8dbbf4c..0000000
--- a/libfiemap/Android.bp
+++ /dev/null
@@ -1,135 +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",
- ],
-}
-
-vts_config {
- name: "VtsFiemapWriterTest",
-}
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..fe46145 100644
--- a/libgsi.cpp
+++ b/libgsi.cpp
@@ -39,7 +39,7 @@ bool IsGsiRunning() {
}
bool IsGsiInstalled() {
- return !access(kDsuInstallStatusFile, F_OK);
+ return !access(kGsiInstallStatusFile, F_OK);
}
static bool WriteAndSyncFile(const std::string& data, const std::string& file) {
@@ -53,12 +53,7 @@ static bool WriteAndSyncFile(const std::string& data, const std::string& file) {
return fsync(fd) == 0;
}
-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
- // get re-created by MarkSystemAsGsi.
- android::base::RemoveFileIfExists(kGsiBootedIndicatorFile);
-
+static bool CanBootIntoGsi(std::string* error) {
if (!IsGsiInstalled()) {
*error = "not detected";
return false;
@@ -79,7 +74,7 @@ bool CanBootIntoGsi(std::string* error) {
}
std::string new_key;
- if (!access(kDsuOneShotBootFile, F_OK)) {
+ if (!access(kGsiOneShotBootFile, F_OK)) {
// Mark the GSI as disabled. This only affects the next boot, not
// the current boot. Note that we leave the one_shot status behind.
// This is so IGsiService can still return GSI_STATE_SINGLE_BOOT
@@ -88,7 +83,7 @@ bool CanBootIntoGsi(std::string* error) {
} else {
new_key = std::to_string(attempts + 1);
}
- if (!WriteAndSyncFile(new_key, kDsuInstallStatusFile)) {
+ if (!WriteAndSyncFile(new_key, kGsiInstallStatusFile)) {
*error = "error ("s + strerror(errno) + ")";
return false;
}
@@ -102,12 +97,26 @@ bool CanBootIntoGsi(std::string* error) {
return true;
}
+bool CanBootIntoGsi(std::string* metadata_file, 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
+ // get re-created by MarkSystemAsGsi.
+ android::base::RemoveFileIfExists(kGsiBootedIndicatorFile);
+
+ if (!CanBootIntoGsi(error)) {
+ return false;
+ }
+
+ *metadata_file = kGsiLpMetadataFile;
+ return true;
+}
+
bool UninstallGsi() {
- return android::base::WriteStringToFile(kInstallStatusWipe, kDsuInstallStatusFile);
+ return android::base::WriteStringToFile(kInstallStatusWipe, kGsiInstallStatusFile);
}
bool DisableGsi() {
- return android::base::WriteStringToFile(kInstallStatusDisabled, kDsuInstallStatusFile);
+ return android::base::WriteStringToFile(kInstallStatusDisabled, kGsiInstallStatusFile);
}
bool MarkSystemAsGsi() {
@@ -115,7 +124,7 @@ bool MarkSystemAsGsi() {
}
bool GetInstallStatus(std::string* status) {
- return android::base::ReadFileToString(kDsuInstallStatusFile, status);
+ return android::base::ReadFileToString(kGsiInstallStatusFile, status);
}
bool GetBootAttempts(const std::string& boot_key, int* attempts) {
diff --git a/libgsid.cpp b/libgsid.cpp
deleted file mode 100644
index cfddb60..0000000
--- a/libgsid.cpp
+++ /dev/null
@@ -1,71 +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 <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android/gsi/IGsiService.h>
-#include <android/gsi/IGsid.h>
-#include <binder/IServiceManager.h>
-#include <libgsi/libgsi.h>
-
-namespace android {
-namespace gsi {
-
-using namespace std::chrono_literals;
-using android::sp;
-
-static sp<IGsid> GetGsid() {
- if (android::base::GetProperty("init.svc.gsid", "") != "running") {
- if (!android::base::SetProperty("ctl.start", "gsid") ||
- !android::base::WaitForProperty("init.svc.gsid", "running", 5s)) {
- LOG(ERROR) << "Unable to start gsid";
- return nullptr;
- }
- }
-
- static const int kSleepTimeMs = 50;
- static const int kTotalWaitTimeMs = 3000;
- for (int i = 0; i < kTotalWaitTimeMs / kSleepTimeMs; i++) {
- auto sm = android::defaultServiceManager();
- auto name = android::String16(kGsiServiceName);
- android::sp<android::IBinder> res = sm->checkService(name);
- if (res) {
- return android::interface_cast<IGsid>(res);
- }
- usleep(kSleepTimeMs * 1000);
- }
-
- LOG(ERROR) << "Timed out trying to start gsid";
- return nullptr;
-}
-
-sp<IGsiService> GetGsiService() {
- auto gsid = GetGsid();
- if (!gsid) {
- return nullptr;
- }
-
- sp<IGsiService> service;
- auto status = gsid->getClient(&service);
- if (!status.isOk() || !service) {
- LOG(ERROR) << "Error acquiring IGsid: " << status.exceptionMessage().string();
- return nullptr;
- }
- return service;
-}
-
-} // namespace gsi
-} // namespace android
diff --git a/tests/Android.bp b/tests/Android.bp
deleted file mode 100644
index e162dfb..0000000
--- a/tests/Android.bp
+++ /dev/null
@@ -1,66 +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.
-//
-
-cc_defaults {
- name: "gsi_boot_defaults",
- shared_libs: [
- "libbase",
- "libcutils",
- "libhardware",
- "libhidlbase",
- "liblog",
- "libutils",
- ],
- static_libs: [
- "libext4_utils",
- "libfstab",
- "android.hardware.weaver@1.0",
- ],
- srcs: [
- "boot_tests.cpp",
- ],
-}
-
-cc_test {
- name: "gsi_boot_test",
- defaults: ["gsi_boot_defaults"],
-}
-
-cc_test {
- name: "vts_gsi_boot_test",
- defaults: ["gsi_boot_defaults"],
- test_suites: ["vts-core"],
- auto_gen_config: true,
- test_min_api_level: 29,
-}
-
-java_test_host {
- name: "DSUEndtoEndTest",
- srcs: ["DSUEndtoEndTest.java"],
- libs: ["tradefed"],
- test_config: "dsu-test.xml",
- test_suites: ["general-tests"],
-}
-
-android_test {
- name: "LockScreenAutomation",
- srcs: ["LockScreenAutomation.java"],
- libs: ["junit", "android.test.base.stubs"],
- static_libs: ["androidx.test.uiautomator"],
- certificate: "platform",
- manifest: "AndroidManifest.xml",
- test_suites: ["general-tests"],
-}
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
deleted file mode 100644
index 19d0c53..0000000
--- a/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 Google Inc.
-
- 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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.google.android.lockscreenautomation"
- android:sharedUserId="android.uid.system"
- >
-
- <uses-sdk android:minSdkVersion="22" android:targetSdkVersion="28"/>
-
- <application debuggable="true">
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation android:name="androidx.test.uiautomator.UiAutomatorInstrumentationTestRunner"
- android:targetPackage="com.google.android.lockscreenautomation"
- android:label="Lock Screen Automation"/>
-</manifest>
diff --git a/tests/DSUEndtoEndTest.java b/tests/DSUEndtoEndTest.java
deleted file mode 100644
index 175ca54..0000000
--- a/tests/DSUEndtoEndTest.java
+++ /dev/null
@@ -1,168 +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 com.android.tests.dsu;
-
-import com.android.tradefed.build.BuildRetrievalError;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.build.IDeviceBuildInfo;
-import com.android.tradefed.config.Option;
-import com.android.tradefed.config.Option.Importance;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-import com.android.tradefed.util.CommandResult;
-import com.android.tradefed.util.ZipUtil2;
-
-import org.apache.commons.compress.archivers.zip.ZipFile;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.Process;
-import java.lang.Runtime;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Test Dynamic System Updates by booting in and out of a supplied system image
- */
-@RunWith(DeviceJUnit4ClassRunner.class)
-public class DSUEndtoEndTest extends BaseHostJUnit4Test {
- private static final long kDefaultUserdataSize = 4L * 1024 * 1024 * 1024;
- private static final String APK = "LockScreenAutomation.apk";
- private static final String PACKAGE = "com.google.android.lockscreenautomation";
- private static final String UI_AUTOMATOR_INSTRUMENTATION_RUNNER =
- "androidx.test.uiautomator.UiAutomatorInstrumentationTestRunner";
- private static final String CLASS = "LockScreenAutomation";
- private static final String SIMG2IMG_PATH = "bin/simg2img";
-
- // Example: atest -v DSUEndtoEndTest -- --test-arg \
- // com.android.tradefed.testtype.HostTest:set-option:system_image_path:/full/path/to/system.img
- @Option(name="system_image_path",
- shortName='s',
- description="full path to the system image to use. If not specified, attempt " +
- "to download the image from the test infrastructure",
- importance=Importance.ALWAYS)
- private String mSystemImagePath;
-
- private File mUnsparseSystemImage;
-
- @After
- public void teardown() throws Exception {
- uninstallPackage(PACKAGE);
- if (mUnsparseSystemImage != null) {
- mUnsparseSystemImage.delete();
- }
- }
-
- @Test
- public void testDSU() throws Exception {
- 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 otaTools = buildInfo.getFile("otatools.zip");
- File tempdir = ZipUtil2.extractZipToTemp(otaTools, "otatools");
- simg2imgPath = new File(tempdir, SIMG2IMG_PATH).getAbsolutePath();
- }
- File gsi = new File(mSystemImagePath);
- Assert.assertTrue("not a valid file", gsi.isFile());
- String[] cmd = {simg2imgPath, mSystemImagePath, mSystemImagePath + ".raw"};
- Process p = Runtime.getRuntime().exec(cmd);
- p.waitFor();
- if (p.exitValue() == 0) {
- mUnsparseSystemImage = new File(mSystemImagePath + ".raw");
- gsi = mUnsparseSystemImage;
- }
-
- boolean wasRoot = getDevice().isAdbRoot();
- if (!wasRoot)
- Assert.assertTrue("Test requires root", getDevice().enableAdbRoot());
-
- expectGsiStatus("normal");
-
- installPackage(APK);
- String method = "setPin";
- String testClass = PACKAGE + "." + CLASS;
- String testMethod = testClass + "." + method;
- Assert.assertTrue(testMethod + " failed.",
- runDeviceTests(UI_AUTOMATOR_INSTRUMENTATION_RUNNER, PACKAGE, testClass, method));
-
- // 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 +
- " --gsi-size " + gsi.length() + " && sleep 10000000", gsi, null, 10, TimeUnit.MINUTES, 1);
- getDevice().waitForDeviceAvailable();
- getDevice().enableAdbRoot();
-
- expectGsiStatus("running");
-
- rebootAndUnlock();
-
- expectGsiStatus("installed");
-
- CommandResult result = getDevice().executeShellV2Command("gsi_tool enable");
- Assert.assertEquals("gsi_tool enable failed", 0, result.getExitCode().longValue());
-
- getDevice().reboot();
-
- expectGsiStatus("running");
-
- getDevice().reboot();
-
- expectGsiStatus("running");
-
- getDevice().executeShellV2Command("gsi_tool wipe");
-
- rebootAndUnlock();
-
- expectGsiStatus("normal");
-
- method = "removePin";
- testClass = PACKAGE + "." + CLASS;
- testMethod = testClass + "." + method;
- Assert.assertTrue(testMethod + " failed.",
- runDeviceTests(UI_AUTOMATOR_INSTRUMENTATION_RUNNER, PACKAGE, testClass, method));
-
- if (wasRoot) {
- getDevice().enableAdbRoot();
- }
- }
-
- private void expectGsiStatus(String expected) throws Exception {
- CommandResult result = getDevice().executeShellV2Command("gsi_tool status");
- String status = result.getStdout().split("\n", 2)[0].trim();
- Assert.assertEquals("Device not in expected DSU state", expected, status);
- }
-
- private void rebootAndUnlock() throws Exception {
- getDevice().rebootUntilOnline();
- getDevice().executeShellV2Command("input keyevent 224"); // KeyEvent.KEYCODE_WAKEUP
- getDevice().executeShellV2Command("wm dismiss-keyguard");
- getDevice().executeShellV2Command("input keyevent 7"); // KeyEvent.KEYCODE_0
- getDevice().executeShellV2Command("input keyevent 7");
- getDevice().executeShellV2Command("input keyevent 7");
- getDevice().executeShellV2Command("input keyevent 7");
- getDevice().executeShellV2Command("input keyevent 66"); // KeyEvent.KEYCODE_ENTER
- }
-}
-
diff --git a/tests/LockScreenAutomation.java b/tests/LockScreenAutomation.java
deleted file mode 100644
index afefa1c..0000000
--- a/tests/LockScreenAutomation.java
+++ /dev/null
@@ -1,151 +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 com.google.android.lockscreenautomation;
-
-import org.junit.Assert;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.provider.Settings;
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.BySelector;
-import androidx.test.uiautomator.UiAutomatorTestCase;
-import androidx.test.uiautomator.UiDevice;
-import androidx.test.uiautomator.UiObject2;
-import androidx.test.uiautomator.UiObjectNotFoundException;
-import androidx.test.uiautomator.UiSelector;
-import androidx.test.uiautomator.Until;
-import android.view.KeyEvent;
-
-/**
- * Methods for configuring lock screen settings
- */
-public class LockScreenAutomation extends UiAutomatorTestCase {
-
- private static final String SETTINGS_PACKAGE = "com.android.settings";
-
- private static final long TIMEOUT = 2000L;
-
- private Context mContext;
- private UiDevice mDevice;
-
- public void setPin() throws Exception {
- mContext = getInstrumentation().getContext();
- mDevice = UiDevice.getInstance(getInstrumentation());
-
- mDevice.wakeUp();
- mDevice.pressKeyCode(KeyEvent.KEYCODE_MENU);
- mDevice.waitForIdle(TIMEOUT);
- launchLockScreenSettings();
-
- PackageManager pm = mContext.getPackageManager();
- Resources res = pm.getResourcesForApplication(SETTINGS_PACKAGE);
-
- int resId = res.getIdentifier("unlock_set_unlock_pin_title", "string", SETTINGS_PACKAGE);
- findAndClick(By.text(res.getString(resId)));
- mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
- mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
- mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
- mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
- mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
- mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
- mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
- mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
- mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
- mDevice.pressEnter();
- mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
-
- // Re-enter PIN
- mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
- mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
- mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
- mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
- mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
- mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
- mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
- mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
- mDevice.pressEnter();
-
- findAndClick(By.res(SETTINGS_PACKAGE, "redact_sensitive"));
- mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
- findAndClick(By.clazz("android.widget.Button"));
- mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
- }
-
- public void unlock() throws Exception {
- mContext = getInstrumentation().getContext();
- mDevice = UiDevice.getInstance(getInstrumentation());
- mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
- mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
- mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
- mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
- mDevice.pressKeyCode(KeyEvent.KEYCODE_ENTER);
- }
-
- public void removePin() throws Exception {
- mContext = getInstrumentation().getContext();
- mDevice = UiDevice.getInstance(getInstrumentation());
-
- mDevice.wakeUp();
- mDevice.pressKeyCode(KeyEvent.KEYCODE_MENU);
- mDevice.waitForIdle(TIMEOUT);
- launchLockScreenSettings();
-
- PackageManager pm = mContext.getPackageManager();
- Resources res = pm.getResourcesForApplication(SETTINGS_PACKAGE);
-
- mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
- mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
- mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
- mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
- mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
- mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
- mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
- mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
- mDevice.pressEnter();
- mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
-
- int resId = res.getIdentifier("unlock_set_unlock_off_title", "string", SETTINGS_PACKAGE);
- findAndClick(By.text(res.getString(resId)));
- mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
-
- findAndClick(By.res("android", "button1"));
- mDevice.waitForIdle(TIMEOUT);
- }
-
- private void findAndClick(BySelector selector)
- {
- for (int i = 0; i < 3; i++) {
- mDevice.wait(Until.findObject(selector), TIMEOUT);
- UiObject2 obj = mDevice.findObject(selector);
- if (obj != null) {
- obj.click();
- return;
- }
- }
- Assert.fail("Could not find and click " + selector);
- }
-
- private void launchLockScreenSettings() {
- final Intent intent = new Intent().setClassName(SETTINGS_PACKAGE, "com.android.settings.password.ChooseLockGeneric");
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- mContext.startActivity(intent);
- mDevice.wait(Until.hasObject(By.pkg(SETTINGS_PACKAGE).depth(0)), TIMEOUT);
- }
-}
diff --git a/tests/dsu-test.xml b/tests/dsu-test.xml
deleted file mode 100644
index bab2dd2..0000000
--- a/tests/dsu-test.xml
+++ /dev/null
@@ -1,22 +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 DSU test case">
- <option name="test-suite-tag" value="dsu-tests" />
- <test class="com.android.tradefed.testtype.HostTest" >
- <option name="jar" value="DSUEndtoEndTest.jar" />
- </test>
-</configuration>
-