1 // Copyright 2021 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include <cstddef> 17 #include <cstdint> 18 #include <string_view> 19 20 #include "pw_bytes/span.h" 21 #include "pw_file/file.pwpb.h" 22 #include "pw_file/file.raw_rpc.pb.h" 23 #include "pw_protobuf/serialized_size.h" 24 #include "pw_result/result.h" 25 #include "pw_rpc/raw/server_reader_writer.h" 26 #include "pw_span/span.h" 27 #include "pw_status/status.h" 28 #include "pw_status/status_with_size.h" 29 30 namespace pw::file { 31 32 // This implements the pw.file.FileSystem RPC service. This implementation 33 // has a strict limitation that everything is treated as if the file system 34 // was "flat" (i.e. no directories). This means there's no concept of logical 35 // directories, despite any "path like" naming that may be employed by a user. 36 class FlatFileSystemService 37 : public pw_rpc::raw::FileSystem::Service<FlatFileSystemService> { 38 public: 39 class Entry { 40 public: 41 using FilePermissions = ::pw::file::pwpb::Path::Permissions; 42 using Id = uint32_t; 43 44 Entry() = default; 45 virtual ~Entry() = default; 46 47 // All readable files MUST be named, and names must be globally unique to 48 // prevent ambiguity. Unnamed file entries will NOT be enumerated by a 49 // FlatFileSystemService. The returned status must indicate the length 50 // of the string written to `dest`, and should NOT include any null 51 // terminator that may have been written. 52 // 53 // Note: The bounded string written to `dest` is not expected to be 54 // null-terminated, and should be treated like a std::string_view. 55 // 56 // Returns: 57 // OK - Successfully read file name to `dest`. 58 // NOT_FOUND - No file to enumerate for this entry. 59 // RESOURCE_EXHAUSTED - `dest` buffer too small to fit the full file name. 60 virtual StatusWithSize Name(span<char> dest) = 0; 61 62 virtual size_t SizeBytes() = 0; 63 virtual FilePermissions Permissions() const = 0; 64 65 // Deleting a file, if allowed, should cause the backing data store to be 66 // cleared. Read-only files should also no longer enumerate (i.e. Name() 67 // should return NOT_FOUND). Write-only and read/write files may still 68 // enumerate but with SizeBytes() reporting zero. 69 virtual Status Delete() = 0; 70 71 // File IDs must be globally unique, and map to a pw_transfer 72 // TransferService read/write handler. 73 virtual Id FileId() const = 0; 74 }; 75 76 // Returns the size of encoding buffer guaranteed to support encoding 77 // minimum_entries paths with file names up max_file_name_length. 78 static constexpr size_t EncodingBufferSizeBytes(size_t max_file_name_length, 79 size_t minimum_entries = 1) { 80 return minimum_entries * 81 protobuf::SizeOfDelimitedField( 82 pwpb::ListResponse::Fields::kPaths, 83 EncodedPathProtoSizeBytes(max_file_name_length)); 84 } 85 86 // Constructs a flat file system from a static list of file entries. 87 // 88 // Args: 89 // entry_list - A list of pointers to all Entry objects that may 90 // contain files. These pointers may not be null. The span's underlying 91 // buffer must outlive this object. 92 // encoding_buffer - Used internally by this class to encode its responses. 93 // file_name_buffer - Used internally by this class to find and enumerate 94 // files. Should be large enough to hold the longest expected file name. 95 // The span's underlying buffer must outlive this object. 96 // max_file_name_length - Number of bytes to reserve for the file name. FlatFileSystemService(span<Entry * > entry_list,span<std::byte> encoding_buffer,span<char> file_name_buffer)97 constexpr FlatFileSystemService(span<Entry*> entry_list, 98 span<std::byte> encoding_buffer, 99 span<char> file_name_buffer) 100 : encoding_buffer_(encoding_buffer), 101 file_name_buffer_(file_name_buffer), 102 entries_(entry_list) {} 103 104 // Method definitions for pw.file.FileSystem. 105 void List(ConstByteSpan request, RawServerWriter& writer); 106 107 // Returns: 108 // OK - File successfully deleted. 109 // NOT_FOUND - Could not find 110 void Delete(ConstByteSpan request, rpc::RawUnaryResponder& responder); 111 112 private: 113 // Returns the maximum size of a single encoded Path proto. EncodedPathProtoSizeBytes(size_t max_file_name_length)114 static constexpr size_t EncodedPathProtoSizeBytes( 115 size_t max_file_name_length) { 116 return protobuf::SizeOfFieldString(pwpb::Path::Fields::kPath, 117 max_file_name_length) + 118 protobuf::SizeOfFieldEnum(pwpb::Path::Fields::kPermissions, 119 pwpb::Path::Permissions::READ_AND_WRITE) + 120 protobuf::SizeOfFieldUint32(pwpb::Path::Fields::kSizeBytes) + 121 protobuf::SizeOfFieldUint32(pwpb::Path::Fields::kFileId); 122 } 123 124 Result<Entry*> FindFile(std::string_view file_name); 125 Status FindAndDeleteFile(std::string_view file_name); 126 127 Status EnumerateFile(Entry& entry, 128 pwpb::ListResponse::StreamEncoder& output_encoder); 129 void EnumerateAllFiles(RawServerWriter& writer); 130 131 const span<std::byte> encoding_buffer_; 132 const span<char> file_name_buffer_; 133 const span<Entry*> entries_; 134 }; 135 136 // Provides the encoding and file name buffers to a FlatFileSystemService. 137 template <unsigned kMaxFileNameLength, 138 unsigned kMinGuaranteedEntriesPerResponse = 1> 139 class FlatFileSystemServiceWithBuffer : public FlatFileSystemService { 140 public: FlatFileSystemServiceWithBuffer(span<Entry * > entry_list)141 constexpr FlatFileSystemServiceWithBuffer(span<Entry*> entry_list) 142 : FlatFileSystemService(entry_list, encoding_buffer_, file_name_buffer_) { 143 } 144 145 private: 146 static_assert(kMaxFileNameLength > 0u); 147 148 std::byte encoding_buffer_[EncodingBufferSizeBytes( 149 kMaxFileNameLength, kMinGuaranteedEntriesPerResponse)]; 150 char file_name_buffer_[kMaxFileNameLength]; 151 }; 152 } // namespace pw::file 153