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 <cstddef>
17#include <cstdint>
18
19#include <base/containers/string.h>
20#include <base/containers/string_view.h>
21#include <base/containers/type_traits.h>
22#include <base/containers/unique_ptr.h>
23#include <base/containers/vector.h>
24#include <base/namespace.h>
25#include <base/util/uid.h>
26#include <core/implementation_uids.h>
27#include <core/io/intf_directory.h>
28#include <core/io/intf_file_manager.h>
29#include <core/io/intf_file_monitor.h>
30#include <core/io/intf_file_system.h>
31#include <core/io/intf_filesystem_api.h>
32#include <core/namespace.h>
33#include <core/plugin/intf_class_factory.h>
34#include <core/plugin/intf_interface.h>
35#include <core/plugin/intf_plugin.h>
36
37#include "dev/file_monitor.h"
38#include "io/file_manager.h"
39#include "io/memory_filesystem.h"
40#include "io/path_tools.h"
41#include "io/rofs_filesystem.h"
42#include "io/std_filesystem.h"
43#include "util/string_util.h"
44
45CORE_BEGIN_NAMESPACE();
46namespace {
47using BASE_NS::string;
48using BASE_NS::string_view;
49using BASE_NS::Uid;
50using BASE_NS::vector;
51
52class FileMonitorImpl final : public IFileMonitor {
53public:
54    const IInterface* GetInterface(const Uid& uid) const override;
55    IInterface* GetInterface(const Uid& uid) override;
56    void Ref() override;
57    void Unref() override;
58
59    void Initialize(IFileManager&) override;
60    bool AddPath(const string_view path) override;
61    bool RemovePath(const string_view path) override;
62    void ScanModifications(vector<string>& added, vector<string>& removed, vector<string>& modified) override;
63
64    FileMonitorImpl() = default;
65    explicit FileMonitorImpl(IFileManager& manager)
66    {
67        Initialize(manager);
68    }
69
70    BASE_NS::unique_ptr<FileMonitor> fileMonitor_;
71    uint32_t refCount_ { 0 };
72};
73
74class FilesystemApi final : public IFileSystemApi {
75public:
76    string basePath_;
77    IFilesystem::Ptr rootFs_;
78    FilesystemApi() : basePath_(GetCurrentDirectory()), rootFs_(CreateStdFileSystem()) {}
79
80    const IInterface* GetInterface(const Uid& uid) const override
81    {
82        return const_cast<FilesystemApi*>(this)->GetInterface(uid);
83    }
84    IInterface* GetInterface(const Uid& uid) override
85    {
86        if ((uid == IFileSystemApi::UID) || (uid == IClassFactory::UID) || (uid == IInterface::UID)) {
87            return this;
88        }
89        return nullptr;
90    }
91    void Ref() override {}
92    void Unref() override {}
93
94    IInterface::Ptr CreateInstance(const Uid& uid) override
95    {
96        if (uid == UID_FILE_MONITOR) {
97            return IInterface::Ptr(new FileMonitorImpl());
98        }
99        if (uid == UID_FILE_MANAGER) {
100            return IInterface::Ptr(new FileManager());
101        }
102        return IInterface::Ptr();
103    }
104
105    IFileManager::Ptr CreateFilemanager() override
106    {
107        return IInterface::Ptr(new FileManager());
108    }
109    IFileMonitor::Ptr CreateFilemonitor(IFileManager& manager) override
110    {
111        return IInterface::Ptr(new FileMonitorImpl(manager));
112    }
113    string ResolvePath(string_view inPathRaw) const
114    {
115#if _WIN32
116        string_view curDrive;
117        string_view curPath;
118        string_view curFilename;
119        string_view curExt;
120        SplitPath(basePath_, curDrive, curPath, curFilename, curExt);
121
122        if (inPathRaw.empty()) {
123            return {};
124        }
125        // fix slashes. (just change \\ to /)
126        string_view pathIn = inPathRaw;
127        string tmp;
128        if (pathIn.find("\\") != string_view::npos) {
129            tmp = pathIn;
130            StringUtil::FindAndReplaceAll(tmp, "\\", "/");
131            pathIn = tmp;
132        }
133
134        string_view drive;
135        string_view path;
136        string_view filename;
137        string_view ext;
138        SplitPath(pathIn, drive, path, filename, ext);
139        string res = "/";
140        if (drive.empty()) {
141            // relative to current drive then
142            res += curDrive;
143        } else {
144            res += drive;
145        }
146        res += ":";
147        string normalizedPath;
148        if (path.empty()) {
149            return "";
150        }
151        if (path[0] != '/') {
152            // relative path.
153            normalizedPath = NormalizePath(curPath + path);
154        } else {
155            normalizedPath = NormalizePath(path);
156        }
157        if (normalizedPath.empty()) {
158            return "";
159        }
160        return res + normalizedPath;
161#else
162        if (IsRelative(inPathRaw)) {
163            return NormalizePath(basePath_ + inPathRaw);
164        }
165        return NormalizePath(inPathRaw);
166#endif
167    }
168
169    IFilesystem::Ptr CreateStdFileSystem() override
170    {
171        return IFilesystem::Ptr(new StdFilesystem("/"));
172    }
173
174    IFilesystem::Ptr CreateStdFileSystem(string_view rootPathIn) override
175    {
176        string_view protocol;
177        string_view path;
178        if (ParseUri(rootPathIn, protocol, path)) {
179            if (protocol != "file") {
180                return {};
181            }
182            rootPathIn = path;
183        }
184        auto rootPath = ResolvePath(rootPathIn);
185        if (!rootPath.empty()) {
186            auto entry = rootFs_->GetEntry(rootPath);
187            if (entry.type == IDirectory::Entry::DIRECTORY) {
188                return IFilesystem::Ptr(new StdFilesystem(rootPath));
189            }
190        }
191        return {};
192    }
193    IFilesystem::Ptr CreateMemFileSystem() override
194    {
195        return IFilesystem::Ptr(new MemoryFilesystem());
196    }
197    IFilesystem::Ptr CreateROFilesystem(const void* const data, uint64_t size) override
198    {
199        return IFilesystem::Ptr { new RoFileSystem(data, static_cast<size_t>(size)) };
200    }
201};
202} // namespace
203
204const IInterface* FileMonitorImpl::GetInterface(const Uid& uid) const
205{
206    if ((uid == IFileMonitor::UID) || (uid == IInterface::UID)) {
207        return this;
208    }
209    return nullptr;
210}
211
212IInterface* FileMonitorImpl::GetInterface(const Uid& uid)
213{
214    if ((uid == IFileMonitor::UID) || (uid == IInterface::UID)) {
215        return this;
216    }
217    return nullptr;
218}
219
220void FileMonitorImpl::Ref()
221{
222    refCount_++;
223}
224
225void FileMonitorImpl::Unref()
226{
227    if (--refCount_ == 0) {
228        delete this;
229    }
230}
231
232void FileMonitorImpl::Initialize(IFileManager& manager)
233{
234    fileMonitor_ = BASE_NS::make_unique<FileMonitor>(manager);
235}
236
237bool FileMonitorImpl::AddPath(const string_view path)
238{
239    if (fileMonitor_) {
240        return fileMonitor_->AddPath(path);
241    }
242    return false;
243}
244
245bool FileMonitorImpl::RemovePath(const string_view path)
246{
247    if (fileMonitor_) {
248        return fileMonitor_->RemovePath(path);
249    }
250    return false;
251}
252
253void FileMonitorImpl::ScanModifications(vector<string>& added, vector<string>& removed, vector<string>& modified)
254{
255    added.clear();
256    removed.clear();
257    modified.clear();
258    if (fileMonitor_) {
259        fileMonitor_->ScanModifications(added, removed, modified);
260    }
261}
262
263IInterface* CreateFileMonitor(IClassFactory& /* registry */, PluginToken /* token */)
264{
265    return new FileMonitorImpl();
266}
267IInterface* GetFileApiFactory(IClassRegister& /* registry */, PluginToken /* token */)
268{
269    static FilesystemApi fact;
270    return &fact;
271}
272CORE_END_NAMESPACE()
273