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