Merge "Remove sbin from fs_config.cpp"
diff --git a/base/Android.bp b/base/Android.bp
index 38f301a..d4268ca 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -29,6 +29,7 @@
vendor_available: true,
recovery_available: true,
host_supported: true,
+ native_bridge_supported: true,
export_include_dirs: ["include"],
target: {
@@ -100,6 +101,7 @@
vendor_available: true,
recovery_available: true,
host_supported: true,
+ native_bridge_supported: true,
vndk: {
enabled: true,
support_system_process: true,
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index f8f7eb3..25df451 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -502,9 +502,8 @@
static bool UnzipToMemory(ZipArchiveHandle zip, const std::string& entry_name,
std::vector<char>* out) {
- ZipString zip_entry_name(entry_name.c_str());
ZipEntry zip_entry;
- if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
+ if (FindEntry(zip, entry_name, &zip_entry) != 0) {
fprintf(stderr, "archive does not contain '%s'\n", entry_name.c_str());
return false;
}
@@ -614,9 +613,8 @@
static int unzip_to_file(ZipArchiveHandle zip, const char* entry_name) {
unique_fd fd(make_temporary_fd(entry_name));
- ZipString zip_entry_name(entry_name);
ZipEntry zip_entry;
- if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
+ if (FindEntry(zip, entry_name, &zip_entry) != 0) {
fprintf(stderr, "archive does not contain '%s'\n", entry_name);
errno = ENOENT;
return -1;
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index c1aafda..6f24fe1 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1610,38 +1610,6 @@
return ret;
}
-bool fs_mgr_load_verity_state(int* mode) {
- /* return the default mode, unless any of the verified partitions are in
- * logging mode, in which case return that */
- *mode = VERITY_MODE_DEFAULT;
-
- Fstab fstab;
- if (!ReadDefaultFstab(&fstab)) {
- LERROR << "Failed to read default fstab";
- return false;
- }
-
- for (const auto& entry : fstab) {
- if (entry.fs_mgr_flags.avb) {
- *mode = VERITY_MODE_RESTART; // avb only supports restart mode.
- break;
- } else if (!entry.fs_mgr_flags.verify) {
- continue;
- }
-
- int current;
- if (load_verity_state(entry, ¤t) < 0) {
- continue;
- }
- if (current != VERITY_MODE_DEFAULT) {
- *mode = current;
- break;
- }
- }
-
- return true;
-}
-
bool fs_mgr_is_verity_enabled(const FstabEntry& entry) {
if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
return false;
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index da049ef..78455d4 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -261,10 +261,6 @@
LWARNING << "Warning: zramsize= flag malformed: " << arg;
}
}
- } else if (StartsWith(flag, "verify=")) {
- // If the verify flag is followed by an = and the location for the verity state.
- entry->fs_mgr_flags.verify = true;
- entry->verity_loc = arg;
} else if (StartsWith(flag, "forceencrypt=")) {
// The forceencrypt flag is followed by an = and the location of the keys.
entry->fs_mgr_flags.force_crypt = true;
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 70abf5b..c36fd3d 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -99,7 +99,6 @@
bool fs_mgr_is_device_unlocked();
const std::string& get_android_dt_dir();
bool is_dt_compatible();
-int load_verity_state(const android::fs_mgr::FstabEntry& entry, int* mode);
bool fs_mgr_is_ext4(const std::string& blk_device);
bool fs_mgr_is_f2fs(const std::string& blk_device);
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 3f09157..1deb1ac 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -275,248 +275,6 @@
return 0;
}
-static int check_verity_restart(const char *fname)
-{
- char buffer[VERITY_KMSG_BUFSIZE + 1];
- int fd;
- int rc = 0;
- ssize_t size;
- struct stat s;
-
- fd = TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_CLOEXEC));
-
- if (fd == -1) {
- if (errno != ENOENT) {
- PERROR << "Failed to open " << fname;
- }
- goto out;
- }
-
- if (fstat(fd, &s) == -1) {
- PERROR << "Failed to fstat " << fname;
- goto out;
- }
-
- size = VERITY_KMSG_BUFSIZE;
-
- if (size > s.st_size) {
- size = s.st_size;
- }
-
- if (lseek(fd, s.st_size - size, SEEK_SET) == -1) {
- PERROR << "Failed to lseek " << (intmax_t)(s.st_size - size) << " " << fname;
- goto out;
- }
-
- if (!android::base::ReadFully(fd, buffer, size)) {
- PERROR << "Failed to read " << size << " bytes from " << fname;
- goto out;
- }
-
- buffer[size] = '\0';
-
- if (strstr(buffer, VERITY_KMSG_RESTART) != NULL) {
- rc = 1;
- }
-
-out:
- if (fd != -1) {
- close(fd);
- }
-
- return rc;
-}
-
-static int was_verity_restart()
-{
- static const char* files[] = {
- // clang-format off
- "/sys/fs/pstore/console-ramoops-0",
- "/sys/fs/pstore/console-ramoops",
- "/proc/last_kmsg",
- NULL
- // clang-format on
- };
- int i;
-
- for (i = 0; files[i]; ++i) {
- if (check_verity_restart(files[i])) {
- return 1;
- }
- }
-
- return 0;
-}
-
-static int metadata_add(FILE *fp, long start, const char *tag,
- unsigned int length, off64_t *offset)
-{
- if (fseek(fp, start, SEEK_SET) < 0 ||
- fprintf(fp, "%s %u\n", tag, length) < 0) {
- return -1;
- }
-
- *offset = ftell(fp);
-
- if (fseek(fp, length, SEEK_CUR) < 0 ||
- fprintf(fp, METADATA_EOD " 0\n") < 0) {
- return -1;
- }
-
- return 0;
-}
-
-static int metadata_find(const char *fname, const char *stag,
- unsigned int slength, off64_t *offset)
-{
- char tag[METADATA_TAG_MAX_LENGTH + 1];
- int rc = -1;
- int n;
- long start = 0x4000; /* skip cryptfs metadata area */
- uint32_t magic;
- unsigned int length = 0;
-
- if (!fname) {
- return -1;
- }
-
- auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(fname, "re+"), fclose};
-
- if (!fp) {
- PERROR << "Failed to open " << fname;
- return -1;
- }
-
- /* check magic */
- if (fseek(fp.get(), start, SEEK_SET) < 0 || fread(&magic, sizeof(magic), 1, fp.get()) != 1) {
- PERROR << "Failed to read magic from " << fname;
- return -1;
- }
-
- if (magic != METADATA_MAGIC) {
- magic = METADATA_MAGIC;
-
- if (fseek(fp.get(), start, SEEK_SET) < 0 ||
- fwrite(&magic, sizeof(magic), 1, fp.get()) != 1) {
- PERROR << "Failed to write magic to " << fname;
- return -1;
- }
-
- rc = metadata_add(fp.get(), start + sizeof(magic), stag, slength, offset);
- if (rc < 0) {
- PERROR << "Failed to add metadata to " << fname;
- }
-
- return rc;
- }
-
- start += sizeof(magic);
-
- while (1) {
- n = fscanf(fp.get(), "%" STRINGIFY(METADATA_TAG_MAX_LENGTH) "s %u\n", tag, &length);
-
- if (n == 2 && strcmp(tag, METADATA_EOD)) {
- /* found a tag */
- start = ftell(fp.get());
-
- if (!strcmp(tag, stag) && length == slength) {
- *offset = start;
- return 0;
- }
-
- start += length;
-
- if (fseek(fp.get(), length, SEEK_CUR) < 0) {
- PERROR << "Failed to seek " << fname;
- return -1;
- }
- } else {
- rc = metadata_add(fp.get(), start, stag, slength, offset);
- if (rc < 0) {
- PERROR << "Failed to write metadata to " << fname;
- }
- return rc;
- }
- }
-}
-
-static int write_verity_state(const char *fname, off64_t offset, int32_t mode)
-{
- int fd;
- int rc = -1;
- struct verity_state s = { VERITY_STATE_HEADER, VERITY_STATE_VERSION, mode };
-
- fd = TEMP_FAILURE_RETRY(open(fname, O_WRONLY | O_SYNC | O_CLOEXEC));
-
- if (fd == -1) {
- PERROR << "Failed to open " << fname;
- goto out;
- }
-
- if (TEMP_FAILURE_RETRY(pwrite64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
- PERROR << "Failed to write " << sizeof(s) << " bytes to " << fname
- << " to offset " << offset;
- goto out;
- }
-
- rc = 0;
-
-out:
- if (fd != -1) {
- close(fd);
- }
-
- return rc;
-}
-
-static int read_verity_state(const char *fname, off64_t offset, int *mode)
-{
- int fd = -1;
- int rc = -1;
- struct verity_state s;
-
- fd = TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_CLOEXEC));
-
- if (fd == -1) {
- PERROR << "Failed to open " << fname;
- goto out;
- }
-
- if (TEMP_FAILURE_RETRY(pread64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
- PERROR << "Failed to read " << sizeof(s) << " bytes from " << fname
- << " offset " << offset;
- goto out;
- }
-
- if (s.header != VERITY_STATE_HEADER) {
- /* space allocated, but no state written. write default state */
- *mode = VERITY_MODE_DEFAULT;
- rc = write_verity_state(fname, offset, *mode);
- goto out;
- }
-
- if (s.version != VERITY_STATE_VERSION) {
- LERROR << "Unsupported verity state version (" << s.version << ")";
- goto out;
- }
-
- if (s.mode < VERITY_MODE_EIO ||
- s.mode > VERITY_MODE_LAST) {
- LERROR << "Unsupported verity mode (" << s.mode << ")";
- goto out;
- }
-
- *mode = s.mode;
- rc = 0;
-
-out:
- if (fd != -1) {
- close(fd);
- }
-
- return rc;
-}
-
static int read_partition(const char *path, uint64_t size)
{
char buf[READ_BUF_SIZE];
@@ -540,119 +298,23 @@
return 0;
}
-static int compare_last_signature(const FstabEntry& entry, int* match) {
- char tag[METADATA_TAG_MAX_LENGTH + 1];
- int fd = -1;
- int rc = -1;
- off64_t offset = 0;
- struct fec_handle *f = NULL;
- struct fec_verity_metadata verity;
- uint8_t curr[SHA256_DIGEST_LENGTH];
- uint8_t prev[SHA256_DIGEST_LENGTH];
-
- *match = 1;
-
- if (fec_open(&f, entry.blk_device.c_str(), O_RDONLY, FEC_VERITY_DISABLE, FEC_DEFAULT_ROOTS) ==
- -1) {
- PERROR << "Failed to open '" << entry.blk_device << "'";
- return rc;
- }
-
- // read verity metadata
- if (fec_verity_get_metadata(f, &verity) == -1) {
- PERROR << "Failed to get verity metadata '" << entry.blk_device << "'";
- goto out;
- }
-
- SHA256(verity.signature, sizeof(verity.signature), curr);
-
- if (snprintf(tag, sizeof(tag), VERITY_LASTSIG_TAG "_%s", basename(entry.mount_point.c_str())) >=
- (int)sizeof(tag)) {
- LERROR << "Metadata tag name too long for " << entry.mount_point;
- goto out;
- }
-
- if (metadata_find(entry.verity_loc.c_str(), tag, SHA256_DIGEST_LENGTH, &offset) < 0) {
- goto out;
- }
-
- fd = TEMP_FAILURE_RETRY(open(entry.verity_loc.c_str(), O_RDWR | O_SYNC | O_CLOEXEC));
-
- if (fd == -1) {
- PERROR << "Failed to open " << entry.verity_loc;
- goto out;
- }
-
- if (TEMP_FAILURE_RETRY(pread64(fd, prev, sizeof(prev), offset)) != sizeof(prev)) {
- PERROR << "Failed to read " << sizeof(prev) << " bytes from " << entry.verity_loc
- << " offset " << offset;
- goto out;
- }
-
- *match = !memcmp(curr, prev, SHA256_DIGEST_LENGTH);
-
- if (!*match) {
- /* update current signature hash */
- if (TEMP_FAILURE_RETRY(pwrite64(fd, curr, sizeof(curr),
- offset)) != sizeof(curr)) {
- PERROR << "Failed to write " << sizeof(curr) << " bytes to " << entry.verity_loc
- << " offset " << offset;
- goto out;
- }
- }
-
- rc = 0;
-
-out:
- fec_close(f);
- return rc;
-}
-
-static int get_verity_state_offset(const FstabEntry& entry, off64_t* offset) {
- char tag[METADATA_TAG_MAX_LENGTH + 1];
-
- if (snprintf(tag, sizeof(tag), VERITY_STATE_TAG "_%s", basename(entry.mount_point.c_str())) >=
- (int)sizeof(tag)) {
- LERROR << "Metadata tag name too long for " << entry.mount_point;
- return -1;
- }
-
- return metadata_find(entry.verity_loc.c_str(), tag, sizeof(struct verity_state), offset);
-}
-
-int load_verity_state(const FstabEntry& entry, int* mode) {
+bool fs_mgr_load_verity_state(int* mode) {
// unless otherwise specified, use EIO mode.
*mode = VERITY_MODE_EIO;
- // use the kernel parameter if set.
- std::string veritymode;
- if (fs_mgr_get_boot_config("veritymode", &veritymode)) {
- if (veritymode == "enforcing") {
- *mode = VERITY_MODE_DEFAULT;
- }
- return 0;
+ // The bootloader communicates verity mode via the kernel commandline
+ std::string verity_mode;
+ if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
+ return false;
}
- off64_t offset = 0;
- if (get_verity_state_offset(entry, &offset) < 0) {
- /* fall back to stateless behavior */
- return 0;
- }
-
- if (was_verity_restart()) {
- /* device was restarted after dm-verity detected a corrupted
- * block, so use EIO mode */
- return write_verity_state(entry.verity_loc.c_str(), offset, *mode);
- }
-
- int match = 0;
- if (!compare_last_signature(entry, &match) && !match) {
- /* partition has been reflashed, reset dm-verity state */
+ if (verity_mode == "enforcing") {
*mode = VERITY_MODE_DEFAULT;
- return write_verity_state(entry.verity_loc.c_str(), offset, *mode);
+ } else if (verity_mode == "logging") {
+ *mode = VERITY_MODE_LOGGING;
}
- return read_verity_state(entry.verity_loc.c_str(), offset, mode);
+ return true;
}
// Update the verity table using the actual block device path.
@@ -759,7 +421,7 @@
params.ecc_dev = entry->blk_device.c_str();
- if (load_verity_state(*entry, ¶ms.mode) < 0) {
+ if (!fs_mgr_load_verity_state(¶ms.mode)) {
/* if accessing or updating the state failed, switch to the default
* safe mode. This makes sure the device won't end up in an endless
* restart loop, and no corrupted data will be exposed to userspace
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index d7afed6..c7193ab 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -38,7 +38,6 @@
std::string fs_options;
std::string key_loc;
std::string key_dir;
- std::string verity_loc;
off64_t length = 0;
std::string label;
int partnum = -1;
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index a4614d0..c2917a4 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -210,6 +210,20 @@
return true;
}
+bool DeviceMapper::GetTargetByName(const std::string& name, DmTargetTypeInfo* info) {
+ std::vector<DmTargetTypeInfo> targets;
+ if (!GetAvailableTargets(&targets)) {
+ return false;
+ }
+ for (const auto& target : targets) {
+ if (target.name() == name) {
+ if (info) *info = target;
+ return true;
+ }
+ }
+ return false;
+}
+
bool DeviceMapper::GetAvailableDevices(std::vector<DmBlockDevice>* devices) {
devices->clear();
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index cb33eea..f440e6d 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -18,6 +18,7 @@
#include <android-base/logging.h>
#include <android-base/macros.h>
+#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <libdm/dm.h>
@@ -115,5 +116,82 @@
return keyid_ + " " + block_device_;
}
+std::string DmTargetSnapshot::name() const {
+ if (mode_ == SnapshotStorageMode::Merge) {
+ return "snapshot-merge";
+ }
+ return "snapshot";
+}
+
+std::string DmTargetSnapshot::GetParameterString() const {
+ std::string mode;
+ switch (mode_) {
+ case SnapshotStorageMode::Persistent:
+ case SnapshotStorageMode::Merge:
+ // Note: "O" lets us query for overflow in the status message. This
+ // is only supported on kernels 4.4+. On earlier kernels, an overflow
+ // will be reported as "Invalid" in the status string.
+ mode = "P";
+ if (ReportsOverflow(name())) {
+ mode += "O";
+ }
+ break;
+ case SnapshotStorageMode::Transient:
+ mode = "N";
+ break;
+ default:
+ LOG(ERROR) << "DmTargetSnapshot unknown mode";
+ break;
+ }
+ return base_device_ + " " + cow_device_ + " " + mode + " " + std::to_string(chunk_size_);
+}
+
+bool DmTargetSnapshot::ReportsOverflow(const std::string& target_type) {
+ DeviceMapper& dm = DeviceMapper::Instance();
+ DmTargetTypeInfo info;
+ if (!dm.GetTargetByName(target_type, &info)) {
+ return false;
+ }
+ if (target_type == "snapshot") {
+ return info.IsAtLeast(1, 15, 0);
+ }
+ if (target_type == "snapshot-merge") {
+ return info.IsAtLeast(1, 4, 0);
+ }
+ return false;
+}
+
+bool DmTargetSnapshot::ParseStatusText(const std::string& text, Status* status) {
+ auto sections = android::base::Split(text, " ");
+ if (sections.size() == 1) {
+ // This is probably an error code, "Invalid" is possible as is "Overflow"
+ // on 4.4+.
+ status->error = text;
+ return true;
+ }
+ if (sections.size() != 2) {
+ LOG(ERROR) << "snapshot status should have two components";
+ return false;
+ }
+ auto sector_info = android::base::Split(sections[0], "/");
+ if (sector_info.size() != 2) {
+ LOG(ERROR) << "snapshot sector info should have two components";
+ return false;
+ }
+ if (!android::base::ParseUint(sections[1], &status->metadata_sectors)) {
+ LOG(ERROR) << "could not parse metadata sectors";
+ return false;
+ }
+ if (!android::base::ParseUint(sector_info[0], &status->sectors_allocated)) {
+ LOG(ERROR) << "could not parse sectors allocated";
+ return false;
+ }
+ if (!android::base::ParseUint(sector_info[1], &status->total_sectors)) {
+ LOG(ERROR) << "could not parse total sectors";
+ return false;
+ }
+ return true;
+}
+
} // namespace dm
} // namespace android
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index 70823c6..72a0e11 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -24,6 +24,7 @@
#include <chrono>
#include <ctime>
+#include <iostream>
#include <map>
#include <thread>
@@ -35,21 +36,15 @@
#include "test_util.h"
using namespace std;
+using namespace std::chrono_literals;
using namespace android::dm;
using unique_fd = android::base::unique_fd;
TEST(libdm, HasMinimumTargets) {
+ DmTargetTypeInfo info;
+
DeviceMapper& dm = DeviceMapper::Instance();
- vector<DmTargetTypeInfo> targets;
- ASSERT_TRUE(dm.GetAvailableTargets(&targets));
-
- map<string, DmTargetTypeInfo> by_name;
- for (const auto& target : targets) {
- by_name[target.name()] = target;
- }
-
- auto iter = by_name.find("linear");
- EXPECT_NE(iter, by_name.end());
+ ASSERT_TRUE(dm.GetTargetByName("linear", &info));
}
// Helper to ensure that device mapper devices are released.
@@ -201,3 +196,245 @@
"2 fec_blocks 126955 fec_start 126955 restart_on_corruption ignore_zero_blocks";
EXPECT_EQ(target.GetParameterString(), expected);
}
+
+TEST(libdm, DmSnapshotArgs) {
+ DmTargetSnapshot target1(0, 512, "base", "cow", SnapshotStorageMode::Persistent, 8);
+ if (DmTargetSnapshot::ReportsOverflow("snapshot")) {
+ EXPECT_EQ(target1.GetParameterString(), "base cow PO 8");
+ } else {
+ EXPECT_EQ(target1.GetParameterString(), "base cow P 8");
+ }
+ EXPECT_EQ(target1.name(), "snapshot");
+
+ DmTargetSnapshot target2(0, 512, "base", "cow", SnapshotStorageMode::Transient, 8);
+ EXPECT_EQ(target2.GetParameterString(), "base cow N 8");
+ EXPECT_EQ(target2.name(), "snapshot");
+
+ DmTargetSnapshot target3(0, 512, "base", "cow", SnapshotStorageMode::Merge, 8);
+ if (DmTargetSnapshot::ReportsOverflow("snapshot-merge")) {
+ EXPECT_EQ(target3.GetParameterString(), "base cow PO 8");
+ } else {
+ EXPECT_EQ(target3.GetParameterString(), "base cow P 8");
+ }
+ EXPECT_EQ(target3.name(), "snapshot-merge");
+}
+
+TEST(libdm, DmSnapshotOriginArgs) {
+ DmTargetSnapshotOrigin target(0, 512, "base");
+ EXPECT_EQ(target.GetParameterString(), "base");
+ EXPECT_EQ(target.name(), "snapshot-origin");
+}
+
+class SnapshotTestHarness final {
+ public:
+ bool Setup();
+ bool Merge();
+
+ std::string origin_dev() const { return origin_dev_->path(); }
+ std::string snapshot_dev() const { return snapshot_dev_->path(); }
+
+ int base_fd() const { return base_fd_; }
+
+ static const uint64_t kBaseDeviceSize = 1024 * 1024;
+ static const uint64_t kCowDeviceSize = 1024 * 64;
+ static const uint64_t kSectorSize = 512;
+
+ private:
+ void SetupImpl();
+ void MergeImpl();
+
+ unique_fd base_fd_;
+ unique_fd cow_fd_;
+ unique_ptr<LoopDevice> base_loop_;
+ unique_ptr<LoopDevice> cow_loop_;
+ unique_ptr<TempDevice> origin_dev_;
+ unique_ptr<TempDevice> snapshot_dev_;
+ bool setup_ok_ = false;
+ bool merge_ok_ = false;
+};
+
+bool SnapshotTestHarness::Setup() {
+ SetupImpl();
+ return setup_ok_;
+}
+
+void SnapshotTestHarness::SetupImpl() {
+ base_fd_ = CreateTempFile("base_device", kBaseDeviceSize);
+ ASSERT_GE(base_fd_, 0);
+ cow_fd_ = CreateTempFile("cow_device", kCowDeviceSize);
+ ASSERT_GE(cow_fd_, 0);
+
+ base_loop_ = std::make_unique<LoopDevice>(base_fd_);
+ ASSERT_TRUE(base_loop_->valid());
+ cow_loop_ = std::make_unique<LoopDevice>(cow_fd_);
+ ASSERT_TRUE(cow_loop_->valid());
+
+ DmTable origin_table;
+ ASSERT_TRUE(origin_table.AddTarget(make_unique<DmTargetSnapshotOrigin>(
+ 0, kBaseDeviceSize / kSectorSize, base_loop_->device())));
+ ASSERT_TRUE(origin_table.valid());
+
+ origin_dev_ = std::make_unique<TempDevice>("libdm-test-dm-snapshot-origin", origin_table);
+ ASSERT_TRUE(origin_dev_->valid());
+ ASSERT_FALSE(origin_dev_->path().empty());
+ ASSERT_TRUE(origin_dev_->WaitForUdev());
+
+ // chunk size = 4K blocks.
+ DmTable snap_table;
+ ASSERT_TRUE(snap_table.AddTarget(make_unique<DmTargetSnapshot>(
+ 0, kBaseDeviceSize / kSectorSize, base_loop_->device(), cow_loop_->device(),
+ SnapshotStorageMode::Persistent, 8)));
+ ASSERT_TRUE(snap_table.valid());
+
+ snapshot_dev_ = std::make_unique<TempDevice>("libdm-test-dm-snapshot", snap_table);
+ ASSERT_TRUE(snapshot_dev_->valid());
+ ASSERT_FALSE(snapshot_dev_->path().empty());
+ ASSERT_TRUE(snapshot_dev_->WaitForUdev());
+
+ setup_ok_ = true;
+}
+
+bool SnapshotTestHarness::Merge() {
+ MergeImpl();
+ return merge_ok_;
+}
+
+void SnapshotTestHarness::MergeImpl() {
+ DmTable merge_table;
+ ASSERT_TRUE(merge_table.AddTarget(
+ make_unique<DmTargetSnapshot>(0, kBaseDeviceSize / kSectorSize, base_loop_->device(),
+ cow_loop_->device(), SnapshotStorageMode::Merge, 8)));
+ ASSERT_TRUE(merge_table.valid());
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ ASSERT_TRUE(dm.LoadTableAndActivate("libdm-test-dm-snapshot", merge_table));
+
+ while (true) {
+ vector<DeviceMapper::TargetInfo> status;
+ ASSERT_TRUE(dm.GetTableStatus("libdm-test-dm-snapshot", &status));
+ ASSERT_EQ(status.size(), 1);
+ ASSERT_EQ(strncmp(status[0].spec.target_type, "snapshot-merge", strlen("snapshot-merge")),
+ 0);
+
+ DmTargetSnapshot::Status merge_status;
+ ASSERT_TRUE(DmTargetSnapshot::ParseStatusText(status[0].data, &merge_status));
+ ASSERT_TRUE(merge_status.error.empty());
+ if (merge_status.sectors_allocated == merge_status.metadata_sectors) {
+ break;
+ }
+
+ std::this_thread::sleep_for(250ms);
+ }
+
+ merge_ok_ = true;
+}
+
+bool CheckSnapshotAvailability() {
+ DmTargetTypeInfo info;
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ if (!dm.GetTargetByName("snapshot", &info)) {
+ cout << "snapshot module not enabled; skipping test" << std::endl;
+ return false;
+ }
+ if (!dm.GetTargetByName("snapshot-merge", &info)) {
+ cout << "snapshot-merge module not enabled; skipping test" << std::endl;
+ return false;
+ }
+ if (!dm.GetTargetByName("snapshot-origin", &info)) {
+ cout << "snapshot-origin module not enabled; skipping test" << std::endl;
+ return false;
+ }
+ return true;
+}
+
+TEST(libdm, DmSnapshot) {
+ if (!CheckSnapshotAvailability()) {
+ return;
+ }
+
+ SnapshotTestHarness harness;
+ ASSERT_TRUE(harness.Setup());
+
+ // Open the dm devices.
+ unique_fd origin_fd(open(harness.origin_dev().c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_GE(origin_fd, 0);
+ unique_fd snapshot_fd(open(harness.snapshot_dev().c_str(), O_RDWR | O_CLOEXEC | O_SYNC));
+ ASSERT_GE(snapshot_fd, 0);
+
+ // Write to the first block of the snapshot device.
+ std::string data("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
+ ASSERT_TRUE(android::base::WriteFully(snapshot_fd, data.data(), data.size()));
+ ASSERT_EQ(lseek(snapshot_fd, 0, SEEK_SET), 0);
+
+ // We should get the same data back from the snapshot device.
+ std::string read(data.size(), '\0');
+ ASSERT_TRUE(android::base::ReadFully(snapshot_fd, read.data(), read.size()));
+ ASSERT_EQ(read, data);
+
+ // We should see the original data from the origin device.
+ std::string zeroes(data.size(), '\0');
+ ASSERT_TRUE(android::base::ReadFully(origin_fd, read.data(), read.size()));
+ ASSERT_EQ(lseek(snapshot_fd, 0, SEEK_SET), 0);
+ ASSERT_EQ(read, zeroes);
+
+ // We should also see the original data from the base device.
+ ASSERT_TRUE(android::base::ReadFully(harness.base_fd(), read.data(), read.size()));
+ ASSERT_EQ(lseek(harness.base_fd(), 0, SEEK_SET), 0);
+ ASSERT_EQ(read, zeroes);
+
+ // Now, perform the merge and wait.
+ ASSERT_TRUE(harness.Merge());
+
+ // Reading from the base device should give us the modified data.
+ ASSERT_TRUE(android::base::ReadFully(harness.base_fd(), read.data(), read.size()));
+ ASSERT_EQ(lseek(harness.base_fd(), 0, SEEK_SET), 0);
+ ASSERT_EQ(read, data);
+}
+
+TEST(libdm, DmSnapshotOverflow) {
+ if (!CheckSnapshotAvailability()) {
+ return;
+ }
+
+ SnapshotTestHarness harness;
+ ASSERT_TRUE(harness.Setup());
+
+ // Open the dm devices.
+ unique_fd snapshot_fd(open(harness.snapshot_dev().c_str(), O_RDWR | O_CLOEXEC));
+ ASSERT_GE(snapshot_fd, 0);
+
+ // Fill the copy-on-write device until it overflows.
+ uint64_t bytes_remaining = SnapshotTestHarness::kCowDeviceSize;
+ uint8_t byte = 1;
+ while (bytes_remaining) {
+ std::string data(4096, char(byte));
+ if (!android::base::WriteFully(snapshot_fd, data.data(), data.size())) {
+ ASSERT_EQ(errno, EIO);
+ break;
+ }
+ bytes_remaining -= data.size();
+ }
+
+ // If writes succeed (because they are buffered), then we should expect an
+ // fsync to fail with EIO.
+ if (!bytes_remaining) {
+ ASSERT_EQ(fsync(snapshot_fd), -1);
+ ASSERT_EQ(errno, EIO);
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+
+ vector<DeviceMapper::TargetInfo> target_status;
+ ASSERT_TRUE(dm.GetTableStatus("libdm-test-dm-snapshot", &target_status));
+ ASSERT_EQ(target_status.size(), 1);
+ ASSERT_EQ(strncmp(target_status[0].spec.target_type, "snapshot", strlen("snapshot")), 0);
+
+ DmTargetSnapshot::Status status;
+ ASSERT_TRUE(DmTargetSnapshot::ParseStatusText(target_status[0].data, &status));
+ if (DmTargetSnapshot::ReportsOverflow("snapshot")) {
+ ASSERT_EQ(status.error, "Overflow");
+ } else {
+ ASSERT_EQ(status.error, "Invalid");
+ }
+}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 28e6e01..d7e8aa9 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -96,6 +96,10 @@
// successfully read and stored in 'targets'. Returns 'false' otherwise.
bool GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets);
+ // Finds a target by name and returns its information if found. |info| may
+ // be null to check for the existence of a target.
+ bool GetTargetByName(const std::string& name, DmTargetTypeInfo* info);
+
// Return 'true' if it can successfully read the list of device mapper block devices
// currently created. 'devices' will be empty if the kernel interactions
// were successful and there are no block devices at the moment. Returns
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index 175b0f0..fce1175 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -40,6 +40,18 @@
return std::to_string(major_) + "." + std::to_string(minor_) + "." + std::to_string(patch_);
}
+ uint32_t major_version() const { return major_; }
+ uint32_t minor_version() const { return minor_; }
+ uint32_t patch_level() const { return patch_; }
+
+ bool IsAtLeast(uint32_t major, uint32_t minor, uint32_t patch) const {
+ if (major_ > major) return true;
+ if (major_ < major) return false;
+ if (minor_ > minor) return true;
+ if (minor_ < minor) return false;
+ return patch_ >= patch;
+ }
+
private:
std::string name_;
uint32_t major_;
@@ -170,6 +182,65 @@
std::string target_string_;
};
+enum class SnapshotStorageMode {
+ // The snapshot will be persisted to the COW device.
+ Persistent,
+ // The snapshot will be lost on reboot.
+ Transient,
+ // The snapshot will be merged from the COW device into the base device,
+ // in the background.
+ Merge
+};
+
+// Writes to a snapshot device will be written to the given COW device. Reads
+// will read from the COW device or base device. The chunk size is specified
+// in sectors.
+class DmTargetSnapshot final : public DmTarget {
+ public:
+ DmTargetSnapshot(uint64_t start, uint64_t length, const std::string& base_device,
+ const std::string& cow_device, SnapshotStorageMode mode, uint64_t chunk_size)
+ : DmTarget(start, length),
+ base_device_(base_device),
+ cow_device_(cow_device),
+ mode_(mode),
+ chunk_size_(chunk_size) {}
+
+ std::string name() const override;
+ std::string GetParameterString() const override;
+ bool Valid() const override { return true; }
+
+ struct Status {
+ uint64_t sectors_allocated;
+ uint64_t total_sectors;
+ uint64_t metadata_sectors;
+ std::string error;
+ };
+
+ static bool ParseStatusText(const std::string& text, Status* status);
+ static bool ReportsOverflow(const std::string& target_type);
+
+ private:
+ std::string base_device_;
+ std::string cow_device_;
+ SnapshotStorageMode mode_;
+ uint64_t chunk_size_;
+};
+
+// snapshot-origin will read/write directly to the backing device, updating any
+// snapshot devices with a matching origin.
+class DmTargetSnapshotOrigin final : public DmTarget {
+ public:
+ DmTargetSnapshotOrigin(uint64_t start, uint64_t length, const std::string& device)
+ : DmTarget(start, length), device_(device) {}
+
+ std::string name() const override { return "snapshot-origin"; }
+ std::string GetParameterString() const override { return device_; }
+ bool Valid() const override { return true; }
+
+ private:
+ std::string device_;
+};
+
} // namespace dm
} // namespace android
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
index 04776ed..c4d7511 100644
--- a/fs_mgr/libfs_avb/fs_avb.cpp
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -338,6 +338,7 @@
nullptr /* custom_device_path */);
}
+// TODO(b/128807537): removes this function.
AvbUniquePtr AvbHandle::Open() {
bool is_device_unlocked = IsDeviceUnlocked();
@@ -353,25 +354,28 @@
AvbSlotVerifyResult verify_result =
avb_ops.AvbSlotVerify(fs_mgr_get_slot_suffix(), flags, &avb_handle->vbmeta_images_);
- // Only allow two verify results:
+ // Only allow the following verify results:
// - AVB_SLOT_VERIFY_RESULT_OK.
- // - AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION (for UNLOCKED state).
- // If the device is UNLOCKED, i.e., |allow_verification_error| is true for
- // AvbSlotVerify(), then the following return values are all non-fatal:
- // * AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION
- // * AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED
- // * AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX
- // The latter two results were checked by bootloader prior to start fs_mgr so
- // we just need to handle the first result here. See *dummy* operations in
- // FsManagerAvbOps and the comments in external/avb/libavb/avb_slot_verify.h
- // for more details.
+ // - AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION (UNLOCKED only).
+ // Might occur in either the top-level vbmeta or a chained vbmeta.
+ // - AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED (UNLOCKED only).
+ // Could only occur in a chained vbmeta. Because we have *dummy* operations in
+ // FsManagerAvbOps such that avb_ops->validate_vbmeta_public_key() used to validate
+ // the public key of the top-level vbmeta always pass in userspace here.
+ //
+ // The following verify result won't happen, because the *dummy* operation
+ // avb_ops->read_rollback_index() always returns the minimum value zero. So rollbacked
+ // vbmeta images, which should be caught in the bootloader stage, won't be detected here.
+ // - AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX
switch (verify_result) {
case AVB_SLOT_VERIFY_RESULT_OK:
avb_handle->status_ = AvbHandleStatus::kSuccess;
break;
case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
+ case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
if (!is_device_unlocked) {
- LERROR << "ERROR_VERIFICATION isn't allowed when the device is LOCKED";
+ LERROR << "ERROR_VERIFICATION / PUBLIC_KEY_REJECTED isn't allowed "
+ << "if the device is LOCKED";
return nullptr;
}
avb_handle->status_ = AvbHandleStatus::kVerificationError;
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 8fc02cb..fcef1f0 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -21,6 +21,8 @@
#include <android-base/file.h>
#include <android-base/unique_fd.h>
+#include <fs_mgr.h>
+#include <fstab/fstab.h>
#include <gtest/gtest.h>
#include <liblp/builder.h>
@@ -702,3 +704,19 @@
ASSERT_EQ(updated->block_devices.size(), static_cast<size_t>(1));
EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[0]), "super");
}
+
+TEST(liblp, ReadSuperPartition) {
+ auto slot_suffix = fs_mgr_get_slot_suffix();
+ auto slot_number = SlotNumberForSlotSuffix(slot_suffix);
+ auto super_name = fs_mgr_get_super_partition_name(slot_number);
+ auto metadata = ReadMetadata(super_name, slot_number);
+ ASSERT_NE(metadata, nullptr);
+
+ if (!slot_suffix.empty()) {
+ auto other_slot_suffix = fs_mgr_get_other_slot_suffix();
+ auto other_slot_number = SlotNumberForSlotSuffix(other_slot_suffix);
+ auto other_super_name = fs_mgr_get_super_partition_name(other_slot_number);
+ auto other_metadata = ReadMetadata(other_super_name, other_slot_number);
+ ASSERT_NE(other_metadata, nullptr);
+ }
+}
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 72afa69..6d87594 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -394,7 +394,7 @@
std::string fstab_contents = R"fs(
source none0 swap defaults encryptable,forceencrypt,fileencryption,forcefdeorfbe,keydirectory,length,swapprio,zramsize,max_comp_streams,reservedsize,eraseblk,logicalblk,sysfs_path,zram_loopback_path,zram_loopback_size,zram_backing_dev_path
-source none1 swap defaults encryptable=,forceencrypt=,fileencryption=,keydirectory=,length=,swapprio=,zramsize=,max_comp_streams=,verify=,avb=,reservedsize=,eraseblk=,logicalblk=,sysfs_path=,zram_loopback_path=,zram_loopback_size=,zram_backing_dev_path=
+source none1 swap defaults encryptable=,forceencrypt=,fileencryption=,keydirectory=,length=,swapprio=,zramsize=,max_comp_streams=,avb=,reservedsize=,eraseblk=,logicalblk=,sysfs_path=,zram_loopback_path=,zram_loopback_size=,zram_backing_dev_path=
source none2 swap defaults forcefdeorfbe=
@@ -413,7 +413,6 @@
}
EXPECT_EQ("", entry->key_loc);
EXPECT_EQ("", entry->key_dir);
- EXPECT_EQ("", entry->verity_loc);
EXPECT_EQ(0, entry->length);
EXPECT_EQ("", entry->label);
EXPECT_EQ(-1, entry->partnum);
@@ -437,13 +436,11 @@
flags.crypt = true;
flags.force_crypt = true;
flags.file_encryption = true;
- flags.verify = true;
flags.avb = true;
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
}
EXPECT_EQ("", entry->key_loc);
EXPECT_EQ("", entry->key_dir);
- EXPECT_EQ("", entry->verity_loc);
EXPECT_EQ(0, entry->length);
EXPECT_EQ("", entry->label);
EXPECT_EQ(-1, entry->partnum);
@@ -639,29 +636,6 @@
EXPECT_EQ(0, entry->zram_size);
}
-TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Verify) {
- TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
- std::string fstab_contents = R"fs(
-source none0 swap defaults verify=/dir/key
-)fs";
-
- ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
-
- Fstab fstab;
- EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_EQ(1U, fstab.size());
-
- auto entry = fstab.begin();
- EXPECT_EQ("none0", entry->mount_point);
-
- FstabEntry::FsMgrFlags flags = {};
- flags.verify = true;
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
-
- EXPECT_EQ("/dir/key", entry->verity_loc);
-}
-
TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_ForceEncrypt) {
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 9309aad..7e6ad5b 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -38,15 +38,7 @@
#include <vector>
using namespace std::literals::string_literals;
-
-using DeviceMapper = ::android::dm::DeviceMapper;
-using DmTable = ::android::dm::DmTable;
-using DmTarget = ::android::dm::DmTarget;
-using DmTargetLinear = ::android::dm::DmTargetLinear;
-using DmTargetZero = ::android::dm::DmTargetZero;
-using DmTargetAndroidVerity = ::android::dm::DmTargetAndroidVerity;
-using DmTargetBow = ::android::dm::DmTargetBow;
-using DmTargetTypeInfo = ::android::dm::DmTargetTypeInfo;
+using namespace android::dm;
using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
static int Usage(void) {
@@ -57,6 +49,7 @@
std::cerr << " delete <dm-name>" << std::endl;
std::cerr << " list <devices | targets> [-v]" << std::endl;
std::cerr << " getpath <dm-name>" << std::endl;
+ std::cerr << " status <dm-name>" << std::endl;
std::cerr << " table <dm-name>" << std::endl;
std::cerr << " help" << std::endl;
std::cerr << std::endl;
@@ -122,6 +115,62 @@
}
std::string block_device = NextArg();
return std::make_unique<DmTargetBow>(start_sector, num_sectors, block_device);
+ } else if (target_type == "snapshot-origin") {
+ if (!HasArgs(1)) {
+ std::cerr << "Expected \"snapshot-origin\" <block_device>" << std::endl;
+ return nullptr;
+ }
+ std::string block_device = NextArg();
+ return std::make_unique<DmTargetSnapshotOrigin>(start_sector, num_sectors,
+ block_device);
+ } else if (target_type == "snapshot") {
+ if (!HasArgs(4)) {
+ std::cerr
+ << "Expected \"snapshot\" <block_device> <block_device> <mode> <chunk_size>"
+ << std::endl;
+ return nullptr;
+ }
+ std::string base_device = NextArg();
+ std::string cow_device = NextArg();
+ std::string mode_str = NextArg();
+ std::string chunk_size_str = NextArg();
+
+ SnapshotStorageMode mode;
+ if (mode_str == "P") {
+ mode = SnapshotStorageMode::Persistent;
+ } else if (mode_str == "N") {
+ mode = SnapshotStorageMode::Transient;
+ } else {
+ std::cerr << "Unrecognized mode: " << mode_str << "\n";
+ return nullptr;
+ }
+
+ uint32_t chunk_size;
+ if (!android::base::ParseUint(chunk_size_str, &chunk_size)) {
+ std::cerr << "Chunk size must be an unsigned integer.\n";
+ return nullptr;
+ }
+ return std::make_unique<DmTargetSnapshot>(start_sector, num_sectors, base_device,
+ cow_device, mode, chunk_size);
+ } else if (target_type == "snapshot-merge") {
+ if (!HasArgs(3)) {
+ std::cerr
+ << "Expected \"snapshot-merge\" <block_device> <block_device> <chunk_size>"
+ << std::endl;
+ return nullptr;
+ }
+ std::string base_device = NextArg();
+ std::string cow_device = NextArg();
+ std::string chunk_size_str = NextArg();
+ SnapshotStorageMode mode = SnapshotStorageMode::Merge;
+
+ uint32_t chunk_size;
+ if (!android::base::ParseUint(chunk_size_str, &chunk_size)) {
+ std::cerr << "Chunk size must be an unsigned integer.\n";
+ return nullptr;
+ }
+ return std::make_unique<DmTargetSnapshot>(start_sector, num_sectors, base_device,
+ cow_device, mode, chunk_size);
} else {
std::cerr << "Unrecognized target type: " << target_type << std::endl;
return nullptr;
@@ -308,7 +357,7 @@
return 0;
}
-static int TableCmdHandler(int argc, char** argv) {
+static int DumpTable(const std::string& mode, int argc, char** argv) {
if (argc != 1) {
std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
return -EINVAL;
@@ -316,9 +365,18 @@
DeviceMapper& dm = DeviceMapper::Instance();
std::vector<DeviceMapper::TargetInfo> table;
- if (!dm.GetTableInfo(argv[0], &table)) {
- std::cerr << "Could not query table status of device \"" << argv[0] << "\"." << std::endl;
- return -EINVAL;
+ if (mode == "status") {
+ if (!dm.GetTableStatus(argv[0], &table)) {
+ std::cerr << "Could not query table status of device \"" << argv[0] << "\"."
+ << std::endl;
+ return -EINVAL;
+ }
+ } else if (mode == "table") {
+ if (!dm.GetTableInfo(argv[0], &table)) {
+ std::cerr << "Could not query table status of device \"" << argv[0] << "\"."
+ << std::endl;
+ return -EINVAL;
+ }
}
std::cout << "Targets in the device-mapper table for " << argv[0] << ":" << std::endl;
for (const auto& target : table) {
@@ -333,6 +391,14 @@
return 0;
}
+static int TableCmdHandler(int argc, char** argv) {
+ return DumpTable("table", argc, argv);
+}
+
+static int StatusCmdHandler(int argc, char** argv) {
+ return DumpTable("status", argc, argv);
+}
+
static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
// clang-format off
{"create", DmCreateCmdHandler},
@@ -341,6 +407,7 @@
{"help", HelpCmdHandler},
{"getpath", GetPathCmdHandler},
{"table", TableCmdHandler},
+ {"status", StatusCmdHandler},
// clang-format on
};
diff --git a/init/README.md b/init/README.md
index 28a106a..79c6237 100644
--- a/init/README.md
+++ b/init/README.md
@@ -412,6 +412,10 @@
not already running. See the start entry for more information on
starting services.
+`class_start_post_data <serviceclass>`
+> Like `class_start`, but only considers services that were started
+ after /data was mounted. Only used for FDE devices.
+
`class_stop <serviceclass>`
> Stop and disable all services of the specified class if they are
currently running.
@@ -421,6 +425,10 @@
currently running, without disabling them. They can be restarted
later using `class_start`.
+`class_reset_post_data <serviceclass>`
+> Like `class_reset`, but only considers services that were started
+ after /data was mounted. Only used for FDE devices.
+
`class_restart <serviceclass>`
> Restarts all services of the specified class.
@@ -494,6 +502,10 @@
`write` command to write to `/proc/sys/kernel/printk` to change that.
Properties are expanded within _level_.
+`mark_post_data`
+> Used to mark the point right after /data is mounted. Used to implement the
+ `class_reset_post_data` and `class_start_post_data` commands.
+
`mkdir <path> [mode] [owner] [group]`
> Create a directory at _path_, optionally with the given mode, owner, and
group. If not provided, the directory is created with permissions 755 and
@@ -586,9 +598,6 @@
`umount <path>`
> Unmount the filesystem mounted at that path.
-`verity_load_state`
-> Internal implementation detail used to load dm-verity state.
-
`verity_update_state <mount-point>`
> Internal implementation detail used to update dm-verity state and
set the partition._mount-point_.verified properties used by adb remount
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 06da4be..ba1c94d 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -104,23 +104,37 @@
}
}
-static Result<Success> do_class_start(const BuiltinArguments& args) {
+static Result<Success> class_start(const std::string& class_name, bool post_data_only) {
// Do not start a class if it has a property persist.dont_start_class.CLASS set to 1.
- if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
+ if (android::base::GetBoolProperty("persist.init.dont_start_class." + class_name, false))
return Success();
// Starting a class does not start services which are explicitly disabled.
// They must be started individually.
for (const auto& service : ServiceList::GetInstance()) {
- if (service->classnames().count(args[1])) {
+ if (service->classnames().count(class_name)) {
+ if (post_data_only && !service->is_post_data()) {
+ continue;
+ }
if (auto result = service->StartIfNotDisabled(); !result) {
LOG(ERROR) << "Could not start service '" << service->name()
- << "' as part of class '" << args[1] << "': " << result.error();
+ << "' as part of class '" << class_name << "': " << result.error();
}
}
}
return Success();
}
+static Result<Success> do_class_start(const BuiltinArguments& args) {
+ return class_start(args[1], false /* post_data_only */);
+}
+
+static Result<Success> do_class_start_post_data(const BuiltinArguments& args) {
+ if (args.context != kInitContext) {
+ return Error() << "command 'class_start_post_data' only available in init context";
+ }
+ return class_start(args[1], true /* post_data_only */);
+}
+
static Result<Success> do_class_stop(const BuiltinArguments& args) {
ForEachServiceInClass(args[1], &Service::Stop);
return Success();
@@ -131,6 +145,14 @@
return Success();
}
+static Result<Success> do_class_reset_post_data(const BuiltinArguments& args) {
+ if (args.context != kInitContext) {
+ return Error() << "command 'class_reset_post_data' only available in init context";
+ }
+ ForEachServiceInClass(args[1], &Service::ResetIfPostData);
+ return Success();
+}
+
static Result<Success> do_class_restart(const BuiltinArguments& args) {
// Do not restart a class if it has a property persist.dont_start_class.CLASS set to 1.
if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
@@ -707,17 +729,6 @@
return Success();
}
-static Result<Success> do_verity_load_state(const BuiltinArguments& args) {
- int mode = -1;
- bool loaded = fs_mgr_load_verity_state(&mode);
- if (loaded && mode != VERITY_MODE_DEFAULT) {
- ActionManager::GetInstance().QueueEventTrigger("verity-logging");
- }
- if (!loaded) return Error() << "Could not load verity state";
-
- return Success();
-}
-
static Result<Success> do_verity_update_state(const BuiltinArguments& args) {
int mode;
if (!fs_mgr_load_verity_state(&mode)) {
@@ -1053,6 +1064,12 @@
{{"exec", "/system/bin/vdc", "--wait", "cryptfs", "init_user0"}, args.context});
}
+static Result<Success> do_mark_post_data(const BuiltinArguments& args) {
+ ServiceList::GetInstance().MarkPostData();
+
+ return Success();
+}
+
static Result<Success> do_parse_apex_configs(const BuiltinArguments& args) {
glob_t glob_result;
// @ is added to filter out the later paths, which are bind mounts of the places
@@ -1104,8 +1121,10 @@
{"chmod", {2, 2, {true, do_chmod}}},
{"chown", {2, 3, {true, do_chown}}},
{"class_reset", {1, 1, {false, do_class_reset}}},
+ {"class_reset_post_data", {1, 1, {false, do_class_reset_post_data}}},
{"class_restart", {1, 1, {false, do_class_restart}}},
{"class_start", {1, 1, {false, do_class_start}}},
+ {"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}}},
{"domainname", {1, 1, {true, do_domainname}}},
@@ -1125,6 +1144,7 @@
{"load_persist_props", {0, 0, {false, do_load_persist_props}}},
{"load_system_props", {0, 0, {false, do_load_system_props}}},
{"loglevel", {1, 1, {false, do_loglevel}}},
+ {"mark_post_data", {0, 0, {false, do_mark_post_data}}},
{"mkdir", {1, 4, {true, do_mkdir}}},
// TODO: Do mount operations in vendor_init.
// mount_all is currently too complex to run in vendor_init as it queues action triggers,
@@ -1150,7 +1170,6 @@
{"symlink", {2, 2, {true, do_symlink}}},
{"sysclktz", {1, 1, {false, do_sysclktz}}},
{"trigger", {1, 1, {false, do_trigger}}},
- {"verity_load_state", {0, 0, {false, do_verity_load_state}}},
{"verity_update_state", {0, 0, {false, do_verity_update_state}}},
{"wait", {1, 2, {true, do_wait}}},
{"wait_for_prop", {2, 2, {false, do_wait_for_prop}}},
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 3e76556..85fa874 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -644,7 +644,6 @@
}
bool FirstStageMountVBootV1::GetDmVerityDevices() {
- std::string verity_loc_device;
need_dm_verity_ = false;
for (const auto& fstab_entry : fstab_) {
@@ -657,21 +656,9 @@
if (fstab_entry.fs_mgr_flags.verify) {
need_dm_verity_ = true;
}
- // Checks if verity metadata is on a separate partition. Note that it is
- // not partition specific, so there must be only one additional partition
- // that carries verity state.
- if (!fstab_entry.verity_loc.empty()) {
- if (verity_loc_device.empty()) {
- verity_loc_device = fstab_entry.verity_loc;
- } else if (verity_loc_device != fstab_entry.verity_loc) {
- LOG(ERROR) << "More than one verity_loc found: " << verity_loc_device << ", "
- << fstab_entry.verity_loc;
- return false;
- }
- }
}
- // Includes the partition names of fstab records and verity_loc_device (if any).
+ // Includes the partition names of fstab records.
// Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used.
for (const auto& fstab_entry : fstab_) {
if (!fstab_entry.fs_mgr_flags.logical) {
@@ -679,10 +666,6 @@
}
}
- if (!verity_loc_device.empty()) {
- required_devices_partition_names_.emplace(basename(verity_loc_device.c_str()));
- }
-
return true;
}
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 132fc13..c49dc9f 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -331,6 +331,12 @@
}
std::string plat_mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
+ std::string plat_compat_cil_file("/system/etc/selinux/mapping/" + vend_plat_vers +
+ ".compat.cil");
+ if (access(plat_compat_cil_file.c_str(), F_OK) == -1) {
+ plat_compat_cil_file.clear();
+ }
+
std::string product_policy_cil_file("/product/etc/selinux/product_sepolicy.cil");
if (access(product_policy_cil_file.c_str(), F_OK) == -1) {
product_policy_cil_file.clear();
@@ -376,6 +382,9 @@
};
// clang-format on
+ if (!plat_compat_cil_file.empty()) {
+ compile_args.push_back(plat_compat_cil_file.c_str());
+ }
if (!product_policy_cil_file.empty()) {
compile_args.push_back(product_policy_cil_file.c_str());
}
diff --git a/init/service.cpp b/init/service.cpp
index 276c2aa..2f96681 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -362,7 +362,7 @@
// Oneshot processes go into the disabled state on exit,
// except when manually restarted.
- if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART)) {
+ if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART) && !(flags_ & SVC_RESET)) {
flags_ |= SVC_DISABLED;
}
@@ -951,6 +951,8 @@
pre_apexd_ = true;
}
+ post_data_ = ServiceList::GetInstance().IsPostData();
+
LOG(INFO) << "starting service '" << name_ << "'...";
pid_t pid = -1;
@@ -1150,6 +1152,12 @@
StopOrReset(SVC_RESET);
}
+void Service::ResetIfPostData() {
+ if (post_data_) {
+ StopOrReset(SVC_RESET);
+ }
+}
+
void Service::Stop() {
StopOrReset(SVC_DISABLED);
}
@@ -1343,6 +1351,14 @@
}
}
+void ServiceList::MarkPostData() {
+ post_data_ = true;
+}
+
+bool ServiceList::IsPostData() {
+ return post_data_;
+}
+
void ServiceList::MarkServicesUpdate() {
services_update_finished_ = true;
diff --git a/init/service.h b/init/service.h
index c42a5a3..dc2b128 100644
--- a/init/service.h
+++ b/init/service.h
@@ -81,6 +81,7 @@
Result<Success> StartIfNotDisabled();
Result<Success> Enable();
void Reset();
+ void ResetIfPostData();
void Stop();
void Terminate();
void Timeout();
@@ -124,6 +125,7 @@
std::optional<std::chrono::seconds> timeout_period() const { return timeout_period_; }
const std::vector<std::string>& args() const { return args_; }
bool is_updatable() const { return updatable_; }
+ bool is_post_data() const { return post_data_; }
private:
using OptionParser = Result<Success> (Service::*)(std::vector<std::string>&& args);
@@ -244,6 +246,8 @@
std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
bool pre_apexd_ = false;
+
+ bool post_data_ = false;
};
class ServiceList {
@@ -285,6 +289,8 @@
const std::vector<std::unique_ptr<Service>>& services() const { return services_; }
const std::vector<Service*> services_in_shutdown_order() const;
+ void MarkPostData();
+ bool IsPostData();
void MarkServicesUpdate();
bool IsServicesUpdated() const { return services_update_finished_; }
void DelayService(const Service& service);
@@ -292,6 +298,7 @@
private:
std::vector<std::unique_ptr<Service>> services_;
+ bool post_data_ = false;
bool services_update_finished_ = false;
std::vector<std::string> delayed_service_names_;
};
diff --git a/liblog/Android.bp b/liblog/Android.bp
index da475cb..53d3ab3 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -45,6 +45,7 @@
host_supported: true,
vendor_available: true,
recovery_available: true,
+ native_bridge_supported: true,
export_include_dirs: ["include"],
system_shared_libs: [],
stl: "none",
@@ -67,6 +68,7 @@
name: "liblog",
host_supported: true,
recovery_available: true,
+ native_bridge_supported: true,
srcs: liblog_sources,
target: {
@@ -138,6 +140,7 @@
llndk_library {
name: "liblog",
+ native_bridge_supported: true,
symbol_file: "liblog.map.txt",
export_include_dirs: ["include_vndk"],
}
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index da5d86c..ccda5d1 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -26,6 +26,19 @@
],
export_include_dirs: ["include"],
+
+ tidy: true,
+ tidy_checks: [
+ "-*",
+ "cert-*",
+ "clang-analyzer-security*",
+ "android-*",
+ ],
+ tidy_checks_as_errors: [
+ "cert-*",
+ "clang-analyzer-security*",
+ "android-*",
+ ],
}
cc_test {
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 9dc2699..8fe7854 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -39,9 +39,12 @@
const int LOCAL_QLOG_NL_EVENT = 112;
const int LOCAL_NFLOG_PACKET = NFNL_SUBSYS_ULOG << 8 | NFULNL_MSG_PACKET;
+#include <android-base/parseint.h>
#include <log/log.h>
#include <sysutils/NetlinkEvent.h>
+using android::base::ParseInt;
+
NetlinkEvent::NetlinkEvent() {
mAction = Action::kUnknown;
memset(mParams, 0, sizeof(mParams));
@@ -301,8 +304,9 @@
raw = (char*)nlAttrData(payload);
}
- char* hex = (char*) calloc(1, 5 + (len * 2));
- strcpy(hex, "HEX=");
+ size_t hexSize = 5 + (len * 2);
+ char* hex = (char*)calloc(1, hexSize);
+ strlcpy(hex, "HEX=", hexSize);
for (int i = 0; i < len; i++) {
hex[4 + (i * 2)] = "0123456789abcdef"[(raw[i] >> 4) & 0xf];
hex[5 + (i * 2)] = "0123456789abcdef"[raw[i] & 0xf];
@@ -474,23 +478,20 @@
struct nd_opt_rdnss *rndss_opt = (struct nd_opt_rdnss *) opthdr;
const uint32_t lifetime = ntohl(rndss_opt->nd_opt_rdnss_lifetime);
- // Construct "SERVERS=<comma-separated string of DNS addresses>".
- static const char kServerTag[] = "SERVERS=";
- static const size_t kTagLength = strlen(kServerTag);
+ // Construct a comma-separated string of DNS addresses.
// Reserve sufficient space for an IPv6 link-local address: all but the
// last address are followed by ','; the last is followed by '\0'.
static const size_t kMaxSingleAddressLength =
INET6_ADDRSTRLEN + strlen("%") + IFNAMSIZ + strlen(",");
- const size_t bufsize = kTagLength + numaddrs * kMaxSingleAddressLength;
+ const size_t bufsize = numaddrs * kMaxSingleAddressLength;
char *buf = (char *) malloc(bufsize);
if (!buf) {
SLOGE("RDNSS option: out of memory\n");
return false;
}
- strcpy(buf, kServerTag);
- size_t pos = kTagLength;
struct in6_addr *addrs = (struct in6_addr *) (rndss_opt + 1);
+ size_t pos = 0;
for (int i = 0; i < numaddrs; i++) {
if (i > 0) {
buf[pos++] = ',';
@@ -508,7 +509,8 @@
mSubsystem = strdup("net");
asprintf(&mParams[0], "INTERFACE=%s", ifname);
asprintf(&mParams[1], "LIFETIME=%u", lifetime);
- mParams[2] = buf;
+ asprintf(&mParams[2], "SERVERS=%s", buf);
+ free(buf);
} else if (opthdr->nd_opt_type == ND_OPT_DNSSL) {
// TODO: support DNSSL.
} else {
@@ -634,7 +636,9 @@
else if (!strcmp(a, "change"))
mAction = Action::kChange;
} else if ((a = HAS_CONST_PREFIX(s, end, "SEQNUM=")) != nullptr) {
- mSeq = atoi(a);
+ if (!ParseInt(a, &mSeq)) {
+ SLOGE("NetlinkEvent::parseAsciiNetlinkMessage: failed to parse SEQNUM=%s", a);
+ }
} else if ((a = HAS_CONST_PREFIX(s, end, "SUBSYSTEM=")) != nullptr) {
mSubsystem = strdup(a);
} else if (param_idx < NL_PARAMS_MAX) {
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 858c0bb..3843252 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -84,6 +84,7 @@
host_supported: true,
vendor_available: true,
recovery_available: true,
+ native_bridge_supported: true,
vndk: {
enabled: true,
},
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index ab38dfd..32c6ea8 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -25,6 +25,8 @@
#include <sys/cdefs.h>
#include <sys/types.h>
+#include <string_view>
+
#include "android-base/off64_t.h"
/* Zip compression methods we support */
@@ -39,10 +41,7 @@
ZipString() {}
- /*
- * entry_name has to be an c-style string with only ASCII characters.
- */
- explicit ZipString(const char* entry_name);
+ explicit ZipString(std::string_view entry_name);
bool operator==(const ZipString& rhs) const {
return name && (name_length == rhs.name_length) && (memcmp(name, rhs.name, name_length) == 0);
@@ -149,8 +148,7 @@
void CloseArchive(ZipArchiveHandle archive);
/*
- * Find an entry in the Zip archive, by name. |entryName| must be a null
- * terminated string, and |data| must point to a writeable memory location.
+ * Find an entry in the Zip archive, by name. |data| must be non-null.
*
* Returns 0 if an entry is found, and populates |data| with information
* about this entry. Returns negative values otherwise.
@@ -164,6 +162,8 @@
* On non-Windows platforms this method does not modify internal state and
* can be called concurrently.
*/
+int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName, ZipEntry* data);
+// TODO: remove this internally, where there is a new user.
int32_t FindEntry(const ZipArchiveHandle archive, const ZipString& entryName, ZipEntry* data);
/*
@@ -179,6 +179,7 @@
*
* Returns 0 on success and negative values on failure.
*/
+// TODO: switch these ZipStrings to std::string_view.
int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
const ZipString* optional_prefix, const ZipString* optional_suffix);
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 596786a..bc7103b 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -690,8 +690,7 @@
struct IterationHandle {
uint32_t position;
- // We're not using vector here because this code is used in the Windows SDK
- // where the STL is not available.
+ // TODO: switch these to std::string now that Windows uses libc++ too.
ZipString prefix;
ZipString suffix;
ZipArchive* archive;
@@ -742,6 +741,7 @@
delete reinterpret_cast<IterationHandle*>(cookie);
}
+// TODO: remove this internally.
int32_t FindEntry(const ZipArchiveHandle archive, const ZipString& entryName, ZipEntry* data) {
if (entryName.name_length == 0) {
ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name);
@@ -758,6 +758,23 @@
return FindEntry(archive, static_cast<uint32_t>(ent), data);
}
+int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName,
+ ZipEntry* data) {
+ if (entryName.empty() || entryName.size() > static_cast<size_t>(UINT16_MAX)) {
+ ALOGW("Zip: Invalid filename of length %zu", entryName.size());
+ return kInvalidEntryName;
+ }
+
+ const int64_t ent = EntryToIndex(archive->hash_table, archive->hash_table_size,
+ ZipString(entryName), archive->central_directory.GetBasePtr());
+ if (ent < 0) {
+ ALOGV("Zip: Could not find entry %.*s", static_cast<int>(entryName.size()), entryName.data());
+ return static_cast<int32_t>(ent); // kEntryNotFound is safe to truncate.
+ }
+ // We know there are at most hast_table_size entries, safe to truncate.
+ return FindEntry(archive, static_cast<uint32_t>(ent), data);
+}
+
int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
if (handle == NULL) {
@@ -1152,8 +1169,9 @@
return archive->mapped_zip.GetFileDescriptor();
}
-ZipString::ZipString(const char* entry_name) : name(reinterpret_cast<const uint8_t*>(entry_name)) {
- size_t len = strlen(entry_name);
+ZipString::ZipString(std::string_view entry_name)
+ : name(reinterpret_cast<const uint8_t*>(entry_name.data())) {
+ size_t len = entry_name.size();
CHECK_LE(len, static_cast<size_t>(UINT16_MAX));
name_length = static_cast<uint16_t>(len);
}
diff --git a/libziparchive/zip_archive_benchmark.cpp b/libziparchive/zip_archive_benchmark.cpp
index 46aa5a6..52166a4 100644
--- a/libziparchive/zip_archive_benchmark.cpp
+++ b/libziparchive/zip_archive_benchmark.cpp
@@ -55,7 +55,7 @@
// In order to walk through all file names in the archive, look for a name
// that does not exist in the archive.
- ZipString name("thisFileNameDoesNotExist");
+ std::string_view name("thisFileNameDoesNotExist");
// Start the benchmark.
while (state.KeepRunning()) {
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index e471d5e..cfbce1c 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -64,12 +64,6 @@
return OpenArchive(abs_path.c_str(), handle);
}
-static void SetZipString(ZipString* zip_str, const std::string& str) {
- zip_str->name = reinterpret_cast<const uint8_t*>(str.c_str());
- CHECK_LE(str.size(), std::numeric_limits<uint16_t>::max());
- zip_str->name_length = static_cast<uint16_t>(str.size());
-}
-
TEST(ziparchive, Open) {
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
@@ -192,9 +186,7 @@
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
ZipEntry data;
- ZipString name;
- SetZipString(&name, kATxtName);
- ASSERT_EQ(0, FindEntry(handle, name, &data));
+ ASSERT_EQ(0, FindEntry(handle, kATxtName, &data));
// Known facts about a.txt, from zipinfo -v.
ASSERT_EQ(63, data.offset);
@@ -205,9 +197,28 @@
ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
// An entry that doesn't exist. Should be a negative return code.
- ZipString absent_name;
- SetZipString(&absent_name, kNonexistentTxtName);
- ASSERT_LT(FindEntry(handle, absent_name, &data), 0);
+ ASSERT_LT(FindEntry(handle, kNonexistentTxtName, &data), 0);
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, FindEntry_empty) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ ZipEntry data;
+ ASSERT_EQ(kInvalidEntryName, FindEntry(handle, "", &data));
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, FindEntry_too_long) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ std::string very_long_name(65536, 'x');
+ ZipEntry data;
+ ASSERT_EQ(kInvalidEntryName, FindEntry(handle, very_long_name, &data));
CloseArchive(handle);
}
@@ -234,9 +245,7 @@
// An entry that's deflated.
ZipEntry data;
- ZipString a_name;
- SetZipString(&a_name, kATxtName);
- ASSERT_EQ(0, FindEntry(handle, a_name, &data));
+ ASSERT_EQ(0, FindEntry(handle, kATxtName, &data));
const uint32_t a_size = data.uncompressed_length;
ASSERT_EQ(a_size, kATxtContents.size());
uint8_t* buffer = new uint8_t[a_size];
@@ -245,9 +254,7 @@
delete[] buffer;
// An entry that's stored.
- ZipString b_name;
- SetZipString(&b_name, kBTxtName);
- ASSERT_EQ(0, FindEntry(handle, b_name, &data));
+ ASSERT_EQ(0, FindEntry(handle, kBTxtName, &data));
const uint32_t b_size = data.uncompressed_length;
ASSERT_EQ(b_size, kBTxtContents.size());
buffer = new uint8_t[b_size];
@@ -302,9 +309,7 @@
ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
ZipEntry entry;
- ZipString empty_name;
- SetZipString(&empty_name, kEmptyTxtName);
- ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
+ ASSERT_EQ(0, FindEntry(handle, kEmptyTxtName, &entry));
ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length);
uint8_t buffer[1];
ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
@@ -327,9 +332,7 @@
ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle, false));
ZipEntry entry;
- ZipString ab_name;
- SetZipString(&ab_name, kAbTxtName);
- ASSERT_EQ(0, FindEntry(handle, ab_name, &entry));
+ ASSERT_EQ(0, FindEntry(handle, kAbTxtName, &entry));
ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
// Extract the entry to memory.
@@ -386,9 +389,7 @@
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
ZipEntry entry;
- ZipString name;
- SetZipString(&name, kATxtName);
- ASSERT_EQ(0, FindEntry(handle, name, &entry));
+ ASSERT_EQ(0, FindEntry(handle, kATxtName, &entry));
ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
// Assert that the first 8 bytes of the file haven't been clobbered.
@@ -424,10 +425,8 @@
OpenArchiveFromMemory(file_map->data(), file_map->size(), zip_path.c_str(), &handle));
// Assert one entry can be found and extracted correctly.
- std::string BINARY_PATH("META-INF/com/google/android/update-binary");
- ZipString binary_path(BINARY_PATH.c_str());
ZipEntry binary_entry;
- ASSERT_EQ(0, FindEntry(handle, binary_path, &binary_entry));
+ ASSERT_EQ(0, FindEntry(handle, "META-INF/com/google/android/update-binary", &binary_entry));
TemporaryFile tmp_binary;
ASSERT_NE(-1, tmp_binary.fd);
ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd));
@@ -436,9 +435,7 @@
static void ZipArchiveStreamTest(ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
- ZipString name;
- SetZipString(&name, entry_name);
- ASSERT_EQ(0, FindEntry(handle, name, entry));
+ ASSERT_EQ(0, FindEntry(handle, entry_name, entry));
std::unique_ptr<ZipArchiveStreamEntry> stream;
if (raw) {
stream.reset(ZipArchiveStreamEntry::CreateRaw(handle, *entry));
@@ -589,11 +586,7 @@
// an entry whose name is "name" and whose size is 12 (contents =
// "abdcdefghijk").
ZipEntry entry;
- ZipString name;
- std::string name_str = "name";
- SetZipString(&name, name_str);
-
- ASSERT_EQ(0, FindEntry(handle, name, &entry));
+ ASSERT_EQ(0, FindEntry(handle, "name", &entry));
ASSERT_EQ(static_cast<uint32_t>(12), entry.uncompressed_length);
entry_out->resize(12);
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index 63adbbc..c3da23c 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -62,7 +62,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
EXPECT_EQ(kCompressStored, data.method);
EXPECT_EQ(0u, data.has_data_descriptor);
EXPECT_EQ(strlen(expected), data.compressed_length);
@@ -95,19 +95,19 @@
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
EXPECT_EQ(kCompressStored, data.method);
EXPECT_EQ(2u, data.compressed_length);
ASSERT_EQ(2u, data.uncompressed_length);
ASSERT_TRUE(AssertFileEntryContentsEq("he", handle, &data));
- ASSERT_EQ(0, FindEntry(handle, ZipString("file/file.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file/file.txt", &data));
EXPECT_EQ(kCompressStored, data.method);
EXPECT_EQ(3u, data.compressed_length);
ASSERT_EQ(3u, data.uncompressed_length);
ASSERT_TRUE(AssertFileEntryContentsEq("llo", handle, &data));
- ASSERT_EQ(0, FindEntry(handle, ZipString("file/file2.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file/file2.txt", &data));
EXPECT_EQ(kCompressStored, data.method);
EXPECT_EQ(0u, data.compressed_length);
EXPECT_EQ(0u, data.uncompressed_length);
@@ -129,7 +129,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
EXPECT_EQ(0, data.offset & 0x03);
CloseArchive(handle);
@@ -163,7 +163,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
EXPECT_EQ(0, data.offset & 0x03);
struct tm mod = data.GetModificationTime();
@@ -191,7 +191,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
EXPECT_EQ(0, data.offset & 0xfff);
CloseArchive(handle);
@@ -213,7 +213,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
EXPECT_EQ(0, data.offset & 0xfff);
struct tm mod = data.GetModificationTime();
@@ -241,7 +241,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
EXPECT_EQ(kCompressDeflated, data.method);
ASSERT_EQ(4u, data.uncompressed_length);
ASSERT_TRUE(AssertFileEntryContentsEq("helo", handle, &data));
@@ -273,7 +273,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
EXPECT_EQ(kCompressDeflated, data.method);
EXPECT_EQ(kBufSize, data.uncompressed_length);
@@ -340,12 +340,12 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("keep.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "keep.txt", &data));
ASSERT_TRUE(AssertFileEntryContentsEq(kKeepThis, handle, &data));
- ASSERT_NE(0, FindEntry(handle, ZipString("drop.txt"), &data));
+ ASSERT_NE(0, FindEntry(handle, "drop.txt", &data));
- ASSERT_EQ(0, FindEntry(handle, ZipString("replace.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "replace.txt", &data));
ASSERT_TRUE(AssertFileEntryContentsEq(kReplaceWithThis, handle, &data));
CloseArchive(handle);
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
index 5c57d69..ac802b5 100644
--- a/property_service/libpropertyinfoparser/Android.bp
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -3,6 +3,7 @@
host_supported: true,
vendor_available: true,
recovery_available: true,
+ native_bridge_supported: true,
srcs: ["property_info_parser.cpp"],
cpp_std: "experimental",
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 295e704..84fa46e 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -80,7 +80,9 @@
chmod 0664 /dev/stune/top-app/tasks
chmod 0664 /dev/stune/rt/tasks
- # Create blkio tuning nodes
+ # 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.
mkdir /dev/blkio/background
chown system system /dev/blkio
chown system system /dev/blkio/background
@@ -88,6 +90,10 @@
chown system system /dev/blkio/background/tasks
chmod 0664 /dev/blkio/tasks
chmod 0664 /dev/blkio/background/tasks
+ write /dev/blkio/blkio.weight 1000
+ write /dev/blkio/background/blkio.weight 500
+ write /dev/blkio/blkio.group_idle 0
+ write /dev/blkio/background/blkio.group_idle 0
restorecon_recursive /mnt
@@ -405,6 +411,8 @@
class_start early_hal
on post-fs-data
+ mark_post_data
+
# Start checkpoint before we touch data
start vold
exec - system system -- /system/bin/vdc checkpoint prepareCheckpoint
@@ -747,9 +755,6 @@
on charger
class_start charger
-on property:vold.decrypt=trigger_reset_main
- class_reset main
-
on property:vold.decrypt=trigger_load_persist_props
load_persist_props
start logd
@@ -767,6 +772,8 @@
on property:vold.decrypt=trigger_restart_framework
# A/B update verifier that marks a successful boot.
exec_start update_verifier
+ class_start_post_data hal
+ class_start_post_data core
class_start main
class_start late_start
setprop service.bootanim.exit 0
@@ -775,6 +782,8 @@
on property:vold.decrypt=trigger_shutdown_framework
class_reset late_start
class_reset main
+ class_reset_post_data core
+ class_reset_post_data hal
on property:sys.boot_completed=1
bootchart stop