Skip to content

Commit 1561807

Browse files
committed
[libcxx] Fix the preexisting directory_iterator code for windows
The directory_iterator.cpp file did contain an incomplete, non-working implementation for windows. Change it to use the wchar version of the APIs. Don't set the windows specific errors from GetLastError() as code in the generic category; remap the errors to the std::errc values. Error out cleanly on empty paths. Invoke FindFirstFile on <directoryname>/* to actually list the entries of the directory. If the first entry retured by FindFirstFile is to be skipped (e.g. being "." or ".."), call advance() (which calls FindNextFile and loops) which doesn't return until a valid entry is found (or the end is reached). Differential Revision: https://reviews.llvm.org/D91140
1 parent de698ae commit 1561807

File tree

3 files changed

+125
-29
lines changed

3 files changed

+125
-29
lines changed

libcxx/src/filesystem/directory_iterator.cpp

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "__config"
1111
#if defined(_LIBCPP_WIN32API)
1212
#define WIN32_LEAN_AND_MEAN
13+
#define NOMINMAX
1314
#include <windows.h>
1415
#else
1516
#include <dirent.h>
@@ -72,16 +73,20 @@ static pair<string_view, file_type> posix_readdir(DIR* dir_stream,
7273
}
7374
}
7475
#else
76+
// defined(_LIBCPP_WIN32API)
7577

76-
static file_type get_file_type(const WIN32_FIND_DATA& data) {
77-
//auto attrs = data.dwFileAttributes;
78-
// FIXME(EricWF)
79-
return file_type::unknown;
78+
static file_type get_file_type(const WIN32_FIND_DATAW& data) {
79+
if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
80+
data.dwReserved0 == IO_REPARSE_TAG_SYMLINK)
81+
return file_type::symlink;
82+
if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
83+
return file_type::directory;
84+
return file_type::regular;
8085
}
81-
static uintmax_t get_file_size(const WIN32_FIND_DATA& data) {
82-
return (data.nFileSizeHigh * (MAXDWORD + 1)) + data.nFileSizeLow;
86+
static uintmax_t get_file_size(const WIN32_FIND_DATAW& data) {
87+
return (static_cast<uint64_t>(data.nFileSizeHigh) << 32) + data.nFileSizeLow;
8388
}
84-
static file_time_type get_write_time(const WIN32_FIND_DATA& data) {
89+
static file_time_type get_write_time(const WIN32_FIND_DATAW& data) {
8590
ULARGE_INTEGER tmp;
8691
const FILETIME& time = data.ftLastWriteTime;
8792
tmp.u.LowPart = time.dwLowDateTime;
@@ -110,15 +115,21 @@ class __dir_stream {
110115

111116
__dir_stream(const path& root, directory_options opts, error_code& ec)
112117
: __stream_(INVALID_HANDLE_VALUE), __root_(root) {
113-
__stream_ = ::FindFirstFile(root.c_str(), &__data_);
118+
if (root.native().empty()) {
119+
ec = make_error_code(errc::no_such_file_or_directory);
120+
return;
121+
}
122+
__stream_ = ::FindFirstFileW((root / "*").c_str(), &__data_);
114123
if (__stream_ == INVALID_HANDLE_VALUE) {
115-
ec = error_code(::GetLastError(), generic_category());
124+
ec = detail::make_windows_error(GetLastError());
116125
const bool ignore_permission_denied =
117126
bool(opts & directory_options::skip_permission_denied);
118127
if (ignore_permission_denied && ec.value() == ERROR_ACCESS_DENIED)
119128
ec.clear();
120129
return;
121130
}
131+
if (!assign())
132+
advance(ec);
122133
}
123134

124135
~__dir_stream() noexcept {
@@ -130,35 +141,39 @@ class __dir_stream {
130141
bool good() const noexcept { return __stream_ != INVALID_HANDLE_VALUE; }
131142

132143
bool advance(error_code& ec) {
133-
while (::FindNextFile(__stream_, &__data_)) {
134-
if (!strcmp(__data_.cFileName, ".") || strcmp(__data_.cFileName, ".."))
135-
continue;
136-
// FIXME: Cache more of this
137-
//directory_entry::__cached_data cdata;
138-
//cdata.__type_ = get_file_type(__data_);
139-
//cdata.__size_ = get_file_size(__data_);
140-
//cdata.__write_time_ = get_write_time(__data_);
141-
__entry_.__assign_iter_entry(
142-
__root_ / __data_.cFileName,
143-
directory_entry::__create_iter_result(detail::get_file_type(__data)));
144-
return true;
144+
while (::FindNextFileW(__stream_, &__data_)) {
145+
if (assign())
146+
return true;
145147
}
146-
ec = error_code(::GetLastError(), generic_category());
147148
close();
148149
return false;
149150
}
150151

152+
bool assign() {
153+
if (!wcscmp(__data_.cFileName, L".") || !wcscmp(__data_.cFileName, L".."))
154+
return false;
155+
// FIXME: Cache more of this
156+
//directory_entry::__cached_data cdata;
157+
//cdata.__type_ = get_file_type(__data_);
158+
//cdata.__size_ = get_file_size(__data_);
159+
//cdata.__write_time_ = get_write_time(__data_);
160+
__entry_.__assign_iter_entry(
161+
__root_ / __data_.cFileName,
162+
directory_entry::__create_iter_result(detail::get_file_type(__data_)));
163+
return true;
164+
}
165+
151166
private:
152167
error_code close() noexcept {
153168
error_code ec;
154169
if (!::FindClose(__stream_))
155-
ec = error_code(::GetLastError(), generic_category());
170+
ec = detail::make_windows_error(GetLastError());
156171
__stream_ = INVALID_HANDLE_VALUE;
157172
return ec;
158173
}
159174

160175
HANDLE __stream_{INVALID_HANDLE_VALUE};
161-
WIN32_FIND_DATA __data_;
176+
WIN32_FIND_DATAW __data_;
162177

163178
public:
164179
path __root_;

libcxx/src/filesystem/filesystem_common.h

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717
#include "cstdlib"
1818
#include "ctime"
1919

20-
#include <unistd.h>
21-
#include <sys/stat.h>
22-
#include <sys/statvfs.h>
23-
#include <sys/time.h> // for ::utimes as used in __last_write_time
24-
#include <fcntl.h> /* values for fchmodat */
20+
#if !defined(_LIBCPP_WIN32API)
21+
# include <unistd.h>
22+
# include <sys/stat.h>
23+
# include <sys/statvfs.h>
24+
# include <sys/time.h> // for ::utimes as used in __last_write_time
25+
# include <fcntl.h> /* values for fchmodat */
26+
#endif
2527

2628
#include "../include/apple_availability.h"
2729

@@ -47,6 +49,12 @@
4749
_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
4850

4951
namespace detail {
52+
53+
#if defined(_LIBCPP_WIN32API)
54+
// Non anonymous, to allow access from two translation units.
55+
errc __win_err_to_errc(int err);
56+
#endif
57+
5058
namespace {
5159

5260
static string format_string_imp(const char* msg, ...) {
@@ -118,6 +126,12 @@ error_code capture_errno() {
118126
return error_code(errno, generic_category());
119127
}
120128

129+
#if defined(_LIBCPP_WIN32API)
130+
error_code make_windows_error(int err) {
131+
return make_error_code(__win_err_to_errc(err));
132+
}
133+
#endif
134+
121135
template <class T>
122136
T error_value();
123137
template <>

libcxx/src/filesystem/operations.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,73 @@ string_view_t createView(PosPtr S, PosPtr E) noexcept {
309309

310310
// POSIX HELPERS
311311

312+
#if defined(_LIBCPP_WIN32API)
313+
namespace detail {
314+
315+
errc __win_err_to_errc(int err) {
316+
constexpr struct {
317+
DWORD win;
318+
errc errc;
319+
} win_error_mapping[] = {
320+
{ERROR_ACCESS_DENIED, errc::permission_denied},
321+
{ERROR_ALREADY_EXISTS, errc::file_exists},
322+
{ERROR_BAD_NETPATH, errc::no_such_file_or_directory},
323+
{ERROR_BAD_UNIT, errc::no_such_device},
324+
{ERROR_BROKEN_PIPE, errc::broken_pipe},
325+
{ERROR_BUFFER_OVERFLOW, errc::filename_too_long},
326+
{ERROR_BUSY, errc::device_or_resource_busy},
327+
{ERROR_BUSY_DRIVE, errc::device_or_resource_busy},
328+
{ERROR_CANNOT_MAKE, errc::permission_denied},
329+
{ERROR_CANTOPEN, errc::io_error},
330+
{ERROR_CANTREAD, errc::io_error},
331+
{ERROR_CANTWRITE, errc::io_error},
332+
{ERROR_CURRENT_DIRECTORY, errc::permission_denied},
333+
{ERROR_DEV_NOT_EXIST, errc::no_such_device},
334+
{ERROR_DEVICE_IN_USE, errc::device_or_resource_busy},
335+
{ERROR_DIR_NOT_EMPTY, errc::directory_not_empty},
336+
{ERROR_DIRECTORY, errc::invalid_argument},
337+
{ERROR_DISK_FULL, errc::no_space_on_device},
338+
{ERROR_FILE_EXISTS, errc::file_exists},
339+
{ERROR_FILE_NOT_FOUND, errc::no_such_file_or_directory},
340+
{ERROR_HANDLE_DISK_FULL, errc::no_space_on_device},
341+
{ERROR_INVALID_ACCESS, errc::permission_denied},
342+
{ERROR_INVALID_DRIVE, errc::no_such_device},
343+
{ERROR_INVALID_FUNCTION, errc::function_not_supported},
344+
{ERROR_INVALID_HANDLE, errc::invalid_argument},
345+
{ERROR_INVALID_NAME, errc::no_such_file_or_directory},
346+
{ERROR_INVALID_PARAMETER, errc::invalid_argument},
347+
{ERROR_LOCK_VIOLATION, errc::no_lock_available},
348+
{ERROR_LOCKED, errc::no_lock_available},
349+
{ERROR_NEGATIVE_SEEK, errc::invalid_argument},
350+
{ERROR_NOACCESS, errc::permission_denied},
351+
{ERROR_NOT_ENOUGH_MEMORY, errc::not_enough_memory},
352+
{ERROR_NOT_READY, errc::resource_unavailable_try_again},
353+
{ERROR_NOT_SAME_DEVICE, errc::cross_device_link},
354+
{ERROR_NOT_SUPPORTED, errc::not_supported},
355+
{ERROR_OPEN_FAILED, errc::io_error},
356+
{ERROR_OPEN_FILES, errc::device_or_resource_busy},
357+
{ERROR_OPERATION_ABORTED, errc::operation_canceled},
358+
{ERROR_OUTOFMEMORY, errc::not_enough_memory},
359+
{ERROR_PATH_NOT_FOUND, errc::no_such_file_or_directory},
360+
{ERROR_READ_FAULT, errc::io_error},
361+
{ERROR_REPARSE_TAG_INVALID, errc::invalid_argument},
362+
{ERROR_RETRY, errc::resource_unavailable_try_again},
363+
{ERROR_SEEK, errc::io_error},
364+
{ERROR_SHARING_VIOLATION, errc::permission_denied},
365+
{ERROR_TOO_MANY_OPEN_FILES, errc::too_many_files_open},
366+
{ERROR_WRITE_FAULT, errc::io_error},
367+
{ERROR_WRITE_PROTECT, errc::permission_denied},
368+
};
369+
370+
for (const auto &pair : win_error_mapping)
371+
if (pair.win == static_cast<DWORD>(err))
372+
return pair.errc;
373+
return errc::invalid_argument;
374+
}
375+
376+
} // namespace detail
377+
#endif
378+
312379
namespace detail {
313380
namespace {
314381

0 commit comments

Comments
 (0)