Skip to content

Commit 3ce234a

Browse files
authored
Add detailed notes on Win32 API limitations for dirfile semantics in … (#1207)
* Add detailed notes on Win32 API limitations for dirfile semantics in fast_io_device.h; update nt_mkdirat_impl to improve directory creation handling in nt_at.h * Rename DOS path invalid character references to DOS file invalid character in char_category and related implementations for consistency. Update function signatures and usage across various platform headers to reflect this change. * update dos * Update nt_readlinkat_impl to include FILE_SYNCHRONOUS_IO_NONALERT in CreateOptions for improved reparse point handling. * update cygwin * Update push_macros.h
1 parent 3cf7625 commit 3ce234a

File tree

17 files changed

+160
-58
lines changed

17 files changed

+160
-58
lines changed

include/fast_io_core.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,13 @@
99
#if !defined(__cpp_concepts)
1010
#error "fast_io requires at least C++20 standard compiler."
1111
#else
12+
13+
#include <version>
14+
#include <cstdint>
15+
#include <cstddef>
16+
#include <climits>
1217
#include <bit>
1318
#include <limits>
14-
#include <cstdint>
15-
#include <version>
1619

1720
#if __cpp_lib_three_way_comparison >= 201907L
1821
#include <compare>

include/fast_io_core_impl/char_category/char_category.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1795,7 +1795,7 @@ inline constexpr bool is_html_whitespace_wide_impl(wchar_t ch) noexcept
17951795
};
17961796
}
17971797

1798-
inline constexpr bool is_dos_path_invalid_character_impl(char32_t ch) noexcept
1798+
inline constexpr bool is_dos_file_invalid_character_impl(char32_t ch) noexcept
17991799
{
18001800
if (ch < static_cast<char32_t>(32u))
18011801
{
@@ -1926,15 +1926,15 @@ To do: to_c_fullwidth
19261926
*/
19271927

19281928
template <::std::integral T>
1929-
inline constexpr bool is_dos_path_invalid_character(T ch) noexcept
1929+
inline constexpr bool is_dos_file_invalid_character(T ch) noexcept
19301930
{
19311931
if constexpr (::std::signed_integral<T>)
19321932
{
1933-
return ::fast_io::char_category::details::is_dos_path_invalid_character_impl(static_cast<char32_t>(static_cast<::std::make_unsigned_t<T>>(ch)));
1933+
return ::fast_io::char_category::details::is_dos_file_invalid_character_impl(static_cast<char32_t>(static_cast<::std::make_unsigned_t<T>>(ch)));
19341934
}
19351935
else
19361936
{
1937-
return ::fast_io::char_category::details::is_dos_path_invalid_character_impl(static_cast<char32_t>(ch));
1937+
return ::fast_io::char_category::details::is_dos_file_invalid_character_impl(static_cast<char32_t>(ch));
19381938
}
19391939
}
19401940

include/fast_io_core_impl/char_category/char_category_traits.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ enum class char_category_family : ::std::uint_least32_t
1919
c_space, // Whitespace characters (space, tab, newline, etc.)
2020
c_upper, // Uppercase alphabetic characters
2121
c_xdigit, // Hexadecimal digits (0-9, A-F, a-f)
22-
dos_path_invalid_character, // DOS Path invalid character
22+
dos_file_invalid_character, // DOS Path invalid character
2323
html_whitespace // HTML whitespace
2424
};
2525

@@ -96,9 +96,9 @@ class char_category_traits
9696
{
9797
ret = ::fast_io::char_category::is_c_fullwidth(ch);
9898
}
99-
else if constexpr (fam == ::fast_io::char_category::char_category_family::dos_path_invalid_character)
99+
else if constexpr (fam == ::fast_io::char_category::char_category_family::dos_file_invalid_character)
100100
{
101-
ret = ::fast_io::char_category::is_dos_path_invalid_character(ch);
101+
ret = ::fast_io::char_category::is_dos_file_invalid_character(ch);
102102
}
103103
if constexpr (negate)
104104
{
@@ -192,7 +192,7 @@ using c_punct = ::fast_io::char_category::char_category_traits<::fast_io::char_c
192192
using c_space = ::fast_io::char_category::char_category_traits<::fast_io::char_category::char_category_family::c_space, false>;
193193
using c_upper = ::fast_io::char_category::char_category_traits<::fast_io::char_category::char_category_family::c_upper, false>;
194194
using c_xdigit = ::fast_io::char_category::char_category_traits<::fast_io::char_category::char_category_family::c_xdigit, false>;
195-
using dos_path_invalid_character = ::fast_io::char_category::char_category_traits<::fast_io::char_category::char_category_family::dos_path_invalid_character, false>;
195+
using dos_file_invalid_character = ::fast_io::char_category::char_category_traits<::fast_io::char_category::char_category_family::dos_file_invalid_character, false>;
196196
using html_whitespace = ::fast_io::char_category::char_category_traits<::fast_io::char_category::char_category_family::html_whitespace, false>;
197197

198198

@@ -210,7 +210,7 @@ using not_c_punct = ::fast_io::char_category::char_category_traits<::fast_io::ch
210210
using not_c_space = ::fast_io::char_category::char_category_traits<::fast_io::char_category::char_category_family::c_space, true>;
211211
using not_c_upper = ::fast_io::char_category::char_category_traits<::fast_io::char_category::char_category_family::c_upper, true>;
212212
using not_c_xdigit = ::fast_io::char_category::char_category_traits<::fast_io::char_category::char_category_family::c_xdigit, true>;
213-
using not_dos_path_invalid_character = ::fast_io::char_category::char_category_traits<::fast_io::char_category::char_category_family::dos_path_invalid_character, true>;
213+
using not_dos_file_invalid_character = ::fast_io::char_category::char_category_traits<::fast_io::char_category::char_category_family::dos_file_invalid_character, true>;
214214
using not_html_whitespace = ::fast_io::char_category::char_category_traits<::fast_io::char_category::char_category_family::html_whitespace, true>;
215215

216216
namespace details

include/fast_io_device.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,20 @@ using dir_file = directory_file_wrapper<
2929
#endif
3030
>;
3131

32+
// Note:
33+
// The Win32 API layer is not well-suited for precise "dirfile" semantics,
34+
// because it abstracts away the underlying NT object types. Unlike the NT I/O
35+
// manager (where FILE_DIRECTORY_FILE and FILE_NON_DIRECTORY_FILE flags enforce
36+
// strict open-type constraints), Win32’s CreateFileW does not distinguish
37+
// between files and directories unless explicitly checked afterwards.
38+
//
39+
// FILE_FLAG_BACKUP_SEMANTICS merely *permits* opening a directory handle,
40+
// but does not *require* the target to be a directory. Similarly, adding
41+
// FILE_ATTRIBUTE_DIRECTORY has no enforcement effect — it's only a metadata hint.
42+
// Therefore, to emulate dirfile-style correctness, one must explicitly query
43+
// the object type (e.g., via GetFileInformationByHandle or GetFileAttributes)
44+
// after opening the handle.
45+
3246
/*
3347
template region
3448
*/

include/fast_io_driver/install_path/impl.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22

33
#include"install_path.h"
44

5-
#if defined(_WIN32)
5+
#if defined(_WIN32) || defined(__CYGWIN__)
66
#include "argv0_null.h"
77
#else
88
#include "argv0.h"
99
#endif
1010

11-
#if (defined(__linux) || defined(__linux__) || defined(__gnu_linux__)) || defined(__CYGWIN__) || defined(__sun)
11+
#if (defined(__linux) || defined(__linux__) || defined(__gnu_linux__)) || defined(__sun)
1212
#include "linux.h"
13-
#elif defined(_WIN32)
13+
#elif defined(_WIN32) || defined(__CYGWIN__)
1414
#if defined(_WIN32_WINDOWS)
1515
#include "win32_9xa.h"
1616
#else

include/fast_io_dsal/impl/misc/push_macros.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,14 @@
1414
#undef move
1515

1616
#pragma push_macro("new")
17+
#if __GNUC__ >= 16
18+
#pragma GCC diagnostic push
19+
#pragma GCC diagnostic ignored "-Wkeyword-macro"
1720
#undef new
21+
#pragma GCC diagnostic pop
22+
#else
23+
#undef new
24+
#endif
1825

1926
#pragma push_macro("refresh")
2027
#undef refresh

include/fast_io_hosted/dll/win32.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ inline void *create_win32_dll_9xa(char const *filename) // 9x kernel does not su
9898
inline void *create_win32_dll_ntw(char16_t const *filename, [[maybe_unused]] dll_mode mode)
9999
{
100100
auto hmodule{
101-
#if _WIN32_WINNT <= 0x0500
101+
#if (defined(_WIN32_WINNT) && _WIN32_WINNT <= 0x0500)
102102
// Windows 2000 does not support Ex apis
103103
::fast_io::win32::LoadLibraryW(filename)
104104
#else

include/fast_io_hosted/filesystem/dos.h

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ namespace posix
6060
// extern char const* my_dos_get_fd_name(int) noexcept __asm__("___get_fd_name");
6161
extern DIR *my_dos_opendir(char const *) noexcept __asm__("_opendir");
6262

63+
extern int my_dos_closedir(DIR *) noexcept __asm__("_closedir");
64+
6365
inline DIR *my_dos_fdopendir(int fd) noexcept
6466
{
6567
return my_dos_opendir(::fast_io::noexcept_call(::__get_fd_name, fd));
@@ -69,6 +71,16 @@ inline DIR *my_dos_fdopendir(int fd) noexcept
6971
namespace details
7072
{
7173

74+
inline void check_dos_fd_is_dir(int fd)
75+
{
76+
auto const dir{::fast_io::posix::my_dos_fdopendir(fd)};
77+
if (dir == nullptr) [[unlikely]]
78+
{
79+
throw_posix_error(ENOTDIR);
80+
}
81+
::fast_io::posix::my_dos_closedir(dir);
82+
}
83+
7284
inline dos_DIR sys_dup_dir(dos_DIR dirp)
7385
{
7486
if (dirp.dirp == nullptr) [[unlikely]]
@@ -127,7 +139,7 @@ class dos_directory_file FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE : public dos_
127139
auto newdir{details::sys_dup_dir(other.dirp)};
128140
if (this->dirp.dirp) [[likely]]
129141
{
130-
noexcept_call(::closedir, this->dirp.dirp);
142+
::fast_io::posix::my_dos_closedir(this->dirp.dirp);
131143
}
132144
if (this->dirp.fd != -1) [[likely]]
133145
{
@@ -149,7 +161,7 @@ class dos_directory_file FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE : public dos_
149161
}
150162
if (this->dirp.dirp) [[likely]]
151163
{
152-
noexcept_call(::closedir, this->dirp.dirp);
164+
::fast_io::posix::my_dos_closedir(this->dirp.dirp);
153165
}
154166
if (this->dirp.fd != -1) [[likely]]
155167
{
@@ -163,7 +175,7 @@ class dos_directory_file FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE : public dos_
163175
{
164176
if (this->dirp.dirp) [[likely]]
165177
{
166-
noexcept_call(::closedir, this->dirp.dirp);
178+
::fast_io::posix::my_dos_closedir(this->dirp.dirp);
167179
}
168180
if (this->dirp.fd != -1) [[likely]]
169181
{
@@ -177,7 +189,7 @@ class dos_directory_file FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE : public dos_
177189
if (*this) [[likely]]
178190
{
179191
int fd_to_close{this->dirp.fd};
180-
int ret{noexcept_call(::closedir, this->dirp.dirp)};
192+
int ret{::fast_io::posix::my_dos_closedir(this->dirp.dirp)};
181193
this->dirp.dirp = nullptr;
182194
this->dirp.fd = -1;
183195
if (fd_to_close != -1)
@@ -195,7 +207,7 @@ class dos_directory_file FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE : public dos_
195207
{
196208
if (this->dirp.dirp) [[likely]]
197209
{
198-
noexcept_call(::closedir, this->dirp.dirp);
210+
::fast_io::posix::my_dos_closedir(this->dirp.dirp);
199211
}
200212
if (this->dirp.fd != -1) [[likely]]
201213
{

include/fast_io_hosted/filesystem/nt_at.h

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,21 @@ inline void nt_unlinkat_impl(void *dirhd, char16_t const *path_c_str, ::std::siz
9393
template <bool zw>
9494
inline void nt_mkdirat_impl(void *dirhd, char16_t const *path_c_str, ::std::size_t path_size, perms pm, bool kernel)
9595
{
96-
constexpr fast_io::win32::nt::details::nt_open_mode create_dir_mode{
97-
fast_io::win32::nt::details::calculate_nt_open_mode(
98-
{fast_io::open_mode::creat | fast_io::open_mode::directory})};
99-
auto m_dir_mode{create_dir_mode};
96+
nt_open_mode m_dir_mode{
97+
.DesiredAccess = 0x00100000 | 0x0001, // SYNCHRONIZE | FILE_LIST_DIRECTORY
98+
.FileAttributes = 0x80, // FILE_ATTRIBUTE_NORMAL
99+
.ShareAccess = 0x00000003, // FILE_SHARE_READ | FILE_SHARE_WRITE
100+
.CreateDisposition = 0x00000002, // CREATE_NEW => FILE_CREATE (0x00000002)
101+
.CreateOptions = 0x00004021 // FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
102+
};
103+
100104
if ((pm & perms::owner_write) == perms::none)
101105
{
102106
m_dir_mode.FileAttributes |= 0x00000001; // FILE_ATTRIBUTE_READONLY
103107
}
108+
104109
auto status{nt_close<zw>(nt_call_determine_kernel_callback(dirhd, path_c_str, path_size, kernel, nt_create_callback<zw>{m_dir_mode}))};
110+
105111
if (status)
106112
{
107113
throw_nt_error(status);
@@ -775,7 +781,7 @@ inline ::fast_io::details::basic_ct_string<char_type> nt_readlinkat_impl(void *d
775781
.FileAttributes = 0x80, // FILE_ATTRIBUTE_NORMAL
776782
.ShareAccess = 0x00000007, // FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE
777783
.CreateDisposition = 0x00000001, // OPEN_EXISTING => FILE_OPEN
778-
.CreateOptions = 0x00200000 // FILE_FLAG_OPEN_REPARSE_POINT => FILE_OPEN_REPARSE_POINT (0x00200000)
784+
.CreateOptions = 0x00200000 | 0x00000020 // FILE_FLAG_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT
779785
};
780786

781787
::fast_io::basic_nt_family_file<(zw ? nt_family::zw : nt_family::nt), char> file{

include/fast_io_hosted/filesystem/posix.h

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,36 @@ namespace fast_io
66

77
namespace details
88
{
9+
#if defined(__CYGWIN__)
10+
struct my_cygwin_DIR
11+
{
12+
/* This is first to set alignment in non _LIBC case. */
13+
unsigned long __d_cookie;
14+
struct dirent *__d_dirent;
15+
char *__d_dirname; /* use for internal caching */
16+
::std::int32_t __d_position; /* used by telldir/seekdir */
17+
int __d_fd;
18+
::std::uintptr_t __d_internal;
19+
void *__handle;
20+
void *__fh;
21+
unsigned __flags;
22+
};
23+
24+
using my_cygwin_DIR_may_alias_ptr =
25+
#if __has_cpp_attribute(__gnu__::__may_alias__)
26+
[[__gnu__::__may_alias__]]
27+
#endif
28+
my_cygwin_DIR *;
29+
#endif
30+
931
inline int dirp_to_fd(DIR *dirp) noexcept
1032
{
1133
if (dirp == nullptr)
1234
{
1335
return -1;
1436
}
1537
#if defined(__CYGWIN__)
16-
return dirp->__d_fd;
38+
return reinterpret_cast<my_cygwin_DIR_may_alias_ptr>(dirp)->__d_fd;
1739
#else
1840
return dirfd(dirp);
1941
#endif
@@ -58,7 +80,7 @@ inline DIR *sys_dup_dir(DIR *dirp)
5880
}
5981
auto fd{
6082
#if defined(__CYGWIN__)
61-
dirp->__d_fd
83+
reinterpret_cast<my_cygwin_DIR_may_alias_ptr>(dirp)->__d_fd
6284
#else
6385
dirfd(dirp)
6486
#endif

0 commit comments

Comments
 (0)