]> git.ipfire.org Git - thirdparty/gcc.git/blob - libstdc++-v3/src/filesystem/ops-common.h
Update copyright years.
[thirdparty/gcc.git] / libstdc++-v3 / src / filesystem / ops-common.h
1 // Filesystem operation utilities -*- C++ -*-
2
3 // Copyright (C) 2014-2022 Free Software Foundation, Inc.
4 //
5 // This file is part of the GNU ISO C++ Library. This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 3, or (at your option)
9 // any later version.
10
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15
16 // Under Section 7 of GPL version 3, you are granted additional
17 // permissions described in the GCC Runtime Library Exception, version
18 // 3.1, as published by the Free Software Foundation.
19
20 // You should have received a copy of the GNU General Public License and
21 // a copy of the GCC Runtime Library Exception along with this program;
22 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 // <http://www.gnu.org/licenses/>.
24
25 #ifndef _GLIBCXX_OPS_COMMON_H
26 #define _GLIBCXX_OPS_COMMON_H 1
27
28 #include <chrono>
29 #include <bits/move.h> // std::__exchange
30
31 #ifdef _GLIBCXX_HAVE_UNISTD_H
32 # include <unistd.h>
33 # ifdef _GLIBCXX_HAVE_FCNTL_H
34 # include <fcntl.h> // AT_FDCWD, O_TRUNC etc.
35 # endif
36 # if defined(_GLIBCXX_HAVE_SYS_STAT_H) && defined(_GLIBCXX_HAVE_SYS_TYPES_H)
37 # include <sys/types.h>
38 # include <sys/stat.h>
39 # endif
40 #endif
41 #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
42 # include <utime.h> // utime
43 #endif
44
45 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
46 # include <wchar.h>
47 #endif
48
49 #ifdef NEED_DO_COPY_FILE
50 # include <filesystem>
51 # include <ext/stdio_filebuf.h>
52 # ifdef _GLIBCXX_USE_SENDFILE
53 # include <sys/sendfile.h> // sendfile
54 # endif
55 #endif
56
57 namespace std _GLIBCXX_VISIBILITY(default)
58 {
59 _GLIBCXX_BEGIN_NAMESPACE_VERSION
60
61 // Get the last OS error (for POSIX this is just errno).
62 inline error_code
63 __last_system_error() noexcept
64 {
65 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
66 return {(int)::GetLastError(), std::system_category()};
67 #else
68 return {errno, std::generic_category()};
69 #endif
70 }
71
72 // Get an error code indicating unsupported functionality.
73 //
74 // This should be used when a function is unable to behave as specified
75 // due to an incomplete or partial implementation, e.g.
76 // filesystem::equivalent(a, b) if is_other(a) && is_other(b) is true.
77 //
78 // Use errc::function_not_supported for functions that are entirely
79 // unimplemented, e.g. create_symlink on Windows.
80 //
81 // Use errc::invalid_argument for requests to perform operations outside
82 // the spec, e.g. trying to copy a directory using filesystem::copy_file.
83 inline error_code
84 __unsupported() noexcept
85 {
86 #if defined ENOTSUP
87 return std::make_error_code(std::errc::not_supported);
88 #elif defined EOPNOTSUPP
89 // This is supposed to be for socket operations
90 return std::make_error_code(std::errc::operation_not_supported);
91 #else
92 return std::make_error_code(std::errc::invalid_argument);
93 #endif
94 }
95
96 namespace filesystem
97 {
98 namespace __gnu_posix
99 {
100 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
101 // Adapt the Windows _wxxx functions to look like POSIX xxx, but for wchar_t*.
102 inline int open(const wchar_t* path, int flags)
103 { return ::_wopen(path, flags); }
104
105 inline int open(const wchar_t* path, int flags, int mode)
106 { return ::_wopen(path, flags, mode); }
107
108 inline int close(int fd)
109 { return ::_close(fd); }
110
111 typedef struct ::__stat64 stat_type;
112
113 inline int stat(const wchar_t* path, stat_type* buffer)
114 { return ::_wstat64(path, buffer); }
115
116 inline int lstat(const wchar_t* path, stat_type* buffer)
117 {
118 // FIXME: symlinks not currently supported
119 return stat(path, buffer);
120 }
121
122 using ::mode_t;
123
124 inline int chmod(const wchar_t* path, mode_t mode)
125 { return ::_wchmod(path, mode); }
126
127 inline int mkdir(const wchar_t* path, mode_t)
128 { return ::_wmkdir(path); }
129
130 inline wchar_t* getcwd(wchar_t* buf, size_t size)
131 { return ::_wgetcwd(buf, size > (size_t)INT_MAX ? INT_MAX : (int)size); }
132
133 inline int chdir(const wchar_t* path)
134 { return ::_wchdir(path); }
135
136 #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
137 using utimbuf = _utimbuf;
138
139 inline int utime(const wchar_t* path, utimbuf* times)
140 { return ::_wutime(path, times); }
141 #endif
142
143 inline int rename(const wchar_t* oldname, const wchar_t* newname)
144 {
145 if (MoveFileExW(oldname, newname,
146 MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
147 return 0;
148 if (GetLastError() == ERROR_ACCESS_DENIED)
149 errno = EACCES;
150 else
151 errno = EIO;
152 return -1;
153 }
154
155 using off_t = _off64_t;
156 inline int truncate(const wchar_t* path, _off64_t length)
157 {
158 const int fd = ::_wopen(path, _O_BINARY|_O_RDWR);
159 if (fd == -1)
160 return fd;
161 const int ret = ::ftruncate64(fd, length);
162 int err;
163 ::_get_errno(&err);
164 ::_close(fd);
165 ::_set_errno(err);
166 return ret;
167 }
168 using char_type = wchar_t;
169 #elif defined _GLIBCXX_HAVE_UNISTD_H
170 using ::open;
171 using ::close;
172 # ifdef _GLIBCXX_HAVE_SYS_STAT_H
173 typedef struct ::stat stat_type;
174 using ::stat;
175 # ifdef _GLIBCXX_USE_LSTAT
176 using ::lstat;
177 # else
178 inline int lstat(const char* path, stat_type* buffer)
179 { return stat(path, buffer); }
180 # endif
181 # endif
182 using ::mode_t;
183 using ::chmod;
184 using ::mkdir;
185 using ::getcwd;
186 using ::chdir;
187 # if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_USE_UTIME
188 using ::utimbuf;
189 using ::utime;
190 # endif
191 using ::rename;
192 using ::off_t;
193 # ifdef _GLIBCXX_HAVE_TRUNCATE
194 using ::truncate;
195 # else
196 inline int truncate(const char* path, off_t length)
197 {
198 if (length == 0)
199 {
200 const int fd = ::open(path, O_WRONLY|O_TRUNC);
201 if (fd == -1)
202 return fd;
203 ::close(fd);
204 return 0;
205 }
206 errno = ENOTSUP;
207 return -1;
208 }
209 # endif
210 using char_type = char;
211 #else // ! _GLIBCXX_FILESYSTEM_IS_WINDOWS && ! _GLIBCXX_HAVE_UNISTD_H
212 inline int open(const char*, int, ...) { errno = ENOSYS; return -1; }
213 inline int close(int) { errno = ENOSYS; return -1; }
214 using mode_t = int;
215 inline int chmod(const char*, mode_t) { errno = ENOSYS; return -1; }
216 inline int mkdir(const char*, mode_t) { errno = ENOSYS; return -1; }
217 inline char* getcwd(char*, size_t) { errno = ENOSYS; return nullptr; }
218 inline int chdir(const char*) { errno = ENOSYS; return -1; }
219 inline int rename(const char*, const char*) { errno = ENOSYS; return -1; }
220 using off_t = long;
221 inline int truncate(const char*, off_t) { errno = ENOSYS; return -1; }
222 using char_type = char;
223 #endif // _GLIBCXX_FILESYSTEM_IS_WINDOWS
224 } // namespace __gnu_posix
225
226 template<typename Bitmask>
227 inline bool is_set(Bitmask obj, Bitmask bits)
228 {
229 return (obj & bits) != Bitmask::none;
230 }
231
232 inline bool
233 is_not_found_errno(int err) noexcept
234 {
235 return err == ENOENT || err == ENOTDIR;
236 }
237
238 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
239 using __gnu_posix::stat_type;
240
241 inline std::chrono::system_clock::time_point
242 file_time(const stat_type& st, std::error_code& ec) noexcept
243 {
244 using namespace std::chrono;
245 #ifdef _GLIBCXX_USE_ST_MTIM
246 time_t s = st.st_mtim.tv_sec;
247 nanoseconds ns{st.st_mtim.tv_nsec};
248 #else
249 time_t s = st.st_mtime;
250 nanoseconds ns{};
251 #endif
252
253 // FIXME
254 // There are possible timespec values which will overflow
255 // chrono::system_clock::time_point but would not overflow
256 // __file_clock::time_point, due to its different epoch.
257 //
258 // By checking for overflow of the intermediate system_clock::duration
259 // type, we report an error for values which are actually representable
260 // in the file_time_type result type.
261 //
262 // Howard Hinnant's solution for this problem is to use
263 // duration<__int128>{s} + ns, which doesn't overflow.
264 // An alternative would be to do the epoch correction on s before
265 // the addition, and then go straight to file_time_type instead of
266 // going via chrono::system_clock::time_point.
267 //
268 // (This only applies to the C++17 Filesystem library, because for the
269 // Filesystem TS we don't have a distinct __file_clock, we just use the
270 // system clock for file timestamps).
271 if (seconds{s} >= floor<seconds>(system_clock::duration::max()))
272 {
273 ec = std::make_error_code(std::errc::value_too_large); // EOVERFLOW
274 return system_clock::time_point::min();
275 }
276 ec.clear();
277 return system_clock::time_point{seconds{s} + ns};
278 }
279
280 struct copy_options_existing_file
281 {
282 bool skip, update, overwrite;
283 };
284
285 #endif // _GLIBCXX_HAVE_SYS_STAT_H
286
287 } // namespace filesystem
288
289 // BEGIN/END macros must be defined before including this file.
290 _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM
291
292 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
293 using std::filesystem::__gnu_posix::stat_type;
294 using std::filesystem::__gnu_posix::char_type;
295
296 bool
297 do_copy_file(const char_type* from, const char_type* to,
298 std::filesystem::copy_options_existing_file options,
299 stat_type* from_st, stat_type* to_st,
300 std::error_code& ec) noexcept;
301
302 void
303 do_space(const char_type* pathname,
304 uintmax_t& capacity, uintmax_t& free, uintmax_t& available,
305 std::error_code&);
306
307
308 inline file_type
309 make_file_type(const stat_type& st) noexcept
310 {
311 #ifdef _GLIBCXX_HAVE_S_ISREG
312 if (S_ISREG(st.st_mode))
313 return file_type::regular;
314 else if (S_ISDIR(st.st_mode))
315 return file_type::directory;
316 else if (S_ISCHR(st.st_mode))
317 return file_type::character;
318 else if (S_ISBLK(st.st_mode))
319 return file_type::block;
320 else if (S_ISFIFO(st.st_mode))
321 return file_type::fifo;
322 #ifdef S_ISLNK // not present in mingw
323 else if (S_ISLNK(st.st_mode))
324 return file_type::symlink;
325 #endif
326 #ifdef S_ISSOCK // not present until POSIX:2001
327 else if (S_ISSOCK(st.st_mode))
328 return file_type::socket;
329 #endif
330 #endif
331 return file_type::unknown;
332 }
333
334 inline file_status
335 make_file_status(const stat_type& st) noexcept
336 {
337 return file_status{
338 make_file_type(st),
339 static_cast<perms>(st.st_mode) & perms::mask
340 };
341 }
342
343 inline std::filesystem::copy_options_existing_file
344 copy_file_options(copy_options opt)
345 {
346 using std::filesystem::is_set;
347 return {
348 is_set(opt, copy_options::skip_existing),
349 is_set(opt, copy_options::update_existing),
350 is_set(opt, copy_options::overwrite_existing)
351 };
352 }
353
354 #ifdef NEED_DO_COPY_FILE
355 bool
356 do_copy_file(const char_type* from, const char_type* to,
357 std::filesystem::copy_options_existing_file options,
358 stat_type* from_st, stat_type* to_st,
359 std::error_code& ec) noexcept
360 {
361 namespace fs = std::filesystem;
362 namespace posix = fs::__gnu_posix;
363
364 stat_type st1, st2;
365 file_status t, f;
366
367 if (to_st == nullptr)
368 {
369 if (posix::stat(to, &st1))
370 {
371 const int err = errno;
372 if (!fs::is_not_found_errno(err))
373 {
374 ec.assign(err, std::generic_category());
375 return false;
376 }
377 }
378 else
379 to_st = &st1;
380 }
381 else if (to_st == from_st)
382 to_st = nullptr;
383
384 if (to_st == nullptr)
385 t = file_status{file_type::not_found};
386 else
387 t = make_file_status(*to_st);
388
389 if (from_st == nullptr)
390 {
391 if (posix::stat(from, &st2))
392 {
393 ec.assign(errno, std::generic_category());
394 return false;
395 }
396 else
397 from_st = &st2;
398 }
399 f = make_file_status(*from_st);
400 // _GLIBCXX_RESOLVE_LIB_DEFECTS
401 // 2712. copy_file() has a number of unspecified error conditions
402 if (!is_regular_file(f))
403 {
404 ec = std::make_error_code(std::errc::invalid_argument);
405 return false;
406 }
407
408 if (exists(t))
409 {
410 if (!is_regular_file(t))
411 {
412 ec = std::make_error_code(std::errc::invalid_argument);
413 return false;
414 }
415
416 if (to_st->st_dev == from_st->st_dev
417 && to_st->st_ino == from_st->st_ino)
418 {
419 ec = std::make_error_code(std::errc::file_exists);
420 return false;
421 }
422
423 if (options.skip)
424 {
425 ec.clear();
426 return false;
427 }
428 else if (options.update)
429 {
430 const auto from_mtime = fs::file_time(*from_st, ec);
431 if (ec)
432 return false;
433 if ((from_mtime <= fs::file_time(*to_st, ec)) || ec)
434 return false;
435 }
436 else if (!options.overwrite)
437 {
438 ec = std::make_error_code(std::errc::file_exists);
439 return false;
440 }
441 else if (!is_regular_file(t))
442 {
443 ec = std::make_error_code(std::errc::invalid_argument);
444 return false;
445 }
446 }
447
448 struct CloseFD {
449 ~CloseFD() { if (fd != -1) posix::close(fd); }
450 bool close() { return posix::close(std::__exchange(fd, -1)) == 0; }
451 int fd;
452 };
453
454 int iflag = O_RDONLY;
455 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
456 iflag |= O_BINARY;
457 #endif
458
459 CloseFD in = { posix::open(from, iflag) };
460 if (in.fd == -1)
461 {
462 ec.assign(errno, std::generic_category());
463 return false;
464 }
465 int oflag = O_WRONLY|O_CREAT;
466 if (options.overwrite || options.update)
467 oflag |= O_TRUNC;
468 else
469 oflag |= O_EXCL;
470 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
471 oflag |= O_BINARY;
472 #endif
473 CloseFD out = { posix::open(to, oflag, S_IWUSR) };
474 if (out.fd == -1)
475 {
476 if (errno == EEXIST && options.skip)
477 ec.clear();
478 else
479 ec.assign(errno, std::generic_category());
480 return false;
481 }
482
483 #if defined _GLIBCXX_USE_FCHMOD && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
484 if (::fchmod(out.fd, from_st->st_mode))
485 #elif defined _GLIBCXX_USE_FCHMODAT && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
486 if (::fchmodat(AT_FDCWD, to, from_st->st_mode, 0))
487 #else
488 if (posix::chmod(to, from_st->st_mode))
489 #endif
490 {
491 ec.assign(errno, std::generic_category());
492 return false;
493 }
494
495 size_t count = from_st->st_size;
496 #if defined _GLIBCXX_USE_SENDFILE && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
497 off_t offset = 0;
498 ssize_t n = ::sendfile(out.fd, in.fd, &offset, count);
499 if (n < 0 && errno != ENOSYS && errno != EINVAL)
500 {
501 ec.assign(errno, std::generic_category());
502 return false;
503 }
504 if ((size_t)n == count)
505 {
506 if (!out.close() || !in.close())
507 {
508 ec.assign(errno, std::generic_category());
509 return false;
510 }
511 ec.clear();
512 return true;
513 }
514 else if (n > 0)
515 count -= n;
516 #endif // _GLIBCXX_USE_SENDFILE
517
518 using std::ios;
519 __gnu_cxx::stdio_filebuf<char> sbin(in.fd, ios::in|ios::binary);
520 __gnu_cxx::stdio_filebuf<char> sbout(out.fd, ios::out|ios::binary);
521
522 if (sbin.is_open())
523 in.fd = -1;
524 if (sbout.is_open())
525 out.fd = -1;
526
527 #ifdef _GLIBCXX_USE_SENDFILE
528 if (n != 0)
529 {
530 if (n < 0)
531 n = 0;
532
533 const auto p1 = sbin.pubseekoff(n, ios::beg, ios::in);
534 const auto p2 = sbout.pubseekoff(n, ios::beg, ios::out);
535
536 const std::streampos errpos(std::streamoff(-1));
537 if (p1 == errpos || p2 == errpos)
538 {
539 ec = std::make_error_code(std::errc::io_error);
540 return false;
541 }
542 }
543 #endif
544
545 if (count && !(std::ostream(&sbout) << &sbin))
546 {
547 ec = std::make_error_code(std::errc::io_error);
548 return false;
549 }
550 if (!sbout.close() || !sbin.close())
551 {
552 ec.assign(errno, std::generic_category());
553 return false;
554 }
555 ec.clear();
556 return true;
557 }
558 #endif // NEED_DO_COPY_FILE
559
560 #ifdef NEED_DO_SPACE
561 #pragma GCC diagnostic push
562 #pragma GCC diagnostic ignored "-Wunused-parameter"
563 void
564 do_space(const char_type* pathname,
565 uintmax_t& capacity, uintmax_t& free, uintmax_t& available,
566 std::error_code& ec)
567 {
568 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
569 struct ::statvfs f;
570 if (::statvfs(pathname, &f))
571 ec.assign(errno, std::generic_category());
572 else
573 {
574 if (f.f_frsize != (unsigned long)-1)
575 {
576 const uintmax_t fragment_size = f.f_frsize;
577 const fsblkcnt_t unknown = -1;
578 if (f.f_blocks != unknown)
579 capacity = f.f_blocks * fragment_size;
580 if (f.f_bfree != unknown)
581 free = f.f_bfree * fragment_size;
582 if (f.f_bavail != unknown)
583 available = f.f_bavail * fragment_size;
584 }
585 ec.clear();
586 }
587 #elif _GLIBCXX_FILESYSTEM_IS_WINDOWS
588 ULARGE_INTEGER bytes_avail = {}, bytes_total = {}, bytes_free = {};
589 if (GetDiskFreeSpaceExW(pathname, &bytes_avail, &bytes_total, &bytes_free))
590 {
591 if (bytes_total.QuadPart != 0)
592 capacity = bytes_total.QuadPart;
593 if (bytes_free.QuadPart != 0)
594 free = bytes_free.QuadPart;
595 if (bytes_avail.QuadPart != 0)
596 available = bytes_avail.QuadPart;
597 ec.clear();
598 }
599 else
600 ec = std::__last_system_error();
601 #else
602 ec = std::make_error_code(std::errc::function_not_supported);
603 #endif
604 }
605 #pragma GCC diagnostic pop
606 #endif // NEED_DO_SPACE
607
608 #endif // _GLIBCXX_HAVE_SYS_STAT_H
609
610 // Find OS-specific name of temporary directory from the environment,
611 // Caller must check that the path is an accessible directory.
612 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
613 inline wstring
614 get_temp_directory_from_env(error_code& ec)
615 {
616 unsigned len = 1024;
617 std::wstring buf;
618 do
619 {
620 buf.resize(len);
621 len = GetTempPathW(buf.size(), buf.data());
622 } while (len > buf.size());
623
624 if (len == 0)
625 ec = __last_system_error();
626 else
627 ec.clear();
628
629 buf.resize(len);
630 return buf;
631 }
632 #else
633 inline const char*
634 get_temp_directory_from_env(error_code& ec) noexcept
635 {
636 ec.clear();
637 for (auto env : { "TMPDIR", "TMP", "TEMP", "TEMPDIR" })
638 {
639 #if _GLIBCXX_HAVE_SECURE_GETENV
640 auto tmpdir = ::secure_getenv(env);
641 #else
642 auto tmpdir = ::getenv(env);
643 #endif
644 if (tmpdir)
645 return tmpdir;
646 }
647 return "/tmp";
648 }
649 #endif
650
651 _GLIBCXX_END_NAMESPACE_FILESYSTEM
652
653 _GLIBCXX_END_NAMESPACE_VERSION
654 } // namespace std
655
656 #endif // _GLIBCXX_OPS_COMMON_H