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