Merge "liblp: Expand the metadata header for future use."
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 6465ffe..813a8a9 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -137,8 +137,8 @@
" run remote shell command (interactive shell if no command given)\n"
" -e: choose escape character, or \"none\"; default '~'\n"
" -n: don't read from stdin\n"
- " -T: disable PTY allocation\n"
- " -t: force PTY allocation\n"
+ " -T: disable pty allocation\n"
+ " -t: allocate a pty if on a tty (-tt: force pty allocation)\n"
" -x: disable remote exit codes and stdout/stderr separation\n"
" emu COMMAND run emulator console command\n"
"\n"
diff --git a/cli-test/.clang-format b/cli-test/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/cli-test/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/cli-test/Android.bp b/cli-test/Android.bp
new file mode 100644
index 0000000..37a1d1b
--- /dev/null
+++ b/cli-test/Android.bp
@@ -0,0 +1,7 @@
+cc_binary {
+ name: "cli-test",
+ host_supported: true,
+ srcs: ["cli-test.cpp"],
+ cflags: ["-Wall", "-Werror"],
+ shared_libs: ["libbase"],
+}
diff --git a/cli-test/README.md b/cli-test/README.md
new file mode 100644
index 0000000..643eb74
--- /dev/null
+++ b/cli-test/README.md
@@ -0,0 +1,90 @@
+# cli-test
+
+## What?
+
+`cli-test` makes integration testing of command-line tools easier.
+
+## Goals
+
+* Readable syntax. Common cases should be concise, and pretty much anyone
+ should be able to read tests even if they've never seen this tool before.
+
+* Minimal issues with quoting. The toybox tests -- being shell scripts --
+ quickly become a nightmare of quoting. Using a non ad hoc format (such as
+ JSON) would have introduced similar but different quoting issues. A custom
+ format, while annoying, side-steps this.
+
+* Sensible defaults. We expect your exit status to be 0 unless you say
+ otherwise. We expect nothing on stderr unless you say otherwise. And so on.
+
+* Convention over configuration. Related to sensible defaults, we don't let you
+ configure things that aren't absolutely necessary. So you can't keep your test
+ data anywhere except in the `files/` subdirectory of the directory containing
+ your test, for example.
+
+## Non Goals
+
+* Portability. Just being able to run on Linux (host and device) is sufficient
+ for our needs. macOS is probably easy enough if we ever need it, but Windows
+ probably doesn't make sense.
+
+## Syntax
+
+Any all-whitespace line, or line starting with `#` is ignored.
+
+A test looks like this:
+```
+name: unzip -l
+command: unzip -l $FILES/example.zip d1/d2/x.txt
+after: [ ! -f d1/d2/x.txt ]
+expected-stdout:
+ Archive: $FILES/example.zip
+ Length Date Time Name
+ --------- ---------- ----- ----
+ 1024 2017-06-04 08:45 d1/d2/x.txt
+ --------- -------
+ 1024 1 file
+---
+```
+
+The `name:` line names the test, and is only for human consumption.
+
+The `command:` line is the command to be run. Additional commands can be
+supplied as zero or more `before:` lines (run before `command:`) and zero or
+more `after:` lines (run after `command:`). These are useful for both
+setup/teardown but also for testing post conditions (as in the example above).
+
+Any `command:`, `before:`, or `after:` line is expected to exit with status 0.
+Anything else is considered a test failure.
+
+The `expected-stdout:` line is followed by zero or more tab-prefixed lines that
+are otherwise the exact output expected from the command. (There's magic behind
+the scenes to rewrite the test files directory to `$FILES` because otherwise any
+path in the output would depend on the temporary directory used to run the test.)
+
+There is currently no `expected-stderr:` line. Standard error is implicitly
+expected to be empty, and any output will cause a test failure. (The support is
+there, but not wired up because we haven't needed it yet.)
+
+The fields can appear in any order, but every test must contain at least a
+`name:` line and a `command:` line.
+
+## Output
+
+The output is intended to resemble gtest.
+
+## Future Directions
+
+* It's often useful to be able to *match* against stdout/stderr/a file rather
+ than give exact expected output. We might want to add explicit support for
+ this. In the meantime, it's possible to use an `after:` with `grep -q` if
+ you redirect in your `command:`.
+
+* In addition to using a `before:` (which will fail a test), it can be useful
+ to be able to specify tests that would cause us to *skip* a test. An example
+ would be "am I running as root?".
+
+* It might be useful to be able to make exit status assertions other than 0?
+
+* There's currently no way (other than the `files/` directory) to share repeated
+ setup between tests.
diff --git a/cli-test/cli-test.cpp b/cli-test/cli-test.cpp
new file mode 100644
index 0000000..d6e27ee
--- /dev/null
+++ b/cli-test/cli-test.cpp
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/test_utils.h>
+
+// Example:
+
+// name: unzip -n
+// before: mkdir -p d1/d2
+// before: echo b > d1/d2/a.txt
+// command: unzip -q -n $FILES/zip/example.zip d1/d2/a.txt && cat d1/d2/a.txt
+// expected-stdout:
+// b
+
+struct Test {
+ std::string test_filename;
+ std::string name;
+ std::string command;
+ std::vector<std::string> befores;
+ std::vector<std::string> afters;
+ std::string expected_stdout;
+ std::string expected_stderr;
+ int exit_status = 0;
+};
+
+static const char* g_progname;
+static bool g_verbose;
+
+static const char* g_file;
+static size_t g_line;
+
+enum Color { kRed, kGreen };
+
+static void Print(Color c, const char* lhs, const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ if (isatty(0)) printf("%s", (c == kRed) ? "\e[31m" : "\e[32m");
+ printf("%s%s", lhs, isatty(0) ? "\e[0m" : "");
+ vfprintf(stdout, fmt, ap);
+ putchar('\n');
+ va_end(ap);
+}
+
+static void Die(int error, const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ fprintf(stderr, "%s: ", g_progname);
+ vfprintf(stderr, fmt, ap);
+ if (error != 0) fprintf(stderr, ": %s", strerror(error));
+ fprintf(stderr, "\n");
+ va_end(ap);
+ _exit(1);
+}
+
+static void V(const char* fmt, ...) {
+ if (!g_verbose) return;
+
+ va_list ap;
+ va_start(ap, fmt);
+ fprintf(stderr, " - ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+}
+
+static void SetField(const char* what, std::string* field, std::string_view value) {
+ if (!field->empty()) {
+ Die(0, "%s:%zu: %s already set to '%s'", g_file, g_line, what, field->c_str());
+ }
+ field->assign(value);
+}
+
+// Similar to ConsumePrefix, but also trims, so "key:value" and "key: value"
+// are equivalent.
+static bool Match(std::string* s, const std::string& prefix) {
+ if (!android::base::StartsWith(*s, prefix)) return false;
+ s->assign(android::base::Trim(s->substr(prefix.length())));
+ return true;
+}
+
+static void CollectTests(std::vector<Test>* tests, const char* test_filename) {
+ std::string absolute_test_filename;
+ if (!android::base::Realpath(test_filename, &absolute_test_filename)) {
+ Die(errno, "realpath '%s'", test_filename);
+ }
+
+ std::string content;
+ if (!android::base::ReadFileToString(test_filename, &content)) {
+ Die(errno, "couldn't read '%s'", test_filename);
+ }
+
+ size_t count = 0;
+ g_file = test_filename;
+ g_line = 0;
+ auto lines = android::base::Split(content, "\n");
+ std::unique_ptr<Test> test(new Test);
+ while (g_line < lines.size()) {
+ auto line = lines[g_line++];
+ if (line.empty() || line[0] == '#') continue;
+
+ if (line[0] == '-') {
+ if (test->name.empty() || test->command.empty()) {
+ Die(0, "%s:%zu: each test requires both a name and a command", g_file, g_line);
+ }
+ test->test_filename = absolute_test_filename;
+ tests->push_back(*test.release());
+ test.reset(new Test);
+ ++count;
+ } else if (Match(&line, "name:")) {
+ SetField("name", &test->name, line);
+ } else if (Match(&line, "command:")) {
+ SetField("command", &test->command, line);
+ } else if (Match(&line, "before:")) {
+ test->befores.push_back(line);
+ } else if (Match(&line, "after:")) {
+ test->afters.push_back(line);
+ } else if (Match(&line, "expected-stdout:")) {
+ // Collect tab-indented lines.
+ std::string text;
+ while (g_line < lines.size() && !lines[g_line].empty() && lines[g_line][0] == '\t') {
+ text += lines[g_line++].substr(1) + "\n";
+ }
+ SetField("expected stdout", &test->expected_stdout, text);
+ } else {
+ Die(0, "%s:%zu: syntax error: \"%s\"", g_file, g_line, line.c_str());
+ }
+ }
+ if (count == 0) Die(0, "no tests found in '%s'", g_file);
+}
+
+static const char* Plural(size_t n) {
+ return (n == 1) ? "" : "s";
+}
+
+static std::string ExitStatusToString(int status) {
+ if (WIFSIGNALED(status)) {
+ return android::base::StringPrintf("was killed by signal %d (%s)", WTERMSIG(status),
+ strsignal(WTERMSIG(status)));
+ }
+ if (WIFSTOPPED(status)) {
+ return android::base::StringPrintf("was stopped by signal %d (%s)", WSTOPSIG(status),
+ strsignal(WSTOPSIG(status)));
+ }
+ return android::base::StringPrintf("exited with status %d", WEXITSTATUS(status));
+}
+
+static bool RunCommands(const char* what, const std::vector<std::string>& commands) {
+ bool result = true;
+ for (auto& command : commands) {
+ V("running %s \"%s\"", what, command.c_str());
+ int exit_status = system(command.c_str());
+ if (exit_status != 0) {
+ result = false;
+ fprintf(stderr, "Command (%s) \"%s\" %s\n", what, command.c_str(),
+ ExitStatusToString(exit_status).c_str());
+ }
+ }
+ return result;
+}
+
+static bool CheckOutput(const char* what, std::string actual_output,
+ const std::string& expected_output, const std::string& FILES) {
+ // Rewrite the output to reverse any expansion of $FILES.
+ actual_output = android::base::StringReplace(actual_output, FILES, "$FILES", true);
+
+ bool result = (actual_output == expected_output);
+ if (!result) {
+ fprintf(stderr, "Incorrect %s.\nExpected:\n%s\nActual:\n%s\n", what, expected_output.c_str(),
+ actual_output.c_str());
+ }
+ return result;
+}
+
+static int RunTests(const std::vector<Test>& tests) {
+ std::vector<std::string> failures;
+
+ Print(kGreen, "[==========]", " Running %zu tests.", tests.size());
+ android::base::Timer total_timer;
+ for (const auto& test : tests) {
+ bool failed = false;
+
+ Print(kGreen, "[ RUN ]", " %s", test.name.c_str());
+ android::base::Timer test_timer;
+
+ // Set $FILES for this test.
+ std::string FILES = android::base::Dirname(test.test_filename) + "/files";
+ V("setenv(\"FILES\", \"%s\")", FILES.c_str());
+ setenv("FILES", FILES.c_str(), 1);
+
+ // Make a safe space to run the test.
+ TemporaryDir td;
+ V("chdir(\"%s\")", td.path);
+ if (chdir(td.path)) Die(errno, "chdir(\"%s\")", td.path);
+
+ // Perform any setup specified for this test.
+ if (!RunCommands("before", test.befores)) failed = true;
+
+ if (!failed) {
+ V("running command \"%s\"", test.command.c_str());
+ CapturedStdout test_stdout;
+ CapturedStderr test_stderr;
+ int exit_status = system(test.command.c_str());
+ test_stdout.Stop();
+ test_stderr.Stop();
+
+ V("exit status %d", exit_status);
+ if (exit_status != test.exit_status) {
+ failed = true;
+ fprintf(stderr, "Incorrect exit status: expected %d but %s\n", test.exit_status,
+ ExitStatusToString(exit_status).c_str());
+ }
+
+ if (!CheckOutput("stdout", test_stdout.str(), test.expected_stdout, FILES)) failed = true;
+ if (!CheckOutput("stderr", test_stderr.str(), test.expected_stderr, FILES)) failed = true;
+
+ if (!RunCommands("after", test.afters)) failed = true;
+ }
+
+ std::stringstream duration;
+ duration << test_timer;
+ if (failed) {
+ failures.push_back(test.name);
+ Print(kRed, "[ FAILED ]", " %s (%s)", test.name.c_str(), duration.str().c_str());
+ } else {
+ Print(kGreen, "[ OK ]", " %s (%s)", test.name.c_str(), duration.str().c_str());
+ }
+ }
+
+ // Summarize the whole run and explicitly list all the failures.
+
+ std::stringstream duration;
+ duration << total_timer;
+ Print(kGreen, "[==========]", " %zu tests ran. (%s total)", tests.size(), duration.str().c_str());
+
+ size_t fail_count = failures.size();
+ size_t pass_count = tests.size() - fail_count;
+ Print(kGreen, "[ PASSED ]", " %zu test%s.", pass_count, Plural(pass_count));
+ if (!failures.empty()) {
+ Print(kRed, "[ FAILED ]", " %zu test%s.", fail_count, Plural(fail_count));
+ for (auto& failure : failures) {
+ Print(kRed, "[ FAILED ]", " %s", failure.c_str());
+ }
+ }
+ return (fail_count == 0) ? 0 : 1;
+}
+
+static void ShowHelp(bool full) {
+ fprintf(full ? stdout : stderr, "usage: %s [-v] FILE...\n", g_progname);
+ if (!full) exit(EXIT_FAILURE);
+
+ printf(
+ "\n"
+ "Run tests.\n"
+ "\n"
+ "-v\tVerbose (show workings)\n");
+ exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char* argv[]) {
+ g_progname = basename(argv[0]);
+
+ static const struct option opts[] = {
+ {"help", no_argument, 0, 'h'},
+ {"verbose", no_argument, 0, 'v'},
+ {},
+ };
+
+ int opt;
+ while ((opt = getopt_long(argc, argv, "hv", opts, nullptr)) != -1) {
+ switch (opt) {
+ case 'h':
+ ShowHelp(true);
+ break;
+ case 'v':
+ g_verbose = true;
+ break;
+ default:
+ ShowHelp(false);
+ break;
+ }
+ }
+
+ argv += optind;
+ if (!*argv) Die(0, "no test files provided");
+ std::vector<Test> tests;
+ for (; *argv; ++argv) CollectTests(&tests, *argv);
+ return RunTests(tests);
+}
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 809318c..254fbed 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -575,5 +575,9 @@
return "/dev/block/" + sub_device_name;
}
+bool DeviceMapper::TargetInfo::IsOverflowSnapshot() const {
+ return spec.target_type == "snapshot"s && data == "Overflow"s;
+}
+
} // namespace dm
} // namespace android
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 418210c..abe9c4c 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -205,6 +205,8 @@
TargetInfo() {}
TargetInfo(const struct dm_target_spec& spec, const std::string& data)
: spec(spec), data(data) {}
+
+ bool IsOverflowSnapshot() const;
};
bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table);
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 7450d19..5738b96 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -155,6 +155,7 @@
// Mark snapshot writes as having completed. After this, new snapshots cannot
// be created, and the device must either cancel the OTA (either before
// rebooting or after rolling back), or merge the OTA.
+ // Before calling this function, all snapshots must be mapped.
bool FinishedSnapshotWrites();
private:
@@ -490,6 +491,11 @@
// This should only be called in recovery.
bool UnmapAllPartitions();
+ // Sanity check no snapshot overflows. Note that this returns false negatives if the snapshot
+ // overflows, then is remapped and not written afterwards. Hence, the function may only serve
+ // as a sanity check.
+ bool EnsureNoOverflowSnapshot(LockedFile* lock);
+
std::string gsid_dir_;
std::string metadata_dir_;
std::unique_ptr<IDeviceInfo> device_;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 830495c..f38db43 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -214,6 +214,11 @@
return false;
}
+ if (!EnsureNoOverflowSnapshot(lock.get())) {
+ LOG(ERROR) << "Cannot ensure there are no overflow snapshots.";
+ return false;
+ }
+
// This file acts as both a quick indicator for init (it can use access(2)
// to decide how to do first-stage mounts), and it stores the old slot, so
// we can tell whether or not we performed a rollback.
@@ -2303,5 +2308,36 @@
return true;
}
+bool SnapshotManager::EnsureNoOverflowSnapshot(LockedFile* lock) {
+ CHECK(lock);
+
+ std::vector<std::string> snapshots;
+ if (!ListSnapshots(lock, &snapshots)) {
+ LOG(ERROR) << "Could not list snapshots.";
+ return false;
+ }
+
+ auto& dm = DeviceMapper::Instance();
+ for (const auto& snapshot : snapshots) {
+ std::vector<DeviceMapper::TargetInfo> targets;
+ if (!dm.GetTableStatus(snapshot, &targets)) {
+ LOG(ERROR) << "Could not read snapshot device table: " << snapshot;
+ return false;
+ }
+ if (targets.size() != 1) {
+ LOG(ERROR) << "Unexpected device-mapper table for snapshot: " << snapshot
+ << ", size = " << targets.size();
+ return false;
+ }
+ if (targets[0].IsOverflowSnapshot()) {
+ LOG(ERROR) << "Detected overflow in snapshot " << snapshot
+ << ", CoW device size computation is wrong!";
+ return false;
+ }
+ }
+
+ return true;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index ff943f2..964b21a 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -273,6 +273,61 @@
return AssertionSuccess();
}
+ // Prepare A/B slot for a partition named "test_partition".
+ AssertionResult PrepareOneSnapshot(uint64_t device_size,
+ std::string* out_snap_device = nullptr) {
+ std::string base_device, cow_device, snap_device;
+ if (!CreatePartition("test_partition_a", device_size)) {
+ return AssertionFailure();
+ }
+ if (!MapUpdatePartitions()) {
+ return AssertionFailure();
+ }
+ if (!dm_.GetDmDevicePathByName("test_partition_b-base", &base_device)) {
+ return AssertionFailure();
+ }
+ SnapshotStatus status;
+ status.set_name("test_partition_b");
+ status.set_device_size(device_size);
+ status.set_snapshot_size(device_size);
+ status.set_cow_file_size(device_size);
+ if (!sm->CreateSnapshot(lock_.get(), &status)) {
+ return AssertionFailure();
+ }
+ if (!CreateCowImage("test_partition_b")) {
+ return AssertionFailure();
+ }
+ if (!MapCowImage("test_partition_b", 10s, &cow_device)) {
+ return AssertionFailure();
+ }
+ if (!sm->MapSnapshot(lock_.get(), "test_partition_b", base_device, cow_device, 10s,
+ &snap_device)) {
+ return AssertionFailure();
+ }
+ if (out_snap_device) {
+ *out_snap_device = std::move(snap_device);
+ }
+ return AssertionSuccess();
+ }
+
+ // Simulate a reboot into the new slot.
+ AssertionResult SimulateReboot() {
+ lock_ = nullptr;
+ if (!sm->FinishedSnapshotWrites()) {
+ return AssertionFailure();
+ }
+ if (!dm_.DeleteDevice("test_partition_b")) {
+ return AssertionFailure();
+ }
+ if (!DestroyLogicalPartition("test_partition_b-base")) {
+ return AssertionFailure();
+ }
+ if (!sm->UnmapCowImage("test_partition_b")) {
+ return AssertionFailure();
+ }
+ return AssertionSuccess();
+ }
+
DeviceMapper& dm_;
std::unique_ptr<SnapshotManager::LockedFile> lock_;
android::fiemap::IImageManager* image_manager_ = nullptr;
@@ -389,21 +444,8 @@
ASSERT_TRUE(AcquireLock());
static const uint64_t kDeviceSize = 1024 * 1024;
-
- std::string base_device, cow_device, snap_device;
- ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
- ASSERT_TRUE(MapUpdatePartitions());
- ASSERT_TRUE(dm_.GetDmDevicePathByName("test_partition_b-base", &base_device));
- SnapshotStatus status;
- status.set_name("test_partition_b");
- status.set_device_size(kDeviceSize);
- status.set_snapshot_size(kDeviceSize);
- status.set_cow_file_size(kDeviceSize);
- ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &status));
- ASSERT_TRUE(CreateCowImage("test_partition_b"));
- ASSERT_TRUE(MapCowImage("test_partition_b", 10s, &cow_device));
- ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test_partition_b", base_device, cow_device, 10s,
- &snap_device));
+ std::string snap_device;
+ ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize, &snap_device));
std::string test_string = "This is a test string.";
{
@@ -455,21 +497,8 @@
ASSERT_TRUE(AcquireLock());
static const uint64_t kDeviceSize = 1024 * 1024;
-
- ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
- ASSERT_TRUE(MapUpdatePartitions());
- SnapshotStatus status;
- status.set_name("test_partition_b");
- status.set_device_size(kDeviceSize);
- status.set_snapshot_size(kDeviceSize);
- status.set_cow_file_size(kDeviceSize);
- ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &status));
- ASSERT_TRUE(CreateCowImage("test_partition_b"));
-
- // Simulate a reboot into the new slot.
- lock_ = nullptr;
- ASSERT_TRUE(sm->FinishedSnapshotWrites());
- ASSERT_TRUE(DestroyLogicalPartition("test_partition_b-base"));
+ ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize));
+ ASSERT_TRUE(SimulateReboot());
auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
ASSERT_NE(init, nullptr);
@@ -479,6 +508,7 @@
ASSERT_TRUE(AcquireLock());
// Validate that we have a snapshot device.
+ SnapshotStatus status;
ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status));
ASSERT_EQ(status.state(), SnapshotState::CREATED);
@@ -492,21 +522,8 @@
ASSERT_TRUE(AcquireLock());
static const uint64_t kDeviceSize = 1024 * 1024;
-
- ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
- ASSERT_TRUE(MapUpdatePartitions());
- SnapshotStatus status;
- status.set_name("test_partition_b");
- status.set_device_size(kDeviceSize);
- status.set_snapshot_size(kDeviceSize);
- status.set_cow_file_size(kDeviceSize);
- ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &status));
- ASSERT_TRUE(CreateCowImage("test_partition_b"));
-
- // Simulate a reboot into the new slot.
- lock_ = nullptr;
- ASSERT_TRUE(sm->FinishedSnapshotWrites());
- ASSERT_TRUE(DestroyLogicalPartition("test_partition_b-base"));
+ ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize));
+ ASSERT_TRUE(SimulateReboot());
// Reflash the super partition.
FormatFakeSuper();
@@ -519,6 +536,7 @@
ASSERT_TRUE(AcquireLock());
+ SnapshotStatus status;
ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status));
// We should not get a snapshot device now.
@@ -535,21 +553,8 @@
ASSERT_TRUE(AcquireLock());
static const uint64_t kDeviceSize = 1024 * 1024;
-
- ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
- ASSERT_TRUE(MapUpdatePartitions());
- SnapshotStatus status;
- status.set_name("test_partition_b");
- status.set_device_size(kDeviceSize);
- status.set_snapshot_size(kDeviceSize);
- status.set_cow_file_size(kDeviceSize);
- ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &status));
- ASSERT_TRUE(CreateCowImage("test_partition_b"));
-
- // Simulate a reboot into the new slot.
- lock_ = nullptr;
- ASSERT_TRUE(sm->FinishedSnapshotWrites());
- ASSERT_TRUE(DestroyLogicalPartition("test_partition_b-base"));
+ ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize));
+ ASSERT_TRUE(SimulateReboot());
auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
ASSERT_NE(init, nullptr);
@@ -905,6 +910,17 @@
<< ", hash: " << hashes_[name];
}
+ AssertionResult MapUpdateSnapshots(const std::vector<std::string>& names = {"sys_b", "vnd_b",
+ "prd_b"}) {
+ for (const auto& name : names) {
+ auto res = MapUpdateSnapshot(name);
+ if (!res) {
+ return res;
+ }
+ }
+ return AssertionSuccess();
+ }
+
std::unique_ptr<TestPartitionOpener> opener_;
DeltaArchiveManifest manifest_;
std::unique_ptr<MetadataBuilder> src_;
@@ -1064,9 +1080,7 @@
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
// Check that target partitions can be mapped.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- EXPECT_TRUE(MapUpdateSnapshot(name));
- }
+ EXPECT_TRUE(MapUpdateSnapshots());
}
// Test that the old partitions are not modified.
@@ -1142,6 +1156,7 @@
// Execute the first update.
ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+ ASSERT_TRUE(MapUpdateSnapshots());
ASSERT_TRUE(sm->FinishedSnapshotWrites());
// Simulate shutting down the device.
@@ -1277,6 +1292,7 @@
// Execute the update.
ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+ ASSERT_TRUE(MapUpdateSnapshots());
ASSERT_TRUE(sm->FinishedSnapshotWrites());
// Simulate shutting down the device.
@@ -1379,6 +1395,7 @@
// Execute the first update.
ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+ ASSERT_TRUE(MapUpdateSnapshots());
ASSERT_TRUE(sm->FinishedSnapshotWrites());
// Simulate shutting down the device.
@@ -1410,6 +1427,7 @@
// Execute the first update.
ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+ ASSERT_TRUE(MapUpdateSnapshots());
ASSERT_TRUE(sm->FinishedSnapshotWrites());
// Simulate shutting down the device.
@@ -1434,6 +1452,7 @@
// Execute the first update.
ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+ ASSERT_TRUE(MapUpdateSnapshots());
ASSERT_TRUE(sm->FinishedSnapshotWrites());
// Simulate shutting down the device.
@@ -1480,7 +1499,8 @@
ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
- // Write some data to target partition.
+ // Map and write some data to target partition.
+ ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"}));
ASSERT_TRUE(WriteSnapshotAndHash("sys_b", partition_size));
// Finish update.
@@ -1500,6 +1520,32 @@
ASSERT_TRUE(IsPartitionUnchanged("sys_b"));
}
+// Test for overflow bit after update
+TEST_F(SnapshotUpdateTest, Overflow) {
+ const auto actual_write_size = GetSize(sys_);
+ const auto declared_write_size = actual_write_size - 1_MiB;
+
+ auto e = sys_->add_operations()->add_dst_extents();
+ e->set_start_block(0);
+ e->set_num_blocks(declared_write_size / manifest_.block_size());
+
+ // Execute the update.
+ ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+ // Map and write some data to target partitions.
+ ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"}));
+ ASSERT_TRUE(WriteSnapshotAndHash("sys_b", actual_write_size));
+
+ std::vector<android::dm::DeviceMapper::TargetInfo> table;
+ ASSERT_TRUE(DeviceMapper::Instance().GetTableStatus("sys_b", &table));
+ ASSERT_EQ(1u, table.size());
+ EXPECT_TRUE(table[0].IsOverflowSnapshot());
+
+ ASSERT_FALSE(sm->FinishedSnapshotWrites())
+ << "FinishedSnapshotWrites should detect overflow of CoW device.";
+}
+
class FlashAfterUpdateTest : public SnapshotUpdateTest,
public WithParamInterface<std::tuple<uint32_t, bool>> {
public:
@@ -1524,7 +1570,7 @@
// Execute the update.
ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
+ ASSERT_TRUE(MapUpdateSnapshots());
ASSERT_TRUE(sm->FinishedSnapshotWrites());
// Simulate shutting down the device.
diff --git a/init/Android.bp b/init/Android.bp
index 9529617..42d0b33 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -71,6 +71,7 @@
"libpropertyinfoserializer",
"libpropertyinfoparser",
"libsnapshot_init",
+ "lib_apex_manifest_proto_lite",
],
shared_libs: [
"libbacktrace",
diff --git a/init/Android.mk b/init/Android.mk
index ee2d89a..07b0f95 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -52,7 +52,6 @@
first_stage_init.cpp \
first_stage_main.cpp \
first_stage_mount.cpp \
- mount_namespace.cpp \
reboot_utils.cpp \
selabel.cpp \
selinux.cpp \
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index c33e0de..648b3bb 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -27,6 +27,7 @@
#include <android-base/properties.h>
#include <android-base/result.h>
#include <android-base/unique_fd.h>
+#include <apex_manifest.pb.h>
#include "util.h"
@@ -90,6 +91,19 @@
return {};
}
+static Result<std::string> GetApexName(const std::string& apex_dir) {
+ const std::string manifest_path = apex_dir + "/apex_manifest.pb";
+ std::string content;
+ if (!android::base::ReadFileToString(manifest_path, &content)) {
+ return Error() << "Failed to read manifest file: " << manifest_path;
+ }
+ apex::proto::ApexManifest manifest;
+ if (!manifest.ParseFromString(content)) {
+ return Error() << "Can't parse manifest file: " << manifest_path;
+ }
+ return manifest.name();
+}
+
static Result<void> ActivateFlattenedApexesFrom(const std::string& from_dir,
const std::string& to_dir) {
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(from_dir.c_str()), closedir);
@@ -101,7 +115,12 @@
if (entry->d_name[0] == '.') continue;
if (entry->d_type == DT_DIR) {
const std::string apex_path = from_dir + "/" + entry->d_name;
- const std::string mount_path = to_dir + "/" + entry->d_name;
+ const auto apex_name = GetApexName(apex_path);
+ if (!apex_name) {
+ LOG(ERROR) << apex_path << " is not an APEX directory: " << apex_name.error();
+ continue;
+ }
+ const std::string mount_path = to_dir + "/" + (*apex_name);
if (auto result = MountDir(apex_path, mount_path); !result) {
return result;
}
@@ -129,26 +148,7 @@
return false;
}
}
- // Special casing for the ART APEX
- constexpr const char kArtApexMountPath[] = "/apex/com.android.art";
- static const std::vector<std::string> kArtApexDirNames = {"com.android.art.release",
- "com.android.art.debug"};
- bool success = false;
- for (const auto& name : kArtApexDirNames) {
- std::string path = kApexTop + "/" + name;
- if (access(path.c_str(), F_OK) == 0) {
- if (auto result = MountDir(path, kArtApexMountPath); !result) {
- LOG(ERROR) << result.error();
- return false;
- }
- success = true;
- break;
- }
- }
- if (!success) {
- PLOG(ERROR) << "Failed to bind mount the ART APEX to " << kArtApexMountPath;
- }
- return success;
+ return true;
}
static android::base::unique_fd bootstrap_ns_fd;
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index e3bb2ab..a20ae03 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -198,3 +198,15 @@
host_supported: true,
corpus: ["testdata/*"],
}
+
+sh_test {
+ name: "ziptool-tests",
+ src: "run-ziptool-tests-on-android.sh",
+ filename: "run-ziptool-tests-on-android.sh",
+ test_suites: ["general-tests"],
+ host_supported: true,
+ device_supported: false,
+ test_config: "ziptool-tests.xml",
+ data: ["cli-tests/**/*"],
+ target_required: ["cli-test", "ziptool"],
+}
diff --git a/libziparchive/cli-tests/files/example.zip b/libziparchive/cli-tests/files/example.zip
new file mode 100644
index 0000000..c3292e9
--- /dev/null
+++ b/libziparchive/cli-tests/files/example.zip
Binary files differ
diff --git a/libziparchive/cli-tests/unzip.test b/libziparchive/cli-tests/unzip.test
new file mode 100755
index 0000000..6e5cbf2
--- /dev/null
+++ b/libziparchive/cli-tests/unzip.test
@@ -0,0 +1,148 @@
+# unzip tests.
+
+# Note: since "master key", Android uses libziparchive for all zip file
+# handling, and that scans the whole central directory immediately. Not only
+# lookups by name but also iteration is implemented using the resulting hash
+# table, meaning that any test that makes assumptions about iteration order
+# will fail on Android.
+
+name: unzip -l
+command: unzip -l $FILES/example.zip d1/d2/x.txt
+after: [ ! -f d1/d2/x.txt ]
+expected-stdout:
+ Archive: $FILES/example.zip
+ Length Date Time Name
+ --------- ---------- ----- ----
+ 1024 2017-06-04 08:45 d1/d2/x.txt
+ --------- -------
+ 1024 1 file
+---
+
+name: unzip -lq
+command: unzip -lq $FILES/example.zip d1/d2/x.txt
+after: [ ! -f d1/d2/x.txt ]
+expected-stdout:
+ Length Date Time Name
+ --------- ---------- ----- ----
+ 1024 2017-06-04 08:45 d1/d2/x.txt
+ --------- -------
+ 1024 1 file
+---
+
+name: unzip -lv
+command: unzip -lv $FILES/example.zip d1/d2/x.txt
+after: [ ! -f d1/d2/file ]
+expected-stdout:
+ Archive: $FILES/example.zip
+ Length Method Size Cmpr Date Time CRC-32 Name
+ -------- ------ ------- ---- ---------- ----- -------- ----
+ 1024 Defl:N 11 99% 2017-06-04 08:45 48d7f063 d1/d2/x.txt
+ -------- ------- --- -------
+ 1024 11 99% 1 file
+---
+
+name: unzip -v
+command: unzip -v $FILES/example.zip d1/d2/x.txt
+after: [ ! -f d1/d2/file ]
+expected-stdout:
+ Archive: $FILES/example.zip
+ Length Method Size Cmpr Date Time CRC-32 Name
+ -------- ------ ------- ---- ---------- ----- -------- ----
+ 1024 Defl:N 11 99% 2017-06-04 08:45 48d7f063 d1/d2/x.txt
+ -------- ------- --- -------
+ 1024 11 99% 1 file
+---
+
+name: unzip one file
+command: unzip -q $FILES/example.zip d1/d2/a.txt && cat d1/d2/a.txt
+after: [ ! -f d1/d2/b.txt ]
+expected-stdout:
+ a
+---
+
+name: unzip all files
+command: unzip -q $FILES/example.zip
+after: [ -f d1/d2/a.txt ]
+after: [ -f d1/d2/b.txt ]
+after: [ -f d1/d2/c.txt ]
+after: [ -f d1/d2/empty.txt ]
+after: [ -f d1/d2/x.txt ]
+after: [ -d d1/d2/dir ]
+expected-stdout:
+---
+
+name: unzip -o
+before: mkdir -p d1/d2
+before: echo b > d1/d2/a.txt
+command: unzip -q -o $FILES/example.zip d1/d2/a.txt && cat d1/d2/a.txt
+expected-stdout:
+ a
+---
+
+name: unzip -n
+before: mkdir -p d1/d2
+before: echo b > d1/d2/a.txt
+command: unzip -q -n $FILES/example.zip d1/d2/a.txt && cat d1/d2/a.txt
+expected-stdout:
+ b
+---
+
+# The reference implementation will create *one* level of missing directories,
+# so this succeeds.
+name: unzip -d shallow non-existent
+command: unzip -q -d will-be-created $FILES/example.zip d1/d2/a.txt
+after: [ -d will-be-created ]
+after: [ -f will-be-created/d1/d2/a.txt ]
+---
+
+# The reference implementation will *only* create one level of missing
+# directories, so this fails.
+name: unzip -d deep non-existent
+command: unzip -q -d oh-no/will-not-be-created $FILES/example.zip d1/d2/a.txt 2> stderr ; echo $? > status
+after: [ ! -d oh-no ]
+after: [ ! -d oh-no/will-not-be-created ]
+after: [ ! -f oh-no/will-not-be-created/d1/d2/a.txt ]
+after: grep -q "oh-no/will-not-be-created" stderr
+after: grep -q "No such file or directory" stderr
+# The reference implementation has *lots* of non-zero exit values, but we stick to 0 and 1.
+after: [ $(cat status) -gt 0 ]
+---
+
+name: unzip -d exists
+before: mkdir dir
+command: unzip -q -d dir $FILES/example.zip d1/d2/a.txt && cat dir/d1/d2/a.txt
+after: [ ! -f d1/d2/a.txt ]
+expected-stdout:
+ a
+---
+
+name: unzip -p
+command: unzip -p $FILES/example.zip d1/d2/a.txt
+after: [ ! -f d1/d2/a.txt ]
+expected-stdout:
+ a
+---
+
+name: unzip -x FILE...
+# Note: the RI ignores -x DIR for some reason, but it's not obvious we should.
+command: unzip -q $FILES/example.zip -x d1/d2/a.txt d1/d2/b.txt d1/d2/empty.txt d1/d2/x.txt && cat d1/d2/c.txt
+after: [ ! -f d1/d2/a.txt ]
+after: [ ! -f d1/d2/b.txt ]
+after: [ ! -f d1/d2/empty.txt ]
+after: [ ! -f d1/d2/x.txt ]
+after: [ -d d1/d2/dir ]
+expected-stdout:
+ ccc
+---
+
+name: unzip FILE -x FILE...
+command: unzip -q $FILES/example.zip d1/d2/a.txt d1/d2/b.txt -x d1/d2/a.txt && cat d1/d2/b.txt
+after: [ ! -f d1/d2/a.txt ]
+after: [ -f d1/d2/b.txt ]
+after: [ ! -f d1/d2/c.txt ]
+after: [ ! -f d1/d2/empty.txt ]
+after: [ ! -f d1/d2/x.txt ]
+after: [ ! -d d1/d2/dir ]
+expected-stdout:
+ bb
+---
diff --git a/libziparchive/cli-tests/zipinfo.test b/libziparchive/cli-tests/zipinfo.test
new file mode 100755
index 0000000..d5bce1c
--- /dev/null
+++ b/libziparchive/cli-tests/zipinfo.test
@@ -0,0 +1,53 @@
+# zipinfo tests.
+
+# Note: since "master key", Android uses libziparchive for all zip file
+# handling, and that scans the whole central directory immediately. Not only
+# lookups by name but also iteration is implemented using the resulting hash
+# table, meaning that any test that makes assumptions about iteration order
+# will fail on Android.
+
+name: zipinfo -1
+command: zipinfo -1 $FILES/example.zip | sort
+expected-stdout:
+ d1/
+ d1/d2/a.txt
+ d1/d2/b.txt
+ d1/d2/c.txt
+ d1/d2/dir/
+ d1/d2/empty.txt
+ d1/d2/x.txt
+---
+
+name: zipinfo header
+command: zipinfo $FILES/example.zip | head -2
+expected-stdout:
+ Archive: $FILES/example.zip
+ Zip file size: 1082 bytes, number of entries: 7
+---
+
+name: zipinfo footer
+command: zipinfo $FILES/example.zip | tail -1
+expected-stdout:
+ 7 files, 1033 bytes uncompressed, 20 bytes compressed: 98.1%
+---
+
+name: zipinfo directory
+# The RI doesn't use ISO dates.
+command: zipinfo $FILES/example.zip d1/ | sed s/17-Jun-/2017-06-/
+expected-stdout:
+ drwxr-x--- 3.0 unx 0 bx stor 2017-06-04 08:40 d1/
+---
+
+name: zipinfo stored
+# The RI doesn't use ISO dates.
+command: zipinfo $FILES/example.zip d1/d2/empty.txt | sed s/17-Jun-/2017-06-/
+expected-stdout:
+ -rw-r----- 3.0 unx 0 bx stor 2017-06-04 08:43 d1/d2/empty.txt
+---
+
+name: zipinfo deflated
+# The RI doesn't use ISO dates.
+command: zipinfo $FILES/example.zip d1/d2/x.txt | sed s/17-Jun-/2017-06-/
+expected-stdout:
+ -rw-r----- 3.0 unx 1024 tx defN 2017-06-04 08:45 d1/d2/x.txt
+---
diff --git a/libziparchive/run-ziptool-tests-on-android.sh b/libziparchive/run-ziptool-tests-on-android.sh
new file mode 100755
index 0000000..3c23d43
--- /dev/null
+++ b/libziparchive/run-ziptool-tests-on-android.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+# Copy the tests across.
+adb shell rm -rf /data/local/tmp/ziptool-tests/
+adb shell mkdir /data/local/tmp/ziptool-tests/
+adb push cli-tests/ /data/local/tmp/ziptool-tests/
+#adb push cli-test /data/local/tmp/ziptool-tests/
+
+if tty -s; then
+ dash_t="-t"
+else
+ dash_t=""
+fi
+
+exec adb shell $dash_t cli-test /data/local/tmp/ziptool-tests/cli-tests/*.test
diff --git a/libziparchive/unzip.cpp b/libziparchive/unzip.cpp
index 11b575e..dd42e90 100644
--- a/libziparchive/unzip.cpp
+++ b/libziparchive/unzip.cpp
@@ -52,7 +52,7 @@
static Role role;
static OverwriteMode overwrite_mode = kPrompt;
static bool flag_1 = false;
-static const char* flag_d = nullptr;
+static std::string flag_d;
static bool flag_l = false;
static bool flag_p = false;
static bool flag_q = false;
@@ -214,12 +214,9 @@
}
// Where are we actually extracting to (for human-readable output)?
- std::string dst;
- if (flag_d) {
- dst = flag_d;
- if (!EndsWith(dst, "/")) dst += '/';
- }
- dst += name;
+ // flag_d is the empty string if -d wasn't used, or has a trailing '/'
+ // otherwise.
+ std::string dst = flag_d + name;
// Ensure the directory hierarchy exists.
if (!MakeDirectoryHierarchy(android::base::Dirname(name))) {
@@ -463,6 +460,7 @@
switch (opt) {
case 'd':
flag_d = optarg;
+ if (!EndsWith(flag_d, "/")) flag_d += '/';
break;
case 'l':
flag_l = true;
@@ -511,9 +509,17 @@
}
// Implement -d by changing into that directory.
- // We'll create implicit directories based on paths in the zip file, but we
- // require that the -d directory already exists.
- if (flag_d && chdir(flag_d) == -1) die(errno, "couldn't chdir to %s", flag_d);
+ // We'll create implicit directories based on paths in the zip file, and we'll create
+ // the -d directory itself, but we require that *parents* of the -d directory already exists.
+ // This is pretty arbitrary, but it's the behavior of the original unzip.
+ if (!flag_d.empty()) {
+ if (mkdir(flag_d.c_str(), 0777) == -1 && errno != EEXIST) {
+ die(errno, "couldn't created %s", flag_d.c_str());
+ }
+ if (chdir(flag_d.c_str()) == -1) {
+ die(errno, "couldn't chdir to %s", flag_d.c_str());
+ }
+ }
ProcessAll(zah);
diff --git a/libziparchive/ziptool-tests.xml b/libziparchive/ziptool-tests.xml
new file mode 100644
index 0000000..211119f
--- /dev/null
+++ b/libziparchive/ziptool-tests.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for running ziptool-tests through Atest or in Infra">
+ <option name="test-suite-tag" value="ziptool-tests" />
+ <!-- This test requires a device, so it's not annotated with a null-device. -->
+ <test class="com.android.tradefed.testtype.binary.ExecutableHostTest" >
+ <option name="binary" value="run-ziptool-tests-on-android.sh" />
+ <!-- Test script assumes a relative path with the cli-tests/ folders. -->
+ <option name="relative-path-execution" value="true" />
+ <!-- Tests shouldn't be that long but set 15m to be safe. -->
+ <option name="per-binary-timeout" value="15m" />
+ </test>
+</configuration>
diff --git a/rootdir/init.rc b/rootdir/init.rc
index b89c45e..c2c9df3 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -421,6 +421,9 @@
# Once everything is setup, no need to modify /.
# The bind+remount combination allows this to work in containers.
mount rootfs rootfs / remount bind ro nodev
+ # Mount default storage into root namespace
+ mount none /mnt/runtime/default /storage bind rec
+ mount none none /storage slave rec
# Make sure /sys/kernel/debug (if present) is labeled properly
# Note that tracefs may be mounted under debug, so we need to cross filesystems
@@ -718,22 +721,6 @@
chown root system /dev/fscklogs/log
chmod 0770 /dev/fscklogs/log
-# Switch between sdcardfs and FUSE depending on persist property
-# TODO: Move this to ro property before launch because FDE devices
-# interact with persistent properties differently during boot
-on zygote-start && property:persist.sys.fuse=true
- # Mount default storage into root namespace
- mount none /mnt/user/0 /storage bind rec
- mount none none /storage slave rec
-on zygote-start && property:persist.sys.fuse=false
- # Mount default storage into root namespace
- mount none /mnt/runtime/default /storage bind rec
- mount none none /storage slave rec
-on zygote-start && property:persist.sys.fuse=""
- # Mount default storage into root namespace
- mount none /mnt/runtime/default /storage bind rec
- mount none none /storage slave rec
-
# It is recommended to put unnecessary data/ initialization from post-fs-data
# to start-zygote in device's init.rc to unblock zygote start.
on zygote-start && property:ro.crypto.state=unencrypted