LibSWOC++ 1.5.14
Solid Wall of C++
Loading...
Searching...
No Matches
swoc_file.cc
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2// SPDX-License-Identifier: Apache-2.0
3// Copyright Apache Software Foundation 2019
8
9#include <variant>
10
11#include <fcntl.h>
12#include <unistd.h>
13#include <dirent.h>
14#include "swoc/swoc_file.h"
15#include "swoc/bwf_base.h"
16
17using namespace swoc::literals;
18
19namespace swoc { inline namespace SWOC_VERSION_NS {
20namespace file {
21
22path
24 TextView parent{_path};
26 return parent ? parent : "/"_tv;
27}
28
29path
31 if (!_path.empty() && _path.front() == SEPARATOR) {
32 return _path.substr(1);
33 }
34 return *this;
35}
36
37auto
38path::filename() const -> self_type {
39 auto const idx = _path.find_last_of(SEPARATOR);
40 return idx == std::string::npos ? self_type(_path) : _path.substr(idx + 1);
41}
42
43path &
44path::operator/=(std::string_view that) {
45 if (!that.empty()) { // don't waste time appending nothing.
46 if (that.front() == SEPARATOR || _path.empty()) {
47 _path.assign(that);
48 } else {
49 if (_path.back() == SEPARATOR) {
50 _path.reserve(_path.size() + that.size());
51 } else {
52 _path.reserve(_path.size() + that.size() + 1);
53 _path.push_back(SEPARATOR);
54 }
55 _path.append(that);
56 }
57 }
58 return *this;
59}
60
61void
62file_status::init() {
63 switch (_stat.st_mode & S_IFMT) {
64 case S_IFREG:
65 _type = file_type::regular;
66 break;
67 case S_IFDIR:
68 _type = file_type::directory;
69 break;
70 case S_IFLNK:
71 _type = file_type::symlink;
72 break;
73 case S_IFBLK:
74 _type = file_type::block;
75 break;
76 case S_IFCHR:
77 _type = file_type::character;
78 break;
79 case S_IFIFO:
80 _type = file_type::fifo;
81 break;
82 case S_IFSOCK:
83 _type = file_type::socket;
84 break;
85 default:
86 _type = file_type::unknown;
87 break;
88 }
89}
90
91file_status
92status(path const &file, std::error_code &ec) noexcept {
93 file_status zret;
94 if (::stat(file.c_str(), &zret._stat) >= 0) {
95 ec.clear();
96 zret.init();
97 } else {
98 ec = std::error_code(errno, std::system_category());
99 if (errno == ENOENT) {
100 zret._type = file_type::not_found;
101 }
102 }
103 return zret;
104}
105
106int
108 return fs._stat.st_mode & S_IFMT;
109}
110
111uintmax_t
113 return fs._stat.st_size;
114}
115
116bool
117exists(const path &p) {
118 std::error_code ec;
119 auto fs = status(p, ec);
120 return exists(fs);
121}
122
123path
124absolute(path const &src, std::error_code &ec) {
125 char buff[4096];
126 ec.clear();
127 if (src.is_absolute()) {
128 return src;
129 }
130 auto s = realpath(src.c_str(), buff);
131 if (s == nullptr) {
132 if (errno == ENAMETOOLONG) {
133 s = realpath(src.c_str(), nullptr);
134 if (s != nullptr) {
135 path zret{s};
136 free(s);
137 return zret;
138 }
139 }
140 ec = std::error_code(errno, std::system_category());
141 return {};
142 }
143 return path{s};
144}
145
146namespace {
147
148inline file_time_type
149chrono_cast(timespec const &ts) {
150 using namespace std::chrono;
151 return system_clock::time_point{duration_cast<system_clock::duration>(seconds{ts.tv_sec} + nanoseconds{ts.tv_nsec})};
152}
153
154// Apple has different names for these members, need to have accessors that account for that.
155// Under -O2 these are completely elided.
156template <typename S>
157auto
158a_time(S const &s, meta::CaseTag<0>) -> decltype(S::st_atim) {
159 return s.st_atim;
160}
161
162template <typename S>
163auto
164a_time(S const &s, meta::CaseTag<1>) -> decltype(S::st_atimespec) {
165 return s.st_atimespec;
166}
167
168template <typename S>
169auto
170m_time(S const &s, meta::CaseTag<0>) -> decltype(S::st_mtim) {
171 return s.st_mtim;
172}
173
174template <typename S>
175auto
176m_time(S const &s, meta::CaseTag<1>) -> decltype(S::st_mtimespec) {
177 return s.st_mtimespec;
178}
179
180template <typename S>
181auto
182c_time(S const &s, meta::CaseTag<0>) -> decltype(S::st_ctim) {
183 return s.st_ctim;
184}
185
186template <typename S>
187auto
188c_time(S const &s, meta::CaseTag<1>) -> decltype(S::st_ctimespec) {
189 return s.st_ctimespec;
190}
191
192} // namespace
193
194file_time_type
196 return chrono_cast(m_time(fs._stat, meta::CaseArg));
197}
198
199file_time_type
201 return chrono_cast(a_time(fs._stat, meta::CaseArg));
202}
203
204file_time_type
206 return chrono_cast(c_time(fs._stat, meta::CaseArg));
207}
208
209file_time_type
210last_write_time(path const &p, std::error_code &ec) {
211 auto fs = status(p, ec);
212 if (ec) {
213 return file_time_type::min();
214 }
215 return last_write_time(fs);
216}
217
218bool
219is_readable(const path &p) {
220 return 0 == access(p.c_str(), R_OK);
221}
222
223path
225 /* ISO/IEC 9945 (POSIX): The path supplied by the first environment variable found in the list TMPDIR, TMP, TEMP, TEMPDIR.
226 * If none of these are found, "/tmp"
227 * */
228 for (char const *tp : {"TMPDIR", "TMP", "TEMPDIR"}) {
229 if (auto v = ::getenv(tp); v) {
230 return path(v);
231 }
232 }
233 return path("/tmp");
234}
235
236path
238 char buff[PATH_MAX + 1];
239 if (auto p = ::getcwd(buff, sizeof(buff)); p) {
240 return path{buff};
241#if !__FreeBSD__ && !__APPLE__ // Freakin' Apple and FreeBSD.
242 } else if (ERANGE == errno) {
243 swoc::unique_malloc<char> raw{::get_current_dir_name()};
244 return path{raw.get()};
245#endif
246 }
247 return {};
248}
249
250path
251canonical(const path &p, std::error_code &ec) {
252 if (p.empty()) {
253 ec = std::error_code(EINVAL, std::system_category());
254 return {};
255 }
256
257 char buf[PATH_MAX + 1];
258
259 if (auto rp = ::realpath(p.c_str(), buf); rp) {
260 return path{rp};
261 }
262
263 if (auto rp = ::realpath(p.c_str(), nullptr); rp) {
264 return path{rp};
265 }
266
267 ec = std::error_code(errno, std::system_category());
268 return {};
269}
270
271bool
272create_directory(const path &path, std::error_code &ec, mode_t mode) noexcept {
273 if (path.empty()) {
274 ec = std::error_code(EINVAL, std::system_category());
275 return false;
276 }
277
278 ec.clear();
279 if (::mkdir(path.c_str(), mode) != 0) {
280 if (EEXIST == errno) {
281 std::error_code local_ec;
282 auto fs = status(path, local_ec);
283 if (!local_ec && is_dir(fs)) {
284 return true;
285 }
286 }
287 ec = std::error_code(errno, std::system_category());
288 return false;
289 }
290 return true;
291}
292
293bool
294create_directories(const path &p, std::error_code &ec, mode_t mode) noexcept {
295 TextView text(p.string());
296
297 if (text.empty()) {
298 ec = std::error_code(EINVAL, std::system_category());
299 return false;
300 }
301
302 path path;
303
304 if (text.front() == path::SEPARATOR) {
305 path = text.prefix(1); // copy leading separator.
306 ++text;
307 if (!text) {
308 ec.clear();
309 return true; // Tried to create root directory, it's already tehre.
310 }
311 }
312
313 path.reserve(p.string().size());
314
315 while (text) {
316 auto elt = text.take_prefix_at(path::SEPARATOR);
317 path /= elt;
318 if (!create_directory(path, ec, mode)) {
319 return false;
320 }
321 }
322
323 return true;
324}
325
326bool
327copy(const path &from, const path &to, std::error_code &ec) {
328 static constexpr size_t BUF_SIZE = 65536;
329 std::error_code local_ec;
330 char buf[BUF_SIZE];
331 swoc::MemSpan span{buf};
332
333 if (from.empty() || to.empty()) {
334 ec = std::error_code(EINVAL, std::system_category());
335 return false;
336 }
337
338 ec.clear();
339
340 unique_fd src_fd{::open(from.c_str(), O_RDONLY)};
341 if (NO_FD == src_fd) {
342 ec = std::error_code(errno, std::system_category());
343 return false;
344 }
345 auto src_fs = file::status(from, local_ec);
346
347 path final_to;
348 if (auto fs = file::status(to, local_ec); !(local_ec && ENOENT == local_ec.value()) && is_dir(fs)) {
349 final_to = to / from.filename();
350 } else {
351 final_to = to;
352 }
353
354 unique_fd dst_fd{::open(final_to.c_str(), O_WRONLY | O_CREAT, src_fs.mode())};
355 if (NO_FD == dst_fd) {
356 ec = std::error_code(errno, std::system_category());
357 return false;
358 }
359
360 while (true) {
361 if (auto n = read(src_fd, span.data(), span.size()); n > 0) {
362 if (::write(dst_fd, span.data(), n) < n) {
363 ec = std::error_code(errno, std::system_category());
364 break;
365 }
366 } else {
367 break;
368 }
369 }
370
371 return true;
372}
373
374uintmax_t
375remove_all(const path &p, std::error_code &ec) {
376 // coverity TOCTOU - issue is doing stat before doing operation. Stupid complaint, ignore.
377 DIR *dir = nullptr;
378 struct dirent *entry = nullptr;
379 std::error_code err;
380 uintmax_t zret = 0;
381
382 struct ::stat s {};
383 if (p.empty()) {
384 ec = std::error_code(EINVAL, std::system_category());
385 return zret;
386 } else if (::stat(p.c_str(), &s) < 0) {
387 ec = std::error_code(errno, std::system_category());
388 return zret;
389 } else if (S_ISREG(s.st_mode)) { // regular file, try to remove it!
390 // coverity[toctou : SUPPRESS]
391 if (unlink(p.c_str()) != 0) {
392 ec = std::error_code(errno, std::system_category());
393 } else {
394 ++zret;
395 }
396 return zret;
397 } else if (!S_ISDIR(s.st_mode)) { // not a directory
398 ec = std::error_code(ENOTDIR, std::system_category());
399 return zret;
400 }
401 // Invariant - @a p is a directory.
402
403 // recursively remove nested files and directories
404 // coverity[toctou : SUPPRESS]
405 if (nullptr == (dir = opendir(p.c_str()))) {
406 ec = std::error_code(errno, std::system_category());
407 return zret;
408 }
409
410 auto child = p; // Minimize string allocations / re-allocations.
411 while (nullptr != (entry = readdir(dir))) {
412 if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) {
413 continue;
414 }
415 child = p;
416 child /= entry->d_name;
417 zret += remove_all(child, ec);
418 }
419
420 if (0 != rmdir(p.c_str())) {
421 ec = std::error_code(errno, std::system_category());
422 }
423 ++zret;
424
425 closedir(dir);
426 return zret;
427}
428
429bool
430remove(path const &p, std::error_code &ec) {
431 // coverity TOCTOU - issue is doing stat before doing operation. Stupid complaint, ignore.
432 struct ::stat fs {};
433 if (p.empty()) {
434 ec = std::error_code(EINVAL, std::system_category());
435 } else if (::stat(p.c_str(), &fs) < 0) {
436 ec = std::error_code(errno, std::system_category());
437 } else if (S_ISREG(fs.st_mode)) { // regular file, try to remove it!
438 // coverity[toctou : SUPPRESS]
439 if (unlink(p.c_str()) != 0) {
440 ec = std::error_code(errno, std::system_category());
441 }
442 } else if (S_ISDIR(fs.st_mode)) { // not a directory
443 // coverity[toctou : SUPPRESS]
444 if (rmdir(p.c_str()) != 0) {
445 ec = std::error_code(errno, std::system_category());
446 }
447 } else {
448 ec = std::error_code(EINVAL, std::system_category());
449 }
450 return !ec;
451}
452
453std::string
454load(const path &p, std::error_code &ec) {
455 std::string zret;
456 ec.clear();
457 if (unique_fd fd(::open(p.c_str(), O_RDONLY)); fd < 0) {
458 ec = std::error_code(errno, std::system_category());
459 } else {
460 struct stat info {};
461 if (0 != ::fstat(fd, &info)) {
462 ec = std::error_code(errno, std::system_category());
463 } else {
464 auto n = info.st_size;
465 zret.resize(n);
466 auto read_len = ::read(fd, zret.data(), n);
467 if (read_len < n) {
468 ec = std::error_code(errno, std::system_category());
469 }
470 }
471 }
472 return zret;
473}
474
475} // namespace file
476
478bwformat(BufferWriter &w, bwf::Spec const &spec, file::path const &p) {
479 return bwformat(w, spec, p.string());
480}
481
482}} // namespace swoc::SWOC_VERSION_NS
constexpr self_type prefix(size_t n) const noexcept
self_type take_prefix_at(char c)
self_type split_suffix_at(char c)
Information about a file.
Definition swoc_file.h:202
friend file_time_type last_write_time(file_status const &fs)
Definition swoc_file.cc:195
friend file_time_type access_time(file_status const &fs)
Definition swoc_file.cc:200
struct::stat _stat
File information.
Definition swoc_file.h:213
friend uintmax_t file_size(const self_type &)
Size of the file or block device.
Definition swoc_file.cc:112
friend self_type status(const path &file, std::error_code &ec) noexcept
Definition swoc_file.cc:92
friend int file_type(const self_type &)
Return the file type value.
Definition swoc_file.cc:107
friend file_time_type status_time(file_status const &fs)
Definition swoc_file.cc:205
bool empty() const
Check if the path is empty.
Definition swoc_file.h:414
static constexpr char SEPARATOR
Default path separator.
Definition swoc_file.h:98
self_type & reserve(size_t n)
Definition swoc_file.h:479
char const * c_str() const
Access the path explicitly.
Definition swoc_file.h:399
self_type relative_path() const
Definition swoc_file.cc:30
self_type filename() const
Definition swoc_file.cc:38
self_type & operator/=(const self_type &that)
Definition swoc_file.h:429
std::string _path
File path.
Definition swoc_file.h:198
self_type parent_path() const
Path of the parent.
Definition swoc_file.cc:23
std::string const & string() const
The path as a string.
Definition swoc_file.h:404
bool is_absolute() const
Check if the path is absolute.
Definition swoc_file.h:419
For template deduction guides.
Definition ArenaWriter.cc:9
BufferWriter & bwformat(BufferWriter &w, bwf::Spec const &spec, std::string_view sv)
Definition bw_format.cc:649
std::unique_ptr< T, detail::malloc_liberator > unique_malloc
A variant of unique_ptr that handles memory from malloc.
Definition MemSpan.h:1720
int strcmp(const std::string_view &lhs, const std::string_view &rhs)
Scoped container for a file descriptor.
Definition swoc_file.h:43
bool exists(const path &p)
Check if path exists.
Definition swoc_file.cc:117
path temp_directory_path()
Directory location suitable for temporary files.
Definition swoc_file.cc:224
uintmax_t remove_all(const path &p, std::error_code &ec)
Definition swoc_file.cc:375
file_time_type last_write_time(file_status const &fs)
Definition swoc_file.cc:195
std::string load(const path &p, std::error_code &ec)
Definition swoc_file.cc:454
bool copy(const path &from, const path &to, std::error_code &ec)
Definition swoc_file.cc:327
bool create_directories(const path &p, std::error_code &ec, mode_t mode) noexcept
Definition swoc_file.cc:294
bool is_readable(const path &p)
Check if file is readable.
Definition swoc_file.cc:219
path absolute(path const &src, std::error_code &ec)
Definition swoc_file.cc:124
path canonical(const path &p, std::error_code &ec)
Definition swoc_file.cc:251
bool create_directory(const path &path, std::error_code &ec, mode_t mode) noexcept
Definition swoc_file.cc:272
bool remove(path const &p, std::error_code &ec)
Definition swoc_file.cc:430
file_status status(path const &file, std::error_code &ec) noexcept
Definition swoc_file.cc:92
path current_path()
Current working directory.
Definition swoc_file.cc:237
bool is_dir(const file_status &p)
Check if the path is to a directory.
Definition swoc_file.h:520