diff --git a/src/ghc/filesystem.hpp b/src/ghc/filesystem.hpp index d178dea4..53f4ad4e 100644 --- a/src/ghc/filesystem.hpp +++ b/src/ghc/filesystem.hpp @@ -67,6 +67,8 @@ #define GHC_OS_WIN32 #elif defined(__CYGWIN__) #define GHC_OS_CYGWIN +#elif defined(__sun) && defined(__SVR4) +#define GHC_OS_SOLARIS #elif defined(__svr4__) #define GHC_OS_SYS5R4 #elif defined(BSD) @@ -76,7 +78,8 @@ #include #elif defined(__QNX__) #define GHC_OS_QNX -#define GHC_NO_DIRENT_D_TYPE +#elif defined(__HAIKU__) +#define GHC_OS_HAIKU #else #error "Operating system currently not supported!" #endif @@ -214,6 +217,7 @@ #include #endif #endif +#include #include #include #include @@ -253,6 +257,10 @@ #include #endif +#if !defined(GHC_OS_WINDOWS) && !defined(PATH_MAX) +#define PATH_MAX 4096 +#endif + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Behaviour Switches (see README.md, should match the config in test/filesystem_test.cpp): //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -264,7 +272,7 @@ // configure LWG conformance () #define LWG_2682_BEHAVIOUR //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// LWG #2395 makes create_directory/create_directories not emit an error if there is a regular +// LWG #2395 makes crate_directory/create_directories not emit an error if there is a regular // file with that name, it is superseded by P1164R1, so only activate if really needed // #define LWG_2935_BEHAVIOUR //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -273,19 +281,19 @@ // * if this->has_root_directory() and !p.has_root_directory() return -1 // * if !this->has_root_directory() and p.has_root_directory() return -1 // * else result of element wise comparison of path iteration where first comparison is != 0 or 0 -// if all comparisons are 0 (on Windows this implementation does case insensitive root_name() +// if all comparisons are 0 (on Windows this implementation does case-insensitive root_name() // comparison) #define LWG_2936_BEHAVIOUR //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // LWG #2937 enforces that fs::equivalent emits an error, if !fs::exists(p1)||!exists(p2) #define LWG_2937_BEHAVIOUR //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// UTF8-Everywhere is the original behaviour of ghc::filesystem. But since v1.5 the windows +// UTF8-Everywhere is the original behaviour of ghc::filesystem. But since v1.5 the Windows // version defaults to std::wstring storage backend. Still all std::string will be interpreted -// as UTF-8 encoded. With this define you can enfoce the old behavior on Windows, using +// as UTF-8 encoded. With this define you can enforce the old behavior on Windows, using // std::string as backend and for fs::path::native() and char for fs::path::c_str(). This -// needs more conversions so it is (an was before v1.5) slower, bot might help keeping source -// homogeneous in a multi platform project. +// needs more conversions, so it is (and was before v1.5) slower, bot might help keeping source +// homogeneous in a multi-platform project. // #define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Raise errors/exceptions when invalid unicode codepoints or UTF-8 sequences are found, @@ -300,7 +308,7 @@ //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // ghc::filesystem version in decimal (major * 10000 + minor * 100 + patch) -#define GHC_FILESYSTEM_VERSION 10509L +#define GHC_FILESYSTEM_VERSION 10514L #if !defined(GHC_WITH_EXCEPTIONS) && (defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND)) #define GHC_WITH_EXCEPTIONS @@ -423,10 +431,11 @@ public: template using path_type_EcharT = typename std::enable_if::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value, path>::type; #else - using path_from_string = typename std::enable_if<_is_basic_string::value || std::is_same::type>::value || std::is_same::type>::value || + using path_from_string = + typename std::enable_if<_is_basic_string::value || std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value, - path>::type; + path>::type; template using path_type_EcharT = typename std::enable_if::value || std::is_same::value || std::is_same::value || std::is_same::value, path>::type; #endif @@ -604,7 +613,7 @@ private: friend bool detail::has_executable_extension(const path& p); #ifdef GHC_WIN_AUTO_PREFIX_LONG_PATH string_type::size_type _prefixLength{0}; -#else // GHC_WIN_AUTO_PREFIX_LONG_PATH +#else // GHC_WIN_AUTO_PREFIX_LONG_PATH static const string_type::size_type _prefixLength{0}; #endif // GHC_WIN_AUTO_PREFIX_LONG_PATH #else @@ -799,6 +808,7 @@ public: file_type type() const noexcept; perms permissions() const noexcept; friend bool operator==(const file_status& lhs, const file_status& rhs) noexcept { return lhs.type() == rhs.type() && lhs.permissions() == rhs.permissions(); } + private: file_type _type; perms _perms; @@ -1295,6 +1305,65 @@ GHC_FS_API std::error_code make_error_code(portable_error err); GHC_FS_API std::error_code make_system_error(uint32_t err = 0); #else GHC_FS_API std::error_code make_system_error(int err = 0); + +template +struct has_d_type : std::false_type{}; + +template +struct has_d_type : std::true_type {}; + +template +GHC_INLINE file_type file_type_from_dirent_impl(const T&, std::false_type) +{ + return file_type::none; +} + +template +GHC_INLINE file_type file_type_from_dirent_impl(const T& t, std::true_type) +{ + switch (t.d_type) { +#ifdef DT_BLK + case DT_BLK: + return file_type::block; +#endif +#ifdef DT_CHR + case DT_CHR: + return file_type::character; +#endif +#ifdef DT_DIR + case DT_DIR: + return file_type::directory; +#endif +#ifdef DT_FIFO + case DT_FIFO: + return file_type::fifo; +#endif +#ifdef DT_LNK + case DT_LNK: + return file_type::symlink; +#endif +#ifdef DT_REG + case DT_REG: + return file_type::regular; +#endif +#ifdef DT_SOCK + case DT_SOCK: + return file_type::socket; +#endif +#ifdef DT_UNKNOWN + case DT_UNKNOWN: + return file_type::none; +#endif + default: + return file_type::unknown; + } +} + +template +GHC_INLINE file_type file_type_from_dirent(const T& t) +{ + return file_type_from_dirent_impl(t, has_d_type{}); +} #endif } // namespace detail @@ -1696,7 +1765,7 @@ inline std::wstring toWChar(const charT* unicodeString) return toWChar(std::basic_string>(unicodeString)); #endif } -#endif // GHC_USE_WCHAR_T +#endif // GHC_USE_WCHAR_T } // namespace detail @@ -1815,16 +1884,16 @@ GHC_INLINE bool equals_simple_insensitive(const path::value_type* str1, const pa return true; } return false; -#else // __GNUC__ +#else // __GNUC__ #ifdef GHC_USE_WCHAR_T return 0 == ::_wcsicmp(str1, str2); -#else // GHC_USE_WCHAR_T +#else // GHC_USE_WCHAR_T return 0 == ::_stricmp(str1, str2); -#endif // GHC_USE_WCHAR_T -#endif // __GNUC__ -#else // GHC_OS_WINDOWS +#endif // GHC_USE_WCHAR_T +#endif // __GNUC__ +#else // GHC_OS_WINDOWS return 0 == ::strcasecmp(str1, str2); -#endif // GHC_OS_WINDOWS +#endif // GHC_OS_WINDOWS } GHC_INLINE int compare_simple_insensitive(const path::value_type* str1, size_t len1, const path::value_type* str2, size_t len2) @@ -1888,10 +1957,15 @@ GHC_INLINE void create_symlink(const path& target_name, const path& new_symlink, #if defined(__GNUC__) && __GNUC__ >= 8 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-function-type" +#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__) +#pragma warning(push) +#pragma warning(disable : 4191) #endif static CreateSymbolicLinkW_fp api_call = reinterpret_cast(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateSymbolicLinkW")); #if defined(__GNUC__) && __GNUC__ >= 8 #pragma GCC diagnostic pop +#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__) +#pragma warning(pop) #endif if (api_call) { if (api_call(GHC_NATIVEWP(new_symlink), GHC_NATIVEWP(target_name), to_directory ? 1 : 0) == 0) { @@ -1912,10 +1986,15 @@ GHC_INLINE void create_hardlink(const path& target_name, const path& new_hardlin #if defined(__GNUC__) && __GNUC__ >= 8 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-function-type" +#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__) +#pragma warning(push) +#pragma warning(disable : 4191) #endif static CreateHardLinkW_fp api_call = reinterpret_cast(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateHardLinkW")); #if defined(__GNUC__) && __GNUC__ >= 8 #pragma GCC diagnostic pop +#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__) +#pragma warning(pop) #endif if (api_call) { if (api_call(GHC_NATIVEWP(new_hardlink), GHC_NATIVEWP(target_name), NULL) == 0) { @@ -2004,6 +2083,52 @@ GHC_INLINE file_status file_status_from_st_mode(T mode) } #ifdef GHC_OS_WINDOWS + +class unique_handle +{ +public: + typedef HANDLE element_type; + + unique_handle() noexcept + : _handle(INVALID_HANDLE_VALUE) + { + } + explicit unique_handle(element_type h) noexcept + : _handle(h) + { + } + unique_handle(unique_handle&& u) noexcept + : _handle(u.release()) + { + } + ~unique_handle() { reset(); } + unique_handle& operator=(unique_handle&& u) noexcept + { + reset(u.release()); + return *this; + } + element_type get() const noexcept { return _handle; } + explicit operator bool() const noexcept { return _handle != INVALID_HANDLE_VALUE; } + element_type release() noexcept + { + element_type tmp = _handle; + _handle = INVALID_HANDLE_VALUE; + return tmp; + } + void reset(element_type h = INVALID_HANDLE_VALUE) noexcept + { + element_type tmp = _handle; + _handle = h; + if (tmp != INVALID_HANDLE_VALUE) { + CloseHandle(tmp); + } + } + void swap(unique_handle& u) noexcept { std::swap(_handle, u._handle); } + +private: + element_type _handle; +}; + #ifndef REPARSE_DATA_BUFFER_HEADER_SIZE typedef struct _REPARSE_DATA_BUFFER { @@ -2040,15 +2165,21 @@ typedef struct _REPARSE_DATA_BUFFER #endif #endif -GHC_INLINE std::shared_ptr getReparseData(const path& p, std::error_code& ec) +template +struct free_deleter { - std::shared_ptr file(CreateFileW(GHC_NATIVEWP(p), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle); - if (file.get() == INVALID_HANDLE_VALUE) { + void operator()(T* p) const { std::free(p); } +}; + +GHC_INLINE std::unique_ptr> getReparseData(const path& p, std::error_code& ec) +{ + unique_handle file(CreateFileW(GHC_NATIVEWP(p), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0)); + if (!file) { ec = detail::make_system_error(); return nullptr; } - std::shared_ptr reparseData((REPARSE_DATA_BUFFER*)std::calloc(1, MAXIMUM_REPARSE_DATA_BUFFER_SIZE), std::free); + std::unique_ptr> reparseData(reinterpret_cast(std::calloc(1, MAXIMUM_REPARSE_DATA_BUFFER_SIZE))); ULONG bufferUsed; if (DeviceIoControl(file.get(), FSCTL_GET_REPARSE_POINT, 0, 0, reparseData.get(), MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bufferUsed, 0)) { return reparseData; @@ -2085,7 +2216,7 @@ GHC_INLINE path resolveSymlink(const path& p, std::error_code& ec) } case IO_REPARSE_TAG_MOUNT_POINT: result = detail::getFullPathName(GHC_NATIVEWP(p), ec); - //result = std::wstring(&reparseData->MountPointReparseBuffer.PathBuffer[reparseData->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], reparseData->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR)); + // result = std::wstring(&reparseData->MountPointReparseBuffer.PathBuffer[reparseData->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], reparseData->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR)); break; default: break; @@ -2122,10 +2253,10 @@ GHC_INLINE time_t timeFromFILETIME(const FILETIME& ft) GHC_INLINE void timeToFILETIME(time_t t, FILETIME& ft) { - LONGLONG ll; - ll = Int32x32To64(t, 10000000) + 116444736000000000; - ft.dwLowDateTime = static_cast(ll); - ft.dwHighDateTime = static_cast(ll >> 32); + ULARGE_INTEGER ull; + ull.QuadPart = static_cast((t * 10000000LL) + 116444736000000000LL); + ft.dwLowDateTime = ull.LowPart; + ft.dwHighDateTime = ull.HighPart; } template @@ -2141,41 +2272,43 @@ GHC_INLINE uintmax_t hard_links_from_INFO(const BY_H } template -GHC_INLINE DWORD reparse_tag_from_INFO(const INFO*) +GHC_INLINE bool is_symlink_from_INFO(const path &p, const INFO* info, std::error_code& ec) { - return 0; + if ((info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + auto reparseData = detail::getReparseData(p, ec); + if (!ec && reparseData && IsReparseTagMicrosoft(reparseData->ReparseTag) && reparseData->ReparseTag == IO_REPARSE_TAG_SYMLINK) { + return true; + } + } + return false; } template <> -GHC_INLINE DWORD reparse_tag_from_INFO(const WIN32_FIND_DATAW* info) +GHC_INLINE bool is_symlink_from_INFO(const path &, const WIN32_FIND_DATAW* info, std::error_code&) { - return info->dwReserved0; + // dwReserved0 is undefined unless dwFileAttributes includes the + // FILE_ATTRIBUTE_REPARSE_POINT attribute according to microsoft + // documentation. In practice, dwReserved0 is not reset which + // causes it to report the incorrect symlink status. + // Note that microsoft documentation does not say whether there is + // a null value for dwReserved0, so we test for symlink directly + // instead of returning the tag which requires returning a null + // value for non-reparse-point files. + return (info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && info->dwReserved0 == IO_REPARSE_TAG_SYMLINK; } template GHC_INLINE file_status status_from_INFO(const path& p, const INFO* info, std::error_code& ec, uintmax_t* sz = nullptr, time_t* lwt = nullptr) { file_type ft = file_type::unknown; - if (sizeof(INFO) == sizeof(WIN32_FIND_DATAW)) { - if (detail::reparse_tag_from_INFO(info) == IO_REPARSE_TAG_SYMLINK) { - ft = file_type::symlink; - } + if (is_symlink_from_INFO(p, info, ec)) { + ft = file_type::symlink; } - else { - if ((info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { - auto reparseData = detail::getReparseData(p, ec); - if (!ec && reparseData && IsReparseTagMicrosoft(reparseData->ReparseTag) && reparseData->ReparseTag == IO_REPARSE_TAG_SYMLINK) { - ft = file_type::symlink; - } - } + else if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + ft = file_type::directory; } - if (ft == file_type::unknown) { - if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { - ft = file_type::directory; - } - else { - ft = file_type::regular; - } + else { + ft = file_type::regular; } perms prms = perms::owner_read | perms::group_read | perms::others_read; if (!(info->dwFileAttributes & FILE_ATTRIBUTE_READONLY)) { @@ -3056,7 +3189,8 @@ GHC_INLINE bool has_executable_extension(const path& p) return false; } const path::value_type* ext = fn._path.c_str() + pos + 1; - if (detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("exe")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("cmd")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("bat")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("com"))) { + if (detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("exe")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("cmd")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("bat")) || + detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("com"))) { return true; } } @@ -3215,7 +3349,7 @@ GHC_INLINE path::iterator::iterator(const path& p, const impl_string_type::const , _root(p.has_root_directory() ? _first + static_cast(p._prefixLength + p.root_name_length()) : _last) , _iter(pos) { - if(pos != _last) { + if (pos != _last) { updateCurrent(); } } @@ -3232,7 +3366,7 @@ GHC_INLINE path::impl_string_type::const_iterator path::iterator::increment(cons // we can only sit on a slash if it is a network name or a root if (i != _last && *i == preferred_separator) { if (fromStart && !(i + 1 != _last && *(i + 1) == preferred_separator)) { - // leading double slashes detected, treat this and the + // leadind double slashes detected, treat this and the // following until a slash as one unit i = std::find(++i, _last, preferred_separator); } @@ -3245,10 +3379,14 @@ GHC_INLINE path::impl_string_type::const_iterator path::iterator::increment(cons } } else { +#ifdef GHC_OS_WINDOWS if (fromStart && i != _last && *i == ':') { ++i; } else { +#else + { +#endif i = std::find(i, _last, preferred_separator); } } @@ -3295,10 +3433,10 @@ GHC_INLINE void path::iterator::updateCurrent() GHC_INLINE path::iterator& path::iterator::operator++() { _iter = increment(_iter); - while (_iter != _last && // we didn't reach the end - _iter != _root && // this is not a root position + while (_iter != _last && // we didn't reach the end + _iter != _root && // this is not a root position *_iter == preferred_separator && // we are on a separator - (_iter + 1) != _last // the slash is not the last char + (_iter + 1) != _last // the slash is not the last char ) { ++_iter; } @@ -3792,11 +3930,14 @@ GHC_INLINE bool copy_file(const path& from, const path& to, copy_options options ec = tecf; return false; } - if (exists(st) && (!is_regular_file(st) || equivalent(from, to, ec) || (options & (copy_options::skip_existing | copy_options::overwrite_existing | copy_options::update_existing)) == copy_options::none)) { - ec = tect ? tect : detail::make_error_code(detail::portable_error::exists); - return false; - } if (exists(st)) { + if ((options & copy_options::skip_existing) == copy_options::skip_existing) { + return false; + } + if (!is_regular_file(st) || equivalent(from, to, ec) || (options & (copy_options::overwrite_existing | copy_options::update_existing)) == copy_options::none) { + ec = tect ? tect : detail::make_error_code(detail::portable_error::exists); + return false; + } if ((options & copy_options::update_existing) == copy_options::update_existing) { auto from_time = last_write_time(from, ec); if (ec) { @@ -3836,15 +3977,33 @@ GHC_INLINE bool copy_file(const path& from, const path& to, copy_options options ::close(in); return false; } + if (st.permissions() != sf.permissions()) { + if (::fchmod(out, static_cast(sf.permissions() & perms::all)) != 0) { + ec = detail::make_system_error(); + ::close(in); + ::close(out); + return false; + } + } ssize_t br, bw; - while ((br = ::read(in, buffer.data(), buffer.size())) > 0) { + while (true) { + do { br = ::read(in, buffer.data(), buffer.size()); } while(errno == EINTR); + if(!br) { + break; + } + if(br < 0) { + ec = detail::make_system_error(); + ::close(in); + ::close(out); + return false; + } ssize_t offset = 0; do { if ((bw = ::write(out, buffer.data() + offset, static_cast(br))) > 0) { br -= bw; offset += bw; } - else if (bw < 0) { + else if (bw < 0 && errno != EINTR) { ec = detail::make_system_error(); ::close(in); ::close(out); @@ -4079,6 +4238,13 @@ GHC_INLINE path current_path(std::error_code& ec) return path(); } return path(std::wstring(buffer.get()), path::native_format); +#elif defined(__GLIBC__) + std::unique_ptr buffer { ::getcwd(NULL, 0), std::free }; + if (buffer == nullptr) { + ec = detail::make_system_error(); + return path(); + } + return path(buffer.get()); #else size_t pathlen = static_cast(std::max(int(::pathconf(".", _PC_PATH_MAX)), int(PATH_MAX))); std::unique_ptr buffer(new char[pathlen + 1]); @@ -4152,10 +4318,10 @@ GHC_INLINE bool equivalent(const path& p1, const path& p2, std::error_code& ec) { ec.clear(); #ifdef GHC_OS_WINDOWS - std::shared_ptr file1(::CreateFileW(GHC_NATIVEWP(p1), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle); + detail::unique_handle file1(::CreateFileW(GHC_NATIVEWP(p1), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)); auto e1 = ::GetLastError(); - std::shared_ptr file2(::CreateFileW(GHC_NATIVEWP(p2), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle); - if (file1.get() == INVALID_HANDLE_VALUE || file2.get() == INVALID_HANDLE_VALUE) { + detail::unique_handle file2(::CreateFileW(GHC_NATIVEWP(p2), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)); + if (!file1 || !file2) { #ifdef LWG_2937_BEHAVIOUR ec = detail::make_system_error(e1 ? e1 : ::GetLastError()); #else @@ -4245,9 +4411,9 @@ GHC_INLINE uintmax_t hard_link_count(const path& p, std::error_code& ec) noexcep ec.clear(); #ifdef GHC_OS_WINDOWS uintmax_t result = static_cast(-1); - std::shared_ptr file(::CreateFileW(GHC_NATIVEWP(p), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle); + detail::unique_handle file(::CreateFileW(GHC_NATIVEWP(p), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)); BY_HANDLE_FILE_INFORMATION inf; - if (file.get() == INVALID_HANDLE_VALUE) { + if (!file) { ec = detail::make_system_error(); } else { @@ -4476,7 +4642,7 @@ GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::err ec.clear(); auto d = new_time.time_since_epoch(); #ifdef GHC_OS_WINDOWS - std::shared_ptr file(::CreateFileW(GHC_NATIVEWP(p), FILE_WRITE_ATTRIBUTES, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL), ::CloseHandle); + detail::unique_handle file(::CreateFileW(GHC_NATIVEWP(p), FILE_WRITE_ATTRIBUTES, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL)); FILETIME ft; auto tt = std::chrono::duration_cast(d).count() * 10 + 116444736000000000; ft.dwLowDateTime = static_cast(tt); @@ -4484,9 +4650,9 @@ GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::err if (!::SetFileTime(file.get(), 0, 0, &ft)) { ec = detail::make_system_error(); } -#elif defined(GHC_OS_MACOS) -#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED -#if __MAC_OS_X_VERSION_MIN_REQUIRED < 101300 +#elif defined(GHC_OS_MACOS) && \ + (__MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_13) || (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_11_0) || \ + (__TV_OS_VERSION_MIN_REQUIRED < __TVOS_11_0) || (__WATCH_OS_VERSION_MIN_REQUIRED < __WATCHOS_4_0) struct ::stat fs; if (::stat(p.c_str(), &fs) == 0) { struct ::timeval tv[2]; @@ -4500,18 +4666,6 @@ GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::err } ec = detail::make_system_error(); return; -#else - struct ::timespec times[2]; - times[0].tv_sec = 0; - times[0].tv_nsec = UTIME_OMIT; - times[1].tv_sec = std::chrono::duration_cast(d).count(); - times[1].tv_nsec = 0; // std::chrono::duration_cast(d).count() % 1000000000; - if (::utimensat(AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { - ec = detail::make_system_error(); - } - return; -#endif -#endif #else #ifndef UTIME_OMIT #define UTIME_OMIT ((1l << 30) - 2l) @@ -4524,7 +4678,7 @@ GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::err #if defined(__ANDROID_API__) && __ANDROID_API__ < 12 if (syscall(__NR_utimensat, AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { #else - if (::utimensat(AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { + if (::utimensat((int)AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { #endif ec = detail::make_system_error(); } @@ -4687,9 +4841,9 @@ GHC_INLINE bool remove(const path& p, std::error_code& ec) noexcept } ec = detail::make_system_error(error); } - else if(attr & FILE_ATTRIBUTE_READONLY) { + else if (attr & FILE_ATTRIBUTE_READONLY) { auto new_attr = attr & ~static_cast(FILE_ATTRIBUTE_READONLY); - if(!SetFileAttributesW(cstr, new_attr)) { + if (!SetFileAttributesW(cstr, new_attr)) { auto error = ::GetLastError(); ec = detail::make_system_error(error); } @@ -4739,7 +4893,7 @@ GHC_INLINE uintmax_t remove_all(const path& p, std::error_code& ec) noexcept return static_cast(-1); } std::error_code tec; - auto fs = status(p, tec); + auto fs = symlink_status(p, tec); if (exists(fs) && is_directory(fs)) { for (auto iter = directory_iterator(p, ec); iter != directory_iterator(); iter.increment(ec)) { if (ec && !detail::is_not_found_error(ec)) { @@ -4830,8 +4984,8 @@ GHC_INLINE void resize_file(const path& p, uintmax_t size, std::error_code& ec) #endif return; } - std::shared_ptr file(CreateFileW(GHC_NATIVEWP(p), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL), CloseHandle); - if (file.get() == INVALID_HANDLE_VALUE) { + detail::unique_handle file(CreateFileW(GHC_NATIVEWP(p), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL)); + if (!file) { ec = detail::make_system_error(); } else if (SetFilePointerEx(file.get(), lisize, NULL, FILE_BEGIN) == 0 || SetEndOfFile(file.get()) == 0) { @@ -5139,7 +5293,7 @@ GHC_INLINE void directory_entry::refresh() { std::error_code ec; refresh(ec); - if (ec) { + if (ec && (_status.type() == file_type::none || _symlink_status.type() != file_type::symlink)) { throw filesystem_error(detail::systemErrorText(ec.value()), _path, ec); } } @@ -5174,7 +5328,7 @@ GHC_INLINE file_type directory_entry::status_file_type() const GHC_INLINE file_type directory_entry::status_file_type(std::error_code& ec) const noexcept { - if(_status.type() != file_type::none) { + if (_status.type() != file_type::none) { ec.clear(); return _status.type(); } @@ -5288,7 +5442,7 @@ GHC_INLINE bool directory_entry::is_symlink() const GHC_INLINE bool directory_entry::is_symlink(std::error_code& ec) const noexcept { - if(_symlink_status.type() != file_type::none) { + if (_symlink_status.type() != file_type::none) { ec.clear(); return _symlink_status.type() == file_type::symlink; } @@ -5550,7 +5704,7 @@ public: , _entry(nullptr) { if (!path.empty()) { - _dir = ::opendir(path.native().c_str()); + do { _dir = ::opendir(path.native().c_str()); } while(errno == EINTR); if (!_dir) { auto error = errno; _base = filesystem::path(); @@ -5577,7 +5731,7 @@ public: do { skip = false; errno = 0; - _entry = ::readdir(_dir); + do { _entry = ::readdir(_dir); } while(errno == EINTR); if (_entry) { _dir_entry._path = _base; _dir_entry._path.append_name(_entry->d_name); @@ -5591,7 +5745,7 @@ public: ::closedir(_dir); _dir = nullptr; _dir_entry._path.clear(); - if (errno) { + if (errno && errno != EINTR) { ec = detail::make_system_error(); } break; @@ -5602,30 +5756,16 @@ public: void copyToDirEntry() { -#ifdef GHC_NO_DIRENT_D_TYPE - _dir_entry._symlink_status = file_status(); - _dir_entry._status = file_status(); -#else _dir_entry._symlink_status.permissions(perms::unknown); - switch(_entry->d_type) { - case DT_BLK: _dir_entry._symlink_status.type(file_type::block); break; - case DT_CHR: _dir_entry._symlink_status.type(file_type::character); break; - case DT_DIR: _dir_entry._symlink_status.type(file_type::directory); break; - case DT_FIFO: _dir_entry._symlink_status.type(file_type::fifo); break; - case DT_LNK: _dir_entry._symlink_status.type(file_type::symlink); break; - case DT_REG: _dir_entry._symlink_status.type(file_type::regular); break; - case DT_SOCK: _dir_entry._symlink_status.type(file_type::socket); break; - case DT_UNKNOWN: _dir_entry._symlink_status.type(file_type::none); break; - default: _dir_entry._symlink_status.type(file_type::unknown); break; - } - if (_entry->d_type != DT_LNK) { + auto ft = detail::file_type_from_dirent(*_entry); + _dir_entry._symlink_status.type(ft); + if (ft != file_type::symlink) { _dir_entry._status = _dir_entry._symlink_status; } else { _dir_entry._status.type(file_type::none); _dir_entry._status.permissions(perms::unknown); } -#endif _dir_entry._file_size = static_cast(-1); _dir_entry._hard_link_count = static_cast(-1); _dir_entry._last_write_time = 0; @@ -5856,10 +5996,10 @@ GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::increment { bool isSymLink = (*this)->is_symlink(ec); bool isDir = !ec && (*this)->is_directory(ec); - if(isSymLink && detail::is_not_found_error(ec)) { + if (isSymLink && detail::is_not_found_error(ec)) { ec.clear(); } - if(!ec) { + if (!ec) { if (recursion_pending() && isDir && (!isSymLink || (options() & directory_options::follow_directory_symlink) != directory_options::none)) { _impl->_dir_iter_stack.push(directory_iterator((*this)->path(), _impl->_options, ec)); }