1 /*
2 * Copyright (c) 2022 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/vector.h>
28 #include <base/namespace.h>
29 #include <core/io/intf_directory.h>
30 #include <core/io/intf_file.h>
31 #include <core/log.h>
32 #include <core/namespace.h>
33
34 CORE_BEGIN_NAMESPACE()
35 namespace {
36 using BASE_NS::array_view;
37 using BASE_NS::CloneData;
38 using BASE_NS::move;
39 using BASE_NS::string;
40 using BASE_NS::string_view;
41 using BASE_NS::vector;
42
43 struct FsEntry {
44 const char fname[256];
45 const uint64_t offset;
46 const uint64_t size;
47 };
48
49 /** Read-only memory file. */
50 class ROFSMemoryFile final : public IFile {
51 public:
52 ~ROFSMemoryFile() override = default;
ROFSMemoryFile(const uint8_t * const data,const size_t size)53 ROFSMemoryFile(const uint8_t* const data, const size_t size) : data_(data), size_(size) {}
54 ROFSMemoryFile(const ROFSMemoryFile&) = delete;
55 ROFSMemoryFile(ROFSMemoryFile&&) = delete;
56 ROFSMemoryFile& operator=(const ROFSMemoryFile&) = delete;
57 ROFSMemoryFile& operator=(ROFSMemoryFile&&) = delete;
58
GetMode() const59 Mode GetMode() const override
60 {
61 return IFile::Mode::READ_ONLY;
62 }
63
Close()64 void Close() override {}
65
Read(void * buffer,uint64_t count)66 uint64_t Read(void* buffer, uint64_t count) override
67 {
68 uint64_t toRead = count;
69 if ((index_ + toRead) > size_) {
70 toRead = size_ - index_;
71 }
72
73 if (toRead > 0) {
74 if (toRead <= SIZE_MAX) {
75 if (CloneData(buffer, static_cast<size_t>(count), data_ + index_, static_cast<size_t>(toRead))) {
76 index_ += toRead;
77 }
78 } else {
79 CORE_ASSERT_MSG(false, "Unable to read chunks bigger than (SIZE_MAX) bytes.");
80 toRead = 0;
81 }
82 }
83
84 return toRead;
85 }
86
Write(const void *,uint64_t)87 uint64_t Write(const void* /* buffer */, uint64_t /* count */) override
88 {
89 return 0;
90 }
91
Append(const void *,uint64_t,uint64_t)92 uint64_t Append(const void* /* buffer */, uint64_t /* count */, uint64_t /* chunkSize */) override
93 {
94 return 0;
95 }
96
GetLength() const97 uint64_t GetLength() const override
98 {
99 return size_;
100 }
101
Seek(uint64_t offset)102 bool Seek(uint64_t offset) override
103 {
104 if (offset < size_) {
105 index_ = offset;
106 return true;
107 }
108
109 return false;
110 }
111
GetPosition() const112 uint64_t GetPosition() const override
113 {
114 return index_;
115 }
116
117 protected:
Destroy()118 void Destroy() override
119 {
120 delete this;
121 }
122
123 private:
124 uint64_t index_ { 0 };
125 const uint8_t* const data_;
126 const size_t size_;
127 };
128
129 class ROFSMemoryDirectory final : public IDirectory {
130 public:
131 ~ROFSMemoryDirectory() override = default;
132
ROFSMemoryDirectory(const vector<IDirectory::Entry> & contents)133 explicit ROFSMemoryDirectory(const vector<IDirectory::Entry>& contents) : contents_(contents) {}
134
135 ROFSMemoryDirectory(const ROFSMemoryDirectory&) = delete;
136 ROFSMemoryDirectory(ROFSMemoryDirectory&&) = delete;
137 ROFSMemoryDirectory& operator=(const ROFSMemoryDirectory&) = delete;
138 ROFSMemoryDirectory& operator=(ROFSMemoryDirectory&&) = delete;
139
Close()140 void Close() override {}
141
GetEntries() const142 vector<Entry> GetEntries() const override
143 {
144 return contents_;
145 }
146
147 protected:
Destroy()148 void Destroy() override
149 {
150 delete this;
151 }
152
153 private:
154 const vector<IDirectory::Entry>& contents_;
155 };
156
Trim(string_view path)157 string_view Trim(string_view path)
158 {
159 // remove leading and trailing slash..
160 if (!path.empty()) {
161 if (path.back() == '/') {
162 path.remove_suffix(1U);
163 }
164 }
165 if (!path.empty()) {
166 if (path.front() == '/') {
167 path.remove_prefix(1);
168 }
169 }
170 return path;
171 }
172 } // namespace
173
RoFileSystem(const void * const blob,size_t blobSize)174 RoFileSystem::RoFileSystem(const void* const blob, size_t blobSize)
175 {
176 for (const auto& romEntry : array_view(static_cast<const FsEntry*>(blob), blobSize)) {
177 if (romEntry.fname[0] == 0) {
178 break;
179 }
180 IDirectory::Entry entry;
181 const string_view tmp = romEntry.fname;
182 size_t t = 0;
183 string path;
184 // parse the rom entry name and add all missing directories.
185 for (;;) {
186 const size_t t2 = tmp.find_first_of('/', t);
187 if (t2 == string_view::npos) {
188 break;
189 }
190 t = t2;
191 const auto pathLength = path.length();
192 entry.name = tmp.substr(pathLength, t - pathLength);
193 path.reserve(pathLength + entry.name.length() + 1U);
194 path += entry.name;
195 path += '/';
196 ++t;
197 if (directories_.find(path) != directories_.end()) {
198 continue;
199 }
200 // new directory seen
201 entry.type = IDirectory::Entry::DIRECTORY;
202 const auto& parentDir = Trim(path.substr(0, pathLength));
203 if (const auto pos = directories_.find(parentDir); pos != directories_.cend()) {
204 // add each subdirectory only once
205 if (std::none_of(pos->second.cbegin(), pos->second.cend(), [&entry](const IDirectory::Entry& child) {
206 return child.type == entry.type && child.name == entry.name;
207 })) {
208 pos->second.push_back(move(entry));
209 }
210 } else {
211 directories_[parentDir].push_back(move(entry));
212 }
213 directories_[path.substr(0, path.length() - 1)].reserve(1);
214 }
215 // add the file entry..
216 entry.name = tmp.substr(t);
217 entry.type = IDirectory::Entry::FILE;
218 const auto pathLength = path.length();
219 path.reserve(pathLength + entry.name.length());
220 path += entry.name;
221 directories_[Trim(path.substr(0, pathLength))].push_back(move(entry));
222 auto* data = reinterpret_cast<const uint8_t*>(blob) + romEntry.offset;
223 files_[move(path)] = array_view(data, static_cast<size_t>(romEntry.size));
224 }
225 }
226
GetEntry(const string_view uri)227 IDirectory::Entry RoFileSystem::GetEntry(const string_view uri)
228 {
229 const string_view t = Trim(uri);
230 // check if it's a file first...
231 const auto it = files_.find(t);
232 if (it != files_.end()) {
233 return { IDirectory::Entry::FILE, string(uri), 0 };
234 }
235 // is it a directory then
236 const auto it2 = directories_.find(t);
237 if (it2 != directories_.end()) {
238 return { IDirectory::Entry::DIRECTORY, string(uri), 0 };
239 }
240 // nope. does not exist.
241 return {};
242 }
243
OpenFile(const string_view path,const IFile::Mode mode)244 IFile::Ptr RoFileSystem::OpenFile(const string_view path, const IFile::Mode mode)
245 {
246 if (mode == IFile::Mode::READ_ONLY) {
247 auto it = files_.find(Trim(path));
248 if (it != files_.end()) {
249 return IFile::Ptr { new ROFSMemoryFile(it->second.data(), it->second.size()) };
250 }
251 }
252 return {};
253 }
254
CreateFile(const string_view)255 IFile::Ptr RoFileSystem::CreateFile(const string_view /* path */)
256 {
257 return {};
258 }
259
DeleteFile(const string_view)260 bool RoFileSystem::DeleteFile(const string_view /* path */)
261 {
262 return false;
263 }
264
FileExists(const string_view path) const265 bool RoFileSystem::FileExists(const string_view path) const
266 {
267 return files_.contains(Trim(path));
268 }
269
OpenDirectory(const string_view path)270 IDirectory::Ptr RoFileSystem::OpenDirectory(const string_view path)
271 {
272 auto it = directories_.find(Trim(path));
273 if (it != directories_.end()) {
274 return IDirectory::Ptr { new ROFSMemoryDirectory(it->second) };
275 }
276 return {};
277 }
278
CreateDirectory(const string_view)279 IDirectory::Ptr RoFileSystem::CreateDirectory(const string_view /* path */)
280 {
281 return {};
282 }
283
DeleteDirectory(const string_view)284 bool RoFileSystem::DeleteDirectory(const string_view /* path */)
285 {
286 return false;
287 }
288
DirectoryExists(const string_view path) const289 bool RoFileSystem::DirectoryExists(const string_view path) const
290 {
291 return directories_.contains(Trim(path));
292 }
293
Rename(const string_view,const string_view)294 bool RoFileSystem::Rename(const string_view /* fromPath */, const string_view /* toPath */)
295 {
296 return false;
297 }
298
GetUriPaths(const string_view) const299 vector<string> RoFileSystem::GetUriPaths(const string_view) const
300 {
301 return {};
302 }
303 CORE_END_NAMESPACE()
304