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