1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://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,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "rofs_filesystem.h"
17
18 #include <algorithm>
19 #include <cstdint>
20
21 #include <base/containers/allocator.h>
22 #include <base/containers/array_view.h>
23 #include <base/containers/iterator.h>
24 #include <base/containers/string.h>
25 #include <base/containers/string_view.h>
26 #include <base/containers/type_traits.h>
27 #include <base/containers/unordered_map.h>
28 #include <base/containers/vector.h>
29 #include <base/namespace.h>
30 #include <core/io/intf_directory.h>
31 #include <core/io/intf_file.h>
32 #include <core/log.h>
33 #include <core/namespace.h>
34
35 CORE_BEGIN_NAMESPACE()
36 namespace {
37 using BASE_NS::array_view;
38 using BASE_NS::CloneData;
39 using BASE_NS::move;
40 using BASE_NS::string;
41 using BASE_NS::string_view;
42 using BASE_NS::vector;
43
44 struct FsEntry {
45 const char fname[256];
46 const uint64_t offset;
47 const uint64_t size;
48 };
49
50 /** Read-only memory file. */
51 class ROFSMemoryFile final : public IFile {
52 public:
53 ~ROFSMemoryFile() override = default;
ROFSMemoryFile(const uint8_t * const data,const size_t size)54 ROFSMemoryFile(const uint8_t* const data, const size_t size) : data_(data), size_(size) {}
55 ROFSMemoryFile(const ROFSMemoryFile&) = delete;
56 ROFSMemoryFile(ROFSMemoryFile&&) = delete;
57 ROFSMemoryFile& operator=(const ROFSMemoryFile&) = delete;
58 ROFSMemoryFile& operator=(ROFSMemoryFile&&) = delete;
59
GetMode() const60 Mode GetMode() const override
61 {
62 return IFile::Mode::READ_ONLY;
63 }
64
Close()65 void Close() override {}
66
Read(void * buffer,uint64_t count)67 uint64_t Read(void* buffer, uint64_t count) override
68 {
69 uint64_t toRead = count;
70 if ((index_ + toRead) > size_) {
71 toRead = size_ - index_;
72 }
73
74 if (toRead > 0) {
75 if (toRead <= SIZE_MAX) {
76 if (CloneData(buffer, static_cast<size_t>(count), data_ + index_, static_cast<size_t>(toRead))) {
77 index_ += toRead;
78 }
79 } else {
80 CORE_ASSERT_MSG(false, "Unable to read chunks bigger than (SIZE_MAX) bytes.");
81 toRead = 0;
82 }
83 }
84
85 return toRead;
86 }
87
Write(const void *,uint64_t)88 uint64_t Write(const void* /* buffer */, uint64_t /* count */) override
89 {
90 return 0;
91 }
92
GetLength() const93 uint64_t GetLength() const override
94 {
95 return size_;
96 }
97
Seek(uint64_t offset)98 bool Seek(uint64_t offset) override
99 {
100 if (offset < size_) {
101 index_ = offset;
102 return true;
103 }
104
105 return false;
106 }
107
GetPosition() const108 uint64_t GetPosition() const override
109 {
110 return index_;
111 }
112
113 protected:
Destroy()114 void Destroy() override
115 {
116 delete this;
117 }
118
119 private:
120 uint64_t index_ { 0 };
121 const uint8_t* const data_;
122 const size_t size_;
123 };
124
125 class ROFSMemoryDirectory final : public IDirectory {
126 public:
127 ~ROFSMemoryDirectory() override = default;
128
ROFSMemoryDirectory(const vector<IDirectory::Entry> & contents)129 explicit ROFSMemoryDirectory(const vector<IDirectory::Entry>& contents) : contents_(contents) {}
130
131 ROFSMemoryDirectory(const ROFSMemoryDirectory&) = delete;
132 ROFSMemoryDirectory(ROFSMemoryDirectory&&) = delete;
133 ROFSMemoryDirectory& operator=(const ROFSMemoryDirectory&) = delete;
134 ROFSMemoryDirectory& operator=(ROFSMemoryDirectory&&) = delete;
135
Close()136 void Close() override {}
137
GetEntries() const138 vector<Entry> GetEntries() const override
139 {
140 return contents_;
141 }
142
143 protected:
Destroy()144 void Destroy() override
145 {
146 delete this;
147 }
148
149 private:
150 const vector<IDirectory::Entry>& contents_;
151 };
152
Trim(string_view path)153 string_view Trim(string_view path)
154 {
155 // remove leading and trailing slash..
156 if (!path.empty()) {
157 if (path.back() == '/') {
158 path.remove_suffix(1U);
159 }
160 }
161 if (!path.empty()) {
162 if (path.front() == '/') {
163 path.remove_prefix(1);
164 }
165 }
166 return path;
167 }
168 } // namespace
169
RoFileSystem(const void * const blob,size_t blobSize)170 RoFileSystem::RoFileSystem(const void* const blob, size_t blobSize)
171 {
172 for (size_t i = 0; i < blobSize; i++) {
173 IDirectory::Entry entry;
174 const auto& romEntry = (reinterpret_cast<const FsEntry*>(blob))[i];
175 if (romEntry.fname[0] == 0) {
176 break;
177 }
178 const string_view tmp = romEntry.fname;
179 size_t t = 0;
180 string path;
181 for (;;) {
182 const size_t t2 = tmp.find_first_of('/', t);
183 if (t2 == string::npos) {
184 break;
185 }
186 t = t2;
187 const auto pathLength = path.length();
188 entry.name = tmp.substr(pathLength, t - pathLength);
189 path.reserve(pathLength + entry.name.length() + 1);
190 path += entry.name;
191 path += '/';
192 if (directories_.find(path) == directories_.end()) {
193 // new directory seen
194 entry.type = IDirectory::Entry::DIRECTORY;
195 const auto& parentDir = Trim(path.substr(0, pathLength));
196 if (const auto pos = directories_.find(parentDir); pos != directories_.cend()) {
197 // add each subdirectory only once
198 if (std::none_of(
199 pos->second.cbegin(), pos->second.cend(), [&entry](const IDirectory::Entry& child) {
200 return child.type == entry.type && child.name == entry.name;
201 })) {
202 pos->second.push_back(move(entry));
203 }
204 } else {
205 directories_[string(parentDir)].push_back(move(entry));
206 }
207 directories_[path.substr(0, path.length() - 1)].reserve(1);
208 }
209 t++;
210 }
211 // add the file entry..
212 entry.name = tmp.substr(t);
213 entry.type = IDirectory::Entry::FILE;
214 const auto pathLength = path.length();
215 path.reserve(pathLength + entry.name.length());
216 path += entry.name;
217 directories_[Trim(path.substr(0, pathLength))].push_back(move(entry));
218 auto* data = reinterpret_cast<const uint8_t*>(blob) + romEntry.offset;
219 files_[move(path)] = array_view(data, static_cast<size_t>(romEntry.size));
220 }
221 }
222
GetEntry(const string_view uri)223 IDirectory::Entry RoFileSystem::GetEntry(const string_view uri)
224 {
225 const string_view t = Trim(uri);
226 // check if it's a file first...
227 const auto it = files_.find(t);
228 if (it != files_.end()) {
229 return { IDirectory::Entry::FILE, string(uri), 0 };
230 }
231 // is it a directory then
232 const auto it2 = directories_.find(t);
233 if (it2 != directories_.end()) {
234 return { IDirectory::Entry::DIRECTORY, string(uri), 0 };
235 }
236 // nope. does not exist.
237 return {};
238 }
239
OpenFile(const string_view path)240 IFile::Ptr RoFileSystem::OpenFile(const string_view path)
241 {
242 auto it = files_.find(Trim(path));
243 if (it != files_.end()) {
244 return IFile::Ptr { new ROFSMemoryFile(it->second.data(), it->second.size()) };
245 }
246 return IFile::Ptr();
247 }
248
CreateFile(const string_view)249 IFile::Ptr RoFileSystem::CreateFile(const string_view /* path */)
250 {
251 return IFile::Ptr();
252 }
253
DeleteFile(const string_view)254 bool RoFileSystem::DeleteFile(const string_view /* path */)
255 {
256 return false;
257 }
258
OpenDirectory(const string_view path)259 IDirectory::Ptr RoFileSystem::OpenDirectory(const string_view path)
260 {
261 const string_view t = Trim(path);
262 auto it = directories_.find(t);
263 if (it != directories_.end()) {
264 return IDirectory::Ptr { new ROFSMemoryDirectory(it->second) };
265 }
266 return IDirectory::Ptr();
267 }
268
CreateDirectory(const string_view)269 IDirectory::Ptr RoFileSystem::CreateDirectory(const string_view /* path */)
270 {
271 return IDirectory::Ptr();
272 }
273
DeleteDirectory(const string_view)274 bool RoFileSystem::DeleteDirectory(const string_view /* path */)
275 {
276 return false;
277 }
278
Rename(const string_view,const string_view)279 bool RoFileSystem::Rename(const string_view /* fromPath */, const string_view /* toPath */)
280 {
281 return false;
282 }
283
GetUriPaths(const string_view) const284 vector<string> RoFileSystem::GetUriPaths(const string_view) const
285 {
286 return {};
287 }
288 CORE_END_NAMESPACE()
289