Merge "installd: implement installApkVerity"
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 8e28432..1627756 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -19,16 +19,18 @@
#define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER
#include <errno.h>
-#include <inttypes.h>
#include <fstream>
#include <fts.h>
+#include <inttypes.h>
#include <regex>
#include <stdlib.h>
#include <string.h>
#include <sys/capability.h>
#include <sys/file.h>
-#include <sys/resource.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
#include <sys/quota.h>
+#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/types.h>
@@ -41,6 +43,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <cutils/ashmem.h>
#include <cutils/fs.h>
#include <cutils/properties.h>
#include <cutils/sched_policy.h>
@@ -84,6 +87,9 @@
static constexpr const char* IDMAP_PREFIX = "/data/resource-cache/";
static constexpr const char* IDMAP_SUFFIX = "@idmap";
+// fsverity assumes the page size is always 4096. If not, the feature can not be
+// enabled.
+static constexpr int kVerityPageSize = 4096;
static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode";
// NOTE: keep in sync with Installer
@@ -184,6 +190,12 @@
} \
}
+#define ASSERT_PAGE_SIZE_4K() { \
+ if (getpagesize() != kVerityPageSize) { \
+ return error("FSVerity only supports 4K page"); \
+ } \
+}
+
} // namespace
status_t InstalldNativeService::start() {
@@ -2337,15 +2349,78 @@
return res ? ok() : error();
}
-binder::Status InstalldNativeService::installApkVerity(const std::string& /*filePath*/,
- const ::android::base::unique_fd& /*verityInput*/) {
+// This kernel feaeture is experimental.
+// TODO: remove local definition once upstreamed
+#ifndef FS_IOC_SET_FSVERITY
+struct fsverity_set {
+ __u64 offset;
+ __u64 flags;
+};
+
+#define FS_IOC_SET_FSVERITY _IOW('f', 2734, struct fsverity_set)
+
+#define FSVERITY_FLAG_ENABLED 0x0001
+#endif
+
+binder::Status InstalldNativeService::installApkVerity(const std::string& filePath,
+ const ::android::base::unique_fd& verityInputAshmem) {
ENFORCE_UID(AID_SYSTEM);
if (!android::base::GetBoolProperty(kPropApkVerityMode, false)) {
return ok();
}
- // TODO: Append verity to filePath then issue ioctl to enable
- // it and hide the tree. See b/30972906.
- return error("not implemented yet");
+#if DEBUG
+ ASSERT_PAGE_SIZE_4K();
+#endif
+ // TODO: also check fsverity support in the current file system if compiled with DEBUG.
+ // TODO: change ashmem to some temporary file to support huge apk.
+ if (!ashmem_valid(verityInputAshmem.get())) {
+ return error("FD is not an ashmem");
+ }
+
+ // TODO(71871109): Validate filePath.
+ // 1. Seek to the next page boundary beyond the end of the file.
+ ::android::base::unique_fd wfd(open(filePath.c_str(), O_WRONLY | O_APPEND));
+ if (wfd.get() < 0) {
+ return error("Failed to open " + filePath + ": " + strerror(errno));
+ }
+ struct stat st;
+ if (fstat(wfd.get(), &st) < 0) {
+ return error("Failed to stat " + filePath + ": " + strerror(errno));
+ }
+ // fsverity starts from the block boundary.
+ if (lseek(wfd.get(), (st.st_size + kVerityPageSize - 1) / kVerityPageSize, SEEK_SET) < 0) {
+ return error("Failed to lseek " + filePath + ": " + strerror(errno));
+ }
+
+ // 2. Write everything in the ashmem to the file.
+ int size = ashmem_get_size_region(verityInputAshmem.get());
+ if (size < 0) {
+ return error("Failed to get ashmem size: " + std::to_string(size));
+ }
+ void* data = mmap(NULL, size, PROT_READ, MAP_SHARED, wfd.get(), 0);
+ if (data == MAP_FAILED) {
+ return error("Failed to mmap the ashmem: " + std::string(strerror(errno)));
+ }
+ int remaining = size;
+ while (remaining > 0) {
+ int ret = TEMP_FAILURE_RETRY(write(wfd.get(), data, remaining));
+ if (ret < 0) {
+ munmap(data, size);
+ return error("Failed to write to " + filePath + " (" + std::to_string(remaining) +
+ + "/" + std::to_string(size) + "): " + strerror(errno));
+ }
+ remaining -= ret;
+ }
+ munmap(data, size);
+
+ // 3. Enable fsverity. Once it's done, the file becomes immutable.
+ struct fsverity_set config;
+ config.offset = st.st_size;
+ config.flags = FSVERITY_FLAG_ENABLED;
+ if (ioctl(wfd.get(), FS_IOC_SET_FSVERITY, &config) < 0) {
+ return error("Failed to enable fsverity on " + filePath + ": " + strerror(errno));
+ }
+ return ok();
}
binder::Status InstalldNativeService::reconcileSecondaryDexFile(