| // Copyright 2021 The Pigweed Authors |
| // |
| // 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 |
| // |
| // https://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 <cstddef> |
| #include <cstdint> |
| #include <span> |
| #include <string_view> |
| |
| #include "pw_bytes/span.h" |
| #include "pw_file/file.pwpb.h" |
| #include "pw_file/file.raw_rpc.pb.h" |
| #include "pw_protobuf/serialized_size.h" |
| #include "pw_result/result.h" |
| #include "pw_rpc/raw/server_reader_writer.h" |
| #include "pw_status/status.h" |
| #include "pw_status/status_with_size.h" |
| |
| namespace pw::file { |
| |
| // This implements the pw.file.FileSystem RPC service. This implementation |
| // has a strict limitation that everything is treated as if the file system |
| // was "flat" (i.e. no directories). This means there's no concept of logical |
| // directories, despite any "path like" naming that may be employed by a user. |
| class FlatFileSystemService |
| : public pw_rpc::raw::FileSystem::Service<FlatFileSystemService> { |
| public: |
| class Entry { |
| public: |
| using FilePermissions = pw::file::Path::Permissions; |
| using Id = uint32_t; |
| |
| Entry() = default; |
| virtual ~Entry() = default; |
| |
| // All readable files MUST be named, and names must be globally unique to |
| // prevent ambiguity. Unnamed file entries will NOT be enumerated by a |
| // FlatFileSystemService. The returned status must indicate the length |
| // of the string written to `dest`, and should NOT include any null |
| // terminator that may have been written. |
| // |
| // Note: The bounded string written to `dest` is not expected to be |
| // null-terminated, and should be treated like a std::string_view. |
| // |
| // Returns: |
| // OK - Successfully read file name to `dest`. |
| // NOT_FOUND - No file to enumerate for this entry. |
| // RESOURCE_EXHAUSTED - `dest` buffer too small to fit the full file name. |
| virtual StatusWithSize Name(std::span<char> dest) = 0; |
| |
| virtual size_t SizeBytes() = 0; |
| virtual FilePermissions Permissions() const = 0; |
| |
| // Deleting a file, if allowed, should cause the backing data store to be |
| // cleared. Read-only files should also no longer enumerate (i.e. Name() |
| // should return NOT_FOUND). Write-only and read/write files may still |
| // enumerate but with SizeBytes() reporting zero. |
| virtual Status Delete() = 0; |
| |
| // File IDs must be globally unique, and map to a pw_transfer |
| // TransferService read/write handler. |
| virtual Id FileId() const = 0; |
| }; |
| |
| // Returns the size of encoding buffer guaranteed to support encoding |
| // minimum_entries paths with file names up max_file_name_length. |
| static constexpr size_t EncodingBufferSizeBytes(size_t max_file_name_length, |
| size_t minimum_entries = 1) { |
| return minimum_entries * |
| protobuf::SizeOfDelimitedField( |
| ListResponse::Fields::PATHS, |
| EncodedPathProtoSizeBytes(max_file_name_length)); |
| } |
| |
| // Constructs a flat file system from a static list of file entries. |
| // |
| // Args: |
| // entry_list - A list of pointers to all Entry objects that may |
| // contain files. These pointers may not be null. The span's underlying |
| // buffer must outlive this object. |
| // encoding_buffer - Used internally by this class to encode its responses. |
| // file_name_buffer - Used internally by this class to find and enumerate |
| // files. Should be large enough to hold the longest expected file name. |
| // The span's underlying buffer must outlive this object. |
| // max_file_name_length - Number of bytes to reserve for the file name. |
| constexpr FlatFileSystemService(std::span<Entry*> entry_list, |
| std::span<std::byte> encoding_buffer, |
| std::span<char> file_name_buffer) |
| : encoding_buffer_(encoding_buffer), |
| file_name_buffer_(file_name_buffer), |
| entries_(entry_list) {} |
| |
| // Method definitions for pw.file.FileSystem. |
| void List(ConstByteSpan request, RawServerWriter& writer); |
| |
| // Returns: |
| // OK - File successfully deleted. |
| // NOT_FOUND - Could not find |
| void Delete(ConstByteSpan request, rpc::RawUnaryResponder& responder); |
| |
| private: |
| // Returns the maximum size of a single encoded Path proto. |
| static constexpr size_t EncodedPathProtoSizeBytes( |
| size_t max_file_name_length) { |
| return protobuf::SizeOfFieldString(Path::Fields::PATH, |
| max_file_name_length) + |
| protobuf::SizeOfFieldEnum(Path::Fields::PERMISSIONS, |
| Path::Permissions::READ_AND_WRITE) + |
| protobuf::SizeOfFieldUint32(Path::Fields::SIZE_BYTES) + |
| protobuf::SizeOfFieldUint32(Path::Fields::FILE_ID); |
| } |
| |
| Result<Entry*> FindFile(std::string_view file_name); |
| Status FindAndDeleteFile(std::string_view file_name); |
| |
| Status EnumerateFile(Entry& entry, |
| pw::file::ListResponse::StreamEncoder& output_encoder); |
| void EnumerateAllFiles(RawServerWriter& writer); |
| |
| const std::span<std::byte> encoding_buffer_; |
| const std::span<char> file_name_buffer_; |
| const std::span<Entry*> entries_; |
| }; |
| |
| // Provides the encoding and file name buffers to a FlatFileSystemService. |
| template <unsigned kMaxFileNameLength, |
| unsigned kMinGuaranteedEntriesPerResponse = 1> |
| class FlatFileSystemServiceWithBuffer : public FlatFileSystemService { |
| public: |
| constexpr FlatFileSystemServiceWithBuffer(std::span<Entry*> entry_list) |
| : FlatFileSystemService(entry_list, encoding_buffer_, file_name_buffer_) { |
| } |
| |
| private: |
| static_assert(kMaxFileNameLength > 0u); |
| |
| std::byte encoding_buffer_[EncodingBufferSizeBytes( |
| kMaxFileNameLength, kMinGuaranteedEntriesPerResponse)]; |
| char file_name_buffer_[kMaxFileNameLength]; |
| }; |
| } // namespace pw::file |