Merge "adb: remove atransport ref counting."
diff --git a/adb/SOCKET-ACTIVATION.txt b/adb/SOCKET-ACTIVATION.txt
new file mode 100644
index 0000000..4ef62ac
--- /dev/null
+++ b/adb/SOCKET-ACTIVATION.txt
@@ -0,0 +1,42 @@
+adb can be configured to work with systemd-style socket activation,
+allowing the daemon to start automatically when the adb control port
+is forwarded across a network. You need two files, placed in the usual
+systemd service directories (e.g., ~/.config/systemd/user for a user
+service).
+
+adb.service:
+
+--- START adb.service CUT HERE ---
+[Unit]
+Description=adb
+After=adb.socket
+Requires=adb.socket
+[Service]
+Type=simple
+# FD 3 is part of the systemd interface
+ExecStart=/path/to/adb server nodaemon -L acceptfd:3
+--- END adb.service CUT HERE ---
+
+--- START adb.socket CUT HERE ---
+[Unit]
+Description=adb
+PartOf=adb.service
+[Socket]
+ListenStream=127.0.0.1:5037
+Accept=no
+[Install]
+WantedBy=sockets.target
+--- END adb.socket CUT HERE ---
+
+After installing the adb service, the adb server will be started
+automatically on any connection to 127.0.0.1:5037 (the default adb
+control port), even after adb kill-server kills the server.
+
+Other "superserver" launcher systems (like macOS launchd) can be
+configured analogously. The important part is that adb be started with
+"server" and "nodaemon" command line arguments and that the listen
+address (passed to -L) name a file descriptor that's ready to
+accept(2) connections and that's already bound to the desired address
+and listening. inetd-style pre-accepted sockets do _not_ work in this
+configuration: the file descriptor passed to acceptfd must be the
+serve socket, not the accepted connection socket.
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
index d91ae35..f724cb5 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -222,7 +222,7 @@
int port;
std::string error;
if (!parse_tcp_socket_spec(__adb_server_socket_spec, nullptr, &port, nullptr, &error)) {
- LOG(FATAL) << "failed to parse server socket spec: " << error;
+ return {};
}
return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adb." + std::to_string(port);
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 0ffdbc2..6465ffe 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -107,6 +107,7 @@
" localfilesystem:<unix domain socket name>\n"
" dev:<character device name>\n"
" jdwp:<process pid> (remote only)\n"
+ " acceptfd:<fd> (listen only)\n"
" forward --remove LOCAL remove specific forward socket connection\n"
" forward --remove-all remove all forward socket connections\n"
" ppp TTY [PARAMETER...] run PPP over USB\n"
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index 27e8c46..9ce443e 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -16,6 +16,7 @@
#include "socket_spec.h"
+#include <limits>
#include <string>
#include <string_view>
#include <unordered_map>
@@ -28,10 +29,12 @@
#include <cutils/sockets.h>
#include "adb.h"
+#include "adb_utils.h"
#include "sysdeps.h"
using namespace std::string_literals;
+using android::base::ConsumePrefix;
using android::base::StringPrintf;
#if defined(__linux__)
@@ -131,7 +134,7 @@
return true;
}
}
- return spec.starts_with("tcp:");
+ return spec.starts_with("tcp:") || spec.starts_with("acceptfd:");
}
bool is_local_socket_spec(std::string_view spec) {
@@ -235,6 +238,9 @@
*error = "vsock is only supported on linux";
return false;
#endif // ADB_LINUX
+ } else if (address.starts_with("acceptfd:")) {
+ *error = "cannot connect to acceptfd";
+ return false;
}
for (const auto& it : kLocalSocketTypes) {
@@ -334,6 +340,46 @@
*error = "vsock is only supported on linux";
return -1;
#endif // ADB_LINUX
+ } else if (ConsumePrefix(&spec, "acceptfd:")) {
+#if ADB_WINDOWS
+ *error = "socket activation not supported under Windows";
+ return -1;
+#else
+ // We inherited the socket from some kind of launcher. It's already bound and
+ // listening. Return a copy of the FD instead of the FD itself so we implement the
+ // normal "listen" contract and can succeed more than once.
+ unsigned int fd_u;
+ if (!ParseUint(&fd_u, spec) || fd_u > std::numeric_limits<int>::max()) {
+ *error = "invalid fd";
+ return -1;
+ }
+ int fd = static_cast<int>(fd_u);
+ int flags = get_fd_flags(fd);
+ if (flags < 0) {
+ *error = android::base::StringPrintf("could not get flags of inherited fd %d: '%s'", fd,
+ strerror(errno));
+ return -1;
+ }
+ if (flags & FD_CLOEXEC) {
+ *error = android::base::StringPrintf("fd %d was not inherited from parent", fd);
+ return -1;
+ }
+
+ int dummy_sock_type;
+ socklen_t dummy_sock_type_size = sizeof(dummy_sock_type);
+ if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &dummy_sock_type, &dummy_sock_type_size)) {
+ *error = android::base::StringPrintf("fd %d does not refer to a socket", fd);
+ return -1;
+ }
+
+ int new_fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
+ if (new_fd < 0) {
+ *error = android::base::StringPrintf("could not dup inherited fd %d: '%s'", fd,
+ strerror(errno));
+ return -1;
+ }
+ return new_fd;
+#endif
}
for (const auto& it : kLocalSocketTypes) {
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 466c2ce..0c5a6b4 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -349,8 +349,15 @@
return c == '/';
}
+static __inline__ int get_fd_flags(borrowed_fd fd) {
+ return fcntl(fd.get(), F_GETFD);
+}
+
static __inline__ void close_on_exec(borrowed_fd fd) {
- fcntl(fd.get(), F_SETFD, FD_CLOEXEC);
+ int flags = get_fd_flags(fd);
+ if (flags >= 0 && (flags & FD_CLOEXEC) == 0) {
+ fcntl(fd.get(), F_SETFD, flags | FD_CLOEXEC);
+ }
}
// Open a file and return a file descriptor that may be used with unix_read(),
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 2dc47bb..7b686f0 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -91,6 +91,7 @@
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
using android::base::Basename;
+using android::base::GetBoolProperty;
using android::base::Realpath;
using android::base::StartsWith;
using android::base::unique_fd;
@@ -1357,14 +1358,33 @@
return ret;
}
+static std::string GetBlockDeviceForMountPoint(const std::string& mount_point) {
+ Fstab mounts;
+ if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
+ LERROR << "Can't read /proc/mounts";
+ return "";
+ }
+ auto entry = GetEntryForMountPoint(&mounts, mount_point);
+ if (entry == nullptr) {
+ LWARNING << mount_point << " is not mounted";
+ return "";
+ }
+ return entry->blk_device;
+}
+
// TODO(b/143970043): return different error codes based on which step failed.
int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) {
- auto entry = GetMountedEntryForUserdata(fstab);
- if (entry == nullptr) {
+ std::string block_device = GetBlockDeviceForMountPoint("/data");
+ if (block_device.empty()) {
+ LERROR << "/data is not mounted";
+ return -1;
+ }
+ auto fstab_entry = GetMountedEntryForUserdata(fstab);
+ if (fstab_entry == nullptr) {
LERROR << "Can't find /data in fstab";
return -1;
}
- if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) {
+ if (!fstab_entry->fs_mgr_flags.checkpoint_blk && !fstab_entry->fs_mgr_flags.checkpoint_fs) {
LINFO << "Userdata doesn't support checkpointing. Nothing to do";
return 0;
}
@@ -1373,34 +1393,43 @@
LINFO << "Checkpointing not needed. Don't remount";
return 0;
}
- if (entry->fs_mgr_flags.checkpoint_fs) {
+ bool force_umount_for_f2fs =
+ GetBoolProperty("sys.init.userdata_remount.force_umount_f2fs", false);
+ if (fstab_entry->fs_mgr_flags.checkpoint_fs && !force_umount_for_f2fs) {
// Userdata is f2fs, simply remount it.
- if (!checkpoint_manager.Update(&(*entry))) {
+ if (!checkpoint_manager.Update(fstab_entry)) {
LERROR << "Failed to remount userdata in checkpointing mode";
return -1;
}
- if (mount(entry->blk_device.c_str(), entry->mount_point.c_str(), "none",
- MS_REMOUNT | entry->flags, entry->fs_options.c_str()) != 0) {
+ if (mount(block_device.c_str(), fstab_entry->mount_point.c_str(), "none",
+ MS_REMOUNT | fstab_entry->flags, fstab_entry->fs_options.c_str()) != 0) {
PERROR << "Failed to remount userdata in checkpointing mode";
return -1;
}
} else {
- // STOPSHIP(b/143970043): support remounting for ext4 + metadata encryption.
- if (should_use_metadata_encryption(*entry)) {
- LWARNING << "Remounting into checkpointing is not supported for metadata encrypted "
- << "ext4 userdata. Proceed with caution";
- return 0;
- }
+ LINFO << "Unmounting /data before remounting into checkpointing mode";
if (umount2("/data", UMOUNT_NOFOLLOW) != 0) {
PERROR << "Failed to umount /data";
return -1;
}
DeviceMapper& dm = DeviceMapper::Instance();
- // TODO(b/143970043): need to delete every dm-device under the one userdata is mounted on.
- if (!dm.DeleteDeviceIfExists("bow")) {
- LERROR << "Failed to delete dm-bow";
- return -1;
+ while (dm.IsDmBlockDevice(block_device)) {
+ auto next_device = dm.GetParentBlockDeviceByPath(block_device);
+ auto name = dm.GetDmDeviceNameByPath(block_device);
+ if (!name) {
+ LERROR << "Failed to get dm-name for " << block_device;
+ return -1;
+ }
+ LINFO << "Deleting " << block_device << " named " << *name;
+ if (!dm.DeleteDevice(*name, 3s)) {
+ return -1;
+ }
+ if (!next_device) {
+ LERROR << "Failed to find parent device for " << block_device;
+ }
+ block_device = *next_device;
}
+ LINFO << "Remounting /data";
// TODO(b/143970043): remove this hack after fs_mgr_mount_all is refactored.
int result = fs_mgr_mount_all(fstab, MOUNT_MODE_ONLY_USERDATA);
return result == FS_MGR_MNTALL_FAIL ? -1 : 0;
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index c81a079..9697a4c 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -582,8 +582,7 @@
} // namespace
void TransformFstabForDsu(Fstab* fstab, const std::vector<std::string>& dsu_partitions) {
- static constexpr char kGsiKeys[] =
- "/avb/q-gsi.avbpubkey:/avb/r-gsi.avbpubkey:/avb/s-gsi.avbpubkey";
+ static constexpr char kDsuKeysDir[] = "/avb";
// Convert userdata
// Inherit fstab properties for userdata.
FstabEntry userdata;
@@ -629,29 +628,18 @@
.fs_type = "ext4",
.flags = MS_RDONLY,
.fs_options = "barrier=1",
- .avb_keys = kGsiKeys,
+ .avb_keys = kDsuKeysDir,
};
entry.fs_mgr_flags.wait = true;
entry.fs_mgr_flags.logical = true;
entry.fs_mgr_flags.first_stage_mount = true;
- // Use the system key which may be in the vbmeta or vbmeta_system
- // TODO: b/141284191
- entry.vbmeta_partition = "vbmeta";
- fstab->emplace_back(entry);
- entry.vbmeta_partition = "vbmeta_system";
- fstab->emplace_back(entry);
} else {
// If the corresponding partition exists, transform all its Fstab
// by pointing .blk_device to the DSU partition.
for (auto&& entry : entries) {
entry->blk_device = partition;
- if (entry->avb_keys.size() > 0) {
- entry->avb_keys += ":";
- }
- // If the DSU is signed by OEM, the original Fstab already has the information
- // required by avb, otherwise the DSU is GSI and will need the avb_keys as listed
- // below.
- entry->avb_keys += kGsiKeys;
+ // AVB keys for DSU should always be under kDsuKeysDir.
+ entry->avb_keys += kDsuKeysDir;
}
// Make sure the ext4 is included to support GSI.
auto partition_ext4 =
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 0579a3d..05f3311 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -1066,6 +1066,25 @@
return candidates;
}
+static void TryMountScratch() {
+ auto scratch_device = fs_mgr_overlayfs_scratch_device();
+ if (!fs_mgr_overlayfs_scratch_can_be_mounted(scratch_device)) {
+ return;
+ }
+ if (!WaitForFile(scratch_device, 10s)) {
+ return;
+ }
+ const auto mount_type = fs_mgr_overlayfs_scratch_mount_type();
+ if (!fs_mgr_overlayfs_mount_scratch(scratch_device, mount_type, true /* readonly */)) {
+ return;
+ }
+ auto has_overlayfs_dir = fs_mgr_access(kScratchMountPoint + kOverlayTopDir);
+ fs_mgr_overlayfs_umount_scratch();
+ if (has_overlayfs_dir) {
+ fs_mgr_overlayfs_mount_scratch(scratch_device, mount_type);
+ }
+}
+
bool fs_mgr_overlayfs_mount_all(Fstab* fstab) {
auto ret = false;
if (fs_mgr_overlayfs_invalid()) return ret;
@@ -1080,19 +1099,7 @@
}
if (scratch_can_be_mounted) {
scratch_can_be_mounted = false;
- auto scratch_device = fs_mgr_overlayfs_scratch_device();
- if (fs_mgr_overlayfs_scratch_can_be_mounted(scratch_device) &&
- WaitForFile(scratch_device, 10s)) {
- const auto mount_type = fs_mgr_overlayfs_scratch_mount_type();
- if (fs_mgr_overlayfs_mount_scratch(scratch_device, mount_type,
- true /* readonly */)) {
- auto has_overlayfs_dir = fs_mgr_access(kScratchMountPoint + kOverlayTopDir);
- fs_mgr_overlayfs_umount_scratch();
- if (has_overlayfs_dir) {
- fs_mgr_overlayfs_mount_scratch(scratch_device, mount_type);
- }
- }
- }
+ TryMountScratch();
}
if (fs_mgr_overlayfs_mount(mount_point)) ret = true;
}
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
index 32702ae..bf51fe7 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -37,11 +37,9 @@
"libfstab",
],
shared_libs: [
+ "libbase",
"libcrypto",
],
- header_libs: [
- "libbase_headers",
- ],
target: {
darwin: {
enabled: false,
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
index c4d7511..8770a6b 100644
--- a/fs_mgr/libfs_avb/fs_avb.cpp
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -22,6 +22,7 @@
#include <sys/ioctl.h>
#include <sys/types.h>
+#include <algorithm>
#include <sstream>
#include <string>
#include <vector>
@@ -308,7 +309,18 @@
return nullptr;
}
- if (!ValidatePublicKeyBlob(public_key_data, Split(fstab_entry.avb_keys, ":"))) {
+ // fstab_entry.avb_keys might be either a directory containing multiple keys,
+ // or a string indicating multiple keys separated by ':'.
+ std::vector<std::string> allowed_avb_keys;
+ auto list_avb_keys_in_dir = ListFiles(fstab_entry.avb_keys);
+ if (list_avb_keys_in_dir) {
+ std::sort(list_avb_keys_in_dir->begin(), list_avb_keys_in_dir->end());
+ allowed_avb_keys = *list_avb_keys_in_dir;
+ } else {
+ allowed_avb_keys = Split(fstab_entry.avb_keys, ":");
+ }
+
+ if (!ValidatePublicKeyBlob(public_key_data, allowed_avb_keys)) {
avb_handle->status_ = AvbHandleStatus::kVerificationError;
LWARNING << "Found unknown public key used to sign " << fstab_entry.mount_point;
if (!allow_verification_error) {
diff --git a/fs_mgr/libfs_avb/tests/util_test.cpp b/fs_mgr/libfs_avb/tests/util_test.cpp
index 12b5acb..e64282b 100644
--- a/fs_mgr/libfs_avb/tests/util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/util_test.cpp
@@ -16,10 +16,12 @@
#include <unistd.h>
+#include <algorithm>
#include <future>
#include <string>
#include <thread>
+#include <android-base/strings.h>
#include <base/files/file_util.h>
#include "fs_avb_test_util.h"
@@ -29,6 +31,7 @@
using android::fs_mgr::BytesToHex;
using android::fs_mgr::FileWaitMode;
using android::fs_mgr::HexToBytes;
+using android::fs_mgr::ListFiles;
using android::fs_mgr::NibbleValue;
using android::fs_mgr::WaitForFile;
@@ -210,4 +213,102 @@
ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));
}
+TEST(BasicUtilTest, ListFiles) {
+ // Gets system tmp dir.
+ base::FilePath tmp_dir;
+ ASSERT_TRUE(GetTempDir(&tmp_dir));
+
+ // Creates a test dir for ListFiles testing.
+ base::FilePath test_dir;
+ ASSERT_TRUE(base::CreateTemporaryDirInDir(tmp_dir, "list-file-tests.", &test_dir));
+
+ // Generates dummy files to list.
+ base::FilePath file_path_1 = test_dir.Append("1.txt");
+ ASSERT_TRUE(base::WriteFile(file_path_1, "1", 1));
+ base::FilePath file_path_2 = test_dir.Append("2.txt");
+ ASSERT_TRUE(base::WriteFile(file_path_2, "22", 2));
+ base::FilePath file_path_3 = test_dir.Append("3.txt");
+ ASSERT_TRUE(base::WriteFile(file_path_3, "333", 3));
+
+ // List files for comparison.
+ auto result = ListFiles(test_dir.value());
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(result.has_value());
+ auto files = result.value();
+ EXPECT_EQ(3UL, files.size());
+ // Sort them offline for comparison.
+ std::sort(files.begin(), files.end());
+ EXPECT_EQ(file_path_1.value(), files[0]);
+ EXPECT_EQ(file_path_2.value(), files[1]);
+ EXPECT_EQ(file_path_3.value(), files[2]);
+
+ ASSERT_TRUE(base::DeleteFile(test_dir, true /* resursive */));
+}
+
+TEST(BasicUtilTest, ListFilesShouldDiscardSymlink) {
+ // Gets system tmp dir.
+ base::FilePath tmp_dir;
+ ASSERT_TRUE(GetTempDir(&tmp_dir));
+
+ // Creates a test dir for ListFiles testing.
+ base::FilePath test_dir;
+ ASSERT_TRUE(base::CreateTemporaryDirInDir(tmp_dir, "list-file-tests.", &test_dir));
+
+ // Generates dummy files to list.
+ base::FilePath file_path_1 = test_dir.Append("1.txt");
+ ASSERT_TRUE(base::WriteFile(file_path_1, "1", 1));
+ base::FilePath file_path_2 = test_dir.Append("2.txt");
+ ASSERT_TRUE(base::WriteFile(file_path_2, "22", 2));
+ // Creates a symlink and checks it won't be returned by ListFiles.
+ base::FilePath file_path_3 = test_dir.Append("3.txt");
+ base::FilePath non_existent_target = test_dir.Append("non_existent_target.txt");
+ ASSERT_TRUE(base::CreateSymbolicLink(non_existent_target, file_path_3));
+
+ // List files for comparison.
+ auto result = ListFiles(test_dir.value());
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(result.has_value());
+ auto files = result.value();
+ EXPECT_EQ(2UL, files.size()); // Should not include the symlink file.
+ // Sort them offline for comparison.
+ std::sort(files.begin(), files.end());
+ EXPECT_EQ(file_path_1.value(), files[0]);
+ EXPECT_EQ(file_path_2.value(), files[1]);
+
+ ASSERT_TRUE(base::DeleteFile(test_dir, true /* resursive */));
+}
+
+TEST(BasicUtilTest, ListFilesOpenDirFailure) {
+ // Gets system tmp dir.
+ base::FilePath tmp_dir;
+ ASSERT_TRUE(GetTempDir(&tmp_dir));
+
+ // Generates dummy files to list.
+ base::FilePath no_such_dir = tmp_dir.Append("not_such_dir");
+
+ auto fail = ListFiles(no_such_dir.value());
+ ASSERT_FALSE(fail);
+ EXPECT_EQ(ENOENT, fail.error().code());
+ EXPECT_TRUE(android::base::StartsWith(fail.error().message(), "Failed to opendir: "));
+}
+
+TEST(BasicUtilTest, ListFilesEmptyDir) {
+ // Gets system tmp dir.
+ base::FilePath tmp_dir;
+ ASSERT_TRUE(GetTempDir(&tmp_dir));
+
+ // Creates a test dir for ListFiles testing.
+ base::FilePath test_dir;
+ ASSERT_TRUE(base::CreateTemporaryDirInDir(tmp_dir, "list-file-tests.", &test_dir));
+
+ // List files without sorting.
+ auto result = ListFiles(test_dir.value());
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(result.has_value());
+ auto files = result.value();
+ EXPECT_EQ(0UL, files.size());
+
+ ASSERT_TRUE(base::DeleteFile(test_dir, true /* resursive */));
+}
+
} // namespace fs_avb_host_test
diff --git a/fs_mgr/libfs_avb/util.cpp b/fs_mgr/libfs_avb/util.cpp
index d214b5b..7783d04 100644
--- a/fs_mgr/libfs_avb/util.cpp
+++ b/fs_mgr/libfs_avb/util.cpp
@@ -16,10 +16,13 @@
#include "util.h"
+#include <dirent.h>
#include <sys/ioctl.h>
+#include <sys/types.h>
#include <thread>
+#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <linux/fs.h>
@@ -122,5 +125,23 @@
return ioctl(fd, BLKROSET, &ON) == 0;
}
+Result<std::vector<std::string>> ListFiles(const std::string& dir) {
+ struct dirent* de;
+ std::vector<std::string> files;
+
+ std::unique_ptr<DIR, int (*)(DIR*)> dirp(opendir(dir.c_str()), closedir);
+ if (!dirp) {
+ return ErrnoError() << "Failed to opendir: " << dir;
+ }
+
+ while ((de = readdir(dirp.get()))) {
+ if (de->d_type != DT_REG) continue;
+ std::string full_path = android::base::StringPrintf("%s/%s", dir.c_str(), de->d_name);
+ files.emplace_back(std::move(full_path));
+ }
+
+ return files;
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/libfs_avb/util.h b/fs_mgr/libfs_avb/util.h
index 7763da5..427ab7c 100644
--- a/fs_mgr/libfs_avb/util.h
+++ b/fs_mgr/libfs_avb/util.h
@@ -18,6 +18,7 @@
#include <chrono>
#include <string>
+#include <vector>
#ifdef HOST_TEST
#include <base/logging.h>
@@ -25,6 +26,11 @@
#include <android-base/logging.h>
#endif
+#include <android-base/result.h>
+
+using android::base::ErrnoError;
+using android::base::Result;
+
#define FS_AVB_TAG "[libfs_avb]"
// Logs a message to kernel
@@ -60,5 +66,8 @@
bool SetBlockDeviceReadOnly(const std::string& blockdev);
+// Returns a list of file under the dir, no order is guaranteed.
+Result<std::vector<std::string>> ListFiles(const std::string& dir);
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 1d72c70..30d01a6 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -121,6 +121,34 @@
],
}
+cc_library_static {
+ name: "libsnapshot_test_helpers",
+ defaults: ["libsnapshot_defaults"],
+ export_include_dirs: [
+ "include_test",
+ ],
+ srcs: [
+ "test_helpers.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.boot@1.1",
+ "libcrypto",
+ ],
+ export_shared_lib_headers: [
+ "android.hardware.boot@1.1",
+ ],
+ header_libs: [
+ "libstorage_literals_headers",
+ ],
+ export_header_lib_headers: [
+ "libstorage_literals_headers",
+ ],
+ static_libs: [
+ "libgtest",
+ "libgmock",
+ ],
+}
+
cc_test {
name: "libsnapshot_test",
defaults: ["libsnapshot_defaults"],
@@ -144,6 +172,7 @@
"libgmock",
"liblp",
"libsnapshot",
+ "libsnapshot_test_helpers",
"libsparse",
"libz",
],
diff --git a/fs_mgr/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
similarity index 100%
rename from fs_mgr/libsnapshot/test_helpers.h
rename to fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index eae6c35..9da3f05 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -18,9 +18,10 @@
#include <liblp/builder.h>
#include <liblp/property_fetcher.h>
+#include <libsnapshot/test_helpers.h>
+
#include "dm_snapshot_internals.h"
#include "partition_cow_creator.h"
-#include "test_helpers.h"
#include "utility.h"
using namespace android::fs_mgr;
diff --git a/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp b/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
index 4fd8759..337be4f 100644
--- a/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
@@ -24,7 +24,7 @@
#include <liblp/builder.h>
#include <storage_literals/storage_literals.h>
-#include "test_helpers.h"
+#include <libsnapshot/test_helpers.h>
using namespace android::storage_literals;
using android::fs_mgr::LpMetadata;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 9e5fef3..ff943f2 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -38,7 +38,7 @@
#include <storage_literals/storage_literals.h>
#include <android/snapshot/snapshot.pb.h>
-#include "test_helpers.h"
+#include <libsnapshot/test_helpers.h>
#include "utility.h"
namespace android {
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index 2d62347..f7f25af 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "test_helpers.h"
+#include <libsnapshot/test_helpers.h>
#include <android-base/file.h>
#include <android-base/logging.h>
diff --git a/init/Android.mk b/init/Android.mk
index 997b2bc..ee2d89a 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -73,9 +73,8 @@
LOCAL_REQUIRED_MODULES := \
adb_debug.prop \
-# Set up the same mount points on the ramdisk that system-as-root contains.
+# Set up the directories that first stage init mounts on.
LOCAL_POST_INSTALL_CMD := mkdir -p \
- $(TARGET_RAMDISK_OUT)/apex \
$(TARGET_RAMDISK_OUT)/debug_ramdisk \
$(TARGET_RAMDISK_OUT)/dev \
$(TARGET_RAMDISK_OUT)/mnt \
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 6bf3997..62a19ab 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -360,57 +360,61 @@
return {};
}
-// mkdir <path> [mode] [owner] [group] [<option> ...]
-static Result<void> do_mkdir(const BuiltinArguments& args) {
- auto options = ParseMkdir(args.args);
- if (!options) return options.error();
+static Result<void> make_dir_with_options(const MkdirOptions& options) {
std::string ref_basename;
- if (options->ref_option == "ref") {
+ if (options.ref_option == "ref") {
ref_basename = fscrypt_key_ref;
- } else if (options->ref_option == "per_boot_ref") {
+ } else if (options.ref_option == "per_boot_ref") {
ref_basename = fscrypt_key_per_boot_ref;
} else {
- return Error() << "Unknown key option: '" << options->ref_option << "'";
+ return Error() << "Unknown key option: '" << options.ref_option << "'";
}
struct stat mstat;
- if (lstat(options->target.c_str(), &mstat) != 0) {
+ if (lstat(options.target.c_str(), &mstat) != 0) {
if (errno != ENOENT) {
- return ErrnoError() << "lstat() failed on " << options->target;
+ return ErrnoError() << "lstat() failed on " << options.target;
}
- if (!make_dir(options->target, options->mode)) {
- return ErrnoErrorIgnoreEnoent() << "mkdir() failed on " << options->target;
+ if (!make_dir(options.target, options.mode)) {
+ return ErrnoErrorIgnoreEnoent() << "mkdir() failed on " << options.target;
}
- if (lstat(options->target.c_str(), &mstat) != 0) {
- return ErrnoError() << "lstat() failed on new " << options->target;
+ if (lstat(options.target.c_str(), &mstat) != 0) {
+ return ErrnoError() << "lstat() failed on new " << options.target;
}
}
if (!S_ISDIR(mstat.st_mode)) {
- return Error() << "Not a directory on " << options->target;
+ return Error() << "Not a directory on " << options.target;
}
- bool needs_chmod = (mstat.st_mode & ~S_IFMT) != options->mode;
- if ((options->uid != static_cast<uid_t>(-1) && options->uid != mstat.st_uid) ||
- (options->gid != static_cast<gid_t>(-1) && options->gid != mstat.st_gid)) {
- if (lchown(options->target.c_str(), options->uid, options->gid) == -1) {
- return ErrnoError() << "lchown failed on " << options->target;
+ bool needs_chmod = (mstat.st_mode & ~S_IFMT) != options.mode;
+ if ((options.uid != static_cast<uid_t>(-1) && options.uid != mstat.st_uid) ||
+ (options.gid != static_cast<gid_t>(-1) && options.gid != mstat.st_gid)) {
+ if (lchown(options.target.c_str(), options.uid, options.gid) == -1) {
+ return ErrnoError() << "lchown failed on " << options.target;
}
// chown may have cleared S_ISUID and S_ISGID, chmod again
needs_chmod = true;
}
if (needs_chmod) {
- if (fchmodat(AT_FDCWD, options->target.c_str(), options->mode, AT_SYMLINK_NOFOLLOW) == -1) {
- return ErrnoError() << "fchmodat() failed on " << options->target;
+ if (fchmodat(AT_FDCWD, options.target.c_str(), options.mode, AT_SYMLINK_NOFOLLOW) == -1) {
+ return ErrnoError() << "fchmodat() failed on " << options.target;
}
}
if (fscrypt_is_native()) {
- if (!FscryptSetDirectoryPolicy(ref_basename, options->fscrypt_action, options->target)) {
+ if (!FscryptSetDirectoryPolicy(ref_basename, options.fscrypt_action, options.target)) {
return reboot_into_recovery(
- {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + options->target});
+ {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + options.target});
}
}
return {};
}
+// mkdir <path> [mode] [owner] [group] [<option> ...]
+static Result<void> do_mkdir(const BuiltinArguments& args) {
+ auto options = ParseMkdir(args.args);
+ if (!options) return options.error();
+ return make_dir_with_options(*options);
+}
+
/* umount <path> */
static Result<void> do_umount(const BuiltinArguments& args) {
if (umount(args[1].c_str()) < 0) {
@@ -1172,7 +1176,7 @@
return {};
}
-static Result<void> do_parse_apex_configs(const BuiltinArguments& args) {
+static Result<void> parse_apex_configs() {
glob_t glob_result;
static constexpr char glob_pattern[] = "/apex/*/etc/*.rc";
const int ret = glob(glob_pattern, GLOB_MARK, nullptr, &glob_result);
@@ -1211,6 +1215,45 @@
}
}
+/*
+ * Creates a directory under /data/misc/apexdata/ for each APEX.
+ */
+static Result<void> create_apex_data_dirs() {
+ auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir("/apex"), closedir);
+ if (!dirp) {
+ return ErrnoError() << "Unable to open apex directory";
+ }
+ struct dirent* entry;
+ while ((entry = readdir(dirp.get())) != nullptr) {
+ if (entry->d_type != DT_DIR) continue;
+
+ const char* name = entry->d_name;
+ // skip any starting with "."
+ if (name[0] == '.') continue;
+
+ if (strchr(name, '@') != nullptr) continue;
+
+ auto path = "/data/misc/apexdata/" + std::string(name);
+ auto system_uid = DecodeUid("system");
+ auto options =
+ MkdirOptions{path, 0700, *system_uid, *system_uid, FscryptAction::kNone, "ref"};
+ make_dir_with_options(options);
+ }
+ return {};
+}
+
+static Result<void> do_perform_apex_config(const BuiltinArguments& args) {
+ auto create_dirs = create_apex_data_dirs();
+ if (!create_dirs) {
+ return create_dirs.error();
+ }
+ auto parse_configs = parse_apex_configs();
+ if (!parse_configs) {
+ return parse_configs.error();
+ }
+ return {};
+}
+
static Result<void> do_enter_default_mount_ns(const BuiltinArguments& args) {
if (SwitchToDefaultMountNamespace()) {
return {};
@@ -1271,7 +1314,7 @@
// mount and umount are run in the same context as mount_all for symmetry.
{"mount_all", {1, kMax, {false, do_mount_all}}},
{"mount", {3, kMax, {false, do_mount}}},
- {"parse_apex_configs", {0, 0, {false, do_parse_apex_configs}}},
+ {"perform_apex_config", {0, 0, {false, do_perform_apex_config}}},
{"umount", {1, 1, {false, do_umount}}},
{"umount_all", {1, 1, {false, do_umount_all}}},
{"readahead", {1, 2, {true, do_readahead}}},
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index ac44796..bd71cb5 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -204,10 +204,6 @@
// part of the product partition, e.g. because they are mounted read-write.
CHECKCALL(mkdir("/mnt/product", 0755));
- // /apex is used to mount APEXes
- CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
- "mode=0755,uid=0,gid=0"));
-
// /debug_ramdisk is used to preserve additional files from the debug ramdisk
CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"));
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index 3acc3cc..22de846 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -191,7 +191,7 @@
}
auto errors = std::vector<std::string>{};
- ParsePropertyInfoFile(file_contents, property_infos, &errors);
+ ParsePropertyInfoFile(file_contents, true, property_infos, &errors);
for (const auto& error : errors) {
LOG(ERROR) << "Could not read line from '" << filename << "': " << error;
}
diff --git a/init/init.cpp b/init/init.cpp
index 6ba64ee..5f97e44 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -512,10 +512,24 @@
static void UmountDebugRamdisk() {
if (umount("/debug_ramdisk") != 0) {
- LOG(ERROR) << "Failed to umount /debug_ramdisk";
+ PLOG(ERROR) << "Failed to umount /debug_ramdisk";
}
}
+static void MountExtraFilesystems() {
+#define CHECKCALL(x) \
+ if ((x) != 0) PLOG(FATAL) << #x " failed.";
+
+ // /apex is used to mount APEXes
+ CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+ "mode=0755,uid=0,gid=0"));
+
+ // /linkerconfig is used to keep generated linker configuration
+ CHECKCALL(mount("tmpfs", "/linkerconfig", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+ "mode=0755,uid=0,gid=0"));
+#undef CHECKCALL
+}
+
static void RecordStageBoottimes(const boot_clock::time_point& second_stage_start_time) {
int64_t first_stage_start_time_ns = -1;
if (auto first_stage_start_time_str = getenv(kEnvFirstStageStartedAt);
@@ -656,6 +670,9 @@
UmountDebugRamdisk();
}
+ // Mount extra filesystems required during second stage init
+ MountExtraFilesystems();
+
// Now set up SELinux for second stage.
SelinuxSetupKernelLogging();
SelabelInitialize();
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 940fb6b..c33e0de 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -172,6 +172,11 @@
// the bootstrap namespace get APEXes from the read-only partition.
if (!(MakePrivate("/apex"))) return false;
+ // /linkerconfig is a private mountpoint to give a different linker configuration
+ // based on the mount namespace. Subdirectory will be bind-mounted based on current mount
+ // namespace
+ if (!(MakePrivate("/linkerconfig"))) return false;
+
bootstrap_ns_fd.reset(OpenMountNamespace());
bootstrap_ns_id = GetMountNamespaceId();
diff --git a/init/property_service.cpp b/init/property_service.cpp
index adf8929..5b35ad2 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -925,7 +925,8 @@
}
auto errors = std::vector<std::string>{};
- ParsePropertyInfoFile(file_contents, property_infos, &errors);
+ bool require_prefix_or_exact = SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__;
+ ParsePropertyInfoFile(file_contents, require_prefix_or_exact, property_infos, &errors);
// Individual parsing errors are reported but do not cause a failed boot, which is what
// returning false would do here.
for (const auto& error : errors) {
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 2fc8f6d..852d6ca 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -531,6 +531,8 @@
selinux_android_restorecon("/dev/device-mapper", 0);
selinux_android_restorecon("/apex", 0);
+
+ selinux_android_restorecon("/linkerconfig", 0);
}
int SelinuxKlogCallback(int type, const char* fmt, ...) {
diff --git a/init/service.cpp b/init/service.cpp
index be46585..a97935e 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -326,6 +326,7 @@
<< (boot_completed ? "in 4 minutes" : "before boot completed");
// Notifies update_verifier and apexd
SetProperty("sys.init.updatable_crashing", "1");
+ SetProperty("sys.init.updatable_crashing_process_name", name_);
}
}
} else {
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 91bd52c..656d4dd 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -25,7 +25,6 @@
]
liblog_host_sources = [
"fake_log_device.cpp",
- "fake_writer.cpp",
]
liblog_target_sources = [
"event_tag_map.cpp",
diff --git a/liblog/fake_log_device.cpp b/liblog/fake_log_device.cpp
index f61bbdc..4143fa6 100644
--- a/liblog/fake_log_device.cpp
+++ b/liblog/fake_log_device.cpp
@@ -23,18 +23,20 @@
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
-#if !defined(_WIN32)
-#include <pthread.h>
-#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
+#include <mutex>
+
#include <android/log.h>
+#include <log/log_id.h>
+#include <log/logprint.h>
#include "log_portability.h"
+#include "logger.h"
#define kMaxTagLen 16 /* from the long-dead utils/Log.cpp */
@@ -46,37 +48,26 @@
#define TRACE(...) ((void)0)
#endif
-/* from the long-dead utils/Log.cpp */
-typedef enum {
- FORMAT_OFF = 0,
- FORMAT_BRIEF,
- FORMAT_PROCESS,
- FORMAT_TAG,
- FORMAT_THREAD,
- FORMAT_RAW,
- FORMAT_TIME,
- FORMAT_THREADTIME,
- FORMAT_LONG
-} LogFormat;
+static int FakeAvailable(log_id_t);
+static int FakeOpen();
+static void FakeClose();
+static int FakeWrite(log_id_t log_id, struct timespec* ts, struct iovec* vec, size_t nr);
-/*
- * Log driver state.
- */
+struct android_log_transport_write fakeLoggerWrite = {
+ .name = "fake",
+ .logMask = 0,
+ .available = FakeAvailable,
+ .open = FakeOpen,
+ .close = FakeClose,
+ .write = FakeWrite,
+};
+
typedef struct LogState {
- /* the fake fd that's seen by the user */
- int fakeFd;
-
- /* a printable name for this fake device */
- char debugName[sizeof("/dev/log/security")];
-
- /* nonzero if this is a binary log */
- int isBinary;
-
/* global minimum priority */
- int globalMinPriority;
+ int global_min_priority;
/* output format */
- LogFormat outputFormat;
+ AndroidLogPrintFormat output_format;
/* tags and priorities */
struct {
@@ -85,81 +76,18 @@
} tagSet[kTagSetSize];
} LogState;
-#if !defined(_WIN32)
/*
* Locking. Since we're emulating a device, we need to be prepared
* to have multiple callers at the same time. This lock is used
* to both protect the fd list and to prevent LogStates from being
* freed out from under a user.
*/
-static pthread_mutex_t fakeLogDeviceLock = PTHREAD_MUTEX_INITIALIZER;
+std::mutex mutex;
-static void lock() {
- /*
- * If we trigger a signal handler in the middle of locked activity and the
- * signal handler logs a message, we could get into a deadlock state.
- */
- pthread_mutex_lock(&fakeLogDeviceLock);
-}
+static LogState log_state;
-static void unlock() {
- pthread_mutex_unlock(&fakeLogDeviceLock);
-}
-
-#else // !defined(_WIN32)
-
-#define lock() ((void)0)
-#define unlock() ((void)0)
-
-#endif // !defined(_WIN32)
-
-/*
- * File descriptor management.
- */
-#define FAKE_FD_BASE 10000
-#define MAX_OPEN_LOGS 8
-static LogState openLogTable[MAX_OPEN_LOGS];
-
-/*
- * Allocate an fd and associate a new LogState with it.
- * The fd is available via the fakeFd field of the return value.
- */
-static LogState* createLogState() {
- size_t i;
-
- for (i = 0; i < (sizeof(openLogTable) / sizeof(openLogTable[0])); i++) {
- if (openLogTable[i].fakeFd == 0) {
- openLogTable[i].fakeFd = FAKE_FD_BASE + i;
- return &openLogTable[i];
- }
- }
- return NULL;
-}
-
-/*
- * Translate an fd to a LogState.
- */
-static LogState* fdToLogState(int fd) {
- if (fd >= FAKE_FD_BASE && fd < FAKE_FD_BASE + MAX_OPEN_LOGS) {
- return &openLogTable[fd - FAKE_FD_BASE];
- }
- return NULL;
-}
-
-/*
- * Unregister the fake fd and free the memory it pointed to.
- */
-static void deleteFakeFd(int fd) {
- LogState* ls;
-
- lock();
-
- ls = fdToLogState(fd);
- if (ls != NULL) {
- memset(&openLogTable[fd - FAKE_FD_BASE], 0, sizeof(openLogTable[0]));
- }
-
- unlock();
+static int FakeAvailable(log_id_t) {
+ return 0;
}
/*
@@ -175,19 +103,11 @@
* We also want to check ANDROID_PRINTF_LOG to determine how the output
* will look.
*/
-static void configureInitialState(const char* pathName, LogState* logState) {
- static const int kDevLogLen = sizeof("/dev/log/") - 1;
-
- strncpy(logState->debugName, pathName, sizeof(logState->debugName));
- logState->debugName[sizeof(logState->debugName) - 1] = '\0';
-
- /* identify binary logs */
- if (!strcmp(pathName + kDevLogLen, "events") || !strcmp(pathName + kDevLogLen, "security")) {
- logState->isBinary = 1;
- }
+int FakeOpen() {
+ std::lock_guard guard{mutex};
/* global min priority defaults to "info" level */
- logState->globalMinPriority = ANDROID_LOG_INFO;
+ log_state.global_min_priority = ANDROID_LOG_INFO;
/*
* This is based on the the long-dead utils/Log.cpp code.
@@ -209,7 +129,7 @@
}
if (i == kMaxTagLen) {
TRACE("ERROR: env tag too long (%d chars max)\n", kMaxTagLen - 1);
- return;
+ return 0;
}
tagName[i] = '\0';
@@ -260,16 +180,16 @@
if (*tags != '\0' && !isspace(*tags)) {
TRACE("ERROR: garbage in tag env; expected whitespace\n");
TRACE(" env='%s'\n", tags);
- return;
+ return 0;
}
}
if (tagName[0] == 0) {
- logState->globalMinPriority = minPrio;
+ log_state.global_min_priority = minPrio;
TRACE("+++ global min prio %d\n", logState->globalMinPriority);
} else {
- logState->tagSet[entry].minPriority = minPrio;
- strcpy(logState->tagSet[entry].tag, tagName);
+ log_state.tagSet[entry].minPriority = minPrio;
+ strcpy(log_state.tagSet[entry].tag, tagName);
TRACE("+++ entry %d: %s:%d\n", entry, logState->tagSet[entry].tag,
logState->tagSet[entry].minPriority);
entry++;
@@ -281,7 +201,7 @@
* Taken from the long-dead utils/Log.cpp
*/
const char* fstr = getenv("ANDROID_PRINTF_LOG");
- LogFormat format;
+ AndroidLogPrintFormat format;
if (fstr == NULL) {
format = FORMAT_BRIEF;
} else {
@@ -300,10 +220,11 @@
else if (strcmp(fstr, "long") == 0)
format = FORMAT_PROCESS;
else
- format = (LogFormat)atoi(fstr); // really?!
+ format = (AndroidLogPrintFormat)atoi(fstr); // really?!
}
- logState->outputFormat = format;
+ log_state.output_format = format;
+ return 0;
}
/*
@@ -348,7 +269,7 @@
*
* Log format parsing taken from the long-dead utils/Log.cpp.
*/
-static void showLog(LogState* state, int logPrio, const char* tag, const char* msg) {
+static void ShowLog(int logPrio, const char* tag, const char* msg) {
#if !defined(_WIN32)
struct tm tmBuf;
#endif
@@ -391,7 +312,7 @@
*/
size_t prefixLen, suffixLen;
- switch (state->outputFormat) {
+ switch (log_state.output_format) {
case FORMAT_TAG:
prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c/%-8s: ", priChar, tag);
strcpy(suffixBuf, "\n");
@@ -548,35 +469,24 @@
* tag (N bytes -- null-terminated ASCII string)
* message (N bytes -- null-terminated ASCII string)
*/
-ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count) {
- LogState* state;
-
+static int FakeWrite(log_id_t log_id, struct timespec*, struct iovec* vector, size_t count) {
/* Make sure that no-one frees the LogState while we're using it.
* Also guarantees that only one thread is in showLog() at a given
* time (if it matters).
*/
- lock();
+ std::lock_guard guard{mutex};
- state = fdToLogState(fd);
- if (state == NULL) {
- errno = EBADF;
- unlock();
- return -1;
- }
-
- if (state->isBinary) {
- TRACE("%s: ignoring binary log\n", state->debugName);
- unlock();
+ if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS || log_id == LOG_ID_SECURITY) {
+ TRACE("%s: ignoring binary log\n", android_log_id_to_name(log_id));
int len = 0;
- for (int i = 0; i < count; ++i) {
+ for (size_t i = 0; i < count; ++i) {
len += vector[i].iov_len;
}
return len;
}
if (count != 3) {
- TRACE("%s: writevLog with count=%d not expected\n", state->debugName, count);
- unlock();
+ TRACE("%s: writevLog with count=%d not expected\n", android_log_id_to_name(log_id), count);
return -1;
}
@@ -586,32 +496,30 @@
const char* msg = (const char*)vector[2].iov_base;
/* see if this log tag is configured */
- int i;
- int minPrio = state->globalMinPriority;
- for (i = 0; i < kTagSetSize; i++) {
- if (state->tagSet[i].minPriority == ANDROID_LOG_UNKNOWN)
+ int minPrio = log_state.global_min_priority;
+ for (size_t i = 0; i < kTagSetSize; i++) {
+ if (log_state.tagSet[i].minPriority == ANDROID_LOG_UNKNOWN)
break; /* reached end of configured values */
- if (strcmp(state->tagSet[i].tag, tag) == 0) {
- minPrio = state->tagSet[i].minPriority;
+ if (strcmp(log_state.tagSet[i].tag, tag) == 0) {
+ minPrio = log_state.tagSet[i].minPriority;
break;
}
}
if (logPrio >= minPrio) {
- showLog(state, logPrio, tag, msg);
+ ShowLog(logPrio, tag, msg);
}
- unlock();
int len = 0;
- for (i = 0; i < count; ++i) {
+ for (size_t i = 0; i < count; ++i) {
len += vector[i].iov_len;
}
return len;
}
/*
- * Free up our state and close the fake descriptor.
+ * Reset out state.
*
* The logger API has no means or need to 'stop' or 'close' using the logs,
* and as such, there is no way for that 'stop' or 'close' to translate into
@@ -623,31 +531,10 @@
* call is in the exit handler. Logging can continue in the exit handler to
* help debug HOST tools ...
*/
-int fakeLogClose(int fd) {
- deleteFakeFd(fd);
- return 0;
-}
+static void FakeClose() {
+ std::lock_guard guard{mutex};
-/*
- * Open a log output device and return a fake fd.
- */
-int fakeLogOpen(const char* pathName) {
- LogState* logState;
- int fd = -1;
-
- lock();
-
- logState = createLogState();
- if (logState != NULL) {
- configureInitialState(pathName, logState);
- fd = logState->fakeFd;
- } else {
- errno = ENFILE;
- }
-
- unlock();
-
- return fd;
+ memset(&log_state, 0, sizeof(log_state));
}
int __android_log_is_loggable(int prio, const char*, int def) {
diff --git a/liblog/fake_writer.cpp b/liblog/fake_writer.cpp
deleted file mode 100644
index f1ddff1..0000000
--- a/liblog/fake_writer.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2007-2016 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 <fcntl.h>
-#include <unistd.h>
-
-#include <log/log.h>
-
-#include "fake_log_device.h"
-#include "log_portability.h"
-#include "logger.h"
-
-static int fakeAvailable(log_id_t);
-static int fakeOpen();
-static void fakeClose();
-static int fakeWrite(log_id_t log_id, struct timespec* ts, struct iovec* vec, size_t nr);
-
-static int logFds[(int)LOG_ID_MAX] = {-1, -1, -1, -1, -1, -1};
-
-struct android_log_transport_write fakeLoggerWrite = {
- .name = "fake",
- .logMask = 0,
- .context.priv = &logFds,
- .available = fakeAvailable,
- .open = fakeOpen,
- .close = fakeClose,
- .write = fakeWrite,
-};
-
-static int fakeAvailable(log_id_t) {
- return 0;
-}
-
-static int fakeOpen() {
- int i;
-
- for (i = 0; i < LOG_ID_MAX; i++) {
- /*
- * Known maximum size string, plus an 8 character margin to deal with
- * possible independent changes to android_log_id_to_name().
- */
- char buf[sizeof("/dev/log_security") + 8];
- if (logFds[i] >= 0) {
- continue;
- }
- snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(static_cast<log_id_t>(i)));
- logFds[i] = fakeLogOpen(buf);
- if (logFds[i] < 0) {
- fprintf(stderr, "fakeLogOpen(%s) failed\n", buf);
- }
- }
- return 0;
-}
-
-static void fakeClose() {
- int i;
-
- for (i = 0; i < LOG_ID_MAX; i++) {
- fakeLogClose(logFds[i]);
- logFds[i] = -1;
- }
-}
-
-static int fakeWrite(log_id_t log_id, struct timespec*, struct iovec* vec, size_t nr) {
- ssize_t ret;
- size_t i;
- int logFd, len;
-
- if (/*(int)log_id >= 0 &&*/ (int)log_id >= (int)LOG_ID_MAX) {
- return -EINVAL;
- }
-
- len = 0;
- for (i = 0; i < nr; ++i) {
- len += vec[i].iov_len;
- }
-
- if (len > LOGGER_ENTRY_MAX_PAYLOAD) {
- len = LOGGER_ENTRY_MAX_PAYLOAD;
- }
-
- logFd = logFds[(int)log_id];
- ret = TEMP_FAILURE_RETRY(fakeLogWritev(logFd, vec, nr));
- if (ret < 0) {
- ret = -errno;
- } else if (ret > len) {
- ret = len;
- }
-
- return ret;
-}
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
index 394fa93..99df4ca 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -96,3 +96,11 @@
"vts",
],
}
+
+cc_test_host {
+ name: "liblog-host-test",
+ static_libs: ["liblog"],
+ shared_libs: ["libbase"],
+ srcs: ["liblog_host_test.cpp"],
+ isolated: true,
+}
diff --git a/liblog/tests/liblog_host_test.cpp b/liblog/tests/liblog_host_test.cpp
new file mode 100644
index 0000000..377550f
--- /dev/null
+++ b/liblog/tests/liblog_host_test.cpp
@@ -0,0 +1,195 @@
+/*
+ * 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 <log/log.h>
+#include <private/android_logger.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+using android::base::StringPrintf;
+using android::base::StringReplace;
+
+void GenerateLogContent() {
+ __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_VERBOSE, "tag", "verbose main");
+ __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO, "tag", "info main");
+ __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_ERROR, "tag", "error main");
+
+ __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, "tag", "verbose radio");
+ __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, "tag", "info radio");
+ __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, "tag", "error radio");
+
+ __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, "tag", "verbose system");
+ __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, "tag", "info system");
+ __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, "tag", "error system");
+
+ __android_log_buf_print(LOG_ID_CRASH, ANDROID_LOG_VERBOSE, "tag", "verbose crash");
+ __android_log_buf_print(LOG_ID_CRASH, ANDROID_LOG_INFO, "tag", "info crash");
+ __android_log_buf_print(LOG_ID_CRASH, ANDROID_LOG_ERROR, "tag", "error crash");
+}
+
+std::string GetPidString() {
+ int pid = getpid();
+ return StringPrintf("%5d", pid);
+}
+
+TEST(liblog, default_write) {
+ setenv("ANDROID_PRINTF_LOG", "brief", true);
+ CapturedStderr captured_stderr;
+
+ GenerateLogContent();
+
+ std::string expected_output = StringReplace(R"init(I/tag (<pid>): info main
+E/tag (<pid>): error main
+I/tag (<pid>): info radio
+E/tag (<pid>): error radio
+I/tag (<pid>): info system
+E/tag (<pid>): error system
+I/tag (<pid>): info crash
+E/tag (<pid>): error crash
+)init",
+ "<pid>", GetPidString(), true);
+
+ EXPECT_EQ(expected_output, captured_stderr.str());
+}
+
+TEST(liblog, format) {
+ setenv("ANDROID_PRINTF_LOG", "process", true);
+ CapturedStderr captured_stderr;
+
+ GenerateLogContent();
+
+ std::string expected_output = StringReplace(R"init(I(<pid>) info main (tag)
+E(<pid>) error main (tag)
+I(<pid>) info radio (tag)
+E(<pid>) error radio (tag)
+I(<pid>) info system (tag)
+E(<pid>) error system (tag)
+I(<pid>) info crash (tag)
+E(<pid>) error crash (tag)
+)init",
+ "<pid>", GetPidString(), true);
+
+ EXPECT_EQ(expected_output, captured_stderr.str());
+ captured_stderr.Stop();
+ captured_stderr.Reset();
+ captured_stderr.Start();
+
+ // Changing the environment after starting writing doesn't change the format.
+ setenv("ANDROID_PRINTF_LOG", "brief", true);
+ GenerateLogContent();
+ EXPECT_EQ(expected_output, captured_stderr.str());
+ captured_stderr.Stop();
+ captured_stderr.Reset();
+ captured_stderr.Start();
+
+ // However calling __android_log_close() does reset logging and allow changing the format.
+ __android_log_close();
+ GenerateLogContent();
+
+ expected_output = StringReplace(R"init(I/tag (<pid>): info main
+E/tag (<pid>): error main
+I/tag (<pid>): info radio
+E/tag (<pid>): error radio
+I/tag (<pid>): info system
+E/tag (<pid>): error system
+I/tag (<pid>): info crash
+E/tag (<pid>): error crash
+)init",
+ "<pid>", GetPidString(), true);
+
+ EXPECT_EQ(expected_output, captured_stderr.str());
+}
+
+TEST(liblog, filter) {
+ setenv("ANDROID_PRINTF_LOG", "brief", true);
+ setenv("ANDROID_LOG_TAGS", "*:w verbose_tag:v debug_tag:d", true);
+ CapturedStderr captured_stderr;
+
+ auto generate_logs = [](log_id_t log_id) {
+ // Check that we show verbose logs when requesting for a given tag.
+ __android_log_buf_print(log_id, ANDROID_LOG_VERBOSE, "verbose_tag", "verbose verbose_tag");
+ __android_log_buf_print(log_id, ANDROID_LOG_ERROR, "verbose_tag", "error verbose_tag");
+
+ // Check that we don't show verbose logs when explicitly requesting debug+ for a given tag.
+ __android_log_buf_print(log_id, ANDROID_LOG_VERBOSE, "debug_tag", "verbose debug_tag");
+ __android_log_buf_print(log_id, ANDROID_LOG_DEBUG, "debug_tag", "debug debug_tag");
+ __android_log_buf_print(log_id, ANDROID_LOG_ERROR, "debug_tag", "error debug_tag");
+
+ // Check that we don't show info logs when requesting globally warn+.
+ __android_log_buf_print(log_id, ANDROID_LOG_INFO, "default_tag", "info default_tag");
+ __android_log_buf_print(log_id, ANDROID_LOG_WARN, "default_tag", "warn default_tag");
+ __android_log_buf_print(log_id, ANDROID_LOG_ERROR, "default_tag", "error default_tag");
+ };
+
+ auto expected_output = StringReplace(R"init(V/verbose_tag(<pid>): verbose verbose_tag
+E/verbose_tag(<pid>): error verbose_tag
+D/debug_tag(<pid>): debug debug_tag
+E/debug_tag(<pid>): error debug_tag
+W/default_tag(<pid>): warn default_tag
+E/default_tag(<pid>): error default_tag
+)init",
+ "<pid>", GetPidString(), true);
+
+ auto test_all_logs = [&] {
+ for (auto log_id : {LOG_ID_MAIN, LOG_ID_SYSTEM, LOG_ID_RADIO, LOG_ID_CRASH}) {
+ generate_logs(log_id);
+ EXPECT_EQ(expected_output, captured_stderr.str());
+ captured_stderr.Stop();
+ captured_stderr.Reset();
+ captured_stderr.Start();
+ }
+ };
+
+ test_all_logs();
+
+ // Changing the environment after starting writing doesn't change the filter.
+ setenv("ANDROID_LOG_TAGS", "*:e", true);
+ test_all_logs();
+
+ // However calling __android_log_close() does reset logging and allow changing the format.
+ __android_log_close();
+ expected_output = StringReplace(R"init(E/verbose_tag(<pid>): error verbose_tag
+E/debug_tag(<pid>): error debug_tag
+E/default_tag(<pid>): error default_tag
+)init",
+ "<pid>", GetPidString(), true);
+ test_all_logs();
+}
+
+TEST(liblog, kernel_no_write) {
+ CapturedStderr captured_stderr;
+ __android_log_buf_print(LOG_ID_KERNEL, ANDROID_LOG_ERROR, "tag", "kernel error");
+ EXPECT_EQ("", captured_stderr.str());
+}
+
+TEST(liblog, binary_no_write) {
+ CapturedStderr captured_stderr;
+ __android_log_buf_print(LOG_ID_EVENTS, ANDROID_LOG_ERROR, "tag", "error events");
+ __android_log_buf_print(LOG_ID_STATS, ANDROID_LOG_ERROR, "tag", "error stats");
+ __android_log_buf_print(LOG_ID_SECURITY, ANDROID_LOG_ERROR, "tag", "error security");
+
+ __android_log_bswrite(0x12, "events");
+ __android_log_stats_bwrite(0x34, "stats", strlen("stats"));
+ __android_log_security_bswrite(0x56, "security");
+
+ EXPECT_EQ("", captured_stderr.str());
+}
diff --git a/libutils/Errors.cpp b/libutils/Errors.cpp
index 2dfd138..74f3bef 100644
--- a/libutils/Errors.cpp
+++ b/libutils/Errors.cpp
@@ -45,7 +45,7 @@
#undef STATUS_CASE
}
- return std::to_string(s) + ' ' + strerror(-s);
+ return std::to_string(s) + " (" + strerror(-s) + ")";
}
} // namespace android
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 3fc5e5d..7b18438 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -104,11 +104,6 @@
bool debug_ = false;
};
-// logd prefixes records with a length field
-#define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t)
-
-enum helpType { HELP_FALSE, HELP_TRUE, HELP_FORMAT };
-
#ifndef F2FS_IOC_SET_PIN_FILE
#define F2FS_IOCTL_MAGIC 0xf5
#define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32)
diff --git a/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h b/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h
index 439813d..dfb1d11 100644
--- a/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h
+++ b/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h
@@ -14,8 +14,7 @@
// limitations under the License.
//
-#ifndef PROPERTY_INFO_SERIALIZER_H
-#define PROPERTY_INFO_SERIALIZER_H
+#pragma once
#include <string>
#include <vector>
@@ -41,11 +40,9 @@
const std::string& default_context, const std::string& default_type,
std::string* serialized_trie, std::string* error);
-void ParsePropertyInfoFile(const std::string& file_contents,
+void ParsePropertyInfoFile(const std::string& file_contents, bool require_prefix_or_exact,
std::vector<PropertyInfoEntry>* property_infos,
std::vector<std::string>* errors);
} // namespace properties
} // namespace android
-
-#endif
diff --git a/property_service/libpropertyinfoserializer/property_info_file.cpp b/property_service/libpropertyinfoserializer/property_info_file.cpp
index 2cdc62d..771a9ce 100644
--- a/property_service/libpropertyinfoserializer/property_info_file.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_file.cpp
@@ -56,7 +56,8 @@
return false;
}
-bool ParsePropertyInfoLine(const std::string& line, PropertyInfoEntry* out, std::string* error) {
+bool ParsePropertyInfoLine(const std::string& line, bool require_prefix_or_exact,
+ PropertyInfoEntry* out, std::string* error) {
auto tokenizer = SpaceTokenizer(line);
auto property = tokenizer.GetNext();
@@ -72,7 +73,7 @@
}
// It is not an error to not find exact_match or a type, as older files will not contain them.
- auto exact_match = tokenizer.GetNext();
+ auto match_operation = tokenizer.GetNext();
// We reformat type to be space deliminated regardless of the input whitespace for easier storage
// and subsequent parsing.
auto type_strings = std::vector<std::string>{};
@@ -82,18 +83,27 @@
type = tokenizer.GetNext();
}
+ bool exact_match = false;
+ if (match_operation == "exact") {
+ exact_match = true;
+ } else if (match_operation != "prefix" && match_operation != "" && require_prefix_or_exact) {
+ *error = "Match operation '" + match_operation +
+ "' is not valid: must be either 'prefix' or 'exact'";
+ return false;
+ }
+
if (!type_strings.empty() && !IsTypeValid(type_strings)) {
*error = "Type '" + Join(type_strings, " ") + "' is not valid";
return false;
}
- *out = {property, context, Join(type_strings, " "), exact_match == "exact"};
+ *out = {property, context, Join(type_strings, " "), exact_match};
return true;
}
} // namespace
-void ParsePropertyInfoFile(const std::string& file_contents,
+void ParsePropertyInfoFile(const std::string& file_contents, bool require_prefix_or_exact,
std::vector<PropertyInfoEntry>* property_infos,
std::vector<std::string>* errors) {
// Do not clear property_infos to allow this function to be called on multiple files, with
@@ -108,7 +118,8 @@
auto property_info_entry = PropertyInfoEntry{};
auto parse_error = std::string{};
- if (!ParsePropertyInfoLine(trimmed_line, &property_info_entry, &parse_error)) {
+ if (!ParsePropertyInfoLine(trimmed_line, require_prefix_or_exact, &property_info_entry,
+ &parse_error)) {
errors->emplace_back(parse_error);
continue;
}
diff --git a/property_service/property_info_checker/property_info_checker.cpp b/property_service/property_info_checker/property_info_checker.cpp
index 52c4383..61b368e 100644
--- a/property_service/property_info_checker/property_info_checker.cpp
+++ b/property_service/property_info_checker/property_info_checker.cpp
@@ -153,7 +153,7 @@
}
auto errors = std::vector<std::string>{};
- ParsePropertyInfoFile(file_contents, &property_info_entries, &errors);
+ ParsePropertyInfoFile(file_contents, true, &property_info_entries, &errors);
if (!errors.empty()) {
for (const auto& error : errors) {
std::cerr << "Could not read line from '" << filename << "': " << error << std::endl;
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 994d9ae..ebc0cde 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -72,11 +72,12 @@
#
# create some directories (some are mount points) and symlinks
LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
- dev proc sys system data odm oem acct config storage mnt apex debug_ramdisk $(BOARD_ROOT_EXTRA_FOLDERS)); \
+ dev proc sys system data odm oem acct config storage mnt apex debug_ramdisk \
+ linkerconfig $(BOARD_ROOT_EXTRA_FOLDERS)); \
ln -sf /system/bin $(TARGET_ROOT_OUT)/bin; \
ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \
- ln -sf /sys/kernel/debug $(TARGET_ROOT_OUT)/d; \
+ ln -sfn /sys/kernel/debug $(TARGET_ROOT_OUT)/d; \
ln -sf /storage/self/primary $(TARGET_ROOT_OUT)/sdcard
ifdef BOARD_USES_VENDORIMAGE
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/vendor
diff --git a/rootdir/init.rc b/rootdir/init.rc
index e2ecad4..2ec0669 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -38,12 +38,9 @@
# Allow up to 32K FDs per process
setrlimit nofile 32768 32768
- # Create directory to keep ld.config.txt
- mkdir /dev/linkerconfig 0755
-
# Generate ld.config.txt for early executed processes
- exec -- /system/bin/linkerconfig --target /dev/linkerconfig/ld.config.txt
- chmod 444 /dev/linkerconfig/ld.config.txt
+ exec -- /system/bin/linkerconfig --target /linkerconfig/ld.config.txt
+ chmod 444 /linkerconfig/ld.config.txt
start ueventd
@@ -579,6 +576,8 @@
mkdir /data/misc/profman 0770 system shell
mkdir /data/misc/gcov 0770 root root
mkdir /data/misc/installd 0700 root root
+ mkdir /data/misc/apexdata 0700 root root
+ mkdir /data/misc/apexrollback 0700 root root
mkdir /data/preloads 0775 system system encryption=None
@@ -668,7 +667,8 @@
# Wait for apexd to finish activating APEXes before starting more processes.
wait_for_prop apexd.status ready
- parse_apex_configs
+ perform_apex_config
+
exec_start derive_sdk
init_user0
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index c61f7d0..5f56408 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -46,6 +46,8 @@
return MMC_RPMB;
} else if (!strcmp(dev_type_name, "virt")) {
return VIRT_RPMB;
+ } else if (!strcmp(dev_type_name, "sock")) {
+ return SOCK_RPMB;
} else {
return UNKNOWN_RPMB;
}
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index 29827e2..0bd9e68 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -21,6 +21,8 @@
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
#include <unistd.h>
#include <linux/major.h>
@@ -192,7 +194,7 @@
msg->result = STORAGE_ERR_GENERIC;
goto err_response;
}
- } else if (dev_type == VIRT_RPMB) {
+ } else if ((dev_type == VIRT_RPMB) || (dev_type == SOCK_RPMB)) {
size_t payload_size = req->reliable_write_size + req->write_size;
rc = send_virt_rpmb_req(rpmb_fd, read_buf, req->read_size, req->payload, payload_size);
if (rc < 0) {
@@ -234,12 +236,33 @@
int rc;
dev_type = open_dev_type;
- rc = open(rpmb_devname, O_RDWR, 0);
- if (rc < 0) {
- ALOGE("unable (%d) to open rpmb device '%s': %s\n", errno, rpmb_devname, strerror(errno));
- return rc;
+ if (dev_type != SOCK_RPMB) {
+ rc = open(rpmb_devname, O_RDWR, 0);
+ if (rc < 0) {
+ ALOGE("unable (%d) to open rpmb device '%s': %s\n", errno, rpmb_devname, strerror(errno));
+ return rc;
+ }
+ rpmb_fd = rc;
+ } else {
+ struct sockaddr_un unaddr;
+ struct sockaddr *addr = (struct sockaddr *)&unaddr;
+ rc = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (rc < 0) {
+ ALOGE("unable (%d) to create socket: %s\n", errno, strerror(errno));
+ return rc;
+ }
+ rpmb_fd = rc;
+
+ memset(&unaddr, 0, sizeof(unaddr));
+ unaddr.sun_family = AF_UNIX;
+ // TODO if it overflowed, bail rather than connecting?
+ strncpy(unaddr.sun_path, rpmb_devname, sizeof(unaddr.sun_path)-1);
+ rc = connect(rpmb_fd, addr, sizeof(unaddr));
+ if (rc < 0) {
+ ALOGE("unable (%d) to connect to rpmb socket '%s': %s\n", errno, rpmb_devname, strerror(errno));
+ return rc;
+ }
}
- rpmb_fd = rc;
return 0;
}
diff --git a/trusty/storage/proxy/rpmb.h b/trusty/storage/proxy/rpmb.h
index 4c330c9..09af3c5 100644
--- a/trusty/storage/proxy/rpmb.h
+++ b/trusty/storage/proxy/rpmb.h
@@ -18,7 +18,7 @@
#include <stdint.h>
#include <trusty/interface/storage.h>
-enum dev_type { UNKNOWN_RPMB, MMC_RPMB, VIRT_RPMB };
+enum dev_type { UNKNOWN_RPMB, MMC_RPMB, VIRT_RPMB, SOCK_RPMB };
int rpmb_open(const char* rpmb_devname, enum dev_type dev_type);
int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len);
diff --git a/trusty/utils/rpmb_dev/Android.bp b/trusty/utils/rpmb_dev/Android.bp
new file mode 100644
index 0000000..e923e82
--- /dev/null
+++ b/trusty/utils/rpmb_dev/Android.bp
@@ -0,0 +1,33 @@
+// 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.
+
+cc_binary {
+ name: "rpmb_dev",
+ vendor: true,
+
+ srcs: [
+ "rpmb_dev.c",
+ ],
+ shared_libs: [
+ "libc",
+ "liblog",
+ "libcrypto",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ init_rc: [
+ "rpmb_dev.rc",
+ ],
+}
diff --git a/trusty/utils/rpmb_dev/rpmb.h b/trusty/utils/rpmb_dev/rpmb.h
new file mode 100644
index 0000000..ab7e8d8
--- /dev/null
+++ b/trusty/utils/rpmb_dev/rpmb.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef __RPMB_H__
+#define __RPMB_H__
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+struct rpmb_key {
+ uint8_t byte[32];
+};
+
+struct rpmb_state;
+
+#define RPMB_BUF_SIZE 256
+
+/* provides */
+int rpmb_init(struct rpmb_state** statep,
+ void* mmc_handle,
+ const struct rpmb_key* key);
+void rpmb_uninit(struct rpmb_state* statep);
+int rpmb_read(struct rpmb_state* state,
+ void* buf,
+ uint16_t addr,
+ uint16_t count);
+/* count must be 1 or 2, addr must be aligned */
+int rpmb_write(struct rpmb_state* state,
+ const void* buf,
+ uint16_t addr,
+ uint16_t count,
+ bool sync);
+
+/* needs */
+int rpmb_send(void* mmc_handle,
+ void* reliable_write_buf,
+ size_t reliable_write_size,
+ void* write_buf,
+ size_t write_buf_size,
+ void* read_buf,
+ size_t read_buf_size,
+ bool sync);
+
+#endif
diff --git a/trusty/utils/rpmb_dev/rpmb_dev.c b/trusty/utils/rpmb_dev/rpmb_dev.c
new file mode 100644
index 0000000..af97eba
--- /dev/null
+++ b/trusty/utils/rpmb_dev/rpmb_dev.c
@@ -0,0 +1,657 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define LOG_TAG "rpmb_mock"
+
+#include "rpmb_protocol.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <log/log.h>
+#include <openssl/hmac.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+/* verbose is an int for getopt */
+static int verbose = false;
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+
+HMAC_CTX* HMAC_CTX_new(void) {
+ HMAC_CTX* ctx = malloc(sizeof(*ctx));
+ if (ctx != NULL) {
+ HMAC_CTX_init(ctx);
+ }
+ return ctx;
+}
+
+void HMAC_CTX_free(HMAC_CTX* ctx) {
+ if (ctx != NULL) {
+ HMAC_CTX_cleanup(ctx);
+ free(ctx);
+ }
+}
+
+#endif
+
+#define MAX_WRITE_COUNTER (0xffffffff)
+
+struct rpmb_data_header {
+ uint32_t write_counter;
+ uint16_t max_block;
+ uint8_t pad1;
+ uint8_t key_programmed;
+ struct rpmb_key key;
+ uint8_t pad[512 - 4 - 2 - 1 - 1 - sizeof(struct rpmb_key)];
+};
+
+#define MAX_PACKET_COUNT (8)
+
+struct rpmb_dev_state {
+ struct rpmb_data_header header;
+ struct rpmb_packet cmd[MAX_PACKET_COUNT];
+ struct rpmb_packet res[MAX_PACKET_COUNT];
+ uint16_t cmd_count;
+ uint16_t res_count;
+ int data_fd;
+};
+
+/* TODO: move to common location */
+static int rpmb_mac(struct rpmb_key key, struct rpmb_packet* packet, size_t packet_count,
+ struct rpmb_key* mac) {
+ size_t i;
+ int hmac_ret;
+ unsigned int md_len;
+ HMAC_CTX* hmac_ctx;
+
+ hmac_ctx = HMAC_CTX_new();
+ hmac_ret = HMAC_Init_ex(hmac_ctx, &key, sizeof(key), EVP_sha256(), NULL);
+ if (!hmac_ret) {
+ ALOGE("HMAC_Init_ex failed\n");
+ goto err;
+ }
+ for (i = 0; i < packet_count; i++) {
+ hmac_ret = HMAC_Update(hmac_ctx, packet[i].data, 284);
+ if (!hmac_ret) {
+ ALOGE("HMAC_Update failed\n");
+ goto err;
+ }
+ }
+ hmac_ret = HMAC_Final(hmac_ctx, mac->byte, &md_len);
+ if (md_len != sizeof(mac->byte)) {
+ ALOGE("bad md_len %d != %zd\n", md_len, sizeof(mac->byte));
+ exit(1);
+ }
+ if (!hmac_ret) {
+ ALOGE("HMAC_Final failed\n");
+ goto err;
+ }
+
+err:
+ HMAC_CTX_free(hmac_ctx);
+ return hmac_ret ? 0 : -1;
+}
+
+static int rpmb_file_seek(struct rpmb_dev_state* s, uint16_t addr) {
+ int ret;
+ int pos = addr * RPMB_PACKET_DATA_SIZE + sizeof(s->header);
+ ret = lseek(s->data_fd, pos, SEEK_SET);
+ if (ret != pos) {
+ ALOGE("rpmb_dev: seek to %d failed, got %d\n", pos, ret);
+ return -1;
+ }
+ return 0;
+}
+
+static uint16_t rpmb_dev_program_key(struct rpmb_dev_state* s) {
+ int ret;
+
+ if (s->header.key_programmed) {
+ return RPMB_RES_WRITE_FAILURE;
+ }
+
+ s->header.key = s->cmd[0].key_mac;
+ s->header.key_programmed = 1;
+
+ ret = lseek(s->data_fd, 0, SEEK_SET);
+ if (ret) {
+ ALOGE("rpmb_dev: Failed to seek rpmb data file\n");
+ return RPMB_RES_WRITE_FAILURE;
+ }
+
+ ret = write(s->data_fd, &s->header, sizeof(s->header));
+ if (ret != sizeof(s->header)) {
+ ALOGE("rpmb_dev: Failed to write rpmb key: %d, %s\n", ret, strerror(errno));
+
+ return RPMB_RES_WRITE_FAILURE;
+ }
+
+ return RPMB_RES_OK;
+}
+
+static uint16_t rpmb_dev_get_counter(struct rpmb_dev_state* s) {
+ s->res[0].write_counter = rpmb_u32(s->header.write_counter);
+
+ return RPMB_RES_OK;
+}
+
+static uint16_t rpmb_dev_data_write(struct rpmb_dev_state* s) {
+ uint16_t addr = rpmb_get_u16(s->cmd[0].address);
+ uint16_t block_count = s->cmd_count;
+ uint32_t write_counter;
+ int ret;
+
+ if (s->header.write_counter == MAX_WRITE_COUNTER) {
+ if (verbose) {
+ ALOGE("rpmb_dev: Write counter expired\n");
+ }
+ return RPMB_RES_WRITE_FAILURE;
+ }
+
+ write_counter = rpmb_get_u32(s->cmd[0].write_counter);
+ if (s->header.write_counter != write_counter) {
+ if (verbose) {
+ ALOGE("rpmb_dev: Invalid write counter %u. Expected: %u\n", write_counter,
+ s->header.write_counter);
+ }
+ return RPMB_RES_COUNT_FAILURE;
+ }
+
+ ret = rpmb_file_seek(s, addr);
+ if (ret) {
+ ALOGE("rpmb_dev: Failed to seek rpmb data file\n");
+ return RPMB_RES_WRITE_FAILURE;
+ }
+
+ for (int i = 0; i < block_count; i++) {
+ ret = write(s->data_fd, s->cmd[i].data, RPMB_PACKET_DATA_SIZE);
+ if (ret != RPMB_PACKET_DATA_SIZE) {
+ ALOGE("rpmb_dev: Failed to write rpmb data file: %d, %s\n", ret, strerror(errno));
+ return RPMB_RES_WRITE_FAILURE;
+ }
+ }
+
+ s->header.write_counter++;
+
+ ret = lseek(s->data_fd, 0, SEEK_SET);
+ if (ret) {
+ ALOGE("rpmb_dev: Failed to seek rpmb data file\n");
+ return RPMB_RES_WRITE_FAILURE;
+ }
+
+ ret = write(s->data_fd, &s->header.write_counter, sizeof(s->header.write_counter));
+ if (ret != sizeof(s->header.write_counter)) {
+ ALOGE("rpmb_dev: Failed to write rpmb write counter: %d, %s\n", ret, strerror(errno));
+
+ return RPMB_RES_WRITE_FAILURE;
+ }
+
+ s->res[0].write_counter = rpmb_u32(s->header.write_counter);
+ return RPMB_RES_OK;
+}
+
+static uint16_t rpmb_dev_data_read(struct rpmb_dev_state* s) {
+ uint16_t addr;
+ uint16_t block_count;
+ int ret;
+
+ addr = rpmb_get_u16(s->cmd[0].address);
+ block_count = s->res_count;
+
+ rpmb_file_seek(s, addr);
+
+ for (int i = 0; i < block_count; i++) {
+ ret = read(s->data_fd, s->res[i].data, RPMB_PACKET_DATA_SIZE);
+ if (ret != 0 && ret != RPMB_PACKET_DATA_SIZE) {
+ ALOGE("rpmb_dev: Failed to read rpmb data file: %d, %s\n", ret, strerror(errno));
+ return RPMB_RES_READ_FAILURE;
+ }
+ }
+
+ return RPMB_RES_OK;
+}
+
+struct rpmb_dev_cmd {
+ uint16_t (*func)(struct rpmb_dev_state* s);
+ uint16_t resp;
+ bool key_mac_is_key;
+ bool check_mac;
+ bool check_result_read;
+ bool check_key_programmed;
+ bool check_addr;
+ bool multi_packet_cmd;
+ bool multi_packet_res;
+ bool res_mac;
+};
+
+static struct rpmb_dev_cmd rpmb_dev_cmd_table[] = {
+ [RPMB_REQ_PROGRAM_KEY] =
+ {
+ .func = rpmb_dev_program_key,
+ .resp = RPMB_RESP_PROGRAM_KEY,
+ .key_mac_is_key = true,
+ .check_result_read = true,
+ },
+ [RPMB_REQ_GET_COUNTER] =
+ {
+ .func = rpmb_dev_get_counter,
+ .resp = RPMB_RESP_GET_COUNTER,
+ .check_key_programmed = true,
+ .res_mac = true,
+ },
+ [RPMB_REQ_DATA_WRITE] =
+ {
+ .func = rpmb_dev_data_write,
+ .resp = RPMB_RESP_DATA_WRITE,
+ .check_mac = true,
+ .check_result_read = true,
+ .check_key_programmed = true,
+ .check_addr = true,
+ .multi_packet_cmd = true,
+ .res_mac = true,
+ },
+ [RPMB_REQ_DATA_READ] =
+ {
+ .func = rpmb_dev_data_read,
+ .resp = RPMB_RESP_DATA_READ,
+ .check_addr = true,
+ .multi_packet_res = true,
+ .res_mac = true,
+ },
+};
+
+#define countof(arr) (sizeof(arr) / sizeof(arr[0]))
+
+static void rpmb_dev_process_cmd(struct rpmb_dev_state* s) {
+ assert(s->cmd_count > 0);
+ assert(s->res_count > 0);
+ uint16_t req_resp = rpmb_get_u16(s->cmd[0].req_resp);
+ uint16_t addr = rpmb_get_u16(s->cmd[0].address);
+ uint16_t sub_req;
+ uint16_t cmd_index = req_resp < countof(rpmb_dev_cmd_table) ? req_resp : 0;
+ struct rpmb_dev_cmd* cmd = &rpmb_dev_cmd_table[cmd_index];
+ uint16_t result = RPMB_RES_GENERAL_FAILURE;
+ struct rpmb_key mac;
+ uint16_t block_count = 0;
+
+ if (cmd->check_result_read) {
+ sub_req = rpmb_get_u16(s->cmd[s->cmd_count - 1].req_resp);
+ if (sub_req != RPMB_REQ_RESULT_READ) {
+ if (verbose) {
+ ALOGE("rpmb_dev: Request %d, missing result read request, got %d, cmd_count %d\n",
+ req_resp, sub_req, s->cmd_count);
+ }
+ goto err;
+ }
+ assert(s->cmd_count > 1);
+ s->cmd_count--;
+ }
+
+ if (cmd->check_mac) {
+ if (rpmb_mac(s->header.key, s->cmd, s->cmd_count, &mac) != 0) {
+ ALOGE("rpmb_dev: failed to caclulate mac\n");
+ goto err;
+ }
+ } else if (cmd->key_mac_is_key) {
+ mac = s->cmd[s->cmd_count - 1].key_mac;
+ } else {
+ memset(mac.byte, 0, sizeof(mac.byte));
+ }
+
+ if (memcmp(&mac, s->cmd[s->cmd_count - 1].key_mac.byte, sizeof(mac))) {
+ if (verbose) {
+ ALOGE("rpmb_dev: Request %d, invalid MAC, cmd_count %d\n", req_resp, s->cmd_count);
+ }
+ if (cmd->check_mac) {
+ result = RPMB_RES_AUTH_FAILURE;
+ }
+ goto err;
+ }
+
+ if (cmd->multi_packet_cmd) {
+ block_count = s->cmd_count;
+ }
+ if (cmd->multi_packet_res) {
+ block_count = s->res_count;
+ }
+
+ if (cmd->check_addr && (addr + block_count > s->header.max_block + 1)) {
+ if (verbose) {
+ ALOGE("rpmb_dev: Request %d, invalid addr: 0x%x count 0x%x, Out of bounds. Max addr "
+ "0x%x\n",
+ req_resp, addr, block_count, s->header.max_block + 1);
+ }
+ result = RPMB_RES_ADDR_FAILURE;
+ goto err;
+ }
+ if (!cmd->check_addr && addr) {
+ if (verbose) {
+ ALOGE("rpmb_dev: Request %d, invalid addr: 0x%x != 0\n", req_resp, addr);
+ }
+ goto err;
+ }
+
+ for (int i = 1; i < s->cmd_count; i++) {
+ sub_req = rpmb_get_u16(s->cmd[i].req_resp);
+ if (sub_req != req_resp) {
+ if (verbose) {
+ ALOGE("rpmb_dev: Request %d, sub-request mismatch, %d, at %d\n", req_resp, i,
+ sub_req);
+ }
+ goto err;
+ }
+ }
+ if (!cmd->multi_packet_cmd && s->cmd_count != 1) {
+ if (verbose) {
+ ALOGE("rpmb_dev: Request %d, bad cmd count %d, expected 1\n", req_resp, s->cmd_count);
+ }
+ goto err;
+ }
+ if (!cmd->multi_packet_res && s->res_count != 1) {
+ if (verbose) {
+ ALOGE("rpmb_dev: Request %d, bad res count %d, expected 1\n", req_resp, s->res_count);
+ }
+ goto err;
+ }
+
+ if (cmd->check_key_programmed && !s->header.key_programmed) {
+ if (verbose) {
+ ALOGE("rpmb_dev: Request %d, key is not programmed\n", req_resp);
+ }
+ s->res[0].result = rpmb_u16(RPMB_RES_NO_AUTH_KEY);
+ return;
+ }
+
+ if (!cmd->func) {
+ if (verbose) {
+ ALOGE("rpmb_dev: Unsupported request: %d\n", req_resp);
+ }
+ goto err;
+ }
+
+ result = cmd->func(s);
+
+err:
+ if (s->header.write_counter == MAX_WRITE_COUNTER) {
+ result |= RPMB_RES_WRITE_COUNTER_EXPIRED;
+ }
+
+ for (int i = 0; i < s->res_count; i++) {
+ s->res[i].nonce = s->cmd[0].nonce;
+ s->res[i].address = rpmb_u16(addr);
+ s->res[i].block_count = rpmb_u16(block_count);
+ s->res[i].result = rpmb_u16(result);
+ s->res[i].req_resp = rpmb_u16(cmd->resp);
+ }
+ if (cmd->res_mac) {
+ rpmb_mac(s->header.key, s->res, s->res_count, &s->res[s->res_count - 1].key_mac);
+ }
+}
+
+/*
+ * Receives data until one of the following is true:
+ * - The buffer is full (return will be len)
+ * - The connection closed (return > 0, < len)
+ * - An error occurred (return will be the negative error code from recv)
+ */
+ssize_t recv_until(int sock, void* dest_in, size_t len) {
+ size_t bytes_recvd = 0;
+ char* dest = dest_in;
+ while (bytes_recvd < len) {
+ ssize_t ret = recv(sock, dest, len - bytes_recvd, 0);
+ if (ret < 0) {
+ return ret;
+ }
+ dest += ret;
+ bytes_recvd += ret;
+ if (ret == 0) {
+ break;
+ }
+ }
+ return bytes_recvd;
+}
+
+/*
+ * Handles an incoming connection to the rpmb daemon.
+ * Returns 0 if the client disconnects without violating the protocol.
+ * Returns a negative value if we terminated the connection abnormally.
+ *
+ * Arguments:
+ * conn_sock - an fd to send/recv on
+ * s - an initialized rpmb device
+ */
+int handle_conn(struct rpmb_dev_state* s, int conn_sock) {
+ int ret;
+
+ while (true) {
+ memset(s->res, 0, sizeof(s->res));
+ ret = recv_until(conn_sock, &s->res_count, sizeof(s->res_count));
+
+ /*
+ * Disconnected while not in the middle of anything.
+ */
+ if (ret <= 0) {
+ return 0;
+ }
+
+ if (s->res_count > MAX_PACKET_COUNT) {
+ ALOGE("rpmb_dev: Receive count too large: %d\n", s->res_count);
+ return -1;
+ }
+ if (s->res_count <= 0) {
+ ALOGE("rpmb_dev: Receive count too small: %d\n", s->res_count);
+ return -1;
+ }
+
+ ret = recv_until(conn_sock, &s->cmd_count, sizeof(s->cmd_count));
+ if (ret != sizeof(s->cmd_count)) {
+ ALOGE("rpmb_dev: Failed to read cmd_count");
+ return -1;
+ }
+
+ if (s->cmd_count == 0) {
+ ALOGE("rpmb_dev: Must contain at least one command\n");
+ return -1;
+ }
+
+ if (s->cmd_count > MAX_PACKET_COUNT) {
+ ALOGE("rpmb_dev: Command count is too large\n");
+ return -1;
+ }
+
+ size_t cmd_size = s->cmd_count * sizeof(s->cmd[0]);
+ ret = recv_until(conn_sock, s->cmd, cmd_size);
+ if (ret != (int)cmd_size) {
+ ALOGE("rpmb_dev: Failed to read command: "
+ "cmd_size: %zu ret: %d, %s\n",
+ cmd_size, ret, strerror(errno));
+ return -1;
+ }
+
+ rpmb_dev_process_cmd(s);
+
+ size_t resp_size = sizeof(s->res[0]) * s->res_count;
+ ret = send(conn_sock, s->res, resp_size, 0);
+ if (ret != (int)resp_size) {
+ ALOGE("rpmb_dev: Failed to send response: %d, %s\n", ret, strerror(errno));
+ return -1;
+ }
+ }
+}
+
+void usage(const char* argv0) {
+ fprintf(stderr, "Usage: %s [-d|--dev] <datafile> [--sock] <socket_path>\n", argv0);
+ fprintf(stderr, "or: %s [-d|--dev] <datafile> [--size <size>] [--key key]\n", argv0);
+}
+
+int main(int argc, char** argv) {
+ struct rpmb_dev_state s;
+ int ret;
+ int cmdres_sock;
+ struct sockaddr_un cmdres_sockaddr;
+ const char* data_file_name = NULL;
+ const char* socket_path = NULL;
+ int open_flags;
+ int init = false;
+
+ struct option long_options[] = {{"size", required_argument, 0, 0},
+ {"key", required_argument, 0, 0},
+ {"sock", required_argument, 0, 0},
+ {"dev", required_argument, 0, 'd'},
+ {"init", no_argument, &init, true},
+ {"verbose", no_argument, &verbose, true},
+ {0, 0, 0, 0}};
+
+ memset(&s.header, 0, sizeof(s.header));
+
+ while (1) {
+ int c;
+ int option_index = 0;
+ c = getopt_long(argc, argv, "d:", long_options, &option_index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ /* long args */
+ case 0:
+ switch (option_index) {
+ /* size */
+ case 0:
+ s.header.max_block = atoi(optarg) - 1;
+ break;
+ /* key */
+ case 1:
+ for (size_t i = 0; i < sizeof(s.header.key.byte); i++) {
+ if (!optarg) {
+ break;
+ }
+ s.header.key.byte[i] = strtol(optarg, &optarg, 16);
+ s.header.key_programmed = 1;
+ }
+ break;
+ /* sock */
+ case 2:
+ socket_path = optarg;
+ break;
+ }
+ break;
+ /* dev */
+ case 'd':
+ data_file_name = optarg;
+ break;
+ default:
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+ }
+
+ /*
+ * We always need a data file, and at exactly one of --init or --sock
+ * must be specified.
+ */
+ if (!data_file_name || (!init == !socket_path)) {
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ /*
+ * If the file is already initialized, exit early.
+ */
+ if (init && !access(data_file_name, F_OK)) {
+ return EXIT_SUCCESS;
+ }
+
+ open_flags = O_RDWR;
+ if (init) {
+ open_flags |= O_CREAT | O_TRUNC;
+ }
+ s.data_fd = open(data_file_name, open_flags, S_IWUSR | S_IRUSR);
+ if (s.data_fd < 0) {
+ ALOGE("rpmb_dev: Failed to open rpmb data file, %s: %s\n", data_file_name, strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ if (init) {
+ /* Create new rpmb data file */
+ if (s.header.max_block == 0) {
+ s.header.max_block = 512 - 1;
+ }
+ ret = write(s.data_fd, &s.header, sizeof(s.header));
+ if (ret != sizeof(s.header)) {
+ ALOGE("rpmb_dev: Failed to write rpmb data file: %d, %s\n", ret, strerror(errno));
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+ }
+
+ ret = read(s.data_fd, &s.header, sizeof(s.header));
+ if (ret != sizeof(s.header)) {
+ ALOGE("rpmb_dev: Failed to read rpmb data file: %d, %s\n", ret, strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ cmdres_sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (cmdres_sock < 0) {
+ ALOGE("rpmb_dev: Failed to create command/response socket: %s\n", strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ cmdres_sockaddr.sun_family = AF_UNIX;
+ strncpy(cmdres_sockaddr.sun_path, socket_path, sizeof(cmdres_sockaddr.sun_path));
+
+ ret = bind(cmdres_sock, (struct sockaddr*)&cmdres_sockaddr, sizeof(struct sockaddr_un));
+ if (ret < 0) {
+ ALOGE("rpmb_dev: Failed to bind command/response socket: %s: %s\n", socket_path,
+ strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ ret = listen(cmdres_sock, 1);
+ if (ret < 0) {
+ ALOGE("rpmb_dev: Failed to listen on command/response socket: %s\n", strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ while (true) {
+ int conn_sock = accept(cmdres_sock, NULL, NULL);
+ if (conn_sock < 0) {
+ ALOGE("rpmb_dev: Could not accept connection: %s\n", strerror(errno));
+ return EXIT_FAILURE;
+ }
+ ret = handle_conn(&s, conn_sock);
+ close(conn_sock);
+ if (ret) {
+ ALOGE("rpmb_dev: Connection terminated: %d", ret);
+ }
+ }
+}
diff --git a/trusty/utils/rpmb_dev/rpmb_dev.rc b/trusty/utils/rpmb_dev/rpmb_dev.rc
new file mode 100644
index 0000000..9f60e81
--- /dev/null
+++ b/trusty/utils/rpmb_dev/rpmb_dev.rc
@@ -0,0 +1,29 @@
+# RPMB Mock
+on post-fs-data
+ mkdir /data/vendor/ss
+ chown root system /data/vendor/ss
+ chmod 0770 /data/vendor/ss
+ rm /data/vendor/ss/rpmb_sock
+ start rpmb_mock_init
+ start rpmb_mock
+
+ # Storage proxy
+ start storageproxyd
+
+service storageproxyd /vendor/bin/storageproxyd -d /dev/trusty-ipc-dev0 \
+ -r /data/vendor/ss/rpmb_sock -p /data/vendor/ss -t sock
+ class main
+ disabled
+ user root
+
+service rpmb_mock_init /vendor/bin/rpmb_dev --dev /data/vendor/ss/RPMB_DATA --init --key "ea df 64 44 ea 65 5d 1c 87 27 d4 20 71 0d 53 42 dd 73 a3 38 63 e1 d7 94 c3 72 a6 ea e0 64 64 e6" --size 2048
+ disabled
+ user system
+ group system
+ oneshot
+
+service rpmb_mock /vendor/bin/rpmb_dev --dev /data/vendor/ss/RPMB_DATA --sock /data/vendor/ss/rpmb_sock
+ class main
+ disabled
+ user system
+ group system
diff --git a/trusty/utils/rpmb_dev/rpmb_protocol.h b/trusty/utils/rpmb_dev/rpmb_protocol.h
new file mode 100644
index 0000000..bfcb806
--- /dev/null
+++ b/trusty/utils/rpmb_dev/rpmb_protocol.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include "rpmb.h" /* For struct rpmb_key */
+
+#define MMC_READ_MULTIPLE_BLOCK 18
+#define MMC_WRITE_MULTIPLE_BLOCK 25
+#define MMC_RELIABLE_WRITE_FLAG (1 << 31)
+
+#define MMC_RSP_PRESENT (1 << 0)
+#define MMC_RSP_CRC (1 << 2)
+#define MMC_RSP_OPCODE (1 << 4)
+#define MMC_CMD_ADTC (1 << 5)
+#define MMC_RSP_SPI_S1 (1 << 7)
+#define MMC_RSP_R1 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
+#define MMC_RSP_SPI_R1 (MMC_RSP_SPI_S1)
+
+struct rpmb_nonce {
+ uint8_t byte[16];
+};
+
+struct rpmb_u16 {
+ uint8_t byte[2];
+};
+
+struct rpmb_u32 {
+ uint8_t byte[4];
+};
+
+#define RPMB_PACKET_DATA_SIZE (256)
+
+struct rpmb_packet {
+ uint8_t pad[196];
+ struct rpmb_key key_mac;
+ uint8_t data[RPMB_PACKET_DATA_SIZE];
+ struct rpmb_nonce nonce;
+ struct rpmb_u32 write_counter;
+ struct rpmb_u16 address;
+ struct rpmb_u16 block_count;
+ struct rpmb_u16 result;
+ struct rpmb_u16 req_resp;
+};
+
+enum rpmb_request {
+ RPMB_REQ_PROGRAM_KEY = 0x0001,
+ RPMB_REQ_GET_COUNTER = 0x0002,
+ RPMB_REQ_DATA_WRITE = 0x0003,
+ RPMB_REQ_DATA_READ = 0x0004,
+ RPMB_REQ_RESULT_READ = 0x0005,
+};
+
+enum rpmb_response {
+ RPMB_RESP_PROGRAM_KEY = 0x0100,
+ RPMB_RESP_GET_COUNTER = 0x0200,
+ RPMB_RESP_DATA_WRITE = 0x0300,
+ RPMB_RESP_DATA_READ = 0x0400,
+};
+
+enum rpmb_result {
+ RPMB_RES_OK = 0x0000,
+ RPMB_RES_GENERAL_FAILURE = 0x0001,
+ RPMB_RES_AUTH_FAILURE = 0x0002,
+ RPMB_RES_COUNT_FAILURE = 0x0003,
+ RPMB_RES_ADDR_FAILURE = 0x0004,
+ RPMB_RES_WRITE_FAILURE = 0x0005,
+ RPMB_RES_READ_FAILURE = 0x0006,
+ RPMB_RES_NO_AUTH_KEY = 0x0007,
+
+ RPMB_RES_WRITE_COUNTER_EXPIRED = 0x0080,
+};
+
+static inline struct rpmb_u16 rpmb_u16(uint16_t val) {
+ struct rpmb_u16 ret = {{
+ (uint8_t)(val >> 8),
+ (uint8_t)(val >> 0),
+ }};
+ return ret;
+}
+
+static inline struct rpmb_u32 rpmb_u32(uint32_t val) {
+ struct rpmb_u32 ret = {{
+ (uint8_t)(val >> 24),
+ (uint8_t)(val >> 16),
+ (uint8_t)(val >> 8),
+ (uint8_t)(val >> 0),
+ }};
+ return ret;
+}
+
+static inline uint16_t rpmb_get_u16(struct rpmb_u16 u16) {
+ size_t i;
+ uint16_t val;
+
+ val = 0;
+ for (i = 0; i < sizeof(u16.byte); i++)
+ val = val << 8 | u16.byte[i];
+
+ return val;
+}
+
+static inline uint32_t rpmb_get_u32(struct rpmb_u32 u32) {
+ size_t i;
+ uint32_t val;
+
+ val = 0;
+ for (i = 0; i < sizeof(u32.byte); i++)
+ val = val << 8 | u32.byte[i];
+
+ return val;
+}