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