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