Index: libcxx/src/filesystem/operations.cpp =================================================================== --- libcxx/src/filesystem/operations.cpp +++ libcxx/src/filesystem/operations.cpp @@ -672,9 +672,11 @@ return static_cast(st.st_mode) & perms::mask; } +#if !defined(_LIBCPP_WIN32API) ::mode_t posix_convert_perms(perms prms) { return static_cast< ::mode_t>(prms & perms::mask); } +#endif file_status create_file_status(error_code& m_ec, path const& p, const StatT& path_stat, error_code* ec) { @@ -746,6 +748,7 @@ return false; } +#if !defined(_LIBCPP_WIN32API) bool posix_fchmod(const FileDescriptor& fd, const StatT& st, error_code& ec) { if (::fchmod(fd.fd, st.st_mode) == -1) { ec = capture_errno(); @@ -754,6 +757,7 @@ ec.clear(); return false; } +#endif bool stat_equivalent(const StatT& st1, const StatT& st2) { return (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino); @@ -1136,8 +1140,10 @@ return err.report(errc::bad_file_descriptor); // Set the permissions and truncate the file we opened. +#if !defined(_LIBCPP_WIN32API) if (detail::posix_fchmod(to_fd, from_stat, m_ec)) return err.report(m_ec); +#endif if (detail::posix_ftruncate(to_fd, 0, m_ec)) return err.report(m_ec); } @@ -1424,6 +1430,7 @@ else if (remove_perms) prms = st.permissions() & ~prms; } +#if !defined(_LIBCPP_WIN32API) const auto real_perms = detail::posix_convert_perms(prms); #if defined(AT_SYMLINK_NOFOLLOW) && defined(AT_FDCWD) @@ -1438,6 +1445,41 @@ return err.report(capture_errno()); } #endif +#else + DWORD attributes = GetFileAttributesW(p.c_str()); + if (attributes == INVALID_FILE_ATTRIBUTES) + return err.report(detail::make_windows_error(GetLastError())); + if (attributes & FILE_ATTRIBUTE_REPARSE_POINT && resolve_symlinks) { + // If the file is a symlink, and we are supposed to operate on the target + // of the symlink, we need to open a handle to it, without the + // FILE_FLAG_OPEN_REPARSE_POINT flag, to open the destination of the + // symlink, and operate on it via the handle. + detail::WinHandle h(p.c_str(), FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, + 0); + if (!h) + return err.report(detail::make_windows_error(GetLastError())); + FILE_BASIC_INFO basic; + if (!GetFileInformationByHandleEx(h, FileBasicInfo, &basic, sizeof(basic))) + return err.report(detail::make_windows_error(GetLastError())); + DWORD orig_attributes = basic.FileAttributes; + basic.FileAttributes &= ~FILE_ATTRIBUTE_READONLY; + if ((static_cast(prms) & 0222) == 0) + basic.FileAttributes |= FILE_ATTRIBUTE_READONLY; + if (basic.FileAttributes != orig_attributes && + !SetFileInformationByHandle(h, FileBasicInfo, &basic, sizeof(basic))) + return err.report(detail::make_windows_error(GetLastError())); + } else { + // For a non-symlink, or if operating on the symlink itself instead of + // its target, we can use SetFileAttributesW, saving a few calls. + DWORD orig_attributes = attributes; + attributes &= ~FILE_ATTRIBUTE_READONLY; + if ((static_cast(prms) & 0222) == 0) + attributes |= FILE_ATTRIBUTE_READONLY; + if (attributes != orig_attributes && + !SetFileAttributesW(p.c_str(), attributes)) + return err.report(detail::make_windows_error(GetLastError())); + } +#endif } path __read_symlink(const path& p, error_code* ec) {