Ticket #7726: operations.cpp

File operations.cpp, 71.5 KB (added by p.brockamp@…, 10 years ago)

filesystem

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