aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hal/Android.mk26
-rw-r--r--hal/fake-nvram-seccomp-arm.policy47
-rw-r--r--hal/fake-nvram-seccomp-arm64.policy47
-rw-r--r--hal/fake-nvram-seccomp-x86.policy48
-rw-r--r--hal/fake-nvram-seccomp-x86_64.policy47
-rw-r--r--hal/fake-nvram.rc8
-rw-r--r--hal/fake_nvram.cpp240
-rw-r--r--hal/fake_nvram_storage.cpp204
-rw-r--r--messages/include/nvram/messages/io.h17
-rw-r--r--messages/include/nvram/messages/nvram_messages.h5
-rw-r--r--messages/nvram_messages.cpp12
11 files changed, 701 insertions, 0 deletions
diff --git a/hal/Android.mk b/hal/Android.mk
index 05ecc17..9ec79b0 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -44,4 +44,30 @@ LOCAL_SHARED_LIBRARIES := libnvram-messages
LOCAL_MODULE_TAGS := optional
include $(BUILD_SHARED_LIBRARY)
+# fake_nvram is a system daemon that provides a software-only access-controlled
+# NVRAM implementation. This is only for illustration and in order to get code
+# using access-controlled NVRAM running on emulators. It *DOES NOT* meet the
+# tamper evidence requirements, so can't be used on production devices.
+include $(CLEAR_VARS)
+LOCAL_MODULE := fake-nvram
+LOCAL_SRC_FILES := \
+ fake_nvram.cpp \
+ fake_nvram_storage.cpp
+LOCAL_CLANG := true
+LOCAL_CFLAGS := -Wall -Werror -Wextra
+LOCAL_STATIC_LIBRARIES := libnvram-core libmincrypt
+LOCAL_SHARED_LIBRARIES := libnvram-messages libminijail libcutils libbase
+LOCAL_INIT_RC := fake-nvram.rc
+LOCAL_REQUIRED_MODULES := fake-nvram-seccomp.policy
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_EXECUTABLE)
+
+# seccomp policy for fake_nvram.
+include $(CLEAR_VARS)
+LOCAL_MODULE := fake-nvram-seccomp.policy
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT)/usr/share/policy/
+LOCAL_SRC_FILES := fake-nvram-seccomp-$(TARGET_ARCH).policy
+include $(BUILD_PREBUILT)
+
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hal/fake-nvram-seccomp-arm.policy b/hal/fake-nvram-seccomp-arm.policy
new file mode 100644
index 0000000..81ab2cb
--- /dev/null
+++ b/hal/fake-nvram-seccomp-arm.policy
@@ -0,0 +1,47 @@
+#
+# Copyright (C) 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.
+#
+
+# Control socket operation.
+accept4: 1
+getsockopt: 1
+ppoll: 1
+
+# File operations.
+fdatasync: 1
+fstat64: 1
+fsync: 1
+openat: 1
+renameat: 1
+unlinkat: 1
+
+# File and socket I/O.
+close: 1
+read: 1
+write: 1
+
+# Logging.
+clock_gettime: 1
+connect: 1
+fcntl64: 1
+getuid32: 1
+socket: 1
+writev: 1
+
+# Memory allocation.
+brk: 1
+mmap2: 1
+munmap: 1
+madvise: 1
diff --git a/hal/fake-nvram-seccomp-arm64.policy b/hal/fake-nvram-seccomp-arm64.policy
new file mode 100644
index 0000000..3de4bb4
--- /dev/null
+++ b/hal/fake-nvram-seccomp-arm64.policy
@@ -0,0 +1,47 @@
+#
+# Copyright (C) 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.
+#
+
+# Control socket operation.
+accept4: 1
+getsockopt: 1
+ppoll: 1
+
+# File operations.
+fdatasync: 1
+fstat: 1
+fsync: 1
+openat: 1
+renameat: 1
+unlinkat: 1
+
+# File and socket I/O.
+close: 1
+read: 1
+write: 1
+
+# Logging.
+clock_gettime: 1
+connect: 1
+fcntl: 1
+getuid: 1
+socket: 1
+writev: 1
+
+# Memory allocation.
+brk: 1
+mmap: 1
+munmap: 1
+madvise: 1
diff --git a/hal/fake-nvram-seccomp-x86.policy b/hal/fake-nvram-seccomp-x86.policy
new file mode 100644
index 0000000..4278947
--- /dev/null
+++ b/hal/fake-nvram-seccomp-x86.policy
@@ -0,0 +1,48 @@
+#
+# Copyright (C) 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.
+#
+
+# Control socket operation.
+accept4: 1
+getsockopt: 1
+ppoll: 1
+socketcall: 1
+
+# File operations.
+fdatasync: 1
+fstat64: 1
+fsync: 1
+openat: 1
+renameat: 1
+unlinkat: 1
+
+# File and socket I/O.
+close: 1
+read: 1
+write: 1
+
+# Logging.
+clock_gettime: 1
+connect: 1
+fcntl64: 1
+getuid32: 1
+socket: 1
+writev: 1
+
+# Memory allocation.
+brk: 1
+mmap2: 1
+munmap: 1
+madvise: 1
diff --git a/hal/fake-nvram-seccomp-x86_64.policy b/hal/fake-nvram-seccomp-x86_64.policy
new file mode 100644
index 0000000..3de4bb4
--- /dev/null
+++ b/hal/fake-nvram-seccomp-x86_64.policy
@@ -0,0 +1,47 @@
+#
+# Copyright (C) 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.
+#
+
+# Control socket operation.
+accept4: 1
+getsockopt: 1
+ppoll: 1
+
+# File operations.
+fdatasync: 1
+fstat: 1
+fsync: 1
+openat: 1
+renameat: 1
+unlinkat: 1
+
+# File and socket I/O.
+close: 1
+read: 1
+write: 1
+
+# Logging.
+clock_gettime: 1
+connect: 1
+fcntl: 1
+getuid: 1
+socket: 1
+writev: 1
+
+# Memory allocation.
+brk: 1
+mmap: 1
+munmap: 1
+madvise: 1
diff --git a/hal/fake-nvram.rc b/hal/fake-nvram.rc
new file mode 100644
index 0000000..7e03783
--- /dev/null
+++ b/hal/fake-nvram.rc
@@ -0,0 +1,8 @@
+on post-fs-data
+ mkdir /data/misc/fake-nvram 0700 nvram nvram
+
+service fake-nvram /system/bin/fake-nvram
+ class late_start
+ user root
+ group root
+ socket nvram seqpacket 0666 nvram nvram
diff --git a/hal/fake_nvram.cpp b/hal/fake_nvram.cpp
new file mode 100644
index 0000000..c944f08
--- /dev/null
+++ b/hal/fake_nvram.cpp
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 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 <getopt.h>
+#include <poll.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+#include <cutils/sockets.h>
+#include <libminijail.h>
+
+#include <nvram/core/nvram_manager.h>
+#include <nvram/messages/nvram_messages.h>
+
+// This is defined in fake_nvram_storage.h
+void InitStorage(int data_dir_fd);
+
+namespace {
+
+// Minijail parameters.
+constexpr char kNvramUser[] = "nvram";
+constexpr char kNvramGroup[] = "nvram";
+constexpr char kNvramSeccompPolicyPath[] =
+ "/system/usr/share/policy/fake-nvram-seccomp.policy";
+
+// Name of the control socket served by this daemon.
+constexpr char kNvramControlSocketName[] = "nvram";
+
+// The default data directory.
+constexpr char kNvramDataDirectory[] = "/data/misc/fake-nvram/";
+
+// Connection backlog on control socket.
+constexpr int kControlSocketBacklog = 20;
+
+// Maximum number of client sockets supported.
+constexpr int kMaxClientSockets = 32;
+
+// Size of the NVRAM message buffer for reading and writing serialized NVRAM
+// command messages from and to the control socket.
+constexpr int kNvramMessageBufferSize = 4096;
+
+// Variables holding command-line flags.
+const char* g_data_directory_path = kNvramDataDirectory;
+const char* g_control_socket_name = kNvramControlSocketName;
+
+// Parses the command line. Returns true if successful.
+bool ParseCommandLine(int argc, char** argv) {
+ while (true) {
+ static const struct option options[] = {
+ {"data_directory", required_argument, nullptr, 'd'},
+ {"control_socket", required_argument, nullptr, 's'},
+ };
+
+ int option_index = 0;
+ int c = getopt_long(argc, argv, "", options, &option_index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'd':
+ g_data_directory_path = optarg;
+ break;
+ case 's':
+ g_control_socket_name = optarg;
+ break;
+ default:
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Sets up a restricted environment using minijail and enters it.
+bool InitMinijail() {
+ std::unique_ptr<struct minijail, void (*)(struct minijail*)> minijail(
+ minijail_new(), &minijail_destroy);
+ if (minijail_change_user(minijail.get(), kNvramUser) ||
+ minijail_change_group(minijail.get(), kNvramGroup)) {
+ return false;
+ }
+ minijail_use_seccomp_filter(minijail.get());
+ minijail_no_new_privs(minijail.get());
+ minijail_parse_seccomp_filters(minijail.get(), kNvramSeccompPolicyPath);
+ minijail_enter(minijail.get());
+ return true;
+}
+
+// Reads a single command from |socket|, decodes the command, executes it on
+// |nvram_manager|, encodes the response, and writes the reply back to |socket|.
+// Returns true on success, false on errors (in which case the caller is
+// expected the close the |socket|).
+bool ProcessCommand(int socket, nvram::NvramManager* nvram_manager) {
+ uint8_t command_buffer[kNvramMessageBufferSize];
+ ssize_t bytes_read =
+ TEMP_FAILURE_RETRY(read(socket, command_buffer, sizeof(command_buffer)));
+ if (bytes_read == 0) {
+ return false;
+ }
+
+ if (bytes_read < 0) {
+ PLOG(ERROR) << "Failed to read command from client socket";
+ return false;
+ }
+
+ nvram::Request request;
+ if (!nvram::Decode(command_buffer, bytes_read, &request)) {
+ LOG(WARNING) << "Failed to decode command request!";
+ return false;
+ }
+
+ nvram::Response response;
+ nvram_manager->Dispatch(request, &response);
+ size_t response_size = sizeof(command_buffer);
+ if (!nvram::Encode(response, command_buffer, &response_size)) {
+ LOG(WARNING) << "Failed to encode command response!";
+ return false;
+ }
+
+ if (TEMP_FAILURE_RETRY(write(socket, command_buffer, response_size)) < 0) {
+ PLOG(ERROR) << "Failed to write response to client socket";
+ return false;
+ }
+
+ return true;
+}
+
+// Listens for incoming connections or data, accepts connections and processes
+// data as needed.
+int ProcessMessages(int control_socket_fd, nvram::NvramManager* nvram_manager) {
+ struct pollfd poll_fds[kMaxClientSockets];
+ memset(poll_fds, 0, sizeof(poll_fds));
+ poll_fds[0].fd = control_socket_fd;
+ poll_fds[0].events = POLLIN;
+ poll_fds[0].revents = 0;
+ nfds_t poll_fds_count = 1;
+ while (TEMP_FAILURE_RETRY(poll(poll_fds, poll_fds_count, -1)) >= 0) {
+ if (poll_fds[0].revents & POLLIN) {
+ // Accept a new connection.
+ int client_socket = accept(control_socket_fd, NULL, 0);
+ if (client_socket < 0) {
+ PLOG(ERROR) << "Error accepting connection";
+ return errno;
+ }
+
+ // Add |client_socket| to |poll_fds|.
+ if (poll_fds_count < kMaxClientSockets) {
+ poll_fds[poll_fds_count].fd = client_socket;
+ poll_fds[poll_fds_count].events = POLLIN;
+ poll_fds[poll_fds_count].revents = 0;
+ ++poll_fds_count;
+ } else {
+ LOG(WARNING) << "Too many open client sockets, rejecting connection.";
+ // No need to handle EINTR specially here as bionic filters it out.
+ if (close(client_socket)) {
+ PLOG(ERROR) << "Failed to close connection socket after error";
+ }
+ }
+ }
+
+ // Walk the connection fds backwards. This way, we can remove fds by
+ // replacing the slot with the last array element, which we have processed
+ // already.
+ for (int i = poll_fds_count - 1; i > 0; --i) {
+ if (poll_fds[i].revents & POLLIN) {
+ if (!ProcessCommand(poll_fds[i].fd, nvram_manager)) {
+ // No need to handle EINTR specially here as bionic filters it out.
+ if (close(poll_fds[i].fd)) {
+ PLOG(ERROR) << "Failed to close connection socket after error";
+ }
+ --poll_fds_count;
+ poll_fds[i] = poll_fds[poll_fds_count];
+ }
+ }
+ poll_fds[i].revents = 0;
+ }
+ }
+
+ // poll error.
+ PLOG(ERROR) << "Failed to poll control socket";
+ return errno;
+};
+
+} // namespace
+
+int main(int argc, char** argv) {
+ if (!ParseCommandLine(argc, argv)) {
+ return EINVAL;
+ }
+
+ int control_socket_fd = android_get_control_socket(g_control_socket_name);
+ if (control_socket_fd < 0) {
+ LOG(ERROR) << "Failed to get control socket.";
+ return EINVAL;
+ }
+
+ if (listen(control_socket_fd, kControlSocketBacklog)) {
+ PLOG(ERROR) << "Failed to listen on control socket";
+ return errno;
+ }
+
+ if (!InitMinijail()) {
+ LOG(ERROR) << "Failed to drop privileges.";
+ return -1;
+ }
+
+ int data_dir_fd =
+ TEMP_FAILURE_RETRY(open(g_data_directory_path, O_RDONLY | O_DIRECTORY));
+ if (data_dir_fd < 0) {
+ PLOG(ERROR) << "Failed to open data directory";
+ return errno;
+ }
+
+ InitStorage(data_dir_fd);
+
+ nvram::NvramManager nvram_manager;
+ return ProcessMessages(control_socket_fd, &nvram_manager);
+}
diff --git a/hal/fake_nvram_storage.cpp b/hal/fake_nvram_storage.cpp
new file mode 100644
index 0000000..47600dc
--- /dev/null
+++ b/hal/fake_nvram_storage.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 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 <nvram/core/storage.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+#include <nvram/core/logger.h>
+
+// An NVRAM storage layer implementation backed by the file system.
+//
+// NOTE: This does not meet the tamper evidence requirements for
+// access-controlled NVRAM implementations, since the file system can't provide
+// sufficient protection against tampering by attackers.
+
+namespace {
+
+// Name of the storage object holding the header.
+const char kHeaderFileName[] = "header";
+
+// Pattern for space data storage object names.
+const char kSpaceDataFileNamePattern[] = "space_%08x";
+
+// Temporary file name used in write-rename atomic write operations.
+const char kTempFileName[] = "temp";
+
+// Maximum size of objects we're willing to read and write.
+const off_t kMaxFileSize = 2048;
+
+// Buffer size for formatting names.
+using NameBuffer = char[16];
+
+// Global data directory descriptor.
+int g_data_dir_fd = -1;
+
+// Formats the storage object name for the given space index.
+bool FormatSpaceFileName(NameBuffer name, uint32_t index) {
+ int ret =
+ snprintf(name, sizeof(NameBuffer), kSpaceDataFileNamePattern, index);
+ return ret >= 0 && ret < static_cast<int>(sizeof(NameBuffer));
+};
+
+nvram::storage::Status DeleteFile(const char* name) {
+ if (TEMP_FAILURE_RETRY(unlinkat(g_data_dir_fd, name, 0))) {
+ if (errno == ENOENT) {
+ return nvram::storage::Status::kNotFound;
+ }
+ PLOG(ERROR) << "Failed to remove " << name;
+ return nvram::storage::Status::kStorageError;
+ }
+
+ return nvram::storage::Status::kSuccess;
+}
+
+// Loads the storage object identified by |name|.
+nvram::storage::Status LoadFile(const char* name, nvram::Blob* blob) {
+ android::base::unique_fd data_file_fd(
+ TEMP_FAILURE_RETRY(openat(g_data_dir_fd, name, O_RDONLY)));
+ if (data_file_fd.get() < 0) {
+ if (errno == ENOENT) {
+ return nvram::storage::Status::kNotFound;
+ }
+ PLOG(ERROR) << "Failed to open " << name;
+ return nvram::storage::Status::kStorageError;
+ }
+
+ struct stat data_file_stat;
+ if (TEMP_FAILURE_RETRY(fstat(data_file_fd.get(), &data_file_stat))) {
+ PLOG(ERROR) << "Failed to stat " << name;
+ return nvram::storage::Status::kStorageError;
+ }
+
+ if (data_file_stat.st_size > kMaxFileSize) {
+ LOG(ERROR) << "Bad size for " << name << ":" << data_file_stat.st_size;
+ return nvram::storage::Status::kStorageError;
+ }
+
+ if (!blob->Resize(data_file_stat.st_size)) {
+ LOG(ERROR) << "Failed to allocate read buffer for " << name;
+ return nvram::storage::Status::kStorageError;
+ }
+
+ if (!android::base::ReadFully(data_file_fd.get(), blob->data(),
+ blob->size())) {
+ PLOG(ERROR) << "Failed to read " << name;
+ return nvram::storage::Status::kStorageError;
+ }
+
+ return nvram::storage::Status::kSuccess;
+}
+
+// Writes blob to the storage object indicated by |name|.
+nvram::storage::Status StoreFile(const char* name, const nvram::Blob& blob) {
+ android::base::unique_fd data_file_fd(TEMP_FAILURE_RETRY(
+ openat(g_data_dir_fd, kTempFileName, O_WRONLY | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IWUSR)));
+ if (data_file_fd.get() < 0) {
+ if (errno == ENOENT) {
+ return nvram::storage::Status::kNotFound;
+ }
+ PLOG(ERROR) << "Failed to open " << kTempFileName;
+ return nvram::storage::Status::kStorageError;
+ }
+
+ if (!android::base::WriteFully(data_file_fd.get(), blob.data(),
+ blob.size())) {
+ PLOG(ERROR) << "Failed to write " << kTempFileName;
+ DeleteFile(kTempFileName);
+ return nvram::storage::Status::kStorageError;
+ }
+
+ // Force the file contents to be written to disk.
+ if (TEMP_FAILURE_RETRY(fdatasync(data_file_fd.get()))) {
+ PLOG(ERROR) << "Failed to sync " << kTempFileName;
+ DeleteFile(kTempFileName);
+ return nvram::storage::Status::kStorageError;
+ }
+
+ data_file_fd.clear();
+
+ // Move the file into place.
+ if (TEMP_FAILURE_RETRY(
+ renameat(g_data_dir_fd, kTempFileName, g_data_dir_fd, name))) {
+ PLOG(ERROR) << "Failed to move " << kTempFileName << " to " << name;
+ DeleteFile(kTempFileName);
+ return nvram::storage::Status::kStorageError;
+ }
+
+ // Force the directory meta data to be written to disk.
+ if (TEMP_FAILURE_RETRY(fsync(g_data_dir_fd))) {
+ PLOG(ERROR) << "Failed to sync data directory";
+ return nvram::storage::Status::kStorageError;
+ }
+
+ return nvram::storage::Status::kSuccess;
+}
+
+} // namespace
+
+// Initializes the storage layer with the provided data directory descriptor.
+void InitStorage(int data_dir_fd) {
+ g_data_dir_fd = data_dir_fd;
+}
+
+namespace nvram {
+namespace storage {
+
+Status LoadHeader(Blob* blob) {
+ return LoadFile(kHeaderFileName, blob);
+}
+
+Status StoreHeader(const Blob& blob) {
+ return StoreFile(kHeaderFileName, blob);
+}
+
+Status LoadSpace(uint32_t index, Blob* blob) {
+ NameBuffer name;
+ if (!FormatSpaceFileName(name, index)) {
+ return Status::kStorageError;
+ }
+ return LoadFile(name, blob);
+}
+
+Status StoreSpace(uint32_t index, const Blob& blob) {
+ NameBuffer name;
+ if (!FormatSpaceFileName(name, index)) {
+ return Status::kStorageError;
+ }
+ return StoreFile(name, blob);
+}
+
+Status DeleteSpace(uint32_t index) {
+ NameBuffer name;
+ if (!FormatSpaceFileName(name, index)) {
+ return Status::kStorageError;
+ }
+
+ return DeleteFile(name);
+}
+
+} // namespace storage
+} // namespace nvram
diff --git a/messages/include/nvram/messages/io.h b/messages/include/nvram/messages/io.h
index 522469f..08c1091 100644
--- a/messages/include/nvram/messages/io.h
+++ b/messages/include/nvram/messages/io.h
@@ -144,6 +144,23 @@ class NVRAM_EXPORT OutputStreamBuffer {
uint8_t* end_ = nullptr;
};
+// An |OutputStreamBuffer| backed by a single data buffer.
+class NVRAM_EXPORT ArrayOutputStreamBuffer : public OutputStreamBuffer {
+ public:
+ ArrayOutputStreamBuffer() = default;
+ ArrayOutputStreamBuffer(void* data, size_t size)
+ : OutputStreamBuffer(data, size), data_(pos_) {}
+ ArrayOutputStreamBuffer(void* data, void* end)
+ : OutputStreamBuffer(data, end), data_(pos_) {}
+ ~ArrayOutputStreamBuffer() override = default;
+
+ // Returns the number of bytes already written.
+ size_t bytes_written() const { return pos_ - data_; }
+
+ private:
+ uint8_t* data_ = nullptr;
+};
+
// An |OutputStream| implementation that doesn't write anything, but just counts
// the number of bytes written.
class NVRAM_EXPORT CountingOutputStreamBuffer : public OutputStreamBuffer {
diff --git a/messages/include/nvram/messages/nvram_messages.h b/messages/include/nvram/messages/nvram_messages.h
index b352d9f..e574707 100644
--- a/messages/include/nvram/messages/nvram_messages.h
+++ b/messages/include/nvram/messages/nvram_messages.h
@@ -162,6 +162,11 @@ struct Response {
template <typename Message>
bool Encode(const Message& msg, Blob* blob);
+// Encode |msg| to |buffer|, which is of size |*size|. Updates |*size| to
+// indicate the number of bytes written. Returns true on success.
+template <typename Message>
+bool Encode(const Message& msg, void* buffer, size_t* size);
+
// Decode |msg| from the |data| buffer, which contains |size| bytes. Returns
// true if successful.
template <typename Message>
diff --git a/messages/nvram_messages.cpp b/messages/nvram_messages.cpp
index a984876..7b1cafb 100644
--- a/messages/nvram_messages.cpp
+++ b/messages/nvram_messages.cpp
@@ -159,6 +159,16 @@ bool Encode(const Message& msg, Blob* blob) {
}
template <typename Message>
+bool Encode(const Message& msg, void* buffer, size_t* size) {
+ ArrayOutputStreamBuffer stream(buffer, *size);
+ if (!nvram::proto::Encode(msg, &stream)) {
+ return false;
+ }
+ *size = stream.bytes_written();
+ return true;
+}
+
+template <typename Message>
bool Decode(const uint8_t* data, size_t size, Message* msg) {
InputStreamBuffer stream(data, size);
return nvram::proto::Decode(msg, &stream) && stream.Done();
@@ -166,9 +176,11 @@ bool Decode(const uint8_t* data, size_t size, Message* msg) {
// Instantiate the templates for the |Request| and |Response| message types.
template NVRAM_EXPORT bool Encode<Request>(const Request&, Blob*);
+template NVRAM_EXPORT bool Encode<Request>(const Request&, void*, size_t*);
template NVRAM_EXPORT bool Decode<Request>(const uint8_t*, size_t, Request*);
template NVRAM_EXPORT bool Encode<Response>(const Response&, Blob*);
+template NVRAM_EXPORT bool Encode<Response>(const Response&, void*, size_t*);
template NVRAM_EXPORT bool Decode<Response>(const uint8_t*, size_t, Response*);
} // namespace nvram