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> // 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 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
); }
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 typedef struct ::stat stat_type
;
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
;
307 #endif // _GLIBCXX_HAVE_SYS_STAT_H
309 } // namespace filesystem
311 // BEGIN/END macros must be defined before including this file.
312 _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM
314 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
315 using std::filesystem::__gnu_posix::stat_type
;
316 using std::filesystem::__gnu_posix::char_type
;
319 do_copy_file(const char_type
* from
, const char_type
* to
,
320 std::filesystem::copy_options_existing_file options
,
321 stat_type
* from_st
, stat_type
* to_st
,
322 std::error_code
& ec
) noexcept
;
325 do_space(const char_type
* pathname
,
326 uintmax_t& capacity
, uintmax_t& free
, uintmax_t& available
,
331 make_file_type(const stat_type
& st
) noexcept
333 #ifdef _GLIBCXX_HAVE_S_ISREG
334 if (S_ISREG(st
.st_mode
))
335 return file_type::regular
;
336 else if (S_ISDIR(st
.st_mode
))
337 return file_type::directory
;
338 else if (S_ISCHR(st
.st_mode
))
339 return file_type::character
;
340 else if (S_ISBLK(st
.st_mode
))
341 return file_type::block
;
342 else if (S_ISFIFO(st
.st_mode
))
343 return file_type::fifo
;
344 #ifdef S_ISLNK // not present in mingw
345 else if (S_ISLNK(st
.st_mode
))
346 return file_type::symlink
;
348 #ifdef S_ISSOCK // not present until POSIX:2001
349 else if (S_ISSOCK(st
.st_mode
))
350 return file_type::socket
;
353 return file_type::unknown
;
357 make_file_status(const stat_type
& st
) noexcept
361 static_cast<perms
>(st
.st_mode
) & perms::mask
365 inline std::filesystem::copy_options_existing_file
366 copy_file_options(copy_options opt
)
368 using std::filesystem::is_set
;
370 is_set(opt
, copy_options::skip_existing
),
371 is_set(opt
, copy_options::update_existing
),
372 is_set(opt
, copy_options::overwrite_existing
)
376 #ifdef NEED_DO_COPY_FILE
377 #ifdef _GLIBCXX_USE_COPY_FILE_RANGE
379 copy_file_copy_file_range(int fd_in
, int fd_out
, size_t length
) noexcept
381 // a zero-length file is either empty, or not copyable by this syscall
382 // return early to avoid the syscall cost
388 size_t bytes_left
= length
;
389 loff_t off_in
= 0, off_out
= 0;
390 ssize_t bytes_copied
;
393 bytes_copied
= ::copy_file_range(fd_in
, &off_in
, fd_out
, &off_out
,
395 bytes_left
-= bytes_copied
;
397 while (bytes_left
> 0 && bytes_copied
> 0);
398 if (bytes_copied
< 0)
403 #if defined _GLIBCXX_USE_SENDFILE && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
405 copy_file_sendfile(int fd_in
, int fd_out
, size_t length
) noexcept
407 // a zero-length file is either empty, or not copyable by this syscall
408 // return early to avoid the syscall cost
414 size_t bytes_left
= length
;
416 ssize_t bytes_copied
;
419 bytes_copied
= ::sendfile(fd_out
, fd_in
, &offset
, bytes_left
);
420 bytes_left
-= bytes_copied
;
422 while (bytes_left
> 0 && bytes_copied
> 0);
423 if (bytes_copied
< 0)
425 ::lseek(fd_out
, 0, SEEK_SET
);
432 do_copy_file(const char_type
* from
, const char_type
* to
,
433 std::filesystem::copy_options_existing_file options
,
434 stat_type
* from_st
, stat_type
* to_st
,
435 std::error_code
& ec
) noexcept
437 namespace fs
= std::filesystem
;
438 namespace posix
= fs::__gnu_posix
;
443 if (to_st
== nullptr)
445 if (posix::stat(to
, &st1
))
447 const int err
= errno
;
448 if (!fs::is_not_found_errno(err
))
450 ec
.assign(err
, std::generic_category());
457 else if (to_st
== from_st
)
460 if (to_st
== nullptr)
461 t
= file_status
{file_type::not_found
};
463 t
= make_file_status(*to_st
);
465 if (from_st
== nullptr)
467 if (posix::stat(from
, &st2
))
469 ec
.assign(errno
, std::generic_category());
475 f
= make_file_status(*from_st
);
476 // _GLIBCXX_RESOLVE_LIB_DEFECTS
477 // 2712. copy_file() has a number of unspecified error conditions
478 if (!is_regular_file(f
))
480 ec
= std::make_error_code(std::errc::invalid_argument
);
486 if (!is_regular_file(t
))
488 ec
= std::make_error_code(std::errc::invalid_argument
);
492 if (to_st
->st_dev
== from_st
->st_dev
493 && to_st
->st_ino
== from_st
->st_ino
)
495 ec
= std::make_error_code(std::errc::file_exists
);
504 else if (options
.update
)
506 const auto from_mtime
= fs::file_time(*from_st
, ec
);
509 if ((from_mtime
<= fs::file_time(*to_st
, ec
)) || ec
)
512 else if (!options
.overwrite
)
514 ec
= std::make_error_code(std::errc::file_exists
);
517 else if (!is_regular_file(t
))
519 ec
= std::make_error_code(std::errc::invalid_argument
);
525 ~CloseFD() { if (fd
!= -1) posix::close(fd
); }
526 bool close() { return posix::close(std::__exchange(fd
, -1)) == 0; }
530 int common_flags
= 0;
532 common_flags
|= O_CLOEXEC
;
534 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
535 common_flags
|= O_BINARY
;
538 const int iflag
= O_RDONLY
| common_flags
;
539 CloseFD in
= { posix::open(from
, iflag
) };
542 ec
.assign(errno
, std::generic_category());
545 int oflag
= O_WRONLY
| O_CREAT
| common_flags
;
546 if (options
.overwrite
|| options
.update
)
550 CloseFD out
= { posix::open(to
, oflag
, S_IWUSR
) };
553 if (errno
== EEXIST
&& options
.skip
)
556 ec
.assign(errno
, std::generic_category());
560 #if defined _GLIBCXX_USE_FCHMOD && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
561 if (::fchmod(out
.fd
, from_st
->st_mode
))
562 #elif defined _GLIBCXX_USE_FCHMODAT && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
563 if (::fchmodat(AT_FDCWD
, to
, from_st
->st_mode
, 0))
564 #elif defined _GLIBCXX_USE_CHMOD
565 if (posix::chmod(to
, from_st
->st_mode
))
570 ec
.assign(errno
, std::generic_category());
574 bool has_copied
= false;
576 #ifdef _GLIBCXX_USE_COPY_FILE_RANGE
578 has_copied
= copy_file_copy_file_range(in
.fd
, out
.fd
, from_st
->st_size
);
581 // EINVAL: src and dst are the same file (this is not cheaply
582 // detectable from userspace)
583 // EINVAL: copy_file_range is unsupported for this file type by the
584 // underlying filesystem
585 // ENOTSUP: undocumented, can arise with old kernels and NFS
586 // EOPNOTSUPP: filesystem does not implement copy_file_range
587 // ETXTBSY: src or dst is an active swapfile (nonsensical, but allowed
588 // with normal copying)
589 // EXDEV: src and dst are on different filesystems that do not support
590 // cross-fs copy_file_range
591 // ENOENT: undocumented, can arise with CIFS
592 // ENOSYS: unsupported by kernel or blocked by seccomp
593 if (errno
!= EINVAL
&& errno
!= ENOTSUP
&& errno
!= EOPNOTSUPP
594 && errno
!= ETXTBSY
&& errno
!= EXDEV
&& errno
!= ENOENT
597 ec
.assign(errno
, std::generic_category());
603 #if defined _GLIBCXX_USE_SENDFILE && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
605 has_copied
= copy_file_sendfile(in
.fd
, out
.fd
, from_st
->st_size
);
608 if (errno
!= ENOSYS
&& errno
!= EINVAL
)
610 ec
.assign(errno
, std::generic_category());
618 if (!out
.close() || !in
.close())
620 ec
.assign(errno
, std::generic_category());
628 __gnu_cxx::stdio_filebuf
<char> sbin(in
.fd
, ios::in
|ios::binary
);
629 __gnu_cxx::stdio_filebuf
<char> sbout(out
.fd
, ios::out
|ios::binary
);
636 // ostream::operator<<(streambuf*) fails if it extracts no characters,
637 // so don't try to use it for empty files. But from_st->st_size == 0 for
638 // some special files (e.g. procfs, see PR libstdc++/108178) so just try
639 // to read a character to decide whether there is anything to copy or not.
640 if (sbin
.sgetc() != char_traits
<char>::eof())
641 if (!(std::ostream(&sbout
) << &sbin
))
643 ec
= std::make_error_code(std::errc::io_error
);
647 if (!sbout
.close() || !sbin
.close())
649 ec
.assign(errno
, std::generic_category());
655 #endif // NEED_DO_COPY_FILE
658 #pragma GCC diagnostic push
659 #pragma GCC diagnostic ignored "-Wunused-parameter"
661 do_space(const char_type
* pathname
,
662 uintmax_t& capacity
, uintmax_t& free
, uintmax_t& available
,
665 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
667 if (::statvfs(pathname
, &f
))
668 ec
.assign(errno
, std::generic_category());
671 if (f
.f_frsize
!= (unsigned long)-1)
673 const uintmax_t fragment_size
= f
.f_frsize
;
674 const fsblkcnt_t unknown
= -1;
675 if (f
.f_blocks
!= unknown
)
676 capacity
= f
.f_blocks
* fragment_size
;
677 if (f
.f_bfree
!= unknown
)
678 free
= f
.f_bfree
* fragment_size
;
679 if (f
.f_bavail
!= unknown
)
680 available
= f
.f_bavail
* fragment_size
;
684 #elif _GLIBCXX_FILESYSTEM_IS_WINDOWS
685 ULARGE_INTEGER bytes_avail
= {}, bytes_total
= {}, bytes_free
= {};
686 if (GetDiskFreeSpaceExW(pathname
, &bytes_avail
, &bytes_total
, &bytes_free
))
688 if (bytes_total
.QuadPart
!= 0)
689 capacity
= bytes_total
.QuadPart
;
690 if (bytes_free
.QuadPart
!= 0)
691 free
= bytes_free
.QuadPart
;
692 if (bytes_avail
.QuadPart
!= 0)
693 available
= bytes_avail
.QuadPart
;
697 ec
= std::__last_system_error();
699 ec
= std::make_error_code(std::errc::function_not_supported
);
702 #pragma GCC diagnostic pop
703 #endif // NEED_DO_SPACE
705 #endif // _GLIBCXX_HAVE_SYS_STAT_H
707 // Find OS-specific name of temporary directory from the environment,
708 // Caller must check that the path is an accessible directory.
709 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
711 get_temp_directory_from_env(error_code
& ec
)
717 buf
.__resize_and_overwrite(len
, [&len
](wchar_t* p
, unsigned n
) {
718 len
= GetTempPathW(n
, p
);
719 return len
> n
? 0 : len
;
722 while (len
> buf
.size());
725 ec
= __last_system_error();
733 get_temp_directory_from_env(error_code
& ec
) noexcept
736 for (auto env
: { "TMPDIR", "TMP", "TEMP", "TEMPDIR" })
738 #if _GLIBCXX_HAVE_SECURE_GETENV
739 auto tmpdir
= ::secure_getenv(env
);
741 auto tmpdir
= ::getenv(env
);
750 _GLIBCXX_END_NAMESPACE_FILESYSTEM
752 _GLIBCXX_END_NAMESPACE_VERSION
755 #endif // _GLIBCXX_OPS_COMMON_H