1 // Filesystem operation utilities -*- C++ -*-
3 // Copyright (C) 2014-2025 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> // mkdir, chmod
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 using stat_type
= struct ::__stat64
;
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
); }
136 #define _GLIBCXX_USE_CHMOD 1
138 inline int mkdir(const wchar_t* path
, mode_t
)
139 { return ::_wmkdir(path
); }
140 #define _GLIBCXX_USE_MKDIR 1
142 inline wchar_t* getcwd(wchar_t* buf
, size_t size
)
143 { return ::_wgetcwd(buf
, size
> (size_t)INT_MAX
? INT_MAX
: (int)size
); }
144 #define _GLIBCXX_USE_GETCWD 1
146 inline int chdir(const wchar_t* path
)
147 { return ::_wchdir(path
); }
148 #define _GLIBCXX_USE_CHDIR 1
150 #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
151 using utimbuf
= _utimbuf
;
153 inline int utime(const wchar_t* path
, utimbuf
* times
)
154 { return ::_wutime(path
, times
); }
157 inline int rename(const wchar_t* oldname
, const wchar_t* newname
)
159 if (MoveFileExW(oldname
, newname
,
160 MOVEFILE_REPLACE_EXISTING
| MOVEFILE_COPY_ALLOWED
))
162 if (GetLastError() == ERROR_ACCESS_DENIED
)
169 using off_t
= _off64_t
;
170 inline int truncate(const wchar_t* path
, _off64_t length
)
172 const int fd
= ::_wopen(path
, _O_BINARY
|_O_RDWR
);
175 const int ret
= ::ftruncate64(fd
, length
);
182 using char_type
= wchar_t;
183 #elif defined _GLIBCXX_HAVE_UNISTD_H && ! defined __AVR__
186 # ifdef _GLIBCXX_HAVE_SYS_STAT_H
187 using stat_type
= struct ::stat
;
189 # ifdef _GLIBCXX_USE_LSTAT
192 inline int lstat(const char* path
, stat_type
* buffer
)
193 { return stat(path
, buffer
); }
197 # if _GLIBCXX_USE_CHMOD
200 # if _GLIBCXX_USE_MKDIR
203 # if _GLIBCXX_USE_GETCWD
206 # if _GLIBCXX_USE_CHDIR
209 # if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_USE_UTIME
215 # ifdef _GLIBCXX_HAVE_TRUNCATE
218 inline int truncate(const char* path
, off_t length
)
222 const int fd
= ::open(path
, O_WRONLY
|O_TRUNC
);
232 using char_type
= char;
233 #else // ! _GLIBCXX_FILESYSTEM_IS_WINDOWS && ! _GLIBCXX_HAVE_UNISTD_H
234 inline int open(const char*, int, ...) { errno
= ENOSYS
; return -1; }
235 inline int close(int) { errno
= ENOSYS
; return -1; }
237 inline int chmod(const char*, mode_t
) { errno
= ENOSYS
; return -1; }
238 inline int mkdir(const char*, mode_t
) { errno
= ENOSYS
; return -1; }
239 inline char* getcwd(char*, size_t) { errno
= ENOSYS
; return nullptr; }
240 inline int chdir(const char*) { errno
= ENOSYS
; return -1; }
241 inline int rename(const char*, const char*) { errno
= ENOSYS
; return -1; }
243 inline int truncate(const char*, off_t
) { errno
= ENOSYS
; return -1; }
244 using char_type
= char;
245 #endif // _GLIBCXX_FILESYSTEM_IS_WINDOWS
246 } // namespace __gnu_posix
248 template<typename Bitmask
>
249 inline bool is_set(Bitmask obj
, Bitmask bits
)
251 return (obj
& bits
) != Bitmask::none
;
255 is_not_found_errno(int err
) noexcept
257 return err
== ENOENT
|| err
== ENOTDIR
;
260 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
261 using __gnu_posix::stat_type
;
263 inline std::chrono::system_clock::time_point
264 file_time(const stat_type
& st
, std::error_code
& ec
) noexcept
266 using namespace std::chrono
;
267 #ifdef _GLIBCXX_USE_ST_MTIM
268 time_t s
= st
.st_mtim
.tv_sec
;
269 nanoseconds ns
{st
.st_mtim
.tv_nsec
};
271 time_t s
= st
.st_mtime
;
276 // There are possible timespec values which will overflow
277 // chrono::system_clock::time_point but would not overflow
278 // __file_clock::time_point, due to its different epoch.
280 // By checking for overflow of the intermediate system_clock::duration
281 // type, we report an error for values which are actually representable
282 // in the file_time_type result type.
284 // Howard Hinnant's solution for this problem is to use
285 // duration<__int128>{s} + ns, which doesn't overflow.
286 // An alternative would be to do the epoch correction on s before
287 // the addition, and then go straight to file_time_type instead of
288 // going via chrono::system_clock::time_point.
290 // (This only applies to the C++17 Filesystem library, because for the
291 // Filesystem TS we don't have a distinct __file_clock, we just use the
292 // system clock for file timestamps).
293 if (seconds
{s
} >= floor
<seconds
>(system_clock::duration::max()))
295 ec
= std::make_error_code(std::errc::value_too_large
); // EOVERFLOW
296 return system_clock::time_point::min();
299 return system_clock::time_point
{seconds
{s
} + ns
};
302 struct copy_options_existing_file
304 bool skip
, update
, overwrite
;
306 #endif // _GLIBCXX_HAVE_SYS_STAT_H
308 } // namespace filesystem
310 // BEGIN/END macros must be defined before including this file.
311 _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM
313 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
314 using std::filesystem::__gnu_posix::stat_type
;
315 using std::filesystem::__gnu_posix::char_type
;
318 do_copy_file(const char_type
* from
, const char_type
* to
,
319 std::filesystem::copy_options_existing_file options
,
320 stat_type
* from_st
, stat_type
* to_st
,
321 std::error_code
& ec
) noexcept
;
324 do_space(const char_type
* pathname
,
325 uintmax_t& capacity
, uintmax_t& free
, uintmax_t& available
,
329 // Test whether two files are the same file.
331 equiv_files(const char_type
*, const stat_type
&,
332 const char_type
*, const stat_type
&,
336 make_file_type(const stat_type
& st
) noexcept
338 #ifdef _GLIBCXX_HAVE_S_ISREG
339 if (S_ISREG(st
.st_mode
))
340 return file_type::regular
;
341 else if (S_ISDIR(st
.st_mode
))
342 return file_type::directory
;
343 else if (S_ISCHR(st
.st_mode
))
344 return file_type::character
;
345 else if (S_ISBLK(st
.st_mode
))
346 return file_type::block
;
347 else if (S_ISFIFO(st
.st_mode
))
348 return file_type::fifo
;
349 #ifdef S_ISLNK // not present in mingw
350 else if (S_ISLNK(st
.st_mode
))
351 return file_type::symlink
;
353 #ifdef S_ISSOCK // not present until POSIX:2001
354 else if (S_ISSOCK(st
.st_mode
))
355 return file_type::socket
;
358 return file_type::unknown
;
362 make_file_status(const stat_type
& st
) noexcept
366 static_cast<perms
>(st
.st_mode
) & perms::mask
370 inline std::filesystem::copy_options_existing_file
371 copy_file_options(copy_options opt
)
373 using std::filesystem::is_set
;
375 is_set(opt
, copy_options::skip_existing
),
376 is_set(opt
, copy_options::update_existing
),
377 is_set(opt
, copy_options::overwrite_existing
)
381 #ifdef NEED_DO_COPY_FILE
382 #ifdef _GLIBCXX_USE_COPY_FILE_RANGE
384 copy_file_copy_file_range(int fd_in
, int fd_out
, size_t length
) noexcept
386 // a zero-length file is either empty, or not copyable by this syscall
387 // return early to avoid the syscall cost
393 size_t bytes_left
= length
;
394 loff_t off_in
= 0, off_out
= 0;
395 ssize_t bytes_copied
;
398 bytes_copied
= ::copy_file_range(fd_in
, &off_in
, fd_out
, &off_out
,
400 bytes_left
-= bytes_copied
;
402 while (bytes_left
> 0 && bytes_copied
> 0);
403 if (bytes_copied
< 0)
409 #if defined _GLIBCXX_USE_SENDFILE && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
411 copy_file_sendfile(int fd_in
, int fd_out
, size_t length
) noexcept
413 // a zero-length file is either empty, or not copyable by this syscall
414 // return early to avoid the syscall cost
420 size_t bytes_left
= length
;
422 ssize_t bytes_copied
;
425 bytes_copied
= ::sendfile(fd_out
, fd_in
, &offset
, bytes_left
);
426 bytes_left
-= bytes_copied
;
428 while (bytes_left
> 0 && bytes_copied
> 0);
429 if (bytes_copied
< 0)
431 ::lseek(fd_out
, 0, SEEK_SET
);
439 do_copy_file(const char_type
* from
, const char_type
* to
,
440 std::filesystem::copy_options_existing_file options
,
441 stat_type
* from_st
, stat_type
* to_st
,
442 std::error_code
& ec
) noexcept
444 namespace fs
= std::filesystem
;
445 namespace posix
= fs::__gnu_posix
;
450 if (to_st
== nullptr)
452 if (posix::stat(to
, &st1
))
454 const int err
= errno
;
455 if (!fs::is_not_found_errno(err
))
457 ec
.assign(err
, std::generic_category());
464 else if (to_st
== from_st
)
467 if (to_st
== nullptr)
468 t
= file_status
{file_type::not_found
};
470 t
= make_file_status(*to_st
);
472 if (from_st
== nullptr)
474 if (posix::stat(from
, &st2
))
476 ec
.assign(errno
, std::generic_category());
482 f
= make_file_status(*from_st
);
483 // _GLIBCXX_RESOLVE_LIB_DEFECTS
484 // 2712. copy_file() has a number of unspecified error conditions
485 if (!is_regular_file(f
))
487 ec
= std::make_error_code(std::errc::invalid_argument
);
493 if (!is_regular_file(t
))
495 ec
= std::make_error_code(std::errc::invalid_argument
);
499 if (equiv_files(from
, *from_st
, to
, *to_st
, ec
))
501 ec
= std::make_error_code(std::errc::file_exists
);
510 else if (options
.update
)
512 const auto from_mtime
= fs::file_time(*from_st
, ec
);
515 if ((from_mtime
<= fs::file_time(*to_st
, ec
)) || ec
)
518 else if (!options
.overwrite
)
520 ec
= std::make_error_code(std::errc::file_exists
);
523 else if (!is_regular_file(t
))
525 ec
= std::make_error_code(std::errc::invalid_argument
);
531 ~CloseFD() { if (fd
!= -1) posix::close(fd
); }
532 bool close() { return posix::close(std::__exchange(fd
, -1)) == 0; }
536 int common_flags
= 0;
538 common_flags
|= O_CLOEXEC
;
540 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
541 common_flags
|= O_BINARY
;
544 const int iflag
= O_RDONLY
| common_flags
;
545 CloseFD in
= { posix::open(from
, iflag
) };
548 ec
.assign(errno
, std::generic_category());
551 int oflag
= O_WRONLY
| O_CREAT
| common_flags
;
552 if (options
.overwrite
|| options
.update
)
556 CloseFD out
= { posix::open(to
, oflag
, S_IWUSR
) };
559 if (errno
== EEXIST
&& options
.skip
)
562 ec
.assign(errno
, std::generic_category());
566 #if defined _GLIBCXX_USE_FCHMOD && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
567 if (::fchmod(out
.fd
, from_st
->st_mode
))
568 #elif defined _GLIBCXX_USE_FCHMODAT && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
569 if (::fchmodat(AT_FDCWD
, to
, from_st
->st_mode
, 0))
570 #elif defined _GLIBCXX_USE_CHMOD
571 if (posix::chmod(to
, from_st
->st_mode
))
576 ec
.assign(errno
, std::generic_category());
580 bool has_copied
= false;
582 #ifdef _GLIBCXX_USE_COPY_FILE_RANGE
584 has_copied
= copy_file_copy_file_range(in
.fd
, out
.fd
, from_st
->st_size
);
587 // EINVAL: src and dst are the same file (this is not cheaply
588 // detectable from userspace)
589 // EINVAL: copy_file_range is unsupported for this file type by the
590 // underlying filesystem
591 // ENOTSUP: undocumented, can arise with old kernels and NFS
592 // EOPNOTSUPP: filesystem does not implement copy_file_range
593 // ETXTBSY: src or dst is an active swapfile (nonsensical, but allowed
594 // with normal copying)
595 // EXDEV: src and dst are on different filesystems that do not support
596 // cross-fs copy_file_range
597 // ENOENT: undocumented, can arise with CIFS
598 // ENOSYS: unsupported by kernel or blocked by seccomp
599 if (errno
!= EINVAL
&& errno
!= ENOTSUP
&& errno
!= EOPNOTSUPP
600 && errno
!= ETXTBSY
&& errno
!= EXDEV
&& errno
!= ENOENT
603 ec
.assign(errno
, std::generic_category());
609 #if defined _GLIBCXX_USE_SENDFILE && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
611 has_copied
= copy_file_sendfile(in
.fd
, out
.fd
, from_st
->st_size
);
614 if (errno
!= ENOSYS
&& errno
!= EINVAL
)
616 ec
.assign(errno
, std::generic_category());
624 if (!out
.close() || !in
.close())
626 ec
.assign(errno
, std::generic_category());
634 __gnu_cxx::stdio_filebuf
<char> sbin(in
.fd
, ios::in
|ios::binary
);
635 __gnu_cxx::stdio_filebuf
<char> sbout(out
.fd
, ios::out
|ios::binary
);
642 // ostream::operator<<(streambuf*) fails if it extracts no characters,
643 // so don't try to use it for empty files. But from_st->st_size == 0 for
644 // some special files (e.g. procfs, see PR libstdc++/108178) so just try
645 // to read a character to decide whether there is anything to copy or not.
646 if (sbin
.sgetc() != char_traits
<char>::eof())
647 if (!(std::ostream(&sbout
) << &sbin
))
649 ec
= std::make_error_code(std::errc::io_error
);
653 if (!sbout
.close() || !sbin
.close())
655 ec
.assign(errno
, std::generic_category());
661 #endif // NEED_DO_COPY_FILE
664 #pragma GCC diagnostic push
665 #pragma GCC diagnostic ignored "-Wunused-parameter"
667 do_space(const char_type
* pathname
,
668 uintmax_t& capacity
, uintmax_t& free
, uintmax_t& available
,
671 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
673 if (::statvfs(pathname
, &f
))
674 ec
.assign(errno
, std::generic_category());
677 if (f
.f_frsize
!= (unsigned long)-1)
679 const uintmax_t fragment_size
= f
.f_frsize
;
680 const fsblkcnt_t unknown
= -1;
681 if (f
.f_blocks
!= unknown
)
682 capacity
= f
.f_blocks
* fragment_size
;
683 if (f
.f_bfree
!= unknown
)
684 free
= f
.f_bfree
* fragment_size
;
685 if (f
.f_bavail
!= unknown
)
686 available
= f
.f_bavail
* fragment_size
;
690 #elif _GLIBCXX_FILESYSTEM_IS_WINDOWS
691 ULARGE_INTEGER bytes_avail
= {}, bytes_total
= {}, bytes_free
= {};
692 if (GetDiskFreeSpaceExW(pathname
, &bytes_avail
, &bytes_total
, &bytes_free
))
694 if (bytes_total
.QuadPart
!= 0)
695 capacity
= bytes_total
.QuadPart
;
696 if (bytes_free
.QuadPart
!= 0)
697 free
= bytes_free
.QuadPart
;
698 if (bytes_avail
.QuadPart
!= 0)
699 available
= bytes_avail
.QuadPart
;
703 ec
= std::__last_system_error();
705 ec
= std::make_error_code(std::errc::function_not_supported
);
708 #pragma GCC diagnostic pop
709 #endif // NEED_DO_SPACE
711 #endif // _GLIBCXX_HAVE_SYS_STAT_H
713 // Find OS-specific name of temporary directory from the environment,
714 // Caller must check that the path is an accessible directory.
715 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
717 get_temp_directory_from_env(error_code
& ec
)
723 buf
.__resize_and_overwrite(len
, [&len
](wchar_t* p
, unsigned n
) {
724 len
= GetTempPathW(n
, p
);
725 return len
> n
? 0 : len
;
728 while (len
> buf
.size());
731 ec
= __last_system_error();
739 get_temp_directory_from_env(error_code
& ec
) noexcept
742 for (auto env
: { "TMPDIR", "TMP", "TEMP", "TEMPDIR" })
744 #if _GLIBCXX_HAVE_SECURE_GETENV
745 auto tmpdir
= ::secure_getenv(env
);
747 auto tmpdir
= ::getenv(env
);
756 _GLIBCXX_END_NAMESPACE_FILESYSTEM
758 _GLIBCXX_END_NAMESPACE_VERSION
761 #endif // _GLIBCXX_OPS_COMMON_H