1 // Filesystem operation utilities -*- C++ -*-
3 // Copyright (C) 2014-2023 Free Software Foundation, Inc.
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)
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
29 #include <bits/move.h> // std::__exchange
31 #ifdef _GLIBCXX_HAVE_UNISTD_H
33 # ifdef _GLIBCXX_HAVE_FCNTL_H
34 # include <fcntl.h> // AT_FDCWD, O_TRUNC etc.
36 # if defined(_GLIBCXX_HAVE_SYS_STAT_H) && defined(_GLIBCXX_HAVE_SYS_TYPES_H)
37 # include <sys/types.h>
38 # include <sys/stat.h>
41 #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
42 # include <utime.h> // utime
45 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
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
55 # ifdef _GLIBCXX_USE_SENDFILE
56 # include <sys/sendfile.h> // sendfile
57 # include <unistd.h> // lseek
61 namespace std
_GLIBCXX_VISIBILITY(default)
63 _GLIBCXX_BEGIN_NAMESPACE_VERSION
65 // Get the last OS error (for POSIX this is just errno).
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()};
73 return {errno
, std::generic_category()};
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.
89 __unsupported() noexcept
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
);
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
);
102 return std::make_error_code(std::errc::invalid_argument
);
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
);
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
); }
153 inline int rename(const wchar_t* oldname
, const wchar_t* newname
)
155 if (MoveFileExW(oldname
, newname
,
156 MOVEFILE_REPLACE_EXISTING
| MOVEFILE_COPY_ALLOWED
))
158 if (GetLastError() == ERROR_ACCESS_DENIED
)
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
);
171 const int ret
= ::ftruncate64(fd
, length
);
178 using char_type
= wchar_t;
179 #elif defined _GLIBCXX_HAVE_UNISTD_H && ! defined __AVR__
182 # ifdef _GLIBCXX_HAVE_SYS_STAT_H
183 typedef struct ::stat stat_type
;
185 # ifdef _GLIBCXX_USE_LSTAT
188 inline int lstat(const char* path
, stat_type
* buffer
)
189 { return stat(path
, buffer
); }
197 # if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_USE_UTIME
203 # ifdef _GLIBCXX_HAVE_TRUNCATE
206 inline int truncate(const char* path
, off_t length
)
210 const int fd
= ::open(path
, O_WRONLY
|O_TRUNC
);
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; }
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; }
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
;
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
};
259 time_t s
= st
.st_mtime
;
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();
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
;
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
;
313 do_space(const char_type
* pathname
,
314 uintmax_t& capacity
, uintmax_t& free
, uintmax_t& available
,
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
;
336 #ifdef S_ISSOCK // not present until POSIX:2001
337 else if (S_ISSOCK(st
.st_mode
))
338 return file_type::socket
;
341 return file_type::unknown
;
345 make_file_status(const stat_type
& st
) noexcept
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
;
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
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
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
,
383 bytes_left
-= bytes_copied
;
385 while (bytes_left
> 0 && bytes_copied
> 0);
386 if (bytes_copied
< 0)
391 #if defined _GLIBCXX_USE_SENDFILE && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
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
402 size_t bytes_left
= length
;
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
);
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
;
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());
445 else if (to_st
== from_st
)
448 if (to_st
== nullptr)
449 t
= file_status
{file_type::not_found
};
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());
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
);
474 if (!is_regular_file(t
))
476 ec
= std::make_error_code(std::errc::invalid_argument
);
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
);
492 else if (options
.update
)
494 const auto from_mtime
= fs::file_time(*from_st
, ec
);
497 if ((from_mtime
<= fs::file_time(*to_st
, ec
)) || ec
)
500 else if (!options
.overwrite
)
502 ec
= std::make_error_code(std::errc::file_exists
);
505 else if (!is_regular_file(t
))
507 ec
= std::make_error_code(std::errc::invalid_argument
);
513 ~CloseFD() { if (fd
!= -1) posix::close(fd
); }
514 bool close() { return posix::close(std::__exchange(fd
, -1)) == 0; }
518 int common_flags
= 0;
520 common_flags
|= O_CLOEXEC
;
522 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
523 common_flags
|= O_BINARY
;
526 const int iflag
= O_RDONLY
| common_flags
;
527 CloseFD in
= { posix::open(from
, iflag
) };
530 ec
.assign(errno
, std::generic_category());
533 int oflag
= O_WRONLY
| O_CREAT
| common_flags
;
534 if (options
.overwrite
|| options
.update
)
538 CloseFD out
= { posix::open(to
, oflag
, S_IWUSR
) };
541 if (errno
== EEXIST
&& options
.skip
)
544 ec
.assign(errno
, std::generic_category());
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))
553 if (posix::chmod(to
, from_st
->st_mode
))
556 ec
.assign(errno
, std::generic_category());
560 bool has_copied
= false;
562 #ifdef _GLIBCXX_USE_COPY_FILE_RANGE
564 has_copied
= copy_file_copy_file_range(in
.fd
, out
.fd
, from_st
->st_size
);
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
583 ec
.assign(errno
, std::generic_category());
589 #if defined _GLIBCXX_USE_SENDFILE && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
591 has_copied
= copy_file_sendfile(in
.fd
, out
.fd
, from_st
->st_size
);
594 if (errno
!= ENOSYS
&& errno
!= EINVAL
)
596 ec
.assign(errno
, std::generic_category());
604 if (!out
.close() || !in
.close())
606 ec
.assign(errno
, std::generic_category());
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
);
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
);
633 if (!sbout
.close() || !sbin
.close())
635 ec
.assign(errno
, std::generic_category());
641 #endif // NEED_DO_COPY_FILE
644 #pragma GCC diagnostic push
645 #pragma GCC diagnostic ignored "-Wunused-parameter"
647 do_space(const char_type
* pathname
,
648 uintmax_t& capacity
, uintmax_t& free
, uintmax_t& available
,
651 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
653 if (::statvfs(pathname
, &f
))
654 ec
.assign(errno
, std::generic_category());
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
;
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
;
683 ec
= std::__last_system_error();
685 ec
= std::make_error_code(std::errc::function_not_supported
);
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
697 get_temp_directory_from_env(error_code
& ec
)
704 len
= GetTempPathW(buf
.size(), buf
.data());
706 while (len
> buf
.size());
709 ec
= __last_system_error();
718 get_temp_directory_from_env(error_code
& ec
) noexcept
721 for (auto env
: { "TMPDIR", "TMP", "TEMP", "TEMPDIR" })
723 #if _GLIBCXX_HAVE_SECURE_GETENV
724 auto tmpdir
= ::secure_getenv(env
);
726 auto tmpdir
= ::getenv(env
);
735 _GLIBCXX_END_NAMESPACE_FILESYSTEM
737 _GLIBCXX_END_NAMESPACE_VERSION
740 #endif // _GLIBCXX_OPS_COMMON_H