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