1// operations.cpp --------------------------------------------------------------------//
2
3// Copyright 2002-2009, 2014 Beman Dawes
4// Copyright 2001 Dietmar Kuehl
5// Copyright 2018-2024 Andrey Semashev
6
7// Distributed under the Boost Software License, Version 1.0.
8// See http://www.boost.org/LICENSE_1_0.txt
9
10// See library home page at http://www.boost.org/libs/filesystem
11
12//--------------------------------------------------------------------------------------//
13
14#include "platform_config.hpp"
15
16#include <boost/predef/os/bsd/open.h>
17#include <boost/filesystem/config.hpp>
18#include <boost/filesystem/operations.hpp>
19#include <boost/filesystem/file_status.hpp>
20#include <boost/filesystem/exception.hpp>
21#include <boost/filesystem/directory.hpp>
22#include <boost/system/error_code.hpp>
23#include <boost/detail/workaround.hpp>
24#include <boost/core/bit.hpp>
25#include <boost/cstdint.hpp>
26#include <boost/assert.hpp>
27#include <new> // std::bad_alloc, std::nothrow
28#include <limits>
29#include <memory>
30#include <string>
31#include <cstddef>
32#include <cstdlib> // for malloc, free
33#include <cstring>
34#include <cerrno>
35#include <stdio.h> // for rename
36
37// Default to POSIX under Emscripten
38// If BOOST_FILESYSTEM_EMSCRIPTEN_USE_WASI is set, use WASI instead
39#if defined(__wasm) && (!defined(__EMSCRIPTEN__) || defined(BOOST_FILESYSTEM_EMSCRIPTEN_USE_WASI))
40#define BOOST_FILESYSTEM_USE_WASI
41#endif
42
43#ifdef BOOST_POSIX_API
44
45#include <sys/types.h>
46#include <sys/stat.h>
47
48#if defined(BOOST_FILESYSTEM_USE_WASI)
49// WASI does not have statfs or statvfs.
50#elif !defined(__APPLE__) && \
51 (!defined(__OpenBSD__) || BOOST_OS_BSD_OPEN >= BOOST_VERSION_NUMBER(4, 4, 0)) && \
52 !defined(__ANDROID__) && \
53 !defined(__VXWORKS__)
54#include <sys/statvfs.h>
55#define BOOST_STATVFS statvfs
56#define BOOST_STATVFS_F_FRSIZE vfs.f_frsize
57#else
58#ifdef __OpenBSD__
59#include <sys/param.h>
60#elif defined(__ANDROID__)
61#include <sys/vfs.h>
62#endif
63#if !defined(__VXWORKS__)
64#include <sys/mount.h>
65#endif
66#define BOOST_STATVFS statfs
67#define BOOST_STATVFS_F_FRSIZE static_cast< uintmax_t >(vfs.f_bsize)
68#endif // BOOST_STATVFS definition
69
70#include <unistd.h>
71#include <fcntl.h>
72#if !defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
73#include <utime.h>
74#endif
75#include <limits.h>
76
77#if defined(linux) || defined(__linux) || defined(__linux__)
78
79#include <sys/vfs.h>
80#include <sys/utsname.h>
81#include <sys/syscall.h>
82#include <sys/sysmacros.h>
83#if !defined(BOOST_FILESYSTEM_DISABLE_SENDFILE)
84#include <sys/sendfile.h>
85#define BOOST_FILESYSTEM_USE_SENDFILE
86#endif // !defined(BOOST_FILESYSTEM_DISABLE_SENDFILE)
87#if !defined(BOOST_FILESYSTEM_DISABLE_COPY_FILE_RANGE) && defined(__NR_copy_file_range)
88#define BOOST_FILESYSTEM_USE_COPY_FILE_RANGE
89#endif // !defined(BOOST_FILESYSTEM_DISABLE_COPY_FILE_RANGE) && defined(__NR_copy_file_range)
90#if !defined(BOOST_FILESYSTEM_DISABLE_STATX) && (defined(BOOST_FILESYSTEM_HAS_STATX) || defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL))
91#if !defined(BOOST_FILESYSTEM_HAS_STATX) && defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
92#include <linux/stat.h>
93#endif
94#define BOOST_FILESYSTEM_USE_STATX
95#endif // !defined(BOOST_FILESYSTEM_DISABLE_STATX) && (defined(BOOST_FILESYSTEM_HAS_STATX) || defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL))
96
97#if defined(__has_include)
98#if __has_include(<linux/magic.h>)
99// This header was introduced in Linux kernel 2.6.19
100#include <linux/magic.h>
101#endif
102#endif
103
104// Some filesystem type magic constants are not defined in older kernel headers
105#ifndef PROC_SUPER_MAGIC
106#define PROC_SUPER_MAGIC 0x9fa0
107#endif
108#ifndef SYSFS_MAGIC
109#define SYSFS_MAGIC 0x62656572
110#endif
111#ifndef TRACEFS_MAGIC
112#define TRACEFS_MAGIC 0x74726163
113#endif
114#ifndef DEBUGFS_MAGIC
115#define DEBUGFS_MAGIC 0x64626720
116#endif
117
118#endif // defined(linux) || defined(__linux) || defined(__linux__)
119
120#include <boost/scope/unique_fd.hpp>
121
122#if defined(POSIX_FADV_SEQUENTIAL) && (!defined(__ANDROID__) || __ANDROID_API__ >= 21)
123#define BOOST_FILESYSTEM_HAS_POSIX_FADVISE
124#endif
125
126#if defined(BOOST_FILESYSTEM_HAS_STAT_ST_MTIM)
127#define BOOST_FILESYSTEM_STAT_ST_MTIMENSEC st_mtim.tv_nsec
128#elif defined(BOOST_FILESYSTEM_HAS_STAT_ST_MTIMESPEC)
129#define BOOST_FILESYSTEM_STAT_ST_MTIMENSEC st_mtimespec.tv_nsec
130#elif defined(BOOST_FILESYSTEM_HAS_STAT_ST_MTIMENSEC)
131#define BOOST_FILESYSTEM_STAT_ST_MTIMENSEC st_mtimensec
132#endif
133
134#if defined(BOOST_FILESYSTEM_HAS_STAT_ST_BIRTHTIM)
135#define BOOST_FILESYSTEM_STAT_ST_BIRTHTIME st_birthtim.tv_sec
136#define BOOST_FILESYSTEM_STAT_ST_BIRTHTIMENSEC st_birthtim.tv_nsec
137#elif defined(BOOST_FILESYSTEM_HAS_STAT_ST_BIRTHTIMESPEC)
138#define BOOST_FILESYSTEM_STAT_ST_BIRTHTIME st_birthtimespec.tv_sec
139#define BOOST_FILESYSTEM_STAT_ST_BIRTHTIMENSEC st_birthtimespec.tv_nsec
140#elif defined(BOOST_FILESYSTEM_HAS_STAT_ST_BIRTHTIMENSEC)
141#define BOOST_FILESYSTEM_STAT_ST_BIRTHTIME st_birthtime
142#define BOOST_FILESYSTEM_STAT_ST_BIRTHTIMENSEC st_birthtimensec
143#endif
144
145#include "posix_tools.hpp"
146
147#else // BOOST_WINDOWS_API
148
149#include <boost/winapi/dll.hpp> // get_proc_address, GetModuleHandleW
150#include <cwchar>
151#include <io.h>
152#include <windows.h>
153#include <winnt.h>
154#if defined(__BORLANDC__) || defined(__MWERKS__)
155#if defined(BOOST_BORLANDC)
156using std::time_t;
157#endif
158#include <utime.h>
159#else
160#include <sys/utime.h>
161#endif
162
163#include "windows_tools.hpp"
164
165#endif // BOOST_WINDOWS_API
166
167#include "atomic_tools.hpp"
168#include "error_handling.hpp"
169#include "private_config.hpp"
170
171#include <boost/filesystem/detail/header.hpp> // must be the last #include
172
173namespace fs = boost::filesystem;
174using boost::filesystem::path;
175using boost::filesystem::filesystem_error;
176using boost::filesystem::perms;
177using boost::system::error_code;
178using boost::system::system_category;
179
180#if defined(BOOST_POSIX_API)
181
182// At least Mac OS X 10.6 and older doesn't support O_CLOEXEC
183#ifndef O_CLOEXEC
184#define O_CLOEXEC 0
185#endif
186
187#if defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0
188#define BOOST_FILESYSTEM_HAS_FDATASYNC
189#endif
190
191#else // defined(BOOST_POSIX_API)
192
193#ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE
194#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE (16 * 1024)
195#endif
196
197#ifndef FSCTL_GET_REPARSE_POINT
198#define FSCTL_GET_REPARSE_POINT 0x900a8
199#endif
200
201#ifndef SYMLINK_FLAG_RELATIVE
202#define SYMLINK_FLAG_RELATIVE 1
203#endif
204
205// Fallback for MinGW/Cygwin
206#ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
207#define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1
208#endif
209
210#ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
211#define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE 0x2
212#endif
213
214#endif // defined(BOOST_POSIX_API)
215
216// POSIX/Windows macros ----------------------------------------------------//
217
218// Portions of the POSIX and Windows API's are very similar, except for name,
219// order of arguments, and meaning of zero/non-zero returns. The macros below
220// abstract away those differences. They follow Windows naming and order of
221// arguments, and return true to indicate no error occurred. [POSIX naming,
222// order of arguments, and meaning of return were followed initially, but
223// found to be less clear and cause more coding errors.]
224
225#if defined(BOOST_POSIX_API)
226
227#define BOOST_SET_CURRENT_DIRECTORY(P) (::chdir(P) == 0)
228#define BOOST_MOVE_FILE(OLD, NEW) (::rename(OLD, NEW) == 0)
229#define BOOST_RESIZE_FILE(P, SZ) (::truncate(P, SZ) == 0)
230
231#else // BOOST_WINDOWS_API
232
233#define BOOST_SET_CURRENT_DIRECTORY(P) (::SetCurrentDirectoryW(P) != 0)
234#define BOOST_MOVE_FILE(OLD, NEW) (::MoveFileExW(OLD, NEW, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) != 0)
235#define BOOST_RESIZE_FILE(P, SZ) (resize_file_impl(P, SZ) != 0)
236
237#endif
238
239namespace boost {
240namespace filesystem {
241namespace detail {
242
243#if defined(linux) || defined(__linux) || defined(__linux__)
244//! Initializes fill_random implementation pointer. Implemented in unique_path.cpp.
245void init_fill_random_impl(unsigned int major_ver, unsigned int minor_ver, unsigned int patch_ver);
246#endif // defined(linux) || defined(__linux) || defined(__linux__)
247
248#if defined(BOOST_WINDOWS_API)
249//! Initializes directory iterator implementation. Implemented in directory.cpp.
250void init_directory_iterator_impl() noexcept;
251#endif // defined(BOOST_WINDOWS_API)
252
253//--------------------------------------------------------------------------------------//
254// //
255// helpers (all operating systems) //
256// //
257//--------------------------------------------------------------------------------------//
258
259namespace {
260
261// The number of retries remove_all should make if it detects that the directory it is about to enter has been replaced with a symlink or a regular file
262BOOST_CONSTEXPR_OR_CONST unsigned int remove_all_directory_replaced_retry_count = 5u;
263
264#if defined(BOOST_POSIX_API)
265
266// Size of a small buffer for a path that can be placed on stack, in character code units
267BOOST_CONSTEXPR_OR_CONST std::size_t small_path_size = 1024u;
268
269// Absolute maximum path length, in character code units, that we're willing to accept from various system calls.
270// This value is arbitrary, it is supposed to be a hard limit to avoid memory exhaustion
271// in some of the algorithms below in case of some corrupted or maliciously broken filesystem.
272// A few examples of path size limits:
273// - Windows: 32767 UTF-16 code units or 260 bytes for legacy multibyte APIs.
274// - Linux: 4096 bytes
275// - IRIX, HP-UX, Mac OS, QNX, FreeBSD, OpenBSD: 1024 bytes
276// - GNU/Hurd: no hard limit
277BOOST_CONSTEXPR_OR_CONST std::size_t absolute_path_max = 32u * 1024u;
278
279#endif // defined(BOOST_POSIX_API)
280
281// Maximum number of resolved symlinks before we register a loop
282BOOST_CONSTEXPR_OR_CONST unsigned int symloop_max =
283#if defined(SYMLOOP_MAX)
284 SYMLOOP_MAX < 40 ? 40 : SYMLOOP_MAX
285#else
286 40
287#endif
288;
289
290// general helpers -----------------------------------------------------------------//
291
292bool is_empty_directory(path const& p, error_code* ec)
293{
294 fs::directory_iterator itr;
295 detail::directory_iterator_construct(it&: itr, p, opts: directory_options::none, params: nullptr, ec);
296 return itr == fs::directory_iterator();
297}
298
299bool not_found_error(int errval) noexcept; // forward declaration
300
301#ifdef BOOST_POSIX_API
302
303//--------------------------------------------------------------------------------------//
304// //
305// POSIX-specific helpers //
306// //
307//--------------------------------------------------------------------------------------//
308
309inline bool not_found_error(int errval) noexcept
310{
311 return errval == ENOENT || errval == ENOTDIR;
312}
313
314/*!
315 * Closes a file descriptor and returns the result, similar to close(2). Unlike close(2), guarantees that the file descriptor is closed even if EINTR error happens.
316 *
317 * Some systems don't close the file descriptor in case if the thread is interrupted by a signal and close(2) returns EINTR.
318 * Other (most) systems do close the file descriptor even when when close(2) returns EINTR, and attempting to close it
319 * again could close a different file descriptor that was opened by a different thread. This function hides this difference in behavior.
320 *
321 * Future POSIX standards will likely fix this by introducing posix_close (see https://www.austingroupbugs.net/view.php?id=529)
322 * and prohibiting returning EINTR from close(2), but we still have to support older systems where this new behavior is not available and close(2)
323 * behaves differently between systems.
324 */
325inline int close_fd(int fd)
326{
327#if defined(hpux) || defined(_hpux) || defined(__hpux)
328 int res;
329 while (true)
330 {
331 res = ::close(fd);
332 if (BOOST_UNLIKELY(res < 0))
333 {
334 int err = errno;
335 if (err == EINTR)
336 continue;
337 }
338
339 break;
340 }
341
342 return res;
343#else
344 return ::close(fd: fd);
345#endif
346}
347
348#if defined(BOOST_FILESYSTEM_HAS_STATX)
349
350//! A wrapper for statx libc function. Disable MSAN since at least on clang 10 it doesn't
351//! know which fields of struct statx are initialized by the syscall and misdetects errors.
352BOOST_FILESYSTEM_NO_SANITIZE_MEMORY
353BOOST_FORCEINLINE int invoke_statx(int dirfd, const char* path, int flags, unsigned int mask, struct ::statx* stx)
354{
355 return ::statx(dirfd: dirfd, path: path, flags: flags, mask: mask, buf: stx);
356}
357
358#elif defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
359
360//! statx emulation through fstatat
361int statx_fstatat(int dirfd, const char* path, int flags, unsigned int mask, struct ::statx* stx)
362{
363 struct ::stat st;
364 flags &= AT_EMPTY_PATH | AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW;
365 int res = ::fstatat(dirfd, path, &st, flags);
366 if (BOOST_LIKELY(res == 0))
367 {
368 std::memset(stx, 0, sizeof(*stx));
369 stx->stx_mask = STATX_BASIC_STATS;
370 stx->stx_blksize = st.st_blksize;
371 stx->stx_nlink = st.st_nlink;
372 stx->stx_uid = st.st_uid;
373 stx->stx_gid = st.st_gid;
374 stx->stx_mode = st.st_mode;
375 stx->stx_ino = st.st_ino;
376 stx->stx_size = st.st_size;
377 stx->stx_blocks = st.st_blocks;
378 stx->stx_atime.tv_sec = st.st_atim.tv_sec;
379 stx->stx_atime.tv_nsec = st.st_atim.tv_nsec;
380 stx->stx_ctime.tv_sec = st.st_ctim.tv_sec;
381 stx->stx_ctime.tv_nsec = st.st_ctim.tv_nsec;
382 stx->stx_mtime.tv_sec = st.st_mtim.tv_sec;
383 stx->stx_mtime.tv_nsec = st.st_mtim.tv_nsec;
384 stx->stx_rdev_major = major(st.st_rdev);
385 stx->stx_rdev_minor = minor(st.st_rdev);
386 stx->stx_dev_major = major(st.st_dev);
387 stx->stx_dev_minor = minor(st.st_dev);
388 }
389
390 return res;
391}
392
393typedef int statx_t(int dirfd, const char* path, int flags, unsigned int mask, struct ::statx* stx);
394
395//! Pointer to the actual implementation of the statx implementation
396statx_t* statx_ptr = &statx_fstatat;
397
398inline int invoke_statx(int dirfd, const char* path, int flags, unsigned int mask, struct ::statx* stx) noexcept
399{
400 return filesystem::detail::atomic_load_relaxed(statx_ptr)(dirfd, path, flags, mask, stx);
401}
402
403//! A wrapper for the statx syscall. Disable MSAN since at least on clang 10 it doesn't
404//! know which fields of struct statx are initialized by the syscall and misdetects errors.
405BOOST_FILESYSTEM_NO_SANITIZE_MEMORY
406int statx_syscall(int dirfd, const char* path, int flags, unsigned int mask, struct ::statx* stx)
407{
408 int res = ::syscall(__NR_statx, dirfd, path, flags, mask, stx);
409 if (res < 0)
410 {
411 const int err = errno;
412 if (BOOST_UNLIKELY(err == ENOSYS))
413 {
414 filesystem::detail::atomic_store_relaxed(statx_ptr, &statx_fstatat);
415 return statx_fstatat(dirfd, path, flags, mask, stx);
416 }
417 }
418
419 return res;
420}
421
422#endif // defined(BOOST_FILESYSTEM_HAS_STATX)
423
424#if defined(linux) || defined(__linux) || defined(__linux__)
425
426//! Initializes statx implementation pointer
427inline void init_statx_impl(unsigned int major_ver, unsigned int minor_ver, unsigned int patch_ver)
428{
429#if !defined(BOOST_FILESYSTEM_HAS_STATX) && defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
430 statx_t* stx = &statx_fstatat;
431 if (major_ver > 4u || (major_ver == 4u && minor_ver >= 11u))
432 stx = &statx_syscall;
433
434 filesystem::detail::atomic_store_relaxed(statx_ptr, stx);
435#endif // !defined(BOOST_FILESYSTEM_HAS_STATX) && defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
436}
437
438#endif // defined(linux) || defined(__linux) || defined(__linux__)
439
440#if defined(BOOST_FILESYSTEM_USE_STATX)
441
442//! Returns \c true if the two \c statx structures refer to the same file
443inline bool equivalent_stat(struct ::statx const& s1, struct ::statx const& s2) noexcept
444{
445 return s1.stx_dev_major == s2.stx_dev_major && s1.stx_dev_minor == s2.stx_dev_minor && s1.stx_ino == s2.stx_ino;
446}
447
448//! Returns file type/access mode from \c statx structure
449inline mode_t get_mode(struct ::statx const& st) noexcept
450{
451 return st.stx_mode;
452}
453
454//! Returns file size from \c statx structure
455inline uintmax_t get_size(struct ::statx const& st) noexcept
456{
457 return st.stx_size;
458}
459
460//! Returns optimal block size from \c statx structure
461inline std::size_t get_blksize(struct ::statx const& st) noexcept
462{
463 return st.stx_blksize;
464}
465
466#else // defined(BOOST_FILESYSTEM_USE_STATX)
467
468//! Returns \c true if the two \c stat structures refer to the same file
469inline bool equivalent_stat(struct ::stat const& s1, struct ::stat const& s2) noexcept
470{
471 // According to the POSIX stat specs, "The st_ino and st_dev fields
472 // taken together uniquely identify the file within the system."
473 return s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino;
474}
475
476//! Returns file type/access mode from \c stat structure
477inline mode_t get_mode(struct ::stat const& st) noexcept
478{
479 return st.st_mode;
480}
481
482//! Returns file size from \c stat structure
483inline uintmax_t get_size(struct ::stat const& st) noexcept
484{
485 return st.st_size;
486}
487
488//! Returns optimal block size from \c stat structure
489inline std::size_t get_blksize(struct ::stat const& st) noexcept
490{
491#if defined(BOOST_FILESYSTEM_HAS_STAT_ST_BLKSIZE)
492 return st.st_blksize;
493#else
494 return 4096u; // a suitable default used on most modern SSDs/HDDs
495#endif
496}
497
498#endif // defined(BOOST_FILESYSTEM_USE_STATX)
499
500} // namespace
501
502//! status() implementation
503file_status status_impl
504(
505 path const& p,
506 system::error_code* ec
507#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) || defined(BOOST_FILESYSTEM_USE_STATX)
508 , int basedir_fd
509#endif
510)
511{
512#if defined(BOOST_FILESYSTEM_USE_STATX)
513 struct ::statx path_stat;
514 int err = invoke_statx(dirfd: basedir_fd, path: p.c_str(), AT_NO_AUTOMOUNT, STATX_TYPE | STATX_MODE, stx: &path_stat);
515#elif defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
516 struct ::stat path_stat;
517 int err = ::fstatat(basedir_fd, p.c_str(), &path_stat, AT_NO_AUTOMOUNT);
518#else
519 struct ::stat path_stat;
520 int err = ::stat(p.c_str(), &path_stat);
521#endif
522
523 if (err != 0)
524 {
525 err = errno;
526 if (ec) // always report errno, even though some
527 ec->assign(val: err, cat: system::system_category()); // errno values are not status_errors
528
529 if (not_found_error(errval: err))
530 return fs::file_status(fs::file_not_found, fs::no_perms);
531
532 if (!ec)
533 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::status", p, system::error_code(err, system::system_category())));
534
535 return fs::file_status(fs::status_error);
536 }
537
538#if defined(BOOST_FILESYSTEM_USE_STATX)
539 if (BOOST_UNLIKELY((path_stat.stx_mask & (STATX_TYPE | STATX_MODE)) != (STATX_TYPE | STATX_MODE)))
540 {
541 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, message: "boost::filesystem::status");
542 return fs::file_status(fs::status_error);
543 }
544#endif
545
546 const mode_t mode = get_mode(st: path_stat);
547 if (S_ISDIR(mode))
548 return fs::file_status(fs::directory_file, static_cast< perms >(mode) & fs::perms_mask);
549 if (S_ISREG(mode))
550 return fs::file_status(fs::regular_file, static_cast< perms >(mode) & fs::perms_mask);
551 if (S_ISBLK(mode))
552 return fs::file_status(fs::block_file, static_cast< perms >(mode) & fs::perms_mask);
553 if (S_ISCHR(mode))
554 return fs::file_status(fs::character_file, static_cast< perms >(mode) & fs::perms_mask);
555 if (S_ISFIFO(mode))
556 return fs::file_status(fs::fifo_file, static_cast< perms >(mode) & fs::perms_mask);
557 if (S_ISSOCK(mode))
558 return fs::file_status(fs::socket_file, static_cast< perms >(mode) & fs::perms_mask);
559
560 return fs::file_status(fs::type_unknown);
561}
562
563//! symlink_status() implementation
564file_status symlink_status_impl
565(
566 path const& p,
567 system::error_code* ec
568#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) || defined(BOOST_FILESYSTEM_USE_STATX)
569 , int basedir_fd
570#endif
571)
572{
573#if defined(BOOST_FILESYSTEM_USE_STATX)
574 struct ::statx path_stat;
575 int err = invoke_statx(dirfd: basedir_fd, path: p.c_str(), AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT, STATX_TYPE | STATX_MODE, stx: &path_stat);
576#elif defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
577 struct ::stat path_stat;
578 int err = ::fstatat(basedir_fd, p.c_str(), &path_stat, AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT);
579#else
580 struct ::stat path_stat;
581 int err = ::lstat(p.c_str(), &path_stat);
582#endif
583
584 if (err != 0)
585 {
586 err = errno;
587 if (ec) // always report errno, even though some
588 ec->assign(val: err, cat: system::system_category()); // errno values are not status_errors
589
590 if (not_found_error(errval: err)) // these are not errors
591 return fs::file_status(fs::file_not_found, fs::no_perms);
592
593 if (!ec)
594 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::symlink_status", p, system::error_code(err, system::system_category())));
595
596 return fs::file_status(fs::status_error);
597 }
598
599#if defined(BOOST_FILESYSTEM_USE_STATX)
600 if (BOOST_UNLIKELY((path_stat.stx_mask & (STATX_TYPE | STATX_MODE)) != (STATX_TYPE | STATX_MODE)))
601 {
602 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, message: "boost::filesystem::symlink_status");
603 return fs::file_status(fs::status_error);
604 }
605#endif
606
607 const mode_t mode = get_mode(st: path_stat);
608 if (S_ISREG(mode))
609 return fs::file_status(fs::regular_file, static_cast< perms >(mode) & fs::perms_mask);
610 if (S_ISDIR(mode))
611 return fs::file_status(fs::directory_file, static_cast< perms >(mode) & fs::perms_mask);
612 if (S_ISLNK(mode))
613 return fs::file_status(fs::symlink_file, static_cast< perms >(mode) & fs::perms_mask);
614 if (S_ISBLK(mode))
615 return fs::file_status(fs::block_file, static_cast< perms >(mode) & fs::perms_mask);
616 if (S_ISCHR(mode))
617 return fs::file_status(fs::character_file, static_cast< perms >(mode) & fs::perms_mask);
618 if (S_ISFIFO(mode))
619 return fs::file_status(fs::fifo_file, static_cast< perms >(mode) & fs::perms_mask);
620 if (S_ISSOCK(mode))
621 return fs::file_status(fs::socket_file, static_cast< perms >(mode) & fs::perms_mask);
622
623 return fs::file_status(fs::type_unknown);
624}
625
626namespace {
627
628//! Flushes buffered data and attributes written to the file to permanent storage
629inline int full_sync(int fd)
630{
631 while (true)
632 {
633#if defined(__APPLE__) && defined(__MACH__) && defined(F_FULLFSYNC)
634 // Mac OS does not flush data to physical storage with fsync()
635 int err = ::fcntl(fd, F_FULLFSYNC);
636#else
637 int err = ::fsync(fd: fd);
638#endif
639 if (BOOST_UNLIKELY(err < 0))
640 {
641 err = errno;
642 // POSIX says fsync can return EINTR (https://pubs.opengroup.org/onlinepubs/9699919799/functions/fsync.html).
643 // fcntl(F_FULLFSYNC) isn't documented to return EINTR, but it doesn't hurt to check.
644 if (err == EINTR)
645 continue;
646
647 return err;
648 }
649
650 break;
651 }
652
653 return 0;
654}
655
656//! Flushes buffered data written to the file to permanent storage
657inline int data_sync(int fd)
658{
659#if defined(BOOST_FILESYSTEM_HAS_FDATASYNC) && !(defined(__APPLE__) && defined(__MACH__) && defined(F_FULLFSYNC))
660 while (true)
661 {
662 int err = ::fdatasync(fildes: fd);
663 if (BOOST_UNLIKELY(err != 0))
664 {
665 err = errno;
666 // POSIX says fsync can return EINTR (https://pubs.opengroup.org/onlinepubs/9699919799/functions/fsync.html).
667 // It doesn't say so for fdatasync, but it is reasonable to expect it as well.
668 if (err == EINTR)
669 continue;
670
671 return err;
672 }
673
674 break;
675 }
676
677 return 0;
678#else
679 return full_sync(fd);
680#endif
681}
682
683//! Hints the filesystem to opportunistically preallocate storage for a file
684inline int preallocate_storage(int file, uintmax_t size)
685{
686#if defined(BOOST_FILESYSTEM_HAS_FALLOCATE)
687 if (BOOST_LIKELY(size > 0 && size <= static_cast< uintmax_t >((std::numeric_limits< off_t >::max)())))
688 {
689 while (true)
690 {
691 // Note: We intentionally use fallocate rather than posix_fallocate to avoid
692 // invoking glibc emulation that writes zeros to the end of the file.
693 // We want this call to act like a hint to the filesystem and an early
694 // check for the free storage space. We don't want to write zeros only
695 // to later overwrite them with the actual data.
696 int err = fallocate(fd: file, FALLOC_FL_KEEP_SIZE, offset: 0, len: static_cast< off_t >(size));
697 if (BOOST_UNLIKELY(err != 0))
698 {
699 err = errno;
700
701 // Ignore the error if the operation is not supported by the kernel or filesystem
702 if (err == EOPNOTSUPP || err == ENOSYS)
703 break;
704
705 if (err == EINTR)
706 continue;
707
708 return err;
709 }
710
711 break;
712 }
713 }
714#endif
715
716 return 0;
717}
718
719//! copy_file implementation wrapper that preallocates storage for the target file
720template< typename CopyFileData >
721struct copy_file_data_preallocate
722{
723 //! copy_file implementation wrapper that preallocates storage for the target file before invoking the underlying copy implementation
724 static int impl(int infile, int outfile, uintmax_t size, std::size_t blksize)
725 {
726 int err = preallocate_storage(file: outfile, size);
727 if (BOOST_UNLIKELY(err != 0))
728 return err;
729
730 return CopyFileData::impl(infile, outfile, size, blksize);
731 }
732};
733
734// Min and max buffer sizes are selected to minimize the overhead from system calls.
735// The values are picked based on coreutils cp(1) benchmarking data described here:
736// https://github.com/coreutils/coreutils/blob/d1b0257077c0b0f0ee25087efd46270345d1dd1f/src/ioblksize.h#L23-L72
737BOOST_CONSTEXPR_OR_CONST uint_least32_t min_read_write_buf_size = 8u * 1024u;
738BOOST_CONSTEXPR_OR_CONST uint_least32_t max_read_write_buf_size = 256u * 1024u;
739
740//! copy_file read/write loop implementation
741int copy_file_data_read_write_impl(int infile, int outfile, char* buf, std::size_t buf_size)
742{
743#if defined(BOOST_FILESYSTEM_HAS_POSIX_FADVISE)
744 ::posix_fadvise(fd: infile, offset: 0, len: 0, POSIX_FADV_SEQUENTIAL);
745#endif
746
747 // Don't use file size to limit the amount of data to copy since some filesystems, like procfs or sysfs,
748 // provide files with generated content and indicate that their size is zero or 4096. Just copy as much data
749 // as we can read from the input file.
750 while (true)
751 {
752 ssize_t sz_read = ::read(fd: infile, buf: buf, nbytes: buf_size);
753 if (sz_read == 0)
754 break;
755 if (BOOST_UNLIKELY(sz_read < 0))
756 {
757 int err = errno;
758 if (err == EINTR)
759 continue;
760 return err;
761 }
762
763 // Allow for partial writes - see Advanced Unix Programming (2nd Ed.),
764 // Marc Rochkind, Addison-Wesley, 2004, page 94
765 for (ssize_t sz_wrote = 0; sz_wrote < sz_read;)
766 {
767 ssize_t sz = ::write(fd: outfile, buf: buf + sz_wrote, n: static_cast< std::size_t >(sz_read - sz_wrote));
768 if (BOOST_UNLIKELY(sz < 0))
769 {
770 int err = errno;
771 if (err == EINTR)
772 continue;
773 return err;
774 }
775
776 sz_wrote += sz;
777 }
778 }
779
780 return 0;
781}
782
783//! copy_file implementation that uses read/write loop (fallback using a stack buffer)
784int copy_file_data_read_write_stack_buf(int infile, int outfile)
785{
786 char stack_buf[min_read_write_buf_size];
787 return copy_file_data_read_write_impl(infile, outfile, buf: stack_buf, buf_size: sizeof(stack_buf));
788}
789
790//! copy_file implementation that uses read/write loop
791int copy_file_data_read_write(int infile, int outfile, uintmax_t size, std::size_t blksize)
792{
793 {
794 uintmax_t buf_sz = size;
795 // Prefer the buffer to be larger than the file size so that we don't have
796 // to perform an extra read if the file fits in the buffer exactly.
797 buf_sz += (buf_sz < ~static_cast< uintmax_t >(0u));
798 if (buf_sz < blksize)
799 buf_sz = blksize;
800 if (buf_sz < min_read_write_buf_size)
801 buf_sz = min_read_write_buf_size;
802 if (buf_sz > max_read_write_buf_size)
803 buf_sz = max_read_write_buf_size;
804 const std::size_t buf_size = static_cast< std::size_t >(boost::core::bit_ceil(x: static_cast< uint_least32_t >(buf_sz)));
805 std::unique_ptr< char[] > buf(new (std::nothrow) char[buf_size]);
806 if (BOOST_LIKELY(!!buf.get()))
807 return copy_file_data_read_write_impl(infile, outfile, buf: buf.get(), buf_size);
808 }
809
810 return copy_file_data_read_write_stack_buf(infile, outfile);
811}
812
813typedef int copy_file_data_t(int infile, int outfile, uintmax_t size, std::size_t blksize);
814
815//! Pointer to the actual implementation of the copy_file_data implementation
816copy_file_data_t* copy_file_data = &copy_file_data_read_write;
817
818#if defined(BOOST_FILESYSTEM_USE_SENDFILE) || defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
819
820//! copy_file_data wrapper that tests if a read/write loop must be used for a given filesystem
821template< typename CopyFileData >
822int check_fs_type(int infile, int outfile, uintmax_t size, std::size_t blksize);
823
824#endif // defined(BOOST_FILESYSTEM_USE_SENDFILE) || defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
825
826#if defined(BOOST_FILESYSTEM_USE_SENDFILE)
827
828struct copy_file_data_sendfile
829{
830 //! copy_file implementation that uses sendfile loop. Requires sendfile to support file descriptors.
831 static int impl(int infile, int outfile, uintmax_t size, std::size_t blksize)
832 {
833 // sendfile will not send more than this amount of data in one call
834 BOOST_CONSTEXPR_OR_CONST std::size_t max_batch_size = 0x7ffff000u;
835 uintmax_t offset = 0u;
836 while (offset < size)
837 {
838 uintmax_t size_left = size - offset;
839 std::size_t size_to_copy = max_batch_size;
840 if (size_left < static_cast< uintmax_t >(max_batch_size))
841 size_to_copy = static_cast< std::size_t >(size_left);
842 ssize_t sz = ::sendfile(out_fd: outfile, in_fd: infile, offset: nullptr, count: size_to_copy);
843 if (BOOST_LIKELY(sz > 0))
844 {
845 offset += sz;
846 }
847 else if (sz < 0)
848 {
849 int err = errno;
850 if (err == EINTR)
851 continue;
852
853 if (offset == 0u)
854 {
855 // sendfile may fail with EINVAL if the underlying filesystem does not support it
856 if (err == EINVAL)
857 {
858 fallback_to_read_write:
859 return copy_file_data_read_write(infile, outfile, size, blksize);
860 }
861
862 if (err == ENOSYS)
863 {
864 filesystem::detail::atomic_store_relaxed(a&: copy_file_data, val: &copy_file_data_read_write);
865 goto fallback_to_read_write;
866 }
867 }
868
869 return err;
870 }
871 else
872 {
873 // EOF: the input file was truncated while copying was in progress
874 break;
875 }
876 }
877
878 return 0;
879 }
880};
881
882#endif // defined(BOOST_FILESYSTEM_USE_SENDFILE)
883
884#if defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
885
886struct copy_file_data_copy_file_range
887{
888 //! copy_file implementation that uses copy_file_range loop. Requires copy_file_range to support cross-filesystem copying.
889 static int impl(int infile, int outfile, uintmax_t size, std::size_t blksize)
890 {
891 // Although copy_file_range does not document any particular upper limit of one transfer, still use some upper bound to guarantee
892 // that size_t is not overflown in case if off_t is larger and the file size does not fit in size_t.
893 BOOST_CONSTEXPR_OR_CONST std::size_t max_batch_size = 0x7ffff000u;
894 uintmax_t offset = 0u;
895 while (offset < size)
896 {
897 uintmax_t size_left = size - offset;
898 std::size_t size_to_copy = max_batch_size;
899 if (size_left < static_cast< uintmax_t >(max_batch_size))
900 size_to_copy = static_cast< std::size_t >(size_left);
901 // Note: Use syscall directly to avoid depending on libc version. copy_file_range is added in glibc 2.27.
902 // uClibc-ng does not have copy_file_range as of the time of this writing (the latest uClibc-ng release is 1.0.33).
903 loff_t sz = ::syscall(__NR_copy_file_range, infile, (loff_t*)nullptr, outfile, (loff_t*)nullptr, size_to_copy, (unsigned int)0u);
904 if (BOOST_LIKELY(sz > 0))
905 {
906 offset += sz;
907 }
908 else if (sz < 0)
909 {
910 int err = errno;
911 if (err == EINTR)
912 continue;
913
914 if (offset == 0u)
915 {
916 // copy_file_range may fail with EINVAL if the underlying filesystem does not support it.
917 // In some RHEL/CentOS 7.7-7.8 kernel versions, copy_file_range on NFSv4 is also known to return EOPNOTSUPP
918 // if the remote server does not support COPY, despite that it is not a documented error code.
919 // See https://patchwork.kernel.org/project/linux-nfs/patch/20190411183418.4510-1-olga.kornievskaia@gmail.com/
920 // and https://bugzilla.redhat.com/show_bug.cgi?id=1783554.
921 if (err == EINVAL || err == EOPNOTSUPP)
922 {
923#if !defined(BOOST_FILESYSTEM_USE_SENDFILE)
924 fallback_to_read_write:
925#endif
926 return copy_file_data_read_write(infile, outfile, size, blksize);
927 }
928
929 if (err == EXDEV)
930 {
931#if defined(BOOST_FILESYSTEM_USE_SENDFILE)
932 fallback_to_sendfile:
933 return copy_file_data_sendfile::impl(infile, outfile, size, blksize);
934#else
935 goto fallback_to_read_write;
936#endif
937 }
938
939 if (err == ENOSYS)
940 {
941#if defined(BOOST_FILESYSTEM_USE_SENDFILE)
942 filesystem::detail::atomic_store_relaxed(a&: copy_file_data, val: &check_fs_type< copy_file_data_preallocate< copy_file_data_sendfile > >);
943 goto fallback_to_sendfile;
944#else
945 filesystem::detail::atomic_store_relaxed(copy_file_data, &copy_file_data_read_write);
946 goto fallback_to_read_write;
947#endif
948 }
949 }
950
951 return err;
952 }
953 else
954 {
955 // EOF: the input file was truncated while copying was in progress
956 break;
957 }
958 }
959
960 return 0;
961 }
962};
963
964#endif // defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
965
966#if defined(BOOST_FILESYSTEM_USE_SENDFILE) || defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
967
968//! copy_file_data wrapper that tests if a read/write loop must be used for a given filesystem
969template< typename CopyFileData >
970int check_fs_type(int infile, int outfile, uintmax_t size, std::size_t blksize)
971{
972 {
973 // Some filesystems have regular files with generated content. Such files have arbitrary size, including zero,
974 // but have actual content. Linux system calls sendfile or copy_file_range will not copy contents of such files,
975 // so we must use a read/write loop to handle them.
976 // https://lore.kernel.org/linux-fsdevel/20210212044405.4120619-1-drinkcat@chromium.org/T/
977 struct statfs sfs;
978 while (true)
979 {
980 int err = ::fstatfs(fildes: infile, buf: &sfs);
981 if (BOOST_UNLIKELY(err < 0))
982 {
983 err = errno;
984 if (err == EINTR)
985 continue;
986
987 goto fallback_to_read_write;
988 }
989
990 break;
991 }
992
993 if (BOOST_UNLIKELY(sfs.f_type == PROC_SUPER_MAGIC ||
994 sfs.f_type == SYSFS_MAGIC ||
995 sfs.f_type == TRACEFS_MAGIC ||
996 sfs.f_type == DEBUGFS_MAGIC))
997 {
998 fallback_to_read_write:
999 return copy_file_data_read_write(infile, outfile, size, blksize);
1000 }
1001 }
1002
1003 return CopyFileData::impl(infile, outfile, size, blksize);
1004}
1005
1006#endif // defined(BOOST_FILESYSTEM_USE_SENDFILE) || defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
1007
1008#if defined(linux) || defined(__linux) || defined(__linux__)
1009
1010//! Initializes copy_file_data implementation pointer
1011inline void init_copy_file_data_impl(unsigned int major_ver, unsigned int minor_ver, unsigned int patch_ver)
1012{
1013#if defined(BOOST_FILESYSTEM_USE_SENDFILE) || defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
1014 copy_file_data_t* cfd = &copy_file_data_read_write;
1015
1016#if defined(BOOST_FILESYSTEM_USE_SENDFILE)
1017 // sendfile started accepting file descriptors as the target in Linux 2.6.33
1018 if (major_ver > 2u || (major_ver == 2u && (minor_ver > 6u || (minor_ver == 6u && patch_ver >= 33u))))
1019 cfd = &check_fs_type< copy_file_data_preallocate< copy_file_data_sendfile > >;
1020#endif
1021
1022#if defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
1023 // Although copy_file_range appeared in Linux 4.5, it did not support cross-filesystem copying until 5.3.
1024 // copy_file_data_copy_file_range will fallback to copy_file_data_sendfile if copy_file_range returns EXDEV.
1025 if (major_ver > 4u || (major_ver == 4u && minor_ver >= 5u))
1026 cfd = &check_fs_type< copy_file_data_preallocate< copy_file_data_copy_file_range > >;
1027#endif
1028
1029 filesystem::detail::atomic_store_relaxed(a&: copy_file_data, val: cfd);
1030#endif // defined(BOOST_FILESYSTEM_USE_SENDFILE) || defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
1031}
1032
1033#endif // defined(linux) || defined(__linux) || defined(__linux__)
1034
1035#if defined(linux) || defined(__linux) || defined(__linux__)
1036
1037struct syscall_initializer
1038{
1039 syscall_initializer()
1040 {
1041 struct ::utsname system_info;
1042 if (BOOST_UNLIKELY(::uname(&system_info) < 0))
1043 return;
1044
1045 unsigned int major_ver = 0u, minor_ver = 0u, patch_ver = 0u;
1046 int count = std::sscanf(s: system_info.release, format: "%u.%u.%u", &major_ver, &minor_ver, &patch_ver);
1047 if (BOOST_UNLIKELY(count < 3))
1048 return;
1049
1050 init_statx_impl(major_ver, minor_ver, patch_ver);
1051 init_copy_file_data_impl(major_ver, minor_ver, patch_ver);
1052 init_fill_random_impl(major_ver, minor_ver, patch_ver);
1053 }
1054};
1055
1056BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_FUNC_PTR_INIT_PRIORITY) BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN
1057const syscall_initializer syscall_init;
1058
1059#endif // defined(linux) || defined(__linux) || defined(__linux__)
1060
1061//! remove() implementation
1062inline bool remove_impl
1063(
1064 path const& p,
1065 fs::file_type type,
1066 error_code* ec
1067#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
1068 , int basedir_fd = AT_FDCWD
1069#endif
1070)
1071{
1072 if (type == fs::file_not_found)
1073 return false;
1074
1075 int res;
1076#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
1077 res = ::unlinkat(fd: basedir_fd, name: p.c_str(), flag: type == fs::directory_file ? AT_REMOVEDIR : 0);
1078#else
1079 if (type == fs::directory_file)
1080 res = ::rmdir(p.c_str());
1081 else
1082 res = ::unlink(p.c_str());
1083#endif
1084
1085 if (res != 0)
1086 {
1087 int err = errno;
1088 if (BOOST_UNLIKELY(!not_found_error(err)))
1089 emit_error(error_num: err, p, ec, message: "boost::filesystem::remove");
1090
1091 return false;
1092 }
1093
1094 return true;
1095}
1096
1097//! remove() implementation
1098inline bool remove_impl(path const& p, error_code* ec)
1099{
1100 // Since POSIX remove() is specified to work with either files or directories, in a
1101 // perfect world it could just be called. But some important real-world operating
1102 // systems (Windows, Mac OS, for example) don't implement the POSIX spec. So
1103 // we have to distinguish between files and directories and call corresponding APIs
1104 // to remove them.
1105
1106 error_code local_ec;
1107 fs::file_type type = fs::detail::symlink_status_impl(p, ec: &local_ec).type();
1108 if (BOOST_UNLIKELY(type == fs::status_error))
1109 {
1110 if (!ec)
1111 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove", p, local_ec));
1112
1113 *ec = local_ec;
1114 return false;
1115 }
1116
1117 return fs::detail::remove_impl(p, type, ec);
1118}
1119
1120//! remove_all() implementation
1121uintmax_t remove_all_impl
1122(
1123 path const& p,
1124 error_code* ec
1125#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
1126 , int parentdir_fd = AT_FDCWD
1127#endif
1128)
1129{
1130#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
1131 fs::path filename;
1132 const fs::path* remove_path = &p;
1133 if (parentdir_fd != AT_FDCWD)
1134 {
1135 filename = path_algorithms::filename_v4(p);
1136 remove_path = &filename;
1137 }
1138#endif
1139
1140 error_code dit_create_ec;
1141 for (unsigned int attempt = 0u; attempt < remove_all_directory_replaced_retry_count; ++attempt)
1142 {
1143 fs::file_type type;
1144 {
1145 error_code local_ec;
1146#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
1147 type = fs::detail::symlink_status_impl(p: *remove_path, ec: &local_ec, basedir_fd: parentdir_fd).type();
1148#else
1149 type = fs::detail::symlink_status_impl(p, &local_ec).type();
1150#endif
1151
1152 if (type == fs::file_not_found)
1153 return 0u;
1154
1155 if (BOOST_UNLIKELY(type == fs::status_error))
1156 {
1157 if (!ec)
1158 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove_all", p, local_ec));
1159
1160 *ec = local_ec;
1161 return static_cast< uintmax_t >(-1);
1162 }
1163 }
1164
1165 uintmax_t count = 0u;
1166 if (type == fs::directory_file) // but not a directory symlink
1167 {
1168 fs::directory_iterator itr;
1169#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
1170 fs::detail::directory_iterator_params params{ .dir_fd: fs::detail::openat_directory(basedir_fd: parentdir_fd, p: *remove_path, opts: directory_options::_detail_no_follow, ec&: dit_create_ec) };
1171 int dir_fd = -1;
1172 if (BOOST_LIKELY(!dit_create_ec))
1173 {
1174 // Save dir_fd as constructing the iterator will move the fd into the iterator context
1175 dir_fd = params.dir_fd.get();
1176 fs::detail::directory_iterator_construct(it&: itr, p: *remove_path, opts: directory_options::_detail_no_follow, params: &params, ec: &dit_create_ec);
1177 }
1178#else
1179 fs::detail::directory_iterator_construct
1180 (
1181 itr,
1182 p,
1183#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
1184 directory_options::_detail_no_follow,
1185#else
1186 directory_options::none,
1187#endif
1188 nullptr,
1189 &dit_create_ec
1190 );
1191#endif
1192
1193 if (BOOST_UNLIKELY(!!dit_create_ec))
1194 {
1195 if (dit_create_ec == error_code(ENOTDIR, system_category()))
1196 continue;
1197
1198#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
1199 // If open(2) with O_NOFOLLOW fails with ELOOP, this means that either the path contains a loop
1200 // of symbolic links, or the last element of the path is a symbolic link. Given that lstat(2) above
1201 // did not fail, most likely it is the latter case. I.e. between the lstat above and this open call
1202 // the filesystem was modified so that the path no longer refers to a directory file (as opposed to a symlink).
1203 if (dit_create_ec == error_code(ELOOP, system_category()))
1204 continue;
1205#endif // defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
1206
1207 if (!ec)
1208 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove_all", p, dit_create_ec));
1209
1210 *ec = dit_create_ec;
1211 return static_cast< uintmax_t >(-1);
1212 }
1213
1214 const fs::directory_iterator end_dit;
1215 while (itr != end_dit)
1216 {
1217 count += fs::detail::remove_all_impl
1218 (
1219 p: itr->path(),
1220 ec
1221#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
1222 , parentdir_fd: dir_fd
1223#endif
1224 );
1225 if (ec && *ec)
1226 return static_cast< uintmax_t >(-1);
1227
1228 fs::detail::directory_iterator_increment(it&: itr, ec);
1229 if (ec && *ec)
1230 return static_cast< uintmax_t >(-1);
1231 }
1232 }
1233
1234#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
1235 count += fs::detail::remove_impl(p: *remove_path, type, ec, basedir_fd: parentdir_fd);
1236#else
1237 count += fs::detail::remove_impl(p, type, ec);
1238#endif
1239 if (ec && *ec)
1240 return static_cast< uintmax_t >(-1);
1241
1242 return count;
1243 }
1244
1245 if (!ec)
1246 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove_all: path cannot be opened as a directory", p, dit_create_ec));
1247
1248 *ec = dit_create_ec;
1249 return static_cast< uintmax_t >(-1);
1250}
1251
1252#else // defined(BOOST_POSIX_API)
1253
1254//--------------------------------------------------------------------------------------//
1255// //
1256// Windows-specific helpers //
1257// //
1258//--------------------------------------------------------------------------------------//
1259
1260//! FILE_BASIC_INFO definition from Windows SDK
1261struct file_basic_info
1262{
1263 LARGE_INTEGER CreationTime;
1264 LARGE_INTEGER LastAccessTime;
1265 LARGE_INTEGER LastWriteTime;
1266 LARGE_INTEGER ChangeTime;
1267 DWORD FileAttributes;
1268};
1269
1270//! FILE_DISPOSITION_INFO definition from Windows SDK
1271struct file_disposition_info
1272{
1273 BOOLEAN DeleteFile;
1274};
1275
1276//! FILE_DISPOSITION_INFO_EX definition from Windows SDK
1277struct file_disposition_info_ex
1278{
1279 DWORD Flags;
1280};
1281
1282#ifndef FILE_DISPOSITION_FLAG_DELETE
1283#define FILE_DISPOSITION_FLAG_DELETE 0x00000001
1284#endif
1285// Available since Windows 10 1709
1286#ifndef FILE_DISPOSITION_FLAG_POSIX_SEMANTICS
1287#define FILE_DISPOSITION_FLAG_POSIX_SEMANTICS 0x00000002
1288#endif
1289// Available since Windows 10 1809
1290#ifndef FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE
1291#define FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE 0x00000010
1292#endif
1293
1294// REPARSE_DATA_BUFFER related definitions are found in ntifs.h, which is part of the
1295// Windows Device Driver Kit. Since that's inconvenient, the definitions are provided
1296// here. See http://msdn.microsoft.com/en-us/library/ms791514.aspx
1297struct reparse_data_buffer
1298{
1299 ULONG ReparseTag;
1300 USHORT ReparseDataLength;
1301 USHORT Reserved;
1302 union
1303 {
1304 /*
1305 * In SymbolicLink and MountPoint reparse points, there are two names.
1306 * SubstituteName is the effective replacement path for the reparse point.
1307 * This is what should be used for path traversal.
1308 * PrintName is intended for presentation to the user and may omit some
1309 * elements of the path or be absent entirely.
1310 *
1311 * Examples of substitute and print names:
1312 * mklink /D ldrive c:\
1313 * SubstituteName: "\??\c:\"
1314 * PrintName: "c:\"
1315 *
1316 * mklink /J ldrive c:\
1317 * SubstituteName: "\??\C:\"
1318 * PrintName: "c:\"
1319 *
1320 * junction ldrive c:\
1321 * SubstituteName: "\??\C:\"
1322 * PrintName: ""
1323 *
1324 * box.com mounted cloud storage
1325 * SubstituteName: "\??\Volume{<UUID>}\"
1326 * PrintName: ""
1327 */
1328 struct
1329 {
1330 USHORT SubstituteNameOffset;
1331 USHORT SubstituteNameLength;
1332 USHORT PrintNameOffset;
1333 USHORT PrintNameLength;
1334 ULONG Flags;
1335 WCHAR PathBuffer[1];
1336 } SymbolicLinkReparseBuffer;
1337 struct
1338 {
1339 USHORT SubstituteNameOffset;
1340 USHORT SubstituteNameLength;
1341 USHORT PrintNameOffset;
1342 USHORT PrintNameLength;
1343 WCHAR PathBuffer[1];
1344 } MountPointReparseBuffer;
1345 struct
1346 {
1347 UCHAR DataBuffer[1];
1348 } GenericReparseBuffer;
1349 };
1350};
1351
1352// Our convenience type for allocating REPARSE_DATA_BUFFER along with sufficient space after it
1353union reparse_data_buffer_with_storage
1354{
1355 reparse_data_buffer rdb;
1356 unsigned char storage[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
1357};
1358
1359// Windows kernel32.dll functions that may or may not be present
1360// must be accessed through pointers
1361
1362typedef BOOL (WINAPI CreateHardLinkW_t)(
1363 /*__in*/ LPCWSTR lpFileName,
1364 /*__in*/ LPCWSTR lpExistingFileName,
1365 /*__reserved*/ LPSECURITY_ATTRIBUTES lpSecurityAttributes);
1366
1367CreateHardLinkW_t* create_hard_link_api = nullptr;
1368
1369typedef BOOLEAN (WINAPI CreateSymbolicLinkW_t)(
1370 /*__in*/ LPCWSTR lpSymlinkFileName,
1371 /*__in*/ LPCWSTR lpTargetFileName,
1372 /*__in*/ DWORD dwFlags);
1373
1374CreateSymbolicLinkW_t* create_symbolic_link_api = nullptr;
1375
1376//! SetFileInformationByHandle signature. Available since Windows Vista.
1377typedef BOOL (WINAPI SetFileInformationByHandle_t)(
1378 /*_In_*/ HANDLE hFile,
1379 /*_In_*/ file_info_by_handle_class FileInformationClass, // the actual type is FILE_INFO_BY_HANDLE_CLASS enum
1380 /*_In_reads_bytes_(dwBufferSize)*/ LPVOID lpFileInformation,
1381 /*_In_*/ DWORD dwBufferSize);
1382
1383SetFileInformationByHandle_t* set_file_information_by_handle_api = nullptr;
1384
1385} // unnamed namespace
1386
1387GetFileInformationByHandleEx_t* get_file_information_by_handle_ex_api = nullptr;
1388
1389NtCreateFile_t* nt_create_file_api = nullptr;
1390NtQueryDirectoryFile_t* nt_query_directory_file_api = nullptr;
1391
1392namespace {
1393
1394//! remove() implementation type
1395enum remove_impl_type
1396{
1397 remove_nt5, //!< Use Windows XP API
1398 remove_disp, //!< Use FILE_DISPOSITION_INFO (Windows Vista and later)
1399 remove_disp_ex_flag_posix_semantics, //!< Use FILE_DISPOSITION_INFO_EX with FILE_DISPOSITION_FLAG_POSIX_SEMANTICS
1400 remove_disp_ex_flag_ignore_readonly //!< Use FILE_DISPOSITION_INFO_EX with FILE_DISPOSITION_FLAG_POSIX_SEMANTICS | FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE
1401};
1402
1403remove_impl_type g_remove_impl_type = remove_nt5;
1404
1405//! Initializes WinAPI function pointers
1406BOOST_FILESYSTEM_INIT_FUNC init_winapi_func_ptrs()
1407{
1408 boost::winapi::HMODULE_ h = boost::winapi::GetModuleHandleW(L"kernel32.dll");
1409 if (BOOST_LIKELY(!!h))
1410 {
1411 GetFileInformationByHandleEx_t* get_file_information_by_handle_ex = (GetFileInformationByHandleEx_t*)boost::winapi::get_proc_address(h, "GetFileInformationByHandleEx");
1412 filesystem::detail::atomic_store_relaxed(get_file_information_by_handle_ex_api, get_file_information_by_handle_ex);
1413 SetFileInformationByHandle_t* set_file_information_by_handle = (SetFileInformationByHandle_t*)boost::winapi::get_proc_address(h, "SetFileInformationByHandle");
1414 filesystem::detail::atomic_store_relaxed(set_file_information_by_handle_api, set_file_information_by_handle);
1415 filesystem::detail::atomic_store_relaxed(create_hard_link_api, (CreateHardLinkW_t*)boost::winapi::get_proc_address(h, "CreateHardLinkW"));
1416 filesystem::detail::atomic_store_relaxed(create_symbolic_link_api, (CreateSymbolicLinkW_t*)boost::winapi::get_proc_address(h, "CreateSymbolicLinkW"));
1417
1418 if (get_file_information_by_handle_ex && set_file_information_by_handle)
1419 {
1420 // Enable the most advanced implementation based on GetFileInformationByHandleEx/SetFileInformationByHandle.
1421 // If certain flags are not supported by the OS, the remove() implementation will downgrade accordingly.
1422 filesystem::detail::atomic_store_relaxed(g_remove_impl_type, remove_disp_ex_flag_ignore_readonly);
1423 }
1424 }
1425
1426 h = boost::winapi::GetModuleHandleW(L"ntdll.dll");
1427 if (BOOST_LIKELY(!!h))
1428 {
1429 filesystem::detail::atomic_store_relaxed(nt_create_file_api, (NtCreateFile_t*)boost::winapi::get_proc_address(h, "NtCreateFile"));
1430 filesystem::detail::atomic_store_relaxed(nt_query_directory_file_api, (NtQueryDirectoryFile_t*)boost::winapi::get_proc_address(h, "NtQueryDirectoryFile"));
1431 }
1432
1433 init_directory_iterator_impl();
1434
1435 return BOOST_FILESYSTEM_INITRETSUCCESS_V;
1436}
1437
1438#if defined(_MSC_VER)
1439
1440#if _MSC_VER >= 1400
1441
1442#pragma section(".CRT$XCL", long, read)
1443__declspec(allocate(".CRT$XCL")) BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN
1444extern const init_func_ptr_t p_init_winapi_func_ptrs = &init_winapi_func_ptrs;
1445
1446#else // _MSC_VER >= 1400
1447
1448#if (_MSC_VER >= 1300) // 1300 == VC++ 7.0
1449#pragma data_seg(push, old_seg)
1450#endif
1451#pragma data_seg(".CRT$XCL")
1452BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN
1453extern const init_func_ptr_t p_init_winapi_func_ptrs = &init_winapi_func_ptrs;
1454#pragma data_seg()
1455#if (_MSC_VER >= 1300) // 1300 == VC++ 7.0
1456#pragma data_seg(pop, old_seg)
1457#endif
1458
1459#endif // _MSC_VER >= 1400
1460
1461#if defined(BOOST_FILESYSTEM_NO_ATTRIBUTE_RETAIN)
1462//! Makes sure the global initializer pointers are referenced and not removed by linker
1463struct globals_retainer
1464{
1465 const init_func_ptr_t* volatile m_p_init_winapi_func_ptrs;
1466
1467 globals_retainer() { m_p_init_winapi_func_ptrs = &p_init_winapi_func_ptrs; }
1468};
1469BOOST_ATTRIBUTE_UNUSED
1470const globals_retainer g_globals_retainer;
1471#endif // defined(BOOST_FILESYSTEM_NO_ATTRIBUTE_RETAIN)
1472
1473#else // defined(_MSC_VER)
1474
1475//! Invokes WinAPI function pointers initialization
1476struct winapi_func_ptrs_initializer
1477{
1478 winapi_func_ptrs_initializer() { init_winapi_func_ptrs(); }
1479};
1480
1481BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_FUNC_PTR_INIT_PRIORITY) BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN
1482const winapi_func_ptrs_initializer winapi_func_ptrs_init;
1483
1484#endif // defined(_MSC_VER)
1485
1486
1487inline std::wstring wgetenv(const wchar_t* name)
1488{
1489 // use a separate buffer since C++03 basic_string is not required to be contiguous
1490 const DWORD size = ::GetEnvironmentVariableW(name, nullptr, 0);
1491 if (size > 0)
1492 {
1493 std::unique_ptr< wchar_t[] > buf(new wchar_t[size]);
1494 if (BOOST_LIKELY(::GetEnvironmentVariableW(name, buf.get(), size) > 0))
1495 return std::wstring(buf.get());
1496 }
1497
1498 return std::wstring();
1499}
1500
1501inline bool not_found_error(int errval) noexcept
1502{
1503 return errval == ERROR_FILE_NOT_FOUND || errval == ERROR_PATH_NOT_FOUND || errval == ERROR_INVALID_NAME // "tools/jam/src/:sys:stat.h", "//foo"
1504 || errval == ERROR_INVALID_DRIVE // USB card reader with no card inserted
1505 || errval == ERROR_NOT_READY // CD/DVD drive with no disc inserted
1506 || errval == ERROR_INVALID_PARAMETER // ":sys:stat.h"
1507 || errval == ERROR_BAD_PATHNAME // "//no-host" on Win64
1508 || errval == ERROR_BAD_NETPATH // "//no-host" on Win32
1509 || errval == ERROR_BAD_NET_NAME; // "//no-host/no-share" on Win10 x64
1510}
1511
1512// these constants come from inspecting some Microsoft sample code
1513inline DWORD to_time_t(FILETIME const& ft, std::time_t& t)
1514{
1515 uint64_t ut = (static_cast< uint64_t >(ft.dwHighDateTime) << 32u) | ft.dwLowDateTime;
1516 if (BOOST_UNLIKELY(ut > static_cast< uint64_t >((std::numeric_limits< int64_t >::max)())))
1517 return ERROR_INVALID_DATA;
1518
1519 // On Windows, time_t is signed, and negative values are possible since FILETIME epoch is earlier than POSIX epoch
1520 int64_t st = static_cast< int64_t >(ut) / 10000000 - 11644473600ll;
1521 if (BOOST_UNLIKELY(st < static_cast< int64_t >((std::numeric_limits< std::time_t >::min)()) ||
1522 st > static_cast< int64_t >((std::numeric_limits< std::time_t >::max)())))
1523 {
1524 return ERROR_INVALID_DATA;
1525 }
1526
1527 t = static_cast< std::time_t >(st);
1528 return 0u;
1529}
1530
1531inline DWORD to_FILETIME(std::time_t t, FILETIME& ft)
1532{
1533 // On Windows, time_t is signed, and negative values are possible since FILETIME epoch is earlier than POSIX epoch
1534 int64_t st = static_cast< int64_t >(t);
1535 if (BOOST_UNLIKELY(st < ((std::numeric_limits< int64_t >::min)() / 10000000 - 11644473600ll) ||
1536 st > ((std::numeric_limits< int64_t >::max)() / 10000000 - 11644473600ll)))
1537 {
1538 return ERROR_INVALID_DATA;
1539 }
1540
1541 st = (st + 11644473600ll) * 10000000;
1542 uint64_t ut = static_cast< uint64_t >(st);
1543 ft.dwLowDateTime = static_cast< DWORD >(ut);
1544 ft.dwHighDateTime = static_cast< DWORD >(ut >> 32u);
1545
1546 return 0u;
1547}
1548
1549} // unnamed namespace
1550
1551//! The flag indicates whether OBJ_DONT_REPARSE flag is not supported by the kernel
1552static bool g_no_obj_dont_reparse = false;
1553
1554//! Creates a file handle for a file relative to a previously opened base directory. The file path must be relative and in preferred format.
1555boost::winapi::NTSTATUS_ nt_create_file_handle_at
1556(
1557 unique_handle& out,
1558 HANDLE basedir_handle,
1559 boost::filesystem::path const& p,
1560 ULONG FileAttributes,
1561 ACCESS_MASK DesiredAccess,
1562 ULONG ShareMode,
1563 ULONG CreateDisposition,
1564 ULONG CreateOptions
1565)
1566{
1567 NtCreateFile_t* nt_create_file = filesystem::detail::atomic_load_relaxed(nt_create_file_api);
1568 if (BOOST_UNLIKELY(!nt_create_file))
1569 return STATUS_NOT_IMPLEMENTED;
1570
1571 unicode_string obj_name = {};
1572 obj_name.Buffer = const_cast< wchar_t* >(p.c_str());
1573 obj_name.Length = obj_name.MaximumLength = static_cast< USHORT >(p.size() * sizeof(wchar_t));
1574
1575 object_attributes obj_attrs = {};
1576 obj_attrs.Length = sizeof(obj_attrs);
1577 obj_attrs.RootDirectory = basedir_handle;
1578 obj_attrs.ObjectName = &obj_name;
1579
1580 obj_attrs.Attributes = OBJ_CASE_INSENSITIVE;
1581 if ((CreateOptions & FILE_OPEN_REPARSE_POINT) != 0u && !filesystem::detail::atomic_load_relaxed(g_no_obj_dont_reparse))
1582 obj_attrs.Attributes |= OBJ_DONT_REPARSE;
1583
1584 io_status_block iosb;
1585 HANDLE out_handle = INVALID_HANDLE_VALUE;
1586 boost::winapi::NTSTATUS_ status = nt_create_file
1587 (
1588 &out_handle,
1589 DesiredAccess,
1590 &obj_attrs,
1591 &iosb,
1592 nullptr, // AllocationSize
1593 FileAttributes,
1594 ShareMode,
1595 CreateDisposition,
1596 CreateOptions,
1597 nullptr, // EaBuffer
1598 0u // EaLength
1599 );
1600
1601 if (BOOST_UNLIKELY(status == STATUS_INVALID_PARAMETER && (obj_attrs.Attributes & OBJ_DONT_REPARSE) != 0u))
1602 {
1603 // OBJ_DONT_REPARSE is supported since Windows 10, retry without it
1604 filesystem::detail::atomic_store_relaxed(g_no_obj_dont_reparse, true);
1605 obj_attrs.Attributes &= ~static_cast< ULONG >(OBJ_DONT_REPARSE);
1606
1607 status = nt_create_file
1608 (
1609 &out_handle,
1610 DesiredAccess,
1611 &obj_attrs,
1612 &iosb,
1613 nullptr, // AllocationSize
1614 FileAttributes,
1615 ShareMode,
1616 CreateDisposition,
1617 CreateOptions,
1618 nullptr, // EaBuffer
1619 0u // EaLength
1620 );
1621 }
1622
1623 out.reset(out_handle);
1624
1625 return status;
1626}
1627
1628ULONG get_reparse_point_tag_ioctl(HANDLE h, path const& p, error_code* ec)
1629{
1630 std::unique_ptr< reparse_data_buffer_with_storage > buf(new (std::nothrow) reparse_data_buffer_with_storage);
1631 if (BOOST_UNLIKELY(!buf.get()))
1632 {
1633 if (!ec)
1634 BOOST_FILESYSTEM_THROW(filesystem_error("Cannot allocate memory to query reparse point", p, make_error_code(system::errc::not_enough_memory)));
1635
1636 *ec = make_error_code(system::errc::not_enough_memory);
1637 return 0u;
1638 }
1639
1640 // Query the reparse data
1641 DWORD dwRetLen = 0u;
1642 BOOL result = ::DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, nullptr, 0, buf.get(), sizeof(*buf), &dwRetLen, nullptr);
1643 if (BOOST_UNLIKELY(!result))
1644 {
1645 DWORD err = ::GetLastError();
1646 if (!ec)
1647 BOOST_FILESYSTEM_THROW(filesystem_error("Failed to query reparse point", p, error_code(err, system_category())));
1648
1649 ec->assign(err, system_category());
1650 return 0u;
1651 }
1652
1653 return buf->rdb.ReparseTag;
1654}
1655
1656namespace {
1657
1658inline std::size_t get_full_path_name(path const& src, std::size_t len, wchar_t* buf, wchar_t** p)
1659{
1660 return static_cast< std::size_t >(::GetFullPathNameW(src.c_str(), static_cast< DWORD >(len), buf, p));
1661}
1662
1663inline fs::file_status process_status_failure(DWORD errval, path const& p, error_code* ec)
1664{
1665 if (ec) // always report errval, even though some
1666 ec->assign(errval, system_category()); // errval values are not status_errors
1667
1668 if (not_found_error(errval))
1669 {
1670 return fs::file_status(fs::file_not_found, fs::no_perms);
1671 }
1672 else if (errval == ERROR_SHARING_VIOLATION)
1673 {
1674 return fs::file_status(fs::type_unknown);
1675 }
1676
1677 if (!ec)
1678 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::status", p, error_code(errval, system_category())));
1679
1680 return fs::file_status(fs::status_error);
1681}
1682
1683inline fs::file_status process_status_failure(path const& p, error_code* ec)
1684{
1685 return process_status_failure(::GetLastError(), p, ec);
1686}
1687
1688} // namespace
1689
1690//! (symlink_)status() by handle implementation
1691fs::file_status status_by_handle(HANDLE h, path const& p, error_code* ec)
1692{
1693 fs::file_type ftype;
1694 DWORD attrs;
1695 ULONG reparse_tag = 0u;
1696 GetFileInformationByHandleEx_t* get_file_information_by_handle_ex = filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api);
1697 if (BOOST_LIKELY(get_file_information_by_handle_ex != nullptr))
1698 {
1699 file_attribute_tag_info info;
1700 BOOL res = get_file_information_by_handle_ex(h, file_attribute_tag_info_class, &info, sizeof(info));
1701 if (BOOST_UNLIKELY(!res))
1702 {
1703 // On FAT/exFAT filesystems requesting FILE_ATTRIBUTE_TAG_INFO returns ERROR_INVALID_PARAMETER.
1704 // Presumably, this is because these filesystems don't support reparse points, so ReparseTag
1705 // cannot be returned. Also check ERROR_NOT_SUPPORTED for good measure. Fall back to the legacy
1706 // code path in this case.
1707 DWORD err = ::GetLastError();
1708 if (err == ERROR_INVALID_PARAMETER || err == ERROR_NOT_SUPPORTED)
1709 goto use_get_file_information_by_handle;
1710
1711 return process_status_failure(err, p, ec);
1712 }
1713
1714 attrs = info.FileAttributes;
1715 reparse_tag = info.ReparseTag;
1716 }
1717 else
1718 {
1719 use_get_file_information_by_handle:
1720 BY_HANDLE_FILE_INFORMATION info;
1721 BOOL res = ::GetFileInformationByHandle(h, &info);
1722 if (BOOST_UNLIKELY(!res))
1723 return process_status_failure(p, ec);
1724
1725 attrs = info.dwFileAttributes;
1726
1727 if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0u)
1728 {
1729 reparse_tag = get_reparse_point_tag_ioctl(h, p, ec);
1730 if (ec)
1731 {
1732 if (BOOST_UNLIKELY(!!ec))
1733 return fs::file_status(fs::status_error);
1734 }
1735 }
1736 }
1737
1738 if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0u)
1739 {
1740 if (reparse_tag == IO_REPARSE_TAG_DEDUP)
1741 ftype = fs::regular_file;
1742 else if (is_reparse_point_tag_a_symlink(reparse_tag))
1743 ftype = fs::symlink_file;
1744 else
1745 ftype = fs::reparse_file;
1746 }
1747 else if ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0u)
1748 {
1749 ftype = fs::directory_file;
1750 }
1751 else
1752 {
1753 ftype = fs::regular_file;
1754 }
1755
1756 return fs::file_status(ftype, make_permissions(p, attrs));
1757}
1758
1759namespace {
1760
1761//! symlink_status() implementation
1762fs::file_status symlink_status_impl(path const& p, error_code* ec)
1763{
1764 // Normally, we only need FILE_READ_ATTRIBUTES access mode. But SMBv1 reports incorrect
1765 // file attributes in GetFileInformationByHandleEx in this case (e.g. it reports FILE_ATTRIBUTE_NORMAL
1766 // for a directory in a SMBv1 share), so we add FILE_READ_EA as a workaround.
1767 // https://github.com/boostorg/filesystem/issues/282
1768 unique_handle h(create_file_handle(
1769 p.c_str(),
1770 FILE_READ_ATTRIBUTES | FILE_READ_EA,
1771 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1772 nullptr, // lpSecurityAttributes
1773 OPEN_EXISTING,
1774 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT));
1775
1776 if (!h)
1777 {
1778 // For some system files and folders like "System Volume Information" CreateFileW fails
1779 // with ERROR_ACCESS_DENIED. GetFileAttributesW succeeds for such files, so try that.
1780 // Though this will only help if the file is not a reparse point (symlink or not).
1781 DWORD err = ::GetLastError();
1782 if (err == ERROR_ACCESS_DENIED)
1783 {
1784 DWORD attrs = ::GetFileAttributesW(p.c_str());
1785 if (attrs != INVALID_FILE_ATTRIBUTES)
1786 {
1787 if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) == 0u)
1788 return fs::file_status((attrs & FILE_ATTRIBUTE_DIRECTORY) ? fs::directory_file : fs::regular_file, make_permissions(p, attrs));
1789 }
1790 else
1791 {
1792 err = ::GetLastError();
1793 }
1794 }
1795
1796 return process_status_failure(err, p, ec);
1797 }
1798
1799 return detail::status_by_handle(h.get(), p, ec);
1800}
1801
1802//! status() implementation
1803fs::file_status status_impl(path const& p, error_code* ec)
1804{
1805 // We should first test if the file is a symlink or a reparse point. Resolving some reparse
1806 // points by opening the file may fail, and status() should return file_status(reparse_file) in this case.
1807 // Which is what symlink_status() returns.
1808 fs::file_status st(detail::symlink_status_impl(p, ec));
1809 if (st.type() == symlink_file)
1810 {
1811 // Resolve the symlink
1812 unique_handle h(create_file_handle(
1813 p.c_str(),
1814 FILE_READ_ATTRIBUTES | FILE_READ_EA, // see the comment in symlink_status_impl re. access mode
1815 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1816 nullptr, // lpSecurityAttributes
1817 OPEN_EXISTING,
1818 FILE_FLAG_BACKUP_SEMANTICS));
1819
1820 if (!h)
1821 return process_status_failure(p, ec);
1822
1823 st = detail::status_by_handle(h.get(), p, ec);
1824 }
1825
1826 return st;
1827}
1828
1829//! remove() implementation for Windows XP and older
1830bool remove_nt5_impl(path const& p, DWORD attrs, error_code* ec)
1831{
1832 const bool is_directory = (attrs & FILE_ATTRIBUTE_DIRECTORY) != 0;
1833 const bool is_read_only = (attrs & FILE_ATTRIBUTE_READONLY) != 0;
1834 if (is_read_only)
1835 {
1836 // RemoveDirectoryW and DeleteFileW do not allow to remove a read-only file, so we have to drop the attribute
1837 DWORD new_attrs = attrs & ~FILE_ATTRIBUTE_READONLY;
1838 BOOL res = ::SetFileAttributesW(p.c_str(), new_attrs);
1839 if (BOOST_UNLIKELY(!res))
1840 {
1841 DWORD err = ::GetLastError();
1842 if (!not_found_error(err))
1843 emit_error(err, p, ec, "boost::filesystem::remove");
1844
1845 return false;
1846 }
1847 }
1848
1849 BOOL res;
1850 if (!is_directory)
1851 {
1852 // DeleteFileW works for file symlinks by removing the symlink, not the target.
1853 res = ::DeleteFileW(p.c_str());
1854 }
1855 else
1856 {
1857 // RemoveDirectoryW works for symlinks and junctions by removing the symlink, not the target,
1858 // even if the target directory is not empty.
1859 // Note that unlike opening the directory with FILE_FLAG_DELETE_ON_CLOSE flag, RemoveDirectoryW
1860 // will fail if the directory is not empty.
1861 res = ::RemoveDirectoryW(p.c_str());
1862 }
1863
1864 if (BOOST_UNLIKELY(!res))
1865 {
1866 DWORD err = ::GetLastError();
1867 if (!not_found_error(err))
1868 {
1869 if (is_read_only)
1870 {
1871 // Try to restore the read-only attribute
1872 ::SetFileAttributesW(p.c_str(), attrs);
1873 }
1874
1875 emit_error(err, p, ec, "boost::filesystem::remove");
1876 }
1877
1878 return false;
1879 }
1880
1881 return true;
1882}
1883
1884//! remove() by handle implementation for Windows Vista and newer
1885DWORD remove_nt6_by_handle(HANDLE handle, remove_impl_type impl)
1886{
1887 GetFileInformationByHandleEx_t* get_file_information_by_handle_ex = filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api);
1888 SetFileInformationByHandle_t* set_file_information_by_handle = filesystem::detail::atomic_load_relaxed(set_file_information_by_handle_api);
1889 DWORD err = 0u;
1890 switch (impl)
1891 {
1892 case remove_disp_ex_flag_ignore_readonly:
1893 {
1894 file_disposition_info_ex info;
1895 info.Flags = FILE_DISPOSITION_FLAG_DELETE | FILE_DISPOSITION_FLAG_POSIX_SEMANTICS | FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE;
1896 BOOL res = set_file_information_by_handle(handle, file_disposition_info_ex_class, &info, sizeof(info));
1897 if (BOOST_LIKELY(!!res))
1898 break;
1899
1900 err = ::GetLastError();
1901 if (BOOST_UNLIKELY(err == ERROR_INVALID_PARAMETER || err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED || err == ERROR_CALL_NOT_IMPLEMENTED))
1902 {
1903 // Downgrade to the older implementation
1904 impl = remove_disp_ex_flag_posix_semantics;
1905 filesystem::detail::atomic_store_relaxed(g_remove_impl_type, impl);
1906 }
1907 else
1908 {
1909 break;
1910 }
1911 }
1912 BOOST_FALLTHROUGH;
1913
1914 case remove_disp_ex_flag_posix_semantics:
1915 {
1916 file_disposition_info_ex info;
1917 info.Flags = FILE_DISPOSITION_FLAG_DELETE | FILE_DISPOSITION_FLAG_POSIX_SEMANTICS;
1918 BOOL res = set_file_information_by_handle(handle, file_disposition_info_ex_class, &info, sizeof(info));
1919 if (BOOST_LIKELY(!!res))
1920 {
1921 err = 0u;
1922 break;
1923 }
1924
1925 err = ::GetLastError();
1926 if (err == ERROR_ACCESS_DENIED)
1927 {
1928 // Check if the file is read-only and reset the attribute
1929 file_basic_info basic_info;
1930 res = get_file_information_by_handle_ex(handle, file_basic_info_class, &basic_info, sizeof(basic_info));
1931 if (BOOST_UNLIKELY(!res || (basic_info.FileAttributes & FILE_ATTRIBUTE_READONLY) == 0))
1932 break; // return ERROR_ACCESS_DENIED
1933
1934 basic_info.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
1935
1936 res = set_file_information_by_handle(handle, file_basic_info_class, &basic_info, sizeof(basic_info));
1937 if (BOOST_UNLIKELY(!res))
1938 {
1939 err = ::GetLastError();
1940 break;
1941 }
1942
1943 // Try to set the flag again
1944 res = set_file_information_by_handle(handle, file_disposition_info_ex_class, &info, sizeof(info));
1945 if (BOOST_LIKELY(!!res))
1946 {
1947 err = 0u;
1948 break;
1949 }
1950
1951 err = ::GetLastError();
1952
1953 // Try to restore the read-only flag
1954 basic_info.FileAttributes |= FILE_ATTRIBUTE_READONLY;
1955 set_file_information_by_handle(handle, file_basic_info_class, &basic_info, sizeof(basic_info));
1956
1957 break;
1958 }
1959 else if (BOOST_UNLIKELY(err == ERROR_INVALID_PARAMETER || err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED || err == ERROR_CALL_NOT_IMPLEMENTED))
1960 {
1961 // Downgrade to the older implementation
1962 impl = remove_disp;
1963 filesystem::detail::atomic_store_relaxed(g_remove_impl_type, impl);
1964 }
1965 else
1966 {
1967 break;
1968 }
1969 }
1970 BOOST_FALLTHROUGH;
1971
1972 default:
1973 {
1974 file_disposition_info info;
1975 info.DeleteFile = true;
1976 BOOL res = set_file_information_by_handle(handle, file_disposition_info_class, &info, sizeof(info));
1977 if (BOOST_LIKELY(!!res))
1978 {
1979 err = 0u;
1980 break;
1981 }
1982
1983 err = ::GetLastError();
1984 if (err == ERROR_ACCESS_DENIED)
1985 {
1986 // Check if the file is read-only and reset the attribute
1987 file_basic_info basic_info;
1988 res = get_file_information_by_handle_ex(handle, file_basic_info_class, &basic_info, sizeof(basic_info));
1989 if (BOOST_UNLIKELY(!res || (basic_info.FileAttributes & FILE_ATTRIBUTE_READONLY) == 0))
1990 break; // return ERROR_ACCESS_DENIED
1991
1992 basic_info.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
1993
1994 res = set_file_information_by_handle(handle, file_basic_info_class, &basic_info, sizeof(basic_info));
1995 if (BOOST_UNLIKELY(!res))
1996 {
1997 err = ::GetLastError();
1998 break;
1999 }
2000
2001 // Try to set the flag again
2002 res = set_file_information_by_handle(handle, file_disposition_info_class, &info, sizeof(info));
2003 if (BOOST_LIKELY(!!res))
2004 {
2005 err = 0u;
2006 break;
2007 }
2008
2009 err = ::GetLastError();
2010
2011 // Try to restore the read-only flag
2012 basic_info.FileAttributes |= FILE_ATTRIBUTE_READONLY;
2013 set_file_information_by_handle(handle, file_basic_info_class, &basic_info, sizeof(basic_info));
2014 }
2015
2016 break;
2017 }
2018 }
2019
2020 return err;
2021}
2022
2023//! remove() implementation for Windows Vista and newer
2024inline bool remove_nt6_impl(path const& p, remove_impl_type impl, error_code* ec)
2025{
2026 unique_handle h(create_file_handle(
2027 p,
2028 DELETE | FILE_READ_ATTRIBUTES | FILE_READ_EA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA,
2029 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2030 nullptr,
2031 OPEN_EXISTING,
2032 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT));
2033 DWORD err = 0u;
2034 if (BOOST_UNLIKELY(!h))
2035 {
2036 err = ::GetLastError();
2037
2038 return_error:
2039 if (!not_found_error(err))
2040 emit_error(err, p, ec, "boost::filesystem::remove");
2041
2042 return false;
2043 }
2044
2045 err = fs::detail::remove_nt6_by_handle(h.get(), impl);
2046 if (BOOST_UNLIKELY(err != 0u))
2047 goto return_error;
2048
2049 return true;
2050}
2051
2052//! remove() implementation
2053inline bool remove_impl(path const& p, error_code* ec)
2054{
2055 remove_impl_type impl = fs::detail::atomic_load_relaxed(g_remove_impl_type);
2056 if (BOOST_LIKELY(impl != remove_nt5))
2057 {
2058 return fs::detail::remove_nt6_impl(p, impl, ec);
2059 }
2060 else
2061 {
2062 const DWORD attrs = ::GetFileAttributesW(p.c_str());
2063 if (BOOST_UNLIKELY(attrs == INVALID_FILE_ATTRIBUTES))
2064 {
2065 DWORD err = ::GetLastError();
2066 if (!not_found_error(err))
2067 emit_error(err, p, ec, "boost::filesystem::remove");
2068
2069 return false;
2070 }
2071
2072 return fs::detail::remove_nt5_impl(p, attrs, ec);
2073 }
2074}
2075
2076//! remove_all() by handle implementation for Windows Vista and newer
2077uintmax_t remove_all_nt6_by_handle(HANDLE h, path const& p, error_code* ec)
2078{
2079 error_code local_ec;
2080 fs::file_status st(fs::detail::status_by_handle(h, p, &local_ec));
2081 if (BOOST_UNLIKELY(st.type() == fs::status_error))
2082 {
2083 if (!ec)
2084 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove_all", p, local_ec));
2085
2086 *ec = local_ec;
2087 return static_cast< uintmax_t >(-1);
2088 }
2089
2090 uintmax_t count = 0u;
2091 if (st.type() == fs::directory_file)
2092 {
2093 local_ec.clear();
2094
2095 fs::directory_iterator itr;
2096 directory_iterator_params params;
2097 params.dir_handle = h;
2098 params.close_handle = false; // the caller will close the handle
2099 fs::detail::directory_iterator_construct(itr, p, directory_options::_detail_no_follow, &params, &local_ec);
2100 if (BOOST_UNLIKELY(!!local_ec))
2101 {
2102 if (!ec)
2103 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove_all", p, local_ec));
2104
2105 *ec = local_ec;
2106 return static_cast< uintmax_t >(-1);
2107 }
2108
2109 NtCreateFile_t* nt_create_file = filesystem::detail::atomic_load_relaxed(nt_create_file_api);
2110 const fs::directory_iterator end_dit;
2111 while (itr != end_dit)
2112 {
2113 fs::path nested_path(itr->path());
2114 unique_handle hh;
2115 if (BOOST_LIKELY(nt_create_file != nullptr))
2116 {
2117 // Note: WinAPI methods like CreateFileW implicitly request SYNCHRONIZE access but NtCreateFile doesn't.
2118 // Without SYNCHRONIZE access querying file attributes via GetFileInformationByHandleEx fails with ERROR_ACCESS_DENIED.
2119 boost::winapi::NTSTATUS_ status = nt_create_file_handle_at
2120 (
2121 hh,
2122 h,
2123 path_algorithms::filename_v4(nested_path),
2124 0u, // FileAttributes
2125 FILE_LIST_DIRECTORY | DELETE | FILE_READ_ATTRIBUTES | FILE_READ_EA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | SYNCHRONIZE,
2126 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2127 FILE_OPEN,
2128 FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT
2129 );
2130
2131 if (!NT_SUCCESS(status))
2132 {
2133 if (not_found_ntstatus(status))
2134 goto next_entry;
2135
2136 DWORD err = translate_ntstatus(status);
2137 emit_error(err, nested_path, ec, "boost::filesystem::remove_all");
2138 return static_cast< uintmax_t >(-1);
2139 }
2140 }
2141 else
2142 {
2143 hh = create_file_handle(
2144 nested_path,
2145 FILE_LIST_DIRECTORY | DELETE | FILE_READ_ATTRIBUTES | FILE_READ_EA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | SYNCHRONIZE,
2146 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2147 nullptr,
2148 OPEN_EXISTING,
2149 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT);
2150
2151 if (BOOST_UNLIKELY(!hh))
2152 {
2153 DWORD err = ::GetLastError();
2154 if (not_found_error(err))
2155 goto next_entry;
2156
2157 emit_error(err, nested_path, ec, "boost::filesystem::remove_all");
2158 return static_cast< uintmax_t >(-1);
2159 }
2160 }
2161
2162 count += fs::detail::remove_all_nt6_by_handle(hh.get(), nested_path, ec);
2163 if (ec && *ec)
2164 return static_cast< uintmax_t >(-1);
2165
2166 next_entry:
2167 fs::detail::directory_iterator_increment(itr, ec);
2168 if (ec && *ec)
2169 return static_cast< uintmax_t >(-1);
2170 }
2171 }
2172
2173 DWORD err = fs::detail::remove_nt6_by_handle(h, fs::detail::atomic_load_relaxed(g_remove_impl_type));
2174 if (BOOST_UNLIKELY(err != 0u))
2175 {
2176 emit_error(err, p, ec, "boost::filesystem::remove_all");
2177 return static_cast< uintmax_t >(-1);
2178 }
2179
2180 ++count;
2181 return count;
2182}
2183
2184//! remove_all() implementation for Windows XP and older
2185uintmax_t remove_all_nt5_impl(path const& p, error_code* ec)
2186{
2187 error_code dit_create_ec;
2188 for (unsigned int attempt = 0u; attempt < remove_all_directory_replaced_retry_count; ++attempt)
2189 {
2190 const DWORD attrs = ::GetFileAttributesW(p.c_str());
2191 if (BOOST_UNLIKELY(attrs == INVALID_FILE_ATTRIBUTES))
2192 {
2193 DWORD err = ::GetLastError();
2194 if (not_found_error(err))
2195 return 0u;
2196
2197 emit_error(err, p, ec, "boost::filesystem::remove_all");
2198 return static_cast< uintmax_t >(-1);
2199 }
2200
2201 // Recurse into directories, but not into junctions or directory symlinks
2202 const bool recurse = (attrs & FILE_ATTRIBUTE_DIRECTORY) != 0 && (attrs & FILE_ATTRIBUTE_REPARSE_POINT) == 0;
2203 uintmax_t count = 0u;
2204 if (recurse)
2205 {
2206 fs::directory_iterator itr;
2207 fs::detail::directory_iterator_construct(itr, p, directory_options::_detail_no_follow, nullptr, &dit_create_ec);
2208 if (BOOST_UNLIKELY(!!dit_create_ec))
2209 {
2210 if (dit_create_ec == make_error_condition(system::errc::not_a_directory) ||
2211 dit_create_ec == make_error_condition(system::errc::too_many_symbolic_link_levels))
2212 {
2213 continue;
2214 }
2215
2216 if (!ec)
2217 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove_all", p, dit_create_ec));
2218
2219 *ec = dit_create_ec;
2220 return static_cast< uintmax_t >(-1);
2221 }
2222
2223 const fs::directory_iterator end_dit;
2224 while (itr != end_dit)
2225 {
2226 count += fs::detail::remove_all_nt5_impl(itr->path(), ec);
2227 if (ec && *ec)
2228 return static_cast< uintmax_t >(-1);
2229
2230 fs::detail::directory_iterator_increment(itr, ec);
2231 if (ec && *ec)
2232 return static_cast< uintmax_t >(-1);
2233 }
2234 }
2235
2236 bool removed = fs::detail::remove_nt5_impl(p, attrs, ec);
2237 if (ec && *ec)
2238 return static_cast< uintmax_t >(-1);
2239
2240 count += removed;
2241 return count;
2242 }
2243
2244 if (!ec)
2245 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove_all: path cannot be opened as a directory", p, dit_create_ec));
2246
2247 *ec = dit_create_ec;
2248 return static_cast< uintmax_t >(-1);
2249}
2250
2251//! remove_all() implementation
2252inline uintmax_t remove_all_impl(path const& p, error_code* ec)
2253{
2254 remove_impl_type impl = fs::detail::atomic_load_relaxed(g_remove_impl_type);
2255 if (BOOST_LIKELY(impl != remove_nt5))
2256 {
2257 unique_handle h(create_file_handle(
2258 p,
2259 FILE_LIST_DIRECTORY | DELETE | FILE_READ_ATTRIBUTES | FILE_READ_EA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | SYNCHRONIZE,
2260 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2261 nullptr,
2262 OPEN_EXISTING,
2263 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT));
2264
2265 if (BOOST_UNLIKELY(!h))
2266 {
2267 DWORD err = ::GetLastError();
2268 if (not_found_error(err))
2269 return 0u;
2270
2271 emit_error(err, p, ec, "boost::filesystem::remove_all");
2272 return static_cast< uintmax_t >(-1);
2273 }
2274
2275 return fs::detail::remove_all_nt6_by_handle(h.get(), p, ec);
2276 }
2277
2278 return fs::detail::remove_all_nt5_impl(p, ec);
2279}
2280
2281inline BOOL resize_file_impl(const wchar_t* p, uintmax_t size)
2282{
2283 unique_handle h(CreateFileW(p, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
2284 LARGE_INTEGER sz;
2285 sz.QuadPart = size;
2286 return !!h && ::SetFilePointerEx(h.get(), sz, 0, FILE_BEGIN) && ::SetEndOfFile(h.get());
2287}
2288
2289//! Converts NT path to a Win32 path
2290inline path convert_nt_path_to_win32_path(const wchar_t* nt_path, std::size_t size)
2291{
2292 // https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html
2293 // https://stackoverflow.com/questions/23041983/path-prefixes-and
2294 //
2295 // NT paths can be used to identify practically any named objects, devices, files, local and remote shares, etc.
2296 // The path starts with a leading backslash and consists of one or more path elements separated with backslashes.
2297 // The set of characters allowed in NT path elements is significantly larger than that of Win32 paths - basically,
2298 // any character except the backslash is allowed. Path elements are case-insensitive.
2299 //
2300 // NT paths that start with the "\??\" prefix are used to indicate the current user's session namespace. The prefix
2301 // indicates to the NT object manager to lookup the object relative to "\Sessions\0\DosDevices\[Logon Authentication ID]".
2302 //
2303 // There is also a special "\Global??\" prefix that refers to the system logon. User's session directory shadows
2304 // the system logon directory, so that when the referenced object is not found in the user's namespace,
2305 // system logon is looked up instead.
2306 //
2307 // There is a symlink "Global" in the user's session namespace that refers to the global namespace, so "\??\Global"
2308 // effectively resolves to "\Global??". This allows Win32 applications to directly refer to the system objects,
2309 // even if shadowed by the current user's logon object.
2310 //
2311 // NT paths can be used to reference not only local filesystems, but also devices and remote shares identifiable via
2312 // UNC paths. For this, there is a special "UNC" device (which is a symlink to "\Device\Mup") in the system logon
2313 // namespace, so "\??\UNC\host\share" (or "\??\Global\UNC\host\share", or "\Global??\UNC\host\share") is equivalent
2314 // to "\\host\share".
2315 //
2316 // NT paths are not universally accepted by Win32 applications and APIs. For example, Far supports paths starting
2317 // with "\??\" and "\??\Global\" but not with "\Global??\". As of Win10 21H1, File Explorer, cmd.exe and PowerShell
2318 // don't support any of these. Given this, and that NT paths have a different set of allowed characters from Win32 paths,
2319 // we should normally avoid exposing NT paths to users that expect Win32 paths.
2320 //
2321 // In Boost.Filesystem we only deal with NT paths that come from reparse points, such as symlinks and mount points,
2322 // including directory junctions. It was observed that reparse points created by junction.exe and mklink use the "\??\"
2323 // prefix for directory junctions and absolute symlink and unqualified relative path for relative symlinks.
2324 // Absolute paths are using drive letters for mounted drives (e.g. "\??\C:\directory"), although it is possible
2325 // to create a junction to an directory using a different way of identifying the filesystem (e.g.
2326 // "\??\Volume{00000000-0000-0000-0000-000000000000}\directory").
2327 // mklink does not support creating junctions pointing to a UNC path. junction.exe does create a junction that
2328 // uses a seemingly invalid syntax like "\??\\\host\share", i.e. it basically does not expect an UNC path. It is not known
2329 // if reparse points that refer to a UNC path are considered valid.
2330 // There are reparse points created as mount points for local and remote filsystems (for example, a cloud storage mounted
2331 // in the local filesystem). Such mount points have the form of "\??\Volume{00000000-0000-0000-0000-000000000000}\",
2332 // "\??\Harddisk0Partition1\" or "\??\HarddiskVolume1\".
2333 // Reparse points that refer directly to a global namespace (through "\??\Global\" or "\Global??\" prefixes) or
2334 // devices (e.g. "\Device\HarddiskVolume1") have not been observed so far.
2335
2336 path win32_path;
2337 std::size_t pos = 0u;
2338 bool global_namespace = false;
2339
2340 // Check for the "\??\" prefix
2341 if (size >= 4u &&
2342 nt_path[0] == path::preferred_separator &&
2343 nt_path[1] == questionmark &&
2344 nt_path[2] == questionmark &&
2345 nt_path[3] == path::preferred_separator)
2346 {
2347 pos = 4u;
2348
2349 // Check "Global"
2350 if ((size - pos) >= 6u &&
2351 (nt_path[pos] == L'G' || nt_path[pos] == L'g') &&
2352 (nt_path[pos + 1] == L'l' || nt_path[pos + 1] == L'L') &&
2353 (nt_path[pos + 2] == L'o' || nt_path[pos + 2] == L'O') &&
2354 (nt_path[pos + 3] == L'b' || nt_path[pos + 3] == L'B') &&
2355 (nt_path[pos + 4] == L'a' || nt_path[pos + 4] == L'A') &&
2356 (nt_path[pos + 5] == L'l' || nt_path[pos + 5] == L'L'))
2357 {
2358 if ((size - pos) == 6u)
2359 {
2360 pos += 6u;
2361 global_namespace = true;
2362 }
2363 else if (detail::is_directory_separator(nt_path[pos + 6u]))
2364 {
2365 pos += 7u;
2366 global_namespace = true;
2367 }
2368 }
2369 }
2370 // Check for the "\Global??\" prefix
2371 else if (size >= 10u &&
2372 nt_path[0] == path::preferred_separator &&
2373 (nt_path[1] == L'G' || nt_path[1] == L'g') &&
2374 (nt_path[2] == L'l' || nt_path[2] == L'L') &&
2375 (nt_path[3] == L'o' || nt_path[3] == L'O') &&
2376 (nt_path[4] == L'b' || nt_path[4] == L'B') &&
2377 (nt_path[5] == L'a' || nt_path[5] == L'A') &&
2378 (nt_path[6] == L'l' || nt_path[6] == L'L') &&
2379 nt_path[7] == questionmark &&
2380 nt_path[8] == questionmark &&
2381 nt_path[9] == path::preferred_separator)
2382 {
2383 pos = 10u;
2384 global_namespace = true;
2385 }
2386
2387 if (pos > 0u)
2388 {
2389 if ((size - pos) >= 2u &&
2390 (
2391 // Check if the following is a drive letter
2392 (
2393 detail::is_letter(nt_path[pos]) && nt_path[pos + 1u] == colon &&
2394 ((size - pos) == 2u || detail::is_directory_separator(nt_path[pos + 2u]))
2395 ) ||
2396 // Check for an "incorrect" syntax for UNC path junction points
2397 (
2398 detail::is_directory_separator(nt_path[pos]) && detail::is_directory_separator(nt_path[pos + 1u]) &&
2399 ((size - pos) == 2u || !detail::is_directory_separator(nt_path[pos + 2u]))
2400 )
2401 ))
2402 {
2403 // Strip the NT path prefix
2404 goto done;
2405 }
2406
2407 static const wchar_t win32_path_prefix[4u] = { path::preferred_separator, path::preferred_separator, questionmark, path::preferred_separator };
2408
2409 // Check for a UNC path
2410 if ((size - pos) >= 4u &&
2411 (nt_path[pos] == L'U' || nt_path[pos] == L'u') &&
2412 (nt_path[pos + 1] == L'N' || nt_path[pos + 1] == L'n') &&
2413 (nt_path[pos + 2] == L'C' || nt_path[pos + 2] == L'c') &&
2414 nt_path[pos + 3] == path::preferred_separator)
2415 {
2416 win32_path.assign(win32_path_prefix, win32_path_prefix + 2);
2417 pos += 4u;
2418 goto done;
2419 }
2420
2421 // This is some other NT path, possibly a volume mount point. Replace the NT prefix with a Win32 filesystem prefix "\\?\".
2422 win32_path.assign(win32_path_prefix, win32_path_prefix + 4);
2423 if (global_namespace)
2424 {
2425 static const wchar_t win32_path_global_prefix[7u] = { L'G', L'l', L'o', L'b', L'a', L'l', path::preferred_separator };
2426 win32_path.concat(win32_path_global_prefix, win32_path_global_prefix + 7);
2427 }
2428 }
2429
2430done:
2431 win32_path.concat(nt_path + pos, nt_path + size);
2432 return win32_path;
2433}
2434
2435#endif // defined(BOOST_POSIX_API)
2436
2437} // unnamed namespace
2438} // namespace detail
2439
2440//--------------------------------------------------------------------------------------//
2441// //
2442// operations functions declared in operations.hpp //
2443// //
2444//--------------------------------------------------------------------------------------//
2445
2446namespace detail {
2447
2448BOOST_FILESYSTEM_DECL bool possible_large_file_size_support()
2449{
2450#ifdef BOOST_POSIX_API
2451 typedef struct stat struct_stat;
2452 return sizeof(struct_stat().st_size) > 4;
2453#else
2454 return true;
2455#endif
2456}
2457
2458BOOST_FILESYSTEM_DECL
2459path absolute_v3(path const& p, path const& base, system::error_code* ec)
2460{
2461 if (ec)
2462 ec->clear();
2463
2464 if (p.is_absolute())
2465 return p;
2466
2467 // recursively calling absolute is sub-optimal, but is sure and simple
2468 path abs_base = base;
2469 if (!base.is_absolute())
2470 {
2471 path cur_path = detail::current_path(ec);
2472 if (ec && *ec)
2473 {
2474 return_empty_path:
2475 return path();
2476 }
2477
2478 if (BOOST_UNLIKELY(!cur_path.is_absolute()))
2479 {
2480 system::error_code local_ec = system::errc::make_error_code(e: system::errc::invalid_argument);
2481 if (!ec)
2482 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::absolute", p, base, local_ec));
2483
2484 *ec = local_ec;
2485 goto return_empty_path;
2486 }
2487
2488 abs_base = detail::absolute_v3(p: base, base: cur_path, ec);
2489 if (ec && *ec)
2490 goto return_empty_path;
2491 }
2492
2493 if (p.empty())
2494 return abs_base;
2495
2496 path res;
2497 if (p.has_root_name())
2498 res = p.root_name();
2499 else
2500 res = abs_base.root_name();
2501
2502 if (p.has_root_directory())
2503 {
2504 res.concat(p: p.root_directory());
2505 }
2506 else
2507 {
2508 res.concat(p: abs_base.root_directory());
2509 path_algorithms::append_v4(left&: res, right: abs_base.relative_path());
2510 }
2511
2512 path p_relative_path(p.relative_path());
2513 if (!p_relative_path.empty())
2514 path_algorithms::append_v4(left&: res, right: p_relative_path);
2515
2516 return res;
2517}
2518
2519BOOST_FILESYSTEM_DECL
2520path absolute_v4(path const& p, path const& base, system::error_code* ec)
2521{
2522 if (ec)
2523 ec->clear();
2524
2525 if (p.is_absolute())
2526 return p;
2527
2528 path abs_base = base;
2529 if (!base.is_absolute())
2530 {
2531 path cur_path = detail::current_path(ec);
2532 if (ec && *ec)
2533 {
2534 return_empty_path:
2535 return path();
2536 }
2537
2538 if (BOOST_UNLIKELY(!cur_path.is_absolute()))
2539 {
2540 system::error_code local_ec = system::errc::make_error_code(e: system::errc::invalid_argument);
2541 if (!ec)
2542 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::absolute", p, base, local_ec));
2543
2544 *ec = local_ec;
2545 goto return_empty_path;
2546 }
2547
2548 abs_base = detail::absolute_v4(p: base, base: cur_path, ec);
2549 if (ec && *ec)
2550 goto return_empty_path;
2551 }
2552
2553 path res;
2554 if (p.has_root_name())
2555 res = p.root_name();
2556 else
2557 res = abs_base.root_name();
2558
2559 if (p.has_root_directory())
2560 {
2561 res.concat(p: p.root_directory());
2562 }
2563 else
2564 {
2565 res.concat(p: abs_base.root_directory());
2566 path_algorithms::append_v4(left&: res, right: abs_base.relative_path());
2567 }
2568
2569 path_algorithms::append_v4(left&: res, right: p.relative_path());
2570
2571 return res;
2572}
2573
2574BOOST_FILESYSTEM_DECL
2575path canonical_v3(path const& p, path const& base, system::error_code* ec)
2576{
2577 if (ec)
2578 ec->clear();
2579
2580 path source(p);
2581 if (!p.is_absolute())
2582 {
2583 source = detail::absolute_v3(p, base, ec);
2584 if (ec && *ec)
2585 {
2586 return_empty_path:
2587 return path();
2588 }
2589 }
2590
2591 system::error_code local_ec;
2592 file_status st(detail::status_impl(p: source, ec: &local_ec));
2593
2594 if (st.type() == fs::file_not_found)
2595 {
2596 local_ec = system::errc::make_error_code(e: system::errc::no_such_file_or_directory);
2597 goto fail_local_ec;
2598 }
2599 else if (local_ec)
2600 {
2601 fail_local_ec:
2602 if (!ec)
2603 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::canonical", source, local_ec));
2604
2605 *ec = local_ec;
2606 goto return_empty_path;
2607 }
2608
2609 path root(source.root_path());
2610 path const& dot_p = dot_path();
2611 path const& dot_dot_p = dot_dot_path();
2612 unsigned int symlinks_allowed = symloop_max;
2613 path result;
2614 while (true)
2615 {
2616 for (path::iterator itr(source.begin()), end(source.end()); itr != end; path_algorithms::increment_v4(it&: itr))
2617 {
2618 if (path_algorithms::compare_v4(left: *itr, right: dot_p) == 0)
2619 continue;
2620 if (path_algorithms::compare_v4(left: *itr, right: dot_dot_p) == 0)
2621 {
2622 if (path_algorithms::compare_v4(left: result, right: root) != 0)
2623 result.remove_filename_and_trailing_separators();
2624 continue;
2625 }
2626
2627 if (itr->size() == 1u && detail::is_directory_separator(c: itr->native()[0]))
2628 {
2629 // Convert generic separator returned by the iterator for the root directory to
2630 // the preferred separator. This is important on Windows, as in some cases,
2631 // like paths for network shares and cloud storage mount points GetFileAttributesW
2632 // will return "file not found" if the path contains forward slashes.
2633 result += path::preferred_separator;
2634 // We don't need to check for a symlink after adding a separator.
2635 continue;
2636 }
2637
2638 path_algorithms::append_v4(left&: result, right: *itr);
2639
2640 // If we don't have an absolute path yet then don't check symlink status.
2641 // This avoids checking "C:" which is "the current directory on drive C"
2642 // and hence not what we want to check/resolve here.
2643 if (!result.is_absolute())
2644 continue;
2645
2646 st = detail::symlink_status_impl(p: result, ec);
2647 if (ec && *ec)
2648 goto return_empty_path;
2649
2650 if (is_symlink(f: st))
2651 {
2652 if (symlinks_allowed == 0)
2653 {
2654 local_ec = system::errc::make_error_code(e: system::errc::too_many_symbolic_link_levels);
2655 goto fail_local_ec;
2656 }
2657
2658 --symlinks_allowed;
2659
2660 path link(detail::read_symlink(p: result, ec));
2661 if (ec && *ec)
2662 goto return_empty_path;
2663 result.remove_filename_and_trailing_separators();
2664
2665 if (link.is_absolute())
2666 {
2667 for (path_algorithms::increment_v4(it&: itr); itr != end; path_algorithms::increment_v4(it&: itr))
2668 {
2669 if (path_algorithms::compare_v4(left: *itr, right: dot_p) != 0)
2670 path_algorithms::append_v4(left&: link, right: *itr);
2671 }
2672 source = link;
2673 root = source.root_path();
2674 }
2675 else // link is relative
2676 {
2677 link.remove_trailing_separator();
2678 if (path_algorithms::compare_v4(left: link, right: dot_p) == 0)
2679 continue;
2680
2681 path new_source(result);
2682 path_algorithms::append_v4(left&: new_source, right: link);
2683 for (path_algorithms::increment_v4(it&: itr); itr != end; path_algorithms::increment_v4(it&: itr))
2684 {
2685 if (path_algorithms::compare_v4(left: *itr, right: dot_p) != 0)
2686 path_algorithms::append_v4(left&: new_source, right: *itr);
2687 }
2688 source = new_source;
2689 }
2690
2691 // symlink causes scan to be restarted
2692 goto restart_scan;
2693 }
2694 }
2695
2696 break;
2697
2698 restart_scan:
2699 result.clear();
2700 }
2701
2702 BOOST_ASSERT_MSG(result.is_absolute(), "canonical() implementation error; please report");
2703 return result;
2704}
2705
2706BOOST_FILESYSTEM_DECL
2707path canonical_v4(path const& p, path const& base, system::error_code* ec)
2708{
2709 if (ec)
2710 ec->clear();
2711
2712 path source(p);
2713 if (!p.is_absolute())
2714 {
2715 source = detail::absolute_v4(p, base, ec);
2716 if (ec && *ec)
2717 {
2718 return_empty_path:
2719 return path();
2720 }
2721 }
2722
2723 system::error_code local_ec;
2724 file_status st(detail::status_impl(p: source, ec: &local_ec));
2725
2726 if (st.type() == fs::file_not_found)
2727 {
2728 local_ec = system::errc::make_error_code(e: system::errc::no_such_file_or_directory);
2729 goto fail_local_ec;
2730 }
2731 else if (local_ec)
2732 {
2733 fail_local_ec:
2734 if (!ec)
2735 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::canonical", source, local_ec));
2736
2737 *ec = local_ec;
2738 goto return_empty_path;
2739 }
2740
2741 path root(source.root_path());
2742 path const& dot_p = dot_path();
2743 path const& dot_dot_p = dot_dot_path();
2744 unsigned int symlinks_allowed = symloop_max;
2745 path result;
2746 while (true)
2747 {
2748 for (path::iterator itr(source.begin()), end(source.end()); itr != end; path_algorithms::increment_v4(it&: itr))
2749 {
2750 if (path_algorithms::compare_v4(left: *itr, right: dot_p) == 0)
2751 continue;
2752 if (path_algorithms::compare_v4(left: *itr, right: dot_dot_p) == 0)
2753 {
2754 if (path_algorithms::compare_v4(left: result, right: root) != 0)
2755 result.remove_filename_and_trailing_separators();
2756 continue;
2757 }
2758
2759 if (itr->size() == 1u && detail::is_directory_separator(c: itr->native()[0]))
2760 {
2761 // Convert generic separator returned by the iterator for the root directory to
2762 // the preferred separator. This is important on Windows, as in some cases,
2763 // like paths for network shares and cloud storage mount points GetFileAttributesW
2764 // will return "file not found" if the path contains forward slashes.
2765 result += path::preferred_separator;
2766 // We don't need to check for a symlink after adding a separator.
2767 continue;
2768 }
2769
2770 path_algorithms::append_v4(left&: result, right: *itr);
2771
2772 // If we don't have an absolute path yet then don't check symlink status.
2773 // This avoids checking "C:" which is "the current directory on drive C"
2774 // and hence not what we want to check/resolve here.
2775 if (!result.is_absolute())
2776 continue;
2777
2778 st = detail::symlink_status_impl(p: result, ec);
2779 if (ec && *ec)
2780 goto return_empty_path;
2781
2782 if (is_symlink(f: st))
2783 {
2784 if (symlinks_allowed == 0)
2785 {
2786 local_ec = system::errc::make_error_code(e: system::errc::too_many_symbolic_link_levels);
2787 goto fail_local_ec;
2788 }
2789
2790 --symlinks_allowed;
2791
2792 path link(detail::read_symlink(p: result, ec));
2793 if (ec && *ec)
2794 goto return_empty_path;
2795 result.remove_filename_and_trailing_separators();
2796
2797 if (link.is_absolute())
2798 {
2799 for (path_algorithms::increment_v4(it&: itr); itr != end; path_algorithms::increment_v4(it&: itr))
2800 {
2801 if (path_algorithms::compare_v4(left: *itr, right: dot_p) != 0)
2802 path_algorithms::append_v4(left&: link, right: *itr);
2803 }
2804 source = link;
2805 root = source.root_path();
2806 }
2807 else // link is relative
2808 {
2809 link.remove_trailing_separator();
2810 if (path_algorithms::compare_v4(left: link, right: dot_p) == 0)
2811 continue;
2812
2813 path new_source(result);
2814 path_algorithms::append_v4(left&: new_source, right: link);
2815 for (path_algorithms::increment_v4(it&: itr); itr != end; path_algorithms::increment_v4(it&: itr))
2816 {
2817 if (path_algorithms::compare_v4(left: *itr, right: dot_p) != 0)
2818 path_algorithms::append_v4(left&: new_source, right: *itr);
2819 }
2820 source = new_source;
2821 }
2822
2823 // symlink causes scan to be restarted
2824 goto restart_scan;
2825 }
2826 }
2827
2828 break;
2829
2830 restart_scan:
2831 result.clear();
2832 }
2833
2834 BOOST_ASSERT_MSG(result.is_absolute(), "canonical() implementation error; please report");
2835 return result;
2836}
2837
2838BOOST_FILESYSTEM_DECL
2839void copy(path const& from, path const& to, copy_options options, system::error_code* ec)
2840{
2841 BOOST_ASSERT((((options & copy_options::overwrite_existing) != copy_options::none) +
2842 ((options & copy_options::skip_existing) != copy_options::none) +
2843 ((options & copy_options::update_existing) != copy_options::none)) <= 1);
2844
2845 BOOST_ASSERT((((options & copy_options::copy_symlinks) != copy_options::none) +
2846 ((options & copy_options::skip_symlinks) != copy_options::none)) <= 1);
2847
2848 BOOST_ASSERT((((options & copy_options::directories_only) != copy_options::none) +
2849 ((options & copy_options::create_symlinks) != copy_options::none) +
2850 ((options & copy_options::create_hard_links) != copy_options::none)) <= 1);
2851
2852 if (ec)
2853 ec->clear();
2854
2855 file_status from_stat;
2856 if ((options & (copy_options::copy_symlinks | copy_options::skip_symlinks | copy_options::create_symlinks)) != copy_options::none)
2857 {
2858 from_stat = detail::symlink_status_impl(p: from, ec);
2859 }
2860 else
2861 {
2862 from_stat = detail::status_impl(p: from, ec);
2863 }
2864
2865 if (ec && *ec)
2866 return;
2867
2868 if (!exists(f: from_stat))
2869 {
2870 emit_error(BOOST_ERROR_FILE_NOT_FOUND, p1: from, p2: to, ec, message: "boost::filesystem::copy");
2871 return;
2872 }
2873
2874 if (is_symlink(f: from_stat))
2875 {
2876 if ((options & copy_options::skip_symlinks) != copy_options::none)
2877 return;
2878
2879 if ((options & copy_options::copy_symlinks) == copy_options::none)
2880 goto fail;
2881
2882 detail::copy_symlink(existing_symlink: from, new_symlink: to, ec);
2883 }
2884 else if (is_regular_file(f: from_stat))
2885 {
2886 if ((options & copy_options::directories_only) != copy_options::none)
2887 return;
2888
2889 if ((options & copy_options::create_symlinks) != copy_options::none)
2890 {
2891 const path* pfrom = &from;
2892 path relative_from;
2893 if (!from.is_absolute())
2894 {
2895 // Try to generate a relative path from the target location to the original file
2896 path cur_dir = detail::current_path(ec);
2897 if (ec && *ec)
2898 return;
2899 path abs_from = detail::absolute_v4(p: from.parent_path(), base: cur_dir, ec);
2900 if (ec && *ec)
2901 return;
2902 path abs_to = to.parent_path();
2903 if (!abs_to.is_absolute())
2904 {
2905 abs_to = detail::absolute_v4(p: abs_to, base: cur_dir, ec);
2906 if (ec && *ec)
2907 return;
2908 }
2909 relative_from = detail::relative(p: abs_from, base: abs_to, ec);
2910 if (ec && *ec)
2911 return;
2912 if (path_algorithms::compare_v4(left: relative_from, right: dot_path()) != 0)
2913 path_algorithms::append_v4(left&: relative_from, right: path_algorithms::filename_v4(p: from));
2914 else
2915 relative_from = path_algorithms::filename_v4(p: from);
2916 pfrom = &relative_from;
2917 }
2918 detail::create_symlink(to: *pfrom, from: to, ec);
2919 return;
2920 }
2921
2922 if ((options & copy_options::create_hard_links) != copy_options::none)
2923 {
2924 detail::create_hard_link(to: from, from: to, ec);
2925 return;
2926 }
2927
2928 error_code local_ec;
2929 file_status to_stat;
2930 if ((options & (copy_options::skip_symlinks | copy_options::create_symlinks)) != copy_options::none)
2931 {
2932 to_stat = detail::symlink_status_impl(p: to, ec: &local_ec);
2933 }
2934 else
2935 {
2936 to_stat = detail::status_impl(p: to, ec: &local_ec);
2937 }
2938
2939 // Note: local_ec may be set by (symlink_)status() even in some non-fatal situations, e.g. when the file does not exist.
2940 // OTOH, when it returns status_error, then a real error have happened and it must have set local_ec.
2941 if (to_stat.type() == fs::status_error)
2942 {
2943 if (!ec)
2944 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::copy", from, to, local_ec));
2945 *ec = local_ec;
2946 return;
2947 }
2948
2949 if (is_directory(f: to_stat))
2950 {
2951 path target(to);
2952 path_algorithms::append_v4(left&: target, right: path_algorithms::filename_v4(p: from));
2953 detail::copy_file(from, to: target, options, ec);
2954 }
2955 else
2956 detail::copy_file(from, to, options, ec);
2957 }
2958 else if (is_directory(f: from_stat))
2959 {
2960 error_code local_ec;
2961 if ((options & copy_options::create_symlinks) != copy_options::none)
2962 {
2963 local_ec = make_error_code(e: system::errc::is_a_directory);
2964 if (!ec)
2965 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::copy", from, to, local_ec));
2966 *ec = local_ec;
2967 return;
2968 }
2969
2970 file_status to_stat;
2971 if ((options & (copy_options::skip_symlinks | copy_options::create_symlinks)) != copy_options::none)
2972 {
2973 to_stat = detail::symlink_status_impl(p: to, ec: &local_ec);
2974 }
2975 else
2976 {
2977 to_stat = detail::status_impl(p: to, ec: &local_ec);
2978 }
2979
2980 // Note: ec may be set by (symlink_)status() even in some non-fatal situations, e.g. when the file does not exist.
2981 // OTOH, when it returns status_error, then a real error have happened and it must have set local_ec.
2982 if (to_stat.type() == fs::status_error)
2983 {
2984 if (!ec)
2985 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::copy", from, to, local_ec));
2986 *ec = local_ec;
2987 return;
2988 }
2989
2990 if (!exists(f: to_stat))
2991 {
2992 detail::create_directory(p: to, existing: &from, ec);
2993 if (ec && *ec)
2994 return;
2995 }
2996
2997 if ((options & copy_options::recursive) != copy_options::none || options == copy_options::none)
2998 {
2999 fs::directory_iterator itr;
3000 detail::directory_iterator_construct(it&: itr, p: from, opts: directory_options::none, params: nullptr, ec);
3001 if (ec && *ec)
3002 return;
3003
3004 const fs::directory_iterator end_dit;
3005 while (itr != end_dit)
3006 {
3007 path const& p = itr->path();
3008 {
3009 path target(to);
3010 path_algorithms::append_v4(left&: target, right: path_algorithms::filename_v4(p));
3011 // Set _detail_recursing flag so that we don't recurse more than for one level deeper into the directory if options are copy_options::none
3012 detail::copy(from: p, to: target, options: options | copy_options::_detail_recursing, ec);
3013 }
3014 if (ec && *ec)
3015 return;
3016
3017 detail::directory_iterator_increment(it&: itr, ec);
3018 if (ec && *ec)
3019 return;
3020 }
3021 }
3022 }
3023 else
3024 {
3025 fail:
3026 emit_error(BOOST_ERROR_NOT_SUPPORTED, p1: from, p2: to, ec, message: "boost::filesystem::copy");
3027 }
3028}
3029
3030BOOST_FILESYSTEM_DECL
3031bool copy_file(path const& from, path const& to, copy_options options, error_code* ec)
3032{
3033 BOOST_ASSERT((((options & copy_options::overwrite_existing) != copy_options::none) +
3034 ((options & copy_options::skip_existing) != copy_options::none) +
3035 ((options & copy_options::update_existing) != copy_options::none)) <= 1);
3036
3037 if (ec)
3038 ec->clear();
3039
3040#if defined(BOOST_POSIX_API)
3041
3042 int err = 0;
3043
3044 // Note: Declare fd wrappers here so that errno is not clobbered by close() that may be called in fd wrapper destructors
3045 boost::scope::unique_fd infile, outfile;
3046
3047 while (true)
3048 {
3049 infile.reset(res: ::open(file: from.c_str(), O_RDONLY | O_CLOEXEC));
3050 if (BOOST_UNLIKELY(!infile))
3051 {
3052 err = errno;
3053 if (err == EINTR)
3054 continue;
3055
3056 fail:
3057 emit_error(error_num: err, p1: from, p2: to, ec, message: "boost::filesystem::copy_file");
3058 return false;
3059 }
3060
3061 break;
3062 }
3063
3064#if defined(BOOST_FILESYSTEM_USE_STATX)
3065 unsigned int statx_data_mask = STATX_TYPE | STATX_MODE | STATX_INO | STATX_SIZE;
3066 if ((options & copy_options::update_existing) != copy_options::none)
3067 statx_data_mask |= STATX_MTIME;
3068
3069 struct ::statx from_stat;
3070 if (BOOST_UNLIKELY(invoke_statx(infile.get(), "", AT_EMPTY_PATH | AT_NO_AUTOMOUNT, statx_data_mask, &from_stat) < 0))
3071 {
3072 fail_errno:
3073 err = errno;
3074 goto fail;
3075 }
3076
3077 if (BOOST_UNLIKELY((from_stat.stx_mask & statx_data_mask) != statx_data_mask))
3078 {
3079 err = ENOSYS;
3080 goto fail;
3081 }
3082#else
3083 struct ::stat from_stat;
3084 if (BOOST_UNLIKELY(::fstat(infile.get(), &from_stat) != 0))
3085 {
3086 fail_errno:
3087 err = errno;
3088 goto fail;
3089 }
3090#endif
3091
3092 const mode_t from_mode = get_mode(st: from_stat);
3093 if (BOOST_UNLIKELY(!S_ISREG(from_mode)))
3094 {
3095 err = ENOSYS;
3096 goto fail;
3097 }
3098
3099 mode_t to_mode = from_mode & fs::perms_mask;
3100#if !defined(BOOST_FILESYSTEM_USE_WASI)
3101 // Enable writing for the newly created files. Having write permission set is important e.g. for NFS,
3102 // which checks the file permission on the server, even if the client's file descriptor supports writing.
3103 to_mode |= S_IWUSR;
3104#endif
3105 int oflag = O_WRONLY | O_CLOEXEC;
3106
3107 if ((options & copy_options::update_existing) != copy_options::none)
3108 {
3109 // Try opening the existing file without truncation to test the modification time later
3110 while (true)
3111 {
3112 outfile.reset(res: ::open(file: to.c_str(), oflag: oflag, to_mode));
3113 if (!outfile)
3114 {
3115 err = errno;
3116 if (err == EINTR)
3117 continue;
3118
3119 if (err == ENOENT)
3120 goto create_outfile;
3121
3122 goto fail;
3123 }
3124
3125 break;
3126 }
3127 }
3128 else
3129 {
3130 create_outfile:
3131 oflag |= O_CREAT | O_TRUNC;
3132 if (((options & copy_options::overwrite_existing) == copy_options::none ||
3133 (options & copy_options::skip_existing) != copy_options::none) &&
3134 (options & copy_options::update_existing) == copy_options::none)
3135 {
3136 oflag |= O_EXCL;
3137 }
3138
3139 while (true)
3140 {
3141 outfile.reset(res: ::open(file: to.c_str(), oflag: oflag, to_mode));
3142 if (!outfile)
3143 {
3144 err = errno;
3145 if (err == EINTR)
3146 continue;
3147
3148 if (err == EEXIST && (options & copy_options::skip_existing) != copy_options::none)
3149 return false;
3150
3151 goto fail;
3152 }
3153
3154 break;
3155 }
3156 }
3157
3158#if defined(BOOST_FILESYSTEM_USE_STATX)
3159 statx_data_mask = STATX_TYPE | STATX_MODE | STATX_INO;
3160 if ((oflag & O_TRUNC) == 0)
3161 {
3162 // O_TRUNC is not set if copy_options::update_existing is set and an existing file was opened.
3163 statx_data_mask |= STATX_MTIME;
3164 }
3165
3166 struct ::statx to_stat;
3167 if (BOOST_UNLIKELY(invoke_statx(outfile.get(), "", AT_EMPTY_PATH | AT_NO_AUTOMOUNT, statx_data_mask, &to_stat) < 0))
3168 goto fail_errno;
3169
3170 if (BOOST_UNLIKELY((to_stat.stx_mask & statx_data_mask) != statx_data_mask))
3171 {
3172 err = ENOSYS;
3173 goto fail;
3174 }
3175#else
3176 struct ::stat to_stat;
3177 if (BOOST_UNLIKELY(::fstat(outfile.get(), &to_stat) != 0))
3178 goto fail_errno;
3179#endif
3180
3181 to_mode = get_mode(st: to_stat);
3182 if (BOOST_UNLIKELY(!S_ISREG(to_mode)))
3183 {
3184 err = ENOSYS;
3185 goto fail;
3186 }
3187
3188 if (BOOST_UNLIKELY(detail::equivalent_stat(from_stat, to_stat)))
3189 {
3190 err = EEXIST;
3191 goto fail;
3192 }
3193
3194 if ((oflag & O_TRUNC) == 0)
3195 {
3196 // O_TRUNC is not set if copy_options::update_existing is set and an existing file was opened.
3197 // We need to check the last write times.
3198#if defined(BOOST_FILESYSTEM_USE_STATX)
3199 if (from_stat.stx_mtime.tv_sec < to_stat.stx_mtime.tv_sec || (from_stat.stx_mtime.tv_sec == to_stat.stx_mtime.tv_sec && from_stat.stx_mtime.tv_nsec <= to_stat.stx_mtime.tv_nsec))
3200 return false;
3201#elif defined(BOOST_FILESYSTEM_STAT_ST_MTIMENSEC)
3202 // Modify time is available with nanosecond precision.
3203 if (from_stat.st_mtime < to_stat.st_mtime || (from_stat.st_mtime == to_stat.st_mtime && from_stat.BOOST_FILESYSTEM_STAT_ST_MTIMENSEC <= to_stat.BOOST_FILESYSTEM_STAT_ST_MTIMENSEC))
3204 return false;
3205#else
3206 if (from_stat.st_mtime <= to_stat.st_mtime)
3207 return false;
3208#endif
3209
3210 if (BOOST_UNLIKELY(::ftruncate(outfile.get(), 0) != 0))
3211 goto fail_errno;
3212 }
3213
3214 // Note: Use block size of the target file since it is most important for writing performance.
3215 err = filesystem::detail::atomic_load_relaxed(a&: filesystem::detail::copy_file_data)(infile.get(), outfile.get(), get_size(st: from_stat), get_blksize(st: to_stat));
3216 if (BOOST_UNLIKELY(err != 0))
3217 goto fail; // err already contains the error code
3218
3219#if !defined(BOOST_FILESYSTEM_USE_WASI)
3220 // If we created a new file with an explicitly added S_IWUSR permission,
3221 // we may need to update its mode bits to match the source file.
3222 if ((to_mode & fs::perms_mask) != (from_mode & fs::perms_mask))
3223 {
3224 if (BOOST_UNLIKELY(::fchmod(outfile.get(), (from_mode & fs::perms_mask)) != 0 &&
3225 (options & copy_options::ignore_attribute_errors) == copy_options::none))
3226 {
3227 goto fail_errno;
3228 }
3229 }
3230#endif
3231
3232 if ((options & (copy_options::synchronize_data | copy_options::synchronize)) != copy_options::none)
3233 {
3234 if ((options & copy_options::synchronize) != copy_options::none)
3235 err = full_sync(fd: outfile.get());
3236 else
3237 err = data_sync(fd: outfile.get());
3238
3239 if (BOOST_UNLIKELY(err != 0))
3240 goto fail;
3241 }
3242
3243 // We have to explicitly close the output file descriptor in order to handle a possible error returned from it. The error may indicate
3244 // a failure of a prior write operation.
3245 err = close_fd(fd: outfile.get());
3246 outfile.release();
3247 if (BOOST_UNLIKELY(err < 0))
3248 {
3249 err = errno;
3250 // EINPROGRESS is an allowed error code in future POSIX revisions, according to https://www.austingroupbugs.net/view.php?id=529#c1200.
3251 if (err != EINTR && err != EINPROGRESS)
3252 goto fail;
3253 }
3254
3255 return true;
3256
3257#else // defined(BOOST_POSIX_API)
3258
3259 DWORD copy_flags = 0u;
3260 if ((options & copy_options::overwrite_existing) == copy_options::none ||
3261 (options & copy_options::skip_existing) != copy_options::none)
3262 {
3263 copy_flags |= COPY_FILE_FAIL_IF_EXISTS;
3264 }
3265
3266 if ((options & copy_options::update_existing) != copy_options::none)
3267 {
3268 // Create unique_handle wrappers here so that CloseHandle calls don't clobber error code returned by GetLastError
3269 unique_handle hw_from, hw_to;
3270
3271 // See the comment in last_write_time regarding access rights used here for GetFileTime.
3272 hw_from = create_file_handle(
3273 from.c_str(),
3274 FILE_READ_ATTRIBUTES | FILE_READ_EA,
3275 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
3276 nullptr,
3277 OPEN_EXISTING,
3278 FILE_FLAG_BACKUP_SEMANTICS);
3279
3280 FILETIME lwt_from;
3281 if (!hw_from)
3282 {
3283 fail_last_error:
3284 DWORD err = ::GetLastError();
3285 emit_error(err, from, to, ec, "boost::filesystem::copy_file");
3286 return false;
3287 }
3288
3289 if (!::GetFileTime(hw_from.get(), nullptr, nullptr, &lwt_from))
3290 goto fail_last_error;
3291
3292 hw_to = create_file_handle(
3293 to.c_str(),
3294 FILE_READ_ATTRIBUTES | FILE_READ_EA,
3295 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
3296 nullptr,
3297 OPEN_EXISTING,
3298 FILE_FLAG_BACKUP_SEMANTICS);
3299
3300 if (!!hw_to)
3301 {
3302 FILETIME lwt_to;
3303 if (!::GetFileTime(hw_to.get(), nullptr, nullptr, &lwt_to))
3304 goto fail_last_error;
3305
3306 ULONGLONG tfrom = (static_cast< ULONGLONG >(lwt_from.dwHighDateTime) << 32) | static_cast< ULONGLONG >(lwt_from.dwLowDateTime);
3307 ULONGLONG tto = (static_cast< ULONGLONG >(lwt_to.dwHighDateTime) << 32) | static_cast< ULONGLONG >(lwt_to.dwLowDateTime);
3308 if (tfrom <= tto)
3309 return false;
3310 }
3311
3312 copy_flags &= ~static_cast< DWORD >(COPY_FILE_FAIL_IF_EXISTS);
3313 }
3314
3315 struct callback_context
3316 {
3317 DWORD flush_error;
3318 };
3319
3320 struct local
3321 {
3322 //! Callback that is called to report progress of \c CopyFileExW
3323 static DWORD WINAPI on_copy_file_progress(
3324 LARGE_INTEGER total_file_size,
3325 LARGE_INTEGER total_bytes_transferred,
3326 LARGE_INTEGER stream_size,
3327 LARGE_INTEGER stream_bytes_transferred,
3328 DWORD stream_number,
3329 DWORD callback_reason,
3330 HANDLE from_handle,
3331 HANDLE to_handle,
3332 LPVOID ctx)
3333 {
3334 // For each stream, CopyFileExW will open a separate pair of file handles, so we need to flush each stream separately.
3335 if (stream_bytes_transferred.QuadPart == stream_size.QuadPart)
3336 {
3337 BOOL res = ::FlushFileBuffers(to_handle);
3338 if (BOOST_UNLIKELY(!res))
3339 {
3340 callback_context* context = static_cast< callback_context* >(ctx);
3341 if (BOOST_LIKELY(context->flush_error == 0u))
3342 context->flush_error = ::GetLastError();
3343 }
3344 }
3345
3346 return PROGRESS_CONTINUE;
3347 }
3348 };
3349
3350 callback_context cb_context = {};
3351 LPPROGRESS_ROUTINE cb = nullptr;
3352 LPVOID cb_ctx = nullptr;
3353
3354 if ((options & (copy_options::synchronize_data | copy_options::synchronize)) != copy_options::none)
3355 {
3356 cb = &local::on_copy_file_progress;
3357 cb_ctx = &cb_context;
3358 }
3359
3360 BOOL cancelled = FALSE;
3361 BOOL res = ::CopyFileExW(from.c_str(), to.c_str(), cb, cb_ctx, &cancelled, copy_flags);
3362 DWORD err;
3363 if (BOOST_UNLIKELY(!res))
3364 {
3365 err = ::GetLastError();
3366 if ((err == ERROR_FILE_EXISTS || err == ERROR_ALREADY_EXISTS) && (options & copy_options::skip_existing) != copy_options::none)
3367 return false;
3368
3369 copy_failed:
3370 emit_error(err, from, to, ec, "boost::filesystem::copy_file");
3371 return false;
3372 }
3373
3374 if (BOOST_UNLIKELY(cb_context.flush_error != 0u))
3375 {
3376 err = cb_context.flush_error;
3377 goto copy_failed;
3378 }
3379
3380 return true;
3381
3382#endif // defined(BOOST_POSIX_API)
3383}
3384
3385BOOST_FILESYSTEM_DECL
3386void copy_symlink(path const& existing_symlink, path const& new_symlink, system::error_code* ec)
3387{
3388 path p(read_symlink(p: existing_symlink, ec));
3389 if (ec && *ec)
3390 return;
3391 create_symlink(to: p, from: new_symlink, ec);
3392}
3393
3394BOOST_FILESYSTEM_DECL
3395bool create_directories(path const& p, system::error_code* ec)
3396{
3397 if (p.empty())
3398 {
3399 if (!ec)
3400 {
3401 BOOST_FILESYSTEM_THROW(filesystem_error(
3402 "boost::filesystem::create_directories", p,
3403 system::errc::make_error_code(system::errc::invalid_argument)));
3404 }
3405 ec->assign(val: system::errc::invalid_argument, cat: system::generic_category());
3406 return false;
3407 }
3408
3409 if (ec)
3410 ec->clear();
3411
3412 path::const_iterator e(p.end()), it(e);
3413 path parent(p);
3414 path const& dot_p = dot_path();
3415 path const& dot_dot_p = dot_dot_path();
3416 error_code local_ec;
3417
3418 // Find the initial part of the path that exists
3419 for (path fname = path_algorithms::filename_v4(p: parent); parent.has_relative_path(); fname = path_algorithms::filename_v4(p: parent))
3420 {
3421 if (!fname.empty() && path_algorithms::compare_v4(left: fname, right: dot_p) != 0 && path_algorithms::compare_v4(left: fname, right: dot_dot_p) != 0)
3422 {
3423 file_status existing_status = detail::status_impl(p: parent, ec: &local_ec);
3424
3425 if (existing_status.type() == directory_file)
3426 {
3427 break;
3428 }
3429 else if (BOOST_UNLIKELY(existing_status.type() == status_error))
3430 {
3431 if (!ec)
3432 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::create_directories", p, parent, local_ec));
3433 *ec = local_ec;
3434 return false;
3435 }
3436 }
3437
3438 path_algorithms::decrement_v4(it);
3439 parent.remove_filename_and_trailing_separators();
3440 }
3441
3442 // Create missing directories
3443 bool created = false;
3444 for (; it != e; path_algorithms::increment_v4(it))
3445 {
3446 path const& fname = *it;
3447 path_algorithms::append_v4(left&: parent, right: fname);
3448 if (!fname.empty() && path_algorithms::compare_v4(left: fname, right: dot_p) != 0 && path_algorithms::compare_v4(left: fname, right: dot_dot_p) != 0)
3449 {
3450 created = detail::create_directory(p: parent, existing: nullptr, ec: &local_ec);
3451 if (BOOST_UNLIKELY(!!local_ec))
3452 {
3453 if (!ec)
3454 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::create_directories", p, parent, local_ec));
3455 *ec = local_ec;
3456 return false;
3457 }
3458 }
3459 }
3460
3461 return created;
3462}
3463
3464BOOST_FILESYSTEM_DECL
3465bool create_directory(path const& p, const path* existing, error_code* ec)
3466{
3467 if (ec)
3468 ec->clear();
3469
3470#if defined(BOOST_POSIX_API)
3471
3472 mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO;
3473 if (existing)
3474 {
3475#if defined(BOOST_FILESYSTEM_USE_STATX)
3476 struct ::statx existing_stat;
3477 if (BOOST_UNLIKELY(invoke_statx(AT_FDCWD, existing->c_str(), AT_NO_AUTOMOUNT, STATX_TYPE | STATX_MODE, &existing_stat) < 0))
3478 {
3479 emit_error(errno, p1: p, p2: *existing, ec, message: "boost::filesystem::create_directory");
3480 return false;
3481 }
3482
3483 if (BOOST_UNLIKELY((existing_stat.stx_mask & (STATX_TYPE | STATX_MODE)) != (STATX_TYPE | STATX_MODE)))
3484 {
3485 emit_error(BOOST_ERROR_NOT_SUPPORTED, p1: p, p2: *existing, ec, message: "boost::filesystem::create_directory");
3486 return false;
3487 }
3488#else
3489 struct ::stat existing_stat;
3490 if (::stat(existing->c_str(), &existing_stat) < 0)
3491 {
3492 emit_error(errno, p, *existing, ec, "boost::filesystem::create_directory");
3493 return false;
3494 }
3495#endif
3496
3497 const mode_t existing_mode = get_mode(st: existing_stat);
3498 if (!S_ISDIR(existing_mode))
3499 {
3500 emit_error(ENOTDIR, p1: p, p2: *existing, ec, message: "boost::filesystem::create_directory");
3501 return false;
3502 }
3503
3504 mode = existing_mode;
3505 }
3506
3507 if (::mkdir(path: p.c_str(), mode: mode) == 0)
3508 return true;
3509
3510#else // defined(BOOST_POSIX_API)
3511
3512 BOOL res;
3513 if (existing)
3514 res = ::CreateDirectoryExW(existing->c_str(), p.c_str(), nullptr);
3515 else
3516 res = ::CreateDirectoryW(p.c_str(), nullptr);
3517
3518 if (res)
3519 return true;
3520
3521#endif // defined(BOOST_POSIX_API)
3522
3523 // attempt to create directory failed
3524 err_t errval = BOOST_ERRNO; // save reason for failure
3525 error_code dummy;
3526
3527 if (is_directory(p, ec&: dummy))
3528 return false;
3529
3530 // attempt to create directory failed && it doesn't already exist
3531 emit_error(error_num: errval, p, ec, message: "boost::filesystem::create_directory");
3532 return false;
3533}
3534
3535BOOST_FILESYSTEM_DECL
3536void create_directory_symlink(path const& to, path const& from, system::error_code* ec)
3537{
3538 if (ec)
3539 ec->clear();
3540
3541#if defined(BOOST_POSIX_API)
3542 int err = ::symlink(from: to.c_str(), to: from.c_str());
3543 if (BOOST_UNLIKELY(err < 0))
3544 {
3545 err = errno;
3546 emit_error(error_num: err, p1: to, p2: from, ec, message: "boost::filesystem::create_directory_symlink");
3547 }
3548#else
3549 // see if actually supported by Windows runtime dll
3550 if (!create_symbolic_link_api)
3551 {
3552 emit_error(BOOST_ERROR_NOT_SUPPORTED, to, from, ec, "boost::filesystem::create_directory_symlink");
3553 return;
3554 }
3555
3556 if (!create_symbolic_link_api(from.c_str(), to.c_str(), SYMBOLIC_LINK_FLAG_DIRECTORY | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE))
3557 {
3558 emit_error(BOOST_ERRNO, to, from, ec, "boost::filesystem::create_directory_symlink");
3559 }
3560#endif
3561}
3562
3563BOOST_FILESYSTEM_DECL
3564void create_hard_link(path const& to, path const& from, error_code* ec)
3565{
3566 if (ec)
3567 ec->clear();
3568
3569#if defined(BOOST_POSIX_API)
3570 int err = ::link(from: to.c_str(), to: from.c_str());
3571 if (BOOST_UNLIKELY(err < 0))
3572 {
3573 err = errno;
3574 emit_error(error_num: err, p1: to, p2: from, ec, message: "boost::filesystem::create_hard_link");
3575 }
3576#else
3577 // see if actually supported by Windows runtime dll
3578 CreateHardLinkW_t* chl_api = filesystem::detail::atomic_load_relaxed(create_hard_link_api);
3579 if (BOOST_UNLIKELY(!chl_api))
3580 {
3581 emit_error(BOOST_ERROR_NOT_SUPPORTED, to, from, ec, "boost::filesystem::create_hard_link");
3582 return;
3583 }
3584
3585 if (BOOST_UNLIKELY(!chl_api(from.c_str(), to.c_str(), nullptr)))
3586 {
3587 emit_error(BOOST_ERRNO, to, from, ec, "boost::filesystem::create_hard_link");
3588 }
3589#endif
3590}
3591
3592BOOST_FILESYSTEM_DECL
3593void create_symlink(path const& to, path const& from, error_code* ec)
3594{
3595 if (ec)
3596 ec->clear();
3597
3598#if defined(BOOST_POSIX_API)
3599 int err = ::symlink(from: to.c_str(), to: from.c_str());
3600 if (BOOST_UNLIKELY(err < 0))
3601 {
3602 err = errno;
3603 emit_error(error_num: err, p1: to, p2: from, ec, message: "boost::filesystem::create_symlink");
3604 }
3605#else
3606 // see if actually supported by Windows runtime dll
3607 CreateSymbolicLinkW_t* csl_api = filesystem::detail::atomic_load_relaxed(create_symbolic_link_api);
3608 if (BOOST_UNLIKELY(!csl_api))
3609 {
3610 emit_error(BOOST_ERROR_NOT_SUPPORTED, to, from, ec, "boost::filesystem::create_symlink");
3611 return;
3612 }
3613
3614 if (BOOST_UNLIKELY(!csl_api(from.c_str(), to.c_str(), SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)))
3615 {
3616 emit_error(BOOST_ERRNO, to, from, ec, "boost::filesystem::create_symlink");
3617 }
3618#endif
3619}
3620
3621BOOST_FILESYSTEM_DECL
3622path current_path(error_code* ec)
3623{
3624#if defined(BOOST_FILESYSTEM_USE_WASI)
3625 // Windows CE has no current directory, so everything's relative to the root of the directory tree.
3626 // WASI also does not support current path.
3627 emit_error(BOOST_ERROR_NOT_SUPPORTED, ec, "boost::filesystem::current_path");
3628 return path();
3629#elif defined(BOOST_POSIX_API)
3630 struct local
3631 {
3632 static bool getcwd_error(error_code* ec)
3633 {
3634 const int err = errno;
3635 return error(error_num: (err != ERANGE
3636#if defined(__MSL__) && (defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__))
3637 // bug in some versions of the Metrowerks C lib on the Mac: wrong errno set
3638 && err != 0
3639#endif
3640 ) ? err : 0,
3641 ec, message: "boost::filesystem::current_path");
3642 }
3643 };
3644
3645 path cur;
3646 char small_buf[small_path_size];
3647 const char* p = ::getcwd(buf: small_buf, size: sizeof(small_buf));
3648 if (BOOST_LIKELY(!!p))
3649 {
3650 cur = p;
3651 if (ec)
3652 ec->clear();
3653 }
3654 else if (BOOST_LIKELY(!local::getcwd_error(ec)))
3655 {
3656 for (std::size_t path_max = sizeof(small_buf) * 2u;; path_max *= 2u) // loop 'til buffer large enough
3657 {
3658 if (BOOST_UNLIKELY(path_max > absolute_path_max))
3659 {
3660 emit_error(ENAMETOOLONG, ec, message: "boost::filesystem::current_path");
3661 break;
3662 }
3663
3664 std::unique_ptr< char[] > buf(new char[path_max]);
3665 p = ::getcwd(buf: buf.get(), size: path_max);
3666 if (BOOST_LIKELY(!!p))
3667 {
3668 cur = buf.get();
3669 if (ec)
3670 ec->clear();
3671 break;
3672 }
3673 else if (BOOST_UNLIKELY(local::getcwd_error(ec)))
3674 {
3675 break;
3676 }
3677 }
3678 }
3679
3680 return cur;
3681#else
3682 DWORD sz;
3683 if ((sz = ::GetCurrentDirectoryW(0, nullptr)) == 0)
3684 sz = 1;
3685 std::unique_ptr< path::value_type[] > buf(new path::value_type[sz]);
3686 error(::GetCurrentDirectoryW(sz, buf.get()) == 0 ? BOOST_ERRNO : 0, ec, "boost::filesystem::current_path");
3687 return path(buf.get());
3688#endif
3689}
3690
3691BOOST_FILESYSTEM_DECL
3692void current_path(path const& p, system::error_code* ec)
3693{
3694#if defined(BOOST_FILESYSTEM_USE_WASI)
3695 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::current_path");
3696#else
3697 error(error_num: !BOOST_SET_CURRENT_DIRECTORY(p.c_str()) ? BOOST_ERRNO : 0, p, ec, message: "boost::filesystem::current_path");
3698#endif
3699}
3700
3701BOOST_FILESYSTEM_DECL
3702bool equivalent_v3(path const& p1, path const& p2, system::error_code* ec)
3703{
3704 if (ec)
3705 ec->clear();
3706
3707#if defined(BOOST_POSIX_API)
3708
3709 // p2 is done first, so any error reported is for p1
3710#if defined(BOOST_FILESYSTEM_USE_STATX)
3711 struct ::statx s2;
3712 int e2 = invoke_statx(AT_FDCWD, path: p2.c_str(), AT_NO_AUTOMOUNT, STATX_INO, stx: &s2);
3713 if (BOOST_LIKELY(e2 == 0))
3714 {
3715 if (BOOST_UNLIKELY((s2.stx_mask & STATX_INO) != STATX_INO))
3716 {
3717 fail_unsupported:
3718 emit_error(BOOST_ERROR_NOT_SUPPORTED, p1, p2, ec, message: "boost::filesystem::equivalent");
3719 return false;
3720 }
3721 }
3722
3723 struct ::statx s1;
3724 int e1 = invoke_statx(AT_FDCWD, path: p1.c_str(), AT_NO_AUTOMOUNT, STATX_INO, stx: &s1);
3725 if (BOOST_LIKELY(e1 == 0))
3726 {
3727 if (BOOST_UNLIKELY((s1.stx_mask & STATX_INO) != STATX_INO))
3728 goto fail_unsupported;
3729 }
3730#else
3731 struct ::stat s2;
3732 int e2 = ::stat(p2.c_str(), &s2);
3733 struct ::stat s1;
3734 int e1 = ::stat(p1.c_str(), &s1);
3735#endif
3736
3737 if (BOOST_UNLIKELY(e1 != 0 || e2 != 0))
3738 {
3739 // if one is invalid and the other isn't then they aren't equivalent,
3740 // but if both are invalid then it is an error
3741 if (e1 != 0 && e2 != 0)
3742 {
3743 int err = errno;
3744 emit_error(error_num: err, p1, p2, ec, message: "boost::filesystem::equivalent");
3745 }
3746 return false;
3747 }
3748
3749 return equivalent_stat(s1, s2);
3750
3751#else // Windows
3752
3753 // Thanks to Jeremy Maitin-Shepard for much help and for permission to
3754 // base the equivalent() implementation on portions of his
3755 // file-equivalence-win32.cpp experimental code.
3756
3757 // Note well: Physical location on external media is part of the
3758 // equivalence criteria. If there are no open handles, physical location
3759 // can change due to defragmentation or other relocations. Thus handles
3760 // must be held open until location information for both paths has
3761 // been retrieved.
3762
3763 // p2 is done first, so any error reported is for p1
3764 unique_handle h2(create_file_handle(
3765 p2.c_str(),
3766 FILE_READ_ATTRIBUTES,
3767 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
3768 nullptr,
3769 OPEN_EXISTING,
3770 FILE_FLAG_BACKUP_SEMANTICS));
3771
3772 unique_handle h1(create_file_handle(
3773 p1.c_str(),
3774 FILE_READ_ATTRIBUTES,
3775 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
3776 nullptr,
3777 OPEN_EXISTING,
3778 FILE_FLAG_BACKUP_SEMANTICS));
3779
3780 if (BOOST_UNLIKELY(!h1 || !h2))
3781 {
3782 // if one is invalid and the other isn't, then they aren't equivalent,
3783 // but if both are invalid then it is an error
3784 if (!h1 && !h2)
3785 error(BOOST_ERRNO, p1, p2, ec, "boost::filesystem::equivalent");
3786 return false;
3787 }
3788
3789 // at this point, both handles are known to be valid
3790
3791 BY_HANDLE_FILE_INFORMATION info1, info2;
3792
3793 if (error(!::GetFileInformationByHandle(h1.get(), &info1) ? BOOST_ERRNO : 0, p1, p2, ec, "boost::filesystem::equivalent"))
3794 return false;
3795
3796 if (error(!::GetFileInformationByHandle(h2.get(), &info2) ? BOOST_ERRNO : 0, p1, p2, ec, "boost::filesystem::equivalent"))
3797 return false;
3798
3799 // In theory, volume serial numbers are sufficient to distinguish between
3800 // devices, but in practice VSN's are sometimes duplicated, so last write
3801 // time and file size are also checked.
3802 return info1.dwVolumeSerialNumber == info2.dwVolumeSerialNumber &&
3803 info1.nFileIndexHigh == info2.nFileIndexHigh &&
3804 info1.nFileIndexLow == info2.nFileIndexLow &&
3805 info1.nFileSizeHigh == info2.nFileSizeHigh &&
3806 info1.nFileSizeLow == info2.nFileSizeLow &&
3807 info1.ftLastWriteTime.dwLowDateTime == info2.ftLastWriteTime.dwLowDateTime &&
3808 info1.ftLastWriteTime.dwHighDateTime == info2.ftLastWriteTime.dwHighDateTime;
3809
3810#endif
3811}
3812
3813BOOST_FILESYSTEM_DECL
3814bool equivalent_v4(path const& p1, path const& p2, system::error_code* ec)
3815{
3816 if (ec)
3817 ec->clear();
3818
3819#if defined(BOOST_POSIX_API)
3820
3821#if defined(BOOST_FILESYSTEM_USE_STATX)
3822 struct ::statx s1;
3823 int err = invoke_statx(AT_FDCWD, path: p1.c_str(), AT_NO_AUTOMOUNT, STATX_INO, stx: &s1);
3824 if (BOOST_UNLIKELY(err != 0))
3825 {
3826 fail_errno:
3827 err = errno;
3828 fail_err:
3829 emit_error(error_num: err, p1, p2, ec, message: "boost::filesystem::equivalent");
3830 return false;
3831 }
3832
3833 if (BOOST_UNLIKELY((s1.stx_mask & STATX_INO) != STATX_INO))
3834 {
3835 fail_unsupported:
3836 err = BOOST_ERROR_NOT_SUPPORTED;
3837 goto fail_err;
3838 }
3839
3840 struct ::statx s2;
3841 err = invoke_statx(AT_FDCWD, path: p2.c_str(), AT_NO_AUTOMOUNT, STATX_INO, stx: &s2);
3842 if (BOOST_UNLIKELY(err != 0))
3843 goto fail_errno;
3844
3845 if (BOOST_UNLIKELY((s1.stx_mask & STATX_INO) != STATX_INO))
3846 goto fail_unsupported;
3847#else
3848 struct ::stat s1;
3849 int err = ::stat(p1.c_str(), &s1);
3850 if (BOOST_UNLIKELY(err != 0))
3851 {
3852 fail_errno:
3853 err = errno;
3854 emit_error(err, p1, p2, ec, "boost::filesystem::equivalent");
3855 return false;
3856 }
3857
3858 struct ::stat s2;
3859 err = ::stat(p2.c_str(), &s2);
3860 if (BOOST_UNLIKELY(err != 0))
3861 goto fail_errno;
3862#endif
3863
3864 return equivalent_stat(s1, s2);
3865
3866#else // Windows
3867
3868 // Thanks to Jeremy Maitin-Shepard for much help and for permission to
3869 // base the equivalent() implementation on portions of his
3870 // file-equivalence-win32.cpp experimental code.
3871
3872 // Note well: Physical location on external media is part of the
3873 // equivalence criteria. If there are no open handles, physical location
3874 // can change due to defragmentation or other relocations. Thus handles
3875 // must be held open until location information for both paths has
3876 // been retrieved.
3877
3878 unique_handle h1(create_file_handle(
3879 p1.c_str(),
3880 FILE_READ_ATTRIBUTES,
3881 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
3882 nullptr,
3883 OPEN_EXISTING,
3884 FILE_FLAG_BACKUP_SEMANTICS));
3885 if (BOOST_UNLIKELY(!h1))
3886 {
3887 fail_errno:
3888 err_t err = BOOST_ERRNO;
3889 emit_error(err, p1, p2, ec, "boost::filesystem::equivalent");
3890 return false;
3891 }
3892
3893 unique_handle h2(create_file_handle(
3894 p2.c_str(),
3895 FILE_READ_ATTRIBUTES,
3896 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
3897 nullptr,
3898 OPEN_EXISTING,
3899 FILE_FLAG_BACKUP_SEMANTICS));
3900 if (BOOST_UNLIKELY(!h2))
3901 goto fail_errno;
3902
3903 BY_HANDLE_FILE_INFORMATION info1;
3904 if (BOOST_UNLIKELY(!::GetFileInformationByHandle(h1.get(), &info1)))
3905 goto fail_errno;
3906
3907 BY_HANDLE_FILE_INFORMATION info2;
3908 if (BOOST_UNLIKELY(!::GetFileInformationByHandle(h2.get(), &info2)))
3909 goto fail_errno;
3910
3911 // In theory, volume serial numbers are sufficient to distinguish between
3912 // devices, but in practice VSN's are sometimes duplicated, so last write
3913 // time and file size are also checked.
3914 return info1.dwVolumeSerialNumber == info2.dwVolumeSerialNumber &&
3915 info1.nFileIndexHigh == info2.nFileIndexHigh &&
3916 info1.nFileIndexLow == info2.nFileIndexLow &&
3917 info1.nFileSizeHigh == info2.nFileSizeHigh &&
3918 info1.nFileSizeLow == info2.nFileSizeLow &&
3919 info1.ftLastWriteTime.dwLowDateTime == info2.ftLastWriteTime.dwLowDateTime &&
3920 info1.ftLastWriteTime.dwHighDateTime == info2.ftLastWriteTime.dwHighDateTime;
3921
3922#endif
3923}
3924
3925BOOST_FILESYSTEM_DECL
3926uintmax_t file_size(path const& p, error_code* ec)
3927{
3928 if (ec)
3929 ec->clear();
3930
3931#if defined(BOOST_POSIX_API)
3932
3933#if defined(BOOST_FILESYSTEM_USE_STATX)
3934 struct ::statx path_stat;
3935 if (BOOST_UNLIKELY(invoke_statx(AT_FDCWD, p.c_str(), AT_NO_AUTOMOUNT, STATX_TYPE | STATX_SIZE, &path_stat) < 0))
3936 {
3937 emit_error(errno, p, ec, message: "boost::filesystem::file_size");
3938 return static_cast< uintmax_t >(-1);
3939 }
3940
3941 if (BOOST_UNLIKELY((path_stat.stx_mask & (STATX_TYPE | STATX_SIZE)) != (STATX_TYPE | STATX_SIZE) || !S_ISREG(path_stat.stx_mode)))
3942 {
3943 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, message: "boost::filesystem::file_size");
3944 return static_cast< uintmax_t >(-1);
3945 }
3946#else
3947 struct ::stat path_stat;
3948 if (BOOST_UNLIKELY(::stat(p.c_str(), &path_stat) < 0))
3949 {
3950 emit_error(errno, p, ec, "boost::filesystem::file_size");
3951 return static_cast< uintmax_t >(-1);
3952 }
3953
3954 if (BOOST_UNLIKELY(!S_ISREG(path_stat.st_mode)))
3955 {
3956 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::file_size");
3957 return static_cast< uintmax_t >(-1);
3958 }
3959#endif
3960
3961 return get_size(st: path_stat);
3962
3963#else // defined(BOOST_POSIX_API)
3964
3965 // assume uintmax_t is 64-bits on all Windows compilers
3966
3967 WIN32_FILE_ATTRIBUTE_DATA fad;
3968
3969 if (BOOST_UNLIKELY(!::GetFileAttributesExW(p.c_str(), ::GetFileExInfoStandard, &fad)))
3970 {
3971 emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::file_size");
3972 return static_cast< uintmax_t >(-1);
3973 }
3974
3975 if (BOOST_UNLIKELY((fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0))
3976 {
3977 emit_error(ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::file_size");
3978 return static_cast< uintmax_t >(-1);
3979 }
3980
3981 return (static_cast< uintmax_t >(fad.nFileSizeHigh)
3982 << (sizeof(fad.nFileSizeLow) * 8u)) |
3983 fad.nFileSizeLow;
3984
3985#endif // defined(BOOST_POSIX_API)
3986}
3987
3988BOOST_FILESYSTEM_DECL
3989uintmax_t hard_link_count(path const& p, system::error_code* ec)
3990{
3991 if (ec)
3992 ec->clear();
3993
3994#if defined(BOOST_POSIX_API)
3995
3996#if defined(BOOST_FILESYSTEM_USE_STATX)
3997 struct ::statx path_stat;
3998 if (BOOST_UNLIKELY(invoke_statx(AT_FDCWD, p.c_str(), AT_NO_AUTOMOUNT, STATX_NLINK, &path_stat) < 0))
3999 {
4000 emit_error(errno, p, ec, message: "boost::filesystem::hard_link_count");
4001 return static_cast< uintmax_t >(-1);
4002 }
4003
4004 if (BOOST_UNLIKELY((path_stat.stx_mask & STATX_NLINK) != STATX_NLINK))
4005 {
4006 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, message: "boost::filesystem::hard_link_count");
4007 return static_cast< uintmax_t >(-1);
4008 }
4009
4010 return static_cast< uintmax_t >(path_stat.stx_nlink);
4011#else
4012 struct ::stat path_stat;
4013 if (BOOST_UNLIKELY(::stat(p.c_str(), &path_stat) < 0))
4014 {
4015 emit_error(errno, p, ec, "boost::filesystem::hard_link_count");
4016 return static_cast< uintmax_t >(-1);
4017 }
4018
4019 return static_cast< uintmax_t >(path_stat.st_nlink);
4020#endif
4021
4022#else // defined(BOOST_POSIX_API)
4023
4024 unique_handle h(create_file_handle(
4025 p.c_str(),
4026 FILE_READ_ATTRIBUTES,
4027 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
4028 nullptr,
4029 OPEN_EXISTING,
4030 FILE_FLAG_BACKUP_SEMANTICS));
4031
4032 if (BOOST_UNLIKELY(!h))
4033 {
4034 fail_errno:
4035 emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::hard_link_count");
4036 return static_cast< uintmax_t >(-1);
4037 }
4038
4039 // Link count info is only available through GetFileInformationByHandle
4040 BY_HANDLE_FILE_INFORMATION info;
4041 if (BOOST_UNLIKELY(!::GetFileInformationByHandle(h.get(), &info)))
4042 goto fail_errno;
4043
4044 return static_cast< uintmax_t >(info.nNumberOfLinks);
4045
4046#endif // defined(BOOST_POSIX_API)
4047}
4048
4049BOOST_FILESYSTEM_DECL
4050path initial_path(error_code* ec)
4051{
4052 static path init_path;
4053 if (init_path.empty())
4054 init_path = current_path(ec);
4055 else if (ec)
4056 ec->clear();
4057 return init_path;
4058}
4059
4060BOOST_FILESYSTEM_DECL
4061bool is_empty(path const& p, system::error_code* ec)
4062{
4063 if (ec)
4064 ec->clear();
4065
4066#if defined(BOOST_POSIX_API)
4067
4068#if defined(BOOST_FILESYSTEM_USE_STATX)
4069 struct ::statx path_stat;
4070 if (BOOST_UNLIKELY(invoke_statx(AT_FDCWD, p.c_str(), AT_NO_AUTOMOUNT, STATX_TYPE | STATX_SIZE, &path_stat) < 0))
4071 {
4072 emit_error(errno, p, ec, message: "boost::filesystem::is_empty");
4073 return false;
4074 }
4075
4076 if (BOOST_UNLIKELY((path_stat.stx_mask & STATX_TYPE) != STATX_TYPE))
4077 {
4078 fail_unsupported:
4079 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, message: "boost::filesystem::is_empty");
4080 return false;
4081 }
4082
4083 if (S_ISDIR(get_mode(path_stat)))
4084 return is_empty_directory(p, ec);
4085
4086 if (BOOST_UNLIKELY((path_stat.stx_mask & STATX_SIZE) != STATX_SIZE))
4087 goto fail_unsupported;
4088
4089 return get_size(st: path_stat) == 0u;
4090#else
4091 struct ::stat path_stat;
4092 if (BOOST_UNLIKELY(::stat(p.c_str(), &path_stat) < 0))
4093 {
4094 emit_error(errno, p, ec, "boost::filesystem::is_empty");
4095 return false;
4096 }
4097
4098 return S_ISDIR(get_mode(path_stat)) ? is_empty_directory(p, ec) : get_size(path_stat) == 0u;
4099#endif
4100
4101#else // defined(BOOST_POSIX_API)
4102
4103 WIN32_FILE_ATTRIBUTE_DATA fad;
4104 if (BOOST_UNLIKELY(!::GetFileAttributesExW(p.c_str(), ::GetFileExInfoStandard, &fad)))
4105 {
4106 emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::is_empty");
4107 return false;
4108 }
4109
4110 return (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? is_empty_directory(p, ec) : (!fad.nFileSizeHigh && !fad.nFileSizeLow);
4111
4112#endif // defined(BOOST_POSIX_API)
4113}
4114
4115BOOST_FILESYSTEM_DECL
4116std::time_t creation_time(path const& p, system::error_code* ec)
4117{
4118 if (ec)
4119 ec->clear();
4120
4121#if defined(BOOST_POSIX_API)
4122
4123#if defined(BOOST_FILESYSTEM_USE_STATX)
4124 struct ::statx stx;
4125 if (BOOST_UNLIKELY(invoke_statx(AT_FDCWD, p.c_str(), AT_NO_AUTOMOUNT, STATX_BTIME, &stx) < 0))
4126 {
4127 emit_error(BOOST_ERRNO, p, ec, message: "boost::filesystem::creation_time");
4128 return (std::numeric_limits< std::time_t >::min)();
4129 }
4130 if (BOOST_UNLIKELY((stx.stx_mask & STATX_BTIME) != STATX_BTIME))
4131 {
4132 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, message: "boost::filesystem::creation_time");
4133 return (std::numeric_limits< std::time_t >::min)();
4134 }
4135 return stx.stx_btime.tv_sec;
4136#elif defined(BOOST_FILESYSTEM_STAT_ST_BIRTHTIME) && defined(BOOST_FILESYSTEM_STAT_ST_BIRTHTIMENSEC)
4137 struct ::stat st;
4138 if (BOOST_UNLIKELY(::stat(p.c_str(), &st) < 0))
4139 {
4140 emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::creation_time");
4141 return (std::numeric_limits< std::time_t >::min)();
4142 }
4143 return st.BOOST_FILESYSTEM_STAT_ST_BIRTHTIME;
4144#else
4145 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::creation_time");
4146 return (std::numeric_limits< std::time_t >::min)();
4147#endif
4148
4149#else // defined(BOOST_POSIX_API)
4150
4151 // See the comment in last_write_time regarding access rights used here for GetFileTime.
4152 unique_handle hw(create_file_handle(
4153 p.c_str(),
4154 FILE_READ_ATTRIBUTES | FILE_READ_EA,
4155 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
4156 nullptr,
4157 OPEN_EXISTING,
4158 FILE_FLAG_BACKUP_SEMANTICS));
4159
4160 DWORD err;
4161 if (BOOST_UNLIKELY(!hw))
4162 {
4163 fail_errno:
4164 err = BOOST_ERRNO;
4165 fail:
4166 emit_error(err, p, ec, "boost::filesystem::creation_time");
4167 return (std::numeric_limits< std::time_t >::min)();
4168 }
4169
4170 FILETIME ct;
4171 if (BOOST_UNLIKELY(!::GetFileTime(hw.get(), &ct, nullptr, nullptr)))
4172 goto fail_errno;
4173
4174 std::time_t t;
4175 err = to_time_t(ct, t);
4176 if (BOOST_UNLIKELY(err != 0u))
4177 goto fail;
4178
4179 return t;
4180
4181#endif // defined(BOOST_POSIX_API)
4182}
4183
4184BOOST_FILESYSTEM_DECL
4185std::time_t last_write_time(path const& p, system::error_code* ec)
4186{
4187 if (ec)
4188 ec->clear();
4189
4190#if defined(BOOST_POSIX_API)
4191
4192#if defined(BOOST_FILESYSTEM_USE_STATX)
4193 struct ::statx stx;
4194 if (BOOST_UNLIKELY(invoke_statx(AT_FDCWD, p.c_str(), AT_NO_AUTOMOUNT, STATX_MTIME, &stx) < 0))
4195 {
4196 emit_error(BOOST_ERRNO, p, ec, message: "boost::filesystem::last_write_time");
4197 return (std::numeric_limits< std::time_t >::min)();
4198 }
4199 if (BOOST_UNLIKELY((stx.stx_mask & STATX_MTIME) != STATX_MTIME))
4200 {
4201 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, message: "boost::filesystem::last_write_time");
4202 return (std::numeric_limits< std::time_t >::min)();
4203 }
4204 return stx.stx_mtime.tv_sec;
4205#else
4206 struct ::stat st;
4207 if (BOOST_UNLIKELY(::stat(p.c_str(), &st) < 0))
4208 {
4209 emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::last_write_time");
4210 return (std::numeric_limits< std::time_t >::min)();
4211 }
4212 return st.st_mtime;
4213#endif
4214
4215#else // defined(BOOST_POSIX_API)
4216
4217 // GetFileTime is documented to require GENERIC_READ access right, but this causes problems if the file
4218 // is opened by another process without FILE_SHARE_READ. In practice, FILE_READ_ATTRIBUTES works, and
4219 // FILE_READ_EA is also added for good measure, in case if it matters for SMBv1.
4220 unique_handle hw(create_file_handle(
4221 p.c_str(),
4222 FILE_READ_ATTRIBUTES | FILE_READ_EA,
4223 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
4224 nullptr,
4225 OPEN_EXISTING,
4226 FILE_FLAG_BACKUP_SEMANTICS));
4227
4228 DWORD err;
4229 if (BOOST_UNLIKELY(!hw))
4230 {
4231 fail_errno:
4232 err = BOOST_ERRNO;
4233 fail:
4234 emit_error(err, p, ec, "boost::filesystem::last_write_time");
4235 return (std::numeric_limits< std::time_t >::min)();
4236 }
4237
4238 FILETIME lwt;
4239 if (BOOST_UNLIKELY(!::GetFileTime(hw.get(), nullptr, nullptr, &lwt)))
4240 goto fail_errno;
4241
4242 std::time_t t;
4243 err = to_time_t(lwt, t);
4244 if (BOOST_UNLIKELY(err != 0u))
4245 goto fail;
4246
4247 return t;
4248
4249#endif // defined(BOOST_POSIX_API)
4250}
4251
4252BOOST_FILESYSTEM_DECL
4253void last_write_time(path const& p, const std::time_t new_time, system::error_code* ec)
4254{
4255 if (ec)
4256 ec->clear();
4257
4258#if defined(BOOST_POSIX_API)
4259
4260#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
4261
4262 struct timespec times[2] = {};
4263
4264 // Keep the last access time unchanged
4265 times[0].tv_nsec = UTIME_OMIT;
4266
4267 times[1].tv_sec = new_time;
4268
4269 if (BOOST_UNLIKELY(::utimensat(AT_FDCWD, p.c_str(), times, 0) != 0))
4270 {
4271 emit_error(BOOST_ERRNO, p, ec, message: "boost::filesystem::last_write_time");
4272 return;
4273 }
4274
4275#else // defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
4276
4277 struct ::stat st;
4278 if (BOOST_UNLIKELY(::stat(p.c_str(), &st) < 0))
4279 {
4280 emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::last_write_time");
4281 return;
4282 }
4283
4284 ::utimbuf buf;
4285 buf.actime = st.st_atime; // utime() updates access time too :-(
4286 buf.modtime = new_time;
4287 if (BOOST_UNLIKELY(::utime(p.c_str(), &buf) < 0))
4288 emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::last_write_time");
4289
4290#endif // defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
4291
4292#else // defined(BOOST_POSIX_API)
4293
4294 unique_handle hw(create_file_handle(
4295 p.c_str(),
4296 FILE_WRITE_ATTRIBUTES,
4297 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
4298 nullptr,
4299 OPEN_EXISTING,
4300 FILE_FLAG_BACKUP_SEMANTICS));
4301
4302 DWORD err;
4303 if (BOOST_UNLIKELY(!hw))
4304 {
4305 fail_errno:
4306 err = BOOST_ERRNO;
4307 fail:
4308 emit_error(err, p, ec, "boost::filesystem::last_write_time");
4309 return;
4310 }
4311
4312 FILETIME lwt;
4313 err = to_FILETIME(new_time, lwt);
4314 if (BOOST_UNLIKELY(err != 0u))
4315 goto fail;
4316
4317 if (BOOST_UNLIKELY(!::SetFileTime(hw.get(), nullptr, nullptr, &lwt)))
4318 goto fail_errno;
4319
4320#endif // defined(BOOST_POSIX_API)
4321}
4322
4323#ifdef BOOST_POSIX_API
4324const perms active_bits(all_all | set_uid_on_exe | set_gid_on_exe | sticky_bit);
4325inline mode_t mode_cast(perms prms)
4326{
4327 return prms & active_bits;
4328}
4329#endif
4330
4331BOOST_FILESYSTEM_DECL
4332void permissions(path const& p, perms prms, system::error_code* ec)
4333{
4334 BOOST_ASSERT_MSG(!((prms & add_perms) && (prms & remove_perms)), "add_perms and remove_perms are mutually exclusive");
4335
4336 if ((prms & add_perms) && (prms & remove_perms)) // precondition failed
4337 return;
4338
4339#if defined(BOOST_FILESYSTEM_USE_WASI)
4340 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::permissions");
4341#elif defined(BOOST_POSIX_API)
4342 error_code local_ec;
4343 file_status current_status((prms & symlink_perms) ? detail::symlink_status_impl(p, ec: &local_ec) : detail::status_impl(p, ec: &local_ec));
4344 if (local_ec)
4345 {
4346 if (!ec)
4347 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::permissions", p, local_ec));
4348
4349 *ec = local_ec;
4350 return;
4351 }
4352
4353 if (prms & add_perms)
4354 prms |= current_status.permissions();
4355 else if (prms & remove_perms)
4356 prms = current_status.permissions() & ~prms;
4357
4358 // OS X <10.10, iOS <8.0 and some other platforms don't support fchmodat().
4359 // Solaris (SunPro and gcc) only support fchmodat() on Solaris 11 and higher,
4360 // and a runtime check is too much trouble.
4361 // Linux does not support permissions on symbolic links and has no plans to
4362 // support them in the future. The chmod() code is thus more practical,
4363 // rather than always hitting ENOTSUP when sending in AT_SYMLINK_NO_FOLLOW.
4364 // - See the 3rd paragraph of
4365 // "Symbolic link ownership, permissions, and timestamps" at:
4366 // "http://man7.org/linux/man-pages/man7/symlink.7.html"
4367 // - See the fchmodat() Linux man page:
4368 // "http://man7.org/linux/man-pages/man2/fchmodat.2.html"
4369#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) && \
4370 !(defined(__SUNPRO_CC) || defined(__sun) || defined(sun)) && \
4371 !(defined(linux) || defined(__linux) || defined(__linux__)) && \
4372 !(defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101000) && \
4373 !(defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 80000) && \
4374 !(defined(__rtems__)) && \
4375 !(defined(__QNX__) && (_NTO_VERSION <= 700))
4376 if (::fchmodat(AT_FDCWD, p.c_str(), mode_cast(prms), !(prms & symlink_perms) ? 0 : AT_SYMLINK_NOFOLLOW))
4377#else // fallback if fchmodat() not supported
4378 if (::chmod(file: p.c_str(), mode: mode_cast(prms)))
4379#endif
4380 {
4381 const int err = errno;
4382 if (!ec)
4383 {
4384 BOOST_FILESYSTEM_THROW(filesystem_error(
4385 "boost::filesystem::permissions", p, error_code(err, system::generic_category())));
4386 }
4387
4388 ec->assign(val: err, cat: system::generic_category());
4389 }
4390
4391#else // Windows
4392
4393 // if not going to alter FILE_ATTRIBUTE_READONLY, just return
4394 if (!(!((prms & (add_perms | remove_perms))) || (prms & (owner_write | group_write | others_write))))
4395 return;
4396
4397 DWORD attr = ::GetFileAttributesW(p.c_str());
4398
4399 if (error(attr == 0 ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::permissions"))
4400 return;
4401
4402 if (prms & add_perms)
4403 attr &= ~FILE_ATTRIBUTE_READONLY;
4404 else if (prms & remove_perms)
4405 attr |= FILE_ATTRIBUTE_READONLY;
4406 else if (prms & (owner_write | group_write | others_write))
4407 attr &= ~FILE_ATTRIBUTE_READONLY;
4408 else
4409 attr |= FILE_ATTRIBUTE_READONLY;
4410
4411 error(::SetFileAttributesW(p.c_str(), attr) == 0 ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::permissions");
4412#endif
4413}
4414
4415BOOST_FILESYSTEM_DECL
4416path read_symlink(path const& p, system::error_code* ec)
4417{
4418 if (ec)
4419 ec->clear();
4420
4421 path symlink_path;
4422
4423#ifdef BOOST_POSIX_API
4424 const char* const path_str = p.c_str();
4425 char small_buf[small_path_size];
4426 ssize_t result = ::readlink(path: path_str, buf: small_buf, len: sizeof(small_buf));
4427 if (BOOST_UNLIKELY(result < 0))
4428 {
4429 fail:
4430 const int err = errno;
4431 if (!ec)
4432 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::read_symlink", p, error_code(err, system_category())));
4433
4434 ec->assign(val: err, cat: system_category());
4435 }
4436 else if (BOOST_LIKELY(static_cast< std::size_t >(result) < sizeof(small_buf)))
4437 {
4438 symlink_path.assign(begin: small_buf, end: small_buf + result);
4439 }
4440 else
4441 {
4442 for (std::size_t path_max = sizeof(small_buf) * 2u;; path_max *= 2u) // loop 'til buffer large enough
4443 {
4444 if (BOOST_UNLIKELY(path_max > absolute_path_max))
4445 {
4446 if (!ec)
4447 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::read_symlink", p, error_code(ENAMETOOLONG, system_category())));
4448
4449 ec->assign(ENAMETOOLONG, cat: system_category());
4450 break;
4451 }
4452
4453 std::unique_ptr< char[] > buf(new char[path_max]);
4454 result = ::readlink(path: path_str, buf: buf.get(), len: path_max);
4455 if (BOOST_UNLIKELY(result < 0))
4456 {
4457 goto fail;
4458 }
4459 else if (BOOST_LIKELY(static_cast< std::size_t >(result) < path_max))
4460 {
4461 symlink_path.assign(begin: buf.get(), end: buf.get() + result);
4462 break;
4463 }
4464 }
4465 }
4466
4467#else
4468
4469 unique_handle h(create_file_handle(
4470 p.c_str(),
4471 FILE_READ_ATTRIBUTES,
4472 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
4473 nullptr,
4474 OPEN_EXISTING,
4475 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT));
4476
4477 DWORD error;
4478 if (BOOST_UNLIKELY(!h))
4479 {
4480 return_last_error:
4481 error = ::GetLastError();
4482 emit_error(error, p, ec, "boost::filesystem::read_symlink");
4483 return symlink_path;
4484 }
4485
4486 std::unique_ptr< reparse_data_buffer_with_storage > buf(new reparse_data_buffer_with_storage);
4487 DWORD sz = 0u;
4488 if (BOOST_UNLIKELY(!::DeviceIoControl(h.get(), FSCTL_GET_REPARSE_POINT, nullptr, 0, buf.get(), sizeof(*buf), &sz, nullptr)))
4489 goto return_last_error;
4490
4491 const wchar_t* buffer;
4492 std::size_t offset, len;
4493 switch (buf->rdb.ReparseTag)
4494 {
4495 case IO_REPARSE_TAG_MOUNT_POINT:
4496 buffer = buf->rdb.MountPointReparseBuffer.PathBuffer;
4497 offset = buf->rdb.MountPointReparseBuffer.SubstituteNameOffset;
4498 len = buf->rdb.MountPointReparseBuffer.SubstituteNameLength;
4499 break;
4500
4501 case IO_REPARSE_TAG_SYMLINK:
4502 buffer = buf->rdb.SymbolicLinkReparseBuffer.PathBuffer;
4503 offset = buf->rdb.SymbolicLinkReparseBuffer.SubstituteNameOffset;
4504 len = buf->rdb.SymbolicLinkReparseBuffer.SubstituteNameLength;
4505 // Note: iff info.rdb.SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE
4506 // -> resulting path is relative to the source
4507 break;
4508
4509 default:
4510 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "Unknown ReparseTag in boost::filesystem::read_symlink");
4511 return symlink_path;
4512 }
4513
4514 symlink_path = convert_nt_path_to_win32_path(buffer + offset / sizeof(wchar_t), len / sizeof(wchar_t));
4515#endif
4516
4517 return symlink_path;
4518}
4519
4520BOOST_FILESYSTEM_DECL
4521path relative(path const& p, path const& base, error_code* ec)
4522{
4523 if (ec)
4524 ec->clear();
4525
4526 error_code local_ec;
4527 path cur_path;
4528 if (!p.is_absolute() || !base.is_absolute())
4529 {
4530 cur_path = detail::current_path(ec: &local_ec);
4531 if (BOOST_UNLIKELY(!!local_ec))
4532 {
4533 fail_local_ec:
4534 if (!ec)
4535 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::relative", p, base, local_ec));
4536
4537 *ec = local_ec;
4538 return path();
4539 }
4540 }
4541
4542 path wc_base(detail::weakly_canonical_v4(p: base, base: cur_path, ec: &local_ec));
4543 if (BOOST_UNLIKELY(!!local_ec))
4544 goto fail_local_ec;
4545 path wc_p(detail::weakly_canonical_v4(p, base: cur_path, ec: &local_ec));
4546 if (BOOST_UNLIKELY(!!local_ec))
4547 goto fail_local_ec;
4548 return wc_p.lexically_relative(base: wc_base);
4549}
4550
4551BOOST_FILESYSTEM_DECL
4552bool remove(path const& p, error_code* ec)
4553{
4554 if (ec)
4555 ec->clear();
4556
4557 return detail::remove_impl(p, ec);
4558}
4559
4560BOOST_FILESYSTEM_DECL
4561uintmax_t remove_all(path const& p, error_code* ec)
4562{
4563 if (ec)
4564 ec->clear();
4565
4566 return detail::remove_all_impl(p, ec);
4567}
4568
4569BOOST_FILESYSTEM_DECL
4570void rename(path const& old_p, path const& new_p, error_code* ec)
4571{
4572 error(error_num: !BOOST_MOVE_FILE(old_p.c_str(), new_p.c_str()) ? BOOST_ERRNO : 0, p1: old_p, p2: new_p, ec, message: "boost::filesystem::rename");
4573}
4574
4575BOOST_FILESYSTEM_DECL
4576void resize_file(path const& p, uintmax_t size, system::error_code* ec)
4577{
4578#if defined(BOOST_POSIX_API)
4579 if (BOOST_UNLIKELY(size > static_cast< uintmax_t >((std::numeric_limits< off_t >::max)())))
4580 {
4581 emit_error(error_num: system::errc::file_too_large, p, ec, message: "boost::filesystem::resize_file");
4582 return;
4583 }
4584#endif
4585 error(error_num: !BOOST_RESIZE_FILE(p.c_str(), size) ? BOOST_ERRNO : 0, p, ec, message: "boost::filesystem::resize_file");
4586}
4587
4588BOOST_FILESYSTEM_DECL
4589space_info space(path const& p, error_code* ec)
4590{
4591 space_info info;
4592 // Initialize members to -1, as required by C++20 [fs.op.space]/1 in case of error
4593 info.capacity = static_cast< uintmax_t >(-1);
4594 info.free = static_cast< uintmax_t >(-1);
4595 info.available = static_cast< uintmax_t >(-1);
4596
4597 if (ec)
4598 ec->clear();
4599
4600#if defined(BOOST_FILESYSTEM_USE_WASI)
4601
4602 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::space");
4603
4604#elif defined(BOOST_POSIX_API)
4605
4606 struct BOOST_STATVFS vfs;
4607 if (!error(error_num: ::BOOST_STATVFS(file: p.c_str(), buf: &vfs) ? BOOST_ERRNO : 0, p, ec, message: "boost::filesystem::space"))
4608 {
4609 info.capacity = static_cast< uintmax_t >(vfs.f_blocks) * BOOST_STATVFS_F_FRSIZE;
4610 info.free = static_cast< uintmax_t >(vfs.f_bfree) * BOOST_STATVFS_F_FRSIZE;
4611 info.available = static_cast< uintmax_t >(vfs.f_bavail) * BOOST_STATVFS_F_FRSIZE;
4612 }
4613
4614#else
4615
4616 // GetDiskFreeSpaceExW requires a directory path, which is unlike statvfs, which accepts any file.
4617 // To work around this, test if the path refers to a directory and use the parent directory if not.
4618 error_code local_ec;
4619 file_status status = detail::status_impl(p, &local_ec);
4620 if (status.type() == fs::status_error || status.type() == fs::file_not_found)
4621 {
4622 fail_local_ec:
4623 if (!ec)
4624 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::space", p, local_ec));
4625 *ec = local_ec;
4626 return info;
4627 }
4628
4629 path dir_path = p;
4630 if (!is_directory(status))
4631 {
4632 path cur_path = detail::current_path(ec);
4633 if (ec && *ec)
4634 return info;
4635
4636 status = detail::symlink_status_impl(p, &local_ec);
4637 if (status.type() == fs::status_error)
4638 goto fail_local_ec;
4639 if (is_symlink(status))
4640 {
4641 // We need to resolve the symlink so that we report the space for the symlink target
4642 dir_path = detail::canonical_v4(p, cur_path, ec);
4643 if (ec && *ec)
4644 return info;
4645 }
4646
4647 dir_path = dir_path.parent_path();
4648 if (dir_path.empty())
4649 {
4650 // The original path was just a filename, which is a relative path wrt. current directory
4651 dir_path = cur_path;
4652 }
4653 }
4654
4655 // For UNC names, the path must also include a trailing slash.
4656 path::string_type str = dir_path.native();
4657 if (str.size() >= 2u && detail::is_directory_separator(str[0]) && detail::is_directory_separator(str[1]) && !detail::is_directory_separator(*(str.end() - 1)))
4658 str.push_back(path::preferred_separator);
4659
4660 ULARGE_INTEGER avail, total, free;
4661 if (!error(::GetDiskFreeSpaceExW(str.c_str(), &avail, &total, &free) == 0 ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::space"))
4662 {
4663 info.capacity = static_cast< uintmax_t >(total.QuadPart);
4664 info.free = static_cast< uintmax_t >(free.QuadPart);
4665 info.available = static_cast< uintmax_t >(avail.QuadPart);
4666 }
4667
4668#endif
4669
4670 return info;
4671}
4672
4673BOOST_FILESYSTEM_DECL
4674file_status status(path const& p, error_code* ec)
4675{
4676 if (ec)
4677 ec->clear();
4678
4679 return detail::status_impl(p, ec);
4680}
4681
4682BOOST_FILESYSTEM_DECL
4683file_status symlink_status(path const& p, error_code* ec)
4684{
4685 if (ec)
4686 ec->clear();
4687
4688 return detail::symlink_status_impl(p, ec);
4689}
4690
4691// contributed by Jeff Flinn
4692BOOST_FILESYSTEM_DECL
4693path temp_directory_path(system::error_code* ec)
4694{
4695 if (ec)
4696 ec->clear();
4697
4698#ifdef BOOST_POSIX_API
4699
4700 const char* val = nullptr;
4701
4702 (val = std::getenv(name: "TMPDIR")) ||
4703 (val = std::getenv(name: "TMP")) ||
4704 (val = std::getenv(name: "TEMP")) ||
4705 (val = std::getenv(name: "TEMPDIR"));
4706
4707#ifdef __ANDROID__
4708 const char* default_tmp = "/data/local/tmp";
4709#else
4710 const char* default_tmp = "/tmp";
4711#endif
4712 path p((val != nullptr) ? val : default_tmp);
4713
4714 if (BOOST_UNLIKELY(p.empty()))
4715 {
4716 fail_not_dir:
4717 error(ENOTDIR, p, ec, message: "boost::filesystem::temp_directory_path");
4718 return p;
4719 }
4720
4721 file_status status = detail::status_impl(p, ec);
4722 if (BOOST_UNLIKELY(ec && *ec))
4723 return path();
4724 if (BOOST_UNLIKELY(!is_directory(status)))
4725 goto fail_not_dir;
4726
4727 return p;
4728
4729#else // Windows
4730
4731 static const wchar_t* const env_list[] = { L"TMP", L"TEMP", L"LOCALAPPDATA", L"USERPROFILE" };
4732 static const wchar_t temp_dir[] = L"Temp";
4733
4734 path p;
4735 for (unsigned int i = 0; i < sizeof(env_list) / sizeof(*env_list); ++i)
4736 {
4737 std::wstring env = wgetenv(env_list[i]);
4738 if (!env.empty())
4739 {
4740 p = env;
4741 if (i >= 2)
4742 path_algorithms::append_v4(p, temp_dir, temp_dir + (sizeof(temp_dir) / sizeof(*temp_dir) - 1u));
4743 error_code lcl_ec;
4744 if (exists(p, lcl_ec) && !lcl_ec && is_directory(p, lcl_ec) && !lcl_ec)
4745 break;
4746 p.clear();
4747 }
4748 }
4749
4750 if (p.empty())
4751 {
4752 // use a separate buffer since in C++03 a string is not required to be contiguous
4753 const UINT size = ::GetWindowsDirectoryW(nullptr, 0);
4754 if (BOOST_UNLIKELY(size == 0))
4755 {
4756 getwindir_error:
4757 int errval = ::GetLastError();
4758 error(errval, ec, "boost::filesystem::temp_directory_path");
4759 return path();
4760 }
4761
4762 std::unique_ptr< wchar_t[] > buf(new wchar_t[size]);
4763 if (BOOST_UNLIKELY(::GetWindowsDirectoryW(buf.get(), size) == 0))
4764 goto getwindir_error;
4765
4766 p = buf.get(); // do not depend on initial buf size, see ticket #10388
4767 path_algorithms::append_v4(p, temp_dir, temp_dir + (sizeof(temp_dir) / sizeof(*temp_dir) - 1u));
4768 }
4769
4770 return p;
4771
4772#endif
4773}
4774
4775BOOST_FILESYSTEM_DECL
4776path system_complete(path const& p, system::error_code* ec)
4777{
4778#ifdef BOOST_POSIX_API
4779
4780 if (p.empty() || p.is_absolute())
4781 return p;
4782
4783 path res(current_path());
4784 path_algorithms::append_v4(left&: res, right: p);
4785 return res;
4786
4787#else
4788
4789 if (p.empty())
4790 {
4791 if (ec)
4792 ec->clear();
4793 return p;
4794 }
4795
4796 BOOST_CONSTEXPR_OR_CONST std::size_t buf_size = 128u;
4797 wchar_t buf[buf_size];
4798 wchar_t* pfn;
4799 std::size_t len = get_full_path_name(p, buf_size, buf, &pfn);
4800
4801 if (error(len == 0 ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::system_complete"))
4802 return path();
4803
4804 if (len < buf_size) // len does not include null termination character
4805 return path(&buf[0]);
4806
4807 std::unique_ptr< wchar_t[] > big_buf(new wchar_t[len]);
4808
4809 return error(get_full_path_name(p, len, big_buf.get(), &pfn) == 0 ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::system_complete") ? path() : path(big_buf.get());
4810
4811#endif
4812}
4813
4814BOOST_FILESYSTEM_DECL
4815path weakly_canonical_v3(path const& p, path const& base, system::error_code* ec)
4816{
4817 system::error_code local_ec;
4818 const path::iterator p_end(p.end());
4819
4820#if defined(BOOST_POSIX_API)
4821
4822 path::iterator itr(p_end);
4823 path head(p);
4824 for (; !head.empty(); path_algorithms::decrement_v4(it&: itr))
4825 {
4826 file_status head_status(detail::status_impl(p: head, ec: &local_ec));
4827 if (BOOST_UNLIKELY(head_status.type() == fs::status_error))
4828 {
4829 if (!ec)
4830 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
4831
4832 *ec = local_ec;
4833 return path();
4834 }
4835
4836 if (head_status.type() != fs::file_not_found)
4837 break;
4838
4839 head.remove_filename_and_trailing_separators();
4840 }
4841
4842 path const& dot_p = dot_path();
4843 path const& dot_dot_p = dot_dot_path();
4844
4845#else
4846
4847 path const& dot_p = dot_path();
4848 path const& dot_dot_p = dot_dot_path();
4849
4850 // On Windows, filesystem APIs such as GetFileAttributesW and CreateFileW perform lexical path normalization
4851 // internally. As a result, a path like "c:\a\.." can be reported as present even if "c:\a" is not. This would
4852 // break canonical, as symlink_status that it calls internally would report an error that the file at the
4853 // intermediate path does not exist. To avoid this, scan the initial path in the forward direction.
4854 // Also, operate on paths with preferred separators. This can be important on Windows since GetFileAttributesW
4855 // or CreateFileW, which is called in status() may return "file not found" for paths to network shares and
4856 // mounted cloud storages that have forward slashes as separators.
4857 // Also, avoid querying status of the root name such as \\?\c: as CreateFileW returns ERROR_INVALID_FUNCTION for
4858 // such path. Querying the status of a root name such as c: is also not right as this path refers to the current
4859 // directory on drive C:, which is not what we want to test for existence anyway.
4860 path::iterator itr(p.begin());
4861 path head;
4862 if (p.has_root_name())
4863 {
4864 BOOST_ASSERT(itr != p_end);
4865 head = *itr;
4866 path_algorithms::increment_v4(itr);
4867 }
4868
4869 if (p.has_root_directory())
4870 {
4871 BOOST_ASSERT(itr != p_end);
4872 // Convert generic separator returned by the iterator for the root directory to
4873 // the preferred separator.
4874 head += path::preferred_separator;
4875 path_algorithms::increment_v4(itr);
4876 }
4877
4878 if (!head.empty())
4879 {
4880 file_status head_status(detail::status_impl(head, &local_ec));
4881 if (BOOST_UNLIKELY(head_status.type() == fs::status_error))
4882 {
4883 if (!ec)
4884 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
4885
4886 *ec = local_ec;
4887 return path();
4888 }
4889
4890 if (head_status.type() == fs::file_not_found)
4891 {
4892 // If the root path does not exist then no path element exists
4893 itr = p.begin();
4894 head.clear();
4895 goto skip_head;
4896 }
4897 }
4898
4899 for (; itr != p_end; path_algorithms::increment_v4(itr))
4900 {
4901 path const& p_elem = *itr;
4902
4903 // Avoid querying status of paths containing dot and dot-dot elements, as this will break
4904 // if the root name starts with "\\?\".
4905 if (path_algorithms::compare_v4(p_elem, dot_p) == 0)
4906 continue;
4907
4908 if (path_algorithms::compare_v4(p_elem, dot_dot_p) == 0)
4909 {
4910 if (head.has_relative_path())
4911 head.remove_filename_and_trailing_separators();
4912
4913 continue;
4914 }
4915
4916 path_algorithms::append_v4(head, p_elem);
4917
4918 file_status head_status(detail::status_impl(head, &local_ec));
4919 if (BOOST_UNLIKELY(head_status.type() == fs::status_error))
4920 {
4921 if (!ec)
4922 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
4923
4924 *ec = local_ec;
4925 return path();
4926 }
4927
4928 if (head_status.type() == fs::file_not_found)
4929 {
4930 head.remove_filename_and_trailing_separators();
4931 break;
4932 }
4933 }
4934
4935skip_head:;
4936
4937#endif
4938
4939 path tail;
4940 bool tail_has_dots = false;
4941 for (; itr != p_end; path_algorithms::increment_v4(it&: itr))
4942 {
4943 path const& tail_elem = *itr;
4944 path_algorithms::append_v4(left&: tail, right: tail_elem);
4945 // for a later optimization, track if any dot or dot-dot elements are present
4946 if (!tail_has_dots && (path_algorithms::compare_v4(left: tail_elem, right: dot_p) == 0 || path_algorithms::compare_v4(left: tail_elem, right: dot_dot_p) == 0))
4947 tail_has_dots = true;
4948 }
4949
4950 head = detail::canonical_v3(p: head, base, ec: &local_ec);
4951 if (BOOST_UNLIKELY(!!local_ec))
4952 {
4953 if (!ec)
4954 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
4955
4956 *ec = local_ec;
4957 return path();
4958 }
4959
4960 if (BOOST_LIKELY(!tail.empty()))
4961 {
4962 path_algorithms::append_v4(left&: head, right: tail);
4963
4964 // optimization: only normalize if tail had dot or dot-dot element
4965 if (tail_has_dots)
4966 return path_algorithms::lexically_normal_v4(p: head);
4967 }
4968
4969 return head;
4970}
4971
4972BOOST_FILESYSTEM_DECL
4973path weakly_canonical_v4(path const& p, path const& base, system::error_code* ec)
4974{
4975 system::error_code local_ec;
4976 const path::iterator p_end(p.end());
4977
4978#if defined(BOOST_POSIX_API)
4979
4980 path::iterator itr(p_end);
4981 path head(p);
4982 for (; !head.empty(); path_algorithms::decrement_v4(it&: itr))
4983 {
4984 file_status head_status(detail::status_impl(p: head, ec: &local_ec));
4985 if (BOOST_UNLIKELY(head_status.type() == fs::status_error))
4986 {
4987 if (!ec)
4988 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
4989
4990 *ec = local_ec;
4991 return path();
4992 }
4993
4994 if (head_status.type() != fs::file_not_found)
4995 break;
4996
4997 head.remove_filename_and_trailing_separators();
4998 }
4999
5000 path const& dot_p = dot_path();
5001 path const& dot_dot_p = dot_dot_path();
5002
5003#else
5004
5005 path const& dot_p = dot_path();
5006 path const& dot_dot_p = dot_dot_path();
5007
5008 // On Windows, filesystem APIs such as GetFileAttributesW and CreateFileW perform lexical path normalization
5009 // internally. As a result, a path like "c:\a\.." can be reported as present even if "c:\a" is not. This would
5010 // break canonical, as symlink_status that it calls internally would report an error that the file at the
5011 // intermediate path does not exist. To avoid this, scan the initial path in the forward direction.
5012 // Also, operate on paths with preferred separators. This can be important on Windows since GetFileAttributesW
5013 // or CreateFileW, which is called in status() may return "file not found" for paths to network shares and
5014 // mounted cloud storages that have forward slashes as separators.
5015 // Also, avoid querying status of the root name such as \\?\c: as CreateFileW returns ERROR_INVALID_FUNCTION for
5016 // such path. Querying the status of a root name such as c: is also not right as this path refers to the current
5017 // directory on drive C:, which is not what we want to test for existence anyway.
5018 path::iterator itr(p.begin());
5019 path head;
5020 if (p.has_root_name())
5021 {
5022 BOOST_ASSERT(itr != p_end);
5023 head = *itr;
5024 path_algorithms::increment_v4(itr);
5025 }
5026
5027 if (p.has_root_directory())
5028 {
5029 BOOST_ASSERT(itr != p_end);
5030 // Convert generic separator returned by the iterator for the root directory to
5031 // the preferred separator.
5032 head += path::preferred_separator;
5033 path_algorithms::increment_v4(itr);
5034 }
5035
5036 if (!head.empty())
5037 {
5038 file_status head_status(detail::status_impl(head, &local_ec));
5039 if (BOOST_UNLIKELY(head_status.type() == fs::status_error))
5040 {
5041 if (!ec)
5042 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
5043
5044 *ec = local_ec;
5045 return path();
5046 }
5047
5048 if (head_status.type() == fs::file_not_found)
5049 {
5050 // If the root path does not exist then no path element exists
5051 itr = p.begin();
5052 head.clear();
5053 goto skip_head;
5054 }
5055 }
5056
5057 for (; itr != p_end; path_algorithms::increment_v4(itr))
5058 {
5059 path const& p_elem = *itr;
5060
5061 // Avoid querying status of paths containing dot and dot-dot elements, as this will break
5062 // if the root name starts with "\\?\".
5063 if (path_algorithms::compare_v4(p_elem, dot_p) == 0)
5064 continue;
5065
5066 if (path_algorithms::compare_v4(p_elem, dot_dot_p) == 0)
5067 {
5068 if (head.has_relative_path())
5069 head.remove_filename_and_trailing_separators();
5070
5071 continue;
5072 }
5073
5074 path_algorithms::append_v4(head, p_elem);
5075
5076 file_status head_status(detail::status_impl(head, &local_ec));
5077 if (BOOST_UNLIKELY(head_status.type() == fs::status_error))
5078 {
5079 if (!ec)
5080 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
5081
5082 *ec = local_ec;
5083 return path();
5084 }
5085
5086 if (head_status.type() == fs::file_not_found)
5087 {
5088 head.remove_filename_and_trailing_separators();
5089 break;
5090 }
5091 }
5092
5093skip_head:;
5094
5095#endif
5096
5097 path tail;
5098 bool tail_has_dots = false;
5099 for (; itr != p_end; path_algorithms::increment_v4(it&: itr))
5100 {
5101 path const& tail_elem = *itr;
5102 path_algorithms::append_v4(left&: tail, right: tail_elem);
5103 // for a later optimization, track if any dot or dot-dot elements are present
5104 if (!tail_has_dots && (path_algorithms::compare_v4(left: tail_elem, right: dot_p) == 0 || path_algorithms::compare_v4(left: tail_elem, right: dot_dot_p) == 0))
5105 tail_has_dots = true;
5106 }
5107
5108 head = detail::canonical_v4(p: head, base, ec: &local_ec);
5109 if (BOOST_UNLIKELY(!!local_ec))
5110 {
5111 if (!ec)
5112 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
5113
5114 *ec = local_ec;
5115 return path();
5116 }
5117
5118 if (BOOST_LIKELY(!tail.empty()))
5119 {
5120 path_algorithms::append_v4(left&: head, right: tail);
5121
5122 // optimization: only normalize if tail had dot or dot-dot element
5123 if (tail_has_dots)
5124 return path_algorithms::lexically_normal_v4(p: head);
5125 }
5126
5127 return head;
5128}
5129
5130} // namespace detail
5131} // namespace filesystem
5132} // namespace boost
5133
5134#include <boost/filesystem/detail/footer.hpp>
5135

source code of boost/libs/filesystem/src/operations.cpp