diff --git a/examples/0036.at/readlink.cc b/examples/0036.at/readlink.cc new file mode 100644 index 000000000..c8ffb93d6 --- /dev/null +++ b/examples/0036.at/readlink.cc @@ -0,0 +1,30 @@ +#include + +int main(int argc, char **argv) +{ + if (argc != 2) + { + if (argc == 0) + { + return 1; + } + ::fast_io::io::perr("Usage: ", fast_io::mnp::os_c_str(*argv), " \n"); + return 1; + } + +#if __cpp_exceptions + try +#endif + { + auto target{::fast_io::native_readlinkat(::fast_io::at_fdcwd(), fast_io::mnp::os_c_str(argv[1]))}; + ::fast_io::println(target); + } +#if __cpp_exceptions + catch (fast_io::error e) + { + ::fast_io::io::perrln(e); + } +#endif +} + + diff --git a/include/fast_io_hosted/filesystem/apis.h b/include/fast_io_hosted/filesystem/apis.h index dd6625ca1..6574729cb 100644 --- a/include/fast_io_hosted/filesystem/apis.h +++ b/include/fast_io_hosted/filesystem/apis.h @@ -8,10 +8,12 @@ enum class posix_api_22 renameat, linkat }; + enum class posix_api_12 { symlinkat }; + enum class posix_api_1x { faccessat, @@ -24,4 +26,9 @@ enum class posix_api_1x unlinkat }; +enum class posix_api_ct +{ + readlinkat +}; + } // namespace fast_io::details diff --git a/include/fast_io_hosted/filesystem/dos.h b/include/fast_io_hosted/filesystem/dos.h index 97b9d3ceb..0f236cf86 100644 --- a/include/fast_io_hosted/filesystem/dos.h +++ b/include/fast_io_hosted/filesystem/dos.h @@ -129,6 +129,10 @@ class dos_directory_file FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE : public dos_ { noexcept_call(::closedir, this->dirp.dirp); } + if (this->dirp.fd != -1) [[likely]] + { + details::sys_close(this->dirp.fd); + } this->dirp = newdir; return *this; } @@ -147,6 +151,10 @@ class dos_directory_file FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE : public dos_ { noexcept_call(::closedir, this->dirp.dirp); } + if (this->dirp.fd != -1) [[likely]] + { + details::sys_close(this->dirp.fd); + } this->dirp = other.release(); return *this; } @@ -157,6 +165,10 @@ class dos_directory_file FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE : public dos_ { noexcept_call(::closedir, this->dirp.dirp); } + if (this->dirp.fd != -1) [[likely]] + { + details::sys_close(this->dirp.fd); + } this->dirp = dirp1; } @@ -164,9 +176,14 @@ class dos_directory_file FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE : public dos_ { if (*this) [[likely]] { + int fd_to_close{this->dirp.fd}; int ret{noexcept_call(::closedir, this->dirp.dirp)}; this->dirp.dirp = nullptr; this->dirp.fd = -1; + if (fd_to_close != -1) + { + details::sys_close(fd_to_close); + } if (ret == -1) { throw_posix_error(); @@ -180,6 +197,11 @@ class dos_directory_file FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE : public dos_ { noexcept_call(::closedir, this->dirp.dirp); } + if (this->dirp.fd != -1) [[likely]] + { + // best-effort close for fd; ignore failures in destructor + details::sys_close(this->dirp.fd); + } } }; diff --git a/include/fast_io_hosted/filesystem/dos_at.h b/include/fast_io_hosted/filesystem/dos_at.h index a63ce23ba..5d9ef87de 100644 --- a/include/fast_io_hosted/filesystem/dos_at.h +++ b/include/fast_io_hosted/filesystem/dos_at.h @@ -263,6 +263,26 @@ inline auto dos1x_api_dispatcher(int dirfd, char const *path, Args... args) } } +template <::std::integral char_type> +inline ::fast_io::details::basic_ct_string dos_readlinkat_impl(int dirfd, char const *pathname) +{ + // DOS does not support readlink, so you must first verify its validity before throwing a einval exception (not symlink). + ::fast_io::system_call_throw_error(::fast_io::posix::my_dos_access(::fast_io::details::my_dos_concat_tlc_path(dirfd, pathname).c_str(), 0)); + + throw_posix_error(EINVAL); + + return {}; +} + +template <::std::integral char_type, posix_api_ct dsp, typename... Args> +inline auto dosct_api_dispatcher(int dirfd, char const *path, Args... args) +{ + if constexpr (dsp == ::fast_io::details::posix_api_ct::readlinkat) + { + return dos_readlinkat_impl(dirfd, path, args...); + } +} + template inline auto dos_deal_with22(int olddirfd, old_path_type const &oldpath, int newdirfd, new_path_type const &newpath, Args... args) @@ -293,6 +313,12 @@ inline auto dos_deal_with1x(int dirfd, path_type const &path, Args... args) return ::fast_io::posix_api_common(path, [&](char const *path_c_str) { return dos1x_api_dispatcher(dirfd, path_c_str, args...); }); } +template <::std::integral char_type, posix_api_ct dsp, ::fast_io::constructible_to_os_c_str path_type, typename... Args> +inline auto dos_deal_withct(int dirfd, path_type const &path, Args... args) +{ + return ::fast_io::posix_api_common(path, [&](char const *path_c_str) { return dosct_api_dispatcher(dirfd, path_c_str, args...); }); +} + } // namespace details template <::fast_io::constructible_to_os_c_str old_path_type, ::fast_io::constructible_to_os_c_str new_path_type> @@ -337,17 +363,17 @@ inline void native_symlinkat(old_path_type const &oldpath, posix_at_entry newent template <::fast_io::constructible_to_os_c_str path_type> -inline void dos_faccessat(posix_at_entry ent, path_type const &path, [[maybe_unused]] access_how mode, - dos_at_flags flags = dos_at_flags::symlink_nofollow) +inline void dos_faccessat(posix_at_entry ent, path_type const &path, access_how mode, + [[maybe_unused]] dos_at_flags flags = dos_at_flags::symlink_nofollow) { - ::fast_io::details::dos_deal_with1x<::fast_io::details::posix_api_1x::faccessat>(ent.fd, path, static_cast(flags)); + ::fast_io::details::dos_deal_with1x<::fast_io::details::posix_api_1x::faccessat>(ent.fd, path, static_cast(mode)); } template <::fast_io::constructible_to_os_c_str path_type> -inline void native_faccessat(posix_at_entry ent, path_type const &path, [[maybe_unused]] access_how mode, - dos_at_flags flags = dos_at_flags::symlink_nofollow) +inline void native_faccessat(posix_at_entry ent, path_type const &path, access_how mode, + [[maybe_unused]] dos_at_flags flags = dos_at_flags::symlink_nofollow) { - ::fast_io::details::dos_deal_with1x<::fast_io::details::posix_api_1x::faccessat>(ent.fd, path, static_cast(flags)); + ::fast_io::details::dos_deal_with1x<::fast_io::details::posix_api_1x::faccessat>(ent.fd, path, static_cast(mode)); } template <::fast_io::constructible_to_os_c_str path_type> @@ -448,4 +474,16 @@ inline void native_utimensat(posix_at_entry ent, path_type const &path, unix_tim last_modification_time); } +template <::std::integral char_type, ::fast_io::constructible_to_os_c_str path_type> +inline ::fast_io::details::basic_ct_string dos_readlinkat(posix_at_entry ent, path_type const &path) +{ + return ::fast_io::details::dos_deal_withct(ent.fd, path); +} + +template <::std::integral char_type, ::fast_io::constructible_to_os_c_str path_type> +inline ::fast_io::details::basic_ct_string native_readlinkat(posix_at_entry ent, path_type const &path) +{ + return ::fast_io::details::dos_deal_withct(ent.fd, path); +} + } // namespace fast_io diff --git a/include/fast_io_hosted/filesystem/fsutils.h b/include/fast_io_hosted/filesystem/fsutils.h index e2fba85e5..c7234676c 100644 --- a/include/fast_io_hosted/filesystem/fsutils.h +++ b/include/fast_io_hosted/filesystem/fsutils.h @@ -65,4 +65,23 @@ find_dot_and_sep(char_type const *beg_ptr, ::std::size_t namlen) noexcept } } +template <::std::integral char_type> +using basic_ct_string = ::fast_io::containers::basic_string; + +template <::std::integral char_type, typename... Args> +inline constexpr basic_ct_string concat_ct(Args &&...args) +{ + constexpr bool type_error{::fast_io::operations::defines::print_freestanding_okay<::fast_io::details::dummy_buffer_output_stream,Args...>}; + if constexpr (type_error) + { + return ::fast_io::details::decay::basic_general_concat_phase1_decay_impl>( + io_print_forward(io_print_alias(args))...); + } + else + { + static_assert(type_error, "some types are not printable, so we cannot concat ::fast_io::containers::basic_string"); + return {}; + } +} + } // namespace fast_io::details diff --git a/include/fast_io_hosted/filesystem/nt_at.h b/include/fast_io_hosted/filesystem/nt_at.h index 75ca4f4fb..802eb55ef 100644 --- a/include/fast_io_hosted/filesystem/nt_at.h +++ b/include/fast_io_hosted/filesystem/nt_at.h @@ -63,7 +63,7 @@ inline constexpr nt_open_mode calculate_nt_delete_flag(nt_at_flags flags) noexce .CreateDisposition = 0x00000001, /*OPEN_EXISTING => FILE_OPEN*/ .CreateOptions = 0x00001000 /*FILE_DELETE_ON_CLOSE*/ }; - if ((flags & nt_at_flags::symlink_nofollow) != nt_at_flags::symlink_nofollow) + if ((flags & nt_at_flags::symlink_nofollow) == nt_at_flags::symlink_nofollow) { mode.CreateOptions |= 0x00200000; // FILE_FLAG_OPEN_REPARSE_POINT => FILE_OPEN_REPARSE_POINT (0x00200000) } @@ -132,7 +132,7 @@ inline void nt_faccessat_impl(void *dirhd, char16_t const *path_c_str, ::std::si .CreateDisposition = 0x00000001, // OPEN_EXISTING => FILE_OPEN }; - if ((flags & nt_at_flags::symlink_nofollow) != nt_at_flags::symlink_nofollow) + if ((flags & nt_at_flags::symlink_nofollow) == nt_at_flags::symlink_nofollow) { md.CreateOptions |= 0x00200000; // FILE_FLAG_OPEN_REPARSE_POINT => FILE_OPEN_REPARSE_POINT (0x00200000) } @@ -185,7 +185,7 @@ inline void nt_fchmodat_impl(void *dirhd, char16_t const *path_c_str, ::std::siz .CreateDisposition = 0x00000001, // OPEN_EXISTING => FILE_OPEN }; - if ((flags & nt_at_flags::symlink_nofollow) != nt_at_flags::symlink_nofollow) + if ((flags & nt_at_flags::symlink_nofollow) == nt_at_flags::symlink_nofollow) { md.CreateOptions |= 0x00200000; // FILE_FLAG_OPEN_REPARSE_POINT => FILE_OPEN_REPARSE_POINT (0x00200000) } @@ -243,7 +243,7 @@ template .CreateDisposition = 0x00000001, // OPEN_EXISTING => FILE_OPEN }; - if ((flags & nt_at_flags::symlink_nofollow) != nt_at_flags::symlink_nofollow) + if ((flags & nt_at_flags::symlink_nofollow) == nt_at_flags::symlink_nofollow) { md.CreateOptions |= 0x00200000; // FILE_FLAG_OPEN_REPARSE_POINT => FILE_OPEN_REPARSE_POINT (0x00200000) } @@ -267,7 +267,7 @@ inline posix_file_status nt_fstatat_impl(void *dirhd, char16_t const *path_c_str .CreateDisposition = 0x00000001, // OPEN_EXISTING => FILE_OPEN }; - if ((flags & nt_at_flags::symlink_nofollow) != nt_at_flags::symlink_nofollow) + if ((flags & nt_at_flags::symlink_nofollow) == nt_at_flags::symlink_nofollow) { md.CreateOptions |= 0x00200000; // FILE_FLAG_OPEN_REPARSE_POINT => FILE_OPEN_REPARSE_POINT (0x00200000) } @@ -289,7 +289,7 @@ inline void nt_utimensat_impl(void *dirhd, char16_t const *path_c_str, ::std::si .CreateDisposition = 0x00000001, // OPEN_EXISTING => FILE_OPEN }; - if ((flags & nt_at_flags::symlink_nofollow) != nt_at_flags::symlink_nofollow) + if ((flags & nt_at_flags::symlink_nofollow) == nt_at_flags::symlink_nofollow) { md.CreateOptions |= 0x00200000; // FILE_FLAG_OPEN_REPARSE_POINT => FILE_OPEN_REPARSE_POINT (0x00200000) } @@ -435,8 +435,8 @@ struct nt_create_nothrow_callback template inline void nt_symlinkat_impl([[maybe_unused]] char16_t const *oldpath_c_str, [[maybe_unused]] ::std::size_t oldpath_size, - [[maybe_unused]] void *newdirhd, [[maybe_unused]] char16_t const *newpath_c_str, - [[maybe_unused]] ::std::size_t newpath_size, [[maybe_unused]] bool kernel) + [[maybe_unused]] void *newdirhd, [[maybe_unused]] char16_t const *newpath_c_str, + [[maybe_unused]] ::std::size_t newpath_size, [[maybe_unused]] bool kernel) { #if !defined(_WIN32_WINNT) || _WIN32_WINNT > 0x0600 @@ -707,7 +707,7 @@ inline constexpr nt_open_mode calculate_nt_link_flag(nt_at_flags flags) noexcept .ShareAccess = 0x00000007, // FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE .CreateDisposition = 0x00000001, /*OPEN_EXISTING => FILE_OPEN*/ }; - if ((flags & nt_at_flags::symlink_nofollow) != nt_at_flags::symlink_nofollow) + if ((flags & nt_at_flags::symlink_nofollow) == nt_at_flags::symlink_nofollow) { mode.CreateOptions |= 0x00200000; // FILE_FLAG_OPEN_REPARSE_POINT => FILE_OPEN_REPARSE_POINT (0x00200000) } @@ -766,6 +766,91 @@ inline void nt_linkat_impl(void *olddirhd, char16_t const *oldpath_c_str, ::std: }); } +template +inline ::fast_io::details::basic_ct_string nt_readlinkat_impl(void *dirhd, char16_t const *path_c_str, ::std::size_t path_size, bool kernel) +{ +#if !defined(_WIN32_WINNT) || _WIN32_WINNT > 0x0600 + constexpr ::fast_io::win32::nt::details::nt_open_mode md{ + .DesiredAccess = 0x00100000 | 0x0080, // SYNCHRONIZE | FILE_READ_ATTRIBUTES + .FileAttributes = 0x80, // FILE_ATTRIBUTE_NORMAL + .ShareAccess = 0x00000007, // FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE + .CreateDisposition = 0x00000001, // OPEN_EXISTING => FILE_OPEN + .CreateOptions = 0x00200000 // FILE_FLAG_OPEN_REPARSE_POINT => FILE_OPEN_REPARSE_POINT (0x00200000) + }; + + ::fast_io::basic_nt_family_file<(zw ? nt_family::zw : nt_family::nt), char> file{ + nt_call_determine_kernel_callback(dirhd, path_c_str, path_size, kernel, nt_create_callback{md})}; + + constexpr ::std::size_t buffer_size{16u * 1024u}; + alignas(::fast_io::win32::nt::reparse_data_buffer)::std::byte buffer[buffer_size]; // no init + + ::fast_io::win32::nt::io_status_block isb; + + auto status{::fast_io::win32::nt::nt_fs_control_file(file.native_handle(), + nullptr, + nullptr, + nullptr, + __builtin_addressof(isb), + 0x900A8, + nullptr, + 0u, + buffer, + static_cast<::std::uint_least32_t>(buffer_size))}; + + + if (status) + { + throw_nt_error(status); + } + + // ::std::size_t const bytesReturned{isb.Information}; + + using reparse_data_buffer_const_may_alias_ptr +#if __has_cpp_attribute(__gnu__::__may_alias__) + [[__gnu__::__may_alias__]] +#endif + = ::fast_io::win32::nt::reparse_data_buffer const *; + + auto const reparse_data{reinterpret_cast(buffer)}; + + if (reparse_data->ReparseTag == 0xA000000C /*IO_REPARSE_TAG_SYMLINK*/) + { + char16_t const *target{reparse_data->u.SymbolicLinkReparseBuffer.PathBuffer + + (reparse_data->u.SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(char16_t))}; + ::std::size_t len{static_cast<::std::size_t>(reparse_data->u.SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(char16_t))}; + return ::fast_io::details::concat_ct(::fast_io::mnp::code_cvt(::fast_io::mnp::strvw(target, target + len))); + } +#if 0 + else if (reparse_data->ReparseTag == 0xA0000003 /*IO_REPARSE_TAG_MOUNT_POINT*/) + { + // When simulating the behavior of Linux's readlink() function, you typically only need to handle IO_REPARSE_TAG_SYMLINK. + } +#endif + else + { + throw_nt_error(0xC0000002); + } + + return {}; + +#else + constexpr nt_open_mode md{ + .DesiredAccess = 0x00100000 | 0x0080, // SYNCHRONIZE | FILE_READ_ATTRIBUTES + .FileAttributes = 0x80, // FILE_ATTRIBUTE_NORMAL + .ShareAccess = 0x00000007, // FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE + .CreateDisposition = 0x00000001, // OPEN_EXISTING => FILE_OPEN + .CreateOptions = 0x00200000 // FILE_FLAG_OPEN_REPARSE_POINT => FILE_OPEN_REPARSE_POINT (0x00200000) + }; + + ::fast_io::basic_nt_family_file<(zw ? nt_family::zw : nt_family::nt), char> file{ + nt_call_determine_kernel_callback(dirhd, path_c_str, path_size, kernel, nt_create_callback{md})}; + + throw_nt_error(0xC0000002); + + return {}; +#endif +} + template inline auto nt22_api_dispatcher(void *olddirhd, char16_t const *oldpath_c_str, ::std::size_t oldpath_size, void *newdirhd, char16_t const *newpath_c_str, ::std::size_t newpath_size, Args... args) @@ -824,12 +909,21 @@ inline auto nt1x_api_dispatcher(void *dir_handle, char16_t const *path_c_str, :: } } +template +inline auto ntct_api_dispatcher(void *dir_handle, char16_t const *path_c_str, ::std::size_t path_size, Args... args) +{ + if constexpr (dsp == ::fast_io::details::posix_api_ct::readlinkat) + { + return nt_readlinkat_impl(dir_handle, path_c_str, path_size, args...); + } +} + template inline auto nt_deal_with1x(void *dir_handle, path_type const &path, Args... args) { return nt_api_common( path, [&](char16_t const *path_c_str, ::std::size_t path_size) { - return nt1x_api_dispatcher < family == nt_family::zw, dsp > (dir_handle, path_c_str, path_size, args...); + return nt1x_api_dispatcher(dir_handle, path_c_str, path_size, args...); }); } @@ -842,7 +936,7 @@ inline auto nt_deal_with12(old_path_type const &oldpath, void *newdirfd, new_pat [&](char16_t const *oldpath_c_str, ::std::size_t oldpath_size) { return nt_api_common( newpath, [&](char16_t const *newpath_c_str, ::std::size_t newpath_size) { - return nt12_api_dispatcher < family == nt_family::zw, dsp > (oldpath_c_str, oldpath_size, newdirfd, newpath_c_str, newpath_size, args...); + return nt12_api_dispatcher(oldpath_c_str, oldpath_size, newdirfd, newpath_c_str, newpath_size, args...); }); }); } @@ -854,12 +948,21 @@ inline auto nt_deal_with22(void *olddirhd, oldpath_type const &oldpath, void *ne [&](char16_t const *oldpath_c_str, ::std::size_t oldpath_size) { return nt_api_common(newpath, [&](char16_t const *newpath_c_str, ::std::size_t newpath_size) { - return nt22_api_dispatcher < family == nt_family::zw, dsp > (olddirhd, oldpath_c_str, oldpath_size, newdirhd, - newpath_c_str, newpath_size, args...); + return nt22_api_dispatcher(olddirhd, oldpath_c_str, oldpath_size, newdirhd, + newpath_c_str, newpath_size, args...); }); }); } +template +inline auto nt_deal_withct(void *dir_handle, path_type const &path, Args... args) +{ + return nt_api_common( + path, [&](char16_t const *path_c_str, ::std::size_t path_size) { + return ntct_api_dispatcher(dir_handle, path_c_str, path_size, args...); + }); +} + } // namespace win32::nt::details // 1x @@ -1130,6 +1233,32 @@ inline void nt_family_renameat(nt_at_entry oldent, old_path_type &&oldpath, nt_a newent.handle, newpath, kernel); } +// ct + +template <::std::integral char_type, ::fast_io::constructible_to_os_c_str path_type> +inline ::fast_io::details::basic_ct_string nt_readlinkat(nt_at_entry ent, path_type const &path) +{ + return ::fast_io::win32::nt::details::nt_deal_withct(ent.handle, path, false); +} + +template <::std::integral char_type, ::fast_io::constructible_to_os_c_str path_type> +inline ::fast_io::details::basic_ct_string zw_readlinkat(nt_at_entry ent, path_type const &path) +{ + return ::fast_io::win32::nt::details::nt_deal_withct(ent.handle, path, false); +} + +template <::std::integral char_type, ::fast_io::constructible_to_os_c_str path_type> +inline ::fast_io::details::basic_ct_string nt_readlinkat(io_kernel_t, nt_at_entry ent, path_type const &path) +{ + return ::fast_io::win32::nt::details::nt_deal_withct(ent.handle, path, true); +} + +template <::std::integral char_type, ::fast_io::constructible_to_os_c_str path_type> +inline ::fast_io::details::basic_ct_string zw_readlinkat(io_kernel_t, nt_at_entry ent, path_type const &path) +{ + return ::fast_io::win32::nt::details::nt_deal_withct(ent.handle, path, true); +} + template <::fast_io::constructible_to_os_c_str old_path_type, ::fast_io::constructible_to_os_c_str new_path_type> inline void nt_renameat(nt_at_entry oldent, old_path_type &&oldpath, nt_at_entry newent, new_path_type &&newpath) { @@ -1260,5 +1389,11 @@ inline void native_linkat(nt_at_entry oldent, old_path_type &&oldpath, nt_at_ent ::fast_io::win32::nt::details::nt_deal_with22(oldent.handle, oldpath, newent.handle, newpath, flags, false); } + +template <::std::integral char_type, ::fast_io::constructible_to_os_c_str path_type> +inline ::fast_io::details::basic_ct_string native_readlinkat(nt_at_entry ent, path_type const &path) +{ + return ::fast_io::win32::nt::details::nt_deal_withct(ent.handle, path, false); +} #endif } // namespace fast_io diff --git a/include/fast_io_hosted/filesystem/posix_at.h b/include/fast_io_hosted/filesystem/posix_at.h index 8d4edd426..3d077620d 100644 --- a/include/fast_io_hosted/filesystem/posix_at.h +++ b/include/fast_io_hosted/filesystem/posix_at.h @@ -5,6 +5,8 @@ namespace fast_io namespace posix { +using posix_ssize_t = ::std::make_signed_t<::std::size_t>; + #ifdef __DARWIN_C_LEVEL extern int libc_faccessat(int dirfd, char const *pathname, int mode, int flags) noexcept __asm__("_faccessat"); extern int libc_renameat(int olddirfd, char const *oldpath, int newdirfd, char const *newpath) noexcept @@ -21,8 +23,7 @@ extern int libc_fstatat(int dirfd, char const *pathname, struct stat *buf, int f extern int libc_mkdirat(int dirfd, char const *pathname, mode_t mode) noexcept __asm__("_mkdirat"); extern int libc_mknodat(int dirfd, char const *pathname, mode_t mode, dev_t dev) noexcept __asm__("_mknodat"); extern int libc_unlinkat(int dirfd, char const *pathname, int flags) noexcept __asm__("_unlinkat"); -extern int libc_readlinkat(int dirfd, char const *pathname, char *buf, ::std::size_t bufsiz) noexcept - __asm__("_readlinkat"); +extern posix_ssize_t libc_readlinkat(int dirfd, char const *pathname, char *buf, ::std::size_t bufsiz) noexcept __asm__("_readlinkat"); #else extern int libc_faccessat(int dirfd, char const *pathname, int mode, int flags) noexcept __asm__("faccessat"); extern int libc_renameat(int olddirfd, char const *oldpath, int newdirfd, char const *newpath) noexcept @@ -39,8 +40,7 @@ extern int libc_fstatat(int dirfd, char const *pathname, struct stat *buf, int f extern int libc_mkdirat(int dirfd, char const *pathname, mode_t mode) noexcept __asm__("mkdirat"); extern int libc_mknodat(int dirfd, char const *pathname, mode_t mode, dev_t dev) noexcept __asm__("mknodat"); extern int libc_unlinkat(int dirfd, char const *pathname, int flags) noexcept __asm__("unlinkat"); -extern int libc_readlinkat(int dirfd, char const *pathname, char *buf, ::std::size_t bufsiz) noexcept - __asm__("readlinkat"); +extern posix_ssize_t libc_readlinkat(int dirfd, char const *pathname, char *buf, ::std::size_t bufsiz) noexcept __asm__("readlinkat"); #endif } // namespace posix @@ -214,7 +214,7 @@ inline void posix_fchownat_impl(int dirfd, char const *pathname, uintmax_t owner if constexpr (sizeof(uintmax_t) > sizeof(gid_t)) { constexpr ::std::uintmax_t mx{::std::numeric_limits::max()}; - if (static_cast<::std::uintmax_t>(owner) > mx) + if (static_cast<::std::uintmax_t>(group) > mx) { throw_posix_error(EOVERFLOW); } @@ -446,6 +446,100 @@ inline void posix_utimensat_impl(int dirfd, char const *path, unix_timestamp_opt (dirfd, path, tsptr, flags)); } +template<::std::integral char_type> +inline ::fast_io::details::basic_ct_string posix_readlinkat_impl(int dirfd, char const *pathname) +{ +#if defined(AT_SYMLINK_NOFOLLOW) + using posix_ssize_t = ::std::make_signed_t<::std::size_t>; + + // The standard POSIX API does not provide a direct interface to call readlink using an fd, so toctou cannot be avoided. + +#if defined(__linux__) + +#if defined(__USE_LARGEFILE64) && (defined(__NR_newfstatat) || defined(__NR_fstatat64)) + struct ::stat64 buf; +#else + struct ::stat buf; +#endif +#if defined(__NR_newfstatat) || defined(__NR_fstatat64) || defined(__NR_fstatat) + system_call_throw_error(system_call< +#if defined(__NR_newfstatat) + __NR_newfstatat +#elif defined(__NR_fstatat64) + __NR_fstatat64 +#else + __NR_fstatat +#endif + , + int>(dirfd, pathname, __builtin_addressof(buf), AT_SYMLINK_NOFOLLOW)); + +#else + if ((::fast_io::posix::libc_fstatat(dirfd, pathname, __builtin_addressof(buf), AT_SYMLINK_NOFOLLOW)) < 0) + { + throw_posix_error(); + } +#endif + +#else + struct ::stat buf; + system_call_throw_error(::fast_io::posix::libc_fstatat(dirfd, pathname, __builtin_addressof(buf),AT_SYMLINK_NOFOLLOW)); +#endif + + auto const symlink_size{static_cast<::std::size_t>(buf.st_size)}; + + if (symlink_size == 0u) + { + return {}; + } + + if constexpr (::std::same_as) + { + ::fast_io::details::basic_ct_string result(symlink_size); + + posix_ssize_t readlink_bytes{ +#if defined(__linux__) && defined(__NR_readlinkat) + system_call<__NR_readlinkat, posix_ssize_t>(dirfd, pathname, result.data(), symlink_size) +#else + static_cast(::fast_io::posix::libc_readlinkat(dirfd, pathname, result.data(), symlink_size)) +#endif + }; + + system_call_throw_error(readlink_bytes); + + if(static_cast<::std::size_t>(readlink_bytes) != symlink_size) + { + throw_posix_error(EIO); + } + + return result; + } + else + { + local_operator_new_array_ptr dynamic_buffer{symlink_size}; + + posix_ssize_t readlink_bytes{ +#if defined(__linux__) && defined(__NR_readlinkat) + system_call<__NR_readlinkat, posix_ssize_t>(dirfd, pathname, dynamic_buffer.get(), symlink_size) +#else + static_cast(::fast_io::posix::libc_readlinkat(dirfd, pathname, dynamic_buffer.get(), symlink_size)) +#endif + }; + + system_call_throw_error(readlink_bytes); + + if(static_cast<::std::size_t>(readlink_bytes) != symlink_size) [[unlikely]] + { + throw_posix_error(EIO); + } + + return ::fast_io::details::concat_ct(::fast_io::mnp::code_cvt(::fast_io::mnp::strvw(dynamic_buffer.get(), dynamic_buffer.get() + symlink_size))); + } +#else + // Since faccessat also requires the AT_SYMLINK_NOFOLLOW flag, it can only result in an error. + throw_posix_error(EINVAL); +#endif +} + template inline auto posix1x_api_dispatcher(int dirfd, char const *path, Args... args) { @@ -483,6 +577,15 @@ inline auto posix1x_api_dispatcher(int dirfd, char const *path, Args... args) } } +template <::std::integral char_type, posix_api_ct dsp, typename... Args> +inline auto posixct_api_dispatcher(int dirfd, char const *path, Args... args) +{ + if constexpr (dsp == posix_api_ct::readlinkat) + { + return posix_readlinkat_impl(dirfd, path, args...); + } +} + template inline auto posix_deal_with22(int olddirfd, old_path_type const &oldpath, int newdirfd, new_path_type const &newpath, Args... args) @@ -513,6 +616,12 @@ inline auto posix_deal_with1x(int dirfd, path_type const &path, Args... args) return fast_io::posix_api_common(path, [&](char const *path_c_str) { return posix1x_api_dispatcher(dirfd, path_c_str, args...); }); } +template <::std::integral char_type, posix_api_ct dsp, ::fast_io::constructible_to_os_c_str path_type, typename... Args> +inline auto posix_deal_withct(int dirfd, path_type const &path, Args... args) +{ + return fast_io::posix_api_common(path, [&](char const *path_c_str) { return posixct_api_dispatcher(dirfd, path_c_str, args...); }); +} + } // namespace details template <::fast_io::constructible_to_os_c_str old_path_type, ::fast_io::constructible_to_os_c_str new_path_type> @@ -626,19 +735,7 @@ inline void native_mkdirat(posix_at_entry ent, path_type const &path, perms perm { return details::posix_deal_with1x(ent.fd, path, static_cast(perm)); } -#if 0 -template<::fast_io::constructible_to_os_c_str path_type> -inline void posix_mknodat(posix_at_entry ent,path_type const& path,perms perm,::std::uintmax_t dev) -{ - return details::posix_deal_with1x(ent.fd,path,static_cast(perm),dev); -} -template<::fast_io::constructible_to_os_c_str path_type> -inline void native_mknodat(posix_at_entry ent,path_type const& path,perms perm,::std::uintmax_t dev) -{ - return details::posix_deal_with1x(ent.fd,path,static_cast(perm),dev); -} -#endif template <::fast_io::constructible_to_os_c_str path_type> inline void posix_unlinkat(posix_at_entry ent, path_type const &path, posix_at_flags flags = {}) { @@ -684,6 +781,34 @@ inline void native_utimensat(posix_at_entry ent, path_type const &path, unix_tim details::posix_deal_with1x(ent.fd, path, creation_time, last_access_time, last_modification_time, static_cast(flags)); } + +// ct +template <::std::integral char_type, ::fast_io::constructible_to_os_c_str path_type> +inline ::fast_io::details::basic_ct_string posix_readlinkat(posix_at_entry ent, path_type const &path) +{ + return details::posix_deal_withct(ent.fd, path); +} + +template <::std::integral char_type, ::fast_io::constructible_to_os_c_str path_type> +inline ::fast_io::details::basic_ct_string native_readlinkat(posix_at_entry ent, path_type const &path) +{ + return details::posix_deal_withct(ent.fd, path); +} + +#if 0 +template<::fast_io::constructible_to_os_c_str path_type> +inline void posix_mknodat(posix_at_entry ent,path_type const& path,perms perm,::std::uintmax_t dev) +{ + return details::posix_deal_with1x(ent.fd,path,static_cast(perm),dev); +} + +template<::fast_io::constructible_to_os_c_str path_type> +inline void native_mknodat(posix_at_entry ent,path_type const& path,perms perm,::std::uintmax_t dev) +{ + return details::posix_deal_with1x(ent.fd,path,static_cast(perm),dev); +} +#endif + #if 0 template<::std::integral ch_type> struct basic_posix_readlinkat_t diff --git a/include/fast_io_hosted/filesystem/win32_9xa_at.h b/include/fast_io_hosted/filesystem/win32_9xa_at.h index 6146cc229..96d6e70b4 100644 --- a/include/fast_io_hosted/filesystem/win32_9xa_at.h +++ b/include/fast_io_hosted/filesystem/win32_9xa_at.h @@ -56,20 +56,19 @@ inline void win32_9xa_unlinkat_impl(::fast_io::win32_9xa_dir_handle const &dirhd { auto file_or_path{concat_tlc_win32_9xa_path_uncheck_whether_exist(dirhd, path_c_str, path_size)}; - switch (flags) + if ((flags & ::fast_io::win32_9xa_at_flags::removedir) == ::fast_io::win32_9xa_at_flags::removedir) { - case ::fast_io::win32_9xa_at_flags::removedir: if (!::fast_io::win32::RemoveDirectoryA(reinterpret_cast(file_or_path.c_str()))) [[unlikely]] { throw_win32_error(); } - break; - default: + } + else + { if (!::fast_io::win32::DeleteFileA(reinterpret_cast(file_or_path.c_str()))) [[unlikely]] { throw_win32_error(); } - break; } } @@ -218,7 +217,7 @@ inline constexpr auto calculate_win32_9xa_readonly_open_mode(bool write_attribut inline posix_file_status win32_9xa_fstatat_impl(::fast_io::win32_9xa_dir_handle const &dirhd, char8_t const *path_c_str, ::std::size_t path_size, win32_9xa_at_flags flags) { auto path{concat_tlc_win32_9xa_path_uncheck_whether_exist(dirhd, path_c_str, path_size)}; - auto md{calculate_win32_9xa_readonly_open_mode(false, (flags & win32_9xa_at_flags::symlink_nofollow) != win32_9xa_at_flags::symlink_nofollow)}; + auto md{calculate_win32_9xa_readonly_open_mode(false, (flags & win32_9xa_at_flags::symlink_nofollow) == win32_9xa_at_flags::symlink_nofollow)}; ::fast_io::win32_file_9xa f{::fast_io::details::win32_family_create_file_internal_impl(reinterpret_cast(path.c_str()), md)}; return ::fast_io::win32::details::win32_status_impl(f.native_handle()); @@ -228,7 +227,7 @@ inline void win32_9xa_utimensat_impl(::fast_io::win32_9xa_dir_handle const &dirh unix_timestamp_option last_access_time, unix_timestamp_option last_modification_time, win32_9xa_at_flags flags) { auto path{concat_tlc_win32_9xa_path_uncheck_whether_exist(dirhd, path_c_str, path_size)}; - auto md{calculate_win32_9xa_readonly_open_mode(true, (flags & win32_9xa_at_flags::symlink_nofollow) != win32_9xa_at_flags::symlink_nofollow)}; + auto md{calculate_win32_9xa_readonly_open_mode(true, (flags & win32_9xa_at_flags::symlink_nofollow) == win32_9xa_at_flags::symlink_nofollow)}; ::fast_io::win32_file_9xa f{::fast_io::details::win32_family_create_file_internal_impl(reinterpret_cast(path.c_str()), md)}; ::fast_io::win32::filetime ftm; @@ -356,6 +355,22 @@ inline void win32_9xa_renameat_impl(::fast_io::win32_9xa_dir_handle const &olddi } } +template <::std::integral char_type> +inline ::fast_io::details::basic_ct_string win32_9xa_readlinkat_impl(::fast_io::win32_9xa_dir_handle const &dirhd, char8_t const *path_c_str, ::std::size_t path_size) +{ + auto const path{concat_tlc_win32_9xa_path_uncheck_whether_exist(dirhd, path_c_str, path_size)}; + + auto const attr{::fast_io::win32::GetFileAttributesA(reinterpret_cast(path.c_str()))}; + if (attr == static_cast<::std::uint_least32_t>(-1)) [[unlikely]] + { + throw_win32_error(0x2); + } + + throw_win32_error(0x1126); /*ERROR_NOT_A_REPARSE_POINT*/ + + return {}; +} + template <::fast_io::details::posix_api_22 dsp, typename... Args> inline auto win32_9xa_22_api_dispatcher(::fast_io::win32_9xa_dir_handle const &olddirhd, char8_t const *oldpath_c_str, ::std::size_t oldpath_size, ::fast_io::win32_9xa_dir_handle const &newdirhd, char8_t const *newpath_c_str, ::std::size_t newpath_size, Args... args) @@ -414,6 +429,15 @@ inline auto win32_9xa_1x_api_dispatcher(::fast_io::win32_9xa_dir_handle const &d } } +template <::std::integral char_type, ::fast_io::details::posix_api_ct dsp, typename... Args> +inline auto win32_9xa_ct_api_dispatcher(::fast_io::win32_9xa_dir_handle const &dir_handle, char8_t const *path_c_str, ::std::size_t path_size, Args... args) +{ + if constexpr (dsp == ::fast_io::details::posix_api_ct::readlinkat) + { + return win32_9xa_readlinkat_impl(dir_handle, path_c_str, path_size, args...); + } +} + template <::fast_io::details::posix_api_1x dsp, typename path_type, typename... Args> inline auto win32_9xa_deal_with1x(::fast_io::win32_9xa_dir_handle const &dir_handle, path_type const &path, Args... args) { @@ -466,9 +490,31 @@ inline auto win32_9xa_deal_with22(::fast_io::win32_9xa_dir_handle const &olddirh }); } + + +template <::std::integral char_type, ::fast_io::details::posix_api_ct dsp, ::fast_io::constructible_to_os_c_str path_type, typename... Args> +inline auto win32_9xa_deal_withct(::fast_io::win32_9xa_dir_handle const &dir_handle, path_type const &path, Args... args) +{ + using char8_t_const_may_alias_ptr +#if __has_cpp_attribute(__gnu__::__may_alias__) + [[__gnu__::__may_alias__]] +#endif + = char8_t const *; + + return win32_api_common_9xa( + path, [&](char const *path_c_str, ::std::size_t path_size) { + return win32_9xa_ct_api_dispatcher( + dir_handle, + reinterpret_cast(path_c_str), + path_size, + args...); + }); +} + } // namespace win32::details // 1x + template <::fast_io::constructible_to_os_c_str path_type> inline void win32_9xa_unlinkat(::fast_io::win32_9xa_at_entry const &ent, path_type &&path, win32_9xa_at_flags flags = {}) { @@ -513,12 +559,16 @@ inline void win32_9xa_symlinkat(old_path_type &&oldpath, win32_9xa_at_entry cons ::fast_io::win32::details::win32_9xa_deal_with12(oldpath, newdirfd.handle, newpath); } +// 12 + template <::fast_io::constructible_to_os_c_str old_path_type, ::fast_io::constructible_to_os_c_str new_path_type> inline void win32_9xa_linkat(win32_9xa_at_entry const &oldent, old_path_type &&oldpath, win32_9xa_at_entry const &newent, new_path_type &&newpath, [[maybe_unused]] win32_9xa_at_flags flags = win32_9xa_at_flags::symlink_nofollow) { ::fast_io::win32::details::win32_9xa_deal_with22<::fast_io::details::posix_api_22::linkat>(oldent.handle, oldpath, newent.handle, newpath); } +// 22 + template <::fast_io::constructible_to_os_c_str old_path_type, ::fast_io::constructible_to_os_c_str new_path_type> inline void win32_9xa_renameat(win32_9xa_at_entry const &oldent, old_path_type &&oldpath, win32_9xa_at_entry const &newent, new_path_type &&newpath) { @@ -531,6 +581,14 @@ inline void win32_9xa_fchmodat(::fast_io::win32_9xa_at_entry const &ent, path_ty ::fast_io::win32::details::win32_9xa_deal_with1x(ent.handle, path, mode); } +// ct + +template <::std::integral char_type, ::fast_io::constructible_to_os_c_str path_type> +inline ::fast_io::details::basic_ct_string win32_9xa_readlinkat(::fast_io::win32_9xa_at_entry const &ent, path_type const &path) +{ + return ::fast_io::win32::details::win32_9xa_deal_withct(ent.handle, path); +} + #if defined(_WIN32_WINDOWS) && !defined(__CYGWIN__) && !defined(__WINE__) using native_at_flags = win32_9xa_at_flags; @@ -600,6 +658,14 @@ inline void native_renameat(win32_9xa_at_entry const &oldent, old_path_type &&ol ::fast_io::win32::details::win32_9xa_deal_with22<::fast_io::details::posix_api_22::renameat>(oldent.handle, oldpath, newent.handle, newpath); } +// ct + +template <::std::integral char_type, ::fast_io::constructible_to_os_c_str path_type> +inline ::fast_io::details::basic_ct_string native_readlinkat(::fast_io::win32_9xa_at_entry const &ent, path_type const &path) +{ + return ::fast_io::win32::details::win32_9xa_deal_withct(ent.handle, path); +} + #endif } // namespace fast_io diff --git a/include/fast_io_hosted/platforms/nt.h b/include/fast_io_hosted/platforms/nt.h index bb62a2624..b8faf8220 100644 --- a/include/fast_io_hosted/platforms/nt.h +++ b/include/fast_io_hosted/platforms/nt.h @@ -96,11 +96,11 @@ inline constexpr nt_open_mode calculate_nt_open_mode(open_mode_perms ompm) noexc } if ((value & open_mode::no_shared_write) == open_mode::none) { - mode.ShareAccess |= 2; // FILE_SHARE_DELETE + mode.ShareAccess |= 2; // FILE_SHARE_WRITE } if ((value & open_mode::shared_delete) != open_mode::none) { - mode.ShareAccess |= 4; // FILE_SHARE_WRITE + mode.ShareAccess |= 4; // FILE_SHARE_DELETE } bool generic_write{}; if ((value & open_mode::app) != open_mode::none) @@ -282,14 +282,17 @@ inline constexpr nt_open_mode calculate_nt_open_mode(open_mode_perms ompm) noexc mode.DesiredAccess |= default_write_attribute | default_read_attribute; // GENERIC_READ | GENERIC_WRITE } } + if ((value & open_mode::no_block) == open_mode::none) { mode.CreateOptions |= 0x00000020; // FILE_SYNCHRONOUS_IO_NONALERT 0x00000020 } +#if 0 else { mode.CreateOptions |= 0x00000010; // FILE_SYNCHRONOUS_IO_ALERT 0x00000010 } +#endif if ((value & open_mode::random_access) == open_mode::none) { diff --git a/include/fast_io_hosted/platforms/win32.h b/include/fast_io_hosted/platforms/win32.h index f10940d09..5a00c15d4 100644 --- a/include/fast_io_hosted/platforms/win32.h +++ b/include/fast_io_hosted/platforms/win32.h @@ -1100,7 +1100,7 @@ inline win32_9xa_dir_handle basic_win32_9xa_create_dir_file_at_fs_dirent_impl(wi if (::fast_io::details::is_invalid_dos_filename_with_size(beg, filename_c_str_len)) [[unlikely]] { - throw_win32_error(3221225530); + throw_win32_error(3); } check_win32_9xa_dir_is_valid(*directory_handle); @@ -1123,7 +1123,7 @@ inline void *basic_win32_9xa_create_file_at_fs_dirent_impl(win32_9xa_dir_handle if (::fast_io::details::is_invalid_dos_filename_with_size(beg, filename_c_str_len)) [[unlikely]] { - throw_win32_error(3221225530); + throw_win32_error(3); } check_win32_9xa_dir_is_valid(*directory_handle); @@ -1132,13 +1132,13 @@ inline void *basic_win32_9xa_create_file_at_fs_dirent_impl(win32_9xa_dir_handle return handle; } -inline ::fast_io::win32::details::tlc_win32_9xa_dir_handle_path_str concat_tlc_win32_9xa_path_uncheck_whether_exist(::fast_io::win32_9xa_dir_handle const &dirhd, char8_t const *path_c_str, ::std::size_t path_size) noexcept +inline ::fast_io::win32::details::tlc_win32_9xa_dir_handle_path_str concat_tlc_win32_9xa_path_uncheck_whether_exist(::fast_io::win32_9xa_dir_handle const &dirhd, char8_t const *path_c_str, ::std::size_t path_size) { auto const beg{path_c_str}; if (::fast_io::details::is_invalid_dos_filename_with_size(beg, path_size)) [[unlikely]] { - throw_win32_error(3221225530); + throw_win32_error(3); } return ::fast_io::win32::details::concat_tlc_win32_9xa_dir_handle_path_str(dirhd.path, u8"\\", ::fast_io::mnp::os_c_str_with_known_size(beg, path_size)); diff --git a/include/fast_io_hosted/process/ipc/win32/named_pipe_win32.h b/include/fast_io_hosted/process/ipc/win32/named_pipe_win32.h index 1acd8aa09..606c2dbf4 100644 --- a/include/fast_io_hosted/process/ipc/win32/named_pipe_win32.h +++ b/include/fast_io_hosted/process/ipc/win32/named_pipe_win32.h @@ -100,7 +100,7 @@ inline void *win32_family_create_named_pipe_ipc_server_impl(win32_named_pipe_cha } else if constexpr (family == win32_family::ansi_9x) { - throw_win32_error(3221225530); + throw_win32_error(3); } } is_new_dir = true; @@ -121,7 +121,7 @@ inline void *win32_family_create_named_pipe_ipc_server_impl(win32_named_pipe_cha } else if constexpr (family == win32_family::ansi_9x) { - throw_win32_error(3221225530); + throw_win32_error(3); } } } @@ -136,7 +136,7 @@ inline void *win32_family_create_named_pipe_ipc_server_impl(win32_named_pipe_cha } else if constexpr (family == win32_family::ansi_9x) { - throw_win32_error(3221225530); + throw_win32_error(3); } } #endif @@ -295,7 +295,7 @@ inline void *win32_family_ipc_named_pipe_client_connect_impl(win32_named_pipe_ch } else if constexpr (family == win32_family::ansi_9x) { - throw_win32_error(3221225530); + throw_win32_error(3); } } is_new_dir = true; @@ -316,7 +316,7 @@ inline void *win32_family_ipc_named_pipe_client_connect_impl(win32_named_pipe_ch } else if constexpr (family == win32_family::ansi_9x) { - throw_win32_error(3221225530); + throw_win32_error(3); } } } @@ -331,7 +331,7 @@ inline void *win32_family_ipc_named_pipe_client_connect_impl(win32_named_pipe_ch } else if constexpr (family == win32_family::ansi_9x) { - throw_win32_error(3221225530); + throw_win32_error(3); } } #endif diff --git a/include/fast_io_hosted/process/process/win32.h b/include/fast_io_hosted/process/process/win32.h index 42a6b18ad..09cc3a14e 100644 --- a/include/fast_io_hosted/process/process/win32.h +++ b/include/fast_io_hosted/process/process/win32.h @@ -570,7 +570,7 @@ struct win32_9xa_win9x_create_process_at_fs_dirent auto const beg{reinterpret_cast(filename)}; if (::fast_io::details::is_invalid_dos_filename_with_size(beg, filename_c_str_len)) [[unlikely]] { - throw_win32_error(3221225530); + throw_win32_error(3); } // check path handle