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