diff options
author | Weston Carvalho <westoncarvalho@google.com> | 2024-01-22 15:22:00 -0600 |
---|---|---|
committer | Weston Carvalho <westoncarvalho@google.com> | 2024-03-28 17:19:01 -0500 |
commit | df16cc4ff58d60e171a64c320f601236541850e4 (patch) | |
tree | 9a40c88c69750fbe337fcf34d2db589a36d81752 | |
parent | 05ac17beee4f083fc8750133cc6bd225c99065de (diff) | |
download | storage-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.cpp | 385 |
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_; }; |