• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "io/std_directory.h"
17 
18 #include <algorithm>
19 
20 #if defined(__OHOS_PLATFORM__)
21 #undef HAS_FILESYSTEM
22 #else
23 #ifdef __has_include
24 #if __has_include(<filesystem>)
25 #include <filesystem>
26 #define HAS_FILESYSTEM
27 #include <chrono>
28 #include <system_error>
29 #endif
30 #endif
31 #endif
32 
33 #if !defined(HAS_FILESYSTEM)
34 #include <dirent.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #endif
38 
39 #include <cstddef>
40 #include <cstdint>
41 
42 #include <base/containers/string.h>
43 #include <base/containers/string_view.h>
44 #include <base/containers/type_traits.h>
45 #include <base/containers/unique_ptr.h>
46 #include <base/containers/vector.h>
47 #include <base/namespace.h>
48 #include <core/io/intf_directory.h>
49 #include <core/log.h>
50 #include <core/namespace.h>
51 
52 CORE_BEGIN_NAMESPACE()
53 using BASE_NS::make_unique;
54 using BASE_NS::string;
55 using BASE_NS::string_view;
56 using BASE_NS::vector;
57 
58 #if defined(HAS_FILESYSTEM)
59 
60 // Hiding the directory implementation from the header.
61 struct DirImpl {
DirImplDirImpl62     explicit DirImpl(string_view path) : path_(path) {}
63     string path_;
64 };
65 
66 namespace {
GetTimeStamp(const std::filesystem::directory_entry & entry)67 uint64_t GetTimeStamp(const std::filesystem::directory_entry& entry)
68 {
69     return static_cast<uint64_t>(entry.last_write_time().time_since_epoch().count());
70 }
71 
GetEntryType(const std::filesystem::directory_entry & entry)72 IDirectory::Entry::Type GetEntryType(const std::filesystem::directory_entry& entry)
73 {
74     if (entry.is_directory()) {
75         return IDirectory::Entry::DIRECTORY;
76     }
77     if (entry.is_regular_file()) {
78         return IDirectory::Entry::FILE;
79     }
80 
81     return IDirectory::Entry::UNKNOWN;
82 }
83 
U8Path(string_view str)84 std::filesystem::path U8Path(string_view str)
85 {
86     return std::filesystem::u8path(str.begin().ptr(), str.end().ptr());
87 }
88 } // namespace
89 
StdDirectory(BASE_NS::unique_ptr<DirImpl> dir)90 StdDirectory::StdDirectory(BASE_NS::unique_ptr<DirImpl> dir) : dir_(BASE_NS::move(dir)) {}
91 
~StdDirectory()92 StdDirectory::~StdDirectory()
93 {
94     Close();
95 }
96 
Close()97 void StdDirectory::Close()
98 {
99     if (dir_) {
100         dir_.reset();
101     }
102 }
103 
Create(BASE_NS::string_view path)104 IDirectory::Ptr StdDirectory::Create(BASE_NS::string_view path)
105 {
106     std::error_code ec;
107     if (std::filesystem::create_directory(U8Path(path), ec)) {
108         // Directory creation successful.
109         return IDirectory::Ptr { BASE_NS::make_unique<StdDirectory>(make_unique<DirImpl>(path)).release() };
110     }
111     return {};
112 }
113 
Open(const string_view path)114 IDirectory::Ptr StdDirectory::Open(const string_view path)
115 {
116     std::error_code ec;
117     if (std::filesystem::is_directory(U8Path(path), ec)) {
118         return IDirectory::Ptr { BASE_NS::make_unique<StdDirectory>(make_unique<DirImpl>(path)).release() };
119     }
120     return {};
121 }
122 
DirectoryExists(const string_view path)123 bool StdDirectory::DirectoryExists(const string_view path)
124 {
125     std::error_code ec;
126     return (std::filesystem::is_directory(U8Path(path), ec));
127 }
128 
GetEntries() const129 vector<IDirectory::Entry> StdDirectory::GetEntries() const
130 {
131     CORE_ASSERT_MSG(dir_, "Dir not open");
132     vector<IDirectory::Entry> result;
133     if (dir_) {
134         std::error_code ec;
135         for (auto& iter : std::filesystem::directory_iterator(U8Path(dir_->path_), ec)) {
136             const auto filename = iter.path().filename().u8string();
137             auto str = string(filename.c_str(), filename.length());
138             result.push_back(IDirectory::Entry { GetEntryType(iter), move(str), GetTimeStamp(iter) });
139         }
140     }
141 
142     return result;
143 }
144 
145 #else // HAS_FILESYSTEM not defined
146 
147 // Hiding the directory implementation from the header.
148 struct DirImpl {
DirImplDirImpl149     DirImpl(const string_view path, DIR* aDir) : path_(path), dir_(aDir) {}
150     string path_;
151     DIR* dir_;
152 };
153 
154 namespace {
CreateEntry(const string_view path,const dirent & entry)155 IDirectory::Entry CreateEntry(const string_view path, const dirent& entry)
156 {
157     const auto fullPath = path + string(entry.d_name);
158     struct stat statBuf {};
159     const auto statResult = (stat(fullPath.c_str(), &statBuf) == 0);
160 
161     IDirectory::Entry::Type type = IDirectory::Entry::UNKNOWN;
162 #ifdef _DIRENT_HAVE_D_TYPE
163     switch (entry.d_type) {
164         case DT_DIR:
165             type = IDirectory::Entry::DIRECTORY;
166             break;
167         case DT_REG:
168             type = IDirectory::Entry::FILE;
169             break;
170         default:
171             type = IDirectory::Entry::UNKNOWN;
172             break;
173     }
174 #else
175     if (statResult) {
176         if (S_ISDIR(statBuf.st_mode)) {
177             type = IDirectory::Entry::DIRECTORY;
178         } else if (S_ISREG(statBuf.st_mode)) {
179             type = IDirectory::Entry::FILE;
180         }
181     }
182 #endif
183 
184     // Get the timestamp for the directory entry.
185     uint64_t timestamp = 0;
186     if (statResult) {
187         timestamp = static_cast<uint64_t>(statBuf.st_mtime);
188     }
189 
190     return IDirectory::Entry { type, entry.d_name, timestamp };
191 }
192 } // namespace
193 
StdDirectory(BASE_NS::unique_ptr<DirImpl> dir)194 StdDirectory::StdDirectory(BASE_NS::unique_ptr<DirImpl> dir) : dir_(BASE_NS::move(dir)) {}
195 
~StdDirectory()196 StdDirectory::~StdDirectory()
197 {
198     Close();
199 }
200 
Close()201 void StdDirectory::Close()
202 {
203     if (dir_) {
204         closedir(dir_->dir_);
205         dir_.reset();
206     }
207 }
208 
Create(BASE_NS::string_view path)209 IDirectory::Ptr StdDirectory::Create(BASE_NS::string_view path)
210 {
211     int result = mkdir(string(path).c_str(), S_IRWXU | S_IRWXO);
212     if (result == 0) {
213         // Directory creation successful.
214         return Open(path);
215     }
216     return {};
217 }
218 
Open(const string_view path)219 IDirectory::Ptr StdDirectory::Open(const string_view path)
220 {
221     DIR* dir = opendir(string(path).c_str());
222     if (dir) {
223         return IDirectory::Ptr { make_unique<StdDirectory>(make_unique<DirImpl>(path, dir)).release() };
224     }
225     return {};
226 }
227 
DirectoryExists(const string_view path)228 bool StdDirectory::DirectoryExists(const string_view path)
229 {
230     struct stat statBuf {};
231     return (stat(path.data(), &statBuf) == 0) && S_ISDIR(statBuf.st_mode);
232 }
233 
GetEntries() const234 vector<IDirectory::Entry> StdDirectory::GetEntries() const
235 {
236     CORE_ASSERT_MSG(dir_, "Dir not open");
237     vector<IDirectory::Entry> result;
238     if (dir_) {
239         // Go back to start.
240         rewinddir(dir_->dir_);
241 
242         // Iterate all entries and write to result.
243         struct dirent* directoryEntry = nullptr;
244         while ((directoryEntry = readdir(dir_->dir_)) != nullptr) {
245             if (!strcmp(directoryEntry->d_name, ".") || !strcmp(directoryEntry->d_name, "..")) {
246                 continue;
247             }
248             result.push_back(CreateEntry(dir_->path_, *directoryEntry));
249         }
250     }
251 
252     return result;
253 }
254 
255 #endif // HAS_FILESYSTEM
256 
ResolveAbsolutePath(const string_view pathIn,bool isDirectory)257 string StdDirectory::ResolveAbsolutePath(const string_view pathIn, bool isDirectory)
258 {
259     string absolutePath;
260     auto path = pathIn;
261 #ifdef _WIN32
262     // remove the '/' slash..
263     path = path.substr(1);
264 #endif
265 
266 #ifdef HAS_FILESYSTEM
267     std::error_code ec;
268     std::filesystem::path fsPath = std::filesystem::canonical(U8Path(path), ec);
269     if (ec.value() == 0) {
270         const auto pathStr = fsPath.string();
271         absolutePath.assign(pathStr.data(), pathStr.size());
272     }
273 #elif defined(_WIN32)
274     char resolvedPath[_MAX_PATH] = {};
275     if (_fullpath(resolvedPath, string(path).c_str(), _MAX_PATH) != nullptr) {
276         if (isDirectory) {
277             auto handle = opendir(resolvedPath);
278             if (handle) {
279                 closedir(handle);
280                 absolutePath = resolvedPath;
281             }
282         } else {
283             auto handle = fopen(resolvedPath, "r");
284             if (handle) {
285                 fclose(handle);
286                 absolutePath = resolvedPath;
287             }
288         }
289     }
290 #elif defined(__OHOS_PLATFORM__) || defined(__linux__)
291     char resolvedPath[PATH_MAX];
292     if (realpath(string(path).c_str(), resolvedPath) != nullptr) {
293         absolutePath = resolvedPath;
294     }
295 #endif
296 
297     FormatPath(absolutePath, isDirectory);
298 
299     return absolutePath;
300 }
301 
FormatPath(string & path,bool isDirectory)302 void StdDirectory::FormatPath(string& path, bool isDirectory)
303 {
304     const size_t length = path.length();
305 
306     // Make directory separators consistent.
307     std::replace(path.begin(), path.end(), '\\', '/');
308 
309     // Ensure there is last separator in place.
310     if (!path.empty() && isDirectory) {
311         if (path[length - 1] != '/') {
312             path += '/';
313         }
314     }
315 }
316 
GetDirName(const string_view fullPath)317 string StdDirectory::GetDirName(const string_view fullPath)
318 {
319     auto path = string(fullPath);
320     StdDirectory::FormatPath(path, false);
321 
322     const size_t separatorPos = path.find_last_of('/');
323     if (separatorPos == string::npos) {
324         return ".";
325     }
326     path.resize(separatorPos);
327     return path;
328 }
329 
GetBaseName(const string_view fullPath)330 string StdDirectory::GetBaseName(const string_view fullPath)
331 {
332     auto path = string(fullPath);
333     StdDirectory::FormatPath(path, false);
334 
335     const size_t separatorPos = path.find_last_of('/');
336     if (separatorPos == string::npos) {
337         return path;
338     }
339     path.erase(0, separatorPos + 1);
340     return path;
341 }
342 CORE_END_NAMESPACE()
343