无法获取OneDrive文件夹的重解析点信息

时间:2019-12-03 07:38:52

标签: c# winapi windows-10 deviceiocontrol reparsepoint

我正在使用下面的代码在我的应用程序中检索重解析点信息。这对于符号链接和结点非常有用,但对于OneDrive文件夹及其所有子项,失败并显示“不是重新解析点”。

   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);
              }
         }
    }

本地声明

    [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,
     }

以下命令可以成功检索OneDrive文件夹的重解析点信息。

fsutil重新解析点查询C:\ Users \ UserName \ OneDrive

确定如何使此代码正常工作将非常有用。令人沮丧的是,确认为具有重定位点的文件夹会收到错误消息,提示它们不是。

我也曾在C ++中尝试过此方法,但遇到相同的错误。

2 个答案:

答案 0 :(得分:0)

使用一些相关的API对其进行了测试。如有任何问题,请随时指出。

使用GetFileAttributes获得的文件属性:

FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY

,但没有属性:FILE_ATTRIBUTE_REPARSE_POINT。而且OneDrive中的通用文件仅具有以下属性:

FILE_ATTRIBUTE_ARCHIVE 

因此,OneDrive文件夹及其所有子项没有重新解析点属性。

这里是测试样品:

#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");
    }
}

使用fsutil reparsepoint query "C:\\Users\\UserName\\OneDrive",输出始终如下:

enter image description here

基本上没有可用信息。此外,关闭“按需文件”将删除重新解析点。

编辑:

GetFileAttributesFindFirstFile不同的是,可以获取此FILE_ATTRIBUTE_REPARSE_POINT属性。根据文档Reparse Point Tags

  

要检索重新解析点标记,请使用FindFirstFile函数。   如果dwFileAttributes成员包括   FILE_ATTRIBUTE_REPARSE_POINT属性,然后是dwReserved0   成员指定重新解析点。

答案 1 :(得分:0)

此答案是一种解决方法,可让OneDrive从备份映像正常工作。

备份过程需要正常复制OneDrive文件,而无需重新解析信息。 OneDrive仍会为仅在线且不在本地存储的任何数据创建存根文件。然后,需要一个命令让OneDrive从备份映像中重置自身。然后,重置过程将为每个文件夹和文件重新创建重新分析信息。为此,备份过程会在备份映像中为reset命令创建一个注册表RunOnce注册表项。

RunOnce命令类似于以下内容:

HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
"Reset" REG_SZ "C:\Users\UserName\AppData\Local\Microsoft\OneDrive\OneDrive.exe" /reset

我更愿意为其他人功劳,因为该答案可以演示对OneDrive的读写信息。这样可以在启动备用映像时删除重置。