Merge changes Id77b87bb,I56a15c80
* changes:
trusty: fuzz: make utils lib use libtrusty_test
trusty: Add libtrusty_test
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 5280121..b3e81b0 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -299,11 +299,8 @@
process_info->abort_msg_address = crash_info->data.s.abort_msg_address;
*siginfo = crash_info->data.s.siginfo;
if (signal_has_si_addr(siginfo)) {
- // Make a copy of the ucontext field because otherwise it is not aligned enough (due to
- // being in a packed struct) and clang complains about that.
- ucontext_t ucontext = crash_info->data.s.ucontext;
process_info->has_fault_address = true;
- process_info->fault_address = get_fault_address(siginfo, &ucontext);
+ process_info->fault_address = reinterpret_cast<uintptr_t>(siginfo->si_addr);
}
regs->reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(),
&crash_info->data.s.ucontext));
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 5565e8b..e5af425 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -34,7 +34,6 @@
#include <android/set_abort_message.h>
#include <bionic/malloc.h>
#include <bionic/mte.h>
-#include <bionic/mte_kernel.h>
#include <bionic/reserved_signals.h>
#include <android-base/cmsg.h>
@@ -386,16 +385,6 @@
#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
static void SetTagCheckingLevelSync() {
- int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
- if (tagged_addr_ctrl < 0) {
- abort();
- }
-
- tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | PR_MTE_TCF_SYNC;
- if (prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) != 0) {
- abort();
- }
-
HeapTaggingLevel heap_tagging_level = M_HEAP_TAGGING_LEVEL_SYNC;
if (!android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level))) {
abort();
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 121a074..85ffc98 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -167,7 +167,7 @@
* mutex is being held, so we don't want to use any libc functions that
* could allocate memory or hold a lock.
*/
-static void log_signal_summary(const siginfo_t* info, const ucontext_t* ucontext) {
+static void log_signal_summary(const siginfo_t* info) {
char thread_name[MAX_TASK_NAME_LEN + 1]; // one more for termination
if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
strcpy(thread_name, "<name unknown>");
@@ -186,8 +186,7 @@
// Many signals don't have an address or sender.
char addr_desc[32] = ""; // ", fault addr 0x1234"
if (signal_has_si_addr(info)) {
- async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p",
- reinterpret_cast<void*>(get_fault_address(info, ucontext)));
+ async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr);
}
pid_t self_pid = __getpid();
char sender_desc[32] = {}; // " from pid 1234, uid 666"
@@ -544,7 +543,7 @@
return;
}
- log_signal_summary(info, ucontext);
+ log_signal_summary(info);
debugger_thread_info thread_info = {
.crashing_tid = __gettid(),
@@ -638,5 +637,11 @@
// Use the alternate signal stack if available so we can catch stack overflows.
action.sa_flags |= SA_ONSTACK;
+
+#define SA_EXPOSE_TAGBITS 0x00000800
+ // Request that the kernel set tag bits in the fault address. This is necessary for diagnosing MTE
+ // faults.
+ action.sa_flags |= SA_EXPOSE_TAGBITS;
+
debuggerd_register_handlers(&action);
}
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index 76155b1..29fb9a4 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -91,6 +91,4 @@
const char* get_signame(const siginfo_t*);
const char* get_sigcode(const siginfo_t*);
-uintptr_t get_fault_address(const siginfo_t* siginfo, const ucontext_t* ucontext);
-
#endif // _DEBUGGERD_UTILITY_H
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 4e6df09..d7067ca 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -451,40 +451,3 @@
_LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(i).c_str());
}
}
-
-#if defined(__aarch64__)
-#define FAR_MAGIC 0x46415201
-
-struct far_context {
- struct _aarch64_ctx head;
- __u64 far;
-};
-#endif
-
-uintptr_t get_fault_address(const siginfo_t* siginfo, const ucontext_t* ucontext) {
- (void)ucontext;
-#if defined(__aarch64__)
- // This relies on a kernel patch:
- // https://patchwork.kernel.org/patch/11435077/
- // that hasn't been accepted into the kernel yet. TODO(pcc): Update this to
- // use the official interface once it lands.
- auto* begin = reinterpret_cast<const char*>(ucontext->uc_mcontext.__reserved);
- auto* end = begin + sizeof(ucontext->uc_mcontext.__reserved);
- auto* ptr = begin;
- while (1) {
- auto* ctx = reinterpret_cast<const _aarch64_ctx*>(ptr);
- if (ctx->magic == 0) {
- break;
- }
- if (ctx->magic == FAR_MAGIC) {
- auto* far_ctx = reinterpret_cast<const far_context*>(ctx);
- return far_ctx->far;
- }
- ptr += ctx->size;
- if (ctx->size % sizeof(void*) != 0 || ptr < begin || ptr >= end) {
- break;
- }
- }
-#endif
- return reinterpret_cast<uintptr_t>(siginfo->si_addr);
-}
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 1bf4c9c..333ca50 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -67,7 +67,7 @@
if ((partition + device->GetCurrentSlot()) == partition_name) {
mount_metadata.emplace();
- fs_mgr_overlayfs_teardown(entry.mount_point.c_str());
+ android::fs_mgr::TeardownAllOverlayForMountPoint(entry.mount_point);
}
}
}
@@ -194,7 +194,7 @@
if (!FlashPartitionTable(super_name, *new_metadata.get())) {
return device->WriteFail("Unable to flash new partition table");
}
- fs_mgr_overlayfs_teardown();
+ android::fs_mgr::TeardownAllOverlayForMountPoint();
sync();
return device->WriteOkay("Successfully flashed partition table");
}
@@ -234,7 +234,7 @@
if (!UpdateAllPartitionMetadata(device, super_name, *new_metadata.get())) {
return device->WriteFail("Unable to write new partition table");
}
- fs_mgr_overlayfs_teardown();
+ android::fs_mgr::TeardownAllOverlayForMountPoint();
sync();
return device->WriteOkay("Successfully updated partition table");
}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 4bf791e..62f6ac7 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -454,6 +454,8 @@
" --skip-reboot Don't reboot device after flashing.\n"
" --disable-verity Sets disable-verity when flashing vbmeta.\n"
" --disable-verification Sets disable-verification when flashing vbmeta.\n"
+ " --fs-options=OPTION[,OPTION]\n"
+ " Enable filesystem features. OPTION supports casefold, projid, compress\n"
#if !defined(_WIN32)
" --wipe-and-use-fbe Enable file-based encryption, wiping userdata.\n"
#endif
@@ -1581,7 +1583,7 @@
static void fb_perform_format(
const std::string& partition, int skip_if_not_supported,
const std::string& type_override, const std::string& size_override,
- const std::string& initial_dir) {
+ const std::string& initial_dir, const unsigned fs_options) {
std::string partition_type, partition_size;
struct fastboot_buffer buf;
@@ -1644,7 +1646,7 @@
logicalBlkSize = fb_get_flash_block_size("logical-block-size");
if (fs_generator_generate(gen, output.path, size, initial_dir,
- eraseBlkSize, logicalBlkSize)) {
+ eraseBlkSize, logicalBlkSize, fs_options)) {
die("Cannot generate image for %s", partition.c_str());
}
@@ -1778,6 +1780,7 @@
bool skip_secondary = false;
bool set_fbe_marker = false;
bool force_flash = false;
+ unsigned fs_options = 0;
int longindex;
std::string slot_override;
std::string next_active;
@@ -1795,6 +1798,7 @@
{"disable-verification", no_argument, 0, 0},
{"disable-verity", no_argument, 0, 0},
{"force", no_argument, 0, 0},
+ {"fs-options", required_argument, 0, 0},
{"header-version", required_argument, 0, 0},
{"help", no_argument, 0, 'h'},
{"kernel-offset", required_argument, 0, 0},
@@ -1834,6 +1838,8 @@
g_disable_verity = true;
} else if (name == "force") {
force_flash = true;
+ } else if (name == "fs-options") {
+ fs_options = ParseFsOption(optarg);
} else if (name == "header-version") {
g_boot_img_hdr.header_version = strtoul(optarg, nullptr, 0);
} else if (name == "dtb") {
@@ -1990,7 +1996,7 @@
std::string partition = next_arg(&args);
auto format = [&](const std::string& partition) {
- fb_perform_format(partition, 0, type_override, size_override, "");
+ fb_perform_format(partition, 0, type_override, size_override, "", fs_options);
};
do_for_partitions(partition, slot_override, format, true);
} else if (command == "signature") {
@@ -2180,10 +2186,10 @@
if (partition == "userdata" && set_fbe_marker) {
fprintf(stderr, "setting FBE marker on initial userdata...\n");
std::string initial_userdata_dir = create_fbemarker_tmpdir();
- fb_perform_format(partition, 1, partition_type, "", initial_userdata_dir);
+ fb_perform_format(partition, 1, partition_type, "", initial_userdata_dir, fs_options);
delete_fbemarker_tmpdir(initial_userdata_dir);
} else {
- fb_perform_format(partition, 1, partition_type, "", "");
+ fb_perform_format(partition, 1, partition_type, "", "", fs_options);
}
}
}
@@ -2233,3 +2239,23 @@
}
hdr->SetOsVersion(major, minor, patch);
}
+
+unsigned FastBootTool::ParseFsOption(const char* arg) {
+ unsigned fsOptions = 0;
+
+ std::vector<std::string> options = android::base::Split(arg, ",");
+ if (options.size() < 1)
+ syntax_error("bad options: %s", arg);
+
+ for (size_t i = 0; i < options.size(); ++i) {
+ if (options[i] == "casefold")
+ fsOptions |= (1 << FS_OPT_CASEFOLD);
+ else if (options[i] == "projid")
+ fsOptions |= (1 << FS_OPT_PROJID);
+ else if (options[i] == "compress")
+ fsOptions |= (1 << FS_OPT_COMPRESS);
+ else
+ syntax_error("unsupported options: %s", options[i].c_str());
+ }
+ return fsOptions;
+}
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 9f18253..c23793a 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -34,4 +34,5 @@
void ParseOsPatchLevel(boot_img_hdr_v1*, const char*);
void ParseOsVersion(boot_img_hdr_v1*, const char*);
+ unsigned ParseFsOption(const char*);
};
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index 8c0aa6b..8addcb6 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -113,7 +113,7 @@
static int generate_ext4_image(const char* fileName, long long partSize,
const std::string& initial_dir, unsigned eraseBlkSize,
- unsigned logicalBlkSize) {
+ unsigned logicalBlkSize, const unsigned fsOptions) {
static constexpr int block_size = 4096;
const std::string exec_dir = android::base::GetExecutableDirectory();
@@ -137,6 +137,12 @@
mke2fs_args.push_back(ext_attr.c_str());
mke2fs_args.push_back("-O");
mke2fs_args.push_back("uninit_bg");
+
+ if (fsOptions & (1 << FS_OPT_PROJID)) {
+ mke2fs_args.push_back("-I");
+ mke2fs_args.push_back("512");
+ }
+
mke2fs_args.push_back(fileName);
std::string size_str = std::to_string(partSize / block_size);
@@ -162,9 +168,9 @@
return exec_cmd(e2fsdroid_args[0], e2fsdroid_args.data(), nullptr);
}
-static int generate_f2fs_image(const char* fileName, long long partSize, const std::string& initial_dir,
- unsigned /* unused */, unsigned /* unused */)
-{
+static int generate_f2fs_image(const char* fileName, long long partSize,
+ const std::string& initial_dir, unsigned /* unused */,
+ unsigned /* unused */, const unsigned fsOptions) {
const std::string exec_dir = android::base::GetExecutableDirectory();
const std::string mkf2fs_path = exec_dir + "/make_f2fs";
std::vector<const char*> mkf2fs_args = {mkf2fs_path.c_str()};
@@ -174,6 +180,26 @@
mkf2fs_args.push_back(size_str.c_str());
mkf2fs_args.push_back("-g");
mkf2fs_args.push_back("android");
+
+ if (fsOptions & (1 << FS_OPT_PROJID)) {
+ mkf2fs_args.push_back("-O");
+ mkf2fs_args.push_back("project_quota,extra_attr");
+ }
+
+ if (fsOptions & (1 << FS_OPT_CASEFOLD)) {
+ mkf2fs_args.push_back("-O");
+ mkf2fs_args.push_back("casefold");
+ mkf2fs_args.push_back("-C");
+ mkf2fs_args.push_back("utf8");
+ }
+
+ if (fsOptions & (1 << FS_OPT_COMPRESS)) {
+ mkf2fs_args.push_back("-O");
+ mkf2fs_args.push_back("compression");
+ mkf2fs_args.push_back("-O");
+ mkf2fs_args.push_back("extra_attr");
+ }
+
mkf2fs_args.push_back(fileName);
mkf2fs_args.push_back(nullptr);
@@ -198,7 +224,7 @@
//returns 0 or error value
int (*generate)(const char* fileName, long long partSize, const std::string& initial_dir,
- unsigned eraseBlkSize, unsigned logicalBlkSize);
+ unsigned eraseBlkSize, unsigned logicalBlkSize, const unsigned fsOptions);
} generators[] = {
{ "ext4", generate_ext4_image},
@@ -215,7 +241,7 @@
}
int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,
- const std::string& initial_dir, unsigned eraseBlkSize, unsigned logicalBlkSize)
-{
- return gen->generate(fileName, partSize, initial_dir, eraseBlkSize, logicalBlkSize);
+ const std::string& initial_dir, unsigned eraseBlkSize,
+ unsigned logicalBlkSize, const unsigned fsOptions) {
+ return gen->generate(fileName, partSize, initial_dir, eraseBlkSize, logicalBlkSize, fsOptions);
}
diff --git a/fastboot/fs.h b/fastboot/fs.h
index 331100d..f832938 100644
--- a/fastboot/fs.h
+++ b/fastboot/fs.h
@@ -5,6 +5,13 @@
struct fs_generator;
+enum FS_OPTION {
+ FS_OPT_CASEFOLD,
+ FS_OPT_PROJID,
+ FS_OPT_COMPRESS,
+};
+
const struct fs_generator* fs_get_generator(const std::string& fs_type);
int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,
- const std::string& initial_dir, unsigned eraseBlkSize = 0, unsigned logicalBlkSize = 0);
+ const std::string& initial_dir, unsigned eraseBlkSize = 0,
+ unsigned logicalBlkSize = 0, unsigned fsOptions = 0);
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 616a06f..42459ec 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -591,7 +591,7 @@
FstabEntry userdata;
if (FstabEntry* entry = GetEntryForMountPoint(fstab, "/data")) {
userdata = *entry;
- userdata.blk_device = "userdata_gsi";
+ userdata.blk_device = android::gsi::kDsuUserdata;
userdata.fs_mgr_flags.logical = true;
userdata.fs_mgr_flags.formattable = true;
if (!userdata.metadata_key_dir.empty()) {
@@ -611,7 +611,11 @@
continue;
}
// userdata has been handled
- if (StartsWith(partition, "user")) {
+ if (partition == android::gsi::kDsuUserdata) {
+ continue;
+ }
+ // scratch is handled by fs_mgr_overlayfs
+ if (partition == android::gsi::kDsuScratch) {
continue;
}
// dsu_partition_name = corresponding_partition_name + kDsuPostfix
@@ -688,11 +692,18 @@
return false;
}
if (!is_proc_mounts && !access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
+ // This is expected to fail if host is android Q, since Q doesn't
+ // support DSU slotting. The DSU "active" indicator file would be
+ // non-existent or empty if DSU is enabled within the guest system.
+ // In that case, just use the default slot name "dsu".
std::string dsu_slot;
if (!android::gsi::GetActiveDsu(&dsu_slot)) {
- PERROR << __FUNCTION__ << "(): failed to get active dsu slot";
- return false;
+ PWARNING << __FUNCTION__ << "(): failed to get active dsu slot";
}
+ if (dsu_slot.empty()) {
+ dsu_slot = "dsu";
+ }
+
std::string lp_names;
ReadFileToString(gsi::kGsiLpNamesFile, &lp_names);
TransformFstabForDsu(fstab, dsu_slot, Split(lp_names, ","));
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index a7704de..388c296 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -113,8 +113,11 @@
namespace android {
namespace fs_mgr {
-void MapScratchPartitionIfNeeded(Fstab*,
- const std::function<bool(const std::set<std::string>&)>&) {}
+void MapScratchPartitionIfNeeded(Fstab*, const std::function<bool(const std::set<std::string>&)>&) {
+}
+
+void TeardownAllOverlayForMountPoint(const std::string&) {}
+
} // namespace fs_mgr
} // namespace android
@@ -166,11 +169,34 @@
(vst.f_bfree * vst.f_bsize) >= kSizeThreshold;
}
+bool fs_mgr_in_recovery() {
+ // Check the existence of recovery binary instead of using the compile time
+ // macro, because first-stage-init is compiled with __ANDROID_RECOVERY__
+ // defined, albeit not in recovery. More details: system/core/init/README.md
+ return fs_mgr_access("/system/bin/recovery");
+}
+
+bool fs_mgr_is_dsu_running() {
+ // Since android::gsi::CanBootIntoGsi() or android::gsi::MarkSystemAsGsi() is
+ // never called in recovery, the return value of android::gsi::IsGsiRunning()
+ // is not well-defined. In this case, just return false as being in recovery
+ // implies not running a DSU system.
+ if (fs_mgr_in_recovery()) return false;
+ auto saved_errno = errno;
+ auto ret = android::gsi::IsGsiRunning();
+ errno = saved_errno;
+ return ret;
+}
+
const auto kPhysicalDevice = "/dev/block/by-name/"s;
constexpr char kScratchImageMetadata[] = "/metadata/gsi/remount/lp_metadata";
// Note: this is meant only for recovery/first-stage init.
bool ScratchIsOnData() {
+ // The scratch partition of DSU is managed by gsid.
+ if (fs_mgr_is_dsu_running()) {
+ return false;
+ }
return fs_mgr_access(kScratchImageMetadata);
}
@@ -464,6 +490,12 @@
// umount and delete kScratchMountPoint storage if we have logical partitions
if (overlay != kScratchMountPoint) return true;
+ // Validation check.
+ if (fs_mgr_is_dsu_running()) {
+ LERROR << "Destroying DSU scratch is not allowed.";
+ return false;
+ }
+
auto save_errno = errno;
if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
fs_mgr_overlayfs_umount_scratch();
@@ -512,10 +544,13 @@
}
bool fs_mgr_overlayfs_teardown_one(const std::string& overlay, const std::string& mount_point,
- bool* change) {
+ bool* change, bool* should_destroy_scratch = nullptr) {
const auto top = overlay + kOverlayTopDir;
- if (!fs_mgr_access(top)) return fs_mgr_overlayfs_teardown_scratch(overlay, change);
+ if (!fs_mgr_access(top)) {
+ if (should_destroy_scratch) *should_destroy_scratch = true;
+ return true;
+ }
auto cleanup_all = mount_point.empty();
const auto partition_name = android::base::Basename(mount_point);
@@ -571,7 +606,7 @@
PERROR << "rmdir " << top;
}
}
- if (cleanup_all) ret &= fs_mgr_overlayfs_teardown_scratch(overlay, change);
+ if (should_destroy_scratch) *should_destroy_scratch = cleanup_all;
return ret;
}
@@ -881,12 +916,29 @@
return "";
}
+// Note: The scratch partition of DSU is managed by gsid, and should be initialized during
+// first-stage-mount. Just check if the DM device for DSU scratch partition is created or not.
+static std::string GetDsuScratchDevice() {
+ auto& dm = DeviceMapper::Instance();
+ std::string device;
+ if (dm.GetState(android::gsi::kDsuScratch) != DmDeviceState::INVALID &&
+ dm.GetDmDevicePathByName(android::gsi::kDsuScratch, &device)) {
+ return device;
+ }
+ return "";
+}
+
// This returns the scratch device that was detected during early boot (first-
// stage init). If the device was created later, for example during setup for
// the adb remount command, it can return an empty string since it does not
// query ImageManager. (Note that ImageManager in first-stage init will always
// use device-mapper, since /data is not available to use loop devices.)
static std::string GetBootScratchDevice() {
+ // Note: fs_mgr_is_dsu_running() always returns false in recovery or fastbootd.
+ if (fs_mgr_is_dsu_running()) {
+ return GetDsuScratchDevice();
+ }
+
auto& dm = DeviceMapper::Instance();
// If there is a scratch partition allocated in /data or on super, we
@@ -1045,7 +1097,7 @@
static bool CreateScratchOnData(std::string* scratch_device, bool* partition_exists, bool* change) {
*partition_exists = false;
- *change = false;
+ if (change) *change = false;
auto images = IImageManager::Open("remount", 10s);
if (!images) {
@@ -1065,7 +1117,7 @@
return false;
}
- *change = true;
+ if (change) *change = true;
// Note: calling RemoveDisabledImages here ensures that we do not race with
// clean_scratch_files and accidentally try to map an image that will be
@@ -1108,6 +1160,14 @@
bool fs_mgr_overlayfs_create_scratch(const Fstab& fstab, std::string* scratch_device,
bool* partition_exists, bool* change) {
+ // Use the DSU scratch device managed by gsid if within a DSU system.
+ if (fs_mgr_is_dsu_running()) {
+ *scratch_device = GetDsuScratchDevice();
+ *partition_exists = !scratch_device->empty();
+ *change = false;
+ return *partition_exists;
+ }
+
// Try a physical partition first.
*scratch_device = GetPhysicalScratchDevice();
if (!scratch_device->empty() && fs_mgr_rw_access(*scratch_device)) {
@@ -1166,12 +1226,8 @@
bool fs_mgr_overlayfs_invalid() {
if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return true;
- // in recovery, fastbootd, or gsi mode, not allowed!
- if (fs_mgr_access("/system/bin/recovery")) return true;
- auto save_errno = errno;
- auto ret = android::gsi::IsGsiRunning();
- errno = save_errno;
- return ret;
+ // in recovery or fastbootd, not allowed!
+ return fs_mgr_in_recovery();
}
} // namespace
@@ -1314,6 +1370,8 @@
return ret;
}
+// Note: This function never returns the DSU scratch device in recovery or fastbootd,
+// because the DSU scratch is created in the first-stage-mount, which is not run in recovery.
static bool EnsureScratchMapped(std::string* device, bool* mapped) {
*mapped = false;
*device = GetBootScratchDevice();
@@ -1321,6 +1379,11 @@
return true;
}
+ if (!fs_mgr_in_recovery()) {
+ errno = EINVAL;
+ return false;
+ }
+
auto partition_name = android::base::Basename(kScratchMountPoint);
// Check for scratch on /data first, before looking for a modified super
@@ -1362,10 +1425,27 @@
return true;
}
-static void UnmapScratchDevice() {
- // This should only be reachable in recovery, where scratch is not
- // automatically mapped and therefore can be unmapped.
- DestroyLogicalPartition(android::base::Basename(kScratchMountPoint));
+// This should only be reachable in recovery, where DSU scratch is not
+// automatically mapped.
+static bool MapDsuScratchDevice(std::string* device) {
+ std::string dsu_slot;
+ if (!android::gsi::IsGsiInstalled() || !android::gsi::GetActiveDsu(&dsu_slot) ||
+ dsu_slot.empty()) {
+ // Nothing to do if no DSU installation present.
+ return false;
+ }
+
+ auto images = IImageManager::Open("dsu/" + dsu_slot, 10s);
+ if (!images || !images->BackingImageExists(android::gsi::kDsuScratch)) {
+ // Nothing to do if DSU scratch device doesn't exist.
+ return false;
+ }
+
+ images->UnmapImageDevice(android::gsi::kDsuScratch);
+ if (!images->MapImageDevice(android::gsi::kDsuScratch, 10s, device)) {
+ return false;
+ }
+ return true;
}
// Returns false if teardown not permitted, errno set to last error.
@@ -1377,21 +1457,27 @@
// If scratch exists, but is not mounted, lets gain access to clean
// specific override entries.
auto mount_scratch = false;
- bool unmap = false;
if ((mount_point != nullptr) && !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
- std::string scratch_device;
- if (EnsureScratchMapped(&scratch_device, &unmap)) {
+ std::string scratch_device = GetBootScratchDevice();
+ if (!scratch_device.empty()) {
mount_scratch = fs_mgr_overlayfs_mount_scratch(scratch_device,
fs_mgr_overlayfs_scratch_mount_type());
}
}
+ bool should_destroy_scratch = false;
for (const auto& overlay_mount_point : kOverlayMountPoints) {
ret &= fs_mgr_overlayfs_teardown_one(
- overlay_mount_point, mount_point ? fs_mgr_mount_point(mount_point) : "", change);
+ overlay_mount_point, mount_point ? fs_mgr_mount_point(mount_point) : "", change,
+ overlay_mount_point == kScratchMountPoint ? &should_destroy_scratch : nullptr);
+ }
+ // Do not attempt to destroy DSU scratch if within a DSU system,
+ // because DSU scratch partition is managed by gsid.
+ if (should_destroy_scratch && !fs_mgr_is_dsu_running()) {
+ ret &= fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, change);
}
if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
// After obligatory teardown to make sure everything is clean, but if
- // we didn't want overlayfs in the the first place, we do not want to
+ // we didn't want overlayfs in the first place, we do not want to
// waste time on a reboot (or reboot request message).
if (change) *change = false;
}
@@ -1405,9 +1491,6 @@
if (mount_scratch) {
fs_mgr_overlayfs_umount_scratch();
}
- if (unmap) {
- UnmapScratchDevice();
- }
return ret;
}
@@ -1475,6 +1558,54 @@
}
}
+void TeardownAllOverlayForMountPoint(const std::string& mount_point) {
+ if (!fs_mgr_in_recovery()) {
+ LERROR << __FUNCTION__ << "(): must be called within recovery.";
+ return;
+ }
+
+ // Empty string means teardown everything.
+ const std::string teardown_dir = mount_point.empty() ? "" : fs_mgr_mount_point(mount_point);
+ constexpr bool* ignore_change = nullptr;
+
+ // Teardown legacy overlay mount points that's not backed by a scratch device.
+ for (const auto& overlay_mount_point : kOverlayMountPoints) {
+ if (overlay_mount_point == kScratchMountPoint) {
+ continue;
+ }
+ fs_mgr_overlayfs_teardown_one(overlay_mount_point, teardown_dir, ignore_change);
+ }
+
+ // Map scratch device, mount kScratchMountPoint and teardown kScratchMountPoint.
+ bool mapped = false;
+ std::string scratch_device;
+ if (EnsureScratchMapped(&scratch_device, &mapped)) {
+ fs_mgr_overlayfs_umount_scratch();
+ if (fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type())) {
+ bool should_destroy_scratch = false;
+ fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change,
+ &should_destroy_scratch);
+ if (should_destroy_scratch) {
+ fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, nullptr);
+ }
+ fs_mgr_overlayfs_umount_scratch();
+ }
+ if (mapped) {
+ DestroyLogicalPartition(android::base::Basename(kScratchMountPoint));
+ }
+ }
+
+ // Teardown DSU overlay if present.
+ if (MapDsuScratchDevice(&scratch_device)) {
+ fs_mgr_overlayfs_umount_scratch();
+ if (fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type())) {
+ fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change);
+ fs_mgr_overlayfs_umount_scratch();
+ }
+ DestroyLogicalPartition(android::gsi::kDsuScratch);
+ }
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 34aded9..d45e2de 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -49,5 +49,12 @@
const std::function<bool(const std::set<std::string>&)>& init);
void CleanupOldScratchFiles();
+// Teardown overlays of all sources (cache dir, scratch device, DSU) for |mount_point|.
+// Teardown all overlays if |mount_point| is empty.
+//
+// Note: This should be called if and only if in recovery or fastbootd to teardown
+// overlays if any partition is flashed or updated.
+void TeardownAllOverlayForMountPoint(const std::string& mount_point = {});
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index a0bc44d..0efe384 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -43,6 +43,7 @@
},
},
ramdisk_available: true,
+ vendor_ramdisk_available: true,
}
filegroup {
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index bb7d8b3..d5b8a61 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -111,8 +111,10 @@
// Check to make sure appropriate uevent is generated so ueventd will
// do the right thing and remove the corresponding device node and symlinks.
- CHECK(io.flags & DM_UEVENT_GENERATED_FLAG)
- << "Didn't generate uevent for [" << name << "] removal";
+ if ((io.flags & DM_UEVENT_GENERATED_FLAG) == 0) {
+ LOG(ERROR) << "Didn't generate uevent for [" << name << "] removal";
+ return false;
+ }
if (timeout_ms <= std::chrono::milliseconds::zero()) {
return true;
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index f1b0031..34049d4 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -46,6 +46,7 @@
header_libs: [
"libchrome",
"libfiemap_headers",
+ "libstorage_literals_headers",
"libupdate_engine_headers",
],
export_static_lib_headers: [
@@ -173,6 +174,7 @@
"libz",
],
ramdisk_available: true,
+ vendor_ramdisk_available: true,
}
cc_defaults {
@@ -407,9 +409,9 @@
"fs_mgr_defaults",
],
srcs: [
- "snapuserd_server.cpp",
- "snapuserd.cpp",
- "snapuserd_daemon.cpp",
+ "snapuserd_server.cpp",
+ "snapuserd.cpp",
+ "snapuserd_daemon.cpp",
],
cflags: [
@@ -420,7 +422,7 @@
static_libs: [
"libbase",
"libbrotli",
- "libcutils_sockets",
+ "libcutils_sockets",
"liblog",
"libdm",
"libz",
@@ -435,15 +437,9 @@
"snapuserd.rc",
],
static_executable: true,
-}
-
-cc_binary {
- name: "snapuserd_ramdisk",
- stem: "snapuserd",
- defaults: ["snapuserd_defaults"],
-
- ramdisk: true,
- static_executable: true,
+ system_shared_libs: [],
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
}
cc_test {
@@ -567,7 +563,7 @@
"libsnapshot_snapuserd",
"libcutils_sockets",
"libz",
- "libdm",
+ "libdm",
],
header_libs: [
"libstorage_literals_headers",
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index 0328132..38c6bf8 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -34,7 +34,7 @@
MERGE_COMPLETED = 3;
}
-// Next: 9
+// Next: 10
message SnapshotStatus {
// Name of the snapshot. This is usually the name of the snapshotted
// logical partition; for example, "system_b".
@@ -84,6 +84,9 @@
// the merge process.
// This is non-zero when |state| == MERGING or MERGE_COMPLETED.
uint64 metadata_sectors = 8;
+
+ // True if compression is enabled, false otherwise.
+ bool compression_enabled = 9;
}
// Next: 8
@@ -115,7 +118,7 @@
Cancelled = 7;
};
-// Next: 5
+// Next: 6
message SnapshotUpdateStatus {
UpdateState state = 1;
@@ -130,6 +133,9 @@
// Sectors allocated for metadata in all the snapshot devices.
uint64 metadata_sectors = 4;
+
+ // Whether compression/dm-user was used for any snapshots.
+ bool compression_enabled = 5;
}
// Next: 4
diff --git a/fs_mgr/libsnapshot/cow_api_test.cpp b/fs_mgr/libsnapshot/cow_api_test.cpp
index 3d0321f..4db6584 100644
--- a/fs_mgr/libsnapshot/cow_api_test.cpp
+++ b/fs_mgr/libsnapshot/cow_api_test.cpp
@@ -264,15 +264,11 @@
ASSERT_EQ(size_before, size_after);
struct stat buf;
- if (fstat(cow_->fd, &buf) < 0) {
- perror("Fails to determine size of cow image written");
- FAIL();
- }
+ ASSERT_GE(fstat(cow_->fd, &buf), 0) << strerror(errno);
ASSERT_EQ(buf.st_size, writer.GetCowSize());
}
-TEST_F(CowTest, Append) {
- cow_->DoNotRemove();
+TEST_F(CowTest, AppendLabelSmall) {
CowOptions options;
auto writer = std::make_unique<CowWriter>(options);
ASSERT_TRUE(writer->Initialize(cow_->fd));
@@ -280,12 +276,13 @@
std::string data = "This is some data, believe it";
data.resize(options.block_size, '\0');
ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
+ ASSERT_TRUE(writer->AddLabel(3));
ASSERT_TRUE(writer->Finalize());
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd, CowWriter::OpenMode::APPEND));
+ ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 3));
std::string data2 = "More data!";
data2.resize(options.block_size, '\0');
@@ -298,11 +295,12 @@
ASSERT_EQ(fstat(cow_->fd, &buf), 0);
ASSERT_EQ(buf.st_size, writer->GetCowSize());
- // Read back both operations.
+ // Read back both operations, and label.
CowReader reader;
uint64_t label;
ASSERT_TRUE(reader.Parse(cow_->fd));
- ASSERT_FALSE(reader.GetLastLabel(&label));
+ ASSERT_TRUE(reader.GetLastLabel(&label));
+ ASSERT_EQ(label, 3);
StringSink sink;
@@ -320,89 +318,6 @@
ASSERT_FALSE(iter->Done());
op = &iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data2);
-
- iter->Next();
- ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, AppendCorrupted) {
- CowOptions options;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
-
- std::string data = "This is some data, believe it";
- data.resize(options.block_size, '\0');
- ASSERT_TRUE(writer->AddLabel(0));
- ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
- ASSERT_TRUE(writer->AddLabel(1));
- ASSERT_TRUE(writer->AddZeroBlocks(50, 1));
- ASSERT_TRUE(writer->Finalize());
- // Drop the tail end of the header. Last entry may be corrupted.
- ftruncate(cow_->fd, writer->GetCowSize() - 5);
-
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd, CowWriter::OpenMode::APPEND));
-
- ASSERT_TRUE(writer->AddLabel(2));
- ASSERT_TRUE(writer->AddZeroBlocks(50, 1));
-
- std::string data2 = "More data!";
- data2.resize(options.block_size, '\0');
- ASSERT_TRUE(writer->AddLabel(3));
- ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));
- ASSERT_TRUE(writer->Finalize());
-
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- struct stat buf;
- ASSERT_EQ(fstat(cow_->fd, &buf), 0);
- ASSERT_EQ(buf.st_size, writer->GetCowSize());
-
- // Read back all three operations.
- CowReader reader;
- ASSERT_TRUE(reader.Parse(cow_->fd));
-
- StringSink sink;
-
- auto iter = reader.GetOpIter();
- ASSERT_NE(iter, nullptr);
-
- ASSERT_FALSE(iter->Done());
- auto op = &iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 0);
-
- iter->Next();
-
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data);
-
- iter->Next();
- sink.Reset();
-
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 2);
-
- iter->Next();
-
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
- ASSERT_EQ(op->type, kCowZeroOp);
-
- iter->Next();
-
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
ASSERT_EQ(op->source, 3);
@@ -418,33 +333,86 @@
ASSERT_TRUE(iter->Done());
}
+TEST_F(CowTest, AppendLabelMissing) {
+ CowOptions options;
+ auto writer = std::make_unique<CowWriter>(options);
+ ASSERT_TRUE(writer->Initialize(cow_->fd));
+
+ ASSERT_TRUE(writer->AddLabel(0));
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+ ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
+ ASSERT_TRUE(writer->AddLabel(1));
+ // Drop the tail end of the last op header, corrupting it.
+ ftruncate(cow_->fd, writer->GetCowSize() - sizeof(CowFooter) - 3);
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ writer = std::make_unique<CowWriter>(options);
+ ASSERT_FALSE(writer->InitializeAppend(cow_->fd, 1));
+ ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 0));
+
+ ASSERT_TRUE(writer->AddZeroBlocks(51, 1));
+ ASSERT_TRUE(writer->Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ struct stat buf;
+ ASSERT_EQ(fstat(cow_->fd, &buf), 0);
+ ASSERT_EQ(buf.st_size, writer->GetCowSize());
+
+ // Read back both operations.
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ StringSink sink;
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+
+ ASSERT_FALSE(iter->Done());
+ auto op = &iter->Get();
+ ASSERT_EQ(op->type, kCowLabelOp);
+ ASSERT_EQ(op->source, 0);
+
+ iter->Next();
+
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+ ASSERT_EQ(op->type, kCowZeroOp);
+
+ iter->Next();
+
+ ASSERT_TRUE(iter->Done());
+}
+
TEST_F(CowTest, AppendExtendedCorrupted) {
CowOptions options;
auto writer = std::make_unique<CowWriter>(options);
ASSERT_TRUE(writer->Initialize(cow_->fd));
ASSERT_TRUE(writer->AddLabel(5));
- ASSERT_TRUE(writer->AddLabel(6));
std::string data = "This is some data, believe it";
data.resize(options.block_size * 2, '\0');
ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
+ ASSERT_TRUE(writer->AddLabel(6));
- // fail to write the footer
+ // fail to write the footer. Cow Format does not know if Label 6 is valid
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
// Get the last known good label
CowReader label_reader;
uint64_t label;
- ASSERT_TRUE(label_reader.Parse(cow_->fd));
+ ASSERT_TRUE(label_reader.Parse(cow_->fd, {5}));
ASSERT_TRUE(label_reader.GetLastLabel(&label));
ASSERT_EQ(label, 5);
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd, CowWriter::OpenMode::APPEND));
+ ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 5));
ASSERT_TRUE(writer->Finalize());
@@ -452,7 +420,7 @@
ASSERT_EQ(fstat(cow_->fd, &buf), 0);
ASSERT_EQ(buf.st_size, writer->GetCowSize());
- // Read back all three operations.
+ // Read back all valid operations
CowReader reader;
ASSERT_TRUE(reader.Parse(cow_->fd));
@@ -475,16 +443,20 @@
auto writer = std::make_unique<CowWriter>(options);
ASSERT_TRUE(writer->Initialize(cow_->fd));
- ASSERT_TRUE(writer->AddLabel(4));
-
- ASSERT_TRUE(writer->AddLabel(5));
std::string data = "This is some data, believe it";
data.resize(options.block_size * 2, '\0');
ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
- ASSERT_TRUE(writer->AddLabel(6));
+ ASSERT_TRUE(writer->AddLabel(4));
+
ASSERT_TRUE(writer->AddZeroBlocks(50, 2));
+ ASSERT_TRUE(writer->AddLabel(5));
+
+ ASSERT_TRUE(writer->AddCopy(5, 6));
+
+ ASSERT_TRUE(writer->AddLabel(6));
+
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
writer = std::make_unique<CowWriter>(options);
@@ -509,20 +481,6 @@
ASSERT_FALSE(iter->Done());
auto op = &iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 4);
-
- iter->Next();
-
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 5);
-
- iter->Next();
-
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
ASSERT_TRUE(reader.ReadData(*op, &sink));
ASSERT_EQ(sink.stream(), data.substr(0, options.block_size));
@@ -539,6 +497,31 @@
iter->Next();
sink.Reset();
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+ ASSERT_EQ(op->type, kCowLabelOp);
+ ASSERT_EQ(op->source, 4);
+
+ iter->Next();
+
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+ ASSERT_EQ(op->type, kCowZeroOp);
+
+ iter->Next();
+
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+ ASSERT_EQ(op->type, kCowZeroOp);
+
+ iter->Next();
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+ ASSERT_EQ(op->type, kCowLabelOp);
+ ASSERT_EQ(op->source, 5);
+
+ iter->Next();
+
ASSERT_TRUE(iter->Done());
}
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index f10ccb6..6b7ada5 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -31,14 +31,7 @@
namespace android {
namespace snapshot {
-CowReader::CowReader()
- : fd_(-1),
- header_(),
- footer_(),
- fd_size_(0),
- has_footer_(false),
- last_label_(0),
- has_last_label_(false) {}
+CowReader::CowReader() : fd_(-1), header_(), fd_size_(0) {}
static void SHA256(const void*, size_t, uint8_t[]) {
#if 0
@@ -49,12 +42,12 @@
#endif
}
-bool CowReader::Parse(android::base::unique_fd&& fd) {
+bool CowReader::Parse(android::base::unique_fd&& fd, std::optional<uint64_t> label) {
owned_fd_ = std::move(fd);
- return Parse(android::base::borrowed_fd{owned_fd_});
+ return Parse(android::base::borrowed_fd{owned_fd_}, label);
}
-bool CowReader::Parse(android::base::borrowed_fd fd) {
+bool CowReader::Parse(android::base::borrowed_fd fd, std::optional<uint64_t> label) {
fd_ = fd;
auto pos = lseek(fd_.get(), 0, SEEK_END);
@@ -99,85 +92,122 @@
return false;
}
- auto footer_pos = lseek(fd_.get(), -header_.footer_size, SEEK_END);
- if (footer_pos != fd_size_ - header_.footer_size) {
- LOG(ERROR) << "Failed to read full footer!";
- return false;
- }
- if (!android::base::ReadFully(fd_, &footer_, sizeof(footer_))) {
- PLOG(ERROR) << "read footer failed";
- return false;
- }
- has_footer_ = (footer_.op.type == kCowFooterOp);
- return ParseOps();
+ return ParseOps(label);
}
-bool CowReader::ParseOps() {
+bool CowReader::ParseOps(std::optional<uint64_t> label) {
uint64_t pos = lseek(fd_.get(), sizeof(header_), SEEK_SET);
if (pos != sizeof(header_)) {
PLOG(ERROR) << "lseek ops failed";
return false;
}
- std::optional<uint64_t> next_last_label;
+
auto ops_buffer = std::make_shared<std::vector<CowOperation>>();
- if (has_footer_) ops_buffer->reserve(footer_.op.num_ops);
- uint64_t current_op_num = 0;
- // Look until we reach the last possible non-footer position.
- uint64_t last_pos = fd_size_ - (has_footer_ ? sizeof(footer_) : sizeof(CowOperation));
// Alternating op and data
- while (pos < last_pos) {
- ops_buffer->resize(current_op_num + 1);
- if (!android::base::ReadFully(fd_, ops_buffer->data() + current_op_num,
- sizeof(CowOperation))) {
+ while (true) {
+ ops_buffer->emplace_back();
+ if (!android::base::ReadFully(fd_, &ops_buffer->back(), sizeof(CowOperation))) {
PLOG(ERROR) << "read op failed";
return false;
}
- auto& current_op = ops_buffer->data()[current_op_num];
- pos = lseek(fd_.get(), GetNextOpOffset(current_op), SEEK_CUR);
- if (pos == uint64_t(-1)) {
+
+ auto& current_op = ops_buffer->back();
+ off_t offs = lseek(fd_.get(), GetNextOpOffset(current_op), SEEK_CUR);
+ if (offs < 0) {
PLOG(ERROR) << "lseek next op failed";
return false;
}
- current_op_num++;
+ pos = static_cast<uint64_t>(offs);
+
if (current_op.type == kCowLabelOp) {
- // If we don't have a footer, the last label may be incomplete
- if (has_footer_) {
- has_last_label_ = true;
- last_label_ = current_op.source;
- } else {
- if (next_last_label) {
- last_label_ = next_last_label.value();
- has_last_label_ = true;
- }
- next_last_label = {current_op.source};
+ last_label_ = {current_op.source};
+
+ // If we reach the requested label, stop reading.
+ if (label && label.value() == current_op.source) {
+ break;
}
} else if (current_op.type == kCowFooterOp) {
- memcpy(&footer_.op, ¤t_op, sizeof(footer_.op));
+ footer_.emplace();
- if (android::base::ReadFully(fd_, &footer_.data, sizeof(footer_.data))) {
- has_footer_ = true;
- if (next_last_label) {
- last_label_ = next_last_label.value();
- has_last_label_ = true;
- }
+ CowFooter* footer = &footer_.value();
+ memcpy(&footer_->op, ¤t_op, sizeof(footer->op));
+
+ if (!android::base::ReadFully(fd_, &footer->data, sizeof(footer->data))) {
+ LOG(ERROR) << "Could not read COW footer";
+ return false;
}
+
+ // Drop the footer from the op stream.
+ ops_buffer->pop_back();
break;
}
}
+ // To successfully parse a COW file, we need either:
+ // (1) a label to read up to, and for that label to be found, or
+ // (2) a valid footer.
+ if (label) {
+ if (!last_label_) {
+ LOG(ERROR) << "Did not find label " << label.value()
+ << " while reading COW (no labels found)";
+ return false;
+ }
+ if (last_label_.value() != label.value()) {
+ LOG(ERROR) << "Did not find label " << label.value()
+ << ", last label=" << last_label_.value();
+ return false;
+ }
+ } else if (!footer_) {
+ LOG(ERROR) << "No COW footer found";
+ return false;
+ }
+
uint8_t csum[32];
memset(csum, 0, sizeof(uint8_t) * 32);
- if (has_footer_) {
- SHA256(ops_buffer.get()->data(), footer_.op.ops_size, csum);
- if (memcmp(csum, footer_.data.ops_checksum, sizeof(csum)) != 0) {
+ if (footer_) {
+ if (ops_buffer->size() != footer_->op.num_ops) {
+ LOG(ERROR) << "num ops does not match";
+ return false;
+ }
+ if (ops_buffer->size() * sizeof(CowOperation) != footer_->op.ops_size) {
+ LOG(ERROR) << "ops size does not match ";
+ return false;
+ }
+ SHA256(&footer_->op, sizeof(footer_->op), footer_->data.footer_checksum);
+ if (memcmp(csum, footer_->data.ops_checksum, sizeof(csum)) != 0) {
+ LOG(ERROR) << "ops checksum does not match";
+ return false;
+ }
+ SHA256(ops_buffer.get()->data(), footer_->op.ops_size, csum);
+ if (memcmp(csum, footer_->data.ops_checksum, sizeof(csum)) != 0) {
LOG(ERROR) << "ops checksum does not match";
return false;
}
} else {
- LOG(INFO) << "No Footer, recovered data";
+ LOG(INFO) << "No COW Footer, recovered data";
}
+
+ if (header_.num_merge_ops > 0) {
+ uint64_t merge_ops = header_.num_merge_ops;
+ uint64_t metadata_ops = 0;
+ uint64_t current_op_num = 0;
+
+ CHECK(ops_buffer->size() >= merge_ops);
+ while (merge_ops) {
+ auto& current_op = ops_buffer->data()[current_op_num];
+ if (current_op.type == kCowLabelOp || current_op.type == kCowFooterOp) {
+ metadata_ops += 1;
+ } else {
+ merge_ops -= 1;
+ }
+ current_op_num += 1;
+ }
+ ops_buffer->erase(ops_buffer.get()->begin(),
+ ops_buffer.get()->begin() + header_.num_merge_ops + metadata_ops);
+ }
+
ops_ = ops_buffer;
return true;
}
@@ -188,14 +218,14 @@
}
bool CowReader::GetFooter(CowFooter* footer) {
- if (!has_footer_) return false;
- *footer = footer_;
+ if (!footer_) return false;
+ *footer = footer_.value();
return true;
}
bool CowReader::GetLastLabel(uint64_t* label) {
- if (!has_last_label_) return false;
- *label = last_label_;
+ if (!last_label_) return false;
+ *label = last_label_.value();
return true;
}
@@ -231,14 +261,50 @@
return (*op_iter_);
}
+class CowOpReverseIter final : public ICowOpReverseIter {
+ public:
+ explicit CowOpReverseIter(std::shared_ptr<std::vector<CowOperation>> ops);
+
+ bool Done() override;
+ const CowOperation& Get() override;
+ void Next() override;
+
+ private:
+ std::shared_ptr<std::vector<CowOperation>> ops_;
+ std::vector<CowOperation>::reverse_iterator op_riter_;
+};
+
+CowOpReverseIter::CowOpReverseIter(std::shared_ptr<std::vector<CowOperation>> ops) {
+ ops_ = ops;
+ op_riter_ = ops_.get()->rbegin();
+}
+
+bool CowOpReverseIter::Done() {
+ return op_riter_ == ops_.get()->rend();
+}
+
+void CowOpReverseIter::Next() {
+ CHECK(!Done());
+ op_riter_++;
+}
+
+const CowOperation& CowOpReverseIter::Get() {
+ CHECK(!Done());
+ return (*op_riter_);
+}
+
std::unique_ptr<ICowOpIter> CowReader::GetOpIter() {
return std::make_unique<CowOpIter>(ops_);
}
+std::unique_ptr<ICowOpReverseIter> CowReader::GetRevOpIter() {
+ return std::make_unique<CowOpReverseIter>(ops_);
+}
+
bool CowReader::GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read) {
// Validate the offset, taking care to acknowledge possible overflow of offset+len.
- if (offset < sizeof(header_) || offset >= fd_size_ - sizeof(footer_) || len >= fd_size_ ||
- offset + len > fd_size_ - sizeof(footer_)) {
+ if (offset < sizeof(header_) || offset >= fd_size_ - sizeof(CowFooter) || len >= fd_size_ ||
+ offset + len > fd_size_ - sizeof(CowFooter)) {
LOG(ERROR) << "invalid data offset: " << offset << ", " << len << " bytes";
return false;
}
diff --git a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
index ab15194..5483fd0 100644
--- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
@@ -41,6 +41,10 @@
class SnapuserdTest : public ::testing::Test {
protected:
void SetUp() override {
+ // TODO: Daemon started through first stage
+ // init does not have permission to read files
+ // from /data/nativetest.
+ system("setenforce 0");
cow_system_ = std::make_unique<TemporaryFile>();
ASSERT_GE(cow_system_->fd, 0) << strerror(errno);
@@ -53,10 +57,6 @@
cow_product_1_ = std::make_unique<TemporaryFile>();
ASSERT_GE(cow_product_1_->fd, 0) << strerror(errno);
- // Create temp files in the PWD as selinux
- // allows kernel domin to read from that directory only
- // on userdebug/eng builds. Creating files under /data/local/tmp
- // will have selinux denials.
std::string path = android::base::GetExecutableDirectory();
system_a_ = std::make_unique<TemporaryFile>(path);
@@ -65,10 +65,11 @@
product_a_ = std::make_unique<TemporaryFile>(path);
ASSERT_GE(product_a_->fd, 0) << strerror(errno);
- size_ = 1_MiB;
+ size_ = 100_MiB;
}
void TearDown() override {
+ system("setenforce 1");
cow_system_ = nullptr;
cow_product_ = nullptr;
@@ -107,6 +108,8 @@
std::unique_ptr<uint8_t[]> product_buffer_;
void Init();
+ void InitCowDevices();
+ void InitDaemon();
void CreateCowDevice(std::unique_ptr<TemporaryFile>& cow);
void CreateSystemDmUser(std::unique_ptr<TemporaryFile>& cow);
void CreateProductDmUser(std::unique_ptr<TemporaryFile>& cow);
@@ -116,10 +119,10 @@
void SwitchSnapshotDevices();
std::string GetSystemControlPath() {
- return std::string("/dev/dm-user-") + system_device_ctrl_name_;
+ return std::string("/dev/dm-user/") + system_device_ctrl_name_;
}
std::string GetProductControlPath() {
- return std::string("/dev/dm-user-") + product_device_ctrl_name_;
+ return std::string("/dev/dm-user/") + product_device_ctrl_name_;
}
void TestIO(unique_fd& snapshot_fd, std::unique_ptr<uint8_t[]>& buffer);
@@ -151,12 +154,12 @@
offset += 1_MiB;
}
- for (size_t j = 0; j < (8_MiB / 1_MiB); j++) {
+ for (size_t j = 0; j < (800_MiB / 1_MiB); j++) {
ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), 1_MiB, 0), true);
ASSERT_EQ(android::base::WriteFully(system_a_->fd, random_buffer.get(), 1_MiB), true);
}
- for (size_t j = 0; j < (8_MiB / 1_MiB); j++) {
+ for (size_t j = 0; j < (800_MiB / 1_MiB); j++) {
ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), 1_MiB, 0), true);
ASSERT_EQ(android::base::WriteFully(product_a_->fd, random_buffer.get(), 1_MiB), true);
}
@@ -237,12 +240,6 @@
system_device_name_.clear();
system_device_ctrl_name_.clear();
- // Create a COW device. Number of sectors is chosen random which can
- // hold at least 400MB of data
-
- int err = ioctl(sys_fd_.get(), BLKGETSIZE, &system_blksize_);
- ASSERT_GE(err, 0);
-
std::string str(cow->path);
std::size_t found = str.find_last_of("/\\");
ASSERT_NE(found, std::string::npos);
@@ -279,12 +276,6 @@
product_device_name_.clear();
product_device_ctrl_name_.clear();
- // Create a COW device. Number of sectors is chosen random which can
- // hold at least 400MB of data
-
- int err = ioctl(product_fd_.get(), BLKGETSIZE, &product_blksize_);
- ASSERT_GE(err, 0);
-
std::string str(cow->path);
std::size_t found = str.find_last_of("/\\");
ASSERT_NE(found, std::string::npos);
@@ -296,19 +287,29 @@
system(cmd.c_str());
}
+void SnapuserdTest::InitCowDevices() {
+ system_blksize_ = client_->InitDmUserCow(system_device_ctrl_name_, cow_system_->path,
+ system_a_loop_->device());
+ ASSERT_NE(system_blksize_, 0);
+
+ product_blksize_ = client_->InitDmUserCow(product_device_ctrl_name_, cow_product_->path,
+ product_a_loop_->device());
+ ASSERT_NE(product_blksize_, 0);
+}
+
+void SnapuserdTest::InitDaemon() {
+ bool ok = client_->AttachDmUser(system_device_ctrl_name_);
+ ASSERT_TRUE(ok);
+
+ ok = client_->AttachDmUser(product_device_ctrl_name_);
+ ASSERT_TRUE(ok);
+}
+
void SnapuserdTest::StartSnapuserdDaemon() {
ASSERT_TRUE(EnsureSnapuserdStarted());
client_ = SnapuserdClient::Connect(kSnapuserdSocket, 5s);
ASSERT_NE(client_, nullptr);
-
- bool ok = client_->InitializeSnapuserd(cow_system_->path, system_a_loop_->device(),
- GetSystemControlPath());
- ASSERT_TRUE(ok);
-
- ok = client_->InitializeSnapuserd(cow_product_->path, product_a_loop_->device(),
- GetProductControlPath());
- ASSERT_TRUE(ok);
}
void SnapuserdTest::CreateSnapshotDevices() {
@@ -434,10 +435,13 @@
CreateCowDevice(cow_system_);
CreateCowDevice(cow_product_);
+ StartSnapuserdDaemon();
+ InitCowDevices();
+
CreateSystemDmUser(cow_system_);
CreateProductDmUser(cow_product_);
- StartSnapuserdDaemon();
+ InitDaemon();
CreateSnapshotDevices();
@@ -451,42 +455,9 @@
snapshot_fd.reset(-1);
- // Sequence of operations for transition
- CreateCowDevice(cow_system_1_);
- CreateCowDevice(cow_product_1_);
-
- // Create dm-user which creates new control devices
- CreateSystemDmUser(cow_system_1_);
- CreateProductDmUser(cow_product_1_);
-
- // Send the path information to second stage daemon through vector
- std::vector<std::vector<std::string>> vec{
- {cow_system_1_->path, system_a_loop_->device(), GetSystemControlPath()},
- {cow_product_1_->path, product_a_loop_->device(), GetProductControlPath()}};
-
- // TODO: This is not switching snapshot device but creates a new table;
- // Second stage daemon will be ready to serve the IO request. From now
- // onwards, we can go ahead and shutdown the first stage daemon
- SwitchSnapshotDevices();
-
DeleteDmUser(cow_system_, "system-snapshot");
DeleteDmUser(cow_product_, "product-snapshot");
- // Test the IO again with the second stage daemon
- snapshot_fd.reset(open("/dev/block/mapper/system-snapshot-1", O_RDONLY));
- ASSERT_TRUE(snapshot_fd > 0);
- TestIO(snapshot_fd, system_buffer_);
-
- snapshot_fd.reset(open("/dev/block/mapper/product-snapshot-1", O_RDONLY));
- ASSERT_TRUE(snapshot_fd > 0);
- TestIO(snapshot_fd, product_buffer_);
-
- snapshot_fd.reset(-1);
-
- DeleteDmUser(cow_system_1_, "system-snapshot-1");
- DeleteDmUser(cow_product_1_, "product-snapshot-1");
-
- // Stop the second stage daemon
ASSERT_TRUE(client_->StopSnapuserd());
}
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
index ec2dc96..957ba35 100644
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -91,6 +91,7 @@
header_.header_size = sizeof(CowHeader);
header_.footer_size = sizeof(CowFooter);
header_.block_size = options_.block_size;
+ header_.num_merge_ops = 0;
footer_ = {};
footer_.op.data_length = 64;
footer_.op.type = kCowFooterOp;
@@ -110,27 +111,45 @@
return true;
}
-bool CowWriter::Initialize(unique_fd&& fd, OpenMode mode) {
- owned_fd_ = std::move(fd);
- return Initialize(borrowed_fd{owned_fd_}, mode);
+bool CowWriter::SetFd(android::base::borrowed_fd fd) {
+ if (fd.get() < 0) {
+ owned_fd_.reset(open("/dev/null", O_RDWR | O_CLOEXEC));
+ if (owned_fd_ < 0) {
+ PLOG(ERROR) << "open /dev/null failed";
+ return false;
+ }
+ fd_ = owned_fd_;
+ is_dev_null_ = true;
+ } else {
+ fd_ = fd;
+
+ struct stat stat;
+ if (fstat(fd.get(), &stat) < 0) {
+ PLOG(ERROR) << "fstat failed";
+ return false;
+ }
+ is_block_device_ = S_ISBLK(stat.st_mode);
+ }
+ return true;
}
-bool CowWriter::Initialize(borrowed_fd fd, OpenMode mode) {
+void CowWriter::InitializeMerge(borrowed_fd fd, CowHeader* header) {
fd_ = fd;
+ memcpy(&header_, header, sizeof(CowHeader));
+ merge_in_progress_ = true;
+}
- if (!ParseOptions()) {
+bool CowWriter::Initialize(unique_fd&& fd) {
+ owned_fd_ = std::move(fd);
+ return Initialize(borrowed_fd{owned_fd_});
+}
+
+bool CowWriter::Initialize(borrowed_fd fd) {
+ if (!SetFd(fd) || !ParseOptions()) {
return false;
}
- switch (mode) {
- case OpenMode::WRITE:
- return OpenForWrite();
- case OpenMode::APPEND:
- return OpenForAppend();
- default:
- LOG(ERROR) << "Unknown open mode in CowWriter";
- return false;
- }
+ return OpenForWrite();
}
bool CowWriter::InitializeAppend(android::base::unique_fd&& fd, uint64_t label) {
@@ -139,9 +158,7 @@
}
bool CowWriter::InitializeAppend(android::base::borrowed_fd fd, uint64_t label) {
- fd_ = fd;
-
- if (!ParseOptions()) {
+ if (!SetFd(fd) || !ParseOptions()) {
return false;
}
@@ -171,95 +188,35 @@
return true;
}
-bool CowWriter::OpenForAppend() {
- auto reader = std::make_unique<CowReader>();
- bool incomplete = false;
- std::queue<CowOperation> toAdd;
- if (!reader->Parse(fd_) || !reader->GetHeader(&header_)) {
- return false;
- }
- incomplete = !reader->GetFooter(&footer_);
-
- options_.block_size = header_.block_size;
-
- // Reset this, since we're going to reimport all operations.
- footer_.op.num_ops = 0;
- next_op_pos_ = sizeof(header_);
-
- auto iter = reader->GetOpIter();
- while (!iter->Done()) {
- CowOperation op = iter->Get();
- if (op.type == kCowFooterOp) break;
- if (incomplete) {
- // Last operation translation may be corrupt. Wait to add it.
- if (op.type == kCowLabelOp) {
- while (!toAdd.empty()) {
- AddOperation(toAdd.front());
- toAdd.pop();
- }
- }
- toAdd.push(op);
- } else {
- AddOperation(op);
- }
- iter->Next();
- }
-
- // Free reader so we own the descriptor position again.
- reader = nullptr;
-
- // Position for new writing
- if (ftruncate(fd_.get(), next_op_pos_) != 0) {
- PLOG(ERROR) << "Failed to trim file";
- return false;
- }
- if (lseek(fd_.get(), 0, SEEK_END) < 0) {
- PLOG(ERROR) << "lseek failed";
- return false;
- }
- return true;
-}
-
bool CowWriter::OpenForAppend(uint64_t label) {
auto reader = std::make_unique<CowReader>();
std::queue<CowOperation> toAdd;
- if (!reader->Parse(fd_) || !reader->GetHeader(&header_)) {
+
+ if (!reader->Parse(fd_, {label}) || !reader->GetHeader(&header_)) {
return false;
}
options_.block_size = header_.block_size;
- bool found_label = false;
// Reset this, since we're going to reimport all operations.
footer_.op.num_ops = 0;
next_op_pos_ = sizeof(header_);
+ ops_.resize(0);
auto iter = reader->GetOpIter();
while (!iter->Done()) {
- CowOperation op = iter->Get();
- if (op.type == kCowFooterOp) break;
- if (op.type == kCowLabelOp) {
- if (found_label) break;
- if (op.source == label) found_label = true;
- }
- AddOperation(op);
+ AddOperation(iter->Get());
iter->Next();
}
- if (!found_label) {
- PLOG(ERROR) << "Failed to find last label";
- return false;
- }
-
// Free reader so we own the descriptor position again.
reader = nullptr;
- // Position for new writing
- if (ftruncate(fd_.get(), next_op_pos_) != 0) {
- PLOG(ERROR) << "Failed to trim file";
+ // Remove excess data
+ if (!Truncate(next_op_pos_)) {
return false;
}
- if (lseek(fd_.get(), 0, SEEK_END) < 0) {
+ if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
PLOG(ERROR) << "lseek failed";
return false;
}
@@ -267,6 +224,7 @@
}
bool CowWriter::EmitCopy(uint64_t new_block, uint64_t old_block) {
+ CHECK(!merge_in_progress_);
CowOperation op = {};
op.type = kCowCopyOp;
op.new_block = new_block;
@@ -277,6 +235,7 @@
bool CowWriter::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
uint64_t pos;
+ CHECK(!merge_in_progress_);
for (size_t i = 0; i < size / header_.block_size; i++) {
CowOperation op = {};
op.type = kCowReplaceOp;
@@ -315,6 +274,7 @@
}
bool CowWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
+ CHECK(!merge_in_progress_);
for (uint64_t i = 0; i < num_blocks; i++) {
CowOperation op = {};
op.type = kCowZeroOp;
@@ -326,10 +286,11 @@
}
bool CowWriter::EmitLabel(uint64_t label) {
+ CHECK(!merge_in_progress_);
CowOperation op = {};
op.type = kCowLabelOp;
op.source = label;
- return WriteOperation(op);
+ return WriteOperation(op) && Sync();
}
std::basic_string<uint8_t> CowWriter::Compress(const void* data, size_t length) {
@@ -384,7 +345,7 @@
}
bool CowWriter::Finalize() {
- footer_.op.ops_size = ops_.size() + sizeof(footer_.op);
+ footer_.op.ops_size = ops_.size();
uint64_t pos;
if (!GetDataPos(&pos)) {
@@ -408,7 +369,7 @@
PLOG(ERROR) << "lseek ops failed";
return false;
}
- return true;
+ return Sync();
}
uint64_t CowWriter::GetCowSize() {
@@ -429,10 +390,11 @@
if (!android::base::WriteFully(fd_, reinterpret_cast<const uint8_t*>(&op), sizeof(op))) {
return false;
}
- if (data != NULL && size > 0)
+ if (data != nullptr && size > 0) {
if (!WriteRawData(data, size)) return false;
+ }
AddOperation(op);
- return !fsync(fd_.get());
+ return true;
}
void CowWriter::AddOperation(const CowOperation& op) {
@@ -448,5 +410,45 @@
return true;
}
+bool CowWriter::Sync() {
+ if (is_dev_null_) {
+ return true;
+ }
+ if (fsync(fd_.get()) < 0) {
+ PLOG(ERROR) << "fsync failed";
+ return false;
+ }
+ return true;
+}
+
+bool CowWriter::CommitMerge(int merged_ops) {
+ CHECK(merge_in_progress_);
+ header_.num_merge_ops += merged_ops;
+
+ if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek failed";
+ return false;
+ }
+
+ if (!android::base::WriteFully(fd_, reinterpret_cast<const uint8_t*>(&header_),
+ sizeof(header_))) {
+ PLOG(ERROR) << "WriteFully failed";
+ return false;
+ }
+
+ return Sync();
+}
+
+bool CowWriter::Truncate(off_t length) {
+ if (is_dev_null_ || is_block_device_) {
+ return true;
+ }
+ if (ftruncate(fd_.get(), length) < 0) {
+ PLOG(ERROR) << "Failed to truncate.";
+ return false;
+ }
+ return true;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 2291e30..80766ff 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -59,6 +59,9 @@
// The size of block operations, in bytes.
uint32_t block_size;
+
+ // Tracks merge operations completed
+ uint64_t num_merge_ops;
} __attribute__((packed));
// This structure is the same size of a normal Operation, but is repurposed for the footer.
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index b863ff2..be69225 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -18,6 +18,7 @@
#include <functional>
#include <memory>
+#include <optional>
#include <android-base/unique_fd.h>
#include <libsnapshot/cow_format.h>
@@ -26,6 +27,7 @@
namespace snapshot {
class ICowOpIter;
+class ICowOpReverseIter;
// A ByteSink object handles requests for a buffer of a specific size. It
// always owns the underlying buffer. It's designed to minimize potential
@@ -73,6 +75,9 @@
// Return an iterator for retrieving CowOperation entries.
virtual std::unique_ptr<ICowOpIter> GetOpIter() = 0;
+ // Return an reverse iterator for retrieving CowOperation entries.
+ virtual std::unique_ptr<ICowOpReverseIter> GetRevOpIter() = 0;
+
// Get decoded bytes from the data section, handling any decompression.
// All retrieved data is passed to the sink.
virtual bool ReadData(const CowOperation& op, IByteSink* sink) = 0;
@@ -93,12 +98,29 @@
virtual void Next() = 0;
};
+// Reverse Iterate over a sequence of COW operations.
+class ICowOpReverseIter {
+ public:
+ virtual ~ICowOpReverseIter() {}
+
+ // True if there are more items to read, false otherwise.
+ virtual bool Done() = 0;
+
+ // Read the current operation.
+ virtual const CowOperation& Get() = 0;
+
+ // Advance to the next item.
+ virtual void Next() = 0;
+};
+
class CowReader : public ICowReader {
public:
CowReader();
- bool Parse(android::base::unique_fd&& fd);
- bool Parse(android::base::borrowed_fd fd);
+ // Parse the COW, optionally, up to the given label. If no label is
+ // specified, the COW must have an intact footer.
+ bool Parse(android::base::unique_fd&& fd, std::optional<uint64_t> label = {});
+ bool Parse(android::base::borrowed_fd fd, std::optional<uint64_t> label = {});
bool GetHeader(CowHeader* header) override;
bool GetFooter(CowFooter* footer) override;
@@ -107,23 +129,26 @@
// Create a CowOpIter object which contains footer_.num_ops
// CowOperation objects. Get() returns a unique CowOperation object
- // whose lifetime depends on the CowOpIter object
+ // whose lifetime depends on the CowOpIter object; the return
+ // value of these will never be null.
std::unique_ptr<ICowOpIter> GetOpIter() override;
+ std::unique_ptr<ICowOpReverseIter> GetRevOpIter() override;
+
bool ReadData(const CowOperation& op, IByteSink* sink) override;
bool GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read);
+ void UpdateMergeProgress(uint64_t merge_ops) { header_.num_merge_ops += merge_ops; }
+
private:
- bool ParseOps();
+ bool ParseOps(std::optional<uint64_t> label);
android::base::unique_fd owned_fd_;
android::base::borrowed_fd fd_;
CowHeader header_;
- CowFooter footer_;
+ std::optional<CowFooter> footer_;
uint64_t fd_size_;
- bool has_footer_;
- uint64_t last_label_;
- bool has_last_label_;
+ std::optional<uint64_t> last_label_;
std::shared_ptr<std::vector<CowOperation>> ops_;
};
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index c031d63..e9320b0 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -82,22 +82,24 @@
class CowWriter : public ICowWriter {
public:
- enum class OpenMode { WRITE, APPEND };
-
explicit CowWriter(const CowOptions& options);
// Set up the writer.
- // If opening for write, the file starts from the beginning.
- // If opening for append, if the file has a footer, we start appending to the last op.
- // If the footer isn't found, the last label is considered corrupt, and dropped.
- bool Initialize(android::base::unique_fd&& fd, OpenMode mode = OpenMode::WRITE);
- bool Initialize(android::base::borrowed_fd fd, OpenMode mode = OpenMode::WRITE);
+ // The file starts from the beginning.
+ //
+ // If fd is < 0, the CowWriter will be opened against /dev/null. This is for
+ // computing COW sizes without using storage space.
+ bool Initialize(android::base::unique_fd&& fd);
+ bool Initialize(android::base::borrowed_fd fd);
// Set up a writer, assuming that the given label is the last valid label.
// This will result in dropping any labels that occur after the given on, and will fail
// if the given label does not appear.
bool InitializeAppend(android::base::unique_fd&&, uint64_t label);
bool InitializeAppend(android::base::borrowed_fd fd, uint64_t label);
+ void InitializeMerge(android::base::borrowed_fd fd, CowHeader* header);
+ bool CommitMerge(int merged_ops);
+
bool Finalize() override;
uint64_t GetCowSize() override;
@@ -112,15 +114,17 @@
void SetupHeaders();
bool ParseOptions();
bool OpenForWrite();
- bool OpenForAppend();
bool OpenForAppend(uint64_t label);
- bool ImportOps(std::unique_ptr<ICowOpIter> iter);
bool GetDataPos(uint64_t* pos);
bool WriteRawData(const void* data, size_t size);
bool WriteOperation(const CowOperation& op, const void* data = nullptr, size_t size = 0);
void AddOperation(const CowOperation& op);
std::basic_string<uint8_t> Compress(const void* data, size_t length);
+ bool SetFd(android::base::borrowed_fd fd);
+ bool Sync();
+ bool Truncate(off_t length);
+
private:
android::base::unique_fd owned_fd_;
android::base::borrowed_fd fd_;
@@ -128,6 +132,9 @@
CowFooter footer_{};
int compression_ = 0;
uint64_t next_op_pos_ = 0;
+ bool is_dev_null_ = false;
+ bool merge_in_progress_ = false;
+ bool is_block_device_ = false;
// :TODO: this is not efficient, but stringstream ubsan aborts because some
// bytes overflow a signed char.
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 8bed1b9..f8d3cdc 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -344,6 +344,18 @@
bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms = {}) override;
bool UnmapAllSnapshots() override;
+ // We can't use WaitForFile during first-stage init, because ueventd is not
+ // running and therefore will not automatically create symlinks. Instead,
+ // we let init provide us with the correct function to use to ensure
+ // uevents have been processed and symlink/mknod calls completed.
+ void SetUeventRegenCallback(std::function<bool(const std::string&)> callback) {
+ uevent_regen_callback_ = callback;
+ }
+
+ // If true, compression is enabled for this update. This is used by
+ // first-stage to decide whether to launch snapuserd.
+ bool IsSnapuserdRequired();
+
private:
FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
FRIEND_TEST(SnapshotTest, CreateSnapshot);
@@ -675,6 +687,12 @@
// Same as above, but for paths only (no major:minor device strings).
bool GetMappedImageDevicePath(const std::string& device_name, std::string* device_path);
+ // Wait for a device to be created by ueventd (eg, its symlink or node to be populated).
+ // This is needed for any code that uses device-mapper path in first-stage init. If
+ // |timeout_ms| is empty or the given device is not a path, WaitForDevice immediately
+ // returns true.
+ bool WaitForDevice(const std::string& device, std::chrono::milliseconds timeout_ms);
+
std::string gsid_dir_;
std::string metadata_dir_;
std::unique_ptr<IDeviceInfo> device_;
@@ -682,6 +700,7 @@
bool has_local_image_manager_ = false;
bool use_first_stage_snapuserd_ = false;
bool in_factory_data_reset_ = false;
+ std::function<bool(const std::string&)> uevent_regen_callback_;
std::unique_ptr<SnapuserdClient> snapuserd_client_;
};
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
index 4732c2d..bf5ce8b 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
@@ -42,9 +42,9 @@
// Open the writer in write mode (no append).
virtual bool Initialize() = 0;
- // Open the writer in append mode, optionally with the last label to resume
+ // Open the writer in append mode, with the last label to resume
// from. See CowWriter::InitializeAppend.
- virtual bool InitializeAppend(std::optional<uint64_t> label = {}) = 0;
+ virtual bool InitializeAppend(uint64_t label) = 0;
virtual std::unique_ptr<FileDescriptor> OpenReader() = 0;
@@ -66,7 +66,7 @@
bool SetCowDevice(android::base::unique_fd&& cow_device);
bool Initialize() override;
- bool InitializeAppend(std::optional<uint64_t> label = {}) override;
+ bool InitializeAppend(uint64_t label) override;
bool Finalize() override;
uint64_t GetCowSize() override;
std::unique_ptr<FileDescriptor> OpenReader() override;
@@ -92,7 +92,7 @@
void SetSnapshotDevice(android::base::unique_fd&& snapshot_fd, uint64_t cow_size);
bool Initialize() override { return true; }
- bool InitializeAppend(std::optional<uint64_t>) override { return true; }
+ bool InitializeAppend(uint64_t) override { return true; }
bool Finalize() override;
uint64_t GetCowSize() override { return cow_size_; }
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
index 80f87d9..24b44fa 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
@@ -24,6 +24,7 @@
#include <limits>
#include <string>
#include <thread>
+#include <unordered_map>
#include <vector>
#include <android-base/file.h>
@@ -60,52 +61,67 @@
class Snapuserd final {
public:
- Snapuserd(const std::string& in_cow_device, const std::string& in_backing_store_device,
- const std::string& in_control_device)
- : cow_device_(in_cow_device),
- backing_store_device_(in_backing_store_device),
- control_device_(in_control_device),
- metadata_read_done_(false) {}
-
- bool Init();
- int Run();
- int ReadDmUserHeader();
- int WriteDmUserPayload(size_t size);
- int ConstructKernelCowHeader();
- int ReadMetadata();
- int ZerofillDiskExceptions(size_t read_size);
- int ReadDiskExceptions(chunk_t chunk, size_t size);
- int ReadData(chunk_t chunk, size_t size);
-
+ Snapuserd(const std::string& misc_name, const std::string& cow_device,
+ const std::string& backing_device);
+ bool InitBackingAndControlDevice();
+ bool InitCowDevice();
+ bool Run();
const std::string& GetControlDevicePath() { return control_device_; }
+ const std::string& GetMiscName() { return misc_name_; }
+ uint64_t GetNumSectors() { return num_sectors_; }
+ bool IsAttached() const { return ctrl_fd_ >= 0; }
private:
- int ProcessReplaceOp(const CowOperation* cow_op);
- int ProcessCopyOp(const CowOperation* cow_op);
- int ProcessZeroOp();
+ bool ReadDmUserHeader();
+ bool ReadDmUserPayload(void* buffer, size_t size);
+ bool WriteDmUserPayload(size_t size);
+ void ConstructKernelCowHeader();
+ bool ReadMetadata();
+ bool ZerofillDiskExceptions(size_t read_size);
+ bool ReadDiskExceptions(chunk_t chunk, size_t size);
+ bool ReadData(chunk_t chunk, size_t size);
+ bool IsChunkIdMetadata(chunk_t chunk);
+ chunk_t GetNextAllocatableChunkId(chunk_t chunk);
+
+ bool ProcessReplaceOp(const CowOperation* cow_op);
+ bool ProcessCopyOp(const CowOperation* cow_op);
+ bool ProcessZeroOp();
+
+ loff_t GetMergeStartOffset(void* merged_buffer, void* unmerged_buffer,
+ int* unmerged_exceptions);
+ int GetNumberOfMergedOps(void* merged_buffer, void* unmerged_buffer, loff_t offset,
+ int unmerged_exceptions);
+ bool AdvanceMergedOps(int merged_ops_cur_iter);
+ bool ProcessMergeComplete(chunk_t chunk, void* buffer);
+ sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
+ chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
std::string cow_device_;
std::string backing_store_device_;
std::string control_device_;
+ std::string misc_name_;
unique_fd cow_fd_;
unique_fd backing_store_fd_;
unique_fd ctrl_fd_;
uint32_t exceptions_per_area_;
+ uint64_t num_sectors_;
std::unique_ptr<ICowOpIter> cowop_iter_;
+ std::unique_ptr<ICowOpReverseIter> cowop_riter_;
std::unique_ptr<CowReader> reader_;
+ std::unique_ptr<CowWriter> writer_;
// Vector of disk exception which is a
// mapping of old-chunk to new-chunk
std::vector<std::unique_ptr<uint8_t[]>> vec_;
- // Index - Chunk ID
+ // Key - Chunk ID
// Value - cow operation
- std::vector<const CowOperation*> chunk_vec_;
+ std::unordered_map<chunk_t, const CowOperation*> chunk_map_;
- bool metadata_read_done_;
+ bool metadata_read_done_ = false;
BufferSink bufsink_;
};
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
index aaec229..aa9ba6e 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
@@ -30,7 +30,6 @@
static constexpr uint32_t PACKET_SIZE = 512;
-static constexpr char kSnapuserdSocketFirstStage[] = "snapuserd_first_stage";
static constexpr char kSnapuserdSocket[] = "snapuserd";
static constexpr char kSnapuserdFirstStagePidVar[] = "FIRST_STAGE_SNAPUSERD_PID";
@@ -38,10 +37,6 @@
// Ensure that the second-stage daemon for snapuserd is running.
bool EnsureSnapuserdStarted();
-// Start the first-stage version of snapuserd, returning its pid. This is used
-// by first-stage init, as well as vts_libsnapshot_test. On failure, -1 is returned.
-pid_t StartFirstStageSnapuserd();
-
class SnapuserdClient {
private:
android::base::unique_fd sockfd_;
@@ -58,13 +53,28 @@
std::chrono::milliseconds timeout_ms);
bool StopSnapuserd();
- int RestartSnapuserd(std::vector<std::vector<std::string>>& vec);
- bool InitializeSnapuserd(const std::string& cow_device, const std::string& backing_device,
- const std::string& control_device);
+
+ // Initializing a snapuserd handler is a three-step process:
+ //
+ // 1. Client invokes InitDmUserCow. This creates the snapuserd handler and validates the
+ // COW. The number of sectors required for the dm-user target is returned.
+ // 2. Client creates the device-mapper device with the dm-user target.
+ // 3. Client calls AttachControlDevice.
+ //
+ // The misc_name must be the "misc_name" given to dm-user in step 2.
+ //
+ uint64_t InitDmUserCow(const std::string& misc_name, const std::string& cow_device,
+ const std::string& backing_device);
+ bool AttachDmUser(const std::string& misc_name);
// Wait for snapuserd to disassociate with a dm-user control device. This
// must ONLY be called if the control device has already been deleted.
bool WaitForDeviceDelete(const std::string& control_device);
+
+ // Detach snapuserd. This shuts down the listener socket, and will cause
+ // snapuserd to gracefully exit once all handler threads have terminated.
+ // This should only be used on first-stage instances of snapuserd.
+ bool DetachSnapuserd();
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
index 1037c12..e8dbe6e 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
@@ -17,6 +17,13 @@
namespace android {
namespace snapshot {
+#define DM_USER_REQ_MAP_READ 0
+#define DM_USER_REQ_MAP_WRITE 1
+
+#define DM_USER_RESP_SUCCESS 0
+#define DM_USER_RESP_ERROR 1
+#define DM_USER_RESP_UNSUPPORTED 2
+
// Kernel COW header fields
static constexpr uint32_t SNAP_MAGIC = 0x70416e53;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
index 181ee33..1491aac 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
@@ -36,10 +36,12 @@
static constexpr uint32_t MAX_PACKET_SIZE = 512;
enum class DaemonOperations {
+ INIT,
START,
QUERY,
STOP,
DELETE,
+ DETACH,
INVALID,
};
@@ -55,7 +57,7 @@
const std::unique_ptr<Snapuserd>& snapuserd() const { return snapuserd_; }
std::thread& thread() { return thread_; }
- const std::string& GetControlDevice() const;
+ const std::string& GetMiscName() const;
};
class Stoppable {
@@ -84,7 +86,9 @@
std::vector<struct pollfd> watched_fds_;
std::mutex lock_;
- std::vector<std::unique_ptr<DmUserHandler>> dm_users_;
+
+ using HandlerList = std::vector<std::shared_ptr<DmUserHandler>>;
+ HandlerList dm_users_;
void AddWatchedFd(android::base::borrowed_fd fd);
void AcceptClient();
@@ -94,7 +98,7 @@
bool Receivemsg(android::base::borrowed_fd fd, const std::string& str);
void ShutdownThreads();
- bool WaitForDelete(const std::string& control_device);
+ bool RemoveHandler(const std::string& control_device, bool wait);
DaemonOperations Resolveop(std::string& input);
std::string GetDaemonStatus();
void Parsemsg(std::string const& msg, const char delim, std::vector<std::string>& out);
@@ -102,11 +106,12 @@
void SetTerminating() { terminating_ = true; }
bool IsTerminating() { return terminating_; }
- void RunThread(DmUserHandler* handler);
+ void RunThread(std::shared_ptr<DmUserHandler> handler);
+ void JoinAllThreads();
- // Remove a DmUserHandler from dm_users_, searching by its control device.
- // If none is found, return nullptr.
- std::unique_ptr<DmUserHandler> RemoveHandler(const std::string& control_device);
+ // Find a DmUserHandler within a lock.
+ HandlerList::iterator FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
+ const std::string& misc_name);
public:
SnapuserdServer() { terminating_ = false; }
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp
index 0df5664..da6fc9d 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp
@@ -18,6 +18,7 @@
#include <android-base/logging.h>
#include <android/snapshot/snapshot.pb.h>
+#include <storage_literals/storage_literals.h>
#include "dm_snapshot_internals.h"
#include "utility.h"
@@ -34,6 +35,10 @@
namespace android {
namespace snapshot {
+static constexpr uint64_t kBlockSize = 4096;
+
+using namespace android::storage_literals;
+
// Intersect two linear extents. If no intersection, return an extent with length 0.
static std::unique_ptr<Extent> Intersect(Extent* target_extent, Extent* existing_extent) {
// Convert target_extent and existing_extent to linear extents. Zero extents
@@ -138,6 +143,22 @@
}
uint64_t PartitionCowCreator::GetCowSize() {
+ if (compression_enabled) {
+ if (update == nullptr || !update->has_estimate_cow_size()) {
+ LOG(ERROR) << "Update manifest does not include a COW size";
+ return 0;
+ }
+
+ // Add an extra 2MB of wiggle room for any minor differences in labels/metadata
+ // that might come up.
+ auto size = update->estimate_cow_size() + 2_MiB;
+
+ // Align to nearest block.
+ size += kBlockSize - 1;
+ size &= ~(kBlockSize - 1);
+ return size;
+ }
+
// WARNING: The origin partition should be READ-ONLY
const uint64_t logical_block_size = current_metadata->logical_block_size();
const unsigned int sectors_per_block = logical_block_size / kSectorSize;
@@ -149,9 +170,9 @@
WriteExtent(&sc, de, sectors_per_block);
}
- if (operations == nullptr) return sc.cow_size_bytes();
+ if (update == nullptr) return sc.cow_size_bytes();
- for (const auto& iop : *operations) {
+ for (const auto& iop : update->operations()) {
const InstallOperation* written_op = &iop;
InstallOperation buf;
// Do not allocate space for extents that are going to be skipped
@@ -213,6 +234,9 @@
LOG(INFO) << "Remaining free space for COW: " << free_region_length << " bytes";
auto cow_size = GetCowSize();
+ if (!cow_size) {
+ return {};
+ }
// Compute the COW partition size.
uint64_t cow_partition_size = std::min(cow_size, free_region_length);
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.h b/fs_mgr/libsnapshot/partition_cow_creator.h
index 699f9a1..64d186b 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.h
+++ b/fs_mgr/libsnapshot/partition_cow_creator.h
@@ -36,6 +36,7 @@
using MetadataBuilder = android::fs_mgr::MetadataBuilder;
using Partition = android::fs_mgr::Partition;
using InstallOperation = chromeos_update_engine::InstallOperation;
+ using PartitionUpdate = chromeos_update_engine::PartitionUpdate;
template <typename T>
using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>;
@@ -50,11 +51,13 @@
MetadataBuilder* current_metadata = nullptr;
// The suffix of the current slot.
std::string current_suffix;
- // List of operations to be applied on the partition.
- const RepeatedPtrField<InstallOperation>* operations = nullptr;
+ // Partition information from the OTA manifest.
+ const PartitionUpdate* update = nullptr;
// Extra extents that are going to be invalidated during the update
// process.
std::vector<ChromeOSExtent> extra_extents = {};
+ // True if compression is enabled.
+ bool compression_enabled = false;
struct Return {
SnapshotStatus snapshot_status;
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index 2970222..e4b476f 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -145,13 +145,15 @@
auto cow_device_size = [](const std::vector<InstallOperation>& iopv, MetadataBuilder* builder_a,
MetadataBuilder* builder_b, Partition* system_b) {
- RepeatedInstallOperationPtr riop(iopv.begin(), iopv.end());
+ PartitionUpdate update;
+ *update.mutable_operations() = RepeatedInstallOperationPtr(iopv.begin(), iopv.end());
+
PartitionCowCreator creator{.target_metadata = builder_b,
.target_suffix = "_b",
.target_partition = system_b,
.current_metadata = builder_a,
.current_suffix = "_a",
- .operations = &riop};
+ .update = &update};
auto ret = creator.Run();
@@ -218,7 +220,7 @@
.target_partition = system_b,
.current_metadata = builder_a.get(),
.current_suffix = "_a",
- .operations = nullptr};
+ .update = nullptr};
auto ret = creator.Run();
@@ -228,6 +230,58 @@
ASSERT_EQ(0u, ret->snapshot_status.cow_partition_size());
}
+TEST_F(PartitionCowCreatorTest, CompressionEnabled) {
+ constexpr uint64_t super_size = 1_MiB;
+ auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);
+ ASSERT_NE(builder_a, nullptr);
+
+ auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);
+ ASSERT_NE(builder_b, nullptr);
+ auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system_b, nullptr);
+ ASSERT_TRUE(builder_b->ResizePartition(system_b, 128_KiB));
+
+ PartitionUpdate update;
+ update.set_estimate_cow_size(256_KiB);
+
+ PartitionCowCreator creator{.target_metadata = builder_b.get(),
+ .target_suffix = "_b",
+ .target_partition = system_b,
+ .current_metadata = builder_a.get(),
+ .current_suffix = "_a",
+ .compression_enabled = true,
+ .update = &update};
+
+ auto ret = creator.Run();
+ ASSERT_TRUE(ret.has_value());
+ ASSERT_EQ(ret->snapshot_status.cow_file_size(), 1458176);
+}
+
+TEST_F(PartitionCowCreatorTest, CompressionWithNoManifest) {
+ constexpr uint64_t super_size = 1_MiB;
+ auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);
+ ASSERT_NE(builder_a, nullptr);
+
+ auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);
+ ASSERT_NE(builder_b, nullptr);
+ auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system_b, nullptr);
+ ASSERT_TRUE(builder_b->ResizePartition(system_b, 128_KiB));
+
+ PartitionUpdate update;
+
+ PartitionCowCreator creator{.target_metadata = builder_b.get(),
+ .target_suffix = "_b",
+ .target_partition = system_b,
+ .current_metadata = builder_a.get(),
+ .current_suffix = "_a",
+ .compression_enabled = true,
+ .update = nullptr};
+
+ auto ret = creator.Run();
+ ASSERT_FALSE(ret.has_value());
+}
+
TEST(DmSnapshotInternals, CowSizeCalculator) {
SKIP_IF_NON_VIRTUAL_AB();
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index f9bb0dd..04c3ccc 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -75,7 +75,7 @@
using chromeos_update_engine::DeltaArchiveManifest;
using chromeos_update_engine::Extent;
using chromeos_update_engine::FileDescriptor;
-using chromeos_update_engine::InstallOperation;
+using chromeos_update_engine::PartitionUpdate;
template <typename T>
using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>;
using std::chrono::duration_cast;
@@ -116,10 +116,6 @@
metadata_dir_ = device_->GetMetadataDir();
}
-static inline bool IsCompressionEnabled() {
- return android::base::GetBoolProperty("ro.virtual_ab.compression.enabled", false);
-}
-
static std::string GetCowName(const std::string& snapshot_name) {
return snapshot_name + "-cow";
}
@@ -284,7 +280,7 @@
return false;
}
- if (!IsCompressionEnabled() && !EnsureNoOverflowSnapshot(lock.get())) {
+ if (!EnsureNoOverflowSnapshot(lock.get())) {
LOG(ERROR) << "Cannot ensure there are no overflow snapshots.";
return false;
}
@@ -353,6 +349,7 @@
status->set_state(SnapshotState::CREATED);
status->set_sectors_allocated(0);
status->set_metadata_sectors(0);
+ status->set_compression_enabled(IsCompressionEnabled());
if (!WriteSnapshotStatus(lock, *status)) {
PLOG(ERROR) << "Could not write snapshot status: " << status->name();
@@ -390,24 +387,6 @@
auto& dm = DeviceMapper::Instance();
- // Use the size of the base device for the COW device. It doesn't really
- // matter, it just needs to look similar enough so the kernel doesn't complain
- // about alignment or being too small.
- uint64_t base_sectors = 0;
- {
- unique_fd fd(open(base_device.c_str(), O_RDONLY | O_CLOEXEC));
- if (fd < 0) {
- PLOG(ERROR) << "open failed: " << base_device;
- return false;
- }
- auto dev_size = get_block_device_size(fd);
- if (!dev_size) {
- PLOG(ERROR) << "Could not determine block device size: " << base_device;
- return false;
- }
- base_sectors = dev_size / kSectorSize;
- }
-
// Use an extra decoration for first-stage init, so we can transition
// to a new table entry in second-stage.
std::string misc_name = name;
@@ -415,18 +394,31 @@
misc_name += "-init";
}
+ if (!EnsureSnapuserdConnected()) {
+ return false;
+ }
+
+ uint64_t base_sectors = snapuserd_client_->InitDmUserCow(misc_name, cow_file, base_device);
+ if (base_sectors == 0) {
+ LOG(ERROR) << "Failed to retrieve base_sectors from Snapuserd";
+ return false;
+ }
+
DmTable table;
table.Emplace<DmTargetUser>(0, base_sectors, misc_name);
if (!dm.CreateDevice(name, table, path, timeout_ms)) {
return false;
}
-
- if (!EnsureSnapuserdConnected()) {
+ if (!WaitForDevice(*path, timeout_ms)) {
return false;
}
auto control_device = "/dev/dm-user/" + misc_name;
- return snapuserd_client_->InitializeSnapuserd(cow_file, base_device, control_device);
+ if (!WaitForDevice(control_device, timeout_ms)) {
+ return false;
+ }
+
+ return snapuserd_client_->AttachDmUser(misc_name);
}
bool SnapshotManager::MapSnapshot(LockedFile* lock, const std::string& name,
@@ -1326,28 +1318,36 @@
size_t num_cows = 0;
size_t ok_cows = 0;
for (const auto& snapshot : snapshots) {
- std::string cow_name = GetDmUserCowName(snapshot);
- if (dm.GetState(cow_name) == DmDeviceState::INVALID) {
+ std::string user_cow_name = GetDmUserCowName(snapshot);
+ if (dm.GetState(user_cow_name) == DmDeviceState::INVALID) {
continue;
}
DeviceMapper::TargetInfo target;
- if (!GetSingleTarget(cow_name, TableQuery::Table, &target)) {
+ if (!GetSingleTarget(user_cow_name, TableQuery::Table, &target)) {
continue;
}
auto target_type = DeviceMapper::GetTargetType(target.spec);
if (target_type != "user") {
- LOG(ERROR) << "Unexpected target type for " << cow_name << ": " << target_type;
+ LOG(ERROR) << "Unexpected target type for " << user_cow_name << ": " << target_type;
continue;
}
num_cows++;
+ SnapshotStatus snapshot_status;
+ if (!ReadSnapshotStatus(lock.get(), snapshot, &snapshot_status)) {
+ LOG(ERROR) << "Unable to read snapshot status: " << snapshot;
+ continue;
+ }
+
+ auto misc_name = user_cow_name;
+
DmTable table;
- table.Emplace<DmTargetUser>(0, target.spec.length, cow_name);
- if (!dm.LoadTableAndActivate(cow_name, table)) {
- LOG(ERROR) << "Unable to swap tables for " << cow_name;
+ table.Emplace<DmTargetUser>(0, target.spec.length, misc_name);
+ if (!dm.LoadTableAndActivate(user_cow_name, table)) {
+ LOG(ERROR) << "Unable to swap tables for " << misc_name;
continue;
}
@@ -1357,23 +1357,41 @@
continue;
}
- std::string cow_device;
- if (!dm.GetDmDevicePathByName(GetCowName(snapshot), &cow_device)) {
- LOG(ERROR) << "Could not get device path for " << GetCowName(snapshot);
+ // If no partition was created (the COW exists entirely on /data), the
+ // device-mapper layering is different than if we had a partition.
+ std::string cow_image_name;
+ if (snapshot_status.cow_partition_size() == 0) {
+ cow_image_name = GetCowImageDeviceName(snapshot);
+ } else {
+ cow_image_name = GetCowName(snapshot);
+ }
+
+ std::string cow_image_device;
+ if (!dm.GetDmDevicePathByName(cow_image_name, &cow_image_device)) {
+ LOG(ERROR) << "Could not get device path for " << cow_image_name;
continue;
}
// Wait for ueventd to acknowledge and create the control device node.
- std::string control_device = "/dev/dm-user/" + cow_name;
- if (!android::fs_mgr::WaitForFile(control_device, 10s)) {
- LOG(ERROR) << "Could not find control device: " << control_device;
+ std::string control_device = "/dev/dm-user/" + misc_name;
+ if (!WaitForDevice(control_device, 10s)) {
continue;
}
- if (!snapuserd_client_->InitializeSnapuserd(cow_device, backing_device, control_device)) {
+ uint64_t base_sectors =
+ snapuserd_client_->InitDmUserCow(misc_name, cow_image_device, backing_device);
+ if (base_sectors == 0) {
+ // Unrecoverable as metadata reads from cow device failed
+ LOG(FATAL) << "Failed to retrieve base_sectors from Snapuserd";
+ return false;
+ }
+
+ CHECK(base_sectors == target.spec.length);
+
+ if (!snapuserd_client_->AttachDmUser(misc_name)) {
// This error is unrecoverable. We cannot proceed because reads to
// the underlying device will fail.
- LOG(FATAL) << "Could not initialize snapuserd for " << cow_name;
+ LOG(FATAL) << "Could not initialize snapuserd for " << user_cow_name;
return false;
}
@@ -1384,19 +1402,6 @@
LOG(ERROR) << "Could not transition all snapuserd consumers.";
return false;
}
-
- int pid;
- const char* pid_str = getenv(kSnapuserdFirstStagePidVar);
- if (pid_str && android::base::ParseInt(pid_str, &pid)) {
- if (kill(pid, SIGTERM) < 0 && errno != ESRCH) {
- LOG(ERROR) << "kill snapuserd failed";
- return false;
- }
- } else {
- LOG(ERROR) << "Could not find or parse " << kSnapuserdFirstStagePidVar
- << " for snapuserd pid";
- return false;
- }
return true;
}
@@ -1741,15 +1746,6 @@
}
}
- if (use_first_stage_snapuserd_) {
- // Remove the first-stage socket as a precaution, there is no need to
- // access the daemon anymore and we'll be killing it once second-stage
- // is running.
- auto socket = ANDROID_SOCKET_DIR + "/"s + kSnapuserdSocketFirstStage;
- snapuserd_client_ = nullptr;
- unlink(socket.c_str());
- }
-
LOG(INFO) << "Created logical partitions with snapshot.";
return true;
}
@@ -1895,22 +1891,29 @@
remaining_time = GetRemainingTime(params.timeout_ms, begin);
if (remaining_time.count() < 0) return false;
- if (context == SnapshotContext::Update && IsCompressionEnabled()) {
+ if (context == SnapshotContext::Update && live_snapshot_status->compression_enabled()) {
// Stop here, we can't run dm-user yet, the COW isn't built.
created_devices.Release();
return true;
}
- if (IsCompressionEnabled()) {
+ if (live_snapshot_status->compression_enabled()) {
auto name = GetDmUserCowName(params.GetPartitionName());
- // :TODO: need to force init to process uevents for these in first-stage.
std::string cow_path;
if (!GetMappedImageDevicePath(cow_name, &cow_path)) {
LOG(ERROR) << "Could not determine path for: " << cow_name;
return false;
}
+ // Ensure both |base_path| and |cow_path| are created, for snapuserd.
+ if (!WaitForDevice(base_path, remaining_time)) {
+ return false;
+ }
+ if (!WaitForDevice(cow_path, remaining_time)) {
+ return false;
+ }
+
std::string new_cow_device;
if (!MapDmUserCow(lock, name, cow_path, base_path, remaining_time, &new_cow_device)) {
LOG(ERROR) << "Could not map dm-user device for partition "
@@ -2050,18 +2053,18 @@
if (!EnsureSnapuserdConnected()) {
return false;
}
- if (!dm.DeleteDevice(dm_user_name)) {
+ if (!dm.DeleteDeviceIfExists(dm_user_name)) {
LOG(ERROR) << "Cannot unmap " << dm_user_name;
return false;
}
- auto control_device = "/dev/dm-user/" + dm_user_name;
- if (!snapuserd_client_->WaitForDeviceDelete(control_device)) {
+ if (!snapuserd_client_->WaitForDeviceDelete(dm_user_name)) {
LOG(ERROR) << "Failed to wait for " << dm_user_name << " control device to delete";
return false;
}
// Ensure the control device is gone so we don't run into ABA problems.
+ auto control_device = "/dev/dm-user/" + dm_user_name;
if (!android::fs_mgr::WaitForFileDeleted(control_device, 10s)) {
LOG(ERROR) << "Timed out waiting for " << control_device << " to unlink";
return false;
@@ -2253,6 +2256,7 @@
bool SnapshotManager::WriteUpdateState(LockedFile* lock, UpdateState state) {
SnapshotUpdateStatus status = {};
status.set_state(state);
+ status.set_compression_enabled(IsCompressionEnabled());
return WriteSnapshotUpdateStatus(lock, status);
}
@@ -2388,28 +2392,11 @@
return true;
}
- std::string socket;
- if (use_first_stage_snapuserd_) {
- auto pid = StartFirstStageSnapuserd();
- if (pid < 0) {
- LOG(ERROR) << "Failed to start snapuserd";
- return false;
- }
-
- auto pid_str = std::to_string(static_cast<int>(pid));
- if (setenv(kSnapuserdFirstStagePidVar, pid_str.c_str(), 1) < 0) {
- PLOG(ERROR) << "setenv failed storing the snapuserd pid";
- }
-
- socket = kSnapuserdSocketFirstStage;
- } else {
- if (!EnsureSnapuserdStarted()) {
- return false;
- }
- socket = kSnapuserdSocket;
+ if (!use_first_stage_snapuserd_ && !EnsureSnapuserdStarted()) {
+ return false;
}
- snapuserd_client_ = SnapuserdClient::Connect(socket, 10s);
+ snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s);
if (!snapuserd_client_) {
LOG(ERROR) << "Unable to connect to snapuserd";
return false;
@@ -2527,8 +2514,9 @@
.target_partition = nullptr,
.current_metadata = current_metadata.get(),
.current_suffix = current_suffix,
- .operations = nullptr,
+ .update = nullptr,
.extra_extents = {},
+ .compression_enabled = IsCompressionEnabled(),
};
auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
@@ -2572,12 +2560,11 @@
return Return::Error();
}
- std::map<std::string, const RepeatedPtrField<InstallOperation>*> install_operation_map;
+ std::map<std::string, const PartitionUpdate*> partition_map;
std::map<std::string, std::vector<Extent>> extra_extents_map;
for (const auto& partition_update : manifest.partitions()) {
auto suffixed_name = partition_update.partition_name() + target_suffix;
- auto&& [it, inserted] =
- install_operation_map.emplace(suffixed_name, &partition_update.operations());
+ auto&& [it, inserted] = partition_map.emplace(suffixed_name, &partition_update);
if (!inserted) {
LOG(ERROR) << "Duplicated partition " << partition_update.partition_name()
<< " in update manifest.";
@@ -2595,10 +2582,10 @@
for (auto* target_partition : ListPartitionsWithSuffix(target_metadata, target_suffix)) {
cow_creator->target_partition = target_partition;
- cow_creator->operations = nullptr;
- auto operations_it = install_operation_map.find(target_partition->name());
- if (operations_it != install_operation_map.end()) {
- cow_creator->operations = operations_it->second;
+ cow_creator->update = nullptr;
+ auto iter = partition_map.find(target_partition->name());
+ if (iter != partition_map.end()) {
+ cow_creator->update = iter->second;
} else {
LOG(INFO) << target_partition->name()
<< " isn't included in the payload, skipping the cow creation.";
@@ -2614,6 +2601,7 @@
// Compute the device sizes for the partition.
auto cow_creator_ret = cow_creator->Run();
if (!cow_creator_ret.has_value()) {
+ LOG(ERROR) << "PartitionCowCreator returned no value for " << target_partition->name();
return Return::Error();
}
@@ -2730,7 +2718,7 @@
return Return::Error();
}
- if (IsCompressionEnabled()) {
+ if (it->second.compression_enabled()) {
unique_fd fd(open(cow_path.c_str(), O_RDWR | O_CLOEXEC));
if (fd < 0) {
PLOG(ERROR) << "open " << cow_path << " failed for snapshot "
@@ -2824,7 +2812,7 @@
return nullptr;
}
- if (IsCompressionEnabled()) {
+ if (status.compression_enabled()) {
return OpenCompressedSnapshotWriter(lock.get(), source_device, params.GetPartitionName(),
status, paths);
}
@@ -3149,6 +3137,14 @@
auto& dm = DeviceMapper::Instance();
for (const auto& snapshot : snapshots) {
+ SnapshotStatus status;
+ if (!ReadSnapshotStatus(lock, snapshot, &status)) {
+ return false;
+ }
+ if (status.compression_enabled()) {
+ continue;
+ }
+
std::vector<DeviceMapper::TargetInfo> targets;
if (!dm.GetTableStatus(snapshot, &targets)) {
LOG(ERROR) << "Could not read snapshot device table: " << snapshot;
@@ -3273,5 +3269,47 @@
return true;
}
+bool SnapshotManager::WaitForDevice(const std::string& device,
+ std::chrono::milliseconds timeout_ms) {
+ if (!android::base::StartsWith(device, "/")) {
+ return true;
+ }
+
+ // In first-stage init, we rely on init setting a callback which can
+ // regenerate uevents and populate /dev for us.
+ if (uevent_regen_callback_) {
+ if (!uevent_regen_callback_(device)) {
+ LOG(ERROR) << "Failed to find device after regenerating uevents: " << device;
+ return false;
+ }
+ return true;
+ }
+
+ // Otherwise, the only kind of device we need to wait for is a dm-user
+ // misc device. Normal calls to DeviceMapper::CreateDevice() guarantee
+ // the path has been created.
+ if (!android::base::StartsWith(device, "/dev/dm-user/")) {
+ return true;
+ }
+
+ if (timeout_ms.count() == 0) {
+ LOG(ERROR) << "No timeout was specified to wait for device: " << device;
+ return false;
+ }
+ if (!android::fs_mgr::WaitForFile(device, timeout_ms)) {
+ LOG(ERROR) << "Timed out waiting for device to appear: " << device;
+ return false;
+ }
+ return true;
+}
+
+bool SnapshotManager::IsSnapuserdRequired() {
+ auto lock = LockExclusive();
+ if (!lock) return false;
+
+ auto status = ReadSnapshotUpdateStatus(lock.get());
+ return status.state() != UpdateState::None && status.compression_enabled();
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_reader.cpp b/fs_mgr/libsnapshot/snapshot_reader.cpp
index a4a652a..b56d879 100644
--- a/fs_mgr/libsnapshot/snapshot_reader.cpp
+++ b/fs_mgr/libsnapshot/snapshot_reader.cpp
@@ -90,6 +90,10 @@
op_iter_ = cow_->GetOpIter();
while (!op_iter_->Done()) {
const CowOperation* op = &op_iter_->Get();
+ if (op->type == kCowLabelOp || op->type == kCowFooterOp) {
+ op_iter_->Next();
+ continue;
+ }
if (op->new_block >= ops_.size()) {
ops_.resize(op->new_block + 1, nullptr);
}
@@ -274,7 +278,7 @@
return -1;
}
} else {
- LOG(ERROR) << "CompressedSnapshotReader unknown op type: " << op->type;
+ LOG(ERROR) << "CompressedSnapshotReader unknown op type: " << uint32_t(op->type);
errno = EINVAL;
return -1;
}
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 7fc64e5..c6a1786 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -82,7 +82,6 @@
std::string fake_super;
void MountMetadata();
-bool IsCompressionEnabled();
class SnapshotTest : public ::testing::Test {
public:
@@ -796,12 +795,15 @@
group_->add_partition_names("prd");
sys_ = manifest_.add_partitions();
sys_->set_partition_name("sys");
+ sys_->set_estimate_cow_size(6_MiB);
SetSize(sys_, 3_MiB);
vnd_ = manifest_.add_partitions();
vnd_->set_partition_name("vnd");
+ vnd_->set_estimate_cow_size(6_MiB);
SetSize(vnd_, 3_MiB);
prd_ = manifest_.add_partitions();
prd_->set_partition_name("prd");
+ prd_->set_estimate_cow_size(6_MiB);
SetSize(prd_, 3_MiB);
// Initialize source partition metadata using |manifest_|.
@@ -1045,7 +1047,11 @@
auto tgt = MetadataBuilder::New(*opener_, "super", 1);
ASSERT_NE(tgt, nullptr);
ASSERT_NE(nullptr, tgt->FindPartition("sys_b-cow"));
- ASSERT_NE(nullptr, tgt->FindPartition("vnd_b-cow"));
+ if (IsCompressionEnabled()) {
+ ASSERT_EQ(nullptr, tgt->FindPartition("vnd_b-cow"));
+ } else {
+ ASSERT_NE(nullptr, tgt->FindPartition("vnd_b-cow"));
+ }
ASSERT_EQ(nullptr, tgt->FindPartition("prd_b-cow"));
// Write some data to target partitions.
@@ -1453,10 +1459,6 @@
MetadataMountedTest().TearDown();
}
-bool IsCompressionEnabled() {
- return android::base::GetBoolProperty("ro.virtual_ab.compression.enabled", false);
-}
-
TEST_F(MetadataMountedTest, Android) {
auto device = sm->EnsureMetadataMounted();
EXPECT_NE(nullptr, device);
@@ -1758,9 +1760,6 @@
GTEST_SKIP() << "Skipping Virtual A/B Compression test";
}
- AutoKill auto_kill(StartFirstStageSnapuserd());
- ASSERT_TRUE(auto_kill.valid());
-
// Ensure a connection to the second-stage daemon, but use the first-stage
// code paths thereafter.
ASSERT_TRUE(sm->EnsureSnapuserdConnected());
diff --git a/fs_mgr/libsnapshot/snapshot_writer.cpp b/fs_mgr/libsnapshot/snapshot_writer.cpp
index 85ed156..8e379e4 100644
--- a/fs_mgr/libsnapshot/snapshot_writer.cpp
+++ b/fs_mgr/libsnapshot/snapshot_writer.cpp
@@ -113,14 +113,11 @@
}
bool CompressedSnapshotWriter::Initialize() {
- return cow_->Initialize(cow_device_, CowWriter::OpenMode::WRITE);
+ return cow_->Initialize(cow_device_);
}
-bool CompressedSnapshotWriter::InitializeAppend(std::optional<uint64_t> label) {
- if (label) {
- return cow_->InitializeAppend(cow_device_, *label);
- }
- return cow_->Initialize(cow_device_, CowWriter::OpenMode::APPEND);
+bool CompressedSnapshotWriter::InitializeAppend(uint64_t label) {
+ return cow_->InitializeAppend(cow_device_, label);
}
OnlineKernelSnapshotWriter::OnlineKernelSnapshotWriter(const CowOptions& options)
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd.cpp
index 40f26d6..49e6c3d 100644
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd.cpp
@@ -28,8 +28,8 @@
using namespace android::dm;
using android::base::unique_fd;
-#define DM_USER_MAP_READ 0
-#define DM_USER_MAP_WRITE 1
+#define SNAP_LOG(level) LOG(level) << misc_name_ << ": "
+#define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
static constexpr size_t PAYLOAD_SIZE = (1UL << 16);
@@ -66,11 +66,19 @@
return header;
}
+Snapuserd::Snapuserd(const std::string& misc_name, const std::string& cow_device,
+ const std::string& backing_device) {
+ misc_name_ = misc_name;
+ cow_device_ = cow_device;
+ backing_store_device_ = backing_device;
+ control_device_ = "/dev/dm-user/" + misc_name;
+}
+
// Construct kernel COW header in memory
// This header will be in sector 0. The IO
// request will always be 4k. After constructing
// the header, zero out the remaining block.
-int Snapuserd::ConstructKernelCowHeader() {
+void Snapuserd::ConstructKernelCowHeader() {
void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SIZE);
CHECK(buffer != nullptr);
@@ -82,25 +90,23 @@
dh->valid = SNAPSHOT_VALID;
dh->version = SNAPSHOT_DISK_VERSION;
dh->chunk_size = CHUNK_SIZE;
-
- return BLOCK_SIZE;
}
// Start the replace operation. This will read the
// internal COW format and if the block is compressed,
// it will be de-compressed.
-int Snapuserd::ProcessReplaceOp(const CowOperation* cow_op) {
+bool Snapuserd::ProcessReplaceOp(const CowOperation* cow_op) {
if (!reader_->ReadData(*cow_op, &bufsink_)) {
- LOG(ERROR) << "ReadData failed for chunk: " << cow_op->new_block;
- return -EIO;
+ SNAP_LOG(ERROR) << "ReadData failed for chunk: " << cow_op->new_block;
+ return false;
}
- return BLOCK_SIZE;
+ return true;
}
// Start the copy operation. This will read the backing
// block device which is represented by cow_op->source.
-int Snapuserd::ProcessCopyOp(const CowOperation* cow_op) {
+bool Snapuserd::ProcessCopyOp(const CowOperation* cow_op) {
void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SIZE);
CHECK(buffer != nullptr);
@@ -108,20 +114,20 @@
// if the successive blocks are contiguous.
if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SIZE,
cow_op->source * BLOCK_SIZE)) {
- LOG(ERROR) << "Copy-op failed. Read from backing store at: " << cow_op->source;
- return -1;
+ SNAP_LOG(ERROR) << "Copy-op failed. Read from backing store at: " << cow_op->source;
+ return false;
}
- return BLOCK_SIZE;
+ return true;
}
-int Snapuserd::ProcessZeroOp() {
+bool Snapuserd::ProcessZeroOp() {
// Zero out the entire block
void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SIZE);
CHECK(buffer != nullptr);
memset(buffer, 0, BLOCK_SIZE);
- return BLOCK_SIZE;
+ return true;
}
/*
@@ -146,11 +152,9 @@
* 3: Zero operation
*
*/
-int Snapuserd::ReadData(chunk_t chunk, size_t size) {
- int ret = 0;
-
+bool Snapuserd::ReadData(chunk_t chunk, size_t size) {
size_t read_size = size;
-
+ bool ret = true;
chunk_t chunk_key = chunk;
uint32_t stride;
lldiv_t divresult;
@@ -159,49 +163,49 @@
CHECK((read_size & (BLOCK_SIZE - 1)) == 0);
while (read_size > 0) {
- const CowOperation* cow_op = chunk_vec_[chunk_key];
+ const CowOperation* cow_op = chunk_map_[chunk_key];
CHECK(cow_op != nullptr);
- int result;
switch (cow_op->type) {
case kCowReplaceOp: {
- result = ProcessReplaceOp(cow_op);
+ ret = ProcessReplaceOp(cow_op);
break;
}
case kCowZeroOp: {
- result = ProcessZeroOp();
+ ret = ProcessZeroOp();
break;
}
case kCowCopyOp: {
- result = ProcessCopyOp(cow_op);
+ ret = ProcessCopyOp(cow_op);
break;
}
default: {
- LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
- ret = -EIO;
- goto done;
+ SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
+ ret = false;
+ break;
}
}
- if (result < 0) {
- ret = result;
- goto done;
+ if (!ret) {
+ SNAP_LOG(ERROR) << "ReadData failed for operation: " << cow_op->type;
+ return false;
}
// Update the buffer offset
bufsink_.UpdateBufferOffset(BLOCK_SIZE);
read_size -= BLOCK_SIZE;
- ret += BLOCK_SIZE;
// Start iterating the chunk incrementally; Since while
// constructing the metadata, we know that the chunk IDs
// are contiguous
chunk_key += 1;
+ if (cow_op->type == kCowCopyOp) CHECK(read_size == 0);
+
// This is similar to the way when chunk IDs were assigned
// in ReadMetadata().
//
@@ -221,8 +225,6 @@
}
}
-done:
-
// Reset the buffer offset
bufsink_.ResetBufferOffset();
return ret;
@@ -239,16 +241,18 @@
* When dm-snap starts parsing the buffer, it will stop
* reading metadata page once the buffer content is zero.
*/
-int Snapuserd::ZerofillDiskExceptions(size_t read_size) {
+bool Snapuserd::ZerofillDiskExceptions(size_t read_size) {
size_t size = exceptions_per_area_ * sizeof(struct disk_exception);
- if (read_size > size) return -EINVAL;
+ if (read_size > size) {
+ return false;
+ }
void* buffer = bufsink_.GetPayloadBuffer(size);
CHECK(buffer != nullptr);
memset(buffer, 0, size);
- return size;
+ return true;
}
/*
@@ -264,7 +268,7 @@
* Convert the chunk ID to index into the vector which gives us
* the metadata page.
*/
-int Snapuserd::ReadDiskExceptions(chunk_t chunk, size_t read_size) {
+bool Snapuserd::ReadDiskExceptions(chunk_t chunk, size_t read_size) {
uint32_t stride = exceptions_per_area_ + 1;
size_t size;
@@ -274,17 +278,185 @@
if (divresult.quot < vec_.size()) {
size = exceptions_per_area_ * sizeof(struct disk_exception);
- if (read_size > size) return -EINVAL;
+ if (read_size > size) {
+ return false;
+ }
void* buffer = bufsink_.GetPayloadBuffer(size);
CHECK(buffer != nullptr);
memcpy(buffer, vec_[divresult.quot].get(), size);
} else {
- size = ZerofillDiskExceptions(read_size);
+ return ZerofillDiskExceptions(read_size);
}
- return size;
+ return true;
+}
+
+loff_t Snapuserd::GetMergeStartOffset(void* merged_buffer, void* unmerged_buffer,
+ int* unmerged_exceptions) {
+ loff_t offset = 0;
+ *unmerged_exceptions = 0;
+
+ while (*unmerged_exceptions <= exceptions_per_area_) {
+ struct disk_exception* merged_de =
+ reinterpret_cast<struct disk_exception*>((char*)merged_buffer + offset);
+ struct disk_exception* cow_de =
+ reinterpret_cast<struct disk_exception*>((char*)unmerged_buffer + offset);
+
+ // Unmerged op by the kernel
+ if (merged_de->old_chunk != 0) {
+ CHECK(merged_de->new_chunk != 0);
+ CHECK(merged_de->old_chunk == cow_de->old_chunk);
+ CHECK(merged_de->new_chunk == cow_de->new_chunk);
+
+ offset += sizeof(struct disk_exception);
+ *unmerged_exceptions += 1;
+ continue;
+ }
+
+ // Merge complete on this exception. However, we don't know how many
+ // merged in this cycle; hence break here.
+ CHECK(merged_de->new_chunk == 0);
+ CHECK(merged_de->old_chunk == 0);
+
+ break;
+ }
+
+ CHECK(!(*unmerged_exceptions == exceptions_per_area_));
+
+ SNAP_LOG(DEBUG) << "Unmerged_Exceptions: " << *unmerged_exceptions << " Offset: " << offset;
+ return offset;
+}
+
+int Snapuserd::GetNumberOfMergedOps(void* merged_buffer, void* unmerged_buffer, loff_t offset,
+ int unmerged_exceptions) {
+ int merged_ops_cur_iter = 0;
+
+ // Find the operations which are merged in this cycle.
+ while ((unmerged_exceptions + merged_ops_cur_iter) < exceptions_per_area_) {
+ struct disk_exception* merged_de =
+ reinterpret_cast<struct disk_exception*>((char*)merged_buffer + offset);
+ struct disk_exception* cow_de =
+ reinterpret_cast<struct disk_exception*>((char*)unmerged_buffer + offset);
+
+ CHECK(merged_de->new_chunk == 0);
+ CHECK(merged_de->old_chunk == 0);
+
+ if (cow_de->new_chunk != 0) {
+ merged_ops_cur_iter += 1;
+ offset += sizeof(struct disk_exception);
+ // zero out to indicate that operation is merged.
+ cow_de->old_chunk = 0;
+ cow_de->new_chunk = 0;
+ } else if (cow_de->old_chunk == 0) {
+ // Already merged op in previous iteration or
+ // This could also represent a partially filled area.
+ //
+ // If the op was merged in previous cycle, we don't have
+ // to count them.
+ CHECK(cow_de->new_chunk == 0);
+ break;
+ } else {
+ SNAP_LOG(ERROR) << "Error in merge operation. Found invalid metadata";
+ SNAP_LOG(ERROR) << "merged_de-old-chunk: " << merged_de->old_chunk;
+ SNAP_LOG(ERROR) << "merged_de-new-chunk: " << merged_de->new_chunk;
+ SNAP_LOG(ERROR) << "cow_de-old-chunk: " << cow_de->old_chunk;
+ SNAP_LOG(ERROR) << "cow_de-new-chunk: " << cow_de->new_chunk;
+ return -1;
+ }
+ }
+
+ return merged_ops_cur_iter;
+}
+
+bool Snapuserd::AdvanceMergedOps(int merged_ops_cur_iter) {
+ // Advance the merge operation pointer in the
+ // vector.
+ //
+ // cowop_iter_ is already initialized in ReadMetadata(). Just resume the
+ // merge process
+ while (!cowop_iter_->Done() && merged_ops_cur_iter) {
+ const CowOperation* cow_op = &cowop_iter_->Get();
+ CHECK(cow_op != nullptr);
+
+ if (cow_op->type == kCowFooterOp || cow_op->type == kCowLabelOp) {
+ cowop_iter_->Next();
+ continue;
+ }
+
+ if (!(cow_op->type == kCowReplaceOp || cow_op->type == kCowZeroOp ||
+ cow_op->type == kCowCopyOp)) {
+ SNAP_LOG(ERROR) << "Unknown operation-type found during merge: " << cow_op->type;
+ return false;
+ }
+
+ merged_ops_cur_iter -= 1;
+ SNAP_LOG(DEBUG) << "Merge op found of type " << cow_op->type
+ << "Pending-merge-ops: " << merged_ops_cur_iter;
+ cowop_iter_->Next();
+ }
+
+ if (cowop_iter_->Done()) {
+ CHECK(merged_ops_cur_iter == 0);
+ SNAP_LOG(DEBUG) << "All cow operations merged successfully in this cycle";
+ }
+
+ return true;
+}
+
+bool Snapuserd::ProcessMergeComplete(chunk_t chunk, void* buffer) {
+ uint32_t stride = exceptions_per_area_ + 1;
+ CowHeader header;
+
+ if (!reader_->GetHeader(&header)) {
+ SNAP_LOG(ERROR) << "Failed to get header";
+ return false;
+ }
+
+ // ChunkID to vector index
+ lldiv_t divresult = lldiv(chunk, stride);
+ CHECK(divresult.quot < vec_.size());
+ SNAP_LOG(DEBUG) << "ProcessMergeComplete: chunk: " << chunk
+ << " Metadata-Index: " << divresult.quot;
+
+ int unmerged_exceptions = 0;
+ loff_t offset = GetMergeStartOffset(buffer, vec_[divresult.quot].get(), &unmerged_exceptions);
+
+ int merged_ops_cur_iter =
+ GetNumberOfMergedOps(buffer, vec_[divresult.quot].get(), offset, unmerged_exceptions);
+
+ // There should be at least one operation merged in this cycle
+ CHECK(merged_ops_cur_iter > 0);
+ if (!AdvanceMergedOps(merged_ops_cur_iter)) return false;
+
+ header.num_merge_ops += merged_ops_cur_iter;
+ reader_->UpdateMergeProgress(merged_ops_cur_iter);
+ if (!writer_->CommitMerge(merged_ops_cur_iter)) {
+ SNAP_LOG(ERROR) << "CommitMerge failed...";
+ return false;
+ }
+
+ SNAP_LOG(DEBUG) << "Merge success";
+ return true;
+}
+
+bool Snapuserd::IsChunkIdMetadata(chunk_t chunk) {
+ uint32_t stride = exceptions_per_area_ + 1;
+ lldiv_t divresult = lldiv(chunk, stride);
+
+ return (divresult.rem == NUM_SNAPSHOT_HDR_CHUNKS);
+}
+
+// Find the next free chunk-id to be assigned. Check if the next free
+// chunk-id represents a metadata page. If so, skip it.
+chunk_t Snapuserd::GetNextAllocatableChunkId(chunk_t chunk) {
+ chunk_t next_chunk = chunk + 1;
+
+ if (IsChunkIdMetadata(next_chunk)) {
+ next_chunk += 1;
+ }
+ return next_chunk;
}
/*
@@ -304,12 +476,26 @@
* This represents the old_chunk in the kernel COW format
* 4: We need to assign new_chunk for a corresponding old_chunk
* 5: The algorithm is similar to how kernel assigns chunk number
- * while creating exceptions.
+ * while creating exceptions. However, there are few cases
+ * which needs to be addressed here:
+ * a: During merge process, kernel scans the metadata page
+ * from backwards when merge is initiated. Since, we need
+ * to make sure that the merge ordering follows our COW format,
+ * we read the COW operation from backwards and populate the
+ * metadata so that when kernel starts the merging from backwards,
+ * those ops correspond to the beginning of our COW format.
+ * b: Kernel can merge successive operations if the two chunk IDs
+ * are contiguous. This can be problematic when there is a crash
+ * during merge; specifically when the merge operation has dependency.
+ * These dependencies can only happen during copy operations.
+ *
+ * To avoid this problem, we make sure that no two copy-operations
+ * do not have contiguous chunk IDs. Additionally, we make sure
+ * that each copy operation is merged individually.
* 6: Use a monotonically increasing chunk number to assign the
* new_chunk
* 7: Each chunk-id represents either a: Metadata page or b: Data page
- * 8: Chunk-id representing a data page is stored in a vector. Index is the
- * chunk-id and value is the pointer to the CowOperation
+ * 8: Chunk-id representing a data page is stored in a map.
* 9: Chunk-id representing a metadata page is converted into a vector
* index. We store this in vector as kernel requests metadata during
* two stage:
@@ -324,130 +510,128 @@
* exceptions_per_area_
* 12: Kernel will stop issuing metadata IO request when new-chunk ID is 0.
*/
-int Snapuserd::ReadMetadata() {
+bool Snapuserd::ReadMetadata() {
reader_ = std::make_unique<CowReader>();
CowHeader header;
- CowFooter footer;
+ CowOptions options;
+ bool prev_copy_op = false;
+ bool metadata_found = false;
+
+ SNAP_LOG(DEBUG) << "ReadMetadata Start...";
if (!reader_->Parse(cow_fd_)) {
- LOG(ERROR) << "Failed to parse";
- return 1;
+ SNAP_LOG(ERROR) << "Failed to parse";
+ return false;
}
if (!reader_->GetHeader(&header)) {
- LOG(ERROR) << "Failed to get header";
- return 1;
- }
-
- if (!reader_->GetFooter(&footer)) {
- LOG(ERROR) << "Failed to get footer";
- return 1;
+ SNAP_LOG(ERROR) << "Failed to get header";
+ return false;
}
CHECK(header.block_size == BLOCK_SIZE);
- LOG(DEBUG) << "Num-ops: " << std::hex << footer.op.num_ops;
- LOG(DEBUG) << "ops-size: " << std::hex << footer.op.ops_size;
+ SNAP_LOG(DEBUG) << "Merge-ops: " << header.num_merge_ops;
- cowop_iter_ = reader_->GetOpIter();
+ writer_ = std::make_unique<CowWriter>(options);
+ writer_->InitializeMerge(cow_fd_.get(), &header);
- if (cowop_iter_ == nullptr) {
- LOG(ERROR) << "Failed to get cowop_iter";
- return 1;
- }
+ // Initialize the iterator for reading metadata
+ cowop_riter_ = reader_->GetRevOpIter();
exceptions_per_area_ = (CHUNK_SIZE << SECTOR_SHIFT) / sizeof(struct disk_exception);
// Start from chunk number 2. Chunk 0 represents header and chunk 1
// represents first metadata page.
chunk_t next_free = NUM_SNAPSHOT_HDR_CHUNKS + 1;
- chunk_vec_.push_back(nullptr);
- chunk_vec_.push_back(nullptr);
loff_t offset = 0;
std::unique_ptr<uint8_t[]> de_ptr =
std::make_unique<uint8_t[]>(exceptions_per_area_ * sizeof(struct disk_exception));
// This memset is important. Kernel will stop issuing IO when new-chunk ID
- // is 0. When Area is not filled completely will all 256 exceptions,
+ // is 0. When Area is not filled completely with all 256 exceptions,
// this memset will ensure that metadata read is completed.
memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
size_t num_ops = 0;
- while (!cowop_iter_->Done()) {
- const CowOperation* cow_op = &cowop_iter_->Get();
+ while (!cowop_riter_->Done()) {
+ const CowOperation* cow_op = &cowop_riter_->Get();
struct disk_exception* de =
reinterpret_cast<struct disk_exception*>((char*)de_ptr.get() + offset);
if (cow_op->type == kCowFooterOp || cow_op->type == kCowLabelOp) {
- cowop_iter_->Next();
+ cowop_riter_->Next();
continue;
}
if (!(cow_op->type == kCowReplaceOp || cow_op->type == kCowZeroOp ||
cow_op->type == kCowCopyOp)) {
- LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
- return 1;
+ SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
+ return false;
}
+ metadata_found = true;
+ if ((cow_op->type == kCowCopyOp || prev_copy_op)) {
+ next_free = GetNextAllocatableChunkId(next_free);
+ }
+
+ prev_copy_op = (cow_op->type == kCowCopyOp);
+
// Construct the disk-exception
de->old_chunk = cow_op->new_block;
de->new_chunk = next_free;
- LOG(DEBUG) << "Old-chunk: " << de->old_chunk << "New-chunk: " << de->new_chunk;
+ SNAP_LOG(DEBUG) << "Old-chunk: " << de->old_chunk << "New-chunk: " << de->new_chunk;
- // Store operation pointer. Note, new-chunk ID is the index
- chunk_vec_.push_back(cow_op);
- CHECK(next_free == (chunk_vec_.size() - 1));
+ // Store operation pointer.
+ chunk_map_[next_free] = cow_op;
+ num_ops += 1;
offset += sizeof(struct disk_exception);
- cowop_iter_->Next();
+ cowop_riter_->Next();
- // Find the next free chunk-id to be assigned. Check if the next free
- // chunk-id represents a metadata page. If so, skip it.
- next_free += 1;
- uint32_t stride = exceptions_per_area_ + 1;
- lldiv_t divresult = lldiv(next_free, stride);
- num_ops += 1;
-
- if (divresult.rem == NUM_SNAPSHOT_HDR_CHUNKS) {
- CHECK(num_ops == exceptions_per_area_);
+ if (num_ops == exceptions_per_area_) {
// Store it in vector at the right index. This maps the chunk-id to
// vector index.
vec_.push_back(std::move(de_ptr));
offset = 0;
num_ops = 0;
- chunk_t metadata_chunk = (next_free - exceptions_per_area_ - NUM_SNAPSHOT_HDR_CHUNKS);
-
- LOG(DEBUG) << "Area: " << vec_.size() - 1;
- LOG(DEBUG) << "Metadata-chunk: " << metadata_chunk;
- LOG(DEBUG) << "Sector number of Metadata-chunk: " << (metadata_chunk << CHUNK_SHIFT);
-
// Create buffer for next area
de_ptr = std::make_unique<uint8_t[]>(exceptions_per_area_ *
sizeof(struct disk_exception));
memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
- // Since this is a metadata, store at this index
- chunk_vec_.push_back(nullptr);
-
- // Find the next free chunk-id
- next_free += 1;
- if (cowop_iter_->Done()) {
+ if (cowop_riter_->Done()) {
vec_.push_back(std::move(de_ptr));
+ SNAP_LOG(DEBUG) << "ReadMetadata() completed; Number of Areas: " << vec_.size();
}
}
+
+ next_free = GetNextAllocatableChunkId(next_free);
}
- // Partially filled area
- if (num_ops) {
- LOG(DEBUG) << "Partially filled area num_ops: " << num_ops;
+ // Partially filled area or there is no metadata
+ // If there is no metadata, fill with zero so that kernel
+ // is aware that merge is completed.
+ if (num_ops || !metadata_found) {
vec_.push_back(std::move(de_ptr));
+ SNAP_LOG(DEBUG) << "ReadMetadata() completed. Partially filled area num_ops: " << num_ops
+ << "Areas : " << vec_.size();
}
- return 0;
+ SNAP_LOG(DEBUG) << "ReadMetadata() completed. chunk_id: " << next_free
+ << "Num Sector: " << ChunkToSector(next_free);
+
+ // Initialize the iterator for merging
+ cowop_iter_ = reader_->GetOpIter();
+
+ // Total number of sectors required for creating dm-user device
+ num_sectors_ = ChunkToSector(next_free);
+ metadata_read_done_ = true;
+ return true;
}
void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
@@ -461,49 +645,39 @@
// Read Header from dm-user misc device. This gives
// us the sector number for which IO is issued by dm-snapshot device
-int Snapuserd::ReadDmUserHeader() {
- int ret;
-
- ret = read(ctrl_fd_, bufsink_.GetBufPtr(), sizeof(struct dm_user_header));
- if (ret < 0) {
- PLOG(ERROR) << "Control-read failed with: " << ret;
- return ret;
+bool Snapuserd::ReadDmUserHeader() {
+ if (!android::base::ReadFully(ctrl_fd_, bufsink_.GetBufPtr(), sizeof(struct dm_user_header))) {
+ SNAP_PLOG(ERROR) << "Control-read failed";
+ return false;
}
- return sizeof(struct dm_user_header);
+ return true;
}
// Send the payload/data back to dm-user misc device.
-int Snapuserd::WriteDmUserPayload(size_t size) {
+bool Snapuserd::WriteDmUserPayload(size_t size) {
if (!android::base::WriteFully(ctrl_fd_, bufsink_.GetBufPtr(),
sizeof(struct dm_user_header) + size)) {
- PLOG(ERROR) << "Write to dm-user failed";
- return -1;
+ SNAP_PLOG(ERROR) << "Write to dm-user failed";
+ return false;
}
- return sizeof(struct dm_user_header) + size;
+ return true;
}
-bool Snapuserd::Init() {
- backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));
- if (backing_store_fd_ < 0) {
- PLOG(ERROR) << "Open Failed: " << backing_store_device_;
+bool Snapuserd::ReadDmUserPayload(void* buffer, size_t size) {
+ if (!android::base::ReadFully(ctrl_fd_, buffer, size)) {
+ SNAP_PLOG(ERROR) << "ReadDmUserPayload failed";
return false;
}
+ return true;
+}
+
+bool Snapuserd::InitCowDevice() {
cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
if (cow_fd_ < 0) {
- PLOG(ERROR) << "Open Failed: " << cow_device_;
- return false;
- }
-
- std::string control_path = GetControlDevicePath();
-
- LOG(DEBUG) << "Opening control device " << control_path;
-
- ctrl_fd_.reset(open(control_path.c_str(), O_RDWR));
- if (ctrl_fd_ < 0) {
- PLOG(ERROR) << "Unable to open " << control_path;
+ SNAP_PLOG(ERROR) << "Open Failed: " << cow_device_;
return false;
}
@@ -514,34 +688,48 @@
size_t buf_size = sizeof(struct dm_user_header) + PAYLOAD_SIZE;
bufsink_.Initialize(buf_size);
+ return ReadMetadata();
+}
+
+bool Snapuserd::InitBackingAndControlDevice() {
+ backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));
+ if (backing_store_fd_ < 0) {
+ SNAP_PLOG(ERROR) << "Open Failed: " << backing_store_device_;
+ return false;
+ }
+
+ ctrl_fd_.reset(open(control_device_.c_str(), O_RDWR));
+ if (ctrl_fd_ < 0) {
+ SNAP_PLOG(ERROR) << "Unable to open " << control_device_;
+ return false;
+ }
+
return true;
}
-int Snapuserd::Run() {
- int ret = 0;
-
+bool Snapuserd::Run() {
struct dm_user_header* header = bufsink_.GetHeaderPtr();
bufsink_.Clear();
- ret = ReadDmUserHeader();
- if (ret < 0) return ret;
+ if (!ReadDmUserHeader()) {
+ SNAP_LOG(ERROR) << "ReadDmUserHeader failed";
+ return false;
+ }
- LOG(DEBUG) << "dm-user returned " << ret << " bytes";
-
- LOG(DEBUG) << "msg->seq: " << std::hex << header->seq;
- LOG(DEBUG) << "msg->type: " << std::hex << header->type;
- LOG(DEBUG) << "msg->flags: " << std::hex << header->flags;
- LOG(DEBUG) << "msg->sector: " << std::hex << header->sector;
- LOG(DEBUG) << "msg->len: " << std::hex << header->len;
+ SNAP_LOG(DEBUG) << "msg->seq: " << std::hex << header->seq;
+ SNAP_LOG(DEBUG) << "msg->type: " << std::hex << header->type;
+ SNAP_LOG(DEBUG) << "msg->flags: " << std::hex << header->flags;
+ SNAP_LOG(DEBUG) << "msg->sector: " << std::hex << header->sector;
+ SNAP_LOG(DEBUG) << "msg->len: " << std::hex << header->len;
switch (header->type) {
- case DM_USER_MAP_READ: {
+ case DM_USER_REQ_MAP_READ: {
size_t remaining_size = header->len;
loff_t offset = 0;
- ret = 0;
do {
size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
+ header->type = DM_USER_RESP_SUCCESS;
// Request to sector 0 is always for kernel
// representation of COW header. This IO should be only
@@ -549,81 +737,113 @@
// never see multiple IO requests. Additionally this IO
// will always be a single 4k.
if (header->sector == 0) {
- // Read the metadata from internal COW device
- // and build the in-memory data structures
- // for all the operations in the internal COW.
- if (!metadata_read_done_ && ReadMetadata()) {
- LOG(ERROR) << "Metadata read failed";
- return 1;
- }
- metadata_read_done_ = true;
-
+ CHECK(metadata_read_done_ == true);
CHECK(read_size == BLOCK_SIZE);
- ret = ConstructKernelCowHeader();
- if (ret < 0) return ret;
+ ConstructKernelCowHeader();
+ SNAP_LOG(DEBUG) << "Kernel header constructed";
} else {
// Convert the sector number to a chunk ID.
//
// Check if the chunk ID represents a metadata
// page. If the chunk ID is not found in the
// vector, then it points to a metadata page.
- chunk_t chunk = (header->sector >> CHUNK_SHIFT);
+ chunk_t chunk = SectorToChunk(header->sector);
- if (chunk >= chunk_vec_.size()) {
- ret = ZerofillDiskExceptions(read_size);
- if (ret < 0) {
- LOG(ERROR) << "ZerofillDiskExceptions failed";
- return ret;
- }
- } else if (chunk_vec_[chunk] == nullptr) {
- ret = ReadDiskExceptions(chunk, read_size);
- if (ret < 0) {
- LOG(ERROR) << "ReadDiskExceptions failed";
- return ret;
+ if (chunk_map_.find(chunk) == chunk_map_.end()) {
+ if (!ReadDiskExceptions(chunk, read_size)) {
+ SNAP_LOG(ERROR) << "ReadDiskExceptions failed for chunk id: " << chunk
+ << "Sector: " << header->sector;
+ header->type = DM_USER_RESP_ERROR;
+ } else {
+ SNAP_LOG(DEBUG) << "ReadDiskExceptions success for chunk id: " << chunk
+ << "Sector: " << header->sector;
}
} else {
chunk_t num_chunks_read = (offset >> BLOCK_SHIFT);
- ret = ReadData(chunk + num_chunks_read, read_size);
- if (ret < 0) {
- LOG(ERROR) << "ReadData failed";
- // TODO: Bug 168259959: All the error paths from this function
- // should send error code to dm-user thereby IO
- // terminates with an error from dm-user. Returning
- // here without sending error code will block the
- // IO.
- return ret;
+ if (!ReadData(chunk + num_chunks_read, read_size)) {
+ SNAP_LOG(ERROR) << "ReadData failed for chunk id: " << chunk
+ << "Sector: " << header->sector;
+ header->type = DM_USER_RESP_ERROR;
+ } else {
+ SNAP_LOG(DEBUG) << "ReadData success for chunk id: " << chunk
+ << "Sector: " << header->sector;
}
}
}
- ssize_t written = WriteDmUserPayload(ret);
- if (written < 0) return written;
-
- remaining_size -= ret;
- offset += ret;
- if (remaining_size) {
- LOG(DEBUG) << "Write done ret: " << ret
- << " remaining size: " << remaining_size;
+ // Daemon will not be terminated if there is any error. We will
+ // just send the error back to dm-user.
+ if (!WriteDmUserPayload(read_size)) {
+ return false;
}
+
+ remaining_size -= read_size;
+ offset += read_size;
} while (remaining_size);
break;
}
- case DM_USER_MAP_WRITE: {
- // TODO: Bug: 168311203: After merge operation is completed, kernel issues write
- // to flush all the exception mappings where the merge is
- // completed. If dm-user routes the WRITE IO, we need to clear
- // in-memory data structures representing those exception
- // mappings.
- abort();
+ case DM_USER_REQ_MAP_WRITE: {
+ // device mapper has the capability to allow
+ // targets to flush the cache when writes are completed. This
+ // is controlled by each target by a flag "flush_supported".
+ // This flag is set by dm-user. When flush is supported,
+ // a number of zero-length bio's will be submitted to
+ // the target for the purpose of flushing cache. It is the
+ // responsibility of the target driver - which is dm-user in this
+ // case, to remap these bio's to the underlying device. Since,
+ // there is no underlying device for dm-user, this zero length
+ // bio's gets routed to daemon.
+ //
+ // Flush operations are generated post merge by dm-snap by having
+ // REQ_PREFLUSH flag set. Snapuser daemon doesn't have anything
+ // to flush per se; hence, just respond back with a success message.
+ if (header->sector == 0) {
+ CHECK(header->len == 0);
+ header->type = DM_USER_RESP_SUCCESS;
+ if (!WriteDmUserPayload(0)) {
+ return false;
+ }
+ break;
+ }
+
+ size_t remaining_size = header->len;
+ size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
+ CHECK(read_size == BLOCK_SIZE);
+
+ CHECK(header->sector > 0);
+ chunk_t chunk = SectorToChunk(header->sector);
+ CHECK(chunk_map_.find(chunk) == chunk_map_.end());
+
+ void* buffer = bufsink_.GetPayloadBuffer(read_size);
+ CHECK(buffer != nullptr);
+ header->type = DM_USER_RESP_SUCCESS;
+
+ if (!ReadDmUserPayload(buffer, read_size)) {
+ SNAP_LOG(ERROR) << "ReadDmUserPayload failed for chunk id: " << chunk
+ << "Sector: " << header->sector;
+ header->type = DM_USER_RESP_ERROR;
+ }
+
+ if (header->type == DM_USER_RESP_SUCCESS && !ProcessMergeComplete(chunk, buffer)) {
+ SNAP_LOG(ERROR) << "ProcessMergeComplete failed for chunk id: " << chunk
+ << "Sector: " << header->sector;
+ header->type = DM_USER_RESP_ERROR;
+ } else {
+ SNAP_LOG(DEBUG) << "ProcessMergeComplete success for chunk id: " << chunk
+ << "Sector: " << header->sector;
+ }
+
+ if (!WriteDmUserPayload(0)) {
+ return false;
+ }
+
break;
}
}
- LOG(DEBUG) << "read() finished, next message";
-
- return 0;
+ return true;
}
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd_client.cpp
index 5650139..7282bff 100644
--- a/fs_mgr/libsnapshot/snapuserd_client.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_client.cpp
@@ -27,9 +27,12 @@
#include <unistd.h>
#include <chrono>
+#include <sstream>
#include <android-base/logging.h>
+#include <android-base/parseint.h>
#include <android-base/properties.h>
+#include <android-base/strings.h>
#include <libsnapshot/snapuserd_client.h>
namespace android {
@@ -51,25 +54,6 @@
return true;
}
-pid_t StartFirstStageSnapuserd() {
- pid_t pid = fork();
- if (pid < 0) {
- PLOG(ERROR) << "fork failed";
- return pid;
- }
- if (pid != 0) {
- return pid;
- }
-
- std::string arg0 = "/system/bin/snapuserd";
- std::string arg1 = kSnapuserdSocketFirstStage;
- char* const argv[] = {arg0.data(), arg1.data(), nullptr};
- if (execv(arg0.c_str(), argv) < 0) {
- PLOG(FATAL) << "execv failed";
- }
- return pid;
-}
-
SnapuserdClient::SnapuserdClient(android::base::unique_fd&& sockfd) : sockfd_(std::move(sockfd)) {}
static inline bool IsRetryErrno() {
@@ -114,7 +98,7 @@
std::string str = Receivemsg();
// If the daemon is passive then fallback to secondary active daemon. Daemon
- // is passive during transition phase. Please see RestartSnapuserd()
+ // is passive during transition phase.
if (str.find("passive") != std::string::npos) {
LOG(ERROR) << "Snapuserd is terminating";
return false;
@@ -180,10 +164,8 @@
return true;
}
-bool SnapuserdClient::InitializeSnapuserd(const std::string& cow_device,
- const std::string& backing_device,
- const std::string& control_device) {
- std::string msg = "start," + cow_device + "," + backing_device + "," + control_device;
+bool SnapuserdClient::AttachDmUser(const std::string& misc_name) {
+ std::string msg = "start," + misc_name;
if (!Sendmsg(msg)) {
LOG(ERROR) << "Failed to send message " << msg << " to snapuserd daemon";
return false;
@@ -199,77 +181,41 @@
return true;
}
-/*
- * Transition from first stage snapuserd daemon to second stage daemon involves
- * series of steps viz:
- *
- * 1: Create new dm-user devices - This is done by libsnapshot
- *
- * 2: Spawn the new snapuserd daemon - This is the second stage daemon which
- * will start the server but the dm-user misc devices is not binded yet.
- *
- * 3: Vector to this function contains pair of cow_device and source device.
- * Ex: {{system_cow,system_a}, {product_cow, product_a}, {vendor_cow,
- * vendor_a}}. This vector will be populated by the libsnapshot.
- *
- * 4: Initialize the Second stage daemon passing the information from the
- * vector. This will bind the daemon with dm-user misc device and will be ready
- * to serve the IO. Up until this point, first stage daemon is still active.
- * However, client library will mark the first stage daemon as passive and hence
- * all the control message from hereon will be sent to active second stage
- * daemon.
- *
- * 5: Create new dm-snapshot table. This is done by libsnapshot. When new table
- * is created, kernel will issue metadata read once again which will be served
- * by second stage daemon. However, any active IO will still be served by first
- * stage daemon.
- *
- * 6: Swap the snapshot table atomically - This is done by libsnapshot. Once
- * the swapping is done, all the IO will be served by second stage daemon.
- *
- * 7: Stop the first stage daemon. After this point second stage daemon is
- * completely active to serve the IO and merging process.
- *
- */
-int SnapuserdClient::RestartSnapuserd(std::vector<std::vector<std::string>>& vec) {
- std::string msg = "terminate-request";
+uint64_t SnapuserdClient::InitDmUserCow(const std::string& misc_name, const std::string& cow_device,
+ const std::string& backing_device) {
+ std::vector<std::string> parts = {"init", misc_name, cow_device, backing_device};
+ std::string msg = android::base::Join(parts, ",");
if (!Sendmsg(msg)) {
LOG(ERROR) << "Failed to send message " << msg << " to snapuserd daemon";
- return -1;
+ return 0;
}
std::string str = Receivemsg();
- if (str.find("fail") != std::string::npos) {
- LOG(ERROR) << "Failed to receive ack for " << msg << " from snapuserd daemon";
- return -1;
+ std::vector<std::string> input = android::base::Split(str, ",");
+
+ if (input.empty() || input[0] != "success") {
+ LOG(ERROR) << "Failed to receive number of sectors for " << msg << " from snapuserd daemon";
+ return 0;
}
- CHECK(str.find("success") != std::string::npos);
+ LOG(DEBUG) << "Snapuserd daemon COW device initialized: " << cow_device
+ << " Num-sectors: " << input[1];
- // Start the new daemon
- if (!EnsureSnapuserdStarted()) {
- LOG(ERROR) << "Failed to start new daemon";
- return -1;
+ uint64_t num_sectors = 0;
+ if (!android::base::ParseUint(input[1], &num_sectors)) {
+ LOG(ERROR) << "Failed to parse input string to sectors";
+ return 0;
}
+ return num_sectors;
+}
- LOG(DEBUG) << "Second stage Snapuserd daemon created successfully";
-
- // Vector contains all the device information to be passed to the new
- // daemon. Note that the caller can choose to initialize separately
- // by calling InitializeSnapuserd() API as well. In that case, vector
- // should be empty
- for (int i = 0; i < vec.size(); i++) {
- std::string& cow_device = vec[i][0];
- std::string& base_device = vec[i][1];
- std::string& control_device = vec[i][2];
-
- InitializeSnapuserd(cow_device, base_device, control_device);
- LOG(DEBUG) << "Daemon initialized with " << cow_device << ", " << base_device << " and "
- << control_device;
+bool SnapuserdClient::DetachSnapuserd() {
+ if (!Sendmsg("detach")) {
+ LOG(ERROR) << "Failed to detach snapuserd.";
+ return false;
}
-
- return 0;
+ return true;
}
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd_server.cpp
index 6a89218..7a5cead 100644
--- a/fs_mgr/libsnapshot/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_server.cpp
@@ -33,10 +33,12 @@
namespace snapshot {
DaemonOperations SnapuserdServer::Resolveop(std::string& input) {
+ if (input == "init") return DaemonOperations::INIT;
if (input == "start") return DaemonOperations::START;
if (input == "stop") return DaemonOperations::STOP;
if (input == "query") return DaemonOperations::QUERY;
if (input == "delete") return DaemonOperations::DELETE;
+ if (input == "detach") return DaemonOperations::DETACH;
return DaemonOperations::INVALID;
}
@@ -71,23 +73,11 @@
void SnapuserdServer::ShutdownThreads() {
StopThreads();
-
- // Acquire the thread list within the lock.
- std::vector<std::unique_ptr<DmUserHandler>> dm_users;
- {
- std::lock_guard<std::mutex> guard(lock_);
- dm_users = std::move(dm_users_);
- }
-
- for (auto& client : dm_users) {
- auto& th = client->thread();
-
- if (th.joinable()) th.join();
- }
+ JoinAllThreads();
}
-const std::string& DmUserHandler::GetControlDevice() const {
- return snapuserd_->GetControlDevicePath();
+const std::string& DmUserHandler::GetMiscName() const {
+ return snapuserd_->GetMiscName();
}
bool SnapuserdServer::Sendmsg(android::base::borrowed_fd fd, const std::string& msg) {
@@ -123,30 +113,61 @@
DaemonOperations op = Resolveop(out[0]);
switch (op) {
- case DaemonOperations::START: {
+ case DaemonOperations::INIT: {
// Message format:
- // start,<cow_device_path>,<source_device_path>,<control_device>
+ // init,<misc_name>,<cow_device_path>,<control_device>
//
- // Start the new thread which binds to dm-user misc device
+ // Reads the metadata and send the number of sectors
if (out.size() != 4) {
- LOG(ERROR) << "Malformed start message, " << out.size() << " parts";
+ LOG(ERROR) << "Malformed init message, " << out.size() << " parts";
return Sendmsg(fd, "fail");
}
auto snapuserd = std::make_unique<Snapuserd>(out[1], out[2], out[3]);
- if (!snapuserd->Init()) {
+ if (!snapuserd->InitCowDevice()) {
LOG(ERROR) << "Failed to initialize Snapuserd";
return Sendmsg(fd, "fail");
}
+ std::string retval = "success," + std::to_string(snapuserd->GetNumSectors());
+
auto handler = std::make_unique<DmUserHandler>(std::move(snapuserd));
{
std::lock_guard<std::mutex> lock(lock_);
-
- handler->thread() =
- std::thread(std::bind(&SnapuserdServer::RunThread, this, handler.get()));
+ if (FindHandler(&lock, out[1]) != dm_users_.end()) {
+ LOG(ERROR) << "Handler already exists: " << out[1];
+ return Sendmsg(fd, "fail");
+ }
dm_users_.push_back(std::move(handler));
}
+
+ return Sendmsg(fd, retval);
+ }
+ case DaemonOperations::START: {
+ // Message format:
+ // start,<misc_name>
+ //
+ // Start the new thread which binds to dm-user misc device
+ if (out.size() != 2) {
+ LOG(ERROR) << "Malformed start message, " << out.size() << " parts";
+ return Sendmsg(fd, "fail");
+ }
+
+ std::lock_guard<std::mutex> lock(lock_);
+ auto iter = FindHandler(&lock, out[1]);
+ if (iter == dm_users_.end()) {
+ LOG(ERROR) << "Could not find handler: " << out[1];
+ return Sendmsg(fd, "fail");
+ }
+ if ((*iter)->snapuserd()->IsAttached()) {
+ LOG(ERROR) << "Tried to re-attach control device: " << out[1];
+ return Sendmsg(fd, "fail");
+ }
+ if (!((*iter)->snapuserd()->InitBackingAndControlDevice())) {
+ LOG(ERROR) << "Failed to initialize control device: " << out[1];
+ return Sendmsg(fd, "fail");
+ }
+ (*iter)->thread() = std::thread(std::bind(&SnapuserdServer::RunThread, this, *iter));
return Sendmsg(fd, "success");
}
case DaemonOperations::STOP: {
@@ -172,16 +193,20 @@
}
case DaemonOperations::DELETE: {
// Message format:
- // delete,<cow_device_path>
+ // delete,<misc_name>
if (out.size() != 2) {
LOG(ERROR) << "Malformed delete message, " << out.size() << " parts";
return Sendmsg(fd, "fail");
}
- if (!WaitForDelete(out[1])) {
+ if (!RemoveHandler(out[1], true)) {
return Sendmsg(fd, "fail");
}
return Sendmsg(fd, "success");
}
+ case DaemonOperations::DETACH: {
+ terminating_ = true;
+ return Sendmsg(fd, "success");
+ }
default: {
LOG(ERROR) << "Received unknown message type from client";
Sendmsg(fd, "fail");
@@ -190,25 +215,21 @@
}
}
-void SnapuserdServer::RunThread(DmUserHandler* handler) {
- LOG(INFO) << "Entering thread for handler: " << handler->GetControlDevice();
+void SnapuserdServer::RunThread(std::shared_ptr<DmUserHandler> handler) {
+ LOG(INFO) << "Entering thread for handler: " << handler->GetMiscName();
while (!StopRequested()) {
- if (handler->snapuserd()->Run() < 0) {
- LOG(INFO) << "Snapuserd: Thread terminating as control device is de-registered";
+ if (!handler->snapuserd()->Run()) {
+ LOG(INFO) << "Snapuserd: Thread terminating";
break;
}
}
- LOG(INFO) << "Exiting thread for handler: " << handler->GetControlDevice();
+ LOG(INFO) << "Exiting thread for handler: " << handler->GetMiscName();
- if (auto client = RemoveHandler(handler->GetControlDevice())) {
- // The main thread did not receive a WaitForDelete request for this
- // control device. Since we transferred ownership within the lock,
- // we know join() was never called, and will never be called. We can
- // safely detach here.
- client->thread().detach();
- }
+ // If the main thread called RemoveHandler, the handler was already removed
+ // from within the lock, and calling RemoveHandler again has no effect.
+ RemoveHandler(handler->GetMiscName(), false);
}
bool SnapuserdServer::Start(const std::string& socketname) {
@@ -258,9 +279,26 @@
}
}
}
+
+ JoinAllThreads();
return true;
}
+void SnapuserdServer::JoinAllThreads() {
+ // Acquire the thread list within the lock.
+ std::vector<std::shared_ptr<DmUserHandler>> dm_users;
+ {
+ std::lock_guard<std::mutex> guard(lock_);
+ dm_users = std::move(dm_users_);
+ }
+
+ for (auto& client : dm_users) {
+ auto& th = client->thread();
+
+ if (th.joinable()) th.join();
+ }
+}
+
void SnapuserdServer::AddWatchedFd(android::base::borrowed_fd fd) {
struct pollfd p = {};
p.fd = fd.get();
@@ -301,34 +339,37 @@
SetTerminating();
}
-std::unique_ptr<DmUserHandler> SnapuserdServer::RemoveHandler(const std::string& control_device) {
- std::unique_ptr<DmUserHandler> client;
- {
- std::lock_guard<std::mutex> lock(lock_);
- auto iter = dm_users_.begin();
- while (iter != dm_users_.end()) {
- if ((*iter)->GetControlDevice() == control_device) {
- client = std::move(*iter);
- iter = dm_users_.erase(iter);
- break;
- }
- iter++;
+auto SnapuserdServer::FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
+ const std::string& misc_name) -> HandlerList::iterator {
+ CHECK(proof_of_lock);
+
+ for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
+ if ((*iter)->GetMiscName() == misc_name) {
+ return iter;
}
}
- return client;
+ return dm_users_.end();
}
-bool SnapuserdServer::WaitForDelete(const std::string& control_device) {
- auto client = RemoveHandler(control_device);
+bool SnapuserdServer::RemoveHandler(const std::string& misc_name, bool wait) {
+ std::shared_ptr<DmUserHandler> handler;
+ {
+ std::lock_guard<std::mutex> lock(lock_);
- // Client already deleted.
- if (!client) {
- return true;
+ auto iter = FindHandler(&lock, misc_name);
+ if (iter == dm_users_.end()) {
+ // Client already deleted.
+ return true;
+ }
+ handler = std::move(*iter);
+ dm_users_.erase(iter);
}
- auto& th = client->thread();
- if (th.joinable()) {
+ auto& th = handler->thread();
+ if (th.joinable() && wait) {
th.join();
+ } else if (handler->snapuserd()->IsAttached()) {
+ th.detach();
}
return true;
}
diff --git a/fs_mgr/libsnapshot/update_engine/update_metadata.proto b/fs_mgr/libsnapshot/update_engine/update_metadata.proto
index 202e39b..dda214e 100644
--- a/fs_mgr/libsnapshot/update_engine/update_metadata.proto
+++ b/fs_mgr/libsnapshot/update_engine/update_metadata.proto
@@ -62,6 +62,7 @@
repeated InstallOperation operations = 8;
optional Extent hash_tree_extent = 11;
optional Extent fec_extent = 15;
+ optional uint64 estimate_cow_size = 19;
}
message DynamicPartitionGroup {
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 4cae83a..7342fd4 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -22,6 +22,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include <fs_mgr/roots.h>
@@ -182,5 +183,9 @@
new_extent->set_num_blocks(num_blocks);
}
+bool IsCompressionEnabled() {
+ return android::base::GetBoolProperty("ro.virtual_ab.compression.enabled", false);
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 482888a..3e6873b 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -129,5 +129,7 @@
void AppendExtent(google::protobuf::RepeatedPtrField<chromeos_update_engine::Extent>* extents,
uint64_t start_block, uint64_t num_blocks);
+bool IsCompressionEnabled();
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index e995888..f5bbe35 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -1380,9 +1380,9 @@
check_eq "${VENDOR_DEVT}" "`adb_sh stat --format=%D /vendor/hello </dev/null`" vendor devt after reboot
check_eq "${SYSTEM_INO}" "`adb_sh stat --format=%i /system/hello </dev/null`" system inode after reboot
check_eq "${VENDOR_INO}" "`adb_sh stat --format=%i /vendor/hello </dev/null`" vendor inode after reboot
-check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/bin/stat </dev/null`" base system devt after reboot
-check_eq "${BASE_VENDOR_DEVT}" "`adb_sh stat --format=%D /vendor/bin/stat </dev/null`" base system devt after reboot
-check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/xbin/su </dev/null`" devt for su after reboot
+check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/bin/stat </dev/null`" --warning base system devt after reboot
+check_eq "${BASE_VENDOR_DEVT}" "`adb_sh stat --format=%D /vendor/bin/stat </dev/null`" --warning base vendor devt after reboot
+check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/xbin/su </dev/null`" --warning devt for su after reboot
# Feed log with selinux denials as a result of overlays
adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null
@@ -1509,8 +1509,8 @@
check_eq "${SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/hello </dev/null`" system devt after reboot
check_eq "${SYSTEM_INO}" "`adb_sh stat --format=%i /system/hello </dev/null`" system inode after reboot
- check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/bin/stat </dev/null`" base system devt after reboot
- check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/xbin/su </dev/null`" devt for su after reboot
+ check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/bin/stat </dev/null`" --warning base system devt after reboot
+ check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/xbin/su </dev/null`" --warning devt for su after reboot
fi
diff --git a/fs_mgr/tools/Android.bp b/fs_mgr/tools/Android.bp
index 4d4aae4..d6ccc4b 100644
--- a/fs_mgr/tools/Android.bp
+++ b/fs_mgr/tools/Android.bp
@@ -29,3 +29,15 @@
cflags: ["-Werror"],
}
+
+cc_binary {
+ name: "dmuserd",
+ srcs: ["dmuserd.cpp"],
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+
+ cflags: ["-Werror"],
+}
diff --git a/fs_mgr/tools/dmuserd.cpp b/fs_mgr/tools/dmuserd.cpp
new file mode 100644
index 0000000..e50a4a2
--- /dev/null
+++ b/fs_mgr/tools/dmuserd.cpp
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#define _LARGEFILE64_SOURCE
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+#include <iostream>
+
+#define SECTOR_SIZE ((__u64)512)
+#define BUFFER_BYTES 4096
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+/* This should be replaced with linux/dm-user.h. */
+#ifndef _LINUX_DM_USER_H
+#define _LINUX_DM_USER_H
+
+#include <linux/types.h>
+
+#define DM_USER_REQ_MAP_READ 0
+#define DM_USER_REQ_MAP_WRITE 1
+#define DM_USER_REQ_MAP_FLUSH 2
+#define DM_USER_REQ_MAP_DISCARD 3
+#define DM_USER_REQ_MAP_SECURE_ERASE 4
+#define DM_USER_REQ_MAP_WRITE_SAME 5
+#define DM_USER_REQ_MAP_WRITE_ZEROES 6
+#define DM_USER_REQ_MAP_ZONE_OPEN 7
+#define DM_USER_REQ_MAP_ZONE_CLOSE 8
+#define DM_USER_REQ_MAP_ZONE_FINISH 9
+#define DM_USER_REQ_MAP_ZONE_APPEND 10
+#define DM_USER_REQ_MAP_ZONE_RESET 11
+#define DM_USER_REQ_MAP_ZONE_RESET_ALL 12
+
+#define DM_USER_REQ_MAP_FLAG_FAILFAST_DEV 0x00001
+#define DM_USER_REQ_MAP_FLAG_FAILFAST_TRANSPORT 0x00002
+#define DM_USER_REQ_MAP_FLAG_FAILFAST_DRIVER 0x00004
+#define DM_USER_REQ_MAP_FLAG_SYNC 0x00008
+#define DM_USER_REQ_MAP_FLAG_META 0x00010
+#define DM_USER_REQ_MAP_FLAG_PRIO 0x00020
+#define DM_USER_REQ_MAP_FLAG_NOMERGE 0x00040
+#define DM_USER_REQ_MAP_FLAG_IDLE 0x00080
+#define DM_USER_REQ_MAP_FLAG_INTEGRITY 0x00100
+#define DM_USER_REQ_MAP_FLAG_FUA 0x00200
+#define DM_USER_REQ_MAP_FLAG_PREFLUSH 0x00400
+#define DM_USER_REQ_MAP_FLAG_RAHEAD 0x00800
+#define DM_USER_REQ_MAP_FLAG_BACKGROUND 0x01000
+#define DM_USER_REQ_MAP_FLAG_NOWAIT 0x02000
+#define DM_USER_REQ_MAP_FLAG_CGROUP_PUNT 0x04000
+#define DM_USER_REQ_MAP_FLAG_NOUNMAP 0x08000
+#define DM_USER_REQ_MAP_FLAG_HIPRI 0x10000
+#define DM_USER_REQ_MAP_FLAG_DRV 0x20000
+#define DM_USER_REQ_MAP_FLAG_SWAP 0x40000
+
+#define DM_USER_RESP_SUCCESS 0
+#define DM_USER_RESP_ERROR 1
+#define DM_USER_RESP_UNSUPPORTED 2
+
+struct dm_user_message {
+ __u64 seq;
+ __u64 type;
+ __u64 flags;
+ __u64 sector;
+ __u64 len;
+ __u8 buf[];
+};
+
+#endif
+
+static bool verbose = false;
+
+ssize_t write_all(int fd, void* buf, size_t len) {
+ char* buf_c = (char*)buf;
+ ssize_t total = 0;
+ ssize_t once;
+
+ while (total < len) {
+ once = write(fd, buf_c + total, len - total);
+ if (once < 0) return once;
+ if (once == 0) {
+ errno = ENOSPC;
+ return 0;
+ }
+ total += once;
+ }
+
+ return total;
+}
+
+ssize_t read_all(int fd, void* buf, size_t len) {
+ char* buf_c = (char*)buf;
+ ssize_t total = 0;
+ ssize_t once;
+
+ while (total < len) {
+ once = read(fd, buf_c + total, len - total);
+ if (once < 0) return once;
+ if (once == 0) {
+ errno = ENOSPC;
+ return 0;
+ }
+ total += once;
+ }
+
+ return total;
+}
+
+int not_splice(int from, int to, __u64 count) {
+ while (count > 0) {
+ char buf[BUFFER_BYTES];
+ __u64 max = count > BUFFER_BYTES ? BUFFER_BYTES : count;
+
+ if (read_all(from, buf, max) <= 0) {
+ perror("Unable to read");
+ return -EIO;
+ }
+
+ if (write_all(to, buf, max) <= 0) {
+ perror("Unable to write");
+ return -EIO;
+ }
+
+ count -= max;
+ }
+
+ return 0;
+}
+
+int simple_daemon(char* control_path, char* backing_path) {
+ int control_fd = open(control_path, O_RDWR);
+ if (control_fd < 0) {
+ fprintf(stderr, "Unable to open control device %s\n", control_path);
+ return -1;
+ }
+
+ int backing_fd = open(backing_path, O_RDWR);
+ if (backing_fd < 0) {
+ fprintf(stderr, "Unable to open backing device %s\n", backing_path);
+ return -1;
+ }
+
+ while (1) {
+ struct dm_user_message msg;
+ char* base;
+ __u64 type;
+
+ if (verbose) std::cerr << "dmuserd: Waiting for message...\n";
+
+ if (read_all(control_fd, &msg, sizeof(msg)) < 0) {
+ if (errno == ENOTBLK) return 0;
+
+ perror("unable to read msg");
+ return -1;
+ }
+
+ if (verbose) {
+ std::string type;
+ switch (msg.type) {
+ case DM_USER_REQ_MAP_WRITE:
+ type = "write";
+ break;
+ case DM_USER_REQ_MAP_READ:
+ type = "read";
+ break;
+ case DM_USER_REQ_MAP_FLUSH:
+ type = "flush";
+ break;
+ default:
+ /*
+ * FIXME: Can't I do "whatever"s here rather that
+ * std::string("whatever")?
+ */
+ type = std::string("(unknown, id=") + std::to_string(msg.type) + ")";
+ break;
+ }
+
+ std::string flags;
+ if (msg.flags & DM_USER_REQ_MAP_FLAG_SYNC) {
+ if (!flags.empty()) flags += "|";
+ flags += "S";
+ }
+ if (msg.flags & DM_USER_REQ_MAP_FLAG_META) {
+ if (!flags.empty()) flags += "|";
+ flags += "M";
+ }
+ if (msg.flags & DM_USER_REQ_MAP_FLAG_FUA) {
+ if (!flags.empty()) flags += "|";
+ flags += "FUA";
+ }
+ if (msg.flags & DM_USER_REQ_MAP_FLAG_PREFLUSH) {
+ if (!flags.empty()) flags += "|";
+ flags += "F";
+ }
+
+ std::cerr << "dmuserd: Got " << type << " request " << flags << " for sector "
+ << std::to_string(msg.sector) << " with length " << std::to_string(msg.len)
+ << "\n";
+ }
+
+ type = msg.type;
+ switch (type) {
+ case DM_USER_REQ_MAP_READ:
+ msg.type = DM_USER_RESP_SUCCESS;
+ break;
+ case DM_USER_REQ_MAP_WRITE:
+ if (msg.flags & DM_USER_REQ_MAP_FLAG_PREFLUSH ||
+ msg.flags & DM_USER_REQ_MAP_FLAG_FUA) {
+ if (fsync(backing_fd) < 0) {
+ perror("Unable to fsync(), just sync()ing instead");
+ sync();
+ }
+ }
+ msg.type = DM_USER_RESP_SUCCESS;
+ if (lseek64(backing_fd, msg.sector * SECTOR_SIZE, SEEK_SET) < 0) {
+ perror("Unable to seek");
+ return -1;
+ }
+ if (not_splice(control_fd, backing_fd, msg.len) < 0) {
+ if (errno == ENOTBLK) return 0;
+ std::cerr << "unable to handle write data\n";
+ return -1;
+ }
+ if (msg.flags & DM_USER_REQ_MAP_FLAG_FUA) {
+ if (fsync(backing_fd) < 0) {
+ perror("Unable to fsync(), just sync()ing instead");
+ sync();
+ }
+ }
+ break;
+ case DM_USER_REQ_MAP_FLUSH:
+ msg.type = DM_USER_RESP_SUCCESS;
+ if (fsync(backing_fd) < 0) {
+ perror("Unable to fsync(), just sync()ing instead");
+ sync();
+ }
+ break;
+ default:
+ std::cerr << "dmuserd: unsupported op " << std::to_string(msg.type) << "\n";
+ msg.type = DM_USER_RESP_UNSUPPORTED;
+ break;
+ }
+
+ if (verbose) std::cerr << "dmuserd: Responding to message\n";
+
+ if (write_all(control_fd, &msg, sizeof(msg)) < 0) {
+ if (errno == ENOTBLK) return 0;
+ perror("unable to write msg");
+ return -1;
+ }
+
+ switch (type) {
+ case DM_USER_REQ_MAP_READ:
+ if (verbose) std::cerr << "dmuserd: Sending read data\n";
+ if (lseek64(backing_fd, msg.sector * SECTOR_SIZE, SEEK_SET) < 0) {
+ perror("Unable to seek");
+ return -1;
+ }
+ if (not_splice(backing_fd, control_fd, msg.len) < 0) {
+ if (errno == ENOTBLK) return 0;
+ std::cerr << "unable to handle read data\n";
+ return -1;
+ }
+ break;
+ }
+ }
+
+ /* The daemon doesn't actully terminate for this test. */
+ perror("Unable to read from control device");
+ return -1;
+}
+
+void usage(char* prog) {
+ printf("Usage: %s\n", prog);
+ printf(" Handles block requests in userspace, backed by memory\n");
+ printf(" -h Display this help message\n");
+ printf(" -c <control dev> Control device to use for the test\n");
+ printf(" -b <store path> The file to use as a backing store, otherwise memory\n");
+ printf(" -v Enable verbose mode\n");
+}
+
+int main(int argc, char* argv[]) {
+ char* control_path = NULL;
+ char* backing_path = NULL;
+ char* store;
+ int c;
+
+ prctl(PR_SET_IO_FLUSHER, 0, 0, 0, 0);
+
+ while ((c = getopt(argc, argv, "h:c:s:b:v")) != -1) {
+ switch (c) {
+ case 'h':
+ usage(basename(argv[0]));
+ exit(0);
+ case 'c':
+ control_path = strdup(optarg);
+ break;
+ case 'b':
+ backing_path = strdup(optarg);
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ default:
+ usage(basename(argv[0]));
+ exit(1);
+ }
+ }
+
+ int r = simple_daemon(control_path, backing_path);
+ if (r) fprintf(stderr, "simple_daemon() errored out\n");
+ return r;
+}
diff --git a/gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl b/gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl
index 4646efc..67e8633 100644
--- a/gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl
+++ b/gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl
@@ -27,6 +27,7 @@
*
* @hide
*/
+@SensitiveData
interface IGateKeeperService {
/**
* Enrolls a password, returning the handle to the enrollment to be stored locally.
diff --git a/init/Android.mk b/init/Android.mk
index ac31ef1..4c1665b 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -82,6 +82,7 @@
$(TARGET_RAMDISK_OUT)/proc \
$(TARGET_RAMDISK_OUT)/second_stage_resources \
$(TARGET_RAMDISK_OUT)/sys \
+ $(TARGET_RAMDISK_OUT)/metadata \
LOCAL_STATIC_LIBRARIES := \
libc++fs \
diff --git a/init/README.md b/init/README.md
index ab6a885..bcbbfbb 100644
--- a/init/README.md
+++ b/init/README.md
@@ -451,6 +451,10 @@
exist. And it will be truncated if dst file is a normal regular file and
already exists.
+`copy_per_line <src> <dst>`
+> Copies a file line by line. Similar to copy, but useful for dst is a sysfs node
+ that doesn't handle multiple lines of data.
+
`domainname <name>`
> Set the domain name.
diff --git a/init/block_dev_initializer.cpp b/init/block_dev_initializer.cpp
index 8db9793..9c2a7bb 100644
--- a/init/block_dev_initializer.cpp
+++ b/init/block_dev_initializer.cpp
@@ -40,8 +40,8 @@
return InitMiscDevice("device-mapper");
}
-bool BlockDevInitializer::InitDmUser() {
- return InitMiscDevice("dm-user");
+bool BlockDevInitializer::InitDmUser(const std::string& name) {
+ return InitMiscDevice("dm-user!" + name);
}
bool BlockDevInitializer::InitMiscDevice(const std::string& name) {
diff --git a/init/block_dev_initializer.h b/init/block_dev_initializer.h
index b8dd3f1..79fe4ec 100644
--- a/init/block_dev_initializer.h
+++ b/init/block_dev_initializer.h
@@ -27,7 +27,7 @@
BlockDevInitializer();
bool InitDeviceMapper();
- bool InitDmUser();
+ bool InitDmUser(const std::string& name);
bool InitDevices(std::set<std::string> devices);
bool InitDmDevice(const std::string& device);
diff --git a/init/builtins.cpp b/init/builtins.cpp
index d00d1b1..b235d2f 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -88,6 +88,7 @@
using android::base::Basename;
using android::base::SetProperty;
+using android::base::Split;
using android::base::StartsWith;
using android::base::StringPrintf;
using android::base::unique_fd;
@@ -968,6 +969,23 @@
return {};
}
+static Result<void> do_copy_per_line(const BuiltinArguments& args) {
+ std::string file_contents;
+ if (!android::base::ReadFileToString(args[1], &file_contents, true)) {
+ return Error() << "Could not read input file '" << args[1] << "'";
+ }
+ auto lines = Split(file_contents, "\n");
+ for (const auto& line : lines) {
+ auto result = WriteFile(args[2], line);
+ if (!result.ok()) {
+ LOG(VERBOSE) << "Could not write to output file '" << args[2] << "' with '" << line
+ << "' : " << result.error();
+ }
+ }
+
+ return {};
+}
+
static Result<void> do_chown(const BuiltinArguments& args) {
auto uid = DecodeUid(args[1]);
if (!uid.ok()) {
@@ -1366,6 +1384,7 @@
{"class_start_post_data", {1, 1, {false, do_class_start_post_data}}},
{"class_stop", {1, 1, {false, do_class_stop}}},
{"copy", {2, 2, {true, do_copy}}},
+ {"copy_per_line", {2, 2, {true, do_copy_per_line}}},
{"domainname", {1, 1, {true, do_domainname}}},
{"enable", {1, 1, {false, do_enable}}},
{"exec", {1, kMax, {false, do_exec}}},
diff --git a/init/devices.cpp b/init/devices.cpp
index f8eb16a..5888c06 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -440,13 +440,6 @@
}
}
unlink(devpath.c_str());
-
- if (android::base::StartsWith(devpath, "/dev/dm-user/")) {
- std::error_code ec;
- if (std::filesystem::is_empty("/dev/dm-user/", ec)) {
- rmdir("/dev/dm-user");
- }
- }
}
}
diff --git a/init/first_stage_console.cpp b/init/first_stage_console.cpp
index cfa0d99..0f01166 100644
--- a/init/first_stage_console.cpp
+++ b/init/first_stage_console.cpp
@@ -30,6 +30,41 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+static bool KernelConsolePresent(const std::string& cmdline) {
+ size_t pos = 0;
+ while (true) {
+ pos = cmdline.find("console=", pos);
+ if (pos == std::string::npos) return false;
+ if (pos == 0 || cmdline[pos - 1] == ' ') return true;
+ pos++;
+ }
+}
+
+static bool SetupConsole() {
+ if (mknod("/dev/console", S_IFCHR | 0600, makedev(5, 1))) {
+ PLOG(ERROR) << "unable to create /dev/console";
+ return false;
+ }
+ int fd = -1;
+ int tries = 50; // should timeout after 5s
+ // The device driver for console may not be ready yet so retry for a while in case of failure.
+ while (tries--) {
+ fd = open("/dev/console", O_RDWR);
+ if (fd != -1) break;
+ std::this_thread::sleep_for(100ms);
+ }
+ if (fd == -1) {
+ PLOG(ERROR) << "could not open /dev/console";
+ return false;
+ }
+ ioctl(fd, TIOCSCTTY, 0);
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ close(fd);
+ return true;
+}
+
static void RunScript() {
LOG(INFO) << "Attempting to run /first_stage.sh...";
pid_t pid = fork();
@@ -48,11 +83,9 @@
namespace android {
namespace init {
-void StartConsole() {
- if (mknod("/dev/console", S_IFCHR | 0600, makedev(5, 1))) {
- PLOG(ERROR) << "unable to create /dev/console";
- return;
- }
+void StartConsole(const std::string& cmdline) {
+ bool console = KernelConsolePresent(cmdline);
+
pid_t pid = fork();
if (pid != 0) {
int status;
@@ -60,31 +93,15 @@
LOG(ERROR) << "console shell exited with status " << status;
return;
}
- int fd = -1;
- int tries = 50; // should timeout after 5s
- // The device driver for console may not be ready yet so retry for a while in case of failure.
- while (tries--) {
- fd = open("/dev/console", O_RDWR);
- if (fd != -1) {
- break;
- }
- std::this_thread::sleep_for(100ms);
- }
- if (fd == -1) {
- LOG(ERROR) << "Could not open /dev/console, errno = " << errno;
- _exit(127);
- }
- ioctl(fd, TIOCSCTTY, 0);
- dup2(fd, STDIN_FILENO);
- dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
- close(fd);
+ if (console) console = SetupConsole();
RunScript();
- const char* path = "/system/bin/sh";
- const char* args[] = {path, nullptr};
- int rv = execv(path, const_cast<char**>(args));
- LOG(ERROR) << "unable to execv, returned " << rv << " errno " << errno;
+ if (console) {
+ const char* path = "/system/bin/sh";
+ const char* args[] = {path, nullptr};
+ int rv = execv(path, const_cast<char**>(args));
+ LOG(ERROR) << "unable to execv, returned " << rv << " errno " << errno;
+ }
_exit(127);
}
diff --git a/init/first_stage_console.h b/init/first_stage_console.h
index 8f36a7c..d5744df 100644
--- a/init/first_stage_console.h
+++ b/init/first_stage_console.h
@@ -28,7 +28,7 @@
MAX_PARAM_VALUE = IGNORE_FAILURE,
};
-void StartConsole();
+void StartConsole(const std::string& cmdline);
int FirstStageConsole(const std::string& cmdline);
} // namespace init
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index d2a952f..83a32e7 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -117,7 +117,7 @@
auto dst_dir = android::base::Dirname(dst);
std::error_code ec;
- if (!fs::create_directories(dst_dir, ec)) {
+ if (!fs::create_directories(dst_dir, ec) && !!ec) {
LOG(FATAL) << "Cannot create " << dst_dir << ": " << ec.message();
}
if (rename(src, dst) != 0) {
@@ -221,6 +221,7 @@
CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
CHECKCALL(mkdir("/dev/pts", 0755));
CHECKCALL(mkdir("/dev/socket", 0755));
+ CHECKCALL(mkdir("/dev/dm-user", 0755));
CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
#define MAKE_STR(x) __STRING(x)
CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
@@ -307,14 +308,14 @@
}
if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) {
- StartConsole();
+ StartConsole(cmdline);
}
if (access(kBootImageRamdiskProp, F_OK) == 0) {
std::string dest = GetRamdiskPropForSecondStage();
std::string dir = android::base::Dirname(dest);
std::error_code ec;
- if (!fs::create_directories(dir, ec)) {
+ if (!fs::create_directories(dir, ec) && !!ec) {
LOG(FATAL) << "Can't mkdir " << dir << ": " << ec.message();
}
if (!fs::copy_file(kBootImageRamdiskProp, dest, ec)) {
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index b7d50cf..a0511cc 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -343,6 +343,15 @@
if (!InitRequiredDevices({"userdata"})) {
return false;
}
+ sm->SetUeventRegenCallback([this](const std::string& device) -> bool {
+ if (android::base::StartsWith(device, "/dev/block/dm-")) {
+ return block_dev_init_.InitDmDevice(device);
+ }
+ if (android::base::StartsWith(device, "/dev/dm-user/")) {
+ return block_dev_init_.InitDmUser(android::base::Basename(device));
+ }
+ return block_dev_init_.InitDevices({device});
+ });
return sm->CreateLogicalAndSnapshotPartitions(super_path_);
}
}
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index ef9a451..db127d3 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -25,6 +25,8 @@
#include <fstream>
#include <iostream>
#include <iterator>
+#include <map>
+#include <set>
#include <string>
#include <vector>
@@ -51,6 +53,7 @@
using namespace std::literals;
+using android::base::EndsWith;
using android::base::ParseInt;
using android::base::ReadFileToString;
using android::base::Split;
@@ -61,6 +64,10 @@
static std::vector<std::string> passwd_files;
+// NOTE: Keep this in sync with the order used by init.cpp LoadBootScripts()
+static const std::vector<std::string> partition_search_order =
+ std::vector<std::string>({"system", "system_ext", "odm", "vendor", "product"});
+
static std::vector<std::pair<std::string, int>> GetVendorPasswd(const std::string& passwd_file) {
std::string passwd;
if (!ReadFileToString(passwd_file, &passwd)) {
@@ -148,13 +155,24 @@
#include "generated_stub_builtin_function_map.h"
void PrintUsage() {
- std::cout << "usage: host_init_verifier [options] <init rc file>\n"
- "\n"
- "Tests an init script for correctness\n"
- "\n"
- "-p FILE\tSearch this passwd file for users and groups\n"
- "--property_contexts=FILE\t Use this file for property_contexts\n"
- << std::endl;
+ fprintf(stdout, R"(usage: host_init_verifier [options]
+
+Tests init script(s) for correctness.
+
+Generic options:
+ -p FILE Search this passwd file for users and groups.
+ --property_contexts=FILE Use this file for property_contexts.
+
+Single script mode options:
+ [init rc file] Positional argument; test this init script.
+
+Multiple script mode options:
+ --out_system=DIR Path to the output product directory for the system partition.
+ --out_system_ext=DIR Path to the output product directory for the system_ext partition.
+ --out_odm=DIR Path to the output product directory for the odm partition.
+ --out_vendor=DIR Path to the output product directory for the vendor partition.
+ --out_product=DIR Path to the output product directory for the product partition.
+)");
}
Result<InterfaceInheritanceHierarchyMap> ReadInterfaceInheritanceHierarchy() {
@@ -203,12 +221,18 @@
android::base::SetMinimumLogSeverity(android::base::ERROR);
auto property_infos = std::vector<PropertyInfoEntry>();
+ std::map<std::string, std::string> partition_map;
while (true) {
static const char kPropertyContexts[] = "property-contexts=";
static const struct option long_options[] = {
{"help", no_argument, nullptr, 'h'},
{kPropertyContexts, required_argument, nullptr, 0},
+ {"out_system", required_argument, nullptr, 0},
+ {"out_system_ext", required_argument, nullptr, 0},
+ {"out_odm", required_argument, nullptr, 0},
+ {"out_vendor", required_argument, nullptr, 0},
+ {"out_product", required_argument, nullptr, 0},
{nullptr, 0, nullptr, 0},
};
@@ -224,6 +248,16 @@
if (long_options[option_index].name == kPropertyContexts) {
HandlePropertyContexts(optarg, &property_infos);
}
+ for (const auto& p : partition_search_order) {
+ if (long_options[option_index].name == "out_" + p) {
+ if (partition_map.find(p) != partition_map.end()) {
+ PrintUsage();
+ return EXIT_FAILURE;
+ }
+ partition_map[p] =
+ EndsWith(optarg, "/") ? optarg : std::string(optarg) + "/";
+ }
+ }
break;
case 'h':
PrintUsage();
@@ -240,7 +274,9 @@
argc -= optind;
argv += optind;
- if (argc != 1) {
+ // If provided, use the partition map to check multiple init rc files.
+ // Otherwise, check a single init rc file.
+ if ((!partition_map.empty() && argc != 0) || (partition_map.empty() && argc != 1)) {
PrintUsage();
return EXIT_FAILURE;
}
@@ -262,24 +298,42 @@
property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_contexts.c_str());
+ if (!partition_map.empty()) {
+ std::vector<std::string> vendor_prefixes;
+ for (const auto& partition : {"vendor", "odm"}) {
+ if (partition_map.find(partition) != partition_map.end()) {
+ vendor_prefixes.push_back(partition_map.at(partition));
+ }
+ }
+ InitializeHostSubcontext(vendor_prefixes);
+ }
+
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
Action::set_function_map(&function_map);
ActionManager& am = ActionManager::GetInstance();
ServiceList& sl = ServiceList::GetInstance();
Parser parser;
- parser.AddSectionParser("service", std::make_unique<ServiceParser>(
- &sl, nullptr, *interface_inheritance_hierarchy_map));
- parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
+ parser.AddSectionParser("service",
+ std::make_unique<ServiceParser>(&sl, GetSubcontext(),
+ *interface_inheritance_hierarchy_map));
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, GetSubcontext()));
parser.AddSectionParser("import", std::make_unique<HostImportParser>());
- if (!parser.ParseConfigFileInsecure(*argv)) {
- LOG(ERROR) << "Failed to open init rc script '" << *argv << "'";
- return EXIT_FAILURE;
+ if (!partition_map.empty()) {
+ for (const auto& p : partition_search_order) {
+ if (partition_map.find(p) != partition_map.end()) {
+ parser.ParseConfig(partition_map.at(p) + "etc/init");
+ }
+ }
+ } else {
+ if (!parser.ParseConfigFileInsecure(*argv)) {
+ LOG(ERROR) << "Failed to open init rc script '" << *argv << "'";
+ return EXIT_FAILURE;
+ }
}
size_t failures = parser.parse_error_count() + am.CheckAllCommands() + sl.CheckAllCommands();
if (failures > 0) {
- LOG(ERROR) << "Failed to parse init script '" << *argv << "' with " << failures
- << " errors";
+ LOG(ERROR) << "Failed to parse init scripts with " << failures << " error(s).";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
diff --git a/init/init.cpp b/init/init.cpp
index c6f2066..1d0a9dc 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -875,13 +875,13 @@
SetProperty(gsi::kGsiInstalledProp, is_installed);
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
- am.QueueBuiltinAction(TransitionSnapuserdAction, "TransitionSnapuserd");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
am.QueueEventTrigger("early-init");
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
+ am.QueueBuiltinAction(TransitionSnapuserdAction, "TransitionSnapuserd");
// ... so that we can start queuing up actions that require stuff from /dev.
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 5a0255a..f03ca6b 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -534,6 +534,7 @@
selinux_android_restorecon("/dev/__properties__", 0);
selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
+ selinux_android_restorecon("/dev/dm-user", SELINUX_ANDROID_RESTORECON_RECURSE);
selinux_android_restorecon("/dev/device-mapper", 0);
selinux_android_restorecon("/apex", 0);
diff --git a/init/service.cpp b/init/service.cpp
index ecc86d9..766eb5d 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -154,6 +154,7 @@
.priority = 0},
namespaces_{.flags = namespace_flags},
seclabel_(seclabel),
+ subcontext_(subcontext_for_restart_commands),
onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0,
"onrestart", {}),
oom_score_adjust_(DEFAULT_OOM_SCORE_ADJUST),
@@ -329,8 +330,8 @@
LOG(FATAL) << "critical process '" << name_ << "' exited 4 times "
<< exit_reason;
} else {
- LOG(ERROR) << "updatable process '" << name_ << "' exited 4 times "
- << exit_reason;
+ LOG(ERROR) << "process with updatable components '" << name_
+ << "' exited 4 times " << exit_reason;
// Notifies update_verifier and apexd
SetProperty("sys.init.updatable_crashing_process_name", name_);
SetProperty("sys.init.updatable_crashing", "1");
diff --git a/init/service.h b/init/service.h
index bc5c90f..aee1e5d 100644
--- a/init/service.h
+++ b/init/service.h
@@ -137,6 +137,7 @@
flags_ &= ~SVC_ONESHOT;
}
}
+ Subcontext* subcontext() const { return subcontext_; }
private:
void NotifyStateChange(const std::string& new_state) const;
@@ -168,6 +169,7 @@
std::vector<FileDescriptor> files_;
std::vector<std::pair<std::string, std::string>> environment_vars_;
+ Subcontext* subcontext_;
Action onrestart_; // Commands to execute on restart.
std::vector<std::string> writepid_files_;
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 97621da..57c311a 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -657,6 +657,14 @@
<< "' with a config in APEX";
}
+ std::string context = service_->subcontext() ? service_->subcontext()->context() : "";
+ std::string old_context =
+ old_service->subcontext() ? old_service->subcontext()->context() : "";
+ if (context != old_context) {
+ return Error() << "service '" << service_->name() << "' overrides another service "
+ << "across the treble boundary.";
+ }
+
service_list_->RemoveService(*old_service);
old_service = nullptr;
}
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index dc2455e..f1fbffe 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -342,6 +342,9 @@
new Subcontext(std::vector<std::string>{"/vendor", "/odm"}, kVendorContext));
}
}
+void InitializeHostSubcontext(std::vector<std::string> vendor_prefixes) {
+ subcontext.reset(new Subcontext(vendor_prefixes, kVendorContext, /*host=*/true));
+}
Subcontext* GetSubcontext() {
return subcontext.get();
diff --git a/init/subcontext.h b/init/subcontext.h
index 788d3be..cb4138e 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -36,9 +36,11 @@
class Subcontext {
public:
- Subcontext(std::vector<std::string> path_prefixes, std::string context)
+ Subcontext(std::vector<std::string> path_prefixes, std::string context, bool host = false)
: path_prefixes_(std::move(path_prefixes)), context_(std::move(context)), pid_(0) {
- Fork();
+ if (!host) {
+ Fork();
+ }
}
Result<void> Execute(const std::vector<std::string>& args);
@@ -61,6 +63,7 @@
int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map);
void InitializeSubcontext();
+void InitializeHostSubcontext(std::vector<std::string> vendor_prefixes);
Subcontext* GetSubcontext();
bool SubcontextChildReap(pid_t pid);
void SubcontextTerminate();
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 31e1679..79c3abc 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -203,9 +203,14 @@
CAP_MASK_LONG(CAP_SETGID),
"system/bin/simpleperf_app_runner" },
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/e2fsck" },
- { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/tune2fs" },
+#ifdef __LP64__
+ { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/linker64" },
+#else
+ { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/linker" },
+#endif
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/resize2fs" },
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/snapuserd" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/tune2fs" },
// generic defaults
{ 00755, AID_ROOT, AID_ROOT, 0, "bin/*" },
{ 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
diff --git a/libprocessgroup/profiles/Android.bp b/libprocessgroup/profiles/Android.bp
index c371ef7..a496237 100644
--- a/libprocessgroup/profiles/Android.bp
+++ b/libprocessgroup/profiles/Android.bp
@@ -15,6 +15,11 @@
prebuilt_etc {
name: "cgroups.json",
src: "cgroups.json",
+ required: [
+ "cgroups_28.json",
+ "cgroups_29.json",
+ "cgroups_30.json",
+ ],
}
prebuilt_etc {
@@ -25,8 +30,49 @@
}
prebuilt_etc {
+ name: "cgroups_28.json",
+ src: "cgroups_28.json",
+ sub_dir: "task_profiles",
+}
+
+prebuilt_etc {
+ name: "cgroups_29.json",
+ src: "cgroups_29.json",
+ sub_dir: "task_profiles",
+}
+
+prebuilt_etc {
+ name: "cgroups_30.json",
+ src: "cgroups_30.json",
+ sub_dir: "task_profiles",
+}
+
+prebuilt_etc {
name: "task_profiles.json",
src: "task_profiles.json",
+ required: [
+ "task_profiles_28.json",
+ "task_profiles_29.json",
+ "task_profiles_30.json",
+ ],
+}
+
+prebuilt_etc {
+ name: "task_profiles_28.json",
+ src: "task_profiles_28.json",
+ sub_dir: "task_profiles",
+}
+
+prebuilt_etc {
+ name: "task_profiles_29.json",
+ src: "task_profiles_29.json",
+ sub_dir: "task_profiles",
+}
+
+prebuilt_etc {
+ name: "task_profiles_30.json",
+ src: "task_profiles_30.json",
+ sub_dir: "task_profiles",
}
cc_defaults {
diff --git a/libprocessgroup/profiles/cgroups_28.json b/libprocessgroup/profiles/cgroups_28.json
new file mode 100644
index 0000000..4518487
--- /dev/null
+++ b/libprocessgroup/profiles/cgroups_28.json
@@ -0,0 +1,59 @@
+{
+ "Cgroups": [
+ {
+ "Controller": "blkio",
+ "Path": "/dev/blkio",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ },
+ {
+ "Controller": "cpu",
+ "Path": "/dev/cpuctl",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ },
+ {
+ "Controller": "cpuacct",
+ "Path": "/acct",
+ "Mode": "0555"
+ },
+ {
+ "Controller": "cpuset",
+ "Path": "/dev/cpuset",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ },
+ {
+ "Controller": "memory",
+ "Path": "/dev/memcg",
+ "Mode": "0700",
+ "UID": "root",
+ "GID": "system"
+ },
+ {
+ "Controller": "schedtune",
+ "Path": "/dev/stune",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ }
+ ],
+ "Cgroups2": {
+ "Path": "/sys/fs/cgroup",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system",
+ "Controllers": [
+ {
+ "Controller": "freezer",
+ "Path": "freezer",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ }
+ ]
+ }
+}
diff --git a/libprocessgroup/profiles/cgroups_29.json b/libprocessgroup/profiles/cgroups_29.json
new file mode 100644
index 0000000..4518487
--- /dev/null
+++ b/libprocessgroup/profiles/cgroups_29.json
@@ -0,0 +1,59 @@
+{
+ "Cgroups": [
+ {
+ "Controller": "blkio",
+ "Path": "/dev/blkio",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ },
+ {
+ "Controller": "cpu",
+ "Path": "/dev/cpuctl",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ },
+ {
+ "Controller": "cpuacct",
+ "Path": "/acct",
+ "Mode": "0555"
+ },
+ {
+ "Controller": "cpuset",
+ "Path": "/dev/cpuset",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ },
+ {
+ "Controller": "memory",
+ "Path": "/dev/memcg",
+ "Mode": "0700",
+ "UID": "root",
+ "GID": "system"
+ },
+ {
+ "Controller": "schedtune",
+ "Path": "/dev/stune",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ }
+ ],
+ "Cgroups2": {
+ "Path": "/sys/fs/cgroup",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system",
+ "Controllers": [
+ {
+ "Controller": "freezer",
+ "Path": "freezer",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ }
+ ]
+ }
+}
diff --git a/libprocessgroup/profiles/cgroups_30.json b/libprocessgroup/profiles/cgroups_30.json
new file mode 100644
index 0000000..4518487
--- /dev/null
+++ b/libprocessgroup/profiles/cgroups_30.json
@@ -0,0 +1,59 @@
+{
+ "Cgroups": [
+ {
+ "Controller": "blkio",
+ "Path": "/dev/blkio",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ },
+ {
+ "Controller": "cpu",
+ "Path": "/dev/cpuctl",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ },
+ {
+ "Controller": "cpuacct",
+ "Path": "/acct",
+ "Mode": "0555"
+ },
+ {
+ "Controller": "cpuset",
+ "Path": "/dev/cpuset",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ },
+ {
+ "Controller": "memory",
+ "Path": "/dev/memcg",
+ "Mode": "0700",
+ "UID": "root",
+ "GID": "system"
+ },
+ {
+ "Controller": "schedtune",
+ "Path": "/dev/stune",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ }
+ ],
+ "Cgroups2": {
+ "Path": "/sys/fs/cgroup",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system",
+ "Controllers": [
+ {
+ "Controller": "freezer",
+ "Path": "freezer",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ }
+ ]
+ }
+}
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index ea0064f..b528fa5 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -100,7 +100,7 @@
"Params":
{
"Controller": "cpu",
- "Path": ""
+ "Path": "system"
}
}
]
diff --git a/libprocessgroup/profiles/task_profiles_28.json b/libprocessgroup/profiles/task_profiles_28.json
new file mode 100644
index 0000000..142b0ba
--- /dev/null
+++ b/libprocessgroup/profiles/task_profiles_28.json
@@ -0,0 +1,627 @@
+{
+ "Attributes": [
+ {
+ "Name": "LowCapacityCPUs",
+ "Controller": "cpuset",
+ "File": "background/cpus"
+ },
+ {
+ "Name": "HighCapacityCPUs",
+ "Controller": "cpuset",
+ "File": "foreground/cpus"
+ },
+ {
+ "Name": "MaxCapacityCPUs",
+ "Controller": "cpuset",
+ "File": "top-app/cpus"
+ },
+ {
+ "Name": "MemLimit",
+ "Controller": "memory",
+ "File": "memory.limit_in_bytes"
+ },
+ {
+ "Name": "MemSoftLimit",
+ "Controller": "memory",
+ "File": "memory.soft_limit_in_bytes"
+ },
+ {
+ "Name": "MemSwappiness",
+ "Controller": "memory",
+ "File": "memory.swappiness"
+ },
+ {
+ "Name": "STuneBoost",
+ "Controller": "schedtune",
+ "File": "schedtune.boost"
+ },
+ {
+ "Name": "STunePreferIdle",
+ "Controller": "schedtune",
+ "File": "schedtune.prefer_idle"
+ },
+ {
+ "Name": "UClampMin",
+ "Controller": "cpu",
+ "File": "cpu.uclamp.min"
+ },
+ {
+ "Name": "UClampMax",
+ "Controller": "cpu",
+ "File": "cpu.uclamp.max"
+ },
+ {
+ "Name": "FreezerState",
+ "Controller": "freezer",
+ "File": "cgroup.freeze"
+ }
+ ],
+
+ "Profiles": [
+ {
+ "Name": "HighEnergySaving",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "Frozen",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "freezer",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "Unfrozen",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "freezer",
+ "Path": "../"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "NormalPerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "HighPerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "foreground"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "MaxPerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "top-app"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "RealtimePerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "rt"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "CameraServicePerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "camera-daemon"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "NNApiHALPerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "nnapi-hal"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "CpuPolicySpread",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "STunePreferIdle",
+ "Value": "1"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "CpuPolicyPack",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "STunePreferIdle",
+ "Value": "0"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "VrKernelCapacity",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrServiceCapacityLow",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "system/background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrServiceCapacityNormal",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "system"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrServiceCapacityHigh",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "system/performance"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrProcessCapacityLow",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "application/background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrProcessCapacityNormal",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "application"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrProcessCapacityHigh",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "application/performance"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "ProcessCapacityLow",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "ProcessCapacityNormal",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "ProcessCapacityHigh",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "foreground"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "ProcessCapacityMax",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "top-app"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "ServiceCapacityLow",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "system-background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "ServiceCapacityRestricted",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "restricted"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "CameraServiceCapacity",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "camera-daemon"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "LowIoPriority",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "blkio",
+ "Path": "background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "NormalIoPriority",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "blkio",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "HighIoPriority",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "blkio",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "MaxIoPriority",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "blkio",
+ "Path": ""
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "TimerSlackHigh",
+ "Actions": [
+ {
+ "Name": "SetTimerSlack",
+ "Params":
+ {
+ "Slack": "40000000"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "TimerSlackNormal",
+ "Actions": [
+ {
+ "Name": "SetTimerSlack",
+ "Params":
+ {
+ "Slack": "50000"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "PerfBoost",
+ "Actions": [
+ {
+ "Name": "SetClamps",
+ "Params":
+ {
+ "Boost": "50%",
+ "Clamp": "0"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "PerfClamp",
+ "Actions": [
+ {
+ "Name": "SetClamps",
+ "Params":
+ {
+ "Boost": "0",
+ "Clamp": "30%"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "LowMemoryUsage",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "MemSoftLimit",
+ "Value": "16MB"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "MemSwappiness",
+ "Value": "150"
+
+ }
+ }
+ ]
+ },
+ {
+ "Name": "HighMemoryUsage",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "MemSoftLimit",
+ "Value": "512MB"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "MemSwappiness",
+ "Value": "100"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "SystemMemoryProcess",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "memory",
+ "Path": "system"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "FreezerDisabled",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "FreezerState",
+ "Value": "0"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "FreezerEnabled",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "FreezerState",
+ "Value": "1"
+ }
+ }
+ ]
+ }
+ ],
+
+ "AggregateProfiles": [
+ {
+ "Name": "SCHED_SP_DEFAULT",
+ "Profiles": [ "TimerSlackNormal" ]
+ },
+ {
+ "Name": "SCHED_SP_BACKGROUND",
+ "Profiles": [ "HighEnergySaving", "LowIoPriority", "TimerSlackHigh" ]
+ },
+ {
+ "Name": "SCHED_SP_FOREGROUND",
+ "Profiles": [ "HighPerformance", "HighIoPriority", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "SCHED_SP_TOP_APP",
+ "Profiles": [ "MaxPerformance", "MaxIoPriority", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "SCHED_SP_RT_APP",
+ "Profiles": [ "RealtimePerformance", "MaxIoPriority", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "CPUSET_SP_DEFAULT",
+ "Profiles": [ "TimerSlackNormal" ]
+ },
+ {
+ "Name": "CPUSET_SP_BACKGROUND",
+ "Profiles": [ "HighEnergySaving", "ProcessCapacityLow", "LowIoPriority", "TimerSlackHigh" ]
+ },
+ {
+ "Name": "CPUSET_SP_FOREGROUND",
+ "Profiles": [ "HighPerformance", "ProcessCapacityHigh", "HighIoPriority", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "CPUSET_SP_TOP_APP",
+ "Profiles": [ "MaxPerformance", "ProcessCapacityMax", "MaxIoPriority", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "CPUSET_SP_SYSTEM",
+ "Profiles": [ "ServiceCapacityLow", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "CPUSET_SP_RESTRICTED",
+ "Profiles": [ "ServiceCapacityRestricted", "TimerSlackNormal" ]
+ }
+ ]
+}
diff --git a/libprocessgroup/profiles/task_profiles_29.json b/libprocessgroup/profiles/task_profiles_29.json
new file mode 100644
index 0000000..142b0ba
--- /dev/null
+++ b/libprocessgroup/profiles/task_profiles_29.json
@@ -0,0 +1,627 @@
+{
+ "Attributes": [
+ {
+ "Name": "LowCapacityCPUs",
+ "Controller": "cpuset",
+ "File": "background/cpus"
+ },
+ {
+ "Name": "HighCapacityCPUs",
+ "Controller": "cpuset",
+ "File": "foreground/cpus"
+ },
+ {
+ "Name": "MaxCapacityCPUs",
+ "Controller": "cpuset",
+ "File": "top-app/cpus"
+ },
+ {
+ "Name": "MemLimit",
+ "Controller": "memory",
+ "File": "memory.limit_in_bytes"
+ },
+ {
+ "Name": "MemSoftLimit",
+ "Controller": "memory",
+ "File": "memory.soft_limit_in_bytes"
+ },
+ {
+ "Name": "MemSwappiness",
+ "Controller": "memory",
+ "File": "memory.swappiness"
+ },
+ {
+ "Name": "STuneBoost",
+ "Controller": "schedtune",
+ "File": "schedtune.boost"
+ },
+ {
+ "Name": "STunePreferIdle",
+ "Controller": "schedtune",
+ "File": "schedtune.prefer_idle"
+ },
+ {
+ "Name": "UClampMin",
+ "Controller": "cpu",
+ "File": "cpu.uclamp.min"
+ },
+ {
+ "Name": "UClampMax",
+ "Controller": "cpu",
+ "File": "cpu.uclamp.max"
+ },
+ {
+ "Name": "FreezerState",
+ "Controller": "freezer",
+ "File": "cgroup.freeze"
+ }
+ ],
+
+ "Profiles": [
+ {
+ "Name": "HighEnergySaving",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "Frozen",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "freezer",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "Unfrozen",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "freezer",
+ "Path": "../"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "NormalPerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "HighPerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "foreground"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "MaxPerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "top-app"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "RealtimePerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "rt"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "CameraServicePerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "camera-daemon"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "NNApiHALPerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "nnapi-hal"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "CpuPolicySpread",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "STunePreferIdle",
+ "Value": "1"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "CpuPolicyPack",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "STunePreferIdle",
+ "Value": "0"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "VrKernelCapacity",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrServiceCapacityLow",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "system/background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrServiceCapacityNormal",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "system"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrServiceCapacityHigh",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "system/performance"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrProcessCapacityLow",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "application/background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrProcessCapacityNormal",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "application"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrProcessCapacityHigh",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "application/performance"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "ProcessCapacityLow",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "ProcessCapacityNormal",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "ProcessCapacityHigh",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "foreground"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "ProcessCapacityMax",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "top-app"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "ServiceCapacityLow",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "system-background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "ServiceCapacityRestricted",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "restricted"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "CameraServiceCapacity",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "camera-daemon"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "LowIoPriority",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "blkio",
+ "Path": "background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "NormalIoPriority",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "blkio",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "HighIoPriority",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "blkio",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "MaxIoPriority",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "blkio",
+ "Path": ""
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "TimerSlackHigh",
+ "Actions": [
+ {
+ "Name": "SetTimerSlack",
+ "Params":
+ {
+ "Slack": "40000000"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "TimerSlackNormal",
+ "Actions": [
+ {
+ "Name": "SetTimerSlack",
+ "Params":
+ {
+ "Slack": "50000"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "PerfBoost",
+ "Actions": [
+ {
+ "Name": "SetClamps",
+ "Params":
+ {
+ "Boost": "50%",
+ "Clamp": "0"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "PerfClamp",
+ "Actions": [
+ {
+ "Name": "SetClamps",
+ "Params":
+ {
+ "Boost": "0",
+ "Clamp": "30%"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "LowMemoryUsage",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "MemSoftLimit",
+ "Value": "16MB"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "MemSwappiness",
+ "Value": "150"
+
+ }
+ }
+ ]
+ },
+ {
+ "Name": "HighMemoryUsage",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "MemSoftLimit",
+ "Value": "512MB"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "MemSwappiness",
+ "Value": "100"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "SystemMemoryProcess",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "memory",
+ "Path": "system"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "FreezerDisabled",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "FreezerState",
+ "Value": "0"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "FreezerEnabled",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "FreezerState",
+ "Value": "1"
+ }
+ }
+ ]
+ }
+ ],
+
+ "AggregateProfiles": [
+ {
+ "Name": "SCHED_SP_DEFAULT",
+ "Profiles": [ "TimerSlackNormal" ]
+ },
+ {
+ "Name": "SCHED_SP_BACKGROUND",
+ "Profiles": [ "HighEnergySaving", "LowIoPriority", "TimerSlackHigh" ]
+ },
+ {
+ "Name": "SCHED_SP_FOREGROUND",
+ "Profiles": [ "HighPerformance", "HighIoPriority", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "SCHED_SP_TOP_APP",
+ "Profiles": [ "MaxPerformance", "MaxIoPriority", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "SCHED_SP_RT_APP",
+ "Profiles": [ "RealtimePerformance", "MaxIoPriority", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "CPUSET_SP_DEFAULT",
+ "Profiles": [ "TimerSlackNormal" ]
+ },
+ {
+ "Name": "CPUSET_SP_BACKGROUND",
+ "Profiles": [ "HighEnergySaving", "ProcessCapacityLow", "LowIoPriority", "TimerSlackHigh" ]
+ },
+ {
+ "Name": "CPUSET_SP_FOREGROUND",
+ "Profiles": [ "HighPerformance", "ProcessCapacityHigh", "HighIoPriority", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "CPUSET_SP_TOP_APP",
+ "Profiles": [ "MaxPerformance", "ProcessCapacityMax", "MaxIoPriority", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "CPUSET_SP_SYSTEM",
+ "Profiles": [ "ServiceCapacityLow", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "CPUSET_SP_RESTRICTED",
+ "Profiles": [ "ServiceCapacityRestricted", "TimerSlackNormal" ]
+ }
+ ]
+}
diff --git a/libprocessgroup/profiles/task_profiles_30.json b/libprocessgroup/profiles/task_profiles_30.json
new file mode 100644
index 0000000..142b0ba
--- /dev/null
+++ b/libprocessgroup/profiles/task_profiles_30.json
@@ -0,0 +1,627 @@
+{
+ "Attributes": [
+ {
+ "Name": "LowCapacityCPUs",
+ "Controller": "cpuset",
+ "File": "background/cpus"
+ },
+ {
+ "Name": "HighCapacityCPUs",
+ "Controller": "cpuset",
+ "File": "foreground/cpus"
+ },
+ {
+ "Name": "MaxCapacityCPUs",
+ "Controller": "cpuset",
+ "File": "top-app/cpus"
+ },
+ {
+ "Name": "MemLimit",
+ "Controller": "memory",
+ "File": "memory.limit_in_bytes"
+ },
+ {
+ "Name": "MemSoftLimit",
+ "Controller": "memory",
+ "File": "memory.soft_limit_in_bytes"
+ },
+ {
+ "Name": "MemSwappiness",
+ "Controller": "memory",
+ "File": "memory.swappiness"
+ },
+ {
+ "Name": "STuneBoost",
+ "Controller": "schedtune",
+ "File": "schedtune.boost"
+ },
+ {
+ "Name": "STunePreferIdle",
+ "Controller": "schedtune",
+ "File": "schedtune.prefer_idle"
+ },
+ {
+ "Name": "UClampMin",
+ "Controller": "cpu",
+ "File": "cpu.uclamp.min"
+ },
+ {
+ "Name": "UClampMax",
+ "Controller": "cpu",
+ "File": "cpu.uclamp.max"
+ },
+ {
+ "Name": "FreezerState",
+ "Controller": "freezer",
+ "File": "cgroup.freeze"
+ }
+ ],
+
+ "Profiles": [
+ {
+ "Name": "HighEnergySaving",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "Frozen",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "freezer",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "Unfrozen",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "freezer",
+ "Path": "../"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "NormalPerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "HighPerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "foreground"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "MaxPerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "top-app"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "RealtimePerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "rt"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "CameraServicePerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "camera-daemon"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "NNApiHALPerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "nnapi-hal"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "CpuPolicySpread",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "STunePreferIdle",
+ "Value": "1"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "CpuPolicyPack",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "STunePreferIdle",
+ "Value": "0"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "VrKernelCapacity",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrServiceCapacityLow",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "system/background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrServiceCapacityNormal",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "system"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrServiceCapacityHigh",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "system/performance"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrProcessCapacityLow",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "application/background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrProcessCapacityNormal",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "application"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrProcessCapacityHigh",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "application/performance"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "ProcessCapacityLow",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "ProcessCapacityNormal",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "ProcessCapacityHigh",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "foreground"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "ProcessCapacityMax",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "top-app"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "ServiceCapacityLow",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "system-background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "ServiceCapacityRestricted",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "restricted"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "CameraServiceCapacity",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "camera-daemon"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "LowIoPriority",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "blkio",
+ "Path": "background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "NormalIoPriority",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "blkio",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "HighIoPriority",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "blkio",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "MaxIoPriority",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "blkio",
+ "Path": ""
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "TimerSlackHigh",
+ "Actions": [
+ {
+ "Name": "SetTimerSlack",
+ "Params":
+ {
+ "Slack": "40000000"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "TimerSlackNormal",
+ "Actions": [
+ {
+ "Name": "SetTimerSlack",
+ "Params":
+ {
+ "Slack": "50000"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "PerfBoost",
+ "Actions": [
+ {
+ "Name": "SetClamps",
+ "Params":
+ {
+ "Boost": "50%",
+ "Clamp": "0"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "PerfClamp",
+ "Actions": [
+ {
+ "Name": "SetClamps",
+ "Params":
+ {
+ "Boost": "0",
+ "Clamp": "30%"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "LowMemoryUsage",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "MemSoftLimit",
+ "Value": "16MB"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "MemSwappiness",
+ "Value": "150"
+
+ }
+ }
+ ]
+ },
+ {
+ "Name": "HighMemoryUsage",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "MemSoftLimit",
+ "Value": "512MB"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "MemSwappiness",
+ "Value": "100"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "SystemMemoryProcess",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "memory",
+ "Path": "system"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "FreezerDisabled",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "FreezerState",
+ "Value": "0"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "FreezerEnabled",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "FreezerState",
+ "Value": "1"
+ }
+ }
+ ]
+ }
+ ],
+
+ "AggregateProfiles": [
+ {
+ "Name": "SCHED_SP_DEFAULT",
+ "Profiles": [ "TimerSlackNormal" ]
+ },
+ {
+ "Name": "SCHED_SP_BACKGROUND",
+ "Profiles": [ "HighEnergySaving", "LowIoPriority", "TimerSlackHigh" ]
+ },
+ {
+ "Name": "SCHED_SP_FOREGROUND",
+ "Profiles": [ "HighPerformance", "HighIoPriority", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "SCHED_SP_TOP_APP",
+ "Profiles": [ "MaxPerformance", "MaxIoPriority", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "SCHED_SP_RT_APP",
+ "Profiles": [ "RealtimePerformance", "MaxIoPriority", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "CPUSET_SP_DEFAULT",
+ "Profiles": [ "TimerSlackNormal" ]
+ },
+ {
+ "Name": "CPUSET_SP_BACKGROUND",
+ "Profiles": [ "HighEnergySaving", "ProcessCapacityLow", "LowIoPriority", "TimerSlackHigh" ]
+ },
+ {
+ "Name": "CPUSET_SP_FOREGROUND",
+ "Profiles": [ "HighPerformance", "ProcessCapacityHigh", "HighIoPriority", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "CPUSET_SP_TOP_APP",
+ "Profiles": [ "MaxPerformance", "ProcessCapacityMax", "MaxIoPriority", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "CPUSET_SP_SYSTEM",
+ "Profiles": [ "ServiceCapacityLow", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "CPUSET_SP_RESTRICTED",
+ "Profiles": [ "ServiceCapacityRestricted", "TimerSlackNormal" ]
+ }
+ ]
+}
diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
index 25f16a6..a53132e 100644
--- a/libprocessgroup/setup/cgroup_map_write.cpp
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -45,7 +45,7 @@
#include "cgroup_descriptor.h"
-using android::base::GetBoolProperty;
+using android::base::GetUintProperty;
using android::base::StringPrintf;
using android::base::unique_fd;
@@ -55,6 +55,8 @@
static constexpr const char* CGROUPS_DESC_FILE = "/etc/cgroups.json";
static constexpr const char* CGROUPS_DESC_VENDOR_FILE = "/vendor/etc/cgroups.json";
+static constexpr const char* TEMPLATE_CGROUPS_DESC_API_FILE = "/etc/task_profiles/cgroups_%u.json";
+
static bool ChangeDirModeAndOwner(const std::string& path, mode_t mode, const std::string& uid,
const std::string& gid, bool permissive_mode = false) {
uid_t pw_uid = -1;
@@ -212,8 +214,20 @@
}
static bool ReadDescriptors(std::map<std::string, CgroupDescriptor>* descriptors) {
+ unsigned int api_level = GetUintProperty<unsigned int>("ro.product.first_api_level", 0);
+ std::string sys_cgroups_path = CGROUPS_DESC_FILE;
+
+ // load API-level specific system cgroups descriptors if available
+ if (api_level > 0) {
+ std::string api_cgroups_path =
+ android::base::StringPrintf(TEMPLATE_CGROUPS_DESC_API_FILE, api_level);
+ if (!access(api_cgroups_path.c_str(), F_OK) || errno != ENOENT) {
+ sys_cgroups_path = api_cgroups_path;
+ }
+ }
+
// load system cgroup descriptors
- if (!ReadDescriptorsFromFile(CGROUPS_DESC_FILE, descriptors)) {
+ if (!ReadDescriptorsFromFile(sys_cgroups_path, descriptors)) {
return false;
}
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index a638fca..db44228 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -23,7 +23,9 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android-base/threads.h>
#include <cutils/android_filesystem_config.h>
@@ -37,12 +39,17 @@
#endif
using android::base::GetThreadId;
+using android::base::GetUintProperty;
using android::base::StringPrintf;
+using android::base::StringReplace;
using android::base::unique_fd;
using android::base::WriteStringToFile;
-#define TASK_PROFILE_DB_FILE "/etc/task_profiles.json"
-#define TASK_PROFILE_DB_VENDOR_FILE "/vendor/etc/task_profiles.json"
+static constexpr const char* TASK_PROFILE_DB_FILE = "/etc/task_profiles.json";
+static constexpr const char* TASK_PROFILE_DB_VENDOR_FILE = "/vendor/etc/task_profiles.json";
+
+static constexpr const char* TEMPLATE_TASK_PROFILE_API_FILE =
+ "/etc/task_profiles/task_profiles_%u.json";
void ProfileAttribute::Reset(const CgroupController& controller, const std::string& file_name) {
controller_ = controller;
@@ -257,6 +264,39 @@
return true;
}
+bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
+ std::string filepath(filepath_), value(value_);
+
+ filepath = StringReplace(filepath, "<uid>", std::to_string(uid), true);
+ filepath = StringReplace(filepath, "<pid>", std::to_string(pid), true);
+ value = StringReplace(value, "<uid>", std::to_string(uid), true);
+ value = StringReplace(value, "<pid>", std::to_string(pid), true);
+
+ if (!WriteStringToFile(value, filepath)) {
+ PLOG(ERROR) << "Failed to write '" << value << "' to " << filepath;
+ return false;
+ }
+
+ return true;
+}
+
+bool WriteFileAction::ExecuteForTask(int tid) const {
+ std::string filepath(filepath_), value(value_);
+ int uid = getuid();
+
+ filepath = StringReplace(filepath, "<uid>", std::to_string(uid), true);
+ filepath = StringReplace(filepath, "<pid>", std::to_string(tid), true);
+ value = StringReplace(value, "<uid>", std::to_string(uid), true);
+ value = StringReplace(value, "<pid>", std::to_string(tid), true);
+
+ if (!WriteStringToFile(value, filepath)) {
+ PLOG(ERROR) << "Failed to write '" << value << "' to " << filepath;
+ return false;
+ }
+
+ return true;
+}
+
bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
for (const auto& profile : profiles_) {
if (!profile->ExecuteForProcess(uid, pid)) {
@@ -351,9 +391,21 @@
}
TaskProfiles::TaskProfiles() {
+ unsigned int api_level = GetUintProperty<unsigned int>("ro.product.first_api_level", 0);
+ std::string sys_profiles_path = TASK_PROFILE_DB_FILE;
+
+ // load API-level specific system task profiles if available
+ if (api_level > 0) {
+ std::string api_profiles_path =
+ android::base::StringPrintf(TEMPLATE_TASK_PROFILE_API_FILE, api_level);
+ if (!access(api_profiles_path.c_str(), F_OK) || errno != ENOENT) {
+ sys_profiles_path = api_profiles_path;
+ }
+ }
+
// load system task profiles
- if (!Load(CgroupMap::GetInstance(), TASK_PROFILE_DB_FILE)) {
- LOG(ERROR) << "Loading " << TASK_PROFILE_DB_FILE << " for [" << getpid() << "] failed";
+ if (!Load(CgroupMap::GetInstance(), sys_profiles_path)) {
+ LOG(ERROR) << "Loading " << sys_profiles_path << " for [" << getpid() << "] failed";
}
// load vendor task profiles if the file exists
@@ -459,6 +511,18 @@
} else {
LOG(WARNING) << "SetClamps: invalid parameter: " << boost_value;
}
+ } else if (action_name == "WriteFile") {
+ std::string attr_filepath = params_val["FilePath"].asString();
+ std::string attr_value = params_val["Value"].asString();
+ if (!attr_filepath.empty() && !attr_value.empty()) {
+ profile->Add(std::make_unique<WriteFileAction>(attr_filepath, attr_value));
+ } else if (attr_filepath.empty()) {
+ LOG(WARNING) << "WriteFile: invalid parameter: "
+ << "empty filepath";
+ } else if (attr_value.empty()) {
+ LOG(WARNING) << "WriteFile: invalid parameter: "
+ << "empty value";
+ }
} else {
LOG(WARNING) << "Unknown profile action: " << action_name;
}
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 2983a09..98bcb0e 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -139,6 +139,19 @@
bool IsFdValid() const { return fd_ > FDS_INACCESSIBLE; }
};
+// Write to file action
+class WriteFileAction : public ProfileAction {
+ public:
+ WriteFileAction(const std::string& filepath, const std::string& value) noexcept
+ : filepath_(filepath), value_(value) {}
+
+ virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
+ virtual bool ExecuteForTask(int tid) const;
+
+ private:
+ std::string filepath_, value_;
+};
+
class TaskProfile {
public:
TaskProfile() : res_cached_(false) {}
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index bf06bbc..860b9ae 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -4,6 +4,7 @@
name: "libsparse",
host_supported: true,
ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
unique_host_soname: true,
vendor_available: true,
diff --git a/libutils/String16_test.cpp b/libutils/String16_test.cpp
index f1f24c3..2505f44 100644
--- a/libutils/String16_test.cpp
+++ b/libutils/String16_test.cpp
@@ -215,4 +215,16 @@
EXPECT_TRUE(tmp.isStaticString());
}
+TEST(String16Test, OverreadUtf8Conversion) {
+ char tmp[] = {'a', static_cast<char>(0xe0), '\0'};
+ String16 another(tmp);
+ EXPECT_TRUE(another.size() == 0);
+}
+
+TEST(String16Test, ValidUtf8Conversion) {
+ String16 another("abcdef");
+ EXPECT_EQ(6U, another.size());
+ EXPECT_STR16EQ(another, u"abcdef");
+}
+
} // namespace android
diff --git a/libutils/String8_test.cpp b/libutils/String8_test.cpp
index 3947a5f..9efcc6f 100644
--- a/libutils/String8_test.cpp
+++ b/libutils/String8_test.cpp
@@ -96,4 +96,9 @@
EXPECT_EQ(10U, string8.length());
}
+TEST_F(String8Test, ValidUtf16Conversion) {
+ char16_t tmp[] = u"abcdef";
+ String8 valid = String8(String16(tmp));
+ EXPECT_STREQ(valid, "abcdef");
+}
}
diff --git a/rootdir/Android.bp b/rootdir/Android.bp
index 96b5e0d..a21f686 100644
--- a/rootdir/Android.bp
+++ b/rootdir/Android.bp
@@ -24,3 +24,9 @@
src: "ueventd.rc",
recovery_available: true,
}
+
+// TODO(b/147210213) Generate list of libraries during build and fill in at build time
+linker_config {
+ name: "system_linker_config",
+ src: "etc/linker.config.json",
+}
diff --git a/rootdir/avb/Android.mk b/rootdir/avb/Android.mk
index 9892ae7..c8fc1d6 100644
--- a/rootdir/avb/Android.mk
+++ b/rootdir/avb/Android.mk
@@ -1,11 +1,17 @@
LOCAL_PATH:= $(call my-dir)
-ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
- my_gsi_avb_keys_path := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
-else ifeq ($(BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT),true)
- my_gsi_avb_keys_path := $(TARGET_VENDOR_RAMDISK_OUT)/avb
+ifeq ($(BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT),true) # AVB keys are installed to vendor ramdisk
+ ifeq ($(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT),true) # no dedicated recovery partition
+ my_gsi_avb_keys_path := $(TARGET_VENDOR_RAMDISK_OUT)/first_stage_ramdisk/avb
+ else # device has a dedicated recovery partition
+ my_gsi_avb_keys_path := $(TARGET_VENDOR_RAMDISK_OUT)/avb
+ endif
else
- my_gsi_avb_keys_path := $(TARGET_RAMDISK_OUT)/avb
+ ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) # no dedicated recovery partition
+ my_gsi_avb_keys_path := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+ else # device has a dedicated recovery partition
+ my_gsi_avb_keys_path := $(TARGET_RAMDISK_OUT)/avb
+ endif
endif
#######################################
diff --git a/rootdir/etc/linker.config.json b/rootdir/etc/linker.config.json
new file mode 100644
index 0000000..d66ab73
--- /dev/null
+++ b/rootdir/etc/linker.config.json
@@ -0,0 +1,72 @@
+{
+ // These are list of libraries which has stub interface and installed
+ // in system image so other partition and APEX modules can link to it.
+ // TODO(b/147210213) : Generate this list on build and read from the file
+ "provideLibs": [
+ // LLNDK libraries
+ "libEGL.so",
+ "libGLESv1_CM.so",
+ "libGLESv2.so",
+ "libGLESv3.so",
+ "libRS.so",
+ "libandroid_net.so",
+ "libbinder_ndk.so",
+ "libc.so",
+ "libcgrouprc.so",
+ "libclang_rt.asan-arm-android.so",
+ "libclang_rt.asan-i686-android.so",
+ "libclang_rt.asan-x86_64-android.so",
+ "libdl.so",
+ "libft2.so",
+ "liblog.so",
+ "libm.so",
+ "libmediandk.so",
+ "libnativewindow.so",
+ "libsync.so",
+ "libvndksupport.so",
+ "libvulkan.so",
+ // NDK libraries
+ "libaaudio.so",
+ "libandroid.so",
+ // adb
+ "libadbd_auth.so",
+ "libadbd_fs.so",
+ // bionic
+ "libdl_android.so",
+ // statsd
+ "libincident.so",
+ // media
+ "libmediametrics.so",
+ // nn
+ "libneuralnetworks_packageinfo.so",
+ // SELinux
+ "libselinux.so"
+ ],
+ "requireLibs": [
+ // Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
+ "libdexfile_external.so",
+ "libdexfiled_external.so",
+ "libnativebridge.so",
+ "libnativehelper.so",
+ "libnativeloader.so",
+ "libandroidicu.so",
+ "libicu.so",
+ // TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+ "libpac.so",
+ // TODO(b/120786417 or b/134659294): libicuuc.so
+ // and libicui18n.so are kept for app compat.
+ "libicui18n.so",
+ "libicuuc.so",
+ // resolv
+ "libnetd_resolv.so",
+ // nn
+ "libneuralnetworks.so",
+ // statsd
+ "libstatspull.so",
+ "libstatssocket.so",
+ // adbd
+ "libadb_pairing_auth.so",
+ "libadb_pairing_connection.so",
+ "libadb_pairing_server.so"
+ ]
+}
\ No newline at end of file
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 746fc61..52ba921 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -153,21 +153,56 @@
mkdir /dev/cpuctl/background
mkdir /dev/cpuctl/top-app
mkdir /dev/cpuctl/rt
+ mkdir /dev/cpuctl/system
chown system system /dev/cpuctl
chown system system /dev/cpuctl/foreground
chown system system /dev/cpuctl/background
chown system system /dev/cpuctl/top-app
chown system system /dev/cpuctl/rt
+ chown system system /dev/cpuctl/system
chown system system /dev/cpuctl/tasks
chown system system /dev/cpuctl/foreground/tasks
chown system system /dev/cpuctl/background/tasks
chown system system /dev/cpuctl/top-app/tasks
chown system system /dev/cpuctl/rt/tasks
+ chown system system /dev/cpuctl/system/tasks
chmod 0664 /dev/cpuctl/tasks
chmod 0664 /dev/cpuctl/foreground/tasks
chmod 0664 /dev/cpuctl/background/tasks
chmod 0664 /dev/cpuctl/top-app/tasks
chmod 0664 /dev/cpuctl/rt/tasks
+ chmod 0664 /dev/cpuctl/system/tasks
+
+ # Create a cpu group for NNAPI HAL processes
+ mkdir /dev/cpuctl/nnapi-hal
+ chown system system /dev/cpuctl/nnapi-hal
+ chown system system /dev/cpuctl/nnapi-hal/tasks
+ chmod 0664 /dev/cpuctl/nnapi-hal/tasks
+ write /dev/cpuctl/nnapi-hal/cpu.uclamp.min 1
+ write /dev/cpuctl/nnapi-hal/cpu.uclamp.latency_sensitive 1
+
+ # Android only use global RT throttling and doesn't use CONFIG_RT_GROUP_SCHED
+ # for RT group throttling. These values here are just to make sure RT threads
+ # can be migrated to those groups. These settings can be removed once we migrate
+ # to GKI kernel.
+ write /dev/cpuctl/cpu.rt_period_us 1000000
+ write /dev/cpuctl/cpu.rt_runtime_us 950000
+ # Surfaceflinger is in FG group so giving it a bit more
+ write /dev/cpuctl/foreground/cpu.rt_runtime_us 450000
+ write /dev/cpuctl/foreground/cpu.rt_period_us 1000000
+ write /dev/cpuctl/background/cpu.rt_runtime_us 100000
+ write /dev/cpuctl/background/cpu.rt_period_us 1000000
+ write /dev/cpuctl/top-app/cpu.rt_runtime_us 100000
+ write /dev/cpuctl/top-app/cpu.rt_period_us 1000000
+ write /dev/cpuctl/rt/cpu.rt_runtime_us 100000
+ write /dev/cpuctl/rt/cpu.rt_period_us 1000000
+ write /dev/cpuctl/system/cpu.rt_runtime_us 100000
+ write /dev/cpuctl/system/cpu.rt_period_us 1000000
+ write /dev/cpuctl/nnapi-hal/cpu.rt_runtime_us 100000
+ write /dev/cpuctl/nnapi-hal/cpu.rt_period_us 1000000
+
+ # Migrate root group to system subgroup
+ copy_per_line /dev/cpuctl/tasks /dev/cpuctl/system/tasks
# Create an stune group for NNAPI HAL processes
mkdir /dev/stune/nnapi-hal
@@ -177,14 +212,6 @@
write /dev/stune/nnapi-hal/schedtune.boost 1
write /dev/stune/nnapi-hal/schedtune.prefer_idle 1
- # cpuctl hierarchy for devices using utilclamp
- mkdir /dev/cpuctl/nnapi-hal
- chown system system /dev/cpuctl/nnapi-hal
- chown system system /dev/cpuctl/nnapi-hal/tasks
- chmod 0664 /dev/cpuctl/nnapi-hal/tasks
- write /dev/cpuctl/nnapi-hal/cpu.uclamp.min 1
- write /dev/cpuctl/nnapi-hal/cpu.uclamp.latency_sensitive 1
-
# Create blkio group and apply initial settings.
# This feature needs kernel to support it, and the
# device's init.rc must actually set the correct values.
@@ -275,8 +302,6 @@
write /proc/sys/vm/mmap_min_addr 32768
write /proc/sys/net/ipv4/ping_group_range "0 2147483647"
write /proc/sys/net/unix/max_dgram_qlen 600
- write /proc/sys/kernel/sched_rt_runtime_us 950000
- write /proc/sys/kernel/sched_rt_period_us 1000000
# Assign reasonable ceiling values for socket rcv/snd buffers.
# These should almost always be overridden by the target per the
@@ -298,13 +323,6 @@
# /proc/net/fib_trie leaks interface IP addresses
chmod 0400 /proc/net/fib_trie
- # Create cgroup mount points for process groups
- chown system system /dev/cpuctl
- chown system system /dev/cpuctl/tasks
- chmod 0666 /dev/cpuctl/tasks
- write /dev/cpuctl/cpu.rt_period_us 1000000
- write /dev/cpuctl/cpu.rt_runtime_us 950000
-
# sets up initial cpusets for ActivityManager
# this ensures that the cpusets are present and usable, but the device's
# init.rc must actually set the correct cpus
@@ -792,6 +810,7 @@
# Delete these if need be, per b/139193659
mkdir /data/rollback 0700 system system encryption=DeleteIfNecessary
mkdir /data/rollback-observer 0700 system system encryption=DeleteIfNecessary
+ mkdir /data/rollback-history 0700 system system encryption=DeleteIfNecessary
# Create root dir for Incremental Service
mkdir /data/incremental 0771 system system encryption=Require
@@ -807,6 +826,10 @@
wait_for_prop apexd.status activated
perform_apex_config
+ # After apexes are mounted, tell keymaster early boot has ended, so it will
+ # stop allowing use of early-boot keys
+ exec - system system -- /system/bin/vdc keymaster early-boot-ended
+
# Special-case /data/media/obb per b/64566063
mkdir /data/media 0770 media_rw media_rw encryption=None
exec - media_rw media_rw -- /system/bin/chattr +F /data/media
@@ -1146,3 +1169,7 @@
on property:sys.boot_completed=1 && property:sys.init.userspace_reboot.in_progress=1
setprop sys.init.userspace_reboot.in_progress ""
+
+# Migrate tasks again in case kernel threads are created during boot
+on property:sys.boot_completed=1
+ copy_per_line /dev/cpuctl/tasks /dev/cpuctl/system/tasks
diff --git a/toolbox/start.cpp b/toolbox/start.cpp
index cffb89c..46314cf 100644
--- a/toolbox/start.cpp
+++ b/toolbox/start.cpp
@@ -37,7 +37,6 @@
static void ControlDefaultServices(bool start) {
std::vector<std::string> services = {
- "iorapd",
"netd",
"surfaceflinger",
"audioserver",
@@ -92,4 +91,4 @@
extern "C" int stop_main(int argc, char** argv) {
return StartStop(argc, argv, false);
-}
\ No newline at end of file
+}
diff --git a/trusty/libtrusty/trusty.c b/trusty/libtrusty/trusty.c
index ad4d8cd..f44f8b4 100644
--- a/trusty/libtrusty/trusty.c
+++ b/trusty/libtrusty/trusty.c
@@ -29,30 +29,27 @@
#include <trusty/ipc.h>
-int tipc_connect(const char *dev_name, const char *srv_name)
-{
- int fd;
- int rc;
+int tipc_connect(const char* dev_name, const char* srv_name) {
+ int fd;
+ int rc;
- fd = open(dev_name, O_RDWR);
- if (fd < 0) {
- rc = -errno;
- ALOGE("%s: cannot open tipc device \"%s\": %s\n",
- __func__, dev_name, strerror(errno));
- return rc < 0 ? rc : -1;
- }
+ fd = TEMP_FAILURE_RETRY(open(dev_name, O_RDWR));
+ if (fd < 0) {
+ rc = -errno;
+ ALOGE("%s: cannot open tipc device \"%s\": %s\n", __func__, dev_name, strerror(errno));
+ return rc < 0 ? rc : -1;
+ }
- rc = ioctl(fd, TIPC_IOC_CONNECT, srv_name);
- if (rc < 0) {
- rc = -errno;
- ALOGE("%s: can't connect to tipc service \"%s\" (err=%d)\n",
- __func__, srv_name, errno);
- close(fd);
- return rc < 0 ? rc : -1;
- }
+ rc = TEMP_FAILURE_RETRY(ioctl(fd, TIPC_IOC_CONNECT, srv_name));
+ if (rc < 0) {
+ rc = -errno;
+ ALOGE("%s: can't connect to tipc service \"%s\" (err=%d)\n", __func__, srv_name, errno);
+ close(fd);
+ return rc < 0 ? rc : -1;
+ }
- ALOGV("%s: connected to \"%s\" fd %d\n", __func__, srv_name, fd);
- return fd;
+ ALOGV("%s: connected to \"%s\" fd %d\n", __func__, srv_name, fd);
+ return fd;
}
ssize_t tipc_send(int fd, const struct iovec* iov, int iovcnt, struct trusty_shm* shms,
@@ -63,7 +60,7 @@
req.shm = (__u64)shms;
req.shm_cnt = (__u64)shmcnt;
- int rc = ioctl(fd, TIPC_IOC_SEND_MSG, &req);
+ int rc = TEMP_FAILURE_RETRY(ioctl(fd, TIPC_IOC_SEND_MSG, &req));
if (rc < 0) {
ALOGE("%s: failed to send message (err=%d)\n", __func__, rc);
}
@@ -71,7 +68,6 @@
return rc;
}
-void tipc_close(int fd)
-{
- close(fd);
+void tipc_close(int fd) {
+ close(fd);
}
diff --git a/trusty/trusty-test.mk b/trusty/trusty-test.mk
index dc4c962..74106ec 100644
--- a/trusty/trusty-test.mk
+++ b/trusty/trusty-test.mk
@@ -15,4 +15,5 @@
PRODUCT_PACKAGES += \
spiproxyd \
trusty_keymaster_set_attestation_key \
- keymaster_soft_attestation_keys.xml \
\ No newline at end of file
+ keymaster_soft_attestation_keys.xml \
+