1 //  operations.cpp  --------------------------------------------------------------------//
2 
3 //  Copyright 2002-2009, 2014 Beman Dawes
4 //  Copyright 2001 Dietmar Kuehl
5 
6 //  Distributed under the Boost Software License, Version 1.0.
7 //  See http://www.boost.org/LICENSE_1_0.txt
8 
9 //  See library home page at http://www.boost.org/libs/filesystem
10 
11 //--------------------------------------------------------------------------------------//
12 
13 //  define 64-bit offset macros BEFORE including boost/config.hpp (see ticket #5355)
14 #if defined(__ANDROID__) && defined(__ANDROID_API__) && __ANDROID_API__ < 24
15 // Android fully supports 64-bit file offsets only for API 24 and above.
16 //
17 // Trying to define _FILE_OFFSET_BITS=64 for APIs below 24
18 // leads to compilation failure for one or another reason,
19 // depending on target Android API level, Android NDK version,
20 // used STL, order of include paths and more.
21 // For more information, please see:
22 // - https://github.com/boostorg/filesystem/issues/65
23 // - https://github.com/boostorg/filesystem/pull/69
24 //
25 // Android NDK developers consider it the expected behavior.
26 // See their official position here:
27 // - https://github.com/android-ndk/ndk/issues/501#issuecomment-326447479
28 // - https://android.googlesource.com/platform/bionic/+/a34817457feee026e8702a1d2dffe9e92b51d7d1/docs/32-bit-abi.md#32_bit-abi-bugs
29 //
30 // Thus we do not define _FILE_OFFSET_BITS in such case.
31 #else
32 // Defining _FILE_OFFSET_BITS=64 should kick in 64-bit off_t's
33 // (and thus st_size) on 32-bit systems that provide the Large File
34 // Support (LFS) interface, such as Linux, Solaris, and IRIX.
35 //
36 // At the time of this comment writing (March 2018), on most systems
37 // _FILE_OFFSET_BITS=64 definition is harmless:
38 // either the definition is supported and enables 64-bit off_t,
39 // or the definition is not supported and is ignored, in which case
40 // off_t does not change its default size for the target system
41 // (which may be 32-bit or 64-bit already).
42 // Thus it makes sense to have _FILE_OFFSET_BITS=64 defined by default,
43 // instead of listing every system that supports the definition.
44 // Those few systems, on which _FILE_OFFSET_BITS=64 is harmful,
45 // for example this definition causes compilation failure on those systems,
46 // should be exempt from defining _FILE_OFFSET_BITS by adding
47 // an appropriate #elif block above with the appropriate comment.
48 //
49 // _FILE_OFFSET_BITS must be defined before any headers are included
50 // to ensure that the definition is available to all included headers.
51 // That is required at least on Solaris, and possibly on other
52 // systems as well.
53 #define _FILE_OFFSET_BITS 64
54 #endif
55 
56 #ifndef BOOST_SYSTEM_NO_DEPRECATED
57 # define BOOST_SYSTEM_NO_DEPRECATED
58 #endif
59 
60 #ifndef _POSIX_PTHREAD_SEMANTICS
61 # define _POSIX_PTHREAD_SEMANTICS  // Sun readdir_r() needs this
62 #endif
63 
64 // Include Boost.Predef first so that windows.h is guaranteed to be not included
65 #include <boost/predef/os/windows.h>
66 #if BOOST_OS_WINDOWS
67 #include <boost/winapi/config.hpp>
68 #endif
69 
70 #include <boost/filesystem/config.hpp>
71 #include <boost/filesystem/operations.hpp>
72 #include <boost/filesystem/file_status.hpp>
73 #include <boost/filesystem/exception.hpp>
74 #include <boost/filesystem/directory.hpp>
75 #include <boost/system/error_code.hpp>
76 #include <boost/smart_ptr/scoped_array.hpp>
77 #include <boost/detail/workaround.hpp>
78 #include <boost/cstdint.hpp>
79 #include <boost/assert.hpp>
80 #include <new> // std::bad_alloc
81 #include <limits>
82 #include <string>
83 #include <cstddef>
84 #include <cstdlib>     // for malloc, free
85 #include <cstring>
86 #include <cstdio>      // for remove, rename
87 #if defined(__QNXNTO__)  // see ticket #5355
88 # include <stdio.h>
89 #endif
90 #include <cerrno>
91 
92 #ifdef BOOST_FILEYSTEM_INCLUDE_IOSTREAM
93 # include <iostream>
94 #endif
95 
96 # ifdef BOOST_POSIX_API
97 
98 #   include <sys/types.h>
99 #   include <sys/stat.h>
100 #   if !defined(__APPLE__) && !defined(__OpenBSD__) && !defined(__ANDROID__) \
101  && !defined(__VXWORKS__)
102 #     include <sys/statvfs.h>
103 #     define BOOST_STATVFS statvfs
104 #     define BOOST_STATVFS_F_FRSIZE vfs.f_frsize
105 #   else
106 #     ifdef __OpenBSD__
107 #       include <sys/param.h>
108 #     elif defined(__ANDROID__)
109 #       include <sys/vfs.h>
110 #     endif
111 #     if !defined(__VXWORKS__)
112 #       include <sys/mount.h>
113 #     endif
114 #     define BOOST_STATVFS statfs
115 #     define BOOST_STATVFS_F_FRSIZE static_cast<boost::uintmax_t>(vfs.f_bsize)
116 #   endif
117 #   include <unistd.h>
118 #   include <fcntl.h>
119 #   if _POSIX_C_SOURCE < 200809L
120 #     include <utime.h>
121 #   endif
122 #   include "limits.h"
123 
124 # else // BOOST_WINDOWS_API
125 
126 #   include <boost/winapi/dll.hpp> // get_proc_address, GetModuleHandleW
127 #   include <cwchar>
128 #   include <io.h>
129 #   include <windows.h>
130 #   include <winnt.h>
131 #   if defined(__BORLANDC__) || defined(__MWERKS__)
132 #     if defined(__BORLANDC__)
133         using std::time_t;
134 #     endif
135 #     include <utime.h>
136 #   else
137 #     include <sys/utime.h>
138 #   endif
139 
140 #include "windows_tools.hpp"
141 
142 # endif  // BOOST_WINDOWS_API
143 
144 #include "error_handling.hpp"
145 
146 namespace fs = boost::filesystem;
147 using boost::filesystem::path;
148 using boost::filesystem::filesystem_error;
149 using boost::filesystem::perms;
150 using boost::system::error_code;
151 using boost::system::system_category;
152 
153 # if defined(BOOST_WINDOWS_API)
154 
155 //  REPARSE_DATA_BUFFER related definitions are found in ntifs.h, which is part of the
156 //  Windows Device Driver Kit. Since that's inconvenient, the definitions are provided
157 //  here. See http://msdn.microsoft.com/en-us/library/ms791514.aspx
158 
159 #if !defined(REPARSE_DATA_BUFFER_HEADER_SIZE)  // mingw winnt.h does provide the defs
160 
161 #define SYMLINK_FLAG_RELATIVE 1
162 
163 typedef struct _REPARSE_DATA_BUFFER {
164   ULONG  ReparseTag;
165   USHORT  ReparseDataLength;
166   USHORT  Reserved;
167   union {
168     struct {
169       USHORT  SubstituteNameOffset;
170       USHORT  SubstituteNameLength;
171       USHORT  PrintNameOffset;
172       USHORT  PrintNameLength;
173       ULONG  Flags;
174       WCHAR  PathBuffer[1];
175   /*  Example of distinction between substitute and print names:
176         mklink /d ldrive c:\
177         SubstituteName: c:\\??\
178         PrintName: c:\
179   */
180      } SymbolicLinkReparseBuffer;
181     struct {
182       USHORT  SubstituteNameOffset;
183       USHORT  SubstituteNameLength;
184       USHORT  PrintNameOffset;
185       USHORT  PrintNameLength;
186       WCHAR  PathBuffer[1];
187     } MountPointReparseBuffer;
188     struct {
189       UCHAR  DataBuffer[1];
190     } GenericReparseBuffer;
191   };
192 } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
193 
194 #define REPARSE_DATA_BUFFER_HEADER_SIZE \
195   FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)
196 
197 #endif
198 
199 #ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE
200 #define MAXIMUM_REPARSE_DATA_BUFFER_SIZE  ( 16 * 1024 )
201 #endif
202 
203 # ifndef FSCTL_GET_REPARSE_POINT
204 #   define FSCTL_GET_REPARSE_POINT 0x900a8
205 # endif
206 
207 # ifndef IO_REPARSE_TAG_SYMLINK
208 #   define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
209 # endif
210 
211 # endif  // BOOST_WINDOWS_API
212 
213 //  POSIX/Windows macros  ----------------------------------------------------//
214 
215 //  Portions of the POSIX and Windows API's are very similar, except for name,
216 //  order of arguments, and meaning of zero/non-zero returns. The macros below
217 //  abstract away those differences. They follow Windows naming and order of
218 //  arguments, and return true to indicate no error occurred. [POSIX naming,
219 //  order of arguments, and meaning of return were followed initially, but
220 //  found to be less clear and cause more coding errors.]
221 
222 # if defined(BOOST_POSIX_API)
223 
224 #   define BOOST_SET_CURRENT_DIRECTORY(P)(::chdir(P)== 0)
225 #   define BOOST_CREATE_DIRECTORY(P)(::mkdir(P, S_IRWXU|S_IRWXG|S_IRWXO)== 0)
226 #   define BOOST_CREATE_HARD_LINK(F,T)(::link(T, F)== 0)
227 #   define BOOST_CREATE_SYMBOLIC_LINK(F,T,Flag)(::symlink(T, F)== 0)
228 #   define BOOST_REMOVE_DIRECTORY(P)(::rmdir(P)== 0)
229 #   define BOOST_DELETE_FILE(P)(::unlink(P)== 0)
230 #   define BOOST_COPY_DIRECTORY(F,T)(!(::stat(from.c_str(), &from_stat)!= 0\
231          || ::mkdir(to.c_str(),from_stat.st_mode)!= 0))
232 #   define BOOST_COPY_FILE(F,T,FailIfExistsBool)copy_file_api(F, T, FailIfExistsBool)
233 #   define BOOST_MOVE_FILE(OLD,NEW)(::rename(OLD, NEW)== 0)
234 #   define BOOST_RESIZE_FILE(P,SZ)(::truncate(P, SZ)== 0)
235 
236 # else  // BOOST_WINDOWS_API
237 
238 #   define BOOST_SET_CURRENT_DIRECTORY(P)(::SetCurrentDirectoryW(P)!= 0)
239 #   define BOOST_CREATE_DIRECTORY(P)(::CreateDirectoryW(P, 0)!= 0)
240 #   define BOOST_CREATE_HARD_LINK(F,T)(create_hard_link_api(F, T, 0)!= 0)
241 #   define BOOST_CREATE_SYMBOLIC_LINK(F,T,Flag)(create_symbolic_link_api(F, T, Flag)!= 0)
242 #   define BOOST_REMOVE_DIRECTORY(P)(::RemoveDirectoryW(P)!= 0)
243 #   define BOOST_DELETE_FILE(P)(::DeleteFileW(P)!= 0)
244 #   define BOOST_COPY_DIRECTORY(F,T)(::CreateDirectoryExW(F, T, 0)!= 0)
245 #   define BOOST_COPY_FILE(F,T,FailIfExistsBool)(::CopyFileW(F, T, FailIfExistsBool)!= 0)
246 #   define BOOST_MOVE_FILE(OLD,NEW)(::MoveFileExW(OLD, NEW, MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED)!= 0)
247 #   define BOOST_RESIZE_FILE(P,SZ)(resize_file_api(P, SZ)!= 0)
248 #   define BOOST_READ_SYMLINK(P,T)
249 
250 # endif
251 
252 namespace boost {
253 namespace filesystem {
254 namespace detail {
255 
256 //--------------------------------------------------------------------------------------//
257 //                                                                                      //
258 //                        helpers (all operating systems)                               //
259 //                                                                                      //
260 //--------------------------------------------------------------------------------------//
261 
262 namespace {
263 
264 // Absolute maximum path length, in bytes, that we're willing to accept from various system calls.
265 // This value is arbitrary, it is supposed to be a hard limit to avoid memory exhaustion
266 // in some of the algorithms below in case of some corrupted or maliciously broken filesystem.
267 BOOST_CONSTEXPR_OR_CONST std::size_t absolute_path_max = 16u * 1024u * 1024u;
268 
269 fs::file_type query_file_type(const path& p, error_code* ec);
270 
271 //  general helpers  -----------------------------------------------------------------//
272 
is_empty_directory(const path & p,error_code * ec)273 bool is_empty_directory(const path& p, error_code* ec)
274 {
275   return (ec != 0 ? fs::directory_iterator(p, *ec) : fs::directory_iterator(p))
276     == fs::directory_iterator();
277 }
278 
279 bool not_found_error(int errval) BOOST_NOEXCEPT; // forward declaration
280 
281 // only called if directory exists
remove_directory(const path & p)282 bool remove_directory(const path& p) // true if succeeds or not found
283 {
284   return BOOST_REMOVE_DIRECTORY(p.c_str())
285     || not_found_error(BOOST_ERRNO);  // mitigate possible file system race. See #11166
286 }
287 
288 // only called if file exists
remove_file(const path & p)289 bool remove_file(const path& p) // true if succeeds or not found
290 {
291   return BOOST_DELETE_FILE(p.c_str())
292     || not_found_error(BOOST_ERRNO);  // mitigate possible file system race. See #11166
293 }
294 
295 // called by remove and remove_all_aux
remove_file_or_directory(const path & p,fs::file_type type,error_code * ec)296 bool remove_file_or_directory(const path& p, fs::file_type type, error_code* ec)
297   // return true if file removed, false if not removed
298 {
299   if (type == fs::file_not_found)
300   {
301     if (ec != 0) ec->clear();
302     return false;
303   }
304 
305   if (type == fs::directory_file
306 #     ifdef BOOST_WINDOWS_API
307       || type == fs::_detail_directory_symlink
308 #     endif
309     )
310   {
311     if (error(!remove_directory(p) ? BOOST_ERRNO : 0, p, ec,
312       "boost::filesystem::remove"))
313         return false;
314   }
315   else
316   {
317     if (error(!remove_file(p) ? BOOST_ERRNO : 0, p, ec,
318       "boost::filesystem::remove"))
319         return false;
320   }
321   return true;
322 }
323 
remove_all_aux(const path & p,fs::file_type type,error_code * ec)324 boost::uintmax_t remove_all_aux(const path& p, fs::file_type type,
325   error_code* ec)
326 {
327   boost::uintmax_t count = 0;
328 
329   if (type == fs::directory_file)  // but not a directory symlink
330   {
331     fs::directory_iterator itr;
332     if (ec != 0)
333     {
334       itr = fs::directory_iterator(p, *ec);
335       if (*ec)
336         return count;
337     }
338     else
339       itr = fs::directory_iterator(p);
340 
341     const fs::directory_iterator end_dit;
342     while(itr != end_dit)
343     {
344       fs::file_type tmp_type = query_file_type(itr->path(), ec);
345       if (ec != 0 && *ec)
346         return count;
347 
348       count += remove_all_aux(itr->path(), tmp_type, ec);
349       if (ec != 0 && *ec)
350         return count;
351 
352       fs::detail::directory_iterator_increment(itr, ec);
353       if (ec != 0 && *ec)
354         return count;
355     }
356   }
357 
358   remove_file_or_directory(p, type, ec);
359   if (ec != 0 && *ec)
360     return count;
361 
362   return ++count;
363 }
364 
365 #ifdef BOOST_POSIX_API
366 
367 //--------------------------------------------------------------------------------------//
368 //                                                                                      //
369 //                            POSIX-specific helpers                                    //
370 //                                                                                      //
371 //--------------------------------------------------------------------------------------//
372 
373 BOOST_CONSTEXPR_OR_CONST char dot = '.';
374 
not_found_error(int errval)375 inline bool not_found_error(int errval) BOOST_NOEXCEPT
376 {
377   return errval == ENOENT || errval == ENOTDIR;
378 }
379 
380 bool // true if ok
copy_file_api(const std::string & from_p,const std::string & to_p,bool fail_if_exists)381 copy_file_api(const std::string& from_p,
382   const std::string& to_p, bool fail_if_exists)
383 {
384   BOOST_CONSTEXPR_OR_CONST std::size_t buf_sz = 65536;
385   boost::scoped_array<char> buf(new char [buf_sz]);
386   int infile=-1, outfile=-1;  // -1 means not open
387 
388   // bug fixed: code previously did a stat()on the from_file first, but that
389   // introduced a gratuitous race condition; the stat()is now done after the open()
390 
391   if ((infile = ::open(from_p.c_str(), O_RDONLY))< 0)
392     { return false; }
393 
394   struct stat from_stat;
395   if (::stat(from_p.c_str(), &from_stat)!= 0)
396   {
397     ::close(infile);
398     return false;
399   }
400 
401   int oflag = O_CREAT | O_WRONLY | O_TRUNC;
402   if (fail_if_exists)
403     oflag |= O_EXCL;
404   if ((outfile = ::open(to_p.c_str(), oflag, from_stat.st_mode)) < 0)
405   {
406     const int open_errno = errno;
407     BOOST_ASSERT(infile >= 0);
408     ::close(infile);
409     errno = open_errno;
410     return false;
411   }
412 
413   ssize_t sz, sz_read=1, sz_write;
414   while (sz_read > 0
415     && (sz_read = ::read(infile, buf.get(), buf_sz)) > 0)
416   {
417     // Allow for partial writes - see Advanced Unix Programming (2nd Ed.),
418     // Marc Rochkind, Addison-Wesley, 2004, page 94
419     sz_write = 0;
420     do
421     {
422       BOOST_ASSERT(sz_read - sz_write > 0);  // #1
423         // ticket 4438 claimed possible infinite loop if write returns 0. My analysis
424         // is that POSIX specifies 0 return only if 3rd arg is 0, and that will never
425         // happen due to loop entry and coninuation conditions. BOOST_ASSERT #1 above
426         // and #2 below added to verify that analysis.
427       if ((sz = ::write(outfile, buf.get() + sz_write,
428         sz_read - sz_write)) < 0)
429       {
430         sz_read = sz; // cause read loop termination
431         break;        //  and error reported after closes
432       }
433       BOOST_ASSERT(sz > 0);                  // #2
434       sz_write += sz;
435     } while (sz_write < sz_read);
436   }
437 
438   if (::close(infile)< 0)
439     sz_read = -1;
440   if (::close(outfile)< 0)
441     sz_read = -1;
442 
443   return sz_read >= 0;
444 }
445 
query_file_type(const path & p,error_code * ec)446 inline fs::file_type query_file_type(const path& p, error_code* ec)
447 {
448   return fs::detail::symlink_status(p, ec).type();
449 }
450 
451 # else
452 
453 //--------------------------------------------------------------------------------------//
454 //                                                                                      //
455 //                            Windows-specific helpers                                  //
456 //                                                                                      //
457 //--------------------------------------------------------------------------------------//
458 
459 BOOST_CONSTEXPR_OR_CONST std::size_t buf_size = 128;
460 
461 BOOST_CONSTEXPR_OR_CONST wchar_t dot = L'.';
462 
wgetenv(const wchar_t * name)463 inline std::wstring wgetenv(const wchar_t* name)
464 {
465   // use a separate buffer since C++03 basic_string is not required to be contiguous
466   const DWORD size = ::GetEnvironmentVariableW(name, NULL, 0);
467   if (size > 0)
468   {
469     boost::scoped_array<wchar_t> buf(new wchar_t[size]);
470     if (BOOST_LIKELY(::GetEnvironmentVariableW(name, buf.get(), size) > 0))
471       return std::wstring(buf.get());
472   }
473 
474   return std::wstring();
475 }
476 
not_found_error(int errval)477 inline bool not_found_error(int errval) BOOST_NOEXCEPT
478 {
479   return errval == ERROR_FILE_NOT_FOUND
480     || errval == ERROR_PATH_NOT_FOUND
481     || errval == ERROR_INVALID_NAME  // "tools/jam/src/:sys:stat.h", "//foo"
482     || errval == ERROR_INVALID_DRIVE  // USB card reader with no card inserted
483     || errval == ERROR_NOT_READY  // CD/DVD drive with no disc inserted
484     || errval == ERROR_INVALID_PARAMETER  // ":sys:stat.h"
485     || errval == ERROR_BAD_PATHNAME  // "//nosuch" on Win64
486     || errval == ERROR_BAD_NETPATH;  // "//nosuch" on Win32
487 }
488 
489 // these constants come from inspecting some Microsoft sample code
to_time_t(const FILETIME & ft)490 std::time_t to_time_t(const FILETIME & ft)
491 {
492   __int64 t = (static_cast<__int64>(ft.dwHighDateTime)<< 32)
493     + ft.dwLowDateTime;
494 #   if !defined(BOOST_MSVC) || BOOST_MSVC > 1300 // > VC++ 7.0
495   t -= 116444736000000000LL;
496 #   else
497   t -= 116444736000000000;
498 #   endif
499   t /= 10000000;
500   return static_cast<std::time_t>(t);
501 }
502 
to_FILETIME(std::time_t t,FILETIME & ft)503 void to_FILETIME(std::time_t t, FILETIME & ft)
504 {
505   __int64 temp = t;
506   temp *= 10000000;
507 #   if !defined(BOOST_MSVC) || BOOST_MSVC > 1300 // > VC++ 7.0
508   temp += 116444736000000000LL;
509 #   else
510   temp += 116444736000000000;
511 #   endif
512   ft.dwLowDateTime = static_cast<DWORD>(temp);
513   ft.dwHighDateTime = static_cast<DWORD>(temp >> 32);
514 }
515 
516 // Thanks to Jeremy Maitin-Shepard for much help and for permission to
517 // base the equivalent()implementation on portions of his
518 // file-equivalence-win32.cpp experimental code.
519 
520 struct handle_wrapper
521 {
522   HANDLE handle;
handle_wrapperboost::filesystem::detail::__anon438e356b0511::handle_wrapper523   handle_wrapper(HANDLE h)
524     : handle(h){}
~handle_wrapperboost::filesystem::detail::__anon438e356b0511::handle_wrapper525   ~handle_wrapper()
526   {
527     if (handle != INVALID_HANDLE_VALUE)
528       ::CloseHandle(handle);
529   }
530 };
531 
create_file_handle(const path & p,DWORD dwDesiredAccess,DWORD dwShareMode,LPSECURITY_ATTRIBUTES lpSecurityAttributes,DWORD dwCreationDisposition,DWORD dwFlagsAndAttributes,HANDLE hTemplateFile)532 HANDLE create_file_handle(const path& p, DWORD dwDesiredAccess,
533   DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes,
534   DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes,
535   HANDLE hTemplateFile)
536 {
537   return ::CreateFileW(p.c_str(), dwDesiredAccess, dwShareMode,
538     lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
539     hTemplateFile);
540 }
541 
is_reparse_point_a_symlink(const path & p)542 bool is_reparse_point_a_symlink(const path& p)
543 {
544   handle_wrapper h(create_file_handle(p, FILE_READ_EA,
545     FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
546     FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL));
547   if (h.handle == INVALID_HANDLE_VALUE)
548     return false;
549 
550   boost::scoped_array<char> buf(new char [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
551 
552   // Query the reparse data
553   DWORD dwRetLen;
554   BOOL result = ::DeviceIoControl(h.handle, FSCTL_GET_REPARSE_POINT, NULL, 0, buf.get(),
555     MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &dwRetLen, NULL);
556   if (!result) return false;
557 
558   return reinterpret_cast<const REPARSE_DATA_BUFFER*>(buf.get())->ReparseTag
559       == IO_REPARSE_TAG_SYMLINK
560       // Issue 9016 asked that NTFS directory junctions be recognized as directories.
561       // That is equivalent to recognizing them as symlinks, and then the normal symlink
562       // mechanism will take care of recognizing them as directories.
563       //
564       // Directory junctions are very similar to symlinks, but have some performance
565       // and other advantages over symlinks. They can be created from the command line
566       // with "mklink /j junction-name target-path".
567     || reinterpret_cast<const REPARSE_DATA_BUFFER*>(buf.get())->ReparseTag
568       == IO_REPARSE_TAG_MOUNT_POINT;  // aka "directory junction" or "junction"
569 }
570 
get_full_path_name(const path & src,std::size_t len,wchar_t * buf,wchar_t ** p)571 inline std::size_t get_full_path_name(
572   const path& src, std::size_t len, wchar_t* buf, wchar_t** p)
573 {
574   return static_cast<std::size_t>(
575     ::GetFullPathNameW(src.c_str(), static_cast<DWORD>(len), buf, p));
576 }
577 
process_status_failure(const path & p,error_code * ec)578 fs::file_status process_status_failure(const path& p, error_code* ec)
579 {
580   int errval(::GetLastError());
581   if (ec != 0)                             // always report errval, even though some
582     ec->assign(errval, system_category());   // errval values are not status_errors
583 
584   if (not_found_error(errval))
585   {
586     return fs::file_status(fs::file_not_found, fs::no_perms);
587   }
588   else if (errval == ERROR_SHARING_VIOLATION)
589   {
590     return fs::file_status(fs::type_unknown);
591   }
592   if (ec == 0)
593     BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::status",
594       p, error_code(errval, system_category())));
595   return fs::file_status(fs::status_error);
596 }
597 
598 //  differs from symlink_status() in that directory symlinks are reported as
599 //  _detail_directory_symlink, as required on Windows by remove() and its helpers.
query_file_type(const path & p,error_code * ec)600 fs::file_type query_file_type(const path& p, error_code* ec)
601 {
602   DWORD attr(::GetFileAttributesW(p.c_str()));
603   if (attr == 0xFFFFFFFF)
604   {
605     return process_status_failure(p, ec).type();
606   }
607 
608   if (ec != 0) ec->clear();
609 
610   if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
611   {
612     if (is_reparse_point_a_symlink(p))
613       return (attr & FILE_ATTRIBUTE_DIRECTORY)
614         ? fs::_detail_directory_symlink
615         : fs::symlink_file;
616     return fs::reparse_file;
617   }
618 
619   return (attr & FILE_ATTRIBUTE_DIRECTORY)
620     ? fs::directory_file
621     : fs::regular_file;
622 }
623 
resize_file_api(const wchar_t * p,boost::uintmax_t size)624 BOOL resize_file_api(const wchar_t* p, boost::uintmax_t size)
625 {
626   handle_wrapper h(CreateFileW(p, GENERIC_WRITE, 0, 0, OPEN_EXISTING,
627                               FILE_ATTRIBUTE_NORMAL, 0));
628   LARGE_INTEGER sz;
629   sz.QuadPart = size;
630   return h.handle != INVALID_HANDLE_VALUE
631     && ::SetFilePointerEx(h.handle, sz, 0, FILE_BEGIN)
632     && ::SetEndOfFile(h.handle);
633 }
634 
635 //  Windows kernel32.dll functions that may or may not be present
636 //  must be accessed through pointers
637 
638 typedef BOOL (WINAPI *PtrCreateHardLinkW)(
639   /*__in*/       LPCWSTR lpFileName,
640   /*__in*/       LPCWSTR lpExistingFileName,
641   /*__reserved*/ LPSECURITY_ATTRIBUTES lpSecurityAttributes
642  );
643 
644 PtrCreateHardLinkW create_hard_link_api = PtrCreateHardLinkW(
645   boost::winapi::get_proc_address(
646     boost::winapi::GetModuleHandleW(L"kernel32.dll"), "CreateHardLinkW"));
647 
648 typedef BOOLEAN (WINAPI *PtrCreateSymbolicLinkW)(
649   /*__in*/ LPCWSTR lpSymlinkFileName,
650   /*__in*/ LPCWSTR lpTargetFileName,
651   /*__in*/ DWORD dwFlags
652  );
653 
654 PtrCreateSymbolicLinkW create_symbolic_link_api = PtrCreateSymbolicLinkW(
655   boost::winapi::get_proc_address(
656     boost::winapi::GetModuleHandleW(L"kernel32.dll"), "CreateSymbolicLinkW"));
657 
658 #endif
659 
660 //#ifdef BOOST_WINDOWS_API
661 //
662 //
663 //  inline bool get_free_disk_space(const std::wstring& ph,
664 //    PULARGE_INTEGER avail, PULARGE_INTEGER total, PULARGE_INTEGER free)
665 //    { return ::GetDiskFreeSpaceExW(ph.c_str(), avail, total, free)!= 0; }
666 //
667 //#endif
668 
669 } // unnamed namespace
670 } // namespace detail
671 
672 //--------------------------------------------------------------------------------------//
673 //                                                                                      //
674 //                operations functions declared in operations.hpp                       //
675 //                            in alphabetic order                                       //
676 //                                                                                      //
677 //--------------------------------------------------------------------------------------//
678 
679 BOOST_FILESYSTEM_DECL
absolute(const path & p,const path & base)680 path absolute(const path& p, const path& base)
681 {
682 //  if ( p.empty() || p.is_absolute() )
683 //    return p;
684 //  //  recursively calling absolute is sub-optimal, but is simple
685 //  path abs_base(base.is_absolute() ? base : absolute(base));
686 //# ifdef BOOST_WINDOWS_API
687 //  if (p.has_root_directory())
688 //    return abs_base.root_name() / p;
689 //  //  !p.has_root_directory
690 //  if (p.has_root_name())
691 //    return p.root_name()
692 //      / abs_base.root_directory() / abs_base.relative_path() / p.relative_path();
693 //  //  !p.has_root_name()
694 //# endif
695 //  return abs_base / p;
696 
697   //  recursively calling absolute is sub-optimal, but is sure and simple
698   path abs_base(base.is_absolute() ? base : absolute(base));
699 
700   //  store expensive to compute values that are needed multiple times
701   path p_root_name (p.root_name());
702   path base_root_name (abs_base.root_name());
703   path p_root_directory (p.root_directory());
704 
705   if (p.empty())
706     return abs_base;
707 
708   if (!p_root_name.empty())  // p.has_root_name()
709   {
710     if (p_root_directory.empty())  // !p.has_root_directory()
711       return p_root_name / abs_base.root_directory()
712       / abs_base.relative_path() / p.relative_path();
713     // p is absolute, so fall through to return p at end of block
714   }
715   else if (!p_root_directory.empty())  // p.has_root_directory()
716   {
717 #   ifdef BOOST_POSIX_API
718     // POSIX can have root name it it is a network path
719     if (base_root_name.empty())   // !abs_base.has_root_name()
720       return p;
721 #   endif
722     return base_root_name / p;
723   }
724   else
725   {
726     return abs_base / p;
727   }
728 
729   return p;  // p.is_absolute() is true
730 }
731 
732 namespace detail {
733 
possible_large_file_size_support()734 BOOST_FILESYSTEM_DECL bool possible_large_file_size_support()
735 {
736 # ifdef BOOST_POSIX_API
737   typedef struct stat struct_stat;
738   return sizeof(struct_stat().st_size) > 4;
739 # else
740   return true;
741 # endif
742 }
743 
744 BOOST_FILESYSTEM_DECL
canonical(const path & p,const path & base,system::error_code * ec)745 path canonical(const path& p, const path& base, system::error_code* ec)
746 {
747   path source (p.is_absolute() ? p : absolute(p, base));
748   path root(source.root_path());
749   path result;
750 
751   system::error_code local_ec;
752   file_status stat (status(source, local_ec));
753 
754   if (stat.type() == fs::file_not_found)
755   {
756     if (ec == 0)
757       BOOST_FILESYSTEM_THROW(filesystem_error(
758         "boost::filesystem::canonical", source,
759         error_code(system::errc::no_such_file_or_directory, system::generic_category())));
760     ec->assign(system::errc::no_such_file_or_directory, system::generic_category());
761     return result;
762   }
763   else if (local_ec)
764   {
765     if (ec == 0)
766       BOOST_FILESYSTEM_THROW(filesystem_error(
767         "boost::filesystem::canonical", source, local_ec));
768     *ec = local_ec;
769     return result;
770   }
771 
772   bool scan = true;
773   while (scan)
774   {
775     scan = false;
776     result.clear();
777     for (path::iterator itr = source.begin(); itr != source.end(); ++itr)
778     {
779       if (*itr == dot_path())
780         continue;
781       if (*itr == dot_dot_path())
782       {
783         if (result != root)
784           result.remove_filename();
785         continue;
786       }
787 
788       result /= *itr;
789 
790       bool is_sym (is_symlink(detail::symlink_status(result, ec)));
791       if (ec && *ec)
792         return path();
793 
794       if (is_sym)
795       {
796         path link(detail::read_symlink(result, ec));
797         if (ec && *ec)
798           return path();
799         result.remove_filename();
800 
801         if (link.is_absolute())
802         {
803           for (++itr; itr != source.end(); ++itr)
804             link /= *itr;
805           source = link;
806         }
807         else // link is relative
808         {
809           path new_source(result);
810           new_source /= link;
811           for (++itr; itr != source.end(); ++itr)
812             new_source /= *itr;
813           source = new_source;
814         }
815         scan = true;  // symlink causes scan to be restarted
816         break;
817       }
818     }
819   }
820   if (ec != 0)
821     ec->clear();
822   BOOST_ASSERT_MSG(result.is_absolute(), "canonical() implementation error; please report");
823   return result;
824 }
825 
826 BOOST_FILESYSTEM_DECL
copy(const path & from,const path & to,system::error_code * ec)827 void copy(const path& from, const path& to, system::error_code* ec)
828 {
829   file_status s(detail::symlink_status(from, ec));
830   if (ec != 0 && *ec) return;
831 
832   if(is_symlink(s))
833   {
834     detail::copy_symlink(from, to, ec);
835   }
836   else if(is_directory(s))
837   {
838     detail::copy_directory(from, to, ec);
839   }
840   else if(is_regular_file(s))
841   {
842     detail::copy_file(from, to, detail::fail_if_exists, ec);
843   }
844   else
845   {
846     if (ec == 0)
847       BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::copy",
848         from, to, error_code(BOOST_ERROR_NOT_SUPPORTED, system_category())));
849     ec->assign(BOOST_ERROR_NOT_SUPPORTED, system_category());
850   }
851 }
852 
853 BOOST_FILESYSTEM_DECL
copy_directory(const path & from,const path & to,system::error_code * ec)854 void copy_directory(const path& from, const path& to, system::error_code* ec)
855 {
856 # ifdef BOOST_POSIX_API
857   struct stat from_stat;
858 # endif
859   error(!BOOST_COPY_DIRECTORY(from.c_str(), to.c_str()) ? BOOST_ERRNO : 0,
860     from, to, ec, "boost::filesystem::copy_directory");
861 }
862 
863 BOOST_FILESYSTEM_DECL
copy_file(const path & from,const path & to,copy_option option,error_code * ec)864 void copy_file(const path& from, const path& to, copy_option option, error_code* ec)
865 {
866   error(!BOOST_COPY_FILE(from.c_str(), to.c_str(),
867     option == fail_if_exists) ? BOOST_ERRNO : 0,
868       from, to, ec, "boost::filesystem::copy_file");
869 }
870 
871 BOOST_FILESYSTEM_DECL
copy_symlink(const path & existing_symlink,const path & new_symlink,system::error_code * ec)872 void copy_symlink(const path& existing_symlink, const path& new_symlink,
873   system::error_code* ec)
874 {
875 # if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0600
876   error(BOOST_ERROR_NOT_SUPPORTED, new_symlink, existing_symlink, ec,
877     "boost::filesystem::copy_symlink");
878 
879 # else  // modern Windows or BOOST_POSIX_API
880   path p(read_symlink(existing_symlink, ec));
881   if (ec != 0 && *ec) return;
882   create_symlink(p, new_symlink, ec);
883 
884 # endif
885 }
886 
887 BOOST_FILESYSTEM_DECL
create_directories(const path & p,system::error_code * ec)888 bool create_directories(const path& p, system::error_code* ec)
889 {
890  if (p.empty())
891  {
892    if (ec == 0)
893      BOOST_FILESYSTEM_THROW(filesystem_error(
894        "boost::filesystem::create_directories", p,
895        system::errc::make_error_code(system::errc::invalid_argument)));
896    else
897      ec->assign(system::errc::invalid_argument, system::generic_category());
898    return false;
899  }
900 
901   if (p.filename_is_dot() || p.filename_is_dot_dot())
902     return create_directories(p.parent_path(), ec);
903 
904   error_code local_ec;
905   file_status p_status = status(p, local_ec);
906 
907   if (p_status.type() == directory_file)
908   {
909     if (ec != 0)
910       ec->clear();
911     return false;
912   }
913 
914   path parent = p.parent_path();
915   BOOST_ASSERT_MSG(parent != p, "internal error: p == p.parent_path()");
916   if (!parent.empty())
917   {
918     // determine if the parent exists
919     file_status parent_status = status(parent, local_ec);
920 
921     // if the parent does not exist, create the parent
922     if (parent_status.type() == file_not_found)
923     {
924       create_directories(parent, local_ec);
925       if (local_ec)
926       {
927         if (ec == 0)
928           BOOST_FILESYSTEM_THROW(filesystem_error(
929             "boost::filesystem::create_directories", parent, local_ec));
930         else
931           *ec = local_ec;
932         return false;
933       }
934     }
935   }
936 
937   // create the directory
938   return create_directory(p, ec);
939 }
940 
941 BOOST_FILESYSTEM_DECL
create_directory(const path & p,error_code * ec)942 bool create_directory(const path& p, error_code* ec)
943 {
944   if (BOOST_CREATE_DIRECTORY(p.c_str()))
945   {
946     if (ec != 0)
947       ec->clear();
948     return true;
949   }
950 
951   //  attempt to create directory failed
952   int errval(BOOST_ERRNO);  // save reason for failure
953   error_code dummy;
954 
955   if (is_directory(p, dummy))
956   {
957     if (ec != 0)
958       ec->clear();
959     return false;
960   }
961 
962   //  attempt to create directory failed && it doesn't already exist
963   if (ec == 0)
964     BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::create_directory",
965       p, error_code(errval, system_category())));
966   else
967     ec->assign(errval, system_category());
968 
969   return false;
970 }
971 
972 BOOST_FILESYSTEM_DECL
create_directory_symlink(const path & to,const path & from,system::error_code * ec)973 void create_directory_symlink(const path& to, const path& from,
974                                system::error_code* ec)
975 {
976 # if defined(BOOST_WINDOWS_API) && _WIN32_WINNT < 0x0600  // SDK earlier than Vista and Server 2008
977 
978   error(BOOST_ERROR_NOT_SUPPORTED, to, from, ec,
979     "boost::filesystem::create_directory_symlink");
980 # else
981 
982 #   if defined(BOOST_WINDOWS_API) && _WIN32_WINNT >= 0x0600
983       // see if actually supported by Windows runtime dll
984       if (error(!create_symbolic_link_api ? BOOST_ERROR_NOT_SUPPORTED : 0, to, from, ec,
985           "boost::filesystem::create_directory_symlink"))
986         return;
987 #   endif
988 
989   error(!BOOST_CREATE_SYMBOLIC_LINK(from.c_str(), to.c_str(),
990     SYMBOLIC_LINK_FLAG_DIRECTORY) ? BOOST_ERRNO : 0,
991     to, from, ec, "boost::filesystem::create_directory_symlink");
992 # endif
993 }
994 
995 BOOST_FILESYSTEM_DECL
create_hard_link(const path & to,const path & from,error_code * ec)996 void create_hard_link(const path& to, const path& from, error_code* ec)
997 {
998 # if defined(BOOST_WINDOWS_API) && _WIN32_WINNT < 0x0500  // SDK earlier than Win 2K
999 
1000   error(BOOST_ERROR_NOT_SUPPORTED, to, from, ec,
1001     "boost::filesystem::create_hard_link");
1002 # else
1003 
1004 #   if defined(BOOST_WINDOWS_API) && _WIN32_WINNT >= 0x0500
1005       // see if actually supported by Windows runtime dll
1006       if (error(!create_hard_link_api ? BOOST_ERROR_NOT_SUPPORTED : 0, to, from, ec,
1007           "boost::filesystem::create_hard_link"))
1008         return;
1009 #   endif
1010 
1011   error(!BOOST_CREATE_HARD_LINK(from.c_str(), to.c_str()) ? BOOST_ERRNO : 0, to, from, ec,
1012     "boost::filesystem::create_hard_link");
1013 # endif
1014 }
1015 
1016 BOOST_FILESYSTEM_DECL
create_symlink(const path & to,const path & from,error_code * ec)1017 void create_symlink(const path& to, const path& from, error_code* ec)
1018 {
1019 # if defined(BOOST_WINDOWS_API) && _WIN32_WINNT < 0x0600  // SDK earlier than Vista and Server 2008
1020   error(BOOST_ERROR_NOT_SUPPORTED, to, from, ec,
1021     "boost::filesystem::create_directory_symlink");
1022 # else
1023 
1024 #   if defined(BOOST_WINDOWS_API) && _WIN32_WINNT >= 0x0600
1025       // see if actually supported by Windows runtime dll
1026       if (error(!create_symbolic_link_api ? BOOST_ERROR_NOT_SUPPORTED : 0, to, from, ec,
1027           "boost::filesystem::create_symlink"))
1028         return;
1029 #   endif
1030 
1031   error(!BOOST_CREATE_SYMBOLIC_LINK(from.c_str(), to.c_str(), 0) ? BOOST_ERRNO : 0,
1032     to, from, ec, "boost::filesystem::create_symlink");
1033 # endif
1034 }
1035 
1036 BOOST_FILESYSTEM_DECL
current_path(error_code * ec)1037 path current_path(error_code* ec)
1038 {
1039 # ifdef BOOST_POSIX_API
1040   struct local
1041   {
1042     static bool getcwd_error(error_code* ec)
1043     {
1044       const int err = errno;
1045       return error((err != ERANGE
1046           // bug in some versions of the Metrowerks C lib on the Mac: wrong errno set
1047 #   if defined(__MSL__) && (defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__))
1048           && err != 0
1049 #   endif
1050         ) ? err : 0, ec, "boost::filesystem::current_path");
1051     }
1052   };
1053 
1054   path cur;
1055   char small_buf[1024];
1056   const char* p = ::getcwd(small_buf, sizeof(small_buf));
1057   if (BOOST_LIKELY(!!p))
1058   {
1059     cur = p;
1060     if (ec != 0) ec->clear();
1061   }
1062   else if (BOOST_LIKELY(!local::getcwd_error(ec)))
1063   {
1064     for (std::size_t path_max = sizeof(small_buf);; path_max *= 2u) // loop 'til buffer large enough
1065     {
1066       if (BOOST_UNLIKELY(path_max > absolute_path_max))
1067       {
1068         if (ec == 0)
1069           BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::current_path",
1070             error_code(ENAMETOOLONG, system_category())));
1071         else
1072           ec->assign(ENAMETOOLONG, system_category());
1073         break;
1074       }
1075 
1076       boost::scoped_array<char> buf(new char[path_max]);
1077       p = ::getcwd(buf.get(), path_max);
1078       if (BOOST_LIKELY(!!p))
1079       {
1080         cur = buf.get();
1081         if (ec != 0)
1082           ec->clear();
1083         break;
1084       }
1085       else if (BOOST_UNLIKELY(local::getcwd_error(ec)))
1086       {
1087         break;
1088       }
1089     }
1090   }
1091 
1092   return cur;
1093 
1094 # elif defined(UNDER_CE)
1095   // Windows CE has no current directory, so everything's relative to the root of the directory tree
1096   return L"\\";
1097 # else
1098   DWORD sz;
1099   if ((sz = ::GetCurrentDirectoryW(0, NULL)) == 0)sz = 1;
1100   boost::scoped_array<path::value_type> buf(new path::value_type[sz]);
1101   error(::GetCurrentDirectoryW(sz, buf.get()) == 0 ? BOOST_ERRNO : 0, ec,
1102     "boost::filesystem::current_path");
1103   return path(buf.get());
1104 # endif
1105 }
1106 
1107 
1108 BOOST_FILESYSTEM_DECL
current_path(const path & p,system::error_code * ec)1109 void current_path(const path& p, system::error_code* ec)
1110 {
1111 # ifdef UNDER_CE
1112   error(BOOST_ERROR_NOT_SUPPORTED, p, ec,
1113     "boost::filesystem::current_path");
1114 # else
1115   error(!BOOST_SET_CURRENT_DIRECTORY(p.c_str()) ? BOOST_ERRNO : 0,
1116     p, ec, "boost::filesystem::current_path");
1117 # endif
1118 }
1119 
1120 BOOST_FILESYSTEM_DECL
equivalent(const path & p1,const path & p2,system::error_code * ec)1121 bool equivalent(const path& p1, const path& p2, system::error_code* ec)
1122 {
1123 # ifdef BOOST_POSIX_API
1124   struct stat s2;
1125   int e2(::stat(p2.c_str(), &s2));
1126   struct stat s1;
1127   int e1(::stat(p1.c_str(), &s1));
1128 
1129   if (e1 != 0 || e2 != 0)
1130   {
1131     // if one is invalid and the other isn't then they aren't equivalent,
1132     // but if both are invalid then it is an error
1133     error (e1 != 0 && e2 != 0, p1, p2, ec, "boost::filesystem::equivalent");
1134     return false;
1135   }
1136 
1137   // both stats now known to be valid
1138   return  s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino
1139       // According to the POSIX stat specs, "The st_ino and st_dev fields
1140       // taken together uniquely identify the file within the system."
1141       // Just to be sure, size and mod time are also checked.
1142       && s1.st_size == s2.st_size && s1.st_mtime == s2.st_mtime;
1143 
1144 # else  // Windows
1145 
1146   // Note well: Physical location on external media is part of the
1147   // equivalence criteria. If there are no open handles, physical location
1148   // can change due to defragmentation or other relocations. Thus handles
1149   // must be held open until location information for both paths has
1150   // been retrieved.
1151 
1152   // p2 is done first, so any error reported is for p1
1153   handle_wrapper h2(
1154     create_file_handle(
1155         p2.c_str(),
1156         0,
1157         FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1158         0,
1159         OPEN_EXISTING,
1160         FILE_FLAG_BACKUP_SEMANTICS,
1161         0));
1162 
1163   handle_wrapper h1(
1164     create_file_handle(
1165         p1.c_str(),
1166         0,
1167         FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1168         0,
1169         OPEN_EXISTING,
1170         FILE_FLAG_BACKUP_SEMANTICS,
1171         0));
1172 
1173   if (h1.handle == INVALID_HANDLE_VALUE
1174     || h2.handle == INVALID_HANDLE_VALUE)
1175   {
1176     // if one is invalid and the other isn't, then they aren't equivalent,
1177     // but if both are invalid then it is an error
1178     error((h1.handle == INVALID_HANDLE_VALUE
1179       && h2.handle == INVALID_HANDLE_VALUE) ? BOOST_ERROR_NOT_SUPPORTED : 0, p1, p2, ec,
1180         "boost::filesystem::equivalent");
1181     return false;
1182   }
1183 
1184   // at this point, both handles are known to be valid
1185 
1186   BY_HANDLE_FILE_INFORMATION info1, info2;
1187 
1188   if (error(!::GetFileInformationByHandle(h1.handle, &info1) ? BOOST_ERRNO : 0,
1189     p1, p2, ec, "boost::filesystem::equivalent"))
1190     return  false;
1191 
1192   if (error(!::GetFileInformationByHandle(h2.handle, &info2) ? BOOST_ERRNO : 0,
1193     p1, p2, ec, "boost::filesystem::equivalent"))
1194     return  false;
1195 
1196   // In theory, volume serial numbers are sufficient to distinguish between
1197   // devices, but in practice VSN's are sometimes duplicated, so last write
1198   // time and file size are also checked.
1199   return
1200     info1.dwVolumeSerialNumber == info2.dwVolumeSerialNumber
1201     && info1.nFileIndexHigh == info2.nFileIndexHigh
1202     && info1.nFileIndexLow == info2.nFileIndexLow
1203     && info1.nFileSizeHigh == info2.nFileSizeHigh
1204     && info1.nFileSizeLow == info2.nFileSizeLow
1205     && info1.ftLastWriteTime.dwLowDateTime
1206       == info2.ftLastWriteTime.dwLowDateTime
1207     && info1.ftLastWriteTime.dwHighDateTime
1208       == info2.ftLastWriteTime.dwHighDateTime;
1209 
1210 # endif
1211 }
1212 
1213 BOOST_FILESYSTEM_DECL
file_size(const path & p,error_code * ec)1214 boost::uintmax_t file_size(const path& p, error_code* ec)
1215 {
1216 # ifdef BOOST_POSIX_API
1217 
1218   struct stat path_stat;
1219   if (error(::stat(p.c_str(), &path_stat)!= 0 ? BOOST_ERRNO : 0,
1220       p, ec, "boost::filesystem::file_size"))
1221     return static_cast<boost::uintmax_t>(-1);
1222   if (error(!S_ISREG(path_stat.st_mode) ? EPERM : 0,
1223       p, ec, "boost::filesystem::file_size"))
1224     return static_cast<boost::uintmax_t>(-1);
1225 
1226   return static_cast<boost::uintmax_t>(path_stat.st_size);
1227 
1228 # else  // Windows
1229 
1230   // assume uintmax_t is 64-bits on all Windows compilers
1231 
1232   WIN32_FILE_ATTRIBUTE_DATA fad;
1233 
1234   if (error(::GetFileAttributesExW(p.c_str(), ::GetFileExInfoStandard, &fad)== 0
1235     ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::file_size"))
1236         return static_cast<boost::uintmax_t>(-1);
1237 
1238   if (error((fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!= 0
1239     ? ERROR_NOT_SUPPORTED : 0, p, ec, "boost::filesystem::file_size"))
1240     return static_cast<boost::uintmax_t>(-1);
1241 
1242   return (static_cast<boost::uintmax_t>(fad.nFileSizeHigh)
1243             << (sizeof(fad.nFileSizeLow)*8)) + fad.nFileSizeLow;
1244 # endif
1245 }
1246 
1247 BOOST_FILESYSTEM_DECL
hard_link_count(const path & p,system::error_code * ec)1248 boost::uintmax_t hard_link_count(const path& p, system::error_code* ec)
1249 {
1250 # ifdef BOOST_POSIX_API
1251 
1252   struct stat path_stat;
1253   return error(::stat(p.c_str(), &path_stat)!= 0 ? BOOST_ERRNO : 0,
1254                 p, ec, "boost::filesystem::hard_link_count")
1255          ? 0
1256          : static_cast<boost::uintmax_t>(path_stat.st_nlink);
1257 
1258 # else // Windows
1259 
1260   // Link count info is only available through GetFileInformationByHandle
1261   BY_HANDLE_FILE_INFORMATION info;
1262   handle_wrapper h(
1263     create_file_handle(p.c_str(), 0,
1264         FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
1265         OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0));
1266   return
1267     !error(h.handle == INVALID_HANDLE_VALUE ? BOOST_ERRNO : 0,
1268             p, ec, "boost::filesystem::hard_link_count")
1269     && !error(::GetFileInformationByHandle(h.handle, &info)== 0 ? BOOST_ERRNO : 0,
1270                p, ec, "boost::filesystem::hard_link_count")
1271          ? info.nNumberOfLinks
1272          : 0;
1273 # endif
1274 }
1275 
1276 BOOST_FILESYSTEM_DECL
initial_path(error_code * ec)1277 path initial_path(error_code* ec)
1278 {
1279   static path init_path;
1280   if (init_path.empty())
1281     init_path = current_path(ec);
1282   else if (ec != 0) ec->clear();
1283   return init_path;
1284 }
1285 
1286 BOOST_FILESYSTEM_DECL
is_empty(const path & p,system::error_code * ec)1287 bool is_empty(const path& p, system::error_code* ec)
1288 {
1289 # ifdef BOOST_POSIX_API
1290 
1291   struct stat path_stat;
1292   if (error(::stat(p.c_str(), &path_stat)!= 0,
1293       p, ec, "boost::filesystem::is_empty"))
1294     return false;
1295   return S_ISDIR(path_stat.st_mode)
1296     ? is_empty_directory(p, ec)
1297     : path_stat.st_size == 0;
1298 
1299 # else
1300 
1301   WIN32_FILE_ATTRIBUTE_DATA fad;
1302   if (error(::GetFileAttributesExW(p.c_str(), ::GetFileExInfoStandard, &fad)== 0
1303     ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::is_empty"))
1304       return false;
1305 
1306   if (ec != 0) ec->clear();
1307   return
1308     (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1309       ? is_empty_directory(p, ec)
1310       : (!fad.nFileSizeHigh && !fad.nFileSizeLow);
1311 
1312 # endif
1313 }
1314 
1315 BOOST_FILESYSTEM_DECL
last_write_time(const path & p,system::error_code * ec)1316 std::time_t last_write_time(const path& p, system::error_code* ec)
1317 {
1318 # ifdef BOOST_POSIX_API
1319 
1320   struct stat path_stat;
1321   if (error(::stat(p.c_str(), &path_stat)!= 0 ? BOOST_ERRNO : 0,
1322     p, ec, "boost::filesystem::last_write_time"))
1323       return std::time_t(-1);
1324   return path_stat.st_mtime;
1325 
1326 # else
1327 
1328   handle_wrapper hw(
1329     create_file_handle(p.c_str(), 0,
1330       FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
1331       OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0));
1332 
1333   if (error(hw.handle == INVALID_HANDLE_VALUE ? BOOST_ERRNO : 0,
1334     p, ec, "boost::filesystem::last_write_time"))
1335       return std::time_t(-1);
1336 
1337   FILETIME lwt;
1338 
1339   if (error(::GetFileTime(hw.handle, 0, 0, &lwt)== 0 ? BOOST_ERRNO : 0,
1340     p, ec, "boost::filesystem::last_write_time"))
1341       return std::time_t(-1);
1342 
1343   return to_time_t(lwt);
1344 
1345 # endif
1346 }
1347 
1348 BOOST_FILESYSTEM_DECL
last_write_time(const path & p,const std::time_t new_time,system::error_code * ec)1349 void last_write_time(const path& p, const std::time_t new_time,
1350                       system::error_code* ec)
1351 {
1352 # ifdef BOOST_POSIX_API
1353 #   if _POSIX_C_SOURCE >= 200809L
1354 
1355   struct timespec times[2] = {};
1356 
1357   // Keep the last access time unchanged
1358   times[0].tv_nsec = UTIME_OMIT;
1359 
1360   times[1].tv_sec = new_time;
1361 
1362   if (BOOST_UNLIKELY(::utimensat(AT_FDCWD, p.c_str(), times, 0) != 0))
1363   {
1364     error(BOOST_ERRNO, p, ec, "boost::filesystem::last_write_time");
1365     return;
1366   }
1367 
1368 #   else // _POSIX_C_SOURCE >= 200809L
1369 
1370   struct stat path_stat;
1371   if (error(::stat(p.c_str(), &path_stat)!= 0,
1372     p, ec, "boost::filesystem::last_write_time"))
1373       return;
1374   ::utimbuf buf;
1375   buf.actime = path_stat.st_atime; // utime()updates access time too:-(
1376   buf.modtime = new_time;
1377   error(::utime(p.c_str(), &buf)!= 0 ? BOOST_ERRNO : 0,
1378     p, ec, "boost::filesystem::last_write_time");
1379 
1380 #   endif // _POSIX_C_SOURCE >= 200809L
1381 
1382 # else
1383 
1384   handle_wrapper hw(
1385     create_file_handle(p.c_str(), FILE_WRITE_ATTRIBUTES,
1386       FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
1387       OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0));
1388 
1389   if (error(hw.handle == INVALID_HANDLE_VALUE ? BOOST_ERRNO : 0,
1390     p, ec, "boost::filesystem::last_write_time"))
1391       return;
1392 
1393   FILETIME lwt;
1394   to_FILETIME(new_time, lwt);
1395 
1396   error(::SetFileTime(hw.handle, 0, 0, &lwt)== 0 ? BOOST_ERRNO : 0,
1397     p, ec, "boost::filesystem::last_write_time");
1398 
1399 # endif
1400 }
1401 
1402 # ifdef BOOST_POSIX_API
1403 const perms active_bits(all_all | set_uid_on_exe | set_gid_on_exe | sticky_bit);
mode_cast(perms prms)1404 inline mode_t mode_cast(perms prms) { return prms & active_bits; }
1405 # endif
1406 
1407 BOOST_FILESYSTEM_DECL
permissions(const path & p,perms prms,system::error_code * ec)1408 void permissions(const path& p, perms prms, system::error_code* ec)
1409 {
1410   BOOST_ASSERT_MSG(!((prms & add_perms) && (prms & remove_perms)),
1411     "add_perms and remove_perms are mutually exclusive");
1412 
1413   if ((prms & add_perms) && (prms & remove_perms))  // precondition failed
1414     return;
1415 
1416 # ifdef BOOST_POSIX_API
1417   error_code local_ec;
1418   file_status current_status((prms & symlink_perms)
1419                              ? fs::symlink_status(p, local_ec)
1420                              : fs::status(p, local_ec));
1421   if (local_ec)
1422   {
1423     if (ec == 0)
1424     BOOST_FILESYSTEM_THROW(filesystem_error(
1425         "boost::filesystem::permissions", p, local_ec));
1426     else
1427       *ec = local_ec;
1428     return;
1429   }
1430 
1431   if (prms & add_perms)
1432     prms |= current_status.permissions();
1433   else if (prms & remove_perms)
1434     prms = current_status.permissions() & ~prms;
1435 
1436   // OS X <10.10, iOS <8.0 and some other platforms don't support fchmodat().
1437   // Solaris (SunPro and gcc) only support fchmodat() on Solaris 11 and higher,
1438   // and a runtime check is too much trouble.
1439   // Linux does not support permissions on symbolic links and has no plans to
1440   // support them in the future.  The chmod() code is thus more practical,
1441   // rather than always hitting ENOTSUP when sending in AT_SYMLINK_NO_FOLLOW.
1442   //  - See the 3rd paragraph of
1443   // "Symbolic link ownership, permissions, and timestamps" at:
1444   //   "http://man7.org/linux/man-pages/man7/symlink.7.html"
1445   //  - See the fchmodat() Linux man page:
1446   //   "http://man7.org/linux/man-pages/man2/fchmodat.2.html"
1447 # if defined(AT_FDCWD) && defined(AT_SYMLINK_NOFOLLOW) \
1448     && !(defined(__SUNPRO_CC) || defined(__sun) || defined(sun)) \
1449     && !(defined(linux) || defined(__linux) || defined(__linux__)) \
1450     && !(defined(__MAC_OS_X_VERSION_MIN_REQUIRED) \
1451          && __MAC_OS_X_VERSION_MIN_REQUIRED < 101000) \
1452     && !(defined(__IPHONE_OS_VERSION_MIN_REQUIRED) \
1453          && __IPHONE_OS_VERSION_MIN_REQUIRED < 80000) \
1454     && !(defined(__QNX__) && (_NTO_VERSION <= 700))
1455   if (::fchmodat(AT_FDCWD, p.c_str(), mode_cast(prms),
1456        !(prms & symlink_perms) ? 0 : AT_SYMLINK_NOFOLLOW))
1457 # else  // fallback if fchmodat() not supported
1458   if (::chmod(p.c_str(), mode_cast(prms)))
1459 # endif
1460   {
1461     const int err = errno;
1462     if (ec == 0)
1463       BOOST_FILESYSTEM_THROW(filesystem_error(
1464         "boost::filesystem::permissions", p,
1465         error_code(err, system::generic_category())));
1466     else
1467       ec->assign(err, system::generic_category());
1468   }
1469 
1470 # else  // Windows
1471 
1472   // if not going to alter FILE_ATTRIBUTE_READONLY, just return
1473   if (!(!((prms & (add_perms | remove_perms)))
1474     || (prms & (owner_write|group_write|others_write))))
1475     return;
1476 
1477   DWORD attr = ::GetFileAttributesW(p.c_str());
1478 
1479   if (error(attr == 0 ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::permissions"))
1480     return;
1481 
1482   if (prms & add_perms)
1483     attr &= ~FILE_ATTRIBUTE_READONLY;
1484   else if (prms & remove_perms)
1485     attr |= FILE_ATTRIBUTE_READONLY;
1486   else if (prms & (owner_write|group_write|others_write))
1487     attr &= ~FILE_ATTRIBUTE_READONLY;
1488   else
1489     attr |= FILE_ATTRIBUTE_READONLY;
1490 
1491   error(::SetFileAttributesW(p.c_str(), attr) == 0 ? BOOST_ERRNO : 0,
1492     p, ec, "boost::filesystem::permissions");
1493 # endif
1494 }
1495 
1496 BOOST_FILESYSTEM_DECL
read_symlink(const path & p,system::error_code * ec)1497 path read_symlink(const path& p, system::error_code* ec)
1498 {
1499   path symlink_path;
1500 
1501 # ifdef BOOST_POSIX_API
1502   const char* const path_str = p.c_str();
1503   char small_buf[1024];
1504   ssize_t result = ::readlink(path_str, small_buf, sizeof(small_buf));
1505   if (BOOST_UNLIKELY(result < 0))
1506   {
1507   fail:
1508     const int err = errno;
1509     if (ec == 0)
1510       BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::read_symlink",
1511         p, error_code(err, system_category())));
1512     else
1513       ec->assign(err, system_category());
1514   }
1515   else if (BOOST_LIKELY(static_cast< std::size_t >(result) < sizeof(small_buf)))
1516   {
1517     symlink_path.assign(small_buf, small_buf + result);
1518     if (ec != 0)
1519       ec->clear();
1520   }
1521   else
1522   {
1523     for (std::size_t path_max = sizeof(small_buf) * 2u;; path_max *= 2u) // loop 'til buffer large enough
1524     {
1525       if (BOOST_UNLIKELY(path_max > absolute_path_max))
1526       {
1527         if (ec == 0)
1528           BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::read_symlink",
1529             p, error_code(ENAMETOOLONG, system_category())));
1530         else
1531           ec->assign(ENAMETOOLONG, system_category());
1532         break;
1533       }
1534 
1535       boost::scoped_array<char> buf(new char[path_max]);
1536       result = ::readlink(path_str, buf.get(), path_max);
1537       if (BOOST_UNLIKELY(result < 0))
1538       {
1539         goto fail;
1540       }
1541       else if (BOOST_LIKELY(static_cast< std::size_t >(result) < path_max))
1542       {
1543         symlink_path.assign(buf.get(), buf.get() + result);
1544         if (ec != 0) ec->clear();
1545         break;
1546       }
1547     }
1548   }
1549 
1550 # elif _WIN32_WINNT < 0x0600  // SDK earlier than Vista and Server 2008
1551   error(BOOST_ERROR_NOT_SUPPORTED, p, ec,
1552         "boost::filesystem::read_symlink");
1553 # else  // Vista and Server 2008 SDK, or later
1554 
1555   union info_t
1556   {
1557     char buf[REPARSE_DATA_BUFFER_HEADER_SIZE+MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
1558     REPARSE_DATA_BUFFER rdb;
1559   } info;
1560 
1561   handle_wrapper h(
1562     create_file_handle(p.c_str(), GENERIC_READ, 0, 0, OPEN_EXISTING,
1563       FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0));
1564 
1565   if (error(h.handle == INVALID_HANDLE_VALUE ? BOOST_ERRNO : 0,
1566     p, ec, "boost::filesystem::read_symlink"))
1567       return symlink_path;
1568 
1569   DWORD sz;
1570 
1571   if (!error(::DeviceIoControl(h.handle, FSCTL_GET_REPARSE_POINT,
1572         0, 0, info.buf, sizeof(info), &sz, 0) == 0 ? BOOST_ERRNO : 0, p, ec,
1573         "boost::filesystem::read_symlink" ))
1574     symlink_path.assign(
1575       static_cast<wchar_t*>(info.rdb.SymbolicLinkReparseBuffer.PathBuffer)
1576       + info.rdb.SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(wchar_t),
1577       static_cast<wchar_t*>(info.rdb.SymbolicLinkReparseBuffer.PathBuffer)
1578       + info.rdb.SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(wchar_t)
1579       + info.rdb.SymbolicLinkReparseBuffer.PrintNameLength/sizeof(wchar_t));
1580 # endif
1581   return symlink_path;
1582 }
1583 
1584 BOOST_FILESYSTEM_DECL
relative(const path & p,const path & base,error_code * ec)1585 path relative(const path& p, const path& base, error_code* ec)
1586 {
1587   error_code tmp_ec;
1588   path wc_base(weakly_canonical(base, &tmp_ec));
1589   if (error(tmp_ec.value(), base, ec, "boost::filesystem::relative"))
1590     return path();
1591   path wc_p(weakly_canonical(p, &tmp_ec));
1592   if (error(tmp_ec.value(), base, ec, "boost::filesystem::relative"))
1593     return path();
1594   return wc_p.lexically_relative(wc_base);
1595 }
1596 
1597 BOOST_FILESYSTEM_DECL
remove(const path & p,error_code * ec)1598 bool remove(const path& p, error_code* ec)
1599 {
1600   error_code tmp_ec;
1601   file_type type = query_file_type(p, &tmp_ec);
1602   if (error(type == status_error ? tmp_ec.value() : 0, p, ec,
1603       "boost::filesystem::remove"))
1604     return false;
1605 
1606   // Since POSIX remove() is specified to work with either files or directories, in a
1607   // perfect world it could just be called. But some important real-world operating
1608   // systems (Windows, Mac OS X, for example) don't implement the POSIX spec. So
1609   // remove_file_or_directory() is always called to keep it simple.
1610   return remove_file_or_directory(p, type, ec);
1611 }
1612 
1613 BOOST_FILESYSTEM_DECL
remove_all(const path & p,error_code * ec)1614 boost::uintmax_t remove_all(const path& p, error_code* ec)
1615 {
1616   error_code tmp_ec;
1617   file_type type = query_file_type(p, &tmp_ec);
1618   if (error(type == status_error ? tmp_ec.value() : 0, p, ec,
1619     "boost::filesystem::remove_all"))
1620     return 0;
1621 
1622   return (type != status_error && type != file_not_found) // exists
1623     ? remove_all_aux(p, type, ec)
1624     : 0;
1625 }
1626 
1627 BOOST_FILESYSTEM_DECL
rename(const path & old_p,const path & new_p,error_code * ec)1628 void rename(const path& old_p, const path& new_p, error_code* ec)
1629 {
1630   error(!BOOST_MOVE_FILE(old_p.c_str(), new_p.c_str()) ? BOOST_ERRNO : 0, old_p, new_p,
1631     ec, "boost::filesystem::rename");
1632 }
1633 
1634 BOOST_FILESYSTEM_DECL
resize_file(const path & p,uintmax_t size,system::error_code * ec)1635 void resize_file(const path& p, uintmax_t size, system::error_code* ec)
1636 {
1637 # if defined(BOOST_POSIX_API)
1638   if (BOOST_UNLIKELY(size > static_cast< uintmax_t >((std::numeric_limits< off_t >::max)()))) {
1639     error(system::errc::file_too_large, p, ec, "boost::filesystem::resize_file");
1640     return;
1641   }
1642 # endif
1643   error(!BOOST_RESIZE_FILE(p.c_str(), size) ? BOOST_ERRNO : 0, p, ec,
1644     "boost::filesystem::resize_file");
1645 }
1646 
1647 BOOST_FILESYSTEM_DECL
space(const path & p,error_code * ec)1648 space_info space(const path& p, error_code* ec)
1649 {
1650 # ifdef BOOST_POSIX_API
1651   struct BOOST_STATVFS vfs;
1652   space_info info;
1653   if (!error(::BOOST_STATVFS(p.c_str(), &vfs) ? BOOST_ERRNO : 0,
1654     p, ec, "boost::filesystem::space"))
1655   {
1656     info.capacity
1657       = static_cast<boost::uintmax_t>(vfs.f_blocks)* BOOST_STATVFS_F_FRSIZE;
1658     info.free
1659       = static_cast<boost::uintmax_t>(vfs.f_bfree)* BOOST_STATVFS_F_FRSIZE;
1660     info.available
1661       = static_cast<boost::uintmax_t>(vfs.f_bavail)* BOOST_STATVFS_F_FRSIZE;
1662   }
1663 
1664 # else
1665 
1666   ULARGE_INTEGER avail, total, free;
1667   space_info info;
1668 
1669   if (!error(::GetDiskFreeSpaceExW(p.c_str(), &avail, &total, &free)== 0,
1670      p, ec, "boost::filesystem::space"))
1671   {
1672     info.capacity
1673       = (static_cast<boost::uintmax_t>(total.HighPart)<< 32)
1674         + total.LowPart;
1675     info.free
1676       = (static_cast<boost::uintmax_t>(free.HighPart)<< 32)
1677         + free.LowPart;
1678     info.available
1679       = (static_cast<boost::uintmax_t>(avail.HighPart)<< 32)
1680         + avail.LowPart;
1681   }
1682 
1683 # endif
1684 
1685   else
1686   {
1687     info.capacity = info.free = info.available = 0;
1688   }
1689   return info;
1690 }
1691 
1692 BOOST_FILESYSTEM_DECL
status(const path & p,error_code * ec)1693 file_status status(const path& p, error_code* ec)
1694 {
1695 # ifdef BOOST_POSIX_API
1696 
1697   struct stat path_stat;
1698   if (::stat(p.c_str(), &path_stat)!= 0)
1699   {
1700     const int err = errno;
1701     if (ec != 0)                            // always report errno, even though some
1702       ec->assign(err, system_category());   // errno values are not status_errors
1703 
1704     if (not_found_error(err))
1705     {
1706       return fs::file_status(fs::file_not_found, fs::no_perms);
1707     }
1708     if (ec == 0)
1709       BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::status",
1710         p, error_code(err, system_category())));
1711     return fs::file_status(fs::status_error);
1712   }
1713   if (ec != 0) ec->clear();;
1714   if (S_ISDIR(path_stat.st_mode))
1715     return fs::file_status(fs::directory_file,
1716       static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1717   if (S_ISREG(path_stat.st_mode))
1718     return fs::file_status(fs::regular_file,
1719       static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1720   if (S_ISBLK(path_stat.st_mode))
1721     return fs::file_status(fs::block_file,
1722       static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1723   if (S_ISCHR(path_stat.st_mode))
1724     return fs::file_status(fs::character_file,
1725       static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1726   if (S_ISFIFO(path_stat.st_mode))
1727     return fs::file_status(fs::fifo_file,
1728       static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1729   if (S_ISSOCK(path_stat.st_mode))
1730     return fs::file_status(fs::socket_file,
1731       static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1732   return fs::file_status(fs::type_unknown);
1733 
1734 # else  // Windows
1735 
1736   DWORD attr(::GetFileAttributesW(p.c_str()));
1737   if (attr == 0xFFFFFFFF)
1738   {
1739     return process_status_failure(p, ec);
1740   }
1741 
1742   perms permissions = make_permissions(p, attr);
1743 
1744   //  reparse point handling;
1745   //    since GetFileAttributesW does not resolve symlinks, try to open a file
1746   //    handle to discover if the file exists
1747   if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
1748   {
1749     handle_wrapper h(
1750       create_file_handle(
1751           p.c_str(),
1752           0,  // dwDesiredAccess; attributes only
1753           FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1754           0,  // lpSecurityAttributes
1755           OPEN_EXISTING,
1756           FILE_FLAG_BACKUP_SEMANTICS,
1757           0)); // hTemplateFile
1758     if (h.handle == INVALID_HANDLE_VALUE)
1759     {
1760       return process_status_failure(p, ec);
1761     }
1762 
1763     if (!is_reparse_point_a_symlink(p))
1764       return file_status(reparse_file, permissions);
1765   }
1766 
1767   if (ec != 0) ec->clear();
1768   return (attr & FILE_ATTRIBUTE_DIRECTORY)
1769     ? file_status(directory_file, permissions)
1770     : file_status(regular_file, permissions);
1771 
1772 # endif
1773 }
1774 
1775 BOOST_FILESYSTEM_DECL
symlink_status(const path & p,error_code * ec)1776 file_status symlink_status(const path& p, error_code* ec)
1777 {
1778 # ifdef BOOST_POSIX_API
1779 
1780   struct stat path_stat;
1781   if (::lstat(p.c_str(), &path_stat)!= 0)
1782   {
1783     const int err = errno;
1784     if (ec != 0)                            // always report errno, even though some
1785       ec->assign(err, system_category());   // errno values are not status_errors
1786 
1787     if (not_found_error(err)) // these are not errors
1788     {
1789       return fs::file_status(fs::file_not_found, fs::no_perms);
1790     }
1791     if (ec == 0)
1792       BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::status",
1793         p, error_code(err, system_category())));
1794     return fs::file_status(fs::status_error);
1795   }
1796   if (ec != 0) ec->clear();
1797   if (S_ISREG(path_stat.st_mode))
1798     return fs::file_status(fs::regular_file,
1799       static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1800   if (S_ISDIR(path_stat.st_mode))
1801     return fs::file_status(fs::directory_file,
1802       static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1803   if (S_ISLNK(path_stat.st_mode))
1804     return fs::file_status(fs::symlink_file,
1805       static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1806   if (S_ISBLK(path_stat.st_mode))
1807     return fs::file_status(fs::block_file,
1808       static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1809   if (S_ISCHR(path_stat.st_mode))
1810     return fs::file_status(fs::character_file,
1811       static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1812   if (S_ISFIFO(path_stat.st_mode))
1813     return fs::file_status(fs::fifo_file,
1814       static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1815   if (S_ISSOCK(path_stat.st_mode))
1816     return fs::file_status(fs::socket_file,
1817       static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1818   return fs::file_status(fs::type_unknown);
1819 
1820 # else  // Windows
1821 
1822   DWORD attr(::GetFileAttributesW(p.c_str()));
1823   if (attr == 0xFFFFFFFF)
1824   {
1825     return process_status_failure(p, ec);
1826   }
1827 
1828   if (ec != 0) ec->clear();
1829 
1830   perms permissions = make_permissions(p, attr);
1831 
1832   if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
1833     return is_reparse_point_a_symlink(p)
1834            ? file_status(symlink_file, permissions)
1835            : file_status(reparse_file, permissions);
1836 
1837   return (attr & FILE_ATTRIBUTE_DIRECTORY)
1838     ? file_status(directory_file, permissions)
1839     : file_status(regular_file, permissions);
1840 
1841 # endif
1842 }
1843 
1844  // contributed by Jeff Flinn
1845 BOOST_FILESYSTEM_DECL
temp_directory_path(system::error_code * ec)1846 path temp_directory_path(system::error_code* ec)
1847 {
1848 # ifdef BOOST_POSIX_API
1849   const char* val = 0;
1850 
1851   (val = std::getenv("TMPDIR" )) ||
1852   (val = std::getenv("TMP"    )) ||
1853   (val = std::getenv("TEMP"   )) ||
1854   (val = std::getenv("TEMPDIR"));
1855 
1856 #   ifdef __ANDROID__
1857   const char* default_tmp = "/data/local/tmp";
1858 #   else
1859   const char* default_tmp = "/tmp";
1860 #   endif
1861   path p((val != NULL) ? val : default_tmp);
1862 
1863   if (p.empty() || (ec && !is_directory(p, *ec)) || (!ec && !is_directory(p)))
1864   {
1865     error(ENOTDIR, p, ec, "boost::filesystem::temp_directory_path");
1866     return p;
1867   }
1868 
1869   return p;
1870 
1871 # else  // Windows
1872 
1873   const wchar_t* tmp_env = L"TMP";
1874   const wchar_t* temp_env = L"TEMP";
1875   const wchar_t* localappdata_env = L"LOCALAPPDATA";
1876   const wchar_t* userprofile_env = L"USERPROFILE";
1877   const wchar_t* env_list[] = { tmp_env, temp_env, localappdata_env, userprofile_env };
1878 
1879   path p;
1880   for (unsigned int i = 0; i < sizeof(env_list) / sizeof(*env_list); ++i)
1881   {
1882     std::wstring env = wgetenv(env_list[i]);
1883     if (!env.empty())
1884     {
1885       p = env;
1886       if (i >= 2)
1887         p /= L"Temp";
1888       error_code lcl_ec;
1889       if (exists(p, lcl_ec) && !lcl_ec && is_directory(p, lcl_ec) && !lcl_ec)
1890         break;
1891       p.clear();
1892     }
1893   }
1894 
1895   if (p.empty())
1896   {
1897     // use a separate buffer since in C++03 a string is not required to be contiguous
1898     const UINT size = ::GetWindowsDirectoryW(NULL, 0);
1899     if (BOOST_UNLIKELY(size == 0))
1900     {
1901     getwindir_error:
1902       int errval = ::GetLastError();
1903       error(errval, ec, "boost::filesystem::temp_directory_path");
1904       return path();
1905     }
1906 
1907     boost::scoped_array<wchar_t> buf(new wchar_t[size]);
1908     if (BOOST_UNLIKELY(::GetWindowsDirectoryW(buf.get(), size) == 0))
1909       goto getwindir_error;
1910 
1911     p = buf.get();  // do not depend on initial buf size, see ticket #10388
1912     p /= L"Temp";
1913   }
1914 
1915   return p;
1916 
1917 # endif
1918 }
1919 
1920 BOOST_FILESYSTEM_DECL
system_complete(const path & p,system::error_code * ec)1921 path system_complete(const path& p, system::error_code* ec)
1922 {
1923 # ifdef BOOST_POSIX_API
1924   return (p.empty() || p.is_absolute())
1925     ? p : current_path() / p;
1926 
1927 # else
1928   if (p.empty())
1929   {
1930     if (ec != 0) ec->clear();
1931     return p;
1932   }
1933   wchar_t buf[buf_size];
1934   wchar_t* pfn;
1935   std::size_t len = get_full_path_name(p, buf_size, buf, &pfn);
1936 
1937   if (error(len == 0 ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::system_complete"))
1938     return path();
1939 
1940   if (len < buf_size)// len does not include null termination character
1941     return path(&buf[0]);
1942 
1943   boost::scoped_array<wchar_t> big_buf(new wchar_t[len]);
1944 
1945   return error(get_full_path_name(p, len , big_buf.get(), &pfn)== 0 ? BOOST_ERRNO : 0,
1946     p, ec, "boost::filesystem::system_complete")
1947     ? path()
1948     : path(big_buf.get());
1949 # endif
1950 }
1951 
1952 BOOST_FILESYSTEM_DECL
weakly_canonical(const path & p,system::error_code * ec)1953 path weakly_canonical(const path& p, system::error_code* ec)
1954 {
1955   path head(p);
1956   path tail;
1957   system::error_code tmp_ec;
1958   path::iterator itr = p.end();
1959 
1960   for (; !head.empty(); --itr)
1961   {
1962     file_status head_status = status(head, tmp_ec);
1963     if (error(head_status.type() == fs::status_error,
1964       head, ec, "boost::filesystem::weakly_canonical"))
1965       return path();
1966     if (head_status.type() != fs::file_not_found)
1967       break;
1968     head.remove_filename();
1969   }
1970 
1971   bool tail_has_dots = false;
1972   for (; itr != p.end(); ++itr)
1973   {
1974     tail /= *itr;
1975     // for a later optimization, track if any dot or dot-dot elements are present
1976     if (itr->native().size() <= 2
1977       && itr->native()[0] == dot
1978       && (itr->native().size() == 1 || itr->native()[1] == dot))
1979       tail_has_dots = true;
1980   }
1981 
1982   if (head.empty())
1983     return p.lexically_normal();
1984   head = canonical(head, tmp_ec);
1985   if (error(tmp_ec.value(), head, ec, "boost::filesystem::weakly_canonical"))
1986     return path();
1987   return tail.empty()
1988     ? head
1989     : (tail_has_dots  // optimization: only normalize if tail had dot or dot-dot element
1990         ? (head/tail).lexically_normal()
1991         : head/tail);
1992 }
1993 
1994 } // namespace detail
1995 } // namespace filesystem
1996 } // namespace boost
1997