aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWeston Carvalho <westoncarvalho@google.com>2024-01-22 15:22:00 -0600
committerWeston Carvalho <westoncarvalho@google.com>2024-03-28 17:19:01 -0500
commitdf16cc4ff58d60e171a64c320f601236541850e4 (patch)
tree9a40c88c69750fbe337fcf34d2db589a36d81752
parent05ac17beee4f083fc8750133cc6bd225c99065de (diff)
downloadstorage-df16cc4ff58d60e171a64c320f601236541850e4.tar.gz
storage: Implement storage aidl service
Implement all operations for `IDir`, `IFile`, `IStorageSession`, and `ISecureStorage`. Bug: 300673722 Change-Id: Ia47c867dc4c39921e7a1fde7524be08b8aaea9b5
-rw-r--r--aidl_service.cpp385
1 files changed, 343 insertions, 42 deletions
diff --git a/aidl_service.cpp b/aidl_service.cpp
index 48728f1..3d18669 100644
--- a/aidl_service.cpp
+++ b/aidl_service.cpp
@@ -16,7 +16,12 @@
#include "aidl_service.h"
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
#include <cstdlib>
+#include <cstring>
+#include <limits>
#include <memory>
#include <vector>
@@ -26,6 +31,7 @@
#include <binder/RpcServerTrusty.h>
#include <binder/Status.h>
+#include <utils/Errors.h>
#include <android/hardware/security/see/storage/BnDir.h>
#include <android/hardware/security/see/storage/BnFile.h>
@@ -33,6 +39,9 @@
#include <android/hardware/security/see/storage/BnStorageSession.h>
#include <android/hardware/security/see/storage/CreationMode.h>
#include <android/hardware/security/see/storage/DeleteOptions.h>
+#include <android/hardware/security/see/storage/FileAvailability.h>
+#include <android/hardware/security/see/storage/FileIntegrity.h>
+#include <android/hardware/security/see/storage/FileMode.h>
#include <android/hardware/security/see/storage/FileProperties.h>
#include <android/hardware/security/see/storage/IDir.h>
#include <android/hardware/security/see/storage/IFile.h>
@@ -44,6 +53,7 @@
#include "client.h"
#include "client_session.h"
+#include "file.h"
#include "storage_limits.h"
using ::android::RpcServerTrusty;
@@ -59,6 +69,7 @@ using ::android::hardware::security::see::storage::CreationMode;
using ::android::hardware::security::see::storage::DeleteOptions;
using ::android::hardware::security::see::storage::FileAvailability;
using ::android::hardware::security::see::storage::FileIntegrity;
+using ::android::hardware::security::see::storage::FileMode;
using ::android::hardware::security::see::storage::FileProperties;
using ::android::hardware::security::see::storage::IDir;
using ::android::hardware::security::see::storage::IFile;
@@ -82,6 +93,51 @@ constexpr uint32_t kAclFlags =
constexpr size_t kMaxBufferSize = STORAGE_MAX_BUFFER_SIZE;
+static Status status_from_storage_err(storage_err err) {
+ switch (err) {
+ case storage_err::STORAGE_NO_ERROR:
+ return Status::ok();
+ case storage_err::STORAGE_ERR_GENERIC:
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+ case storage_err::STORAGE_ERR_NOT_VALID:
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
+ case storage_err::STORAGE_ERR_UNIMPLEMENTED:
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
+ case storage_err::STORAGE_ERR_ACCESS:
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+ case storage_err::STORAGE_ERR_NOT_FOUND:
+ return Status::fromServiceSpecificError(ISecureStorage::ERR_NOT_FOUND);
+ case storage_err::STORAGE_ERR_EXIST:
+ return Status::fromServiceSpecificError(
+ ISecureStorage::ERR_ALREADY_EXISTS);
+ case storage_err::STORAGE_ERR_TRANSACT:
+ return Status::fromServiceSpecificError(
+ ISecureStorage::ERR_BAD_TRANSACTION);
+ case storage_err::STORAGE_ERR_NOT_ALLOWED:
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+ case storage_err::STORAGE_ERR_CORRUPTED:
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+ case storage_err::STORAGE_ERR_FS_REPAIRED:
+ // TODO: Distinguish rolled back vs reset; catch other tampering
+ return Status::fromServiceSpecificError(
+ ISecureStorage::ERR_FS_ROLLED_BACK);
+ default:
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
+ "Unknown error code.");
+ }
+}
+
+static file_create_mode create_mode(CreationMode mode) {
+ switch (mode) {
+ case CreationMode::CREATE_EXCLUSIVE:
+ return file_create_mode::FILE_OPEN_CREATE_EXCLUSIVE;
+ case CreationMode::CREATE:
+ return file_create_mode::FILE_OPEN_CREATE;
+ case CreationMode::NO_CREATE:
+ return file_create_mode::FILE_OPEN_NO_CREATE;
+ }
+}
+
static Status get_fs(const FileProperties& properties,
storage_aidl_filesystem* out) {
switch (properties.integrity) {
@@ -148,67 +204,201 @@ private:
class Dir : public BnDir {
public:
- Dir(std::weak_ptr<StorageClientSession> session)
- : session_(std::move(session)) {}
+ Dir(std::weak_ptr<StorageClientSession> session, ReadIntegrity integrity)
+ : session_(std::move(session)),
+ integrity_(integrity),
+ last_state_(storage_file_list_flag::STORAGE_FILE_LIST_START),
+ last_name_() {}
Status readNextFilenames(int32_t max_count,
std::vector<std::string>* out) final {
+ constexpr size_t kMaxFilenames = STORAGE_MAX_BUFFER_SIZE / FS_PATH_MAX;
+
+ if (max_count < 0) {
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT,
+ "maxCount must not be negative.");
+ }
+ size_t max_names = (max_count == 0)
+ ? kMaxFilenames
+ : std::min(kMaxFilenames,
+ static_cast<size_t>(max_count));
+
std::shared_ptr<StorageClientSession> session = session_.lock();
if (session == nullptr) {
return Status::fromExceptionCode(
Status::EX_ILLEGAL_STATE,
"IDir cannot be used after its parent session has been destroyed.");
}
- if (session->get() == nullptr) {
+ storage_client_session* client_session = session->get();
+ if (client_session == nullptr) {
return Status::fromExceptionCode(
Status::EX_ILLEGAL_STATE,
"Connection to underlying filesystem lost. Start a new session.");
}
- return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
+ if (last_state_ == storage_file_list_flag::STORAGE_FILE_LIST_END) {
+ return Status::ok();
+ }
+
+ ListCallbackData data = {
+ .out = out,
+ .curr_flags = last_state_,
+ };
+
+ storage_err result = storage_file_list(
+ client_session, max_names, last_state_, last_name_.data(),
+ last_name_.size(), op_flags(),
+ [](void* callback_data, size_t max_path_len) { return true; },
+ [](void* callback_data, enum storage_file_list_flag flags,
+ const char* path, size_t path_len) {
+ auto& data = *static_cast<ListCallbackData*>(callback_data);
+
+ data.curr_flags = flags;
+ if (flags ==
+ storage_file_list_flag::STORAGE_FILE_LIST_END) {
+ return;
+ }
+
+ data.out->emplace_back(path, path_len);
+
+ // TODO: Do we need to tell the caller whether the file is
+ // committed, added, removed?
+ },
+ &data);
+
+ last_state_ = data.curr_flags;
+ last_name_ = out->empty() ? "" : out->back();
+
+ return status_from_storage_err(result);
}
private:
+ struct ListCallbackData {
+ std::vector<std::string>* out;
+ enum storage_file_list_flag curr_flags;
+ };
+
+ storage_op_flags op_flags() {
+ return storage_op_flags{
+ // TODO: support acking reset only
+ .allow_repaired = integrity_ == ReadIntegrity::IGNORE_ROLLBACK,
+ .complete_transaction = false,
+ .update_checkpoint = false,
+ };
+ }
+
std::weak_ptr<StorageClientSession> session_;
+ ReadIntegrity integrity_;
+
+ enum storage_file_list_flag last_state_;
+ std::string last_name_;
};
class File : public BnFile {
public:
- File(std::weak_ptr<StorageClientSession> session)
- : session_(std::move(session)) {}
+ File(std::weak_ptr<StorageClientSession> session,
+ uint32_t file_handle,
+ FileMode access_mode,
+ ReadIntegrity integrity,
+ bool allow_writes_during_ab_update)
+ : session_(std::move(session)),
+ file_handle_(file_handle),
+ access_mode_(access_mode),
+ integrity_(integrity),
+ allow_writes_during_ab_update_(allow_writes_during_ab_update) {
+ assert(!allow_writes_during_ab_update_);
+ }
+
+ ~File() {
+ std::shared_ptr<StorageClientSession> session = session_.lock();
+ if (session == nullptr) {
+ return;
+ }
+ storage_client_session* client_session = session->get();
+ if (client_session == nullptr) {
+ return;
+ }
+
+ (void)storage_file_close(client_session, file_handle_, op_flags());
+ }
Status read(int64_t size, int64_t offset, std::vector<uint8_t>* out) final {
+ if (access_mode_ != FileMode::READ_ONLY &&
+ access_mode_ != FileMode::READ_WRITE) {
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT,
+ "File not opened for reading.");
+ }
+ if (size < 0) {
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT,
+ "Size must not be negative.");
+ }
+ if (size > std::numeric_limits<uint32_t>::max()) {
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT,
+ "Size would overflow");
+ }
+ if (offset < 0) {
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT,
+ "Offset must not be negative.");
+ }
std::shared_ptr<StorageClientSession> session = session_.lock();
if (session == nullptr) {
return Status::fromExceptionCode(
Status::EX_ILLEGAL_STATE,
"IFile cannot be used after its parent session has been destroyed.");
}
- if (session->get() == nullptr) {
+ storage_client_session* client_session = session->get();
+ if (client_session == nullptr) {
return Status::fromExceptionCode(
Status::EX_ILLEGAL_STATE,
"Connection to underlying filesystem lost. Start a new session.");
}
- return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
+
+ out->resize(
+ std::min(size, static_cast<int64_t>(STORAGE_MAX_BUFFER_SIZE)));
+ size_t out_len = out->size();
+
+ storage_err result =
+ storage_file_read(client_session, file_handle_, size, offset,
+ op_flags(), out->data(), &out_len);
+
+ out->resize(out_len);
+ return status_from_storage_err(result);
}
Status write(int64_t offset,
const std::vector<uint8_t>& buffer,
int64_t* out) final {
+ if (access_mode_ != FileMode::WRITE_ONLY &&
+ access_mode_ != FileMode::READ_WRITE) {
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT,
+ "File not opened for writing.");
+ }
+ if (offset < 0) {
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT,
+ "Offset must not be negative.");
+ }
std::shared_ptr<StorageClientSession> session = session_.lock();
if (session == nullptr) {
return Status::fromExceptionCode(
Status::EX_ILLEGAL_STATE,
"IFile cannot be used after its parent session has been destroyed.");
}
- if (session->get() == nullptr) {
+ storage_client_session* client_session = session->get();
+ if (client_session == nullptr) {
return Status::fromExceptionCode(
Status::EX_ILLEGAL_STATE,
"Connection to underlying filesystem lost. Start a new session.");
}
- *out = 0;
- return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
+ storage_err result =
+ storage_file_write(session->get(), file_handle_, offset,
+ buffer.data(), buffer.size(), op_flags());
+ if (result != storage_err::STORAGE_NO_ERROR) {
+ return status_from_storage_err(result);
+ }
+
+ *out = buffer.size();
+ return Status::ok();
}
Status getSize(int64_t* out) final {
@@ -218,49 +408,92 @@ public:
Status::EX_ILLEGAL_STATE,
"IFile cannot be used after its parent session has been destroyed.");
}
- if (session->get() == nullptr) {
+ storage_client_session* client_session = session->get();
+ if (client_session == nullptr) {
return Status::fromExceptionCode(
Status::EX_ILLEGAL_STATE,
"Connection to underlying filesystem lost. Start a new session.");
}
- *out = 0;
- return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
+ uint64_t size;
+ storage_err result = storage_file_get_size(session->get(), file_handle_,
+ op_flags(), &size);
+ if (result != storage_err::STORAGE_NO_ERROR) {
+ return status_from_storage_err(result);
+ }
+
+ if (size > std::numeric_limits<int64_t>::max()) {
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE,
+ "Size would overflow");
+ }
+ *out = static_cast<int64_t>(size);
+ return Status::ok();
}
Status setSize(int64_t new_size) final {
+ if (access_mode_ != FileMode::WRITE_ONLY &&
+ access_mode_ != FileMode::READ_WRITE) {
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT,
+ "File not opened for writing.");
+ }
std::shared_ptr<StorageClientSession> session = session_.lock();
if (session == nullptr) {
return Status::fromExceptionCode(
Status::EX_ILLEGAL_STATE,
"IFile cannot be used after its parent session has been destroyed.");
}
- if (session->get() == nullptr) {
+ storage_client_session* client_session = session->get();
+ if (client_session == nullptr) {
return Status::fromExceptionCode(
Status::EX_ILLEGAL_STATE,
"Connection to underlying filesystem lost. Start a new session.");
}
- return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
+ storage_err result = storage_file_set_size(session->get(), file_handle_,
+ new_size, op_flags());
+ return status_from_storage_err(result);
}
Status rename(const std::string& new_name, CreationMode dest_create_mode) {
+ if (access_mode_ != FileMode::WRITE_ONLY &&
+ access_mode_ != FileMode::READ_WRITE) {
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT,
+ "File not opened for writing.");
+ }
std::shared_ptr<StorageClientSession> session = session_.lock();
if (session == nullptr) {
return Status::fromExceptionCode(
Status::EX_ILLEGAL_STATE,
"IFile cannot be used after its parent session has been destroyed.");
}
- if (session->get() == nullptr) {
+ storage_client_session* client_session = session->get();
+ if (client_session == nullptr) {
return Status::fromExceptionCode(
Status::EX_ILLEGAL_STATE,
"Connection to underlying filesystem lost. Start a new session.");
}
- return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
+
+ storage_err result = storage_file_move(
+ session->get(), file_handle_, true, nullptr, 0, new_name.data(),
+ new_name.size(), create_mode(dest_create_mode), op_flags());
+ return status_from_storage_err(result);
}
private:
+ storage_op_flags op_flags() {
+ return storage_op_flags{
+ // TODO: support acking reset only
+ .allow_repaired = integrity_ == ReadIntegrity::IGNORE_ROLLBACK,
+ .complete_transaction = false,
+ .update_checkpoint = false,
+ };
+ }
+
std::weak_ptr<StorageClientSession> session_;
+ uint32_t file_handle_;
+ FileMode access_mode_;
+ ReadIntegrity integrity_;
+ bool allow_writes_during_ab_update_;
};
class StorageSession : public BnStorageSession {
@@ -268,71 +501,139 @@ public:
StorageSession(std::shared_ptr<StorageClientSession> session)
: session_(std::move(session)) {}
- Status commitChanges() final {
- if (session_->get() == nullptr) {
- return Status::fromExceptionCode(
- Status::EX_ILLEGAL_STATE,
- "Connection to underlying filesystem lost. Start a new session.");
- }
- return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
- }
-
- Status abandonChanges() final {
- if (session_->get() == nullptr) {
- return Status::fromExceptionCode(
- Status::EX_ILLEGAL_STATE,
- "Connection to underlying filesystem lost. Start a new session.");
- }
- return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
- }
+ Status commitChanges() final { return endTransactions(true); }
+ Status abandonChanges() final { return endTransactions(false); }
Status openFile(const std::string& file_name,
const OpenOptions& options,
sp<IFile>* out) final {
- if (session_->get() == nullptr) {
+ if (options.allowWritesDuringAbUpdate) {
+ return Status::fromExceptionCode(
+ Status::EX_UNSUPPORTED_OPERATION,
+ "Unsupported option: allowWritesDuringAbUpdate");
+ }
+ storage_client_session* client_session = session_->get();
+ if (client_session == nullptr) {
return Status::fromExceptionCode(
Status::EX_ILLEGAL_STATE,
"Connection to underlying filesystem lost. Start a new session.");
}
- *out = sp<File>::make(session_);
+ uint32_t file_handle;
+ storage_err err = storage_file_open(
+ client_session, file_name.data(), file_name.size(),
+ create_mode(options.createMode), options.truncateOnOpen,
+ storage_op_flags{
+ // TODO: support acking reset only
+ .allow_repaired = options.readIntegrity ==
+ ReadIntegrity::IGNORE_ROLLBACK,
+ .complete_transaction = false,
+ .update_checkpoint = false,
+ },
+ &file_handle);
+ if (err != storage_err::STORAGE_NO_ERROR) {
+ return status_from_storage_err(err);
+ }
+
+ *out = sp<File>::make(session_, file_handle, options.accessMode,
+ options.readIntegrity,
+ options.allowWritesDuringAbUpdate);
return Status::ok();
}
+
Status deleteFile(const std::string& file_name,
const DeleteOptions& options) final {
- if (session_->get() == nullptr) {
+ if (options.allowWritesDuringAbUpdate) {
+ return Status::fromExceptionCode(
+ Status::EX_UNSUPPORTED_OPERATION,
+ "Unsupported option: allowWritesDuringAbUpdate");
+ }
+ storage_client_session* client_session = session_->get();
+ if (client_session == nullptr) {
return Status::fromExceptionCode(
Status::EX_ILLEGAL_STATE,
"Connection to underlying filesystem lost. Start a new session.");
}
- return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
+
+ storage_err err = storage_file_delete(
+ client_session, file_name.data(), file_name.size(),
+ storage_op_flags{
+ // TODO: support acking reset only
+ .allow_repaired = options.readIntegrity ==
+ ReadIntegrity::IGNORE_ROLLBACK,
+ .complete_transaction = false,
+ .update_checkpoint = false,
+ });
+ return status_from_storage_err(err);
}
Status renameFile(const std::string& file_name,
const std::string& new_name,
const RenameOptions& options) final {
- if (session_->get() == nullptr) {
+ if (options.allowWritesDuringAbUpdate) {
+ return Status::fromExceptionCode(
+ Status::EX_UNSUPPORTED_OPERATION,
+ "Unsupported option: allowWritesDuringAbUpdate");
+ }
+ storage_client_session* client_session = session_->get();
+ if (client_session == nullptr) {
return Status::fromExceptionCode(
Status::EX_ILLEGAL_STATE,
"Connection to underlying filesystem lost. Start a new session.");
}
- return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
+
+ storage_err err = storage_file_move(
+ client_session, 0, false, file_name.data(), file_name.size(),
+ new_name.data(), new_name.size(),
+ create_mode(options.destCreateMode),
+ storage_op_flags{
+ // TODO: support acking reset only
+ .allow_repaired = options.readIntegrity ==
+ ReadIntegrity::IGNORE_ROLLBACK,
+ .complete_transaction = false,
+ .update_checkpoint = false,
+ });
+ return status_from_storage_err(err);
}
Status openDir(const std::string& file_name,
ReadIntegrity integrity,
sp<IDir>* out) final {
+ if (!file_name.empty()) {
+ return Status::fromExceptionCode(
+ Status::EX_ILLEGAL_ARGUMENT,
+ "Service currently only supports opening the root dir.");
+ }
if (session_->get() == nullptr) {
return Status::fromExceptionCode(
Status::EX_ILLEGAL_STATE,
"Connection to underlying filesystem lost. Start a new session.");
}
- *out = sp<Dir>::make(session_);
+ // TODO: Catch tampering?
+ *out = sp<Dir>::make(session_, integrity);
return Status::ok();
}
private:
+ Status endTransactions(bool commit_changes) {
+ storage_op_flags flags = {
+ .allow_repaired = false,
+ .complete_transaction = commit_changes,
+ // TODO: Allow updating checkpoint
+ .update_checkpoint = false,
+ };
+
+ storage_client_session* client_session = session_->get();
+ if (client_session == nullptr) {
+ return Status::fromExceptionCode(
+ Status::EX_ILLEGAL_STATE,
+ "Connection to underlying filesystem lost. Start a new session.");
+ }
+ storage_err result = storage_transaction_end(client_session, flags);
+ return status_from_storage_err(result);
+ }
+
std::shared_ptr<StorageClientSession> session_;
};