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
15 #define PW_LOG_MODULE_NAME "FS"
16
17 #include "pw_file/flat_file_system.h"
18
19 #include <cstddef>
20 #include <cstdint>
21 #include <string_view>
22
23 #include "pw_assert/check.h"
24 #include "pw_bytes/span.h"
25 #include "pw_file/file.pwpb.h"
26 #include "pw_log/log.h"
27 #include "pw_protobuf/decoder.h"
28 #include "pw_protobuf/encoder.h"
29 #include "pw_protobuf/serialized_size.h"
30 #include "pw_rpc/raw/server_reader_writer.h"
31 #include "pw_span/span.h"
32 #include "pw_status/status.h"
33 #include "pw_status/status_with_size.h"
34
35 namespace pw::file {
36
37 using Entry = FlatFileSystemService::Entry;
38
EnumerateFile(Entry & entry,pwpb::ListResponse::StreamEncoder & output_encoder)39 Status FlatFileSystemService::EnumerateFile(
40 Entry& entry, pwpb::ListResponse::StreamEncoder& output_encoder) {
41 StatusWithSize sws = entry.Name(file_name_buffer_);
42 if (!sws.ok()) {
43 return sws.status();
44 }
45 {
46 pwpb::Path::StreamEncoder encoder = output_encoder.GetPathsEncoder();
47
48 encoder
49 .WritePath(reinterpret_cast<const char*>(file_name_buffer_.data()),
50 sws.size())
51 .IgnoreError();
52 encoder.WriteSizeBytes(entry.SizeBytes()).IgnoreError();
53 encoder.WritePermissions(entry.Permissions()).IgnoreError();
54 encoder.WriteFileId(entry.FileId()).IgnoreError();
55 }
56 return output_encoder.status();
57 }
58
EnumerateAllFiles(RawServerWriter & writer)59 void FlatFileSystemService::EnumerateAllFiles(RawServerWriter& writer) {
60 for (Entry* entry : entries_) {
61 PW_DCHECK_NOTNULL(entry);
62 // For now, don't try to pack entries.
63 pwpb::ListResponse::MemoryEncoder encoder(encoding_buffer_);
64 if (Status status = EnumerateFile(*entry, encoder); !status.ok()) {
65 if (status != Status::NotFound()) {
66 PW_LOG_ERROR("Failed to enumerate file (id: %u) with status %d",
67 static_cast<unsigned>(entry->FileId()),
68 static_cast<int>(status.code()));
69 }
70 continue;
71 }
72
73 Status write_status = writer.Write(encoder);
74 if (!write_status.ok()) {
75 writer.Finish(write_status)
76 .IgnoreError(); // TODO: b/242598609 - Handle Status properly
77 return;
78 }
79 }
80 writer.Finish(OkStatus())
81 .IgnoreError(); // TODO: b/242598609 - Handle Status properly
82 }
83
List(ConstByteSpan request,RawServerWriter & writer)84 void FlatFileSystemService::List(ConstByteSpan request,
85 RawServerWriter& writer) {
86 protobuf::Decoder decoder(request);
87 // If a file name was provided, try and find and enumerate the file.
88 while (decoder.Next().ok()) {
89 if (decoder.FieldNumber() !=
90 static_cast<uint32_t>(pwpb::ListRequest::Fields::kPath)) {
91 continue;
92 }
93
94 std::string_view file_name_view;
95 if (!decoder.ReadString(&file_name_view).ok() || file_name_view.empty()) {
96 writer.Finish(Status::DataLoss())
97 .IgnoreError(); // TODO: b/242598609 - Handle Status properly
98 return;
99 }
100
101 // Find and enumerate the file requested.
102 Result<Entry*> result = FindFile(file_name_view);
103 if (!result.ok()) {
104 writer.Finish(result.status())
105 .IgnoreError(); // TODO: b/242598609 - Handle Status properly
106 return;
107 }
108
109 pwpb::ListResponse::MemoryEncoder encoder(encoding_buffer_);
110 Status proto_encode_status = EnumerateFile(*result.value(), encoder);
111 if (!proto_encode_status.ok()) {
112 writer.Finish(proto_encode_status)
113 .IgnoreError(); // TODO: b/242598609 - Handle Status properly
114 return;
115 }
116
117 writer.Finish(writer.Write(encoder))
118 .IgnoreError(); // TODO: b/242598609 - Handle Status properly
119 return;
120 }
121
122 // If no path was provided in the ListRequest, just enumerate everything.
123 EnumerateAllFiles(writer);
124 }
125
Delete(ConstByteSpan request,rpc::RawUnaryResponder & responder)126 void FlatFileSystemService::Delete(ConstByteSpan request,
127 rpc::RawUnaryResponder& responder) {
128 protobuf::Decoder decoder(request);
129 while (decoder.Next().ok()) {
130 if (decoder.FieldNumber() !=
131 static_cast<uint32_t>(pwpb::DeleteRequest::Fields::kPath)) {
132 continue;
133 }
134
135 std::string_view file_name_view;
136 if (!decoder.ReadString(&file_name_view).ok()) {
137 responder.Finish({}, Status::DataLoss()).IgnoreError();
138 return;
139 }
140 responder.Finish({}, FindAndDeleteFile(file_name_view)).IgnoreError();
141 return;
142 }
143 responder.Finish({}, Status::InvalidArgument()).IgnoreError();
144 }
145
FindFile(std::string_view file_name)146 Result<Entry*> FlatFileSystemService::FindFile(std::string_view file_name) {
147 Status search_status;
148 for (Entry* entry : entries_) {
149 PW_DCHECK_NOTNULL(entry);
150 StatusWithSize sws = entry->Name(file_name_buffer_);
151
152 // If there not an exact file name length match, don't try and check against
153 // a prefix.
154 if (!sws.ok() || file_name.length() != sws.size()) {
155 if (sws.status() != Status::NotFound()) {
156 PW_LOG_ERROR("Failed to read file name (id: %u) with status %d",
157 static_cast<unsigned>(entry->FileId()),
158 static_cast<int>(sws.status().code()));
159 }
160 continue;
161 }
162
163 if (memcmp(file_name.data(), file_name_buffer_.data(), file_name.size()) ==
164 0) {
165 return entry;
166 }
167 }
168
169 search_status.Update(Status::NotFound());
170 return search_status;
171 }
172
FindAndDeleteFile(std::string_view file_name)173 Status FlatFileSystemService::FindAndDeleteFile(std::string_view file_name) {
174 Result<Entry*> result = FindFile(file_name);
175 if (!result.ok()) {
176 return result.status();
177 }
178
179 return result.value()->Delete();
180 }
181
182 } // namespace pw::file
183