diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp --- a/libcxx/src/filesystem/operations.cpp +++ b/libcxx/src/filesystem/operations.cpp @@ -405,7 +405,7 @@ static FileDescriptor create(const path* p, error_code& ec, Args... args) { ec.clear(); int fd; - if ((fd = ::open(p->c_str(), args...)) == -1) { + if ((fd = detail::open(p->c_str(), args...)) == -1) { ec = capture_errno(); return FileDescriptor{p}; } @@ -431,7 +431,7 @@ void close() noexcept { if (fd != -1) - ::close(fd); + detail::close(fd); fd = -1; } @@ -521,7 +521,7 @@ // http://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html bool posix_ftruncate(const FileDescriptor& fd, off_t to_size, error_code& ec) { - if (::ftruncate(fd.fd, to_size) == -1) { + if (detail::ftruncate(fd.fd, to_size) == -1) { ec = capture_errno(); return true; } @@ -828,8 +828,8 @@ ErrorHandler err("copy_file", ec, &to, &from); error_code m_ec; - FileDescriptor from_fd = - FileDescriptor::create_with_status(&from, m_ec, O_RDONLY | O_NONBLOCK); + FileDescriptor from_fd = FileDescriptor::create_with_status( + &from, m_ec, O_RDONLY | O_NONBLOCK | O_BINARY); if (m_ec) return err.report(m_ec); @@ -881,7 +881,7 @@ // Don't truncate right away. We may not be opening the file we originally // looked at; we'll check this later. - int to_open_flags = O_WRONLY; + int to_open_flags = O_WRONLY | O_BINARY; if (!to_exists) to_open_flags |= O_CREAT; FileDescriptor to_fd = FileDescriptor::create_with_status( @@ -917,10 +917,13 @@ if (ec && *ec) { return; } - // NOTE: proposal says you should detect if you should call - // create_symlink or create_directory_symlink. I don't think this - // is needed with POSIX - __create_symlink(real_path, new_symlink, ec); +#if defined(_LIBCPP_WIN32API) + error_code local_ec; + if (is_directory(real_path, local_ec)) + __create_directory_symlink(real_path, new_symlink, ec); + else +#endif + __create_symlink(real_path, new_symlink, ec); } bool __create_directories(const path& p, error_code* ec) { @@ -953,7 +956,7 @@ bool __create_directory(const path& p, error_code* ec) { ErrorHandler err("create_directory", ec, &p); - if (::mkdir(p.c_str(), static_cast(perms::all)) == 0) + if (detail::mkdir(p.c_str(), static_cast(perms::all)) == 0) return true; if (errno == EEXIST) { @@ -981,7 +984,7 @@ return err.report(errc::not_a_directory, "the specified attribute path is invalid"); - if (::mkdir(p.c_str(), attr_stat.st_mode) == 0) + if (detail::mkdir(p.c_str(), attr_stat.st_mode) == 0) return true; if (errno == EEXIST) { @@ -1000,19 +1003,19 @@ void __create_directory_symlink(path const& from, path const& to, error_code* ec) { ErrorHandler err("create_directory_symlink", ec, &from, &to); - if (::symlink(from.c_str(), to.c_str()) != 0) + if (detail::symlink_dir(from.c_str(), to.c_str()) == -1) return err.report(capture_errno()); } void __create_hard_link(const path& from, const path& to, error_code* ec) { ErrorHandler err("create_hard_link", ec, &from, &to); - if (::link(from.c_str(), to.c_str()) == -1) + if (detail::link(from.c_str(), to.c_str()) == -1) return err.report(capture_errno()); } void __create_symlink(path const& from, path const& to, error_code* ec) { ErrorHandler err("create_symlink", ec, &from, &to); - if (::symlink(from.c_str(), to.c_str()) == -1) + if (detail::symlink_file(from.c_str(), to.c_str()) == -1) return err.report(capture_errno()); } @@ -1032,7 +1035,7 @@ void __current_path(const path& p, error_code* ec) { ErrorHandler err("current_path", ec, &p); - if (::chdir(p.c_str()) == -1) + if (detail::chdir(p.c_str()) == -1) err.report(capture_errno()); } @@ -1236,7 +1239,7 @@ bool __remove(const path& p, error_code* ec) { ErrorHandler err("remove", ec, &p); - if (::remove(p.c_str()) == -1) { + if (detail::remove(p.c_str()) == -1) { if (errno != ENOENT) err.report(capture_errno()); return false; @@ -1285,13 +1288,13 @@ void __rename(const path& from, const path& to, error_code* ec) { ErrorHandler err("rename", ec, &from, &to); - if (::rename(from.c_str(), to.c_str()) == -1) + if (detail::rename(from.c_str(), to.c_str()) == -1) err.report(capture_errno()); } void __resize_file(const path& p, uintmax_t size, error_code* ec) { ErrorHandler err("resize_file", ec, &p); - if (::truncate(p.c_str(), static_cast< ::off_t>(size)) == -1) + if (detail::truncate(p.c_str(), static_cast< ::off_t>(size)) == -1) return err.report(capture_errno()); } diff --git a/libcxx/src/filesystem/posix_compat.h b/libcxx/src/filesystem/posix_compat.h --- a/libcxx/src/filesystem/posix_compat.h +++ b/libcxx/src/filesystem/posix_compat.h @@ -12,6 +12,8 @@ // These generally behave like the proper posix functions, with these // exceptions: // On Windows, they take paths in wchar_t* form, instead of char* form. +// The symlink() function is split into two frontends, symlink_file() +// and symlink_dir(). // // These are provided within an anonymous namespace within the detail // namespace - callers need to include this header and call them as @@ -82,6 +84,8 @@ #define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK) #define S_ISSOCK(m) (((m) & _S_IFMT) == _S_IFSOCK) +#define O_NONBLOCK 0 + // There were 369 years and 89 leap days from the Windows epoch // (1601) to the Unix epoch (1970). @@ -190,10 +194,108 @@ HANDLE h = reinterpret_cast(_get_osfhandle(fd)); return stat_handle(h, buf); } + +int mkdir(const wchar_t *path, int permissions) { + (void)permissions; + return _wmkdir(path); +} + +int symlink_file_dir(const wchar_t *oldname, const wchar_t *newname, + bool is_dir) { + DWORD flags = is_dir ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0; + if (CreateSymbolicLinkW(newname, oldname, + flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)) + return 0; + int e = GetLastError(); + if (e != ERROR_INVALID_PARAMETER) + return set_errno(e); + if (CreateSymbolicLinkW(newname, oldname, flags)) + return 0; + return set_errno(); +} + +int symlink_file(const wchar_t *oldname, const wchar_t *newname) { + return symlink_file_dir(oldname, newname, false); +} + +int symlink_dir(const wchar_t *oldname, const wchar_t *newname) { + return symlink_file_dir(oldname, newname, true); +} + +int link(const wchar_t *oldname, const wchar_t *newname) { + if (CreateHardLinkW(newname, oldname, nullptr)) + return 0; + return set_errno(); +} + +int remove(const wchar_t *path) { + detail::WinHandle h(path, DELETE, FILE_FLAG_OPEN_REPARSE_POINT); + if (!h) + return set_errno(); + FILE_DISPOSITION_INFO info; + info.DeleteFile = TRUE; + if (!SetFileInformationByHandle(h, FileDispositionInfo, &info, sizeof(info))) + return set_errno(); + return 0; +} + +int truncate_handle(HANDLE h, off_t length) { + LARGE_INTEGER size_param; + size_param.QuadPart = length; + if (!SetFilePointerEx(h, size_param, 0, FILE_BEGIN)) + return set_errno(); + if (!SetEndOfFile(h)) + return set_errno(); + return 0; +} + +int ftruncate(int fd, off_t length) { + HANDLE h = reinterpret_cast(_get_osfhandle(fd)); + return truncate_handle(h, length); +} + +int truncate(const wchar_t *path, off_t length) { + detail::WinHandle h(path, GENERIC_WRITE, 0); + if (!h) + return set_errno(); + return truncate_handle(h, length); +} + +int rename(const wchar_t *from, const wchar_t *to) { + if (!(MoveFileExW(from, to, + MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | + MOVEFILE_WRITE_THROUGH))) + return set_errno(); + return 0; +} + +template int open(const wchar_t *filename, Args... args) { + return _wopen(filename, args...); +} +int close(int fd) { return _close(fd); } +int chdir(const wchar_t *path) { return _wchdir(path); } #else +int symlink_file(const char *oldname, const char *newname) { + return ::symlink(oldname, newname); +} +int symlink_dir(const char *oldname, const char *newname) { + return ::symlink(oldname, newname); +} +using ::chdir; +using ::close; using ::fstat; +using ::ftruncate; +using ::link; using ::lstat; +using ::mkdir; +using ::open; +using ::remove; +using ::rename; using ::stat; +using ::truncate; + +#define O_BINARY 0 + #endif } // namespace