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