summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apexd/Android.bp43
-rw-r--r--apexd/apex_file.cpp38
-rw-r--r--apexd/apex_file.h8
-rw-r--r--apexd/apex_file_test.cpp70
-rw-r--r--apexd/apexd.cpp4
-rw-r--r--apexd/apexd_testdata/Android.bp21
6 files changed, 161 insertions, 23 deletions
diff --git a/apexd/Android.bp b/apexd/Android.bp
index b2f8202..a166277 100644
--- a/apexd/Android.bp
+++ b/apexd/Android.bp
@@ -237,6 +237,22 @@ genrule {
}
genrule {
+ // Generates an apex with a corrupted filesystem superblock, which should cause
+ // Apex::Open to fail
+ name: "gen_corrupt_superblock_apex",
+ out: ["apex.apexd_test_corrupt_superblock_apex.apex"],
+ srcs: [":apex.apexd_test"],
+ tools: ["soong_zip", "zipalign"],
+ cmd: "unzip -q $(in) -d $(genDir) && " +
+ "dd if=/dev/zero of=$(genDir)/apex_payload.img conv=notrunc bs=1024 seek=1 count=1 && " +
+ "$(location soong_zip) -d -C $(genDir) -D $(genDir) " +
+ "-s apex_manifest.pb -s apex_payload.img -s apex_pubkey " +
+ "-o $(genDir)/unaligned.apex && " +
+ "$(location zipalign) -f 4096 $(genDir)/unaligned.apex " +
+ "$(genDir)/apex.apexd_test_corrupt_superblock_apex.apex"
+}
+
+genrule {
// Generates an apex with a corrupted filesystem image, which should cause
// dm-verity verification to fail
name: "gen_corrupt_apex",
@@ -252,6 +268,28 @@ genrule {
"$(genDir)/apex.apexd_test_corrupt_apex.apex"
}
+genrule {
+ // Extract the root digest with avbtool
+ name: "apex.apexd_test_digest",
+ out: ["apex.apexd_test_digest.txt"],
+ srcs: [":apex.apexd_test"],
+ tools: ["avbtool"],
+ cmd: "unzip -q $(in) -d $(genDir) apex_payload.img && " +
+ "$(location avbtool) print_partition_digests --image $(genDir)/apex_payload.img " +
+ "| cut -c 3-| tee $(out)"
+}
+
+genrule {
+ // Extract the root digest with avbtool
+ name: "apex.apexd_test_f2fs_digest",
+ out: ["apex.apexd_test_f2fs_digest.txt"],
+ srcs: [":apex.apexd_test_f2fs"],
+ tools: ["avbtool"],
+ cmd: "unzip -q $(in) -d $(genDir) apex_payload.img && " +
+ "$(location avbtool) print_partition_digests --image $(genDir)/apex_payload.img " +
+ "| cut -c 3-| tee $(out)"
+}
+
cc_test {
name: "ApexTestCases",
defaults: [
@@ -266,10 +304,14 @@ cc_test {
],
data: [
":apex.apexd_test",
+ ":apex.apexd_test_f2fs",
+ ":apex.apexd_test_digest",
+ ":apex.apexd_test_f2fs_digest",
":apex.apexd_test_different_app",
":apex.apexd_test_no_hashtree",
":apex.apexd_test_no_hashtree_2",
":apex.apexd_test_no_inst_key",
+ ":apex.apexd_test_f2fs_no_inst_key",
":apex.apexd_test_nocode",
":apex.apexd_test_postinstall",
":apex.apexd_test_preinstall",
@@ -277,6 +319,7 @@ cc_test {
":apex.apexd_test_v2",
":apex.corrupted_b146895998",
":gen_bad_apexes",
+ ":gen_corrupt_superblock_apex",
":gen_corrupt_apex",
":com.android.apex.cts.shim.v1_prebuilt",
":com.android.apex.cts.shim.v2_prebuilt",
diff --git a/apexd/apex_file.cpp b/apexd/apex_file.cpp
index f91b5d1..abcda7d 100644
--- a/apexd/apex_file.cpp
+++ b/apexd/apex_file.cpp
@@ -37,6 +37,7 @@
#include "apexd_utils.h"
#include "string_log.h"
+using android::base::borrowed_fd;
using android::base::EndsWith;
using android::base::Error;
using android::base::ReadFullyAtOffset;
@@ -52,6 +53,28 @@ namespace {
constexpr const char* kImageFilename = "apex_payload.img";
constexpr const char* kBundledPublicKeyFilename = "apex_pubkey";
+struct FsMagic {
+ const char* type;
+ int32_t offset;
+ int16_t len;
+ const char* magic;
+};
+constexpr const FsMagic kFsType[] = {{"f2fs", 1024, 4, "\x10\x20\xf5\xf2"},
+ {"ext4", 1024 + 0x38, 2, "\123\357"}};
+
+Result<std::string> RetrieveFsType(borrowed_fd fd, int32_t image_offset) {
+ for (const auto& fs : kFsType) {
+ char buf[fs.len];
+ if (!ReadFullyAtOffset(fd, buf, fs.len, image_offset + fs.offset)) {
+ return ErrnoError() << "Couldn't read filesystem magic";
+ }
+ if (memcmp(buf, fs.magic, fs.len) == 0) {
+ return std::string(fs.type);
+ }
+ }
+ return Error() << "Couldn't find filesystem magic";
+}
+
} // namespace
Result<ApexFile> ApexFile::Open(const std::string& path) {
@@ -63,7 +86,12 @@ Result<ApexFile> ApexFile::Open(const std::string& path) {
ZipArchiveHandle handle;
auto handle_guard =
android::base::make_scope_guard([&handle] { CloseArchive(handle); });
- int ret = OpenArchive(path.c_str(), &handle);
+ unique_fd fd(open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
+ if (fd < 0) {
+ return Error() << "Failed to open package " << path << ": "
+ << "I/O error";
+ }
+ int ret = OpenArchiveFd(fd.get(), path.c_str(), &handle, false);
if (ret < 0) {
return Error() << "Failed to open package " << path << ": "
<< ErrorCodeString(ret);
@@ -79,6 +107,12 @@ Result<ApexFile> ApexFile::Open(const std::string& path) {
image_offset = entry.offset;
image_size = entry.uncompressed_length;
+ auto fs_type = RetrieveFsType(fd, image_offset);
+ if (!fs_type.ok()) {
+ return Error() << "Failed to retrieve filesystem type for " << path << ": "
+ << fs_type.error();
+ }
+
ret = FindEntry(handle, kManifestFilenamePb, &entry);
if (ret < 0) {
return Error() << "Could not find entry \"" << kManifestFilenamePb
@@ -113,7 +147,7 @@ Result<ApexFile> ApexFile::Open(const std::string& path) {
}
return ApexFile(path, image_offset, image_size, std::move(*manifest), pubkey,
- isPathForBuiltinApexes(path));
+ isPathForBuiltinApexes(path), *fs_type);
}
// AVB-related code.
diff --git a/apexd/apex_file.h b/apexd/apex_file.h
index 0476911..88914a9 100644
--- a/apexd/apex_file.h
+++ b/apexd/apex_file.h
@@ -53,6 +53,7 @@ class ApexFile {
const ApexManifest& GetManifest() const { return manifest_; }
const std::string& GetBundledPublicKey() const { return apex_pubkey_; }
bool IsBuiltin() const { return is_builtin_; }
+ const std::string& GetFsType() const { return fs_type_; }
android::base::Result<ApexVerityData> VerifyApexVerity() const;
android::base::Result<void> VerifyManifestMatches(
const std::string& mount_path) const;
@@ -60,13 +61,15 @@ class ApexFile {
private:
ApexFile(const std::string& apex_path, int32_t image_offset,
size_t image_size, ApexManifest manifest,
- const std::string& apex_pubkey, bool is_builtin)
+ const std::string& apex_pubkey, bool is_builtin,
+ const std::string& fs_type)
: apex_path_(apex_path),
image_offset_(image_offset),
image_size_(image_size),
manifest_(std::move(manifest)),
apex_pubkey_(apex_pubkey),
- is_builtin_(is_builtin) {}
+ is_builtin_(is_builtin),
+ fs_type_(fs_type) {}
std::string apex_path_;
int32_t image_offset_;
@@ -74,6 +77,7 @@ class ApexFile {
ApexManifest manifest_;
std::string apex_pubkey_;
bool is_builtin_;
+ std::string fs_type_;
};
android::base::Result<std::vector<std::string>> FindApexes(
diff --git a/apexd/apex_file_test.cpp b/apexd/apex_file_test.cpp
index 711d024..9d2cf17 100644
--- a/apexd/apex_file_test.cpp
+++ b/apexd/apex_file_test.cpp
@@ -19,6 +19,8 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/scopeguard.h>
+#include <android-base/strings.h>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <libavb/libavb.h>
#include <ziparchive/zip_archive.h>
@@ -34,8 +36,20 @@ namespace android {
namespace apex {
namespace {
-TEST(ApexFileTest, GetOffsetOfSimplePackage) {
- const std::string filePath = testDataDir + "apex.apexd_test.apex";
+struct ApexFileTestParam {
+ const char* type;
+ const char* prefix;
+};
+
+constexpr const ApexFileTestParam kParameters[] = {
+ {"ext4", "apex.apexd_test"}, {"f2fs", "apex.apexd_test_f2fs"}};
+
+class ApexFileTest : public ::testing::TestWithParam<ApexFileTestParam> {};
+
+INSTANTIATE_TEST_SUITE_P(Apex, ApexFileTest, testing::ValuesIn(kParameters));
+
+TEST_P(ApexFileTest, GetOffsetOfSimplePackage) {
+ const std::string filePath = testDataDir + GetParam().prefix + ".apex";
Result<ApexFile> apexFile = ApexFile::Open(filePath);
ASSERT_TRUE(apexFile.ok());
@@ -66,22 +80,21 @@ TEST(ApexFileTest, GetOffsetMissingFile) {
const std::string filePath = testDataDir + "missing.apex";
Result<ApexFile> apexFile = ApexFile::Open(filePath);
ASSERT_FALSE(apexFile.ok());
- EXPECT_NE(std::string::npos,
- apexFile.error().message().find("Failed to open package"))
- << apexFile.error();
+ ASSERT_THAT(apexFile.error().message(),
+ testing::HasSubstr("Failed to open package"));
}
-TEST(ApexFileTest, GetApexManifest) {
- const std::string filePath = testDataDir + "apex.apexd_test.apex";
+TEST_P(ApexFileTest, GetApexManifest) {
+ const std::string filePath = testDataDir + GetParam().prefix + ".apex";
Result<ApexFile> apexFile = ApexFile::Open(filePath);
ASSERT_RESULT_OK(apexFile);
EXPECT_EQ("com.android.apex.test_package", apexFile->GetManifest().name());
EXPECT_EQ(1u, apexFile->GetManifest().version());
}
-TEST(ApexFileTest, VerifyApexVerity) {
+TEST_P(ApexFileTest, VerifyApexVerity) {
ASSERT_RESULT_OK(collectPreinstalledData({"/system_ext/apex"}));
- const std::string filePath = testDataDir + "apex.apexd_test.apex";
+ const std::string filePath = testDataDir + GetParam().prefix + ".apex";
Result<ApexFile> apexFile = ApexFile::Open(filePath);
ASSERT_RESULT_OK(apexFile);
@@ -93,10 +106,15 @@ TEST(ApexFileTest, VerifyApexVerity) {
EXPECT_EQ(std::string("368a22e64858647bc45498e92f749f85482ac468"
"50ca7ec8071f49dfa47a243c"),
data.salt);
- EXPECT_EQ(
- std::string(
- "8e841019e41e8c40bca6dd6304cbf163ea257ba0a268304832c4105eba1c2747"),
- data.root_digest);
+
+ const std::string digestPath =
+ testDataDir + GetParam().prefix + "_digest.txt";
+ std::string rootDigest;
+ ASSERT_TRUE(android::base::ReadFileToString(digestPath, &rootDigest))
+ << "Failed to read " << digestPath;
+ rootDigest = android::base::Trim(rootDigest);
+
+ EXPECT_EQ(std::string(rootDigest), data.root_digest);
}
// TODO: May consider packaging a debug key in debug builds (again).
@@ -109,8 +127,9 @@ TEST(ApexFileTest, DISABLED_VerifyApexVerityNoKeyDir) {
ASSERT_FALSE(verity_or.ok());
}
-TEST(ApexFileTest, VerifyApexVerityNoKeyInst) {
- const std::string filePath = testDataDir + "apex.apexd_test_no_inst_key.apex";
+TEST_P(ApexFileTest, VerifyApexVerityNoKeyInst) {
+ const std::string filePath =
+ testDataDir + GetParam().prefix + "_no_inst_key.apex";
Result<ApexFile> apexFile = ApexFile::Open(filePath);
ASSERT_RESULT_OK(apexFile);
@@ -118,8 +137,8 @@ TEST(ApexFileTest, VerifyApexVerityNoKeyInst) {
ASSERT_FALSE(verity_or.ok());
}
-TEST(ApexFileTest, GetBundledPublicKey) {
- const std::string filePath = testDataDir + "apex.apexd_test.apex";
+TEST_P(ApexFileTest, GetBundledPublicKey) {
+ const std::string filePath = testDataDir + GetParam().prefix + ".apex";
Result<ApexFile> apexFile = ApexFile::Open(filePath);
ASSERT_RESULT_OK(apexFile);
@@ -139,6 +158,23 @@ TEST(ApexFileTest, CorrutedApex_b146895998) {
ASSERT_FALSE(apex->VerifyApexVerity());
}
+TEST_P(ApexFileTest, RetrieveFsType) {
+ const std::string filePath = testDataDir + GetParam().prefix + ".apex";
+ Result<ApexFile> apexFile = ApexFile::Open(filePath);
+ ASSERT_TRUE(apexFile.ok());
+
+ EXPECT_EQ(std::string(GetParam().type), apexFile->GetFsType());
+}
+
+TEST(ApexFileTest, OpenInvalidFilesystem) {
+ const std::string filePath =
+ testDataDir + "apex.apexd_test_corrupt_superblock_apex.apex";
+ Result<ApexFile> apexFile = ApexFile::Open(filePath);
+ ASSERT_FALSE(apexFile.ok());
+ ASSERT_THAT(apexFile.error().message(),
+ testing::HasSubstr("Failed to retrieve filesystem type"));
+}
+
} // namespace
} // namespace apex
} // namespace android
diff --git a/apexd/apexd.cpp b/apexd/apexd.cpp
index c302c70..c48dd4d 100644
--- a/apexd/apexd.cpp
+++ b/apexd/apexd.cpp
@@ -469,8 +469,8 @@ Result<MountedApexData> MountPackageImpl(const ApexFile& apex,
mountFlags |= MS_NOEXEC;
}
- if (mount(blockDevice.c_str(), mountPoint.c_str(), "ext4", mountFlags,
- nullptr) == 0) {
+ if (mount(blockDevice.c_str(), mountPoint.c_str(), apex.GetFsType().c_str(),
+ mountFlags, nullptr) == 0) {
LOG(INFO) << "Successfully mounted package " << full_path << " on "
<< mountPoint;
auto status = VerifyMountedImage(apex, mountPoint);
diff --git a/apexd/apexd_testdata/Android.bp b/apexd/apexd_testdata/Android.bp
index 1f74a53..9c41241 100644
--- a/apexd/apexd_testdata/Android.bp
+++ b/apexd/apexd_testdata/Android.bp
@@ -33,6 +33,17 @@ apex {
}
apex {
+ name: "apex.apexd_test_f2fs",
+ manifest: "manifest.json",
+ file_contexts: ":apex.test-file_contexts",
+ prebuilts: ["sample_prebuilt_file"],
+ key: "com.android.apex.test_package.key",
+ installable: false,
+ min_sdk_version: "current",
+ payload_fs_type: "f2fs",
+}
+
+apex {
name: "apex.apexd_test_no_hashtree",
manifest: "manifest.json",
file_contexts: ":apex.test-file_contexts",
@@ -161,6 +172,16 @@ apex {
installable: false,
}
+apex {
+ name: "apex.apexd_test_f2fs_no_inst_key",
+ manifest: "manifest_no_inst_key.json",
+ file_contexts: ":apex.test-file_contexts",
+ prebuilts: ["sample_prebuilt_file"],
+ key: "com.android.apex.test_package.no_inst_key.key",
+ installable: false,
+ payload_fs_type: "f2fs",
+}
+
apex_key {
name: "com.android.apex.test_package_2.key",
public_key: "com.android.apex.test_package_2.avbpubkey",