Can't Get Reparse Point Information for the OneDrive Folder

2

I'm using the code below to retrieve reparse point information in my application. This works great for symbolic links and junctions, but fails with 'Not a reparse point' for the OneDrive folder and all it's child items.

   using (SafeFileHandle srcHandle = NativeMethods.CreateFile(@"C:\Users\UserName\OneDrive",
                                                              0,
                                                              System.IO.FileShare.Read,
                                                              IntPtr.Zero,
                                                              System.IO.FileMode.Open,
                                                              NativeMethods.FileFlags.BackupSemantics | NativeMethods.FileFlags.OpenReparsePoint,
                                                              IntPtr.Zero))
    {
         if (!srcHandle.IsInvalid)
         {
              NativeMethods.REPARSE_DATA_BUFFER rdb = new NativeMethods.REPARSE_DATA_BUFFER();
              IntPtr pMem = Marshal.AllocHGlobal(Marshal.SizeOf(rdb) + sizeof(uint) + sizeof(ushort) + sizeof(ushort) + 0xFFFF);

              var outBufferSize = Marshal.SizeOf(typeof(NativeMethods.REPARSE_DATA_BUFFER));
              var outBuffer = Marshal.AllocHGlobal(outBufferSize);

              // Determine if it's a symbolic link or a junction point
              try
              {
                   int bytesRet = 0;
                   if (NativeMethods.DeviceIoControl(srcHandle, NativeMethods.FSCTL_GET_REPARSE_POINT, IntPtr.Zero, 0, outBuffer, outBufferSize, ref bytesRet, IntPtr.Zero) != 0)
                   {
                        rdb = (NativeMethods.REPARSE_DATA_BUFFER)Marshal.PtrToStructure(pMem, rdb.GetType());
                        ...
                   }
                   else     // Fails with ERROR_NOT_A_REPARSE_POINT** (0x1126) on OneDrive folder and all it's child items
                   {
                        log.LogError("FSCTL_GET_REPARSE_POINT error=" + Marshal.GetHRForLastWin32Error());
                   }
              }
              catch (Exception e1)
              {
                   log.LogError("FSCTL_GET_REPARSE_POINT exception error=" + e1.Message + " -> GetLastWin32Error=" + Marshal.GetLastWin32Error().ToString());
              }
              finally
              {
                   Marshal.FreeHGlobal(pMem);
              }
         }
    }

Native declarations:

    [DllImport("kernel32.dll", EntryPoint = "CreateFile", SetLastError = true, CharSet = CharSet.Unicode)]
    public static extern SafeFileHandle CreateFile(string fileName, FileAccessAPI desiredAccess, FileShare shareMode, IntPtr secAttrib, FileMode createDisp, FileFlags flags, IntPtr template);

    public const int FSCTL_GET_REPARSE_POINT = 0x000900A8;

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    public static extern unsafe int DeviceIoControl(SafeFileHandle hFile,
                                                    int control,
                                                    IntPtr inbuffer,
                                                    int bufferSize,
                                                    IntPtr outBuffer,
                                                    int outBufferSize,
                                                    ref int bytesRet,
                                                    IntPtr overlapped);

    public const uint RP_SYMBOLICLINK   = 0xA000000C;
    public const uint RP_JUNCTION       = 0xA0000003;
    public const uint RP_REPARSETAG_WCI = 0x80000018;
    public const uint RP_REPARSETAG_APP = 0x8000001b;
    public const uint RP_CLOUD          = 0x9000001A;
    public const uint RP_CLOUD_1        = 0x9000101A;
    ...

    [StructLayout(LayoutKind.Sequential)]
     public struct REPARSE_DATA_BUFFER
     {
          public uint   ReparseTag;
          public ushort ReparseDataLength;
          public ushort Reserved;
          public ushort SubstituteNameOffset;
          public ushort SubstituteNameLength;
          public ushort PrintNameOffset;
          public ushort PrintNameLength;
          [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0xFFF0)]
          public byte[] PathBuffer;
     }

     [Flags()]
     public enum FileFlags : uint
     {
          ...
          OpenReparsePoint    = 0x00200000,
          BackupSemantics     = 0x02000000,
     }

The following command can successfully retrieve reparse point info for the OneDrive folder.

fsutil reparsepoint query C:\Users\UserName\OneDrive

Would be great to determine how to get this code to work. Very frustrating that folders that are confirmed as having reparse points get an error message that they aren't.

I also have tried this in C++, but get the same error.

c#
winapi
windows-10
deviceiocontrol
reparsepoint
asked on Stack Overflow Dec 3, 2019 by Randall Deetz

1 Answer

3

Tested it with the some relevant APIs. If there is any issue, please feel free to point out.

The file attributes obtained by using GetFileAttributes:

FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY

but without attribute: FILE_ATTRIBUTE_REPARSE_POINT. And the common file in OneDrive only have attributes:

FILE_ATTRIBUTE_ARCHIVE 

Therefore, OneDrive folder and all it's child items do not have the reparse points property.

Here is the sample for testingļ¼š

#include <windows.h>
#include <iostream>
typedef struct _REPARSE_DATA_BUFFER {
    ULONG  ReparseTag;
    USHORT ReparseDataLength;
    USHORT Reserved;
    union {
        struct {
            USHORT SubstituteNameOffset;
            USHORT SubstituteNameLength;
            USHORT PrintNameOffset;
            USHORT PrintNameLength;
            ULONG  Flags;
            WCHAR  PathBuffer[1];
        } SymbolicLinkReparseBuffer;
        struct {
            USHORT SubstituteNameOffset;
            USHORT SubstituteNameLength;
            USHORT PrintNameOffset;
            USHORT PrintNameLength;
            WCHAR  PathBuffer[1];
        } MountPointReparseBuffer;
        struct {
            UCHAR DataBuffer[1];
        } GenericReparseBuffer;
    } DUMMYUNIONNAME;
} REPARSE_DATA_BUFFER, * PREPARSE_DATA_BUFFER;

int main()
{
    DWORD attr = GetFileAttributes(TEXT("C:\\Users\\UserName\\OneDrive"));
    if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
        printf("with Attributes: FILE_ATTRIBUTE_REPARSE_POINT\n");
    else
        printf("without FILE_ATTRIBUTE_REPARSE_POINT, Attributes = %x\n",attr);
    HANDLE hFile = CreateFile(
        TEXT("C:\\Users\\UserName\\OneDrive"),
        0,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_BACKUP_SEMANTICS| FILE_FLAG_OPEN_REPARSE_POINT,
        NULL
        );
    if (hFile != INVALID_HANDLE_VALUE)
    {
        REPARSE_DATA_BUFFER *rdb = (REPARSE_DATA_BUFFER*)malloc(sizeof(REPARSE_DATA_BUFFER)+ sizeof(ULONG)+sizeof(USHORT)+0xffff);
        if (rdb)
        {
            DWORD outBufferSize = sizeof(REPARSE_DATA_BUFFER) + sizeof(ULONG) + sizeof(USHORT) + 0xffff;
            DWORD bytesRet = 0;
            if (DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, 0, rdb, outBufferSize, &bytesRet, NULL))
                wprintf(L"DeviceIoControl succeed! printfname = %s\n", rdb->MountPointReparseBuffer.PathBuffer[rdb->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR)]);
            else
                wprintf(L"error code = %d\n", GetLastError());
            free(rdb);
            rdb = NULL;
        }
        else
            printf("malloc failed\n");
    }
}

And using fsutil reparsepoint query "C:\\Users\\UserName\\OneDrive", the output is always as follows:

enter image description here

There is basically no information available. In addition, turn off "Files On Demand" will remove the reparse points.

EDIT:

Different with GetFileAttributes, FindFirstFile can get this FILE_ATTRIBUTE_REPARSE_POINT attribute. According to the document Reparse Point Tags:

To retrieve the reparse point tag, use the FindFirstFile function. If the dwFileAttributes member includes the FILE_ATTRIBUTE_REPARSE_POINT attribute, then the dwReserved0 member specifies the reparse point.

UPDATE:

After confirm with relevant engineer, This issue due to cloud files will hides the reparse information below is the documentation for the same To overcome this issue they are two ways.

  1. Place the execution file under %systemroot%
  2. Call the below lines of code before calling the reparse related activity

https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-rtlsetprocessplaceholdercompatibilitymode

typedef NTSYSAPI CHAR(*PGNSI)(CHAR Mode);
#define PHCM_EXPOSE_PLACEHOLDERS    ((CHAR)2)
HMODULE hmod = LoadLibrary(L"ntdll.dll");
if (hmod == NULL)
{
    wprintf(L"LoadLibrary failed with %u\n", GetLastError());
    return 0;
}

PGNSI pGNSI;
pGNSI = (PGNSI)GetProcAddress(hmod,"RtlSetProcessPlaceholderCompatibilityMode");
if (pGNSI == NULL)
{
    wprintf(L"GetProcAddress failed with %u\n", GetLastError());
    return 0;
}
CHAR c = pGNSI(PHCM_EXPOSE_PLACEHOLDERS);

Documentation:

https://docs.microsoft.com/en-us/windows/win32/cfapi/build-a-cloud-file-sync-engine#compatibility-with-applications-that-use-reparse-points

Compatibility with applications that use reparse points

The cloud files API implements the placeholder system using reparse points. A common misconception about reparse points is that they are the same as symbolic links. This misconception is occasionally reflected in application implementations, and as a result, many existing applications hit errors when encountering any reparse point.

To mitigate this compatibility issue, the cloud files API always hides its reparse points from all applications except for sync engines and processes whose main image resides under %systemroot%. Applications that understand reparse points correctly can force the platform to expose cloud files API reparse points using RtlSetProcessPlaceholderCompatibilityMode or RtlSetThreadProcessPlaceholderCompatibilityMode.

answered on Stack Overflow Dec 4, 2019 by Drake Wu • edited Jan 14, 2020 by Drake Wu

User contributions licensed under CC BY-SA 3.0