0

Goal: To create symbolic link using DeviceIoControl with FSCTL_SET_REPARSE_POINT

I know there is CreateSymbolicLink WinApi call, it works, but I cannot use it for my purpose.

Problem: I keep getting the 0x80071128 (The data present in the reparse point buffer is invalid) when I call DeviceIOControl(hNewSymLink,FSCTL_SET_REPARSE_POINT ...

the 'c:' drive is NTFS, OS is Windows 10, I am debugging as Admin.

Initially I tried to put together the REPARSE_DATA_BUFFER, as per https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/b41f1cbf-10df-4a47-98d4-1c52a833d913 to the above effect.

In the test3() I read the structure of good symlink and try to use it to create new one, but the structure gets rejected, again with the same error.

void test3()
{
    using (SafeFileHandle hSymLink = NativeMethods.CreateFile(
        @"c:\Temp\link2",
        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:\Temp\linkNew",
                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);
        }
    }
}

When I inspect the structure it looks fine. The docs state the same REPARSE_DATA_BUFFER is used for get and set, so I expect to be able to use well-formed structure retrieved from existing symlink (created with MKLINK system command) to create another one.

Obviously I am missing something - any hints would be much appreciated.

  • 2
    the `var reparseDataSize = Marshal.SizeOf(typeof(NativeMethods.REPARSE_DATA_BUFFER));` of course error - here must be real data size, which is more – RbMm Apr 10 '19 at 15:59
  • `PrintNameLength`, `SubstituteNameLength` - not intialized.. i can not understand c# code in details, but obvivois code is wrong. `ReparseTag` not set, `ReparseDataLength` not set.. – RbMm Apr 10 '19 at 16:01
  • @RbMm the reparse structure is being read from existing symlink into reparseData, reparseDataSize, there is some unpacking on the side, just to see what's inside, but the very same untouched buffer is passed back when creating new symlink – Michal Kupczyk Apr 11 '19 at 09:38
  • your code is wrong of course. you incorrect init buffer – RbMm Apr 11 '19 at 09:49

2 Answers2

1

fix the disk drive using the command-

  1. Open Windows PowerShell as admin
  2. Run "chkdsk /f"
  3. Drive will be lock schedule it for next restart in next step
Priyanshu
  • 73
  • 6
0

One needs to pass exact size of the structure, not the size of entire allocated buffer. Thanks @RbMm

The call should look like

var result2 = NativeMethods.DeviceIoControl(hNewSymLink, 
    NativeMethods.DeviceIOControlCode.FSCTL_SET_REPARSE_POINT,
    reparseData, bytesReturned, IntPtr.Zero, 0, IntPtr.Zero, IntPtr.Zero);