`<filesystem>` support junctions in `read_symlink` (#2877)

Co-authored-by: nicole mazzuca <mazzucan@outlook.com>
Co-authored-by: Stephan T. Lavavej <stl@nuwen.net>
This commit is contained in:
nicole mazzuca 2022-07-27 18:57:18 -07:00 коммит произвёл GitHub
Родитель b4c409cc50
Коммит a0b84e0d6a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 119 добавлений и 9 удалений

Просмотреть файл

@ -3203,7 +3203,7 @@ namespace filesystem {
}
}
_NODISCARD inline __std_win_error _Read_symlink_reparse_data(
_NODISCARD inline __std_win_error _Read_reparse_data(
const _Fs_file& _Handle, unique_ptr<char[]>& _Buffer_unique_ptr) noexcept {
constexpr auto _Buffer_size = 16 * 1024 + sizeof(wchar_t); // MAXIMUM_REPARSE_DATA_BUFFER_SIZE + sizeof(wchar_t)
@ -3231,7 +3231,7 @@ namespace filesystem {
return _Err;
}
_Err = _Read_symlink_reparse_data(_Handle, _Buffer_unique_ptr);
_Err = _Read_reparse_data(_Handle, _Buffer_unique_ptr);
if (_Err != __std_win_error::_Success) {
return _Err;
}
@ -3278,7 +3278,7 @@ namespace filesystem {
return _Err;
}
_Err = _Read_symlink_reparse_data(_Handle, _Buffer_unique_ptr);
_Err = _Read_reparse_data(_Handle, _Buffer_unique_ptr);
if (_Err != __std_win_error::_Success) {
return _Err;
}
@ -3293,6 +3293,14 @@ namespace filesystem {
} // Close _Handle
const auto _Buffer = reinterpret_cast<__std_fs_reparse_data_buffer*>(_Buffer_unique_ptr.get());
// LWG-3744: `copy_symlink(junction, new_symlink)`'s behavior is unclear
// `read_symlink(junction)` should be allowed, but `copy_symlink(junction)` is not.
if (__std_fs_is_junction_from_reparse_data_buffer(_Buffer)) {
_Err = __std_win_error::_Reparse_tag_invalid;
return _Err;
}
unsigned short _Length;
wchar_t* _Offset;
_Err = __std_fs_read_name_from_reparse_data_buffer(_Buffer, &_Offset, &_Length);
@ -3311,6 +3319,56 @@ namespace filesystem {
return _Err;
}
_NODISCARD inline __std_win_error _Copy_junction(const path& _Junction, const path& _New_junction) noexcept {
__std_win_error _Err;
unique_ptr<char[]> _Buffer_unique_ptr;
{
const _Fs_file _Handle(_Junction.c_str(), __std_access_rights::_File_read_attributes,
__std_fs_file_flags::_Backup_semantics | __std_fs_file_flags::_Open_reparse_point, &_Err);
if (_Err != __std_win_error::_Success) {
return _Err;
}
_Err = _Read_reparse_data(_Handle, _Buffer_unique_ptr);
if (_Err != __std_win_error::_Success) {
return _Err;
}
} // Close _Handle
const auto _Buffer = reinterpret_cast<__std_fs_reparse_data_buffer*>(_Buffer_unique_ptr.get());
const auto _Create_dir_res = __std_fs_create_directory(_New_junction.c_str());
if (_Create_dir_res._Error != __std_win_error::_Success) {
return _Create_dir_res._Error;
} else if (!_Create_dir_res._Created) {
return __std_win_error::_Already_exists;
}
struct _NODISCARD _Delete_directory_scope_guard {
const wchar_t* _Path;
~_Delete_directory_scope_guard() {
if (_Path) {
(void) __std_fs_remove(_Path);
}
}
};
_Delete_directory_scope_guard _Delete_directory{_New_junction.c_str()};
_Fs_file _To_handle{_New_junction.c_str(), __std_access_rights::_File_write_attributes,
__std_fs_file_flags::_Backup_semantics, &_Err};
if (_Err != __std_win_error::_Success) {
return _Err;
}
_Err = __std_fs_write_reparse_data_buffer(_To_handle._Raw, _Buffer);
if (_Err == __std_win_error::_Success) {
// don't delete the directory if we succeeded in making it a junction
_Delete_directory._Path = nullptr;
}
return _Err;
}
inline void copy_symlink(const path& _Symlink, const path& _New_symlink, error_code& _Ec) {
_Ec = _Make_ec(_Copy_symlink(_Symlink, _New_symlink));
}
@ -4216,8 +4274,12 @@ namespace filesystem {
}
}
if (_STD filesystem::is_other(_Fstat._Status) || _STD filesystem::is_other(_Tstat._Status)) {
// report an error if is_other(f) || is_other(t) is true
const bool _Fstat_is_other =
_STD filesystem::is_other(_Fstat._Status) && _Fstat._Status.type() != file_type::junction;
const bool _Tstat_is_other =
_STD filesystem::is_other(_Tstat._Status) && _Tstat._Status.type() != file_type::junction;
if (_Fstat_is_other || _Tstat_is_other) {
// report an error if is_other(f) || is_other(t) is true, and it's not a junction
_Ec = _STD make_error_code(errc::operation_not_supported);
return;
}
@ -4228,14 +4290,26 @@ namespace filesystem {
return;
}
if (_Fstat._Status.type() == file_type::junction) {
if ((_Options & copy_options::skip_symlinks) != copy_options::none) {
return;
}
// _Options includes copy_options::copy_symlinks,
// since _Fstat is only allowed to be a symbolic link when either skip_symlinks or copy_symlinks
if (!_STD filesystem::exists(_Tstat._Status)) {
_Ec = _Make_ec(_Copy_junction(_From, _To));
}
}
if (_STD filesystem::is_symlink(_Fstat._Status)) {
if ((_Options & copy_options::skip_symlinks) != copy_options::none) {
return;
}
if (!_STD filesystem::exists(_Tstat._Status)
&& (_Options & copy_options::copy_symlinks) != copy_options::none) {
// if (condition) then copy_symlink(from, to)
// _Options includes copy_options::copy_symlinks,
// since _Fstat is only allowed to be a symbolic link when either skip_symlinks or copy_symlinks
if (!_STD filesystem::exists(_Tstat._Status)) {
_STD filesystem::copy_symlink(_From, _To, _Ec);
return;
}

Просмотреть файл

@ -41,6 +41,7 @@ enum class __std_win_error : unsigned long {
_Already_exists = 183, // #define ERROR_ALREADY_EXISTS 183L
_Filename_exceeds_range = 206, // #define ERROR_FILENAME_EXCED_RANGE 206L
_Directory_name_is_invalid = 267, // #define ERROR_DIRECTORY 267L
_Reparse_tag_invalid = 4393L, // #define ERROR_REPARSE_TAG_INVALID 4393L
_Max = ~0UL // sentinel not used by Win32
};
@ -309,6 +310,12 @@ _NODISCARD __std_win_error __stdcall __std_fs_create_symbolic_link(
_NODISCARD __std_win_error __stdcall __std_fs_read_reparse_data_buffer(_In_ __std_fs_file_handle _Handle,
_Out_writes_bytes_(_Buffer_size) void* _Buffer, _In_ unsigned long _Buffer_size) noexcept;
_NODISCARD __std_win_error __stdcall __std_fs_write_reparse_data_buffer(
_In_ __std_fs_file_handle _Handle, _In_ const __std_fs_reparse_data_buffer* _Buffer) noexcept;
_NODISCARD bool __stdcall __std_fs_is_junction_from_reparse_data_buffer(
_In_ const __std_fs_reparse_data_buffer* _Buffer) noexcept;
_NODISCARD _Success_(return == __std_win_error::_Success) __std_win_error
__stdcall __std_fs_read_name_from_reparse_data_buffer(
_In_ __std_fs_reparse_data_buffer* _Handle, _Out_ wchar_t** _Offset, _Out_ unsigned short* _Length) noexcept;

Просмотреть файл

@ -490,6 +490,23 @@ _Success_(return == __std_win_error::_Success) __std_win_error
return __std_win_error{GetLastError()};
}
[[nodiscard]] __std_win_error __stdcall __std_fs_write_reparse_data_buffer(
_In_ const __std_fs_file_handle _Handle, _In_ const __std_fs_reparse_data_buffer* const _Buffer) noexcept {
if (DeviceIoControl(reinterpret_cast<HANDLE>(_Handle), FSCTL_SET_REPARSE_POINT,
const_cast<__std_fs_reparse_data_buffer*>(_Buffer), sizeof(_Buffer) + _Buffer->_Reparse_data_length,
nullptr, 0, nullptr, nullptr)) {
return __std_win_error::_Success;
}
// If DeviceIoControl fails, _Bytes_returned is 0.
return __std_win_error{GetLastError()};
}
[[nodiscard]] bool __stdcall __std_fs_is_junction_from_reparse_data_buffer(
_In_ const __std_fs_reparse_data_buffer* const _Buffer) noexcept {
return _Buffer->_Reparse_tag == IO_REPARSE_TAG_MOUNT_POINT;
}
[[nodiscard]] _Success_(return == __std_win_error::_Success) __std_win_error
__stdcall __std_fs_read_name_from_reparse_data_buffer(_In_ __std_fs_reparse_data_buffer* const _Buffer,
_Out_ wchar_t** const _Offset, _Out_ unsigned short* const _Length) noexcept {
@ -504,8 +521,20 @@ _Success_(return == __std_win_error::_Success) __std_win_error
*_Length = _Temp_length;
*_Offset = &_Symlink_buffer._Path_buffer[_Symlink_buffer._Print_name_offset / sizeof(wchar_t)];
}
} else if (_Buffer->_Reparse_tag == IO_REPARSE_TAG_MOUNT_POINT) {
// junction
auto& _Junction_buffer = _Buffer->_Mount_point_reparse_buffer;
const unsigned short _Temp_length = _Junction_buffer._Print_name_length / sizeof(wchar_t);
if (_Temp_length == 0) {
*_Length = _Junction_buffer._Substitute_name_length / sizeof(wchar_t);
*_Offset = &_Junction_buffer._Path_buffer[_Junction_buffer._Substitute_name_offset / sizeof(wchar_t)];
} else {
*_Length = _Temp_length;
*_Offset = &_Junction_buffer._Path_buffer[_Junction_buffer._Print_name_offset / sizeof(wchar_t)];
}
} else {
return __std_win_error{ERROR_REPARSE_TAG_INVALID};
return __std_win_error::_Reparse_tag_invalid;
}
return __std_win_error::_Success;