]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/filesystem/src/operations.cpp
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / boost / libs / filesystem / src / operations.cpp
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
60 namespace fs = boost::filesystem;
61 using boost::filesystem::path;
62 using boost::filesystem::filesystem_error;
63 using boost::filesystem::perms;
64 using boost::system::error_code;
65 using boost::system::error_category;
66 using boost::system::system_category;
67 using std::string;
68 using 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
127 typedef 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
175 inline 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
209 typedef 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
230 typedef 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
257 namespace
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
722 namespace boost
723 {
724 namespace 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
782 namespace 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
1973 namespace 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
2006 namespace
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
2218 namespace boost
2219 {
2220 namespace filesystem
2221 {
2222
2223 namespace 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