hppa: Modify TLS patterns to provide both 32 and 64-bit support.
[official-gcc.git] / libstdc++-v3 / src / filesystem / ops-common.h
blob2e4331bb682915519bac3cfb59d0bf75405b3224
1 // Filesystem operation utilities -*- C++ -*-
3 // Copyright (C) 2014-2023 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.
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.
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.
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/>.
25 #ifndef _GLIBCXX_OPS_COMMON_H
26 #define _GLIBCXX_OPS_COMMON_H 1
28 #include <chrono>
29 #include <bits/move.h> // std::__exchange
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
45 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
46 # include <wchar.h>
47 #endif
49 #ifdef NEED_DO_COPY_FILE
50 # include <filesystem>
51 # include <ext/stdio_filebuf.h>
52 # ifdef _GLIBCXX_USE_COPY_FILE_RANGE
53 # include <unistd.h> // copy_file_range
54 # endif
55 # ifdef _GLIBCXX_USE_SENDFILE
56 # include <sys/sendfile.h> // sendfile
57 # include <unistd.h> // lseek
58 # endif
59 #endif
61 namespace std _GLIBCXX_VISIBILITY(default)
63 _GLIBCXX_BEGIN_NAMESPACE_VERSION
65 // Get the last OS error (for POSIX this is just errno).
66 inline error_code
67 __last_system_error() noexcept
69 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
70 // N.B. use error_code::default_error_condition() to convert to generic.
71 return {(int)::GetLastError(), std::system_category()};
72 #else
73 return {errno, std::generic_category()};
74 #endif
77 // Get an error code indicating unsupported functionality.
79 // This should be used when a function is unable to behave as specified
80 // due to an incomplete or partial implementation, e.g.
81 // filesystem::equivalent(a, b) if is_other(a) && is_other(b) is true.
83 // Use errc::function_not_supported for functions that are entirely
84 // unimplemented, e.g. create_symlink on Windows.
86 // Use errc::invalid_argument for requests to perform operations outside
87 // the spec, e.g. trying to copy a directory using filesystem::copy_file.
88 inline error_code
89 __unsupported() noexcept
91 #if defined __AVR__
92 // avr-libc defines ENOTSUP and EOPNOTSUPP but with nonsense values.
93 // ENOSYS is defined though, so use an error_code corresponding to that.
94 // This contradicts the comment above, but we don't have much choice.
95 return std::make_error_code(std::errc::function_not_supported);
96 #elif defined ENOTSUP
97 return std::make_error_code(std::errc::not_supported);
98 #elif defined EOPNOTSUPP
99 // This is supposed to be for socket operations
100 return std::make_error_code(std::errc::operation_not_supported);
101 #else
102 return std::make_error_code(std::errc::invalid_argument);
103 #endif
106 namespace filesystem
108 namespace __gnu_posix
110 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
111 // Adapt the Windows _wxxx functions to look like POSIX xxx, but for wchar_t*.
112 inline int open(const wchar_t* path, int flags)
113 { return ::_wopen(path, flags); }
115 inline int open(const wchar_t* path, int flags, int mode)
116 { return ::_wopen(path, flags, mode); }
118 inline int close(int fd)
119 { return ::_close(fd); }
121 typedef struct ::__stat64 stat_type;
123 inline int stat(const wchar_t* path, stat_type* buffer)
124 { return ::_wstat64(path, buffer); }
126 inline int lstat(const wchar_t* path, stat_type* buffer)
128 // FIXME: symlinks not currently supported
129 return stat(path, buffer);
132 using ::mode_t;
134 inline int chmod(const wchar_t* path, mode_t mode)
135 { return ::_wchmod(path, mode); }
137 inline int mkdir(const wchar_t* path, mode_t)
138 { return ::_wmkdir(path); }
140 inline wchar_t* getcwd(wchar_t* buf, size_t size)
141 { return ::_wgetcwd(buf, size > (size_t)INT_MAX ? INT_MAX : (int)size); }
143 inline int chdir(const wchar_t* path)
144 { return ::_wchdir(path); }
146 #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
147 using utimbuf = _utimbuf;
149 inline int utime(const wchar_t* path, utimbuf* times)
150 { return ::_wutime(path, times); }
151 #endif
153 inline int rename(const wchar_t* oldname, const wchar_t* newname)
155 if (MoveFileExW(oldname, newname,
156 MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
157 return 0;
158 if (GetLastError() == ERROR_ACCESS_DENIED)
159 errno = EACCES;
160 else
161 errno = EIO;
162 return -1;
165 using off_t = _off64_t;
166 inline int truncate(const wchar_t* path, _off64_t length)
168 const int fd = ::_wopen(path, _O_BINARY|_O_RDWR);
169 if (fd == -1)
170 return fd;
171 const int ret = ::ftruncate64(fd, length);
172 int err;
173 ::_get_errno(&err);
174 ::_close(fd);
175 ::_set_errno(err);
176 return ret;
178 using char_type = wchar_t;
179 #elif defined _GLIBCXX_HAVE_UNISTD_H && ! defined __AVR__
180 using ::open;
181 using ::close;
182 # ifdef _GLIBCXX_HAVE_SYS_STAT_H
183 typedef struct ::stat stat_type;
184 using ::stat;
185 # ifdef _GLIBCXX_USE_LSTAT
186 using ::lstat;
187 # else
188 inline int lstat(const char* path, stat_type* buffer)
189 { return stat(path, buffer); }
190 # endif
191 # endif
192 using ::mode_t;
193 using ::chmod;
194 using ::mkdir;
195 using ::getcwd;
196 using ::chdir;
197 # if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_USE_UTIME
198 using ::utimbuf;
199 using ::utime;
200 # endif
201 using ::rename;
202 using ::off_t;
203 # ifdef _GLIBCXX_HAVE_TRUNCATE
204 using ::truncate;
205 # else
206 inline int truncate(const char* path, off_t length)
208 if (length == 0)
210 const int fd = ::open(path, O_WRONLY|O_TRUNC);
211 if (fd == -1)
212 return fd;
213 ::close(fd);
214 return 0;
216 errno = ENOTSUP;
217 return -1;
219 # endif
220 using char_type = char;
221 #else // ! _GLIBCXX_FILESYSTEM_IS_WINDOWS && ! _GLIBCXX_HAVE_UNISTD_H
222 inline int open(const char*, int, ...) { errno = ENOSYS; return -1; }
223 inline int close(int) { errno = ENOSYS; return -1; }
224 using mode_t = int;
225 inline int chmod(const char*, mode_t) { errno = ENOSYS; return -1; }
226 inline int mkdir(const char*, mode_t) { errno = ENOSYS; return -1; }
227 inline char* getcwd(char*, size_t) { errno = ENOSYS; return nullptr; }
228 inline int chdir(const char*) { errno = ENOSYS; return -1; }
229 inline int rename(const char*, const char*) { errno = ENOSYS; return -1; }
230 using off_t = long;
231 inline int truncate(const char*, off_t) { errno = ENOSYS; return -1; }
232 using char_type = char;
233 #endif // _GLIBCXX_FILESYSTEM_IS_WINDOWS
234 } // namespace __gnu_posix
236 template<typename Bitmask>
237 inline bool is_set(Bitmask obj, Bitmask bits)
239 return (obj & bits) != Bitmask::none;
242 inline bool
243 is_not_found_errno(int err) noexcept
245 return err == ENOENT || err == ENOTDIR;
248 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
249 using __gnu_posix::stat_type;
251 inline std::chrono::system_clock::time_point
252 file_time(const stat_type& st, std::error_code& ec) noexcept
254 using namespace std::chrono;
255 #ifdef _GLIBCXX_USE_ST_MTIM
256 time_t s = st.st_mtim.tv_sec;
257 nanoseconds ns{st.st_mtim.tv_nsec};
258 #else
259 time_t s = st.st_mtime;
260 nanoseconds ns{};
261 #endif
263 // FIXME
264 // There are possible timespec values which will overflow
265 // chrono::system_clock::time_point but would not overflow
266 // __file_clock::time_point, due to its different epoch.
268 // By checking for overflow of the intermediate system_clock::duration
269 // type, we report an error for values which are actually representable
270 // in the file_time_type result type.
272 // Howard Hinnant's solution for this problem is to use
273 // duration<__int128>{s} + ns, which doesn't overflow.
274 // An alternative would be to do the epoch correction on s before
275 // the addition, and then go straight to file_time_type instead of
276 // going via chrono::system_clock::time_point.
278 // (This only applies to the C++17 Filesystem library, because for the
279 // Filesystem TS we don't have a distinct __file_clock, we just use the
280 // system clock for file timestamps).
281 if (seconds{s} >= floor<seconds>(system_clock::duration::max()))
283 ec = std::make_error_code(std::errc::value_too_large); // EOVERFLOW
284 return system_clock::time_point::min();
286 ec.clear();
287 return system_clock::time_point{seconds{s} + ns};
290 struct copy_options_existing_file
292 bool skip, update, overwrite;
295 #endif // _GLIBCXX_HAVE_SYS_STAT_H
297 } // namespace filesystem
299 // BEGIN/END macros must be defined before including this file.
300 _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM
302 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
303 using std::filesystem::__gnu_posix::stat_type;
304 using std::filesystem::__gnu_posix::char_type;
306 bool
307 do_copy_file(const char_type* from, const char_type* to,
308 std::filesystem::copy_options_existing_file options,
309 stat_type* from_st, stat_type* to_st,
310 std::error_code& ec) noexcept;
312 void
313 do_space(const char_type* pathname,
314 uintmax_t& capacity, uintmax_t& free, uintmax_t& available,
315 std::error_code&);
318 inline file_type
319 make_file_type(const stat_type& st) noexcept
321 #ifdef _GLIBCXX_HAVE_S_ISREG
322 if (S_ISREG(st.st_mode))
323 return file_type::regular;
324 else if (S_ISDIR(st.st_mode))
325 return file_type::directory;
326 else if (S_ISCHR(st.st_mode))
327 return file_type::character;
328 else if (S_ISBLK(st.st_mode))
329 return file_type::block;
330 else if (S_ISFIFO(st.st_mode))
331 return file_type::fifo;
332 #ifdef S_ISLNK // not present in mingw
333 else if (S_ISLNK(st.st_mode))
334 return file_type::symlink;
335 #endif
336 #ifdef S_ISSOCK // not present until POSIX:2001
337 else if (S_ISSOCK(st.st_mode))
338 return file_type::socket;
339 #endif
340 #endif
341 return file_type::unknown;
344 inline file_status
345 make_file_status(const stat_type& st) noexcept
347 return file_status{
348 make_file_type(st),
349 static_cast<perms>(st.st_mode) & perms::mask
353 inline std::filesystem::copy_options_existing_file
354 copy_file_options(copy_options opt)
356 using std::filesystem::is_set;
357 return {
358 is_set(opt, copy_options::skip_existing),
359 is_set(opt, copy_options::update_existing),
360 is_set(opt, copy_options::overwrite_existing)
364 #ifdef NEED_DO_COPY_FILE
365 #ifdef _GLIBCXX_USE_COPY_FILE_RANGE
366 bool
367 copy_file_copy_file_range(int fd_in, int fd_out, size_t length) noexcept
369 // a zero-length file is either empty, or not copyable by this syscall
370 // return early to avoid the syscall cost
371 if (length == 0)
373 errno = EINVAL;
374 return false;
376 size_t bytes_left = length;
377 loff_t off_in = 0, off_out = 0;
378 ssize_t bytes_copied;
381 bytes_copied = ::copy_file_range(fd_in, &off_in, fd_out, &off_out,
382 bytes_left, 0);
383 bytes_left -= bytes_copied;
385 while (bytes_left > 0 && bytes_copied > 0);
386 if (bytes_copied < 0)
387 return false;
388 return true;
390 #endif
391 #if defined _GLIBCXX_USE_SENDFILE && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
392 bool
393 copy_file_sendfile(int fd_in, int fd_out, size_t length) noexcept
395 // a zero-length file is either empty, or not copyable by this syscall
396 // return early to avoid the syscall cost
397 if (length == 0)
399 errno = EINVAL;
400 return false;
402 size_t bytes_left = length;
403 off_t offset = 0;
404 ssize_t bytes_copied;
407 bytes_copied = ::sendfile(fd_out, fd_in, &offset, bytes_left);
408 bytes_left -= bytes_copied;
410 while (bytes_left > 0 && bytes_copied > 0);
411 if (bytes_copied < 0)
413 ::lseek(fd_out, 0, SEEK_SET);
414 return false;
416 return true;
418 #endif
419 bool
420 do_copy_file(const char_type* from, const char_type* to,
421 std::filesystem::copy_options_existing_file options,
422 stat_type* from_st, stat_type* to_st,
423 std::error_code& ec) noexcept
425 namespace fs = std::filesystem;
426 namespace posix = fs::__gnu_posix;
428 stat_type st1, st2;
429 file_status t, f;
431 if (to_st == nullptr)
433 if (posix::stat(to, &st1))
435 const int err = errno;
436 if (!fs::is_not_found_errno(err))
438 ec.assign(err, std::generic_category());
439 return false;
442 else
443 to_st = &st1;
445 else if (to_st == from_st)
446 to_st = nullptr;
448 if (to_st == nullptr)
449 t = file_status{file_type::not_found};
450 else
451 t = make_file_status(*to_st);
453 if (from_st == nullptr)
455 if (posix::stat(from, &st2))
457 ec.assign(errno, std::generic_category());
458 return false;
460 else
461 from_st = &st2;
463 f = make_file_status(*from_st);
464 // _GLIBCXX_RESOLVE_LIB_DEFECTS
465 // 2712. copy_file() has a number of unspecified error conditions
466 if (!is_regular_file(f))
468 ec = std::make_error_code(std::errc::invalid_argument);
469 return false;
472 if (exists(t))
474 if (!is_regular_file(t))
476 ec = std::make_error_code(std::errc::invalid_argument);
477 return false;
480 if (to_st->st_dev == from_st->st_dev
481 && to_st->st_ino == from_st->st_ino)
483 ec = std::make_error_code(std::errc::file_exists);
484 return false;
487 if (options.skip)
489 ec.clear();
490 return false;
492 else if (options.update)
494 const auto from_mtime = fs::file_time(*from_st, ec);
495 if (ec)
496 return false;
497 if ((from_mtime <= fs::file_time(*to_st, ec)) || ec)
498 return false;
500 else if (!options.overwrite)
502 ec = std::make_error_code(std::errc::file_exists);
503 return false;
505 else if (!is_regular_file(t))
507 ec = std::make_error_code(std::errc::invalid_argument);
508 return false;
512 struct CloseFD {
513 ~CloseFD() { if (fd != -1) posix::close(fd); }
514 bool close() { return posix::close(std::__exchange(fd, -1)) == 0; }
515 int fd;
518 int common_flags = 0;
519 #ifdef O_CLOEXEC
520 common_flags |= O_CLOEXEC;
521 #endif
522 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
523 common_flags |= O_BINARY;
524 #endif
526 const int iflag = O_RDONLY | common_flags;
527 CloseFD in = { posix::open(from, iflag) };
528 if (in.fd == -1)
530 ec.assign(errno, std::generic_category());
531 return false;
533 int oflag = O_WRONLY | O_CREAT | common_flags;
534 if (options.overwrite || options.update)
535 oflag |= O_TRUNC;
536 else
537 oflag |= O_EXCL;
538 CloseFD out = { posix::open(to, oflag, S_IWUSR) };
539 if (out.fd == -1)
541 if (errno == EEXIST && options.skip)
542 ec.clear();
543 else
544 ec.assign(errno, std::generic_category());
545 return false;
548 #if defined _GLIBCXX_USE_FCHMOD && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
549 if (::fchmod(out.fd, from_st->st_mode))
550 #elif defined _GLIBCXX_USE_FCHMODAT && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
551 if (::fchmodat(AT_FDCWD, to, from_st->st_mode, 0))
552 #else
553 if (posix::chmod(to, from_st->st_mode))
554 #endif
556 ec.assign(errno, std::generic_category());
557 return false;
560 bool has_copied = false;
562 #ifdef _GLIBCXX_USE_COPY_FILE_RANGE
563 if (!has_copied)
564 has_copied = copy_file_copy_file_range(in.fd, out.fd, from_st->st_size);
565 if (!has_copied)
567 // EINVAL: src and dst are the same file (this is not cheaply
568 // detectable from userspace)
569 // EINVAL: copy_file_range is unsupported for this file type by the
570 // underlying filesystem
571 // ENOTSUP: undocumented, can arise with old kernels and NFS
572 // EOPNOTSUPP: filesystem does not implement copy_file_range
573 // ETXTBSY: src or dst is an active swapfile (nonsensical, but allowed
574 // with normal copying)
575 // EXDEV: src and dst are on different filesystems that do not support
576 // cross-fs copy_file_range
577 // ENOENT: undocumented, can arise with CIFS
578 // ENOSYS: unsupported by kernel or blocked by seccomp
579 if (errno != EINVAL && errno != ENOTSUP && errno != EOPNOTSUPP
580 && errno != ETXTBSY && errno != EXDEV && errno != ENOENT
581 && errno != ENOSYS)
583 ec.assign(errno, std::generic_category());
584 return false;
587 #endif
589 #if defined _GLIBCXX_USE_SENDFILE && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
590 if (!has_copied)
591 has_copied = copy_file_sendfile(in.fd, out.fd, from_st->st_size);
592 if (!has_copied)
594 if (errno != ENOSYS && errno != EINVAL)
596 ec.assign(errno, std::generic_category());
597 return false;
600 #endif
602 if (has_copied)
604 if (!out.close() || !in.close())
606 ec.assign(errno, std::generic_category());
607 return false;
609 ec.clear();
610 return true;
613 using std::ios;
614 __gnu_cxx::stdio_filebuf<char> sbin(in.fd, ios::in|ios::binary);
615 __gnu_cxx::stdio_filebuf<char> sbout(out.fd, ios::out|ios::binary);
617 if (sbin.is_open())
618 in.fd = -1;
619 if (sbout.is_open())
620 out.fd = -1;
622 // ostream::operator<<(streambuf*) fails if it extracts no characters,
623 // so don't try to use it for empty files. But from_st->st_size == 0 for
624 // some special files (e.g. procfs, see PR libstdc++/108178) so just try
625 // to read a character to decide whether there is anything to copy or not.
626 if (sbin.sgetc() != char_traits<char>::eof())
627 if (!(std::ostream(&sbout) << &sbin))
629 ec = std::make_error_code(std::errc::io_error);
630 return false;
633 if (!sbout.close() || !sbin.close())
635 ec.assign(errno, std::generic_category());
636 return false;
638 ec.clear();
639 return true;
641 #endif // NEED_DO_COPY_FILE
643 #ifdef NEED_DO_SPACE
644 #pragma GCC diagnostic push
645 #pragma GCC diagnostic ignored "-Wunused-parameter"
646 void
647 do_space(const char_type* pathname,
648 uintmax_t& capacity, uintmax_t& free, uintmax_t& available,
649 std::error_code& ec)
651 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
652 struct ::statvfs f;
653 if (::statvfs(pathname, &f))
654 ec.assign(errno, std::generic_category());
655 else
657 if (f.f_frsize != (unsigned long)-1)
659 const uintmax_t fragment_size = f.f_frsize;
660 const fsblkcnt_t unknown = -1;
661 if (f.f_blocks != unknown)
662 capacity = f.f_blocks * fragment_size;
663 if (f.f_bfree != unknown)
664 free = f.f_bfree * fragment_size;
665 if (f.f_bavail != unknown)
666 available = f.f_bavail * fragment_size;
668 ec.clear();
670 #elif _GLIBCXX_FILESYSTEM_IS_WINDOWS
671 ULARGE_INTEGER bytes_avail = {}, bytes_total = {}, bytes_free = {};
672 if (GetDiskFreeSpaceExW(pathname, &bytes_avail, &bytes_total, &bytes_free))
674 if (bytes_total.QuadPart != 0)
675 capacity = bytes_total.QuadPart;
676 if (bytes_free.QuadPart != 0)
677 free = bytes_free.QuadPart;
678 if (bytes_avail.QuadPart != 0)
679 available = bytes_avail.QuadPart;
680 ec.clear();
682 else
683 ec = std::__last_system_error();
684 #else
685 ec = std::make_error_code(std::errc::function_not_supported);
686 #endif
688 #pragma GCC diagnostic pop
689 #endif // NEED_DO_SPACE
691 #endif // _GLIBCXX_HAVE_SYS_STAT_H
693 // Find OS-specific name of temporary directory from the environment,
694 // Caller must check that the path is an accessible directory.
695 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
696 inline wstring
697 get_temp_directory_from_env(error_code& ec)
699 unsigned len = 1024;
700 std::wstring buf;
703 buf.resize(len);
704 len = GetTempPathW(buf.size(), buf.data());
706 while (len > buf.size());
708 if (len == 0)
709 ec = __last_system_error();
710 else
711 ec.clear();
713 buf.resize(len);
714 return buf;
716 #else
717 inline const char*
718 get_temp_directory_from_env(error_code& ec) noexcept
720 ec.clear();
721 for (auto env : { "TMPDIR", "TMP", "TEMP", "TEMPDIR" })
723 #if _GLIBCXX_HAVE_SECURE_GETENV
724 auto tmpdir = ::secure_getenv(env);
725 #else
726 auto tmpdir = ::getenv(env);
727 #endif
728 if (tmpdir)
729 return tmpdir;
731 return "/tmp";
733 #endif
735 _GLIBCXX_END_NAMESPACE_FILESYSTEM
737 _GLIBCXX_END_NAMESPACE_VERSION
738 } // namespace std
740 #endif // _GLIBCXX_OPS_COMMON_H