1 /**
2 * Copyright 2019 Huawei Technologies Co., Ltd
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "minddata/dataset/util/path.h"
17
18 #include <sys/stat.h>
19 #include <fcntl.h>
20 #include <unistd.h>
21 #include <new>
22 #include <sstream>
23
24 #include "./securec.h"
25 #include "utils/ms_utils.h"
26 #include "minddata/dataset/util/log_adapter.h"
27
28 namespace mindspore {
29 namespace dataset {
30 #if defined(_WIN32) || defined(_WIN64)
31 char Path::separator_ = '\\';
32 #else
33 char Path::separator_ = '/';
34 #endif
35
Path(const std::string & s)36 Path::Path(const std::string &s) : path_(s) {}
37
Path(const char * p)38 Path::Path(const char *p) : path_(p) {}
39
Path(const Path & p)40 Path::Path(const Path &p) : path_(p.path_) {}
41
operator =(const Path & p)42 Path &Path::operator=(const Path &p) {
43 if (&p != this) {
44 this->path_ = p.path_;
45 }
46 return *this;
47 }
48
operator =(Path && p)49 Path &Path::operator=(Path &&p) noexcept {
50 if (&p != this) {
51 this->path_ = std::move(p.path_);
52 }
53 return *this;
54 }
55
Path(Path && p)56 Path::Path(Path &&p) noexcept { this->path_ = std::move(p.path_); }
57
operator +(const Path & p)58 Path Path::operator+(const Path &p) {
59 std::string q = path_ + p.ToString();
60 return Path(q);
61 }
62
operator +(const std::string & p)63 Path Path::operator+(const std::string &p) {
64 std::string q = path_ + p;
65 return Path(q);
66 }
67
operator +(const char * p)68 Path Path::operator+(const char *p) {
69 std::string q = path_ + p;
70 return Path(q);
71 }
72
operator +=(const Path & rhs)73 Path &Path::operator+=(const Path &rhs) {
74 path_ += rhs.ToString();
75 return *this;
76 }
77
operator +=(const std::string & p)78 Path &Path::operator+=(const std::string &p) {
79 path_ += p;
80 return *this;
81 }
82
operator +=(const char * p)83 Path &Path::operator+=(const char *p) {
84 path_ += p;
85 return *this;
86 }
87
operator /(const Path & p)88 Path Path::operator/(const Path &p) {
89 std::string q = path_ + separator_ + p.ToString();
90 return Path(q);
91 }
92
operator /(const std::string & p)93 Path Path::operator/(const std::string &p) {
94 std::string q = path_ + separator_ + p;
95 return Path(q);
96 }
97
operator /(const char * p)98 Path Path::operator/(const char *p) {
99 std::string q = path_ + separator_ + p;
100 return Path(q);
101 }
102
Extension() const103 std::string Path::Extension() const {
104 std::size_t found = path_.find_last_of('.');
105 if (found != std::string::npos) {
106 return path_.substr(found);
107 } else {
108 return std::string("");
109 }
110 }
111
Exists()112 bool Path::Exists() {
113 struct stat sb;
114 int rc = stat(common::SafeCStr(path_), &sb);
115 if (rc == -1 && errno != ENOENT) {
116 MS_LOG(WARNING) << "Unable to query the status of " << path_ << ". Errno = " << errno << ".";
117 }
118 return (rc == 0);
119 }
120
IsDirectory()121 bool Path::IsDirectory() {
122 struct stat sb;
123 int rc = stat(common::SafeCStr(path_), &sb);
124 if (rc == 0) {
125 return S_ISDIR(sb.st_mode);
126 } else {
127 return false;
128 }
129 }
130
CreateDirectory(bool is_common_dir)131 Status Path::CreateDirectory(bool is_common_dir) {
132 if (!Exists()) {
133 #if defined(_WIN32) || defined(_WIN64)
134 int rc = mkdir(common::SafeCStr(path_));
135 #else
136 int rc = mkdir(common::SafeCStr(path_), S_IRUSR | S_IWUSR | S_IXUSR);
137 if (rc == 0 && is_common_dir) {
138 rc = chmod(common::SafeCStr(path_), S_IRWXU | S_IRWXG | S_IRWXO);
139 }
140 #endif
141 if (rc) {
142 std::ostringstream oss;
143 oss << "Unable to create directory " << path_ << ". Errno = " << errno;
144 RETURN_STATUS_UNEXPECTED(oss.str());
145 }
146 return Status::OK();
147 } else {
148 if (IsDirectory()) {
149 return Status::OK();
150 } else {
151 std::ostringstream oss;
152 oss << "Unable to create directory " << path_ << ". It exists but is not a directory";
153 RETURN_STATUS_UNEXPECTED(oss.str());
154 }
155 }
156 }
157
ParentPath()158 std::string Path::ParentPath() {
159 std::string r("");
160 std::size_t found = path_.find_last_of(separator_);
161 if (found != std::string::npos) {
162 if (found == 0) {
163 r += separator_;
164 } else {
165 r = std::string(path_.substr(0, found));
166 }
167 }
168 return r;
169 }
170
CreateDirectories(bool is_common_dir)171 Status Path::CreateDirectories(bool is_common_dir) {
172 if (IsDirectory()) {
173 MS_LOG(DEBUG) << "Directory " << ToString() << " already exists.";
174 return Status::OK();
175 } else {
176 MS_LOG(DEBUG) << "Creating directory " << ToString() << ".";
177 std::string parent = ParentPath();
178 if (!parent.empty()) {
179 if (Path(parent).CreateDirectories(is_common_dir)) {
180 return CreateDirectory(is_common_dir);
181 }
182 } else {
183 return CreateDirectory(is_common_dir);
184 }
185 }
186 return Status::OK();
187 }
188
CreateCommonDirectories()189 Status Path::CreateCommonDirectories() { return CreateDirectories(true); }
190
Remove()191 Status Path::Remove() {
192 if (Exists()) {
193 if (IsDirectory()) {
194 errno_t err = rmdir(common::SafeCStr(path_));
195 if (err == -1) {
196 std::ostringstream oss;
197 oss << "Unable to delete directory " << path_ << ". Errno = " << errno;
198 RETURN_STATUS_UNEXPECTED(oss.str());
199 }
200 } else {
201 errno_t err = unlink(common::SafeCStr(path_));
202 if (err == -1) {
203 std::ostringstream oss;
204 oss << "Unable to delete file " << path_ << ". Errno = " << errno;
205 RETURN_STATUS_UNEXPECTED(oss.str());
206 }
207 }
208 }
209 return Status::OK();
210 }
211
CreateFile(int * file_descriptor)212 Status Path::CreateFile(int *file_descriptor) { return OpenFile(file_descriptor, true); }
213
OpenFile(int * file_descriptor,bool create)214 Status Path::OpenFile(int *file_descriptor, bool create) {
215 int fd;
216 if (file_descriptor == nullptr) {
217 RETURN_STATUS_UNEXPECTED("null pointer");
218 }
219 if (IsDirectory()) {
220 std::ostringstream oss;
221 oss << "Unable to create file " << path_ << " which is a directory.";
222 RETURN_STATUS_UNEXPECTED(oss.str());
223 }
224 // Convert to canonical form.
225 if (strlen(common::SafeCStr(path_)) >= PATH_MAX) {
226 RETURN_STATUS_UNEXPECTED(strerror(errno));
227 }
228 char canonical_path[PATH_MAX] = {0x00};
229 #if defined(_WIN32) || defined(_WIN64)
230 auto err = _fullpath(canonical_path, common::SafeCStr(path_), PATH_MAX);
231 #else
232 auto err = realpath(common::SafeCStr(path_), canonical_path);
233 #endif
234 if (err == nullptr) {
235 if (errno == ENOENT && create) {
236 // File doesn't exist and we are to create it. Let's break it down.
237 auto file_part = Basename();
238 auto parent_part = ParentPath();
239 #if defined(_WIN32) || defined(_WIN64)
240 auto parent_err = _fullpath(canonical_path, common::SafeCStr(parent_part), PATH_MAX);
241 #else
242 auto parent_err = realpath(common::SafeCStr(parent_part), canonical_path);
243 #endif
244 if (parent_err == nullptr) {
245 RETURN_STATUS_UNEXPECTED(strerror(errno));
246 }
247 auto cur_inx = strlen(canonical_path);
248 if (cur_inx + file_part.length() >= PATH_MAX) {
249 RETURN_STATUS_UNEXPECTED(strerror(errno));
250 }
251 canonical_path[cur_inx++] = separator_;
252 if (strncpy_s(canonical_path + cur_inx, PATH_MAX - cur_inx, common::SafeCStr(file_part), file_part.length()) !=
253 EOK) {
254 RETURN_STATUS_UNEXPECTED(strerror(errno));
255 }
256 } else {
257 RETURN_STATUS_UNEXPECTED(strerror(errno));
258 }
259 }
260 if (create) {
261 fd = open(canonical_path, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP);
262 } else {
263 fd = open(canonical_path, O_RDWR);
264 }
265 if (fd == -1) {
266 RETURN_STATUS_UNEXPECTED(strerror(errno));
267 }
268 *file_descriptor = fd;
269 return Status::OK();
270 }
271
CloseFile(int fd) const272 Status Path::CloseFile(int fd) const {
273 if (close(fd) < 0) {
274 RETURN_STATUS_UNEXPECTED(strerror(errno));
275 }
276 return Status::OK();
277 }
278
TruncateFile(int fd) const279 Status Path::TruncateFile(int fd) const {
280 int rc = ftruncate(fd, 0);
281 if (rc == 0) {
282 return Status::OK();
283 } else {
284 RETURN_STATUS_UNEXPECTED(strerror(errno));
285 }
286 }
287
Basename()288 std::string Path::Basename() {
289 std::size_t found = path_.find_last_of(separator_);
290 if (found != std::string::npos) {
291 return path_.substr(found + 1);
292 } else {
293 return path_;
294 }
295 }
296
OpenDirectory(Path * f)297 std::shared_ptr<Path::DirIterator> Path::DirIterator::OpenDirectory(Path *f) {
298 auto it = new (std::nothrow) DirIterator(f);
299
300 if (it == nullptr) {
301 return nullptr;
302 }
303
304 if (it->dp_) {
305 return std::shared_ptr<DirIterator>(it);
306 } else {
307 delete it;
308 return nullptr;
309 }
310 }
311
~DirIterator()312 Path::DirIterator::~DirIterator() {
313 if (dp_) {
314 (void)closedir(dp_);
315 }
316 dp_ = nullptr;
317 dir_ = nullptr;
318 entry_ = nullptr;
319 }
320
DirIterator(Path * f)321 Path::DirIterator::DirIterator(Path *f) : dir_(f), dp_(nullptr), entry_(nullptr) {
322 MS_LOG(DEBUG) << "Open directory " << f->ToString() << ".";
323 dp_ = opendir(f->ToString().c_str());
324 }
325
HasNext()326 bool Path::DirIterator::HasNext() {
327 do {
328 entry_ = readdir(dp_);
329 if (entry_) {
330 if (strcmp(entry_->d_name, ".") == 0 || strcmp(entry_->d_name, "..") == 0) {
331 continue;
332 }
333 }
334 break;
335 } while (true);
336 return (entry_ != nullptr);
337 }
338
Next()339 Path Path::DirIterator::Next() { return (*(this->dir_) / Path(entry_->d_name)); }
340
RealPath(const std::string & path,std::string & realpath_str)341 Status Path::RealPath(const std::string &path, std::string &realpath_str) {
342 char real_path[PATH_MAX] = {0};
343 // input_path is only file_name
344 #if defined(_WIN32) || defined(_WIN64)
345 CHECK_FAIL_RETURN_UNEXPECTED(path.length() < PATH_MAX,
346 "The length of path: " + path + " exceeds limit: " + std::to_string(PATH_MAX));
347 auto ret = _fullpath(real_path, common::SafeCStr(path), PATH_MAX);
348 CHECK_FAIL_RETURN_UNEXPECTED(ret != nullptr, "The file " + path + " does not exist.");
349 #else
350 CHECK_FAIL_RETURN_UNEXPECTED(path.length() < NAME_MAX,
351 "The length of path: " + path + " exceeds limit: " + std::to_string(NAME_MAX));
352 auto ret = realpath(common::SafeCStr(path), real_path);
353 CHECK_FAIL_RETURN_UNEXPECTED(ret != nullptr, "The file " + path + " does not exist.");
354 #endif
355 realpath_str = std::string(real_path);
356 return Status::OK();
357 }
358
operator <<(std::ostream & os,const Path & s)359 std::ostream &operator<<(std::ostream &os, const Path &s) {
360 os << s.path_;
361 return os;
362 }
363 } // namespace dataset
364 } // namespace mindspore
365