#winapi #symlink #deviceiocontrol
#winapi #символическая ссылка #deviceiocontrol
Вопрос:
Цель: создать символическую ссылку с помощью DeviceIoControl с помощью FSCTL_SET_REPARSE_POINT
Я знаю, что есть вызов WinAPI CreateSymbolicLink, он работает, но я не могу использовать его для своих целей.
Проблема: я продолжаю получать 0x80071128 (данные, присутствующие в буфере точки повторной обработки, недопустимы), когда я вызываю DeviceIoControl(hNewSymLink,FSCTL_SET_REPARSE_POINT …
диск ‘c:’ — NTFS, ОС — Windows 10, я выполняю отладку от имени администратора.
Первоначально я попытался собрать REPARSE_DATA_BUFFER, согласно https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/b41f1cbf-10df-4a47-98d4-1c52a833d913 к вышеуказанному эффекту.
В test3() я прочитал структуру good symlink и пытаюсь использовать ее для создания новой, но структура снова отклоняется с той же ошибкой.
void test3()
{
using (SafeFileHandle hSymLink = NativeMethods.CreateFile(
@"c:Templink2",
FileAccess.Read,
FileShare.Read,
IntPtr.Zero,
FileMode.Open,
(FileAttributes)(NativeMethods.EFileAttributes.FILE_FLAG_OPEN_REPARSE_POINT),
IntPtr.Zero))
{
var reparseDataSize = Marshal.SizeOf(typeof(NativeMethods.REPARSE_DATA_BUFFER));
var reparseData = Marshal.AllocHGlobal(reparseDataSize);
try
{
int bytesReturned = 0;
var result = NativeMethods.DeviceIoControl(hSymLink, NativeMethods.DeviceIOControlCode.FSCTL_GET_REPARSE_POINT,
IntPtr.Zero, 0,
reparseData, reparseDataSize,
ref bytesReturned, IntPtr.Zero);
if (!result)
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
var reparseDataBuffer = (NativeMethods.REPARSE_DATA_BUFFER)
Marshal.PtrToStructure(reparseData, typeof(NativeMethods.REPARSE_DATA_BUFFER));
var printNameDir = Encoding.Unicode.GetString(reparseDataBuffer.PathBuffer,
reparseDataBuffer.PrintNameOffset, reparseDataBuffer.PrintNameLength);
//value: "c:\temp\target.txt"
var substituteNameDir = Encoding.Unicode.GetString(reparseDataBuffer.PathBuffer,
reparseDataBuffer.SubstituteNameOffset, reparseDataBuffer.SubstituteNameLength);
//value: "\??\c:\temp\target.txt"
using (SafeFileHandle hNewSymLink = NativeMethods.CreateFile(
@"c:TemplinkNew",
FileAccess.Write,
FileShare.Read,
IntPtr.Zero,
FileMode.Create,
(FileAttributes)(NativeMethods.EFileAttributes.FILE_FLAG_OPEN_REPARSE_POINT),
IntPtr.Zero))
{
var result2 = NativeMethods.DeviceIoControl(hNewSymLink, NativeMethods.DeviceIOControlCode.FSCTL_SET_REPARSE_POINT,
reparseData, reparseDataSize, IntPtr.Zero, 0, IntPtr.Zero, IntPtr.Zero);
if (!result2)
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
//error 0x80071128: wrong reparse buffer
}
}
finally
{
Marshal.FreeHGlobal(reparseData);
}
}
}
Когда я проверяю структуру, она выглядит нормально.
В документах указано, что для get и set используется один и тот же REPARSE_DATA_BUFFER, поэтому я ожидаю, что смогу использовать правильно сформированную структуру, полученную из существующей символьной ссылки (созданной с помощью системной команды MKLINK), для создания другой.
Очевидно, я что-то упускаю — любые подсказки будут высоко оценены.
Комментарии:
1.
var reparseDataSize = Marshal.SizeOf(typeof(NativeMethods.REPARSE_DATA_BUFFER));
конечно, ошибка — здесь должен быть реальный размер данных, который больше2.
PrintNameLength
,SubstituteNameLength
— не инициализировано.. я не могу понять код c # в деталях, но код obvivois неверен.ReparseTag
не установлено,ReparseDataLength
не установлено..3. @RbMm структура повторной обработки считывается из существующей символьной ссылки в ReparseData, reparseDataSize, сбоку есть некоторая распаковка, просто чтобы посмотреть, что внутри, но тот же самый нетронутый буфер передается обратно при создании новой символьной ссылки
4. конечно, ваш код неверен. вы неправильно инициализируете буфер
Ответ №1:
Нужно передать точный размер структуры, а не размер всего выделенного буфера. Спасибо @RbMm
Вызов должен выглядеть так
var result2 = NativeMethods.DeviceIoControl(hNewSymLink,
NativeMethods.DeviceIOControlCode.FSCTL_SET_REPARSE_POINT,
reparseData, bytesReturned, IntPtr.Zero, 0, IntPtr.Zero, IntPtr.Zero);
Ответ №2:
исправьте дисковод с помощью команды-
- Откройте Windows PowerShell от имени администратора
- Запустите «chkdsk / f»
- Диск будет заблокирован, запланируйте его для следующего перезапуска на следующем шаге