]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/filesystem/src/operations.cpp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / libs / filesystem / src / operations.cpp
CommitLineData
7c673cae
FG
1// operations.cpp --------------------------------------------------------------------//
2
3// Copyright 2002-2009, 2014 Beman Dawes
4// Copyright 2001 Dietmar Kuehl
20effc67 5// Copyright 2018-2020 Andrey Semashev
7c673cae
FG
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
92f5a8d4 12//--------------------------------------------------------------------------------------//
7c673cae 13
20effc67 14#include "platform_config.hpp"
92f5a8d4 15
20effc67 16#include <boost/predef/os/bsd/open.h>
7c673cae 17#include <boost/filesystem/operations.hpp>
92f5a8d4
TL
18#include <boost/filesystem/file_status.hpp>
19#include <boost/filesystem/exception.hpp>
20#include <boost/filesystem/directory.hpp>
21#include <boost/system/error_code.hpp>
20effc67 22#include <boost/smart_ptr/scoped_ptr.hpp>
92f5a8d4 23#include <boost/smart_ptr/scoped_array.hpp>
7c673cae 24#include <boost/detail/workaround.hpp>
92f5a8d4
TL
25#include <boost/cstdint.hpp>
26#include <boost/assert.hpp>
20effc67 27#include <new> // std::bad_alloc, std::nothrow
92f5a8d4
TL
28#include <limits>
29#include <string>
30#include <cstddef>
7c673cae
FG
31#include <cstdlib> // for malloc, free
32#include <cstring>
33#include <cstdio> // for remove, rename
92f5a8d4 34#if defined(__QNXNTO__) // see ticket #5355
7c673cae
FG
35# include <stdio.h>
36#endif
37#include <cerrno>
38
39#ifdef BOOST_FILEYSTEM_INCLUDE_IOSTREAM
40# include <iostream>
41#endif
42
7c673cae
FG
43# ifdef BOOST_POSIX_API
44
45# include <sys/types.h>
46# include <sys/stat.h>
20effc67
TL
47# if defined(__wasm)
48// WASI does not have statfs or statvfs.
49# elif !defined(__APPLE__) && (!defined(__OpenBSD__) || BOOST_OS_BSD_OPEN >= BOOST_VERSION_NUMBER(4, 4, 0)) && !defined(__ANDROID__) && !defined(__VXWORKS__)
7c673cae
FG
50# include <sys/statvfs.h>
51# define BOOST_STATVFS statvfs
52# define BOOST_STATVFS_F_FRSIZE vfs.f_frsize
53# else
54# ifdef __OpenBSD__
55# include <sys/param.h>
56# elif defined(__ANDROID__)
57# include <sys/vfs.h>
58# endif
59# if !defined(__VXWORKS__)
60# include <sys/mount.h>
61# endif
62# define BOOST_STATVFS statfs
20effc67 63# define BOOST_STATVFS_F_FRSIZE static_cast<uintmax_t>(vfs.f_bsize)
7c673cae 64# endif
7c673cae
FG
65# include <unistd.h>
66# include <fcntl.h>
92f5a8d4
TL
67# if _POSIX_C_SOURCE < 200809L
68# include <utime.h>
69# endif
20effc67
TL
70# include <limits.h>
71# if defined(linux) || defined(__linux) || defined(__linux__)
72# include <sys/utsname.h>
73# include <sys/sendfile.h>
74# include <sys/syscall.h>
75# define BOOST_FILESYSTEM_USE_SENDFILE
76# if defined(__NR_copy_file_range)
77# define BOOST_FILESYSTEM_USE_COPY_FILE_RANGE
78# endif
79# if !defined(BOOST_FILESYSTEM_HAS_STATX) && defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
80# include <linux/stat.h>
81# endif
82# endif
83
84# if defined(BOOST_FILESYSTEM_HAS_STAT_ST_MTIM)
85# define BOOST_FILESYSTEM_STAT_ST_MTIMENSEC st_mtim.tv_nsec
86# elif defined(BOOST_FILESYSTEM_HAS_STAT_ST_MTIMESPEC)
87# define BOOST_FILESYSTEM_STAT_ST_MTIMENSEC st_mtimespec.tv_nsec
88# elif defined(BOOST_FILESYSTEM_HAS_STAT_ST_MTIMENSEC)
89# define BOOST_FILESYSTEM_STAT_ST_MTIMENSEC st_mtimensec
90# endif
91
92# if defined(BOOST_FILESYSTEM_HAS_STAT_ST_BIRTHTIM)
93# define BOOST_FILESYSTEM_STAT_ST_BIRTHTIME st_birthtim.tv_sec
94# define BOOST_FILESYSTEM_STAT_ST_BIRTHTIMENSEC st_birthtim.tv_nsec
95# elif defined(BOOST_FILESYSTEM_HAS_STAT_ST_BIRTHTIMESPEC)
96# define BOOST_FILESYSTEM_STAT_ST_BIRTHTIME st_birthtimespec.tv_sec
97# define BOOST_FILESYSTEM_STAT_ST_BIRTHTIMENSEC st_birthtimespec.tv_nsec
98# elif defined(BOOST_FILESYSTEM_HAS_STAT_ST_BIRTHTIMENSEC)
99# define BOOST_FILESYSTEM_STAT_ST_BIRTHTIME st_birthtime
100# define BOOST_FILESYSTEM_STAT_ST_BIRTHTIMENSEC st_birthtimensec
101# endif
7c673cae 102
92f5a8d4 103# else // BOOST_WINDOWS_API
7c673cae 104
92f5a8d4 105# include <boost/winapi/dll.hpp> // get_proc_address, GetModuleHandleW
b32b8144 106# include <cwchar>
7c673cae
FG
107# include <io.h>
108# include <windows.h>
109# include <winnt.h>
7c673cae 110# if defined(__BORLANDC__) || defined(__MWERKS__)
20effc67 111# if defined(BOOST_BORLANDC)
7c673cae
FG
112 using std::time_t;
113# endif
114# include <utime.h>
115# else
116# include <sys/utime.h>
117# endif
118
92f5a8d4
TL
119#include "windows_tools.hpp"
120
121# endif // BOOST_WINDOWS_API
122
123#include "error_handling.hpp"
124
125namespace fs = boost::filesystem;
126using boost::filesystem::path;
127using boost::filesystem::filesystem_error;
128using boost::filesystem::perms;
129using boost::system::error_code;
130using boost::system::system_category;
131
20effc67
TL
132#if defined(BOOST_POSIX_API)
133
134// At least Mac OS X 10.6 and older doesn't support O_CLOEXEC
135#ifndef O_CLOEXEC
136#define O_CLOEXEC 0
137#endif
138
139#if defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0
140#define BOOST_FILESYSTEM_HAS_FDATASYNC
141#endif
142
143#else // defined(BOOST_POSIX_API)
92f5a8d4
TL
144
145// REPARSE_DATA_BUFFER related definitions are found in ntifs.h, which is part of the
7c673cae
FG
146// Windows Device Driver Kit. Since that's inconvenient, the definitions are provided
147// here. See http://msdn.microsoft.com/en-us/library/ms791514.aspx
148
149#if !defined(REPARSE_DATA_BUFFER_HEADER_SIZE) // mingw winnt.h does provide the defs
150
151#define SYMLINK_FLAG_RELATIVE 1
152
153typedef struct _REPARSE_DATA_BUFFER {
154 ULONG ReparseTag;
155 USHORT ReparseDataLength;
156 USHORT Reserved;
157 union {
158 struct {
159 USHORT SubstituteNameOffset;
160 USHORT SubstituteNameLength;
161 USHORT PrintNameOffset;
162 USHORT PrintNameLength;
163 ULONG Flags;
164 WCHAR PathBuffer[1];
165 /* Example of distinction between substitute and print names:
166 mklink /d ldrive c:\
167 SubstituteName: c:\\??\
168 PrintName: c:\
169 */
170 } SymbolicLinkReparseBuffer;
171 struct {
172 USHORT SubstituteNameOffset;
173 USHORT SubstituteNameLength;
174 USHORT PrintNameOffset;
175 USHORT PrintNameLength;
176 WCHAR PathBuffer[1];
92f5a8d4 177 } MountPointReparseBuffer;
7c673cae
FG
178 struct {
179 UCHAR DataBuffer[1];
180 } GenericReparseBuffer;
181 };
182} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
183
184#define REPARSE_DATA_BUFFER_HEADER_SIZE \
185 FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)
186
20effc67 187#endif // !defined(REPARSE_DATA_BUFFER_HEADER_SIZE)
7c673cae
FG
188
189#ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE
20effc67 190#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE (16 * 1024)
7c673cae
FG
191#endif
192
20effc67
TL
193#ifndef FSCTL_GET_REPARSE_POINT
194#define FSCTL_GET_REPARSE_POINT 0x900a8
195#endif
7c673cae 196
20effc67
TL
197#ifndef IO_REPARSE_TAG_SYMLINK
198#define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
199#endif
7c673cae 200
20effc67
TL
201// Fallback for MinGW/Cygwin
202#ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
203#define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1
204#endif
205
206// Our convenience type for allocating REPARSE_DATA_BUFFER along with sufficient space after it
207union reparse_data_buffer
208{
209 REPARSE_DATA_BUFFER rdb;
210 unsigned char storage[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
211};
212
213# endif // defined(BOOST_POSIX_API)
7c673cae 214
7c673cae
FG
215// POSIX/Windows macros ----------------------------------------------------//
216
217// Portions of the POSIX and Windows API's are very similar, except for name,
218// order of arguments, and meaning of zero/non-zero returns. The macros below
219// abstract away those differences. They follow Windows naming and order of
220// arguments, and return true to indicate no error occurred. [POSIX naming,
221// order of arguments, and meaning of return were followed initially, but
222// found to be less clear and cause more coding errors.]
223
224# if defined(BOOST_POSIX_API)
225
7c673cae 226# define BOOST_SET_CURRENT_DIRECTORY(P)(::chdir(P)== 0)
7c673cae
FG
227# define BOOST_CREATE_HARD_LINK(F,T)(::link(T, F)== 0)
228# define BOOST_CREATE_SYMBOLIC_LINK(F,T,Flag)(::symlink(T, F)== 0)
229# define BOOST_REMOVE_DIRECTORY(P)(::rmdir(P)== 0)
230# define BOOST_DELETE_FILE(P)(::unlink(P)== 0)
7c673cae
FG
231# define BOOST_MOVE_FILE(OLD,NEW)(::rename(OLD, NEW)== 0)
232# define BOOST_RESIZE_FILE(P,SZ)(::truncate(P, SZ)== 0)
233
7c673cae
FG
234# else // BOOST_WINDOWS_API
235
7c673cae 236# define BOOST_SET_CURRENT_DIRECTORY(P)(::SetCurrentDirectoryW(P)!= 0)
7c673cae
FG
237# define BOOST_CREATE_HARD_LINK(F,T)(create_hard_link_api(F, T, 0)!= 0)
238# define BOOST_CREATE_SYMBOLIC_LINK(F,T,Flag)(create_symbolic_link_api(F, T, Flag)!= 0)
239# define BOOST_REMOVE_DIRECTORY(P)(::RemoveDirectoryW(P)!= 0)
240# define BOOST_DELETE_FILE(P)(::DeleteFileW(P)!= 0)
7c673cae
FG
241# define BOOST_MOVE_FILE(OLD,NEW)(::MoveFileExW(OLD, NEW, MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED)!= 0)
242# define BOOST_RESIZE_FILE(P,SZ)(resize_file_api(P, SZ)!= 0)
243# define BOOST_READ_SYMLINK(P,T)
244
7c673cae
FG
245# endif
246
92f5a8d4
TL
247namespace boost {
248namespace filesystem {
249namespace detail {
250
7c673cae
FG
251//--------------------------------------------------------------------------------------//
252// //
253// helpers (all operating systems) //
254// //
255//--------------------------------------------------------------------------------------//
256
92f5a8d4 257namespace {
7c673cae 258
92f5a8d4
TL
259// Absolute maximum path length, in bytes, that we're willing to accept from various system calls.
260// This value is arbitrary, it is supposed to be a hard limit to avoid memory exhaustion
261// in some of the algorithms below in case of some corrupted or maliciously broken filesystem.
262BOOST_CONSTEXPR_OR_CONST std::size_t absolute_path_max = 16u * 1024u * 1024u;
7c673cae 263
92f5a8d4 264fs::file_type query_file_type(const path& p, error_code* ec);
7c673cae 265
92f5a8d4 266// general helpers -----------------------------------------------------------------//
7c673cae 267
92f5a8d4
TL
268bool is_empty_directory(const path& p, error_code* ec)
269{
20effc67
TL
270 fs::directory_iterator itr;
271 detail::directory_iterator_construct(itr, p, static_cast< unsigned int >(directory_options::none), ec);
272 return itr == fs::directory_iterator();
92f5a8d4 273}
7c673cae 274
92f5a8d4 275bool not_found_error(int errval) BOOST_NOEXCEPT; // forward declaration
7c673cae 276
92f5a8d4 277// only called if directory exists
20effc67 278inline bool remove_directory(const path& p) // true if succeeds or not found
92f5a8d4
TL
279{
280 return BOOST_REMOVE_DIRECTORY(p.c_str())
281 || not_found_error(BOOST_ERRNO); // mitigate possible file system race. See #11166
282}
7c673cae 283
92f5a8d4 284// only called if file exists
20effc67 285inline bool remove_file(const path& p) // true if succeeds or not found
92f5a8d4
TL
286{
287 return BOOST_DELETE_FILE(p.c_str())
288 || not_found_error(BOOST_ERRNO); // mitigate possible file system race. See #11166
289}
7c673cae 290
92f5a8d4
TL
291// called by remove and remove_all_aux
292bool remove_file_or_directory(const path& p, fs::file_type type, error_code* ec)
293 // return true if file removed, false if not removed
294{
295 if (type == fs::file_not_found)
7c673cae 296 {
92f5a8d4
TL
297 if (ec != 0) ec->clear();
298 return false;
7c673cae
FG
299 }
300
92f5a8d4
TL
301 if (type == fs::directory_file
302# ifdef BOOST_WINDOWS_API
303 || type == fs::_detail_directory_symlink
304# endif
305 )
7c673cae 306 {
92f5a8d4
TL
307 if (error(!remove_directory(p) ? BOOST_ERRNO : 0, p, ec,
308 "boost::filesystem::remove"))
309 return false;
7c673cae 310 }
92f5a8d4 311 else
7c673cae 312 {
92f5a8d4
TL
313 if (error(!remove_file(p) ? BOOST_ERRNO : 0, p, ec,
314 "boost::filesystem::remove"))
315 return false;
7c673cae 316 }
92f5a8d4
TL
317 return true;
318}
7c673cae 319
20effc67 320uintmax_t remove_all_aux(const path& p, fs::file_type type, error_code* ec)
92f5a8d4 321{
20effc67 322 uintmax_t count = 0u;
7c673cae 323
92f5a8d4 324 if (type == fs::directory_file) // but not a directory symlink
7c673cae 325 {
92f5a8d4 326 fs::directory_iterator itr;
20effc67
TL
327 fs::detail::directory_iterator_construct(itr, p, static_cast< unsigned int >(directory_options::none), ec);
328 if (ec && *ec)
329 return count;
92f5a8d4
TL
330
331 const fs::directory_iterator end_dit;
332 while(itr != end_dit)
7c673cae 333 {
92f5a8d4
TL
334 fs::file_type tmp_type = query_file_type(itr->path(), ec);
335 if (ec != 0 && *ec)
336 return count;
337
338 count += remove_all_aux(itr->path(), tmp_type, ec);
339 if (ec != 0 && *ec)
340 return count;
341
342 fs::detail::directory_iterator_increment(itr, ec);
343 if (ec != 0 && *ec)
344 return count;
7c673cae 345 }
7c673cae
FG
346 }
347
92f5a8d4
TL
348 remove_file_or_directory(p, type, ec);
349 if (ec != 0 && *ec)
7c673cae 350 return count;
92f5a8d4
TL
351
352 return ++count;
353}
7c673cae
FG
354
355#ifdef BOOST_POSIX_API
356
357//--------------------------------------------------------------------------------------//
358// //
359// POSIX-specific helpers //
360// //
361//--------------------------------------------------------------------------------------//
362
92f5a8d4 363BOOST_CONSTEXPR_OR_CONST char dot = '.';
7c673cae 364
20effc67
TL
365#if !defined(BOOST_FILESYSTEM_HAS_STATX) && defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
366//! A wrapper for the statx syscall
367inline int statx(int dirfd, const char* path, int flags, unsigned int mask, struct ::statx* stx) BOOST_NOEXCEPT
368{
369 return ::syscall(__NR_statx, dirfd, path, flags, mask, stx);
370}
371#endif // !defined(BOOST_FILESYSTEM_HAS_STATX) && defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
372
373struct fd_wrapper
374{
375 int fd;
376
377 fd_wrapper() BOOST_NOEXCEPT : fd(-1) {}
378 explicit fd_wrapper(int fd) BOOST_NOEXCEPT : fd(fd) {}
379 ~fd_wrapper() BOOST_NOEXCEPT
380 {
381 if (fd >= 0)
382 ::close(fd);
383 }
384 BOOST_DELETED_FUNCTION(fd_wrapper(fd_wrapper const&))
385 BOOST_DELETED_FUNCTION(fd_wrapper& operator= (fd_wrapper const&))
386};
387
92f5a8d4
TL
388inline bool not_found_error(int errval) BOOST_NOEXCEPT
389{
390 return errval == ENOENT || errval == ENOTDIR;
391}
7c673cae 392
20effc67
TL
393#if defined(BOOST_FILESYSTEM_HAS_STATX) || defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
394
395//! Returns \c true if the two \c statx structures refer to the same file
396inline bool equivalent_stat(struct ::statx const& s1, struct ::statx const& s2) BOOST_NOEXCEPT
92f5a8d4 397{
20effc67
TL
398 return s1.stx_dev_major == s2.stx_dev_major && s1.stx_dev_minor == s2.stx_dev_minor && s1.stx_ino == s2.stx_ino;
399}
7c673cae 400
20effc67
TL
401//! Returns file type/access mode from \c statx structure
402inline mode_t get_mode(struct ::statx const& st) BOOST_NOEXCEPT
403{
404 return st.stx_mode;
405}
7c673cae 406
20effc67
TL
407//! Returns file size from \c statx structure
408inline uintmax_t get_size(struct ::statx const& st) BOOST_NOEXCEPT
409{
410 return st.stx_size;
411}
7c673cae 412
20effc67 413#else // defined(BOOST_FILESYSTEM_HAS_STATX) || defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
7c673cae 414
20effc67
TL
415//! Returns \c true if the two \c stat structures refer to the same file
416inline bool equivalent_stat(struct ::stat const& s1, struct ::stat const& s2) BOOST_NOEXCEPT
417{
418 // According to the POSIX stat specs, "The st_ino and st_dev fields
419 // taken together uniquely identify the file within the system."
420 return s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino;
421}
422
423//! Returns file type/access mode from \c stat structure
424inline mode_t get_mode(struct ::stat const& st) BOOST_NOEXCEPT
425{
426 return st.st_mode;
427}
428
429//! Returns file size from \c stat structure
430inline uintmax_t get_size(struct ::stat const& st) BOOST_NOEXCEPT
431{
432 return st.st_size;
433}
434
435#endif // defined(BOOST_FILESYSTEM_HAS_STATX) || defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
7c673cae 436
20effc67
TL
437typedef int (copy_file_data_t)(int infile, int outfile, uintmax_t size);
438
439//! copy_file implementation that uses read/write loop
440int copy_file_data_read_write(int infile, int outfile, uintmax_t size)
441{
442 BOOST_CONSTEXPR_OR_CONST std::size_t buf_sz = 65536u;
443 boost::scoped_array<char> buf(new (std::nothrow) char[buf_sz]);
444 if (BOOST_UNLIKELY(!buf.get()))
445 return ENOMEM;
446
447 while (true)
92f5a8d4 448 {
20effc67
TL
449 ssize_t sz_read = ::read(infile, buf.get(), buf_sz);
450 if (sz_read == 0)
451 break;
452 if (BOOST_UNLIKELY(sz_read < 0))
453 {
454 int err = errno;
455 if (err == EINTR)
456 continue;
457 return err;
458 }
459
92f5a8d4
TL
460 // Allow for partial writes - see Advanced Unix Programming (2nd Ed.),
461 // Marc Rochkind, Addison-Wesley, 2004, page 94
20effc67 462 for (ssize_t sz_wrote = 0; sz_wrote < sz_read;)
7c673cae 463 {
20effc67
TL
464 ssize_t sz = ::write(outfile, buf.get() + sz_wrote, static_cast< std::size_t >(sz_read - sz_wrote));
465 if (BOOST_UNLIKELY(sz < 0))
7c673cae 466 {
20effc67
TL
467 int err = errno;
468 if (err == EINTR)
469 continue;
470 return err;
92f5a8d4 471 }
20effc67
TL
472
473 sz_wrote += sz;
474 }
475 }
476
477 return 0;
478}
479
480//! Pointer to the actual implementation of the copy_file_data implementation
481copy_file_data_t* copy_file_data = &copy_file_data_read_write;
482
483#if defined(BOOST_FILESYSTEM_USE_SENDFILE)
484
485//! copy_file implementation that uses sendfile loop. Requires sendfile to support file descriptors.
486int copy_file_data_sendfile(int infile, int outfile, uintmax_t size)
487{
488 // sendfile will not send more than this amount of data in one call
489 BOOST_CONSTEXPR_OR_CONST std::size_t max_send_size = 0x7ffff000u;
490 uintmax_t offset = 0u;
491 while (offset < size)
492 {
493 uintmax_t size_left = size - offset;
494 std::size_t size_to_copy = max_send_size;
495 if (size_left < static_cast< uintmax_t >(max_send_size))
496 size_to_copy = static_cast< std::size_t >(size_left);
497 ssize_t sz = ::sendfile(outfile, infile, NULL, size_to_copy);
498 if (BOOST_UNLIKELY(sz < 0))
499 {
500 int err = errno;
501 if (err == EINTR)
502 continue;
503 return err;
504 }
505
506 offset += sz;
507 }
508
509 return 0;
510}
511
512#endif // defined(BOOST_FILESYSTEM_USE_SENDFILE)
513
514#if defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
515
516//! copy_file implementation that uses copy_file_range loop. Requires copy_file_range to support cross-filesystem copying.
517int copy_file_data_copy_file_range(int infile, int outfile, uintmax_t size)
518{
519 // Although copy_file_range does not document any particular upper limit of one transfer, still use some upper bound to guarantee
520 // that size_t is not overflown in case if off_t is larger and the file size does not fit in size_t.
521 BOOST_CONSTEXPR_OR_CONST std::size_t max_send_size = 0x7ffff000u;
522 uintmax_t offset = 0u;
523 while (offset < size)
524 {
525 uintmax_t size_left = size - offset;
526 std::size_t size_to_copy = max_send_size;
527 if (size_left < static_cast< uintmax_t >(max_send_size))
528 size_to_copy = static_cast< std::size_t >(size_left);
529 // Note: Use syscall directly to avoid depending on libc version. copy_file_range is added in glibc 2.27.
530 // uClibc-ng does not have copy_file_range as of the time of this writing (the latest uClibc-ng release is 1.0.33).
531 loff_t sz = ::syscall(__NR_copy_file_range, infile, (loff_t*)NULL, outfile, (loff_t*)NULL, size_to_copy, (unsigned int)0u);
532 if (BOOST_UNLIKELY(sz < 0))
533 {
534 int err = errno;
535 if (err == EINTR)
536 continue;
537 return err;
538 }
539
540 offset += sz;
92f5a8d4 541 }
7c673cae 542
20effc67
TL
543 return 0;
544}
545
546#endif // defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
547
548#if defined(BOOST_FILESYSTEM_USE_SENDFILE) || defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
549
550struct copy_file_data_initializer
551{
552 copy_file_data_initializer()
553 {
554 struct ::utsname system_info;
555 if (BOOST_UNLIKELY(::uname(&system_info) < 0))
556 return;
557
558 unsigned int major = 0u, minor = 0u, patch = 0u;
559 int count = std::sscanf(system_info.release, "%u.%u.%u", &major, &minor, &patch);
560 if (BOOST_UNLIKELY(count < 3))
561 return;
562
563 copy_file_data_t* cfd = &copy_file_data_read_write;
564
565#if defined(BOOST_FILESYSTEM_USE_SENDFILE)
566 // sendfile started accepting file descriptors as the target in Linux 2.6.33
567 if (major > 2u || (major == 2u && (minor > 6u || (minor == 6u && patch >= 33u))))
568 cfd = &copy_file_data_sendfile;
569#endif
570
571#if defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
572 // Although copy_file_range appeared in Linux 4.5, it did not support cross-filesystem copying until 5.3
573 if (major > 5u || (major == 5u && minor >= 3u))
574 cfd = &copy_file_data_copy_file_range;
575#endif
7c673cae 576
20effc67
TL
577 copy_file_data = cfd;
578 }
92f5a8d4 579}
20effc67
TL
580const copy_file_data_init;
581
582#endif // defined(BOOST_FILESYSTEM_USE_SENDFILE) || defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
7c673cae 583
92f5a8d4
TL
584inline fs::file_type query_file_type(const path& p, error_code* ec)
585{
586 return fs::detail::symlink_status(p, ec).type();
587}
7c673cae
FG
588
589# else
590
591//--------------------------------------------------------------------------------------//
592// //
593// Windows-specific helpers //
594// //
595//--------------------------------------------------------------------------------------//
596
92f5a8d4 597BOOST_CONSTEXPR_OR_CONST wchar_t dot = L'.';
7c673cae 598
20effc67
TL
599// Windows CE has no environment variables
600#if !defined(UNDER_CE)
92f5a8d4
TL
601inline std::wstring wgetenv(const wchar_t* name)
602{
603 // use a separate buffer since C++03 basic_string is not required to be contiguous
604 const DWORD size = ::GetEnvironmentVariableW(name, NULL, 0);
605 if (size > 0)
7c673cae 606 {
92f5a8d4
TL
607 boost::scoped_array<wchar_t> buf(new wchar_t[size]);
608 if (BOOST_LIKELY(::GetEnvironmentVariableW(name, buf.get(), size) > 0))
609 return std::wstring(buf.get());
7c673cae 610 }
b32b8144 611
92f5a8d4
TL
612 return std::wstring();
613}
20effc67 614#endif // !defined(UNDER_CE)
b32b8144 615
92f5a8d4
TL
616inline bool not_found_error(int errval) BOOST_NOEXCEPT
617{
618 return errval == ERROR_FILE_NOT_FOUND
619 || errval == ERROR_PATH_NOT_FOUND
620 || errval == ERROR_INVALID_NAME // "tools/jam/src/:sys:stat.h", "//foo"
621 || errval == ERROR_INVALID_DRIVE // USB card reader with no card inserted
622 || errval == ERROR_NOT_READY // CD/DVD drive with no disc inserted
623 || errval == ERROR_INVALID_PARAMETER // ":sys:stat.h"
624 || errval == ERROR_BAD_PATHNAME // "//nosuch" on Win64
625 || errval == ERROR_BAD_NETPATH; // "//nosuch" on Win32
626}
7c673cae 627
92f5a8d4 628// these constants come from inspecting some Microsoft sample code
20effc67 629inline std::time_t to_time_t(const FILETIME & ft) BOOST_NOEXCEPT
92f5a8d4 630{
20effc67
TL
631 uint64_t t = (static_cast<uint64_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
632 t -= 116444736000000000ull;
633 t /= 10000000u;
92f5a8d4
TL
634 return static_cast<std::time_t>(t);
635}
7c673cae 636
20effc67 637inline void to_FILETIME(std::time_t t, FILETIME & ft) BOOST_NOEXCEPT
92f5a8d4 638{
20effc67
TL
639 uint64_t temp = t;
640 temp *= 10000000u;
641 temp += 116444736000000000ull;
92f5a8d4
TL
642 ft.dwLowDateTime = static_cast<DWORD>(temp);
643 ft.dwHighDateTime = static_cast<DWORD>(temp >> 32);
644}
7c673cae 645
92f5a8d4
TL
646// Thanks to Jeremy Maitin-Shepard for much help and for permission to
647// base the equivalent()implementation on portions of his
648// file-equivalence-win32.cpp experimental code.
7c673cae 649
92f5a8d4
TL
650struct handle_wrapper
651{
652 HANDLE handle;
20effc67
TL
653
654 handle_wrapper() BOOST_NOEXCEPT : handle(INVALID_HANDLE_VALUE) {}
655 explicit handle_wrapper(HANDLE h) BOOST_NOEXCEPT : handle(h) {}
656 ~handle_wrapper() BOOST_NOEXCEPT
7c673cae 657 {
92f5a8d4
TL
658 if (handle != INVALID_HANDLE_VALUE)
659 ::CloseHandle(handle);
7c673cae 660 }
20effc67
TL
661 BOOST_DELETED_FUNCTION(handle_wrapper(handle_wrapper const&))
662 BOOST_DELETED_FUNCTION(handle_wrapper& operator= (handle_wrapper const&))
92f5a8d4 663};
7c673cae 664
20effc67 665inline HANDLE create_file_handle(const path& p, DWORD dwDesiredAccess,
92f5a8d4
TL
666 DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes,
667 DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes,
668 HANDLE hTemplateFile)
669{
670 return ::CreateFileW(p.c_str(), dwDesiredAccess, dwShareMode,
671 lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
672 hTemplateFile);
673}
7c673cae 674
92f5a8d4
TL
675bool is_reparse_point_a_symlink(const path& p)
676{
20effc67 677 handle_wrapper h(create_file_handle(p, 0,
92f5a8d4
TL
678 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
679 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL));
680 if (h.handle == INVALID_HANDLE_VALUE)
681 return false;
7c673cae 682
20effc67 683 boost::scoped_ptr<reparse_data_buffer> buf(new reparse_data_buffer);
92f5a8d4
TL
684
685 // Query the reparse data
20effc67 686 DWORD dwRetLen = 0u;
92f5a8d4 687 BOOL result = ::DeviceIoControl(h.handle, FSCTL_GET_REPARSE_POINT, NULL, 0, buf.get(),
20effc67 688 sizeof(*buf), &dwRetLen, NULL);
92f5a8d4
TL
689 if (!result) return false;
690
20effc67 691 return buf->rdb.ReparseTag == IO_REPARSE_TAG_SYMLINK
92f5a8d4
TL
692 // Issue 9016 asked that NTFS directory junctions be recognized as directories.
693 // That is equivalent to recognizing them as symlinks, and then the normal symlink
694 // mechanism will take care of recognizing them as directories.
695 //
696 // Directory junctions are very similar to symlinks, but have some performance
697 // and other advantages over symlinks. They can be created from the command line
698 // with "mklink /j junction-name target-path".
20effc67 699 || buf->rdb.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT; // aka "directory junction" or "junction"
92f5a8d4
TL
700}
701
702inline std::size_t get_full_path_name(
703 const path& src, std::size_t len, wchar_t* buf, wchar_t** p)
704{
705 return static_cast<std::size_t>(
706 ::GetFullPathNameW(src.c_str(), static_cast<DWORD>(len), buf, p));
707}
708
709fs::file_status process_status_failure(const path& p, error_code* ec)
710{
711 int errval(::GetLastError());
712 if (ec != 0) // always report errval, even though some
713 ec->assign(errval, system_category()); // errval values are not status_errors
714
715 if (not_found_error(errval))
7c673cae 716 {
92f5a8d4 717 return fs::file_status(fs::file_not_found, fs::no_perms);
7c673cae 718 }
92f5a8d4 719 else if (errval == ERROR_SHARING_VIOLATION)
7c673cae 720 {
92f5a8d4 721 return fs::file_status(fs::type_unknown);
7c673cae 722 }
92f5a8d4
TL
723 if (ec == 0)
724 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::status",
725 p, error_code(errval, system_category())));
726 return fs::file_status(fs::status_error);
727}
7c673cae 728
92f5a8d4
TL
729// differs from symlink_status() in that directory symlinks are reported as
730// _detail_directory_symlink, as required on Windows by remove() and its helpers.
731fs::file_type query_file_type(const path& p, error_code* ec)
732{
733 DWORD attr(::GetFileAttributesW(p.c_str()));
734 if (attr == 0xFFFFFFFF)
7c673cae 735 {
92f5a8d4 736 return process_status_failure(p, ec).type();
7c673cae
FG
737 }
738
92f5a8d4
TL
739 if (ec != 0) ec->clear();
740
741 if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
7c673cae 742 {
92f5a8d4
TL
743 if (is_reparse_point_a_symlink(p))
744 return (attr & FILE_ATTRIBUTE_DIRECTORY)
745 ? fs::_detail_directory_symlink
746 : fs::symlink_file;
747 return fs::reparse_file;
7c673cae
FG
748 }
749
92f5a8d4
TL
750 return (attr & FILE_ATTRIBUTE_DIRECTORY)
751 ? fs::directory_file
752 : fs::regular_file;
753}
754
20effc67 755inline BOOL resize_file_api(const wchar_t* p, uintmax_t size)
92f5a8d4
TL
756{
757 handle_wrapper h(CreateFileW(p, GENERIC_WRITE, 0, 0, OPEN_EXISTING,
758 FILE_ATTRIBUTE_NORMAL, 0));
759 LARGE_INTEGER sz;
760 sz.QuadPart = size;
761 return h.handle != INVALID_HANDLE_VALUE
762 && ::SetFilePointerEx(h.handle, sz, 0, FILE_BEGIN)
763 && ::SetEndOfFile(h.handle);
764}
765
766// Windows kernel32.dll functions that may or may not be present
767// must be accessed through pointers
7c673cae 768
92f5a8d4
TL
769typedef BOOL (WINAPI *PtrCreateHardLinkW)(
770 /*__in*/ LPCWSTR lpFileName,
771 /*__in*/ LPCWSTR lpExistingFileName,
772 /*__reserved*/ LPSECURITY_ATTRIBUTES lpSecurityAttributes
773 );
7c673cae 774
92f5a8d4
TL
775PtrCreateHardLinkW create_hard_link_api = PtrCreateHardLinkW(
776 boost::winapi::get_proc_address(
777 boost::winapi::GetModuleHandleW(L"kernel32.dll"), "CreateHardLinkW"));
7c673cae 778
92f5a8d4
TL
779typedef BOOLEAN (WINAPI *PtrCreateSymbolicLinkW)(
780 /*__in*/ LPCWSTR lpSymlinkFileName,
781 /*__in*/ LPCWSTR lpTargetFileName,
782 /*__in*/ DWORD dwFlags
783 );
7c673cae 784
92f5a8d4
TL
785PtrCreateSymbolicLinkW create_symbolic_link_api = PtrCreateSymbolicLinkW(
786 boost::winapi::get_proc_address(
787 boost::winapi::GetModuleHandleW(L"kernel32.dll"), "CreateSymbolicLinkW"));
7c673cae
FG
788
789#endif
790
791//#ifdef BOOST_WINDOWS_API
792//
793//
794// inline bool get_free_disk_space(const std::wstring& ph,
795// PULARGE_INTEGER avail, PULARGE_INTEGER total, PULARGE_INTEGER free)
796// { return ::GetDiskFreeSpaceExW(ph.c_str(), avail, total, free)!= 0; }
797//
798//#endif
799
800} // unnamed namespace
92f5a8d4 801} // namespace detail
7c673cae
FG
802
803//--------------------------------------------------------------------------------------//
804// //
805// operations functions declared in operations.hpp //
7c673cae
FG
806// //
807//--------------------------------------------------------------------------------------//
808
20effc67
TL
809namespace detail {
810
811BOOST_FILESYSTEM_DECL bool possible_large_file_size_support()
812{
813# ifdef BOOST_POSIX_API
814 typedef struct stat struct_stat;
815 return sizeof(struct_stat().st_size) > 4;
816# else
817 return true;
818# endif
819}
820
92f5a8d4 821BOOST_FILESYSTEM_DECL
20effc67 822path absolute(const path& p, const path& base, system::error_code* ec)
7c673cae 823{
92f5a8d4
TL
824// if ( p.empty() || p.is_absolute() )
825// return p;
826// // recursively calling absolute is sub-optimal, but is simple
827// path abs_base(base.is_absolute() ? base : absolute(base));
7c673cae 828//# ifdef BOOST_WINDOWS_API
92f5a8d4
TL
829// if (p.has_root_directory())
830// return abs_base.root_name() / p;
831// // !p.has_root_directory
832// if (p.has_root_name())
833// return p.root_name()
834// / abs_base.root_directory() / abs_base.relative_path() / p.relative_path();
835// // !p.has_root_name()
7c673cae 836//# endif
92f5a8d4 837// return abs_base / p;
7c673cae 838
20effc67
TL
839 if (ec != 0)
840 ec->clear();
841
92f5a8d4 842 // recursively calling absolute is sub-optimal, but is sure and simple
20effc67
TL
843 path abs_base = base;
844 if (!base.is_absolute())
845 {
846 if (ec)
847 {
848 abs_base = absolute(base, *ec);
849 if (*ec)
850 return path();
851 }
852 else
853 {
854 abs_base = absolute(base);
855 }
856 }
7c673cae 857
92f5a8d4
TL
858 // store expensive to compute values that are needed multiple times
859 path p_root_name (p.root_name());
860 path base_root_name (abs_base.root_name());
861 path p_root_directory (p.root_directory());
7c673cae 862
92f5a8d4
TL
863 if (p.empty())
864 return abs_base;
7c673cae 865
92f5a8d4
TL
866 if (!p_root_name.empty()) // p.has_root_name()
867 {
868 if (p_root_directory.empty()) // !p.has_root_directory()
869 return p_root_name / abs_base.root_directory()
870 / abs_base.relative_path() / p.relative_path();
871 // p is absolute, so fall through to return p at end of block
7c673cae 872 }
92f5a8d4 873 else if (!p_root_directory.empty()) // p.has_root_directory()
7c673cae
FG
874 {
875# ifdef BOOST_POSIX_API
92f5a8d4
TL
876 // POSIX can have root name it it is a network path
877 if (base_root_name.empty()) // !abs_base.has_root_name()
878 return p;
7c673cae 879# endif
92f5a8d4 880 return base_root_name / p;
7c673cae 881 }
92f5a8d4 882 else
7c673cae 883 {
92f5a8d4
TL
884 return abs_base / p;
885 }
7c673cae 886
92f5a8d4
TL
887 return p; // p.is_absolute() is true
888}
7c673cae 889
92f5a8d4
TL
890BOOST_FILESYSTEM_DECL
891path canonical(const path& p, const path& base, system::error_code* ec)
892{
20effc67
TL
893 if (ec != 0)
894 ec->clear();
895
92f5a8d4 896 path result;
20effc67
TL
897 path source = p;
898 if (!p.is_absolute())
899 {
900 source = detail::absolute(p, base, ec);
901 if (ec && *ec)
902 return result;
903 }
904
905 path root(source.root_path());
92f5a8d4
TL
906
907 system::error_code local_ec;
908 file_status stat (status(source, local_ec));
909
910 if (stat.type() == fs::file_not_found)
911 {
912 if (ec == 0)
913 BOOST_FILESYSTEM_THROW(filesystem_error(
914 "boost::filesystem::canonical", source,
915 error_code(system::errc::no_such_file_or_directory, system::generic_category())));
916 ec->assign(system::errc::no_such_file_or_directory, system::generic_category());
917 return result;
918 }
919 else if (local_ec)
920 {
921 if (ec == 0)
922 BOOST_FILESYSTEM_THROW(filesystem_error(
923 "boost::filesystem::canonical", source, local_ec));
924 *ec = local_ec;
925 return result;
926 }
7c673cae 927
92f5a8d4
TL
928 bool scan = true;
929 while (scan)
930 {
931 scan = false;
932 result.clear();
933 for (path::iterator itr = source.begin(); itr != source.end(); ++itr)
7c673cae 934 {
92f5a8d4
TL
935 if (*itr == dot_path())
936 continue;
937 if (*itr == dot_dot_path())
7c673cae 938 {
92f5a8d4
TL
939 if (result != root)
940 result.remove_filename();
941 continue;
942 }
943
944 result /= *itr;
7c673cae 945
20effc67
TL
946 // If we don't have an absolute path yet then don't check symlink status.
947 // This avoids checking "C:" which is "the current directory on drive C"
948 // and hence not what we want to check/resolve here.
949 if (!result.is_absolute())
950 continue;
951
92f5a8d4
TL
952 bool is_sym (is_symlink(detail::symlink_status(result, ec)));
953 if (ec && *ec)
954 return path();
7c673cae 955
92f5a8d4
TL
956 if (is_sym)
957 {
958 path link(detail::read_symlink(result, ec));
7c673cae
FG
959 if (ec && *ec)
960 return path();
92f5a8d4 961 result.remove_filename();
7c673cae 962
92f5a8d4 963 if (link.is_absolute())
7c673cae 964 {
92f5a8d4
TL
965 for (++itr; itr != source.end(); ++itr)
966 link /= *itr;
967 source = link;
7c673cae 968 }
92f5a8d4
TL
969 else // link is relative
970 {
971 path new_source(result);
972 new_source /= link;
973 for (++itr; itr != source.end(); ++itr)
974 new_source /= *itr;
975 source = new_source;
976 }
977 scan = true; // symlink causes scan to be restarted
978 break;
7c673cae
FG
979 }
980 }
7c673cae 981 }
92f5a8d4
TL
982 BOOST_ASSERT_MSG(result.is_absolute(), "canonical() implementation error; please report");
983 return result;
984}
7c673cae 985
92f5a8d4 986BOOST_FILESYSTEM_DECL
20effc67 987void copy(const path& from, const path& to, unsigned int options, system::error_code* ec)
92f5a8d4 988{
20effc67
TL
989 BOOST_ASSERT((((options & static_cast< unsigned int >(copy_options::overwrite_existing)) != 0u) +
990 ((options & static_cast< unsigned int >(copy_options::skip_existing)) != 0u) +
991 ((options & static_cast< unsigned int >(copy_options::update_existing)) != 0u)) <= 1);
7c673cae 992
20effc67
TL
993 BOOST_ASSERT((((options & static_cast< unsigned int >(copy_options::copy_symlinks)) != 0u) +
994 ((options & static_cast< unsigned int >(copy_options::skip_symlinks)) != 0u)) <= 1);
995
996 BOOST_ASSERT((((options & static_cast< unsigned int >(copy_options::directories_only)) != 0u) +
997 ((options & static_cast< unsigned int >(copy_options::create_symlinks)) != 0u) +
998 ((options & static_cast< unsigned int >(copy_options::create_hard_links)) != 0u)) <= 1);
999
1000 file_status from_stat;
1001 if ((options & (static_cast< unsigned int >(copy_options::copy_symlinks) |
1002 static_cast< unsigned int >(copy_options::skip_symlinks) |
1003 static_cast< unsigned int >(copy_options::create_symlinks))) != 0u)
7c673cae 1004 {
20effc67 1005 from_stat = detail::symlink_status(from, ec);
7c673cae 1006 }
92f5a8d4 1007 else
7c673cae 1008 {
20effc67 1009 from_stat = detail::status(from, ec);
92f5a8d4 1010 }
7c673cae 1011
20effc67
TL
1012 if (ec && *ec)
1013 return;
7c673cae 1014
20effc67
TL
1015 if (!exists(from_stat))
1016 {
1017 emit_error(BOOST_ERROR_FILE_NOT_FOUND, from, to, ec, "boost::filesystem::copy");
1018 return;
1019 }
7c673cae 1020
20effc67
TL
1021 if (is_symlink(from_stat))
1022 {
1023 if ((options & static_cast< unsigned int >(copy_options::skip_symlinks)) != 0u)
1024 return;
7c673cae 1025
20effc67
TL
1026 if ((options & static_cast< unsigned int >(copy_options::copy_symlinks)) == 0u)
1027 goto fail;
7c673cae 1028
20effc67 1029 detail::copy_symlink(from, to, ec);
7c673cae 1030 }
20effc67 1031 else if (is_regular_file(from_stat))
7c673cae 1032 {
20effc67
TL
1033 if ((options & static_cast< unsigned int >(copy_options::directories_only)) != 0u)
1034 return;
92f5a8d4 1035
20effc67 1036 if ((options & static_cast< unsigned int >(copy_options::create_symlinks)) != 0u)
7c673cae 1037 {
20effc67
TL
1038 const path* pfrom = &from;
1039 path relative_from;
1040 if (!from.is_absolute())
92f5a8d4 1041 {
20effc67
TL
1042 // Try to generate a relative path from the target location to the original file
1043 path cur_dir = detail::current_path(ec);
1044 if (ec && *ec)
1045 return;
1046 path abs_from = detail::absolute(from.parent_path(), cur_dir, ec);
1047 if (ec && *ec)
1048 return;
1049 path abs_to = to.parent_path();
1050 if (!abs_to.is_absolute())
1051 {
1052 abs_to = detail::absolute(abs_to, cur_dir, ec);
1053 if (ec && *ec)
1054 return;
1055 }
1056 relative_from = detail::relative(abs_from, abs_to, ec);
1057 if (ec && *ec)
1058 return;
1059 if (relative_from != dot_path())
1060 relative_from /= from.filename();
92f5a8d4 1061 else
20effc67
TL
1062 relative_from = from.filename();
1063 pfrom = &relative_from;
92f5a8d4 1064 }
20effc67
TL
1065 detail::create_symlink(*pfrom, to, ec);
1066 return;
7c673cae
FG
1067 }
1068
20effc67
TL
1069 if ((options & static_cast< unsigned int >(copy_options::create_hard_links)) != 0u)
1070 {
1071 detail::create_hard_link(from, to, ec);
1072 return;
1073 }
b32b8144 1074
20effc67
TL
1075 error_code local_ec;
1076 file_status to_stat;
1077 if ((options & (static_cast< unsigned int >(copy_options::skip_symlinks) |
1078 static_cast< unsigned int >(copy_options::create_symlinks))) != 0u)
1079 {
1080 to_stat = detail::symlink_status(to, &local_ec);
1081 }
1082 else
1083 {
1084 to_stat = detail::status(to, &local_ec);
1085 }
1086
1087 // Note: local_ec may be set by (symlink_)status() even in some non-fatal situations, e.g. when the file does not exist.
1088 // OTOH, when it returns status_error, then a real error have happened and it must have set local_ec.
1089 if (to_stat.type() == fs::status_error)
1090 {
1091 if (!ec)
1092 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::copy", from, to, local_ec));
1093 *ec = local_ec;
1094 return;
1095 }
1096
1097 if (is_directory(to_stat))
1098 detail::copy_file(from, to / from.filename(), options, ec);
1099 else
1100 detail::copy_file(from, to, options, ec);
92f5a8d4 1101 }
20effc67
TL
1102 else if (is_directory(from_stat))
1103 {
1104 error_code local_ec;
1105 if ((options & static_cast< unsigned int >(copy_options::create_symlinks)) != 0u)
1106 {
1107 local_ec = make_error_code(system::errc::is_a_directory);
1108 if (!ec)
1109 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::copy", from, to, local_ec));
1110 *ec = local_ec;
1111 return;
1112 }
7c673cae 1113
20effc67
TL
1114 file_status to_stat;
1115 if ((options & (static_cast< unsigned int >(copy_options::skip_symlinks) |
1116 static_cast< unsigned int >(copy_options::create_symlinks))) != 0u)
1117 {
1118 to_stat = detail::symlink_status(to, &local_ec);
1119 }
1120 else
1121 {
1122 to_stat = detail::status(to, &local_ec);
1123 }
b32b8144 1124
20effc67
TL
1125 // Note: ec may be set by (symlink_)status() even in some non-fatal situations, e.g. when the file does not exist.
1126 // OTOH, when it returns status_error, then a real error have happened and it must have set local_ec.
1127 if (to_stat.type() == fs::status_error)
1128 {
1129 if (!ec)
1130 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::copy", from, to, local_ec));
1131 *ec = local_ec;
1132 return;
1133 }
1134
1135 if (!exists(to_stat))
1136 {
1137 detail::create_directory(to, &from, ec);
1138 if (ec && *ec)
1139 return;
1140 }
1141
1142 if ((options & static_cast< unsigned int >(copy_options::recursive)) != 0u || options == 0u)
1143 {
1144 fs::directory_iterator itr;
1145 detail::directory_iterator_construct(itr, from, static_cast< unsigned int >(directory_options::none), ec);
1146 if (ec && *ec)
1147 return;
1148
1149 const fs::directory_iterator end_dit;
1150 while (itr != end_dit)
1151 {
1152 path const& p = itr->path();
1153 // 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
1154 detail::copy(p, to / p.filename(), options | static_cast< unsigned int >(copy_options::_detail_recursing), ec);
1155 if (ec && *ec)
1156 return;
1157
1158 detail::directory_iterator_increment(itr, ec);
1159 if (ec && *ec)
1160 return;
1161 }
1162 }
1163 }
1164 else
92f5a8d4 1165 {
20effc67
TL
1166 fail:
1167 emit_error(BOOST_ERROR_NOT_SUPPORTED, from, to, ec, "boost::filesystem::copy");
1168 }
1169}
1170
1171BOOST_FILESYSTEM_DECL
1172bool copy_file(const path& from, const path& to, unsigned int options, error_code* ec)
1173{
1174 BOOST_ASSERT((((options & static_cast< unsigned int >(copy_options::overwrite_existing)) != 0u) +
1175 ((options & static_cast< unsigned int >(copy_options::skip_existing)) != 0u) +
1176 ((options & static_cast< unsigned int >(copy_options::update_existing)) != 0u)) <= 1);
1177
1178 if (ec)
1179 ec->clear();
1180
1181#if defined(BOOST_POSIX_API)
1182
1183 int err = 0;
1184
1185 // Note: Declare fd_wrappers here so that errno is not clobbered by close() that may be called in fd_wrapper destructors
1186 fd_wrapper infile, outfile;
1187
1188 while (true)
1189 {
1190 infile.fd = ::open(from.c_str(), O_RDONLY | O_CLOEXEC);
1191 if (BOOST_UNLIKELY(infile.fd < 0))
1192 {
1193 err = errno;
1194 if (err == EINTR)
1195 continue;
1196
1197 fail:
1198 emit_error(err, from, to, ec, "boost::filesystem::copy_file");
1199 return false;
1200 }
1201
1202 break;
1203 }
1204
1205#if defined(BOOST_FILESYSTEM_HAS_STATX) || defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
1206 unsigned int statx_data_mask = STATX_TYPE | STATX_MODE | STATX_INO | STATX_SIZE;
1207 if ((options & static_cast< unsigned int >(copy_options::update_existing)) != 0u)
1208 statx_data_mask |= STATX_MTIME;
1209
1210 struct ::statx from_stat;
1211 if (BOOST_UNLIKELY(statx(infile.fd, "", AT_EMPTY_PATH | AT_NO_AUTOMOUNT, statx_data_mask, &from_stat) < 0))
1212 {
1213 fail_errno:
1214 err = errno;
1215 goto fail;
1216 }
1217
1218 if (BOOST_UNLIKELY((from_stat.stx_mask & statx_data_mask) != statx_data_mask))
1219 {
1220 err = ENOSYS;
1221 goto fail;
1222 }
1223#else
1224 struct ::stat from_stat;
1225 if (BOOST_UNLIKELY(::fstat(infile.fd, &from_stat) != 0))
1226 {
1227 fail_errno:
1228 err = errno;
1229 goto fail;
1230 }
1231#endif
1232
1233 const mode_t from_mode = get_mode(from_stat);
1234 if (BOOST_UNLIKELY(!S_ISREG(from_mode)))
1235 {
1236 err = ENOSYS;
1237 goto fail;
1238 }
1239
1240 // Enable writing for the newly created files. Having write permission set is important e.g. for NFS,
1241 // which checks the file permission on the server, even if the client's file descriptor supports writing.
1242 mode_t to_mode = from_mode | S_IWUSR;
1243 int oflag = O_WRONLY | O_CLOEXEC;
1244
1245 if ((options & static_cast< unsigned int >(copy_options::update_existing)) != 0u)
1246 {
1247 // Try opening the existing file without truncation to test the modification time later
1248 while (true)
1249 {
1250 outfile.fd = ::open(to.c_str(), oflag, to_mode);
1251 if (outfile.fd < 0)
1252 {
1253 err = errno;
1254 if (err == EINTR)
1255 continue;
1256
1257 if (err == ENOENT)
1258 goto create_outfile;
1259
1260 goto fail;
1261 }
1262
1263 break;
1264 }
1265 }
1266 else
1267 {
1268 create_outfile:
1269 oflag |= O_CREAT | O_TRUNC;
1270 if (((options & static_cast< unsigned int >(copy_options::overwrite_existing)) == 0u ||
1271 (options & static_cast< unsigned int >(copy_options::skip_existing)) != 0u) &&
1272 (options & static_cast< unsigned int >(copy_options::update_existing)) == 0u)
1273 {
1274 oflag |= O_EXCL;
1275 }
1276
1277 while (true)
1278 {
1279 outfile.fd = ::open(to.c_str(), oflag, to_mode);
1280 if (outfile.fd < 0)
1281 {
1282 err = errno;
1283 if (err == EINTR)
1284 continue;
1285
1286 if (err == EEXIST && (options & static_cast< unsigned int >(copy_options::skip_existing)) != 0u)
1287 return false;
1288
1289 goto fail;
1290 }
1291
1292 break;
1293 }
1294 }
1295
1296#if defined(BOOST_FILESYSTEM_HAS_STATX) || defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
1297 statx_data_mask = STATX_TYPE | STATX_MODE | STATX_INO;
1298 if ((oflag & O_TRUNC) == 0)
1299 {
1300 // O_TRUNC is not set if copy_options::update_existing is set and an existing file was opened.
1301 statx_data_mask |= STATX_MTIME;
1302 }
1303
1304 struct ::statx to_stat;
1305 if (BOOST_UNLIKELY(statx(outfile.fd, "", AT_EMPTY_PATH | AT_NO_AUTOMOUNT, statx_data_mask, &to_stat) < 0))
1306 goto fail_errno;
1307
1308 if (BOOST_UNLIKELY((to_stat.stx_mask & statx_data_mask) != statx_data_mask))
1309 {
1310 err = ENOSYS;
1311 goto fail;
1312 }
1313#else
1314 struct ::stat to_stat;
1315 if (BOOST_UNLIKELY(::fstat(outfile.fd, &to_stat) != 0))
1316 goto fail_errno;
1317#endif
1318
1319 to_mode = get_mode(to_stat);
1320 if (BOOST_UNLIKELY(!S_ISREG(to_mode)))
1321 {
1322 err = ENOSYS;
1323 goto fail;
1324 }
1325
1326 if (BOOST_UNLIKELY(detail::equivalent_stat(from_stat, to_stat)))
1327 {
1328 err = EEXIST;
1329 goto fail;
1330 }
1331
1332 if ((oflag & O_TRUNC) == 0)
1333 {
1334 // O_TRUNC is not set if copy_options::update_existing is set and an existing file was opened.
1335 // We need to check the last write times.
1336#if defined(BOOST_FILESYSTEM_HAS_STATX) || defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
1337 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))
1338 return false;
1339#elif defined(BOOST_FILESYSTEM_STAT_ST_MTIMENSEC)
1340 // Modify time is available with nanosecond precision.
1341 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))
1342 return false;
1343#else
1344 if (from_stat.st_mtime <= to_stat.st_mtime)
1345 return false;
1346#endif
1347
1348 if (BOOST_UNLIKELY(::ftruncate(outfile.fd, 0) != 0))
1349 goto fail_errno;
1350 }
1351
1352 err = detail::copy_file_data(infile.fd, outfile.fd, get_size(from_stat));
1353 if (BOOST_UNLIKELY(err != 0))
1354 goto fail; // err already contains the error code
1355
1356 // If we created a new file with an explicitly added S_IWUSR permission,
1357 // we may need to update its mode bits to match the source file.
1358 if (to_mode != from_mode)
1359 {
1360 if (BOOST_UNLIKELY(::fchmod(outfile.fd, from_mode) != 0))
1361 goto fail_errno;
1362 }
1363
1364 // Note: Use fsync/fdatasync followed by close to avoid dealing with the possibility of close failing with EINTR.
1365 // Even if close fails, including with EINTR, most operating systems (presumably, except HP-UX) will close the
1366 // file descriptor upon its return. This means that if an error happens later, when the OS flushes data to the
1367 // underlying media, this error will go unnoticed and we have no way to receive it from close. Calling fsync/fdatasync
1368 // ensures that all data have been written, and even if close fails for some unfathomable reason, we don't really
1369 // care at that point.
1370#if defined(BOOST_FILESYSTEM_HAS_FDATASYNC)
1371 err = ::fdatasync(outfile.fd);
1372#else
1373 err = ::fsync(outfile.fd);
1374#endif
1375 if (BOOST_UNLIKELY(err != 0))
1376 goto fail_errno;
1377
1378 return true;
1379
1380#else // defined(BOOST_POSIX_API)
1381
1382 bool fail_if_exists = (options & static_cast< unsigned int >(copy_options::overwrite_existing)) == 0u ||
1383 (options & static_cast< unsigned int >(copy_options::skip_existing)) != 0u;
1384
1385 if ((options & static_cast< unsigned int >(copy_options::update_existing)) != 0u)
1386 {
1387 // Create handle_wrappers here so that CloseHandle calls don't clobber error code returned by GetLastError
1388 handle_wrapper hw_from, hw_to;
1389
1390 hw_from.handle = create_file_handle(from.c_str(), 0,
1391 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
1392 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
1393
1394 FILETIME lwt_from;
1395 if (hw_from.handle == INVALID_HANDLE_VALUE)
1396 {
1397 fail_last_error:
1398 DWORD err = ::GetLastError();
1399 emit_error(err, from, to, ec, "boost::filesystem::copy_file");
1400 return false;
1401 }
1402
1403 if (!::GetFileTime(hw_from.handle, 0, 0, &lwt_from))
1404 goto fail_last_error;
1405
1406 hw_to.handle = create_file_handle(to.c_str(), 0,
1407 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
1408 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
1409
1410 if (hw_to.handle != INVALID_HANDLE_VALUE)
1411 {
1412 FILETIME lwt_to;
1413 if (!::GetFileTime(hw_to.handle, 0, 0, &lwt_to))
1414 goto fail_last_error;
1415
1416 ULONGLONG tfrom = (static_cast< ULONGLONG >(lwt_from.dwHighDateTime) << 32) | static_cast< ULONGLONG >(lwt_from.dwLowDateTime);
1417 ULONGLONG tto = (static_cast< ULONGLONG >(lwt_to.dwHighDateTime) << 32) | static_cast< ULONGLONG >(lwt_to.dwLowDateTime);
1418 if (tfrom <= tto)
1419 return false;
1420 }
1421
1422 fail_if_exists = false;
1423 }
1424
1425 BOOL res = ::CopyFileW(from.c_str(), to.c_str(), fail_if_exists);
1426 if (!res)
1427 {
1428 DWORD err = ::GetLastError();
1429 if ((err == ERROR_FILE_EXISTS || err == ERROR_ALREADY_EXISTS) && (options & static_cast< unsigned int >(copy_options::skip_existing)) != 0u)
1430 return false;
1431 emit_error(err, from, to, ec, "boost::filesystem::copy_file");
1432 return false;
1433 }
1434
1435 return true;
1436
1437#endif // defined(BOOST_POSIX_API)
1438}
1439
1440BOOST_FILESYSTEM_DECL
1441void copy_symlink(const path& existing_symlink, const path& new_symlink, system::error_code* ec)
1442{
1443 path p(read_symlink(existing_symlink, ec));
1444 if (ec && *ec)
1445 return;
1446 create_symlink(p, new_symlink, ec);
1447}
1448
1449BOOST_FILESYSTEM_DECL
1450bool create_directories(const path& p, system::error_code* ec)
1451{
1452 if (p.empty())
1453 {
1454 if (!ec)
1455 {
1456 BOOST_FILESYSTEM_THROW(filesystem_error(
1457 "boost::filesystem::create_directories", p,
1458 system::errc::make_error_code(system::errc::invalid_argument)));
1459 }
1460 ec->assign(system::errc::invalid_argument, system::generic_category());
1461 return false;
1462 }
1463
1464 if (p.filename_is_dot() || p.filename_is_dot_dot())
1465 return create_directories(p.parent_path(), ec);
1466
1467 error_code local_ec;
1468 file_status p_status = detail::status(p, &local_ec);
1469
1470 if (p_status.type() == directory_file)
1471 {
1472 if (ec)
92f5a8d4 1473 ec->clear();
7c673cae
FG
1474 return false;
1475 }
1476
20effc67
TL
1477 path parent = p.parent_path();
1478 BOOST_ASSERT_MSG(parent != p, "internal error: p == p.parent_path()");
1479 if (!parent.empty())
1480 {
1481 // determine if the parent exists
1482 file_status parent_status = detail::status(parent, &local_ec);
1483
1484 // if the parent does not exist, create the parent
1485 if (parent_status.type() == file_not_found)
1486 {
1487 create_directories(parent, local_ec);
1488 if (local_ec)
1489 {
1490 if (!ec)
1491 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::create_directories", parent, local_ec));
1492 *ec = local_ec;
1493 return false;
1494 }
1495 }
1496 }
1497
1498 // create the directory
1499 return create_directory(p, NULL, ec);
1500}
1501
1502BOOST_FILESYSTEM_DECL
1503bool create_directory(const path& p, const path* existing, error_code* ec)
1504{
1505 if (ec)
1506 ec->clear();
1507
1508#if defined(BOOST_POSIX_API)
1509
1510 mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO;
1511 if (existing)
1512 {
1513#if defined(BOOST_FILESYSTEM_HAS_STATX) || defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
1514 struct ::statx existing_stat;
1515 if (BOOST_UNLIKELY(statx(AT_FDCWD, existing->c_str(), AT_NO_AUTOMOUNT, STATX_TYPE | STATX_MODE, &existing_stat) < 0))
1516 {
1517 emit_error(errno, p, *existing, ec, "boost::filesystem::create_directory");
1518 return false;
1519 }
1520
1521 if (BOOST_UNLIKELY((existing_stat.stx_mask & (STATX_TYPE | STATX_MODE)) != (STATX_TYPE | STATX_MODE)))
1522 {
1523 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, *existing, ec, "boost::filesystem::create_directory");
1524 return false;
1525 }
1526#else
1527 struct ::stat existing_stat;
1528 if (::stat(existing->c_str(), &existing_stat) < 0)
1529 {
1530 emit_error(errno, p, *existing, ec, "boost::filesystem::create_directory");
1531 return false;
1532 }
1533#endif
1534
1535 const mode_t existing_mode = get_mode(existing_stat);
1536 if (!S_ISDIR(existing_mode))
1537 {
1538 emit_error(ENOTDIR, p, *existing, ec, "boost::filesystem::create_directory");
1539 return false;
1540 }
1541
1542 mode = existing_mode;
1543 }
1544
1545 if (::mkdir(p.c_str(), mode) == 0)
1546 return true;
1547
1548#else // defined(BOOST_POSIX_API)
1549
1550 BOOL res;
1551 if (existing)
1552 res = ::CreateDirectoryExW(existing->c_str(), p.c_str(), NULL);
92f5a8d4 1553 else
20effc67
TL
1554 res = ::CreateDirectoryW(p.c_str(), NULL);
1555
1556 if (res)
1557 return true;
1558
1559#endif // defined(BOOST_POSIX_API)
1560
1561 // attempt to create directory failed
1562 err_t errval = BOOST_ERRNO; // save reason for failure
1563 error_code dummy;
1564
1565 if (is_directory(p, dummy))
1566 return false;
7c673cae 1567
20effc67
TL
1568 // attempt to create directory failed && it doesn't already exist
1569 emit_error(errval, p, ec, "boost::filesystem::create_directory");
92f5a8d4
TL
1570 return false;
1571}
7c673cae 1572
20effc67 1573// Deprecated, to be removed in a future release
92f5a8d4 1574BOOST_FILESYSTEM_DECL
20effc67 1575void copy_directory(const path& from, const path& to, system::error_code* ec)
92f5a8d4 1576{
20effc67
TL
1577 if (ec)
1578 ec->clear();
7c673cae 1579
20effc67 1580#if defined(BOOST_POSIX_API)
7c673cae 1581
20effc67
TL
1582#if defined(BOOST_FILESYSTEM_HAS_STATX) || defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
1583 int err;
1584 struct ::statx from_stat;
1585 if (BOOST_UNLIKELY(statx(AT_FDCWD, from.c_str(), AT_NO_AUTOMOUNT, STATX_TYPE | STATX_MODE, &from_stat) < 0))
1586 {
1587 fail_errno:
1588 err = errno;
1589 fail:
1590 emit_error(err, from, to, ec, "boost::filesystem::copy_directory");
1591 return;
1592 }
1593
1594 if (BOOST_UNLIKELY((from_stat.stx_mask & (STATX_TYPE | STATX_MODE)) != (STATX_TYPE | STATX_MODE)))
1595 {
1596 err = BOOST_ERROR_NOT_SUPPORTED;
1597 goto fail;
1598 }
1599#else
1600 struct ::stat from_stat;
1601 if (BOOST_UNLIKELY(::stat(from.c_str(), &from_stat) < 0))
1602 {
1603 fail_errno:
1604 emit_error(errno, from, to, ec, "boost::filesystem::copy_directory");
1605 return;
1606 }
1607#endif
1608
1609 if (BOOST_UNLIKELY(::mkdir(to.c_str(), get_mode(from_stat)) < 0))
1610 goto fail_errno;
1611
1612#else // defined(BOOST_POSIX_API)
1613
1614 if (BOOST_UNLIKELY(!::CreateDirectoryExW(from.c_str(), to.c_str(), 0)))
1615 emit_error(BOOST_ERRNO, from, to, ec, "boost::filesystem::copy_directory");
1616
1617#endif // defined(BOOST_POSIX_API)
1618}
1619
1620BOOST_FILESYSTEM_DECL
1621void create_directory_symlink(const path& to, const path& from, system::error_code* ec)
1622{
1623#if defined(BOOST_WINDOWS_API)
1624 // see if actually supported by Windows runtime dll
1625 if (error(!create_symbolic_link_api ? BOOST_ERROR_NOT_SUPPORTED : 0, to, from, ec,
1626 "boost::filesystem::create_directory_symlink"))
1627 return;
1628#endif
7c673cae 1629
92f5a8d4
TL
1630 error(!BOOST_CREATE_SYMBOLIC_LINK(from.c_str(), to.c_str(),
1631 SYMBOLIC_LINK_FLAG_DIRECTORY) ? BOOST_ERRNO : 0,
1632 to, from, ec, "boost::filesystem::create_directory_symlink");
92f5a8d4 1633}
7c673cae 1634
92f5a8d4
TL
1635BOOST_FILESYSTEM_DECL
1636void create_hard_link(const path& to, const path& from, error_code* ec)
1637{
20effc67
TL
1638#if defined(BOOST_WINDOWS_API)
1639 // see if actually supported by Windows runtime dll
1640 if (error(!create_hard_link_api ? BOOST_ERROR_NOT_SUPPORTED : 0, to, from, ec,
1641 "boost::filesystem::create_hard_link"))
1642 return;
1643#endif
7c673cae 1644
92f5a8d4
TL
1645 error(!BOOST_CREATE_HARD_LINK(from.c_str(), to.c_str()) ? BOOST_ERRNO : 0, to, from, ec,
1646 "boost::filesystem::create_hard_link");
92f5a8d4 1647}
7c673cae 1648
92f5a8d4
TL
1649BOOST_FILESYSTEM_DECL
1650void create_symlink(const path& to, const path& from, error_code* ec)
1651{
20effc67
TL
1652#if defined(BOOST_WINDOWS_API)
1653 // see if actually supported by Windows runtime dll
1654 if (error(!create_symbolic_link_api ? BOOST_ERROR_NOT_SUPPORTED : 0, to, from, ec,
1655 "boost::filesystem::create_symlink"))
1656 return;
1657#endif
7c673cae 1658
92f5a8d4
TL
1659 error(!BOOST_CREATE_SYMBOLIC_LINK(from.c_str(), to.c_str(), 0) ? BOOST_ERRNO : 0,
1660 to, from, ec, "boost::filesystem::create_symlink");
92f5a8d4
TL
1661}
1662
1663BOOST_FILESYSTEM_DECL
1664path current_path(error_code* ec)
1665{
20effc67
TL
1666# if defined(__wasm)
1667 emit_error(BOOST_ERROR_NOT_SUPPORTED, ec, "boost::filesystem::current_path");
1668 return path();
1669# elif defined(BOOST_POSIX_API)
92f5a8d4
TL
1670 struct local
1671 {
1672 static bool getcwd_error(error_code* ec)
1673 {
1674 const int err = errno;
1675 return error((err != ERANGE
1676 // bug in some versions of the Metrowerks C lib on the Mac: wrong errno set
1677# if defined(__MSL__) && (defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__))
1678 && err != 0
1679# endif
1680 ) ? err : 0, ec, "boost::filesystem::current_path");
1681 }
1682 };
1683
1684 path cur;
1685 char small_buf[1024];
1686 const char* p = ::getcwd(small_buf, sizeof(small_buf));
1687 if (BOOST_LIKELY(!!p))
7c673cae 1688 {
92f5a8d4
TL
1689 cur = p;
1690 if (ec != 0) ec->clear();
1691 }
1692 else if (BOOST_LIKELY(!local::getcwd_error(ec)))
1693 {
1694 for (std::size_t path_max = sizeof(small_buf);; path_max *= 2u) // loop 'til buffer large enough
7c673cae 1695 {
92f5a8d4 1696 if (BOOST_UNLIKELY(path_max > absolute_path_max))
7c673cae 1697 {
20effc67 1698 emit_error(ENAMETOOLONG, ec, "boost::filesystem::current_path");
92f5a8d4 1699 break;
7c673cae 1700 }
92f5a8d4
TL
1701
1702 boost::scoped_array<char> buf(new char[path_max]);
1703 p = ::getcwd(buf.get(), path_max);
1704 if (BOOST_LIKELY(!!p))
7c673cae
FG
1705 {
1706 cur = buf.get();
92f5a8d4
TL
1707 if (ec != 0)
1708 ec->clear();
1709 break;
1710 }
1711 else if (BOOST_UNLIKELY(local::getcwd_error(ec)))
1712 {
7c673cae
FG
1713 break;
1714 }
1715 }
7c673cae
FG
1716 }
1717
92f5a8d4 1718 return cur;
7c673cae 1719
92f5a8d4
TL
1720# elif defined(UNDER_CE)
1721 // Windows CE has no current directory, so everything's relative to the root of the directory tree
1722 return L"\\";
1723# else
1724 DWORD sz;
1725 if ((sz = ::GetCurrentDirectoryW(0, NULL)) == 0)sz = 1;
1726 boost::scoped_array<path::value_type> buf(new path::value_type[sz]);
1727 error(::GetCurrentDirectoryW(sz, buf.get()) == 0 ? BOOST_ERRNO : 0, ec,
1728 "boost::filesystem::current_path");
1729 return path(buf.get());
1730# endif
1731}
7c673cae 1732
7c673cae 1733
92f5a8d4
TL
1734BOOST_FILESYSTEM_DECL
1735void current_path(const path& p, system::error_code* ec)
1736{
20effc67
TL
1737# if defined(UNDER_CE) || defined(__wasm)
1738 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::current_path");
92f5a8d4
TL
1739# else
1740 error(!BOOST_SET_CURRENT_DIRECTORY(p.c_str()) ? BOOST_ERRNO : 0,
1741 p, ec, "boost::filesystem::current_path");
1742# endif
1743}
7c673cae 1744
92f5a8d4
TL
1745BOOST_FILESYSTEM_DECL
1746bool equivalent(const path& p1, const path& p2, system::error_code* ec)
1747{
20effc67
TL
1748#if defined(BOOST_POSIX_API)
1749
1750 // p2 is done first, so any error reported is for p1
1751#if defined(BOOST_FILESYSTEM_HAS_STATX) || defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
1752 struct ::statx s2;
1753 int e2 = statx(AT_FDCWD, p2.c_str(), AT_NO_AUTOMOUNT, STATX_INO, &s2);
1754 if (BOOST_LIKELY(e2 == 0))
1755 {
1756 if (BOOST_UNLIKELY((s2.stx_mask & STATX_INO) != STATX_INO))
1757 {
1758 fail_unsupported:
1759 emit_error(BOOST_ERROR_NOT_SUPPORTED, p1, p2, ec, "boost::filesystem::equivalent");
1760 return false;
1761 }
1762 }
1763
1764 struct ::statx s1;
1765 int e1 = statx(AT_FDCWD, p1.c_str(), AT_NO_AUTOMOUNT, STATX_INO, &s1);
1766 if (BOOST_LIKELY(e1 == 0))
1767 {
1768 if (BOOST_UNLIKELY((s1.stx_mask & STATX_INO) != STATX_INO))
1769 goto fail_unsupported;
1770 }
1771#else
1772 struct ::stat s2;
1773 int e2 = ::stat(p2.c_str(), &s2);
1774 struct ::stat s1;
1775 int e1 = ::stat(p1.c_str(), &s1);
1776#endif
7c673cae 1777
20effc67 1778 if (BOOST_UNLIKELY(e1 != 0 || e2 != 0))
92f5a8d4
TL
1779 {
1780 // if one is invalid and the other isn't then they aren't equivalent,
1781 // but if both are invalid then it is an error
20effc67
TL
1782 if (e1 != 0 && e2 != 0)
1783 emit_error(errno, p1, p2, ec, "boost::filesystem::equivalent");
92f5a8d4
TL
1784 return false;
1785 }
7c673cae 1786
20effc67 1787 return equivalent_stat(s1, s2);
7c673cae 1788
92f5a8d4 1789# else // Windows
7c673cae 1790
92f5a8d4
TL
1791 // Note well: Physical location on external media is part of the
1792 // equivalence criteria. If there are no open handles, physical location
1793 // can change due to defragmentation or other relocations. Thus handles
1794 // must be held open until location information for both paths has
1795 // been retrieved.
1796
1797 // p2 is done first, so any error reported is for p1
1798 handle_wrapper h2(
1799 create_file_handle(
1800 p2.c_str(),
1801 0,
1802 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1803 0,
1804 OPEN_EXISTING,
1805 FILE_FLAG_BACKUP_SEMANTICS,
1806 0));
1807
1808 handle_wrapper h1(
1809 create_file_handle(
1810 p1.c_str(),
1811 0,
1812 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1813 0,
1814 OPEN_EXISTING,
1815 FILE_FLAG_BACKUP_SEMANTICS,
1816 0));
1817
20effc67 1818 if (BOOST_UNLIKELY(h1.handle == INVALID_HANDLE_VALUE || h2.handle == INVALID_HANDLE_VALUE))
92f5a8d4
TL
1819 {
1820 // if one is invalid and the other isn't, then they aren't equivalent,
1821 // but if both are invalid then it is an error
20effc67
TL
1822 if (h1.handle == INVALID_HANDLE_VALUE && h2.handle == INVALID_HANDLE_VALUE)
1823 error(BOOST_ERRNO, p1, p2, ec, "boost::filesystem::equivalent");
92f5a8d4
TL
1824 return false;
1825 }
7c673cae 1826
92f5a8d4 1827 // at this point, both handles are known to be valid
7c673cae 1828
92f5a8d4 1829 BY_HANDLE_FILE_INFORMATION info1, info2;
7c673cae 1830
92f5a8d4
TL
1831 if (error(!::GetFileInformationByHandle(h1.handle, &info1) ? BOOST_ERRNO : 0,
1832 p1, p2, ec, "boost::filesystem::equivalent"))
1833 return false;
7c673cae 1834
92f5a8d4
TL
1835 if (error(!::GetFileInformationByHandle(h2.handle, &info2) ? BOOST_ERRNO : 0,
1836 p1, p2, ec, "boost::filesystem::equivalent"))
1837 return false;
7c673cae 1838
92f5a8d4
TL
1839 // In theory, volume serial numbers are sufficient to distinguish between
1840 // devices, but in practice VSN's are sometimes duplicated, so last write
1841 // time and file size are also checked.
1842 return
1843 info1.dwVolumeSerialNumber == info2.dwVolumeSerialNumber
1844 && info1.nFileIndexHigh == info2.nFileIndexHigh
1845 && info1.nFileIndexLow == info2.nFileIndexLow
1846 && info1.nFileSizeHigh == info2.nFileSizeHigh
1847 && info1.nFileSizeLow == info2.nFileSizeLow
1848 && info1.ftLastWriteTime.dwLowDateTime
1849 == info2.ftLastWriteTime.dwLowDateTime
1850 && info1.ftLastWriteTime.dwHighDateTime
1851 == info2.ftLastWriteTime.dwHighDateTime;
7c673cae 1852
92f5a8d4
TL
1853# endif
1854}
7c673cae 1855
92f5a8d4 1856BOOST_FILESYSTEM_DECL
20effc67 1857uintmax_t file_size(const path& p, error_code* ec)
92f5a8d4 1858{
20effc67
TL
1859 if (ec)
1860 ec->clear();
7c673cae 1861
20effc67
TL
1862#if defined(BOOST_POSIX_API)
1863
1864#if defined(BOOST_FILESYSTEM_HAS_STATX) || defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
1865 struct ::statx path_stat;
1866 if (BOOST_UNLIKELY(statx(AT_FDCWD, p.c_str(), AT_NO_AUTOMOUNT, STATX_TYPE | STATX_SIZE, &path_stat) < 0))
1867 {
1868 emit_error(errno, p, ec, "boost::filesystem::file_size");
1869 return static_cast<uintmax_t>(-1);
1870 }
7c673cae 1871
20effc67
TL
1872 if (BOOST_UNLIKELY((path_stat.stx_mask & (STATX_TYPE | STATX_SIZE)) != (STATX_TYPE | STATX_SIZE) || !S_ISREG(path_stat.stx_mode)))
1873 {
1874 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::file_size");
1875 return static_cast<uintmax_t>(-1);
1876 }
1877#else
1878 struct ::stat path_stat;
1879 if (BOOST_UNLIKELY(::stat(p.c_str(), &path_stat) < 0))
1880 {
1881 emit_error(errno, p, ec, "boost::filesystem::file_size");
1882 return static_cast<uintmax_t>(-1);
1883 }
7c673cae 1884
20effc67
TL
1885 if (BOOST_UNLIKELY(!S_ISREG(path_stat.st_mode)))
1886 {
1887 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::file_size");
1888 return static_cast<uintmax_t>(-1);
1889 }
1890#endif
1891
1892 return get_size(path_stat);
1893
1894#else // defined(BOOST_POSIX_API)
7c673cae 1895
92f5a8d4 1896 // assume uintmax_t is 64-bits on all Windows compilers
7c673cae 1897
92f5a8d4 1898 WIN32_FILE_ATTRIBUTE_DATA fad;
7c673cae 1899
20effc67
TL
1900 if (BOOST_UNLIKELY(!::GetFileAttributesExW(p.c_str(), ::GetFileExInfoStandard, &fad)))
1901 {
1902 emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::file_size");
1903 return static_cast<uintmax_t>(-1);
1904 }
1905
1906 if (BOOST_UNLIKELY((fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0))
1907 {
1908 emit_error(ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::file_size");
1909 return static_cast<uintmax_t>(-1);
1910 }
7c673cae 1911
20effc67
TL
1912 return (static_cast<uintmax_t>(fad.nFileSizeHigh)
1913 << (sizeof(fad.nFileSizeLow) * 8u)) | fad.nFileSizeLow;
7c673cae 1914
20effc67 1915#endif // defined(BOOST_POSIX_API)
92f5a8d4 1916}
7c673cae 1917
92f5a8d4 1918BOOST_FILESYSTEM_DECL
20effc67 1919uintmax_t hard_link_count(const path& p, system::error_code* ec)
92f5a8d4 1920{
20effc67
TL
1921 if (ec)
1922 ec->clear();
7c673cae 1923
20effc67 1924#if defined(BOOST_POSIX_API)
7c673cae 1925
20effc67
TL
1926#if defined(BOOST_FILESYSTEM_HAS_STATX) || defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
1927 struct ::statx path_stat;
1928 if (BOOST_UNLIKELY(statx(AT_FDCWD, p.c_str(), AT_NO_AUTOMOUNT, STATX_NLINK, &path_stat) < 0))
1929 {
1930 emit_error(errno, p, ec, "boost::filesystem::hard_link_count");
1931 return static_cast<uintmax_t>(-1);
1932 }
1933
1934 if (BOOST_UNLIKELY((path_stat.stx_mask & STATX_NLINK) != STATX_NLINK))
1935 {
1936 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::hard_link_count");
1937 return static_cast<uintmax_t>(-1);
1938 }
1939
1940 return static_cast<uintmax_t>(path_stat.stx_nlink);
1941#else
1942 struct ::stat path_stat;
1943 if (BOOST_UNLIKELY(::stat(p.c_str(), &path_stat) < 0))
1944 {
1945 emit_error(errno, p, ec, "boost::filesystem::hard_link_count");
1946 return static_cast<uintmax_t>(-1);
1947 }
1948
1949 return static_cast<uintmax_t>(path_stat.st_nlink);
1950#endif
1951
1952#else // defined(BOOST_POSIX_API)
7c673cae 1953
92f5a8d4
TL
1954 handle_wrapper h(
1955 create_file_handle(p.c_str(), 0,
1956 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
1957 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0));
20effc67
TL
1958
1959 if (BOOST_UNLIKELY(h.handle == INVALID_HANDLE_VALUE))
1960 {
1961 fail_errno:
1962 emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::hard_link_count");
1963 return static_cast<uintmax_t>(-1);
1964 }
1965
1966 // Link count info is only available through GetFileInformationByHandle
1967 BY_HANDLE_FILE_INFORMATION info;
1968 if (BOOST_UNLIKELY(!::GetFileInformationByHandle(h.handle, &info)))
1969 goto fail_errno;
1970
1971 return static_cast<uintmax_t>(info.nNumberOfLinks);
1972
1973#endif // defined(BOOST_POSIX_API)
92f5a8d4 1974}
7c673cae 1975
92f5a8d4
TL
1976BOOST_FILESYSTEM_DECL
1977path initial_path(error_code* ec)
1978{
1979 static path init_path;
1980 if (init_path.empty())
1981 init_path = current_path(ec);
1982 else if (ec != 0) ec->clear();
1983 return init_path;
1984}
7c673cae 1985
92f5a8d4
TL
1986BOOST_FILESYSTEM_DECL
1987bool is_empty(const path& p, system::error_code* ec)
1988{
20effc67
TL
1989 if (ec)
1990 ec->clear();
1991
1992#if defined(BOOST_POSIX_API)
1993
1994#if defined(BOOST_FILESYSTEM_HAS_STATX) || defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
1995 struct ::statx path_stat;
1996 if (BOOST_UNLIKELY(statx(AT_FDCWD, p.c_str(), AT_NO_AUTOMOUNT, STATX_TYPE | STATX_SIZE, &path_stat) < 0))
1997 {
1998 emit_error(errno, p, ec, "boost::filesystem::is_empty");
1999 return false;
2000 }
2001
2002 if (BOOST_UNLIKELY((path_stat.stx_mask & STATX_TYPE) != STATX_TYPE))
2003 {
2004 fail_unsupported:
2005 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::is_empty");
2006 return false;
2007 }
2008
2009 if (S_ISDIR(get_mode(path_stat)))
2010 return is_empty_directory(p, ec);
7c673cae 2011
20effc67
TL
2012 if (BOOST_UNLIKELY((path_stat.stx_mask & STATX_SIZE) != STATX_SIZE))
2013 goto fail_unsupported;
2014
2015 return get_size(path_stat) == 0u;
2016#else
2017 struct ::stat path_stat;
2018 if (BOOST_UNLIKELY(::stat(p.c_str(), &path_stat) < 0))
2019 {
2020 emit_error(errno, p, ec, "boost::filesystem::is_empty");
92f5a8d4 2021 return false;
20effc67
TL
2022 }
2023
2024 return S_ISDIR(get_mode(path_stat))
92f5a8d4 2025 ? is_empty_directory(p, ec)
20effc67
TL
2026 : get_size(path_stat) == 0u;
2027#endif
7c673cae 2028
20effc67 2029#else // defined(BOOST_POSIX_API)
7c673cae 2030
92f5a8d4 2031 WIN32_FILE_ATTRIBUTE_DATA fad;
20effc67
TL
2032 if (BOOST_UNLIKELY(!::GetFileAttributesExW(p.c_str(), ::GetFileExInfoStandard, &fad)))
2033 {
2034 emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::is_empty");
2035 return false;
2036 }
7c673cae 2037
92f5a8d4
TL
2038 return
2039 (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
2040 ? is_empty_directory(p, ec)
2041 : (!fad.nFileSizeHigh && !fad.nFileSizeLow);
7c673cae 2042
20effc67
TL
2043#endif // defined(BOOST_POSIX_API)
2044}
2045
2046BOOST_FILESYSTEM_DECL
2047std::time_t creation_time(const path& p, system::error_code* ec)
2048{
2049 if (ec)
2050 ec->clear();
2051
2052#if defined(BOOST_POSIX_API)
2053
2054#if defined(BOOST_FILESYSTEM_HAS_STATX) || defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
2055 struct ::statx stx;
2056 if (BOOST_UNLIKELY(statx(AT_FDCWD, p.c_str(), AT_NO_AUTOMOUNT, STATX_BTIME, &stx) < 0))
2057 {
2058 emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::creation_time");
2059 return (std::numeric_limits< std::time_t >::min)();
2060 }
2061 if (BOOST_UNLIKELY((stx.stx_mask & STATX_BTIME) != STATX_BTIME))
2062 {
2063 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::creation_time");
2064 return (std::numeric_limits< std::time_t >::min)();
2065 }
2066 return stx.stx_btime.tv_sec;
2067#elif defined(BOOST_FILESYSTEM_STAT_ST_BIRTHTIME) && defined(BOOST_FILESYSTEM_STAT_ST_BIRTHTIMENSEC)
2068 struct ::stat st;
2069 if (BOOST_UNLIKELY(::stat(p.c_str(), &st) < 0))
2070 {
2071 emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::creation_time");
2072 return (std::numeric_limits< std::time_t >::min)();
2073 }
2074 return st.BOOST_FILESYSTEM_STAT_ST_BIRTHTIME;
2075#else
2076 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::creation_time");
2077 return (std::numeric_limits< std::time_t >::min)();
2078#endif
2079
2080#else // defined(BOOST_POSIX_API)
2081
2082 handle_wrapper hw(
2083 create_file_handle(p.c_str(), 0,
2084 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
2085 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0));
2086
2087 if (BOOST_UNLIKELY(hw.handle == INVALID_HANDLE_VALUE))
2088 {
2089 fail:
2090 emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::creation_time");
2091 return (std::numeric_limits< std::time_t >::min)();
2092 }
2093
2094 FILETIME ct;
2095
2096 if (BOOST_UNLIKELY(!::GetFileTime(hw.handle, &ct, NULL, NULL)))
2097 goto fail;
2098
2099 return to_time_t(ct);
2100
2101#endif // defined(BOOST_POSIX_API)
92f5a8d4 2102}
7c673cae 2103
92f5a8d4
TL
2104BOOST_FILESYSTEM_DECL
2105std::time_t last_write_time(const path& p, system::error_code* ec)
2106{
20effc67
TL
2107 if (ec)
2108 ec->clear();
7c673cae 2109
20effc67 2110#if defined(BOOST_POSIX_API)
7c673cae 2111
20effc67
TL
2112#if defined(BOOST_FILESYSTEM_HAS_STATX) || defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
2113 struct ::statx stx;
2114 if (BOOST_UNLIKELY(statx(AT_FDCWD, p.c_str(), AT_NO_AUTOMOUNT, STATX_MTIME, &stx) < 0))
2115 {
2116 emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::last_write_time");
2117 return (std::numeric_limits< std::time_t >::min)();
2118 }
2119 if (BOOST_UNLIKELY((stx.stx_mask & STATX_MTIME) != STATX_MTIME))
2120 {
2121 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::last_write_time");
2122 return (std::numeric_limits< std::time_t >::min)();
2123 }
2124 return stx.stx_mtime.tv_sec;
2125#else
2126 struct ::stat st;
2127 if (BOOST_UNLIKELY(::stat(p.c_str(), &st) < 0))
2128 {
2129 emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::last_write_time");
2130 return (std::numeric_limits< std::time_t >::min)();
2131 }
2132 return st.st_mtime;
2133#endif
2134
2135#else // defined(BOOST_POSIX_API)
7c673cae 2136
92f5a8d4
TL
2137 handle_wrapper hw(
2138 create_file_handle(p.c_str(), 0,
2139 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
2140 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0));
7c673cae 2141
20effc67
TL
2142 if (BOOST_UNLIKELY(hw.handle == INVALID_HANDLE_VALUE))
2143 {
2144 fail:
2145 emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::last_write_time");
2146 return (std::numeric_limits< std::time_t >::min)();
2147 }
7c673cae 2148
92f5a8d4 2149 FILETIME lwt;
7c673cae 2150
20effc67
TL
2151 if (BOOST_UNLIKELY(!::GetFileTime(hw.handle, NULL, NULL, &lwt)))
2152 goto fail;
7c673cae 2153
92f5a8d4 2154 return to_time_t(lwt);
7c673cae 2155
20effc67 2156#endif // defined(BOOST_POSIX_API)
92f5a8d4 2157}
7c673cae 2158
92f5a8d4 2159BOOST_FILESYSTEM_DECL
20effc67 2160void last_write_time(const path& p, const std::time_t new_time, system::error_code* ec)
92f5a8d4 2161{
20effc67
TL
2162 if (ec)
2163 ec->clear();
2164
2165#if defined(BOOST_POSIX_API)
2166
2167#if _POSIX_C_SOURCE >= 200809L
7c673cae 2168
92f5a8d4 2169 struct timespec times[2] = {};
7c673cae 2170
92f5a8d4
TL
2171 // Keep the last access time unchanged
2172 times[0].tv_nsec = UTIME_OMIT;
7c673cae 2173
92f5a8d4 2174 times[1].tv_sec = new_time;
7c673cae 2175
92f5a8d4
TL
2176 if (BOOST_UNLIKELY(::utimensat(AT_FDCWD, p.c_str(), times, 0) != 0))
2177 {
20effc67 2178 emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::last_write_time");
92f5a8d4 2179 return;
7c673cae
FG
2180 }
2181
20effc67
TL
2182#else // _POSIX_C_SOURCE >= 200809L
2183
2184 struct ::stat st;
2185 if (BOOST_UNLIKELY(::stat(p.c_str(), &st) < 0))
2186 {
2187 emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::last_write_time");
2188 return;
2189 }
7c673cae 2190
92f5a8d4 2191 ::utimbuf buf;
20effc67 2192 buf.actime = st.st_atime; // utime()updates access time too:-(
92f5a8d4 2193 buf.modtime = new_time;
20effc67
TL
2194 if (BOOST_UNLIKELY(::utime(p.c_str(), &buf) < 0))
2195 emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::last_write_time");
7c673cae 2196
20effc67 2197#endif // _POSIX_C_SOURCE >= 200809L
7c673cae 2198
20effc67 2199#else // defined(BOOST_POSIX_API)
7c673cae 2200
92f5a8d4
TL
2201 handle_wrapper hw(
2202 create_file_handle(p.c_str(), FILE_WRITE_ATTRIBUTES,
2203 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
2204 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0));
7c673cae 2205
20effc67
TL
2206 if (BOOST_UNLIKELY(hw.handle == INVALID_HANDLE_VALUE))
2207 {
2208 fail:
2209 emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::last_write_time");
2210 return;
2211 }
7c673cae 2212
92f5a8d4
TL
2213 FILETIME lwt;
2214 to_FILETIME(new_time, lwt);
7c673cae 2215
20effc67
TL
2216 if (BOOST_UNLIKELY(!::SetFileTime(hw.handle, 0, 0, &lwt)))
2217 goto fail;
7c673cae 2218
20effc67 2219#endif // defined(BOOST_POSIX_API)
92f5a8d4 2220}
7c673cae 2221
20effc67 2222#ifdef BOOST_POSIX_API
92f5a8d4
TL
2223const perms active_bits(all_all | set_uid_on_exe | set_gid_on_exe | sticky_bit);
2224inline mode_t mode_cast(perms prms) { return prms & active_bits; }
20effc67 2225#endif
7c673cae 2226
92f5a8d4
TL
2227BOOST_FILESYSTEM_DECL
2228void permissions(const path& p, perms prms, system::error_code* ec)
2229{
2230 BOOST_ASSERT_MSG(!((prms & add_perms) && (prms & remove_perms)),
2231 "add_perms and remove_perms are mutually exclusive");
7c673cae 2232
92f5a8d4
TL
2233 if ((prms & add_perms) && (prms & remove_perms)) // precondition failed
2234 return;
7c673cae 2235
20effc67
TL
2236# if defined(__wasm)
2237 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::permissions");
2238# elif defined(BOOST_POSIX_API)
92f5a8d4
TL
2239 error_code local_ec;
2240 file_status current_status((prms & symlink_perms)
2241 ? fs::symlink_status(p, local_ec)
2242 : fs::status(p, local_ec));
2243 if (local_ec)
7c673cae 2244 {
92f5a8d4 2245 if (ec == 0)
20effc67 2246 BOOST_FILESYSTEM_THROW(filesystem_error(
92f5a8d4
TL
2247 "boost::filesystem::permissions", p, local_ec));
2248 else
2249 *ec = local_ec;
2250 return;
2251 }
2252
2253 if (prms & add_perms)
2254 prms |= current_status.permissions();
2255 else if (prms & remove_perms)
2256 prms = current_status.permissions() & ~prms;
2257
2258 // OS X <10.10, iOS <8.0 and some other platforms don't support fchmodat().
2259 // Solaris (SunPro and gcc) only support fchmodat() on Solaris 11 and higher,
2260 // and a runtime check is too much trouble.
2261 // Linux does not support permissions on symbolic links and has no plans to
2262 // support them in the future. The chmod() code is thus more practical,
2263 // rather than always hitting ENOTSUP when sending in AT_SYMLINK_NO_FOLLOW.
2264 // - See the 3rd paragraph of
2265 // "Symbolic link ownership, permissions, and timestamps" at:
2266 // "http://man7.org/linux/man-pages/man7/symlink.7.html"
2267 // - See the fchmodat() Linux man page:
2268 // "http://man7.org/linux/man-pages/man2/fchmodat.2.html"
2269# if defined(AT_FDCWD) && defined(AT_SYMLINK_NOFOLLOW) \
2270 && !(defined(__SUNPRO_CC) || defined(__sun) || defined(sun)) \
2271 && !(defined(linux) || defined(__linux) || defined(__linux__)) \
2272 && !(defined(__MAC_OS_X_VERSION_MIN_REQUIRED) \
2273 && __MAC_OS_X_VERSION_MIN_REQUIRED < 101000) \
2274 && !(defined(__IPHONE_OS_VERSION_MIN_REQUIRED) \
2275 && __IPHONE_OS_VERSION_MIN_REQUIRED < 80000) \
2276 && !(defined(__QNX__) && (_NTO_VERSION <= 700))
2277 if (::fchmodat(AT_FDCWD, p.c_str(), mode_cast(prms),
2278 !(prms & symlink_perms) ? 0 : AT_SYMLINK_NOFOLLOW))
2279# else // fallback if fchmodat() not supported
2280 if (::chmod(p.c_str(), mode_cast(prms)))
2281# endif
7c673cae 2282 {
92f5a8d4
TL
2283 const int err = errno;
2284 if (ec == 0)
2285 BOOST_FILESYSTEM_THROW(filesystem_error(
2286 "boost::filesystem::permissions", p,
2287 error_code(err, system::generic_category())));
2288 else
2289 ec->assign(err, system::generic_category());
7c673cae
FG
2290 }
2291
92f5a8d4 2292# else // Windows
7c673cae 2293
92f5a8d4
TL
2294 // if not going to alter FILE_ATTRIBUTE_READONLY, just return
2295 if (!(!((prms & (add_perms | remove_perms)))
2296 || (prms & (owner_write|group_write|others_write))))
2297 return;
7c673cae 2298
92f5a8d4 2299 DWORD attr = ::GetFileAttributesW(p.c_str());
7c673cae 2300
92f5a8d4
TL
2301 if (error(attr == 0 ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::permissions"))
2302 return;
7c673cae 2303
92f5a8d4
TL
2304 if (prms & add_perms)
2305 attr &= ~FILE_ATTRIBUTE_READONLY;
2306 else if (prms & remove_perms)
2307 attr |= FILE_ATTRIBUTE_READONLY;
2308 else if (prms & (owner_write|group_write|others_write))
2309 attr &= ~FILE_ATTRIBUTE_READONLY;
2310 else
2311 attr |= FILE_ATTRIBUTE_READONLY;
7c673cae 2312
92f5a8d4
TL
2313 error(::SetFileAttributesW(p.c_str(), attr) == 0 ? BOOST_ERRNO : 0,
2314 p, ec, "boost::filesystem::permissions");
2315# endif
2316}
7c673cae 2317
92f5a8d4
TL
2318BOOST_FILESYSTEM_DECL
2319path read_symlink(const path& p, system::error_code* ec)
2320{
2321 path symlink_path;
7c673cae 2322
92f5a8d4
TL
2323# ifdef BOOST_POSIX_API
2324 const char* const path_str = p.c_str();
2325 char small_buf[1024];
2326 ssize_t result = ::readlink(path_str, small_buf, sizeof(small_buf));
2327 if (BOOST_UNLIKELY(result < 0))
2328 {
2329 fail:
2330 const int err = errno;
2331 if (ec == 0)
2332 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::read_symlink",
2333 p, error_code(err, system_category())));
7c673cae 2334 else
92f5a8d4 2335 ec->assign(err, system_category());
7c673cae 2336 }
92f5a8d4 2337 else if (BOOST_LIKELY(static_cast< std::size_t >(result) < sizeof(small_buf)))
7c673cae 2338 {
92f5a8d4
TL
2339 symlink_path.assign(small_buf, small_buf + result);
2340 if (ec != 0)
2341 ec->clear();
2342 }
2343 else
2344 {
2345 for (std::size_t path_max = sizeof(small_buf) * 2u;; path_max *= 2u) // loop 'til buffer large enough
7c673cae 2346 {
92f5a8d4 2347 if (BOOST_UNLIKELY(path_max > absolute_path_max))
7c673cae 2348 {
92f5a8d4
TL
2349 if (ec == 0)
2350 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::read_symlink",
2351 p, error_code(ENAMETOOLONG, system_category())));
2352 else
2353 ec->assign(ENAMETOOLONG, system_category());
2354 break;
7c673cae 2355 }
7c673cae 2356
92f5a8d4
TL
2357 boost::scoped_array<char> buf(new char[path_max]);
2358 result = ::readlink(path_str, buf.get(), path_max);
2359 if (BOOST_UNLIKELY(result < 0))
7c673cae 2360 {
92f5a8d4
TL
2361 goto fail;
2362 }
2363 else if (BOOST_LIKELY(static_cast< std::size_t >(result) < path_max))
2364 {
2365 symlink_path.assign(buf.get(), buf.get() + result);
2366 if (ec != 0) ec->clear();
2367 break;
7c673cae 2368 }
7c673cae 2369 }
7c673cae
FG
2370 }
2371
20effc67 2372# else
7c673cae 2373
92f5a8d4 2374 handle_wrapper h(
20effc67
TL
2375 create_file_handle(p.c_str(), 0,
2376 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING,
92f5a8d4 2377 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0));
7c673cae 2378
92f5a8d4
TL
2379 if (error(h.handle == INVALID_HANDLE_VALUE ? BOOST_ERRNO : 0,
2380 p, ec, "boost::filesystem::read_symlink"))
2381 return symlink_path;
7c673cae 2382
20effc67
TL
2383 boost::scoped_ptr<reparse_data_buffer> buf(new reparse_data_buffer);
2384 DWORD sz = 0u;
92f5a8d4 2385 if (!error(::DeviceIoControl(h.handle, FSCTL_GET_REPARSE_POINT,
20effc67 2386 0, 0, buf.get(), sizeof(*buf), &sz, 0) == 0 ? BOOST_ERRNO : 0, p, ec,
92f5a8d4 2387 "boost::filesystem::read_symlink" ))
20effc67
TL
2388 {
2389 const wchar_t* buffer;
2390 std::size_t offset, len;
2391 switch (buf->rdb.ReparseTag)
2392 {
2393 case IO_REPARSE_TAG_MOUNT_POINT:
2394 buffer = buf->rdb.MountPointReparseBuffer.PathBuffer;
2395 offset = buf->rdb.MountPointReparseBuffer.PrintNameOffset;
2396 len = buf->rdb.MountPointReparseBuffer.PrintNameLength;
2397 break;
2398 case IO_REPARSE_TAG_SYMLINK:
2399 buffer = buf->rdb.SymbolicLinkReparseBuffer.PathBuffer;
2400 offset = buf->rdb.SymbolicLinkReparseBuffer.PrintNameOffset;
2401 len = buf->rdb.SymbolicLinkReparseBuffer.PrintNameLength;
2402 // Note: iff info.rdb.SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE
2403 // -> resulting path is relative to the source
2404 break;
2405 default:
2406 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "Unknown ReparseTag in boost::filesystem::read_symlink");
2407 return symlink_path;
2408 }
92f5a8d4 2409 symlink_path.assign(
20effc67
TL
2410 buffer + offset / sizeof(wchar_t),
2411 buffer + (offset + len) / sizeof(wchar_t));
2412 }
92f5a8d4
TL
2413# endif
2414 return symlink_path;
2415}
7c673cae 2416
92f5a8d4
TL
2417BOOST_FILESYSTEM_DECL
2418path relative(const path& p, const path& base, error_code* ec)
2419{
2420 error_code tmp_ec;
2421 path wc_base(weakly_canonical(base, &tmp_ec));
2422 if (error(tmp_ec.value(), base, ec, "boost::filesystem::relative"))
2423 return path();
2424 path wc_p(weakly_canonical(p, &tmp_ec));
2425 if (error(tmp_ec.value(), base, ec, "boost::filesystem::relative"))
2426 return path();
2427 return wc_p.lexically_relative(wc_base);
2428}
7c673cae 2429
92f5a8d4
TL
2430BOOST_FILESYSTEM_DECL
2431bool remove(const path& p, error_code* ec)
2432{
2433 error_code tmp_ec;
2434 file_type type = query_file_type(p, &tmp_ec);
2435 if (error(type == status_error ? tmp_ec.value() : 0, p, ec,
2436 "boost::filesystem::remove"))
2437 return false;
7c673cae 2438
92f5a8d4
TL
2439 // Since POSIX remove() is specified to work with either files or directories, in a
2440 // perfect world it could just be called. But some important real-world operating
2441 // systems (Windows, Mac OS X, for example) don't implement the POSIX spec. So
2442 // remove_file_or_directory() is always called to keep it simple.
2443 return remove_file_or_directory(p, type, ec);
2444}
7c673cae 2445
92f5a8d4 2446BOOST_FILESYSTEM_DECL
20effc67 2447uintmax_t remove_all(const path& p, error_code* ec)
92f5a8d4
TL
2448{
2449 error_code tmp_ec;
2450 file_type type = query_file_type(p, &tmp_ec);
2451 if (error(type == status_error ? tmp_ec.value() : 0, p, ec,
2452 "boost::filesystem::remove_all"))
2453 return 0;
7c673cae 2454
92f5a8d4
TL
2455 return (type != status_error && type != file_not_found) // exists
2456 ? remove_all_aux(p, type, ec)
2457 : 0;
2458}
7c673cae 2459
92f5a8d4
TL
2460BOOST_FILESYSTEM_DECL
2461void rename(const path& old_p, const path& new_p, error_code* ec)
2462{
2463 error(!BOOST_MOVE_FILE(old_p.c_str(), new_p.c_str()) ? BOOST_ERRNO : 0, old_p, new_p,
2464 ec, "boost::filesystem::rename");
2465}
7c673cae 2466
92f5a8d4
TL
2467BOOST_FILESYSTEM_DECL
2468void resize_file(const path& p, uintmax_t size, system::error_code* ec)
2469{
2470# if defined(BOOST_POSIX_API)
2471 if (BOOST_UNLIKELY(size > static_cast< uintmax_t >((std::numeric_limits< off_t >::max)()))) {
20effc67 2472 emit_error(system::errc::file_too_large, p, ec, "boost::filesystem::resize_file");
92f5a8d4 2473 return;
7c673cae 2474 }
92f5a8d4
TL
2475# endif
2476 error(!BOOST_RESIZE_FILE(p.c_str(), size) ? BOOST_ERRNO : 0, p, ec,
2477 "boost::filesystem::resize_file");
2478}
2479
2480BOOST_FILESYSTEM_DECL
2481space_info space(const path& p, error_code* ec)
2482{
92f5a8d4 2483 space_info info;
20effc67
TL
2484 // Initialize members to -1, as required by C++20 [fs.op.space]/1 in case of error
2485 info.capacity = static_cast<uintmax_t>(-1);
2486 info.free = static_cast<uintmax_t>(-1);
2487 info.available = static_cast<uintmax_t>(-1);
2488
2489 if (ec)
2490 ec->clear();
2491
2492# if defined(__wasm)
2493
2494 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::space");
2495
2496# elif defined(BOOST_POSIX_API)
2497
2498 struct BOOST_STATVFS vfs;
92f5a8d4
TL
2499 if (!error(::BOOST_STATVFS(p.c_str(), &vfs) ? BOOST_ERRNO : 0,
2500 p, ec, "boost::filesystem::space"))
7c673cae 2501 {
92f5a8d4 2502 info.capacity
20effc67 2503 = static_cast<uintmax_t>(vfs.f_blocks) * BOOST_STATVFS_F_FRSIZE;
92f5a8d4 2504 info.free
20effc67 2505 = static_cast<uintmax_t>(vfs.f_bfree) * BOOST_STATVFS_F_FRSIZE;
92f5a8d4 2506 info.available
20effc67 2507 = static_cast<uintmax_t>(vfs.f_bavail) * BOOST_STATVFS_F_FRSIZE;
92f5a8d4 2508 }
7c673cae 2509
92f5a8d4 2510# else
7c673cae 2511
20effc67
TL
2512 // GetDiskFreeSpaceExW requires a directory path, which is unlike statvfs, which accepts any file.
2513 // To work around this, test if the path refers to a directory and use the parent directory if not.
2514 error_code local_ec;
2515 file_status status = detail::status(p, &local_ec);
2516 if (status.type() == fs::status_error || status.type() == fs::file_not_found)
2517 {
2518 fail_local_ec:
2519 if (!ec)
2520 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::space", p, local_ec));
2521 *ec = local_ec;
2522 return info;
2523 }
7c673cae 2524
20effc67
TL
2525 path dir_path = p;
2526 if (!is_directory(status))
92f5a8d4 2527 {
20effc67
TL
2528 path cur_path = detail::current_path(ec);
2529 if (ec && *ec)
2530 return info;
2531
2532 status = detail::symlink_status(p, &local_ec);
2533 if (status.type() == fs::status_error)
2534 goto fail_local_ec;
2535 if (is_symlink(status))
2536 {
2537 // We need to resolve the symlink so that we report the space for the symlink target
2538 dir_path = detail::canonical(p, cur_path, ec);
2539 if (ec && *ec)
2540 return info;
2541 }
2542
2543 dir_path = dir_path.parent_path();
2544 if (dir_path.empty())
2545 {
2546 // The original path was just a filename, which is a relative path wrt. current directory
2547 dir_path = cur_path;
2548 }
92f5a8d4 2549 }
7c673cae 2550
20effc67
TL
2551 // For UNC names, the path must also include a trailing slash.
2552 path::string_type str = dir_path.native();
2553 if (str.size() >= 2u && detail::is_directory_separator(str[0]) && detail::is_directory_separator(str[1]) && !detail::is_directory_separator(*(str.end() - 1)))
2554 str.push_back(path::preferred_separator);
7c673cae 2555
20effc67
TL
2556 ULARGE_INTEGER avail, total, free;
2557 if (!error(::GetDiskFreeSpaceExW(str.c_str(), &avail, &total, &free) == 0,
2558 p, ec, "boost::filesystem::space"))
92f5a8d4 2559 {
20effc67
TL
2560 info.capacity = static_cast<uintmax_t>(total.QuadPart);
2561 info.free = static_cast<uintmax_t>(free.QuadPart);
2562 info.available = static_cast<uintmax_t>(avail.QuadPart);
7c673cae 2563 }
20effc67
TL
2564
2565# endif
2566
92f5a8d4
TL
2567 return info;
2568}
2569
2570BOOST_FILESYSTEM_DECL
2571file_status status(const path& p, error_code* ec)
2572{
20effc67
TL
2573 if (ec)
2574 ec->clear();
2575
2576#if defined(BOOST_POSIX_API)
2577
2578#if defined(BOOST_FILESYSTEM_HAS_STATX) || defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
2579 struct ::statx path_stat;
2580 int err = statx(AT_FDCWD, p.c_str(), AT_NO_AUTOMOUNT, STATX_TYPE | STATX_MODE, &path_stat);
2581#else
2582 struct ::stat path_stat;
2583 int err = ::stat(p.c_str(), &path_stat);
2584#endif
7c673cae 2585
20effc67 2586 if (err != 0)
7c673cae 2587 {
20effc67 2588 err = errno;
92f5a8d4
TL
2589 if (ec != 0) // always report errno, even though some
2590 ec->assign(err, system_category()); // errno values are not status_errors
7c673cae 2591
92f5a8d4 2592 if (not_found_error(err))
7c673cae 2593 {
92f5a8d4 2594 return fs::file_status(fs::file_not_found, fs::no_perms);
7c673cae 2595 }
92f5a8d4
TL
2596 if (ec == 0)
2597 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::status",
2598 p, error_code(err, system_category())));
2599 return fs::file_status(fs::status_error);
2600 }
20effc67
TL
2601
2602#if defined(BOOST_FILESYSTEM_HAS_STATX) || defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
2603 if (BOOST_UNLIKELY((path_stat.stx_mask & (STATX_TYPE | STATX_MODE)) != (STATX_TYPE | STATX_MODE)))
2604 {
2605 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::status");
2606 return fs::file_status(fs::status_error);
2607 }
2608#endif
2609
2610 const mode_t mode = get_mode(path_stat);
2611 if (S_ISDIR(mode))
92f5a8d4 2612 return fs::file_status(fs::directory_file,
20effc67
TL
2613 static_cast<perms>(mode) & fs::perms_mask);
2614 if (S_ISREG(mode))
92f5a8d4 2615 return fs::file_status(fs::regular_file,
20effc67
TL
2616 static_cast<perms>(mode) & fs::perms_mask);
2617 if (S_ISBLK(mode))
92f5a8d4 2618 return fs::file_status(fs::block_file,
20effc67
TL
2619 static_cast<perms>(mode) & fs::perms_mask);
2620 if (S_ISCHR(mode))
92f5a8d4 2621 return fs::file_status(fs::character_file,
20effc67
TL
2622 static_cast<perms>(mode) & fs::perms_mask);
2623 if (S_ISFIFO(mode))
92f5a8d4 2624 return fs::file_status(fs::fifo_file,
20effc67
TL
2625 static_cast<perms>(mode) & fs::perms_mask);
2626 if (S_ISSOCK(mode))
92f5a8d4 2627 return fs::file_status(fs::socket_file,
20effc67 2628 static_cast<perms>(mode) & fs::perms_mask);
92f5a8d4 2629 return fs::file_status(fs::type_unknown);
7c673cae 2630
20effc67 2631#else // defined(BOOST_POSIX_API)
92f5a8d4
TL
2632
2633 DWORD attr(::GetFileAttributesW(p.c_str()));
2634 if (attr == 0xFFFFFFFF)
2635 {
2636 return process_status_failure(p, ec);
7c673cae 2637 }
7c673cae 2638
92f5a8d4 2639 perms permissions = make_permissions(p, attr);
7c673cae 2640
92f5a8d4
TL
2641 // reparse point handling;
2642 // since GetFileAttributesW does not resolve symlinks, try to open a file
2643 // handle to discover if the file exists
2644 if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
7c673cae 2645 {
20effc67
TL
2646 if (!is_reparse_point_a_symlink(p))
2647 {
2648 return file_status(reparse_file, permissions);
2649 }
2650
2651 // try to resolve symlink
92f5a8d4
TL
2652 handle_wrapper h(
2653 create_file_handle(
2654 p.c_str(),
2655 0, // dwDesiredAccess; attributes only
2656 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
2657 0, // lpSecurityAttributes
2658 OPEN_EXISTING,
2659 FILE_FLAG_BACKUP_SEMANTICS,
2660 0)); // hTemplateFile
20effc67 2661
92f5a8d4 2662 if (h.handle == INVALID_HANDLE_VALUE)
7c673cae 2663 {
92f5a8d4 2664 return process_status_failure(p, ec);
7c673cae 2665 }
7c673cae 2666
20effc67
TL
2667 // take attributes of target
2668 BY_HANDLE_FILE_INFORMATION info;
2669 if (!::GetFileInformationByHandle(h.handle, &info))
2670 {
2671 return process_status_failure(p, ec);
2672 }
2673
2674 attr = info.dwFileAttributes;
7c673cae
FG
2675 }
2676
92f5a8d4
TL
2677 return (attr & FILE_ATTRIBUTE_DIRECTORY)
2678 ? file_status(directory_file, permissions)
2679 : file_status(regular_file, permissions);
2680
20effc67 2681#endif // defined(BOOST_POSIX_API)
92f5a8d4 2682}
7c673cae 2683
92f5a8d4
TL
2684BOOST_FILESYSTEM_DECL
2685file_status symlink_status(const path& p, error_code* ec)
7c673cae 2686{
20effc67
TL
2687 if (ec)
2688 ec->clear();
2689
2690#if defined(BOOST_POSIX_API)
2691
2692#if defined(BOOST_FILESYSTEM_HAS_STATX) || defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
2693 struct ::statx path_stat;
2694 int err = statx(AT_FDCWD, p.c_str(), AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT, STATX_TYPE | STATX_MODE, &path_stat);
2695#else
2696 struct ::stat path_stat;
2697 int err = ::lstat(p.c_str(), &path_stat);
2698#endif
92f5a8d4 2699
20effc67 2700 if (err != 0)
7c673cae 2701 {
20effc67 2702 err = errno;
92f5a8d4
TL
2703 if (ec != 0) // always report errno, even though some
2704 ec->assign(err, system_category()); // errno values are not status_errors
2705
2706 if (not_found_error(err)) // these are not errors
2707 {
2708 return fs::file_status(fs::file_not_found, fs::no_perms);
2709 }
2710 if (ec == 0)
20effc67 2711 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::symlink_status",
92f5a8d4
TL
2712 p, error_code(err, system_category())));
2713 return fs::file_status(fs::status_error);
7c673cae 2714 }
20effc67
TL
2715
2716#if defined(BOOST_FILESYSTEM_HAS_STATX) || defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
2717 if (BOOST_UNLIKELY((path_stat.stx_mask & (STATX_TYPE | STATX_MODE)) != (STATX_TYPE | STATX_MODE)))
2718 {
2719 emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::symlink_status");
2720 return fs::file_status(fs::status_error);
2721 }
2722#endif
2723
2724 const mode_t mode = get_mode(path_stat);
2725 if (S_ISREG(mode))
92f5a8d4 2726 return fs::file_status(fs::regular_file,
20effc67
TL
2727 static_cast<perms>(mode) & fs::perms_mask);
2728 if (S_ISDIR(mode))
92f5a8d4 2729 return fs::file_status(fs::directory_file,
20effc67
TL
2730 static_cast<perms>(mode) & fs::perms_mask);
2731 if (S_ISLNK(mode))
92f5a8d4 2732 return fs::file_status(fs::symlink_file,
20effc67
TL
2733 static_cast<perms>(mode) & fs::perms_mask);
2734 if (S_ISBLK(mode))
92f5a8d4 2735 return fs::file_status(fs::block_file,
20effc67
TL
2736 static_cast<perms>(mode) & fs::perms_mask);
2737 if (S_ISCHR(mode))
92f5a8d4 2738 return fs::file_status(fs::character_file,
20effc67
TL
2739 static_cast<perms>(mode) & fs::perms_mask);
2740 if (S_ISFIFO(mode))
92f5a8d4 2741 return fs::file_status(fs::fifo_file,
20effc67
TL
2742 static_cast<perms>(mode) & fs::perms_mask);
2743 if (S_ISSOCK(mode))
92f5a8d4 2744 return fs::file_status(fs::socket_file,
20effc67 2745 static_cast<perms>(mode) & fs::perms_mask);
92f5a8d4 2746 return fs::file_status(fs::type_unknown);
7c673cae 2747
20effc67 2748#else // defined(BOOST_POSIX_API)
92f5a8d4
TL
2749
2750 DWORD attr(::GetFileAttributesW(p.c_str()));
2751 if (attr == 0xFFFFFFFF)
7c673cae 2752 {
92f5a8d4 2753 return process_status_failure(p, ec);
7c673cae 2754 }
7c673cae 2755
92f5a8d4
TL
2756 perms permissions = make_permissions(p, attr);
2757
2758 if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
2759 return is_reparse_point_a_symlink(p)
2760 ? file_status(symlink_file, permissions)
2761 : file_status(reparse_file, permissions);
7c673cae 2762
92f5a8d4
TL
2763 return (attr & FILE_ATTRIBUTE_DIRECTORY)
2764 ? file_status(directory_file, permissions)
2765 : file_status(regular_file, permissions);
2766
20effc67 2767#endif // defined(BOOST_POSIX_API)
92f5a8d4
TL
2768}
2769
2770 // contributed by Jeff Flinn
2771BOOST_FILESYSTEM_DECL
2772path temp_directory_path(system::error_code* ec)
7c673cae 2773{
20effc67
TL
2774 if (ec)
2775 ec->clear();
2776
7c673cae 2777# ifdef BOOST_POSIX_API
92f5a8d4 2778 const char* val = 0;
7c673cae 2779
92f5a8d4
TL
2780 (val = std::getenv("TMPDIR" )) ||
2781 (val = std::getenv("TMP" )) ||
2782 (val = std::getenv("TEMP" )) ||
2783 (val = std::getenv("TEMPDIR"));
2784
2785# ifdef __ANDROID__
2786 const char* default_tmp = "/data/local/tmp";
7c673cae 2787# else
92f5a8d4 2788 const char* default_tmp = "/tmp";
7c673cae 2789# endif
92f5a8d4 2790 path p((val != NULL) ? val : default_tmp);
7c673cae 2791
20effc67 2792 if (BOOST_UNLIKELY(p.empty()))
7c673cae 2793 {
20effc67 2794 fail_not_dir:
92f5a8d4
TL
2795 error(ENOTDIR, p, ec, "boost::filesystem::temp_directory_path");
2796 return p;
7c673cae
FG
2797 }
2798
20effc67
TL
2799 file_status status = detail::status(p, ec);
2800 if (BOOST_UNLIKELY(ec && *ec))
2801 return path();
2802 if (BOOST_UNLIKELY(!is_directory(status)))
2803 goto fail_not_dir;
2804
92f5a8d4 2805 return p;
7c673cae 2806
20effc67
TL
2807# else // Windows
2808# if !defined(UNDER_CE)
92f5a8d4
TL
2809
2810 const wchar_t* tmp_env = L"TMP";
2811 const wchar_t* temp_env = L"TEMP";
2812 const wchar_t* localappdata_env = L"LOCALAPPDATA";
2813 const wchar_t* userprofile_env = L"USERPROFILE";
2814 const wchar_t* env_list[] = { tmp_env, temp_env, localappdata_env, userprofile_env };
7c673cae 2815
92f5a8d4
TL
2816 path p;
2817 for (unsigned int i = 0; i < sizeof(env_list) / sizeof(*env_list); ++i)
7c673cae 2818 {
92f5a8d4
TL
2819 std::wstring env = wgetenv(env_list[i]);
2820 if (!env.empty())
7c673cae 2821 {
92f5a8d4
TL
2822 p = env;
2823 if (i >= 2)
2824 p /= L"Temp";
2825 error_code lcl_ec;
2826 if (exists(p, lcl_ec) && !lcl_ec && is_directory(p, lcl_ec) && !lcl_ec)
2827 break;
2828 p.clear();
7c673cae 2829 }
7c673cae
FG
2830 }
2831
92f5a8d4 2832 if (p.empty())
7c673cae 2833 {
92f5a8d4
TL
2834 // use a separate buffer since in C++03 a string is not required to be contiguous
2835 const UINT size = ::GetWindowsDirectoryW(NULL, 0);
2836 if (BOOST_UNLIKELY(size == 0))
7c673cae 2837 {
92f5a8d4
TL
2838 getwindir_error:
2839 int errval = ::GetLastError();
2840 error(errval, ec, "boost::filesystem::temp_directory_path");
2841 return path();
7c673cae 2842 }
92f5a8d4
TL
2843
2844 boost::scoped_array<wchar_t> buf(new wchar_t[size]);
2845 if (BOOST_UNLIKELY(::GetWindowsDirectoryW(buf.get(), size) == 0))
2846 goto getwindir_error;
2847
2848 p = buf.get(); // do not depend on initial buf size, see ticket #10388
2849 p /= L"Temp";
7c673cae 2850 }
7c673cae 2851
92f5a8d4 2852 return p;
7c673cae 2853
20effc67
TL
2854# else // Windows CE
2855
2856 // Windows CE has no environment variables, so the same code as used for
2857 // regular Windows, above, doesn't work.
2858
2859 DWORD size = ::GetTempPathW(0, NULL);
2860 if (size == 0u)
2861 {
2862 fail:
2863 int errval = ::GetLastError();
2864 error(errval, ec, "boost::filesystem::temp_directory_path");
2865 return path();
2866 }
2867
2868 boost::scoped_array<wchar_t> buf(new wchar_t[size]);
2869 if (::GetTempPathW(size, buf.get()) == 0)
2870 goto fail;
2871
2872 path p(buf.get());
2873 p.remove_trailing_separator();
2874
2875 file_status status = detail::status(p, ec);
2876 if (ec && *ec)
2877 return path();
2878 if (!is_directory(status))
2879 {
2880 error(ERROR_PATH_NOT_FOUND, p, ec, "boost::filesystem::temp_directory_path");
2881 return path();
2882 }
2883
2884 return p;
2885
2886# endif // !defined(UNDER_CE)
92f5a8d4
TL
2887# endif
2888}
7c673cae 2889
92f5a8d4
TL
2890BOOST_FILESYSTEM_DECL
2891path system_complete(const path& p, system::error_code* ec)
7c673cae 2892{
92f5a8d4
TL
2893# ifdef BOOST_POSIX_API
2894 return (p.empty() || p.is_absolute())
2895 ? p : current_path() / p;
7c673cae 2896
92f5a8d4
TL
2897# else
2898 if (p.empty())
7c673cae 2899 {
92f5a8d4
TL
2900 if (ec != 0) ec->clear();
2901 return p;
7c673cae 2902 }
20effc67
TL
2903
2904 BOOST_CONSTEXPR_OR_CONST std::size_t buf_size = 128;
92f5a8d4
TL
2905 wchar_t buf[buf_size];
2906 wchar_t* pfn;
2907 std::size_t len = get_full_path_name(p, buf_size, buf, &pfn);
7c673cae 2908
92f5a8d4
TL
2909 if (error(len == 0 ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::system_complete"))
2910 return path();
7c673cae 2911
92f5a8d4
TL
2912 if (len < buf_size)// len does not include null termination character
2913 return path(&buf[0]);
7c673cae 2914
92f5a8d4 2915 boost::scoped_array<wchar_t> big_buf(new wchar_t[len]);
7c673cae 2916
92f5a8d4
TL
2917 return error(get_full_path_name(p, len , big_buf.get(), &pfn)== 0 ? BOOST_ERRNO : 0,
2918 p, ec, "boost::filesystem::system_complete")
2919 ? path()
2920 : path(big_buf.get());
2921# endif
2922}
7c673cae 2923
92f5a8d4
TL
2924BOOST_FILESYSTEM_DECL
2925path weakly_canonical(const path& p, system::error_code* ec)
2926{
2927 path head(p);
2928 path tail;
2929 system::error_code tmp_ec;
2930 path::iterator itr = p.end();
7c673cae 2931
92f5a8d4
TL
2932 for (; !head.empty(); --itr)
2933 {
2934 file_status head_status = status(head, tmp_ec);
2935 if (error(head_status.type() == fs::status_error,
2936 head, ec, "boost::filesystem::weakly_canonical"))
2937 return path();
2938 if (head_status.type() != fs::file_not_found)
2939 break;
2940 head.remove_filename();
2941 }
2942
2943 bool tail_has_dots = false;
2944 for (; itr != p.end(); ++itr)
2945 {
2946 tail /= *itr;
2947 // for a later optimization, track if any dot or dot-dot elements are present
2948 if (itr->native().size() <= 2
2949 && itr->native()[0] == dot
2950 && (itr->native().size() == 1 || itr->native()[1] == dot))
2951 tail_has_dots = true;
2952 }
2953
2954 if (head.empty())
2955 return p.lexically_normal();
2956 head = canonical(head, tmp_ec);
2957 if (error(tmp_ec.value(), head, ec, "boost::filesystem::weakly_canonical"))
2958 return path();
2959 return tail.empty()
2960 ? head
2961 : (tail_has_dots // optimization: only normalize if tail had dot or dot-dot element
2962 ? (head/tail).lexically_normal()
2963 : head/tail);
2964}
7c673cae 2965
92f5a8d4 2966} // namespace detail
7c673cae
FG
2967} // namespace filesystem
2968} // namespace boost