1 // Filesystem operation utilities -*- C++ -*- 2 3 // Copyright (C) 2014-2019 Free Software Foundation, Inc. 4 // 5 // This file is part of the GNU ISO C++ Library. This library is free 6 // software; you can redistribute it and/or modify it under the 7 // terms of the GNU General Public License as published by the 8 // Free Software Foundation; either version 3, or (at your option) 9 // any later version. 10 11 // This library is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 16 // Under Section 7 of GPL version 3, you are granted additional 17 // permissions described in the GCC Runtime Library Exception, version 18 // 3.1, as published by the Free Software Foundation. 19 20 // You should have received a copy of the GNU General Public License and 21 // a copy of the GCC Runtime Library Exception along with this program; 22 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 23 // <http://www.gnu.org/licenses/>. 24 25 #ifndef _GLIBCXX_OPS_COMMON_H 26 #define _GLIBCXX_OPS_COMMON_H 1 27 28 #include <chrono> 29 30 #ifdef _GLIBCXX_HAVE_UNISTD_H 31 # include <unistd.h> 32 # ifdef _GLIBCXX_HAVE_FCNTL_H 33 # include <fcntl.h> // AT_FDCWD, O_TRUNC etc. 34 # endif 35 # if defined(_GLIBCXX_HAVE_SYS_STAT_H) && defined(_GLIBCXX_HAVE_SYS_TYPES_H) 36 # include <sys/types.h> 37 # include <sys/stat.h> 38 # endif 39 #endif 40 #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H 41 # include <utime.h> // utime 42 #endif 43 44 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 45 # include <wchar.h> 46 #endif 47 48 #ifdef NEED_DO_COPY_FILE 49 # include <filesystem> 50 # include <ext/stdio_filebuf.h> 51 # ifdef _GLIBCXX_USE_SENDFILE 52 # include <sys/sendfile.h> // sendfile 53 # endif 54 #endif 55 56 namespace std _GLIBCXX_VISIBILITY(default) 57 { 58 _GLIBCXX_BEGIN_NAMESPACE_VERSION 59 namespace filesystem 60 { 61 namespace __gnu_posix 62 { 63 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 64 // Adapt the Windows _wxxx functions to look like POSIX xxx, but for wchar_t*. 65 inline int open(const wchar_t* path, int flags) 66 { return ::_wopen(path, flags); } 67 68 inline int open(const wchar_t* path, int flags, int mode) 69 { return ::_wopen(path, flags, mode); } 70 71 inline int close(int fd) 72 { return ::_close(fd); } 73 74 typedef struct ::_stat stat_type; 75 76 inline int stat(const wchar_t* path, stat_type* buffer) 77 { return ::_wstat(path, buffer); } 78 79 inline int lstat(const wchar_t* path, stat_type* buffer) 80 { 81 // TODO symlinks not currently supported 82 return stat(path, buffer); 83 } 84 85 using ::mode_t; 86 87 inline int chmod(const wchar_t* path, mode_t mode) 88 { return ::_wchmod(path, mode); } 89 90 inline int mkdir(const wchar_t* path, mode_t) 91 { return ::_wmkdir(path); } 92 93 inline wchar_t* getcwd(wchar_t* buf, size_t size) 94 { return ::_wgetcwd(buf, size > (size_t)INT_MAX ? INT_MAX : (int)size); } 95 96 inline int chdir(const wchar_t* path) 97 { return ::_wchdir(path); } 98 99 #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H 100 using utimbuf = _utimbuf; 101 102 inline int utime(const wchar_t* path, utimbuf* times) 103 { return ::_wutime(path, times); } 104 #endif 105 106 inline int rename(const wchar_t* oldname, const wchar_t* newname) 107 { return _wrename(oldname, newname); } 108 109 inline int truncate(const wchar_t* path, _off64_t length) 110 { 111 const int fd = ::_wopen(path, _O_BINARY|_O_RDWR); 112 if (fd == -1) 113 return fd; 114 const int ret = ::ftruncate64(fd, length); 115 int err; 116 ::_get_errno(&err); 117 ::_close(fd); 118 ::_set_errno(err); 119 return ret; 120 } 121 using char_type = wchar_t; 122 #elif defined _GLIBCXX_HAVE_UNISTD_H 123 using ::open; 124 using ::close; 125 # ifdef _GLIBCXX_HAVE_SYS_STAT_H 126 typedef struct ::stat stat_type; 127 using ::stat; 128 # ifdef _GLIBCXX_USE_LSTAT 129 using ::lstat; 130 # else 131 inline int lstat(const char* path, stat_type* buffer) 132 { return stat(path, buffer); } 133 # endif 134 # endif 135 using ::mode_t; 136 using ::chmod; 137 using ::mkdir; 138 using ::getcwd; 139 using ::chdir; 140 # if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_USE_UTIME 141 using ::utimbuf; 142 using ::utime; 143 # endif 144 using ::rename; 145 # ifdef _GLIBCXX_HAVE_TRUNCATE 146 using ::truncate; 147 # else 148 inline int truncate(const char* path, off_t length) 149 { 150 if (length == 0) 151 { 152 const int fd = ::open(path, O_WRONLY|O_TRUNC); 153 if (fd == -1) 154 return fd; 155 ::close(fd); 156 return 0; 157 } 158 errno = ENOTSUP; 159 return -1; 160 } 161 # endif 162 using char_type = char; 163 #else // ! _GLIBCXX_FILESYSTEM_IS_WINDOWS && ! _GLIBCXX_HAVE_UNISTD_H 164 inline int open(const char*, int, ...) { errno = ENOTSUP; return -1; } 165 inline int close(int) { errno = ENOTSUP; return -1; } 166 using mode_t = int; 167 inline int chmod(const char*, mode_t) { errno = ENOTSUP; return -1; } 168 inline int mkdir(const char*, mode_t) { errno = ENOTSUP; return -1; } 169 inline char* getcwd(char*, size_t) { errno = ENOTSUP; return nullptr; } 170 inline int chdir(const char*) { errno = ENOTSUP; return -1; } 171 inline int rename(const char*, const char*) { errno = ENOTSUP; return -1; } 172 inline int truncate(const char*, long) { errno = ENOTSUP; return -1; } 173 using char_type = char; 174 #endif // _GLIBCXX_FILESYSTEM_IS_WINDOWS 175 } // namespace __gnu_posix 176 177 template<typename Bitmask> 178 inline bool is_set(Bitmask obj, Bitmask bits) 179 { 180 return (obj & bits) != Bitmask::none; 181 } 182 183 inline bool 184 is_not_found_errno(int err) noexcept 185 { 186 return err == ENOENT || err == ENOTDIR; 187 } 188 189 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 190 using __gnu_posix::stat_type; 191 192 inline std::chrono::system_clock::time_point 193 file_time(const stat_type& st, std::error_code& ec) noexcept 194 { 195 using namespace std::chrono; 196 #ifdef _GLIBCXX_USE_ST_MTIM 197 time_t s = st.st_mtim.tv_sec; 198 nanoseconds ns{st.st_mtim.tv_nsec}; 199 #else 200 time_t s = st.st_mtime; 201 nanoseconds ns{}; 202 #endif 203 204 // FIXME 205 // There are possible timespec values which will overflow 206 // chrono::system_clock::time_point but would not overflow 207 // __file_clock::time_point, due to its different epoch. 208 // 209 // By checking for overflow of the intermediate system_clock::duration 210 // type, we report an error for values which are actually representable 211 // in the file_time_type result type. 212 // 213 // Howard Hinnant's solution for this problem is to use 214 // duration<__int128>{s} + ns, which doesn't overflow. 215 // An alternative would be to do the epoch correction on s before 216 // the addition, and then go straight to file_time_type instead of 217 // going via chrono::system_clock::time_point. 218 // 219 // (This only applies to the C++17 Filesystem library, because for the 220 // Filesystem TS we don't have a distinct __file_clock, we just use the 221 // system clock for file timestamps). 222 if (s >= (nanoseconds::max().count() / 1e9)) 223 { 224 ec = std::make_error_code(std::errc::value_too_large); // EOVERFLOW 225 return system_clock::time_point::min(); 226 } 227 ec.clear(); 228 return system_clock::time_point{seconds{s} + ns}; 229 } 230 231 struct copy_options_existing_file 232 { 233 bool skip, update, overwrite; 234 }; 235 236 #endif // _GLIBCXX_HAVE_SYS_STAT_H 237 238 } // namespace filesystem 239 240 // BEGIN/END macros must be defined before including this file. 241 _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM 242 243 #ifdef _GLIBCXX_HAVE_SYS_STAT_H 244 using std::filesystem::__gnu_posix::stat_type; 245 using std::filesystem::__gnu_posix::char_type; 246 247 bool 248 do_copy_file(const char_type* from, const char_type* to, 249 std::filesystem::copy_options_existing_file options, 250 stat_type* from_st, stat_type* to_st, 251 std::error_code& ec) noexcept; 252 253 void 254 do_space(const char_type* pathname, 255 uintmax_t& capacity, uintmax_t& free, uintmax_t& available, 256 std::error_code&); 257 258 259 inline file_type 260 make_file_type(const stat_type& st) noexcept 261 { 262 #ifdef _GLIBCXX_HAVE_S_ISREG 263 if (S_ISREG(st.st_mode)) 264 return file_type::regular; 265 else if (S_ISDIR(st.st_mode)) 266 return file_type::directory; 267 else if (S_ISCHR(st.st_mode)) 268 return file_type::character; 269 else if (S_ISBLK(st.st_mode)) 270 return file_type::block; 271 else if (S_ISFIFO(st.st_mode)) 272 return file_type::fifo; 273 #ifdef S_ISLNK // not present in mingw 274 else if (S_ISLNK(st.st_mode)) 275 return file_type::symlink; 276 #endif 277 #ifdef S_ISSOCK // not present until POSIX:2001 278 else if (S_ISSOCK(st.st_mode)) 279 return file_type::socket; 280 #endif 281 #endif 282 return file_type::unknown; 283 } 284 285 inline file_status 286 make_file_status(const stat_type& st) noexcept 287 { 288 return file_status{ 289 make_file_type(st), 290 static_cast<perms>(st.st_mode) & perms::mask 291 }; 292 } 293 294 inline std::filesystem::copy_options_existing_file 295 copy_file_options(copy_options opt) 296 { 297 using std::filesystem::is_set; 298 return { 299 is_set(opt, copy_options::skip_existing), 300 is_set(opt, copy_options::update_existing), 301 is_set(opt, copy_options::overwrite_existing) 302 }; 303 } 304 305 #ifdef NEED_DO_COPY_FILE 306 bool 307 do_copy_file(const char_type* from, const char_type* to, 308 std::filesystem::copy_options_existing_file options, 309 stat_type* from_st, stat_type* to_st, 310 std::error_code& ec) noexcept 311 { 312 namespace fs = std::filesystem; 313 namespace posix = fs::__gnu_posix; 314 315 stat_type st1, st2; 316 file_status t, f; 317 318 if (to_st == nullptr) 319 { 320 if (posix::stat(to, &st1)) 321 { 322 const int err = errno; 323 if (!fs::is_not_found_errno(err)) 324 { 325 ec.assign(err, std::generic_category()); 326 return false; 327 } 328 } 329 else 330 to_st = &st1; 331 } 332 else if (to_st == from_st) 333 to_st = nullptr; 334 335 if (to_st == nullptr) 336 t = file_status{file_type::not_found}; 337 else 338 t = make_file_status(*to_st); 339 340 if (from_st == nullptr) 341 { 342 if (posix::stat(from, &st2)) 343 { 344 ec.assign(errno, std::generic_category()); 345 return false; 346 } 347 else 348 from_st = &st2; 349 } 350 f = make_file_status(*from_st); 351 // _GLIBCXX_RESOLVE_LIB_DEFECTS 352 // 2712. copy_file() has a number of unspecified error conditions 353 if (!is_regular_file(f)) 354 { 355 ec = std::make_error_code(std::errc::not_supported); 356 return false; 357 } 358 359 if (exists(t)) 360 { 361 if (!is_regular_file(t)) 362 { 363 ec = std::make_error_code(std::errc::not_supported); 364 return false; 365 } 366 367 if (to_st->st_dev == from_st->st_dev 368 && to_st->st_ino == from_st->st_ino) 369 { 370 ec = std::make_error_code(std::errc::file_exists); 371 return false; 372 } 373 374 if (options.skip) 375 { 376 ec.clear(); 377 return false; 378 } 379 else if (options.update) 380 { 381 const auto from_mtime = fs::file_time(*from_st, ec); 382 if (ec) 383 return false; 384 if ((from_mtime <= fs::file_time(*to_st, ec)) || ec) 385 return false; 386 } 387 else if (!options.overwrite) 388 { 389 ec = std::make_error_code(std::errc::file_exists); 390 return false; 391 } 392 else if (!is_regular_file(t)) 393 { 394 ec = std::make_error_code(std::errc::not_supported); 395 return false; 396 } 397 } 398 399 struct CloseFD { 400 ~CloseFD() { if (fd != -1) posix::close(fd); } 401 bool close() { return posix::close(std::exchange(fd, -1)) == 0; } 402 int fd; 403 }; 404 405 int iflag = O_RDONLY; 406 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 407 iflag |= O_BINARY; 408 #endif 409 410 CloseFD in = { posix::open(from, iflag) }; 411 if (in.fd == -1) 412 { 413 ec.assign(errno, std::generic_category()); 414 return false; 415 } 416 int oflag = O_WRONLY|O_CREAT; 417 if (options.overwrite || options.update) 418 oflag |= O_TRUNC; 419 else 420 oflag |= O_EXCL; 421 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 422 oflag |= O_BINARY; 423 #endif 424 CloseFD out = { posix::open(to, oflag, S_IWUSR) }; 425 if (out.fd == -1) 426 { 427 if (errno == EEXIST && options.skip) 428 ec.clear(); 429 else 430 ec.assign(errno, std::generic_category()); 431 return false; 432 } 433 434 #if defined _GLIBCXX_USE_FCHMOD && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS 435 if (::fchmod(out.fd, from_st->st_mode)) 436 #elif defined _GLIBCXX_USE_FCHMODAT && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS 437 if (::fchmodat(AT_FDCWD, to, from_st->st_mode, 0)) 438 #else 439 if (posix::chmod(to, from_st->st_mode)) 440 #endif 441 { 442 ec.assign(errno, std::generic_category()); 443 return false; 444 } 445 446 size_t count = from_st->st_size; 447 #if defined _GLIBCXX_USE_SENDFILE && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS 448 off_t offset = 0; 449 ssize_t n = ::sendfile(out.fd, in.fd, &offset, count); 450 if (n < 0 && errno != ENOSYS && errno != EINVAL) 451 { 452 ec.assign(errno, std::generic_category()); 453 return false; 454 } 455 if ((size_t)n == count) 456 { 457 if (!out.close() || !in.close()) 458 { 459 ec.assign(errno, std::generic_category()); 460 return false; 461 } 462 ec.clear(); 463 return true; 464 } 465 else if (n > 0) 466 count -= n; 467 #endif // _GLIBCXX_USE_SENDFILE 468 469 using std::ios; 470 __gnu_cxx::stdio_filebuf<char> sbin(in.fd, ios::in|ios::binary); 471 __gnu_cxx::stdio_filebuf<char> sbout(out.fd, ios::out|ios::binary); 472 473 if (sbin.is_open()) 474 in.fd = -1; 475 if (sbout.is_open()) 476 out.fd = -1; 477 478 #ifdef _GLIBCXX_USE_SENDFILE 479 if (n != 0) 480 { 481 if (n < 0) 482 n = 0; 483 484 const auto p1 = sbin.pubseekoff(n, ios::beg, ios::in); 485 const auto p2 = sbout.pubseekoff(n, ios::beg, ios::out); 486 487 const std::streampos errpos(std::streamoff(-1)); 488 if (p1 == errpos || p2 == errpos) 489 { 490 ec = std::make_error_code(std::errc::io_error); 491 return false; 492 } 493 } 494 #endif 495 496 if (count && !(std::ostream(&sbout) << &sbin)) 497 { 498 ec = std::make_error_code(std::errc::io_error); 499 return false; 500 } 501 if (!sbout.close() || !sbin.close()) 502 { 503 ec.assign(errno, std::generic_category()); 504 return false; 505 } 506 ec.clear(); 507 return true; 508 } 509 #endif // NEED_DO_COPY_FILE 510 511 #ifdef NEED_DO_SPACE 512 #pragma GCC diagnostic push 513 #pragma GCC diagnostic ignored "-Wunused-parameter" 514 void 515 do_space(const char_type* pathname, 516 uintmax_t& capacity, uintmax_t& free, uintmax_t& available, 517 std::error_code& ec) 518 { 519 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H 520 struct ::statvfs f; 521 if (::statvfs(pathname, &f)) 522 ec.assign(errno, std::generic_category()); 523 else 524 { 525 if (f.f_frsize != (unsigned long)-1) 526 { 527 const uintmax_t fragment_size = f.f_frsize; 528 const fsblkcnt_t unknown = -1; 529 if (f.f_blocks != unknown) 530 capacity = f.f_blocks * fragment_size; 531 if (f.f_bfree != unknown) 532 free = f.f_bfree * fragment_size; 533 if (f.f_bavail != unknown) 534 available = f.f_bavail * fragment_size; 535 } 536 ec.clear(); 537 } 538 #elif _GLIBCXX_FILESYSTEM_IS_WINDOWS 539 ULARGE_INTEGER bytes_avail = {}, bytes_total = {}, bytes_free = {}; 540 if (GetDiskFreeSpaceExW(pathname, &bytes_avail, &bytes_total, &bytes_free)) 541 { 542 if (bytes_total.QuadPart != 0) 543 capacity = bytes_total.QuadPart; 544 if (bytes_free.QuadPart != 0) 545 free = bytes_free.QuadPart; 546 if (bytes_avail.QuadPart != 0) 547 available = bytes_avail.QuadPart; 548 ec.clear(); 549 } 550 else 551 ec.assign((int)GetLastError(), std::system_category()); 552 #else 553 ec = std::make_error_code(std::errc::not_supported); 554 #endif 555 } 556 #pragma GCC diagnostic pop 557 #endif // NEED_DO_SPACE 558 559 #endif // _GLIBCXX_HAVE_SYS_STAT_H 560 561 _GLIBCXX_END_NAMESPACE_FILESYSTEM 562 563 _GLIBCXX_END_NAMESPACE_VERSION 564 } // namespace std 565 566 #endif // _GLIBCXX_OPS_COMMON_H 567