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