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