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