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