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