diff options
-rw-r--r-- | hal/Android.mk | 26 | ||||
-rw-r--r-- | hal/fake-nvram-seccomp-arm.policy | 47 | ||||
-rw-r--r-- | hal/fake-nvram-seccomp-arm64.policy | 47 | ||||
-rw-r--r-- | hal/fake-nvram-seccomp-x86.policy | 48 | ||||
-rw-r--r-- | hal/fake-nvram-seccomp-x86_64.policy | 47 | ||||
-rw-r--r-- | hal/fake-nvram.rc | 8 | ||||
-rw-r--r-- | hal/fake_nvram.cpp | 240 | ||||
-rw-r--r-- | hal/fake_nvram_storage.cpp | 204 | ||||
-rw-r--r-- | messages/include/nvram/messages/io.h | 17 | ||||
-rw-r--r-- | messages/include/nvram/messages/nvram_messages.h | 5 | ||||
-rw-r--r-- | messages/nvram_messages.cpp | 12 |
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 |