xref: /aosp_15_r20/external/pigweed/pw_file/flat_file_system.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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