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

Ran*_*etz 4 c# winapi reparsepoint deviceiocontrol windows-10

我使用下面的代码在我的应用程序中检索重新分析点信息。这对于符号链接和连接非常有效,但对于 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);
              }
         }
    }
Run Code Online (Sandbox Code Playgroud)

本机声明:

    [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,
     }
Run Code Online (Sandbox Code Playgroud)

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

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

确定如何让这段代码工作会很棒。非常令人沮丧的是,被确认具有重新分析点的文件夹收到一条错误消息,表明它们没有。

我也在 C++ 中尝试过这个,但得到了同样的错误。

Dra*_*SFT 6

使用一些相关的API对其进行了测试。如有问题,欢迎指出。

\n\n

通过使用获得的文件属性GetFileAttributes

\n\n
FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY\n
Run Code Online (Sandbox Code Playgroud)\n\n

但没有属性:FILE_ATTRIBUTE_REPARSE_POINT. 而OneDrive中的普通文件只有属性:

\n\n
FILE_ATTRIBUTE_ARCHIVE \n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

这是测试样本\xef\xbc\x9a

\n\n
#include <windows.h>\n#include <iostream>\ntypedef struct _REPARSE_DATA_BUFFER {\n    ULONG  ReparseTag;\n    USHORT ReparseDataLength;\n    USHORT Reserved;\n    union {\n        struct {\n            USHORT SubstituteNameOffset;\n            USHORT SubstituteNameLength;\n            USHORT PrintNameOffset;\n            USHORT PrintNameLength;\n            ULONG  Flags;\n            WCHAR  PathBuffer[1];\n        } SymbolicLinkReparseBuffer;\n        struct {\n            USHORT SubstituteNameOffset;\n            USHORT SubstituteNameLength;\n            USHORT PrintNameOffset;\n            USHORT PrintNameLength;\n            WCHAR  PathBuffer[1];\n        } MountPointReparseBuffer;\n        struct {\n            UCHAR DataBuffer[1];\n        } GenericReparseBuffer;\n    } DUMMYUNIONNAME;\n} REPARSE_DATA_BUFFER, * PREPARSE_DATA_BUFFER;\n\nint main()\n{\n    DWORD attr = GetFileAttributes(TEXT("C:\\\\Users\\\\UserName\\\\OneDrive"));\n    if (attr & FILE_ATTRIBUTE_REPARSE_POINT)\n        printf("with Attributes: FILE_ATTRIBUTE_REPARSE_POINT\\n");\n    else\n        printf("without FILE_ATTRIBUTE_REPARSE_POINT, Attributes = %x\\n",attr);\n    HANDLE hFile = CreateFile(\n        TEXT("C:\\\\Users\\\\UserName\\\\OneDrive"),\n        0,\n        FILE_SHARE_READ,\n        NULL,\n        OPEN_EXISTING,\n        FILE_FLAG_BACKUP_SEMANTICS| FILE_FLAG_OPEN_REPARSE_POINT,\n        NULL\n        );\n    if (hFile != INVALID_HANDLE_VALUE)\n    {\n        REPARSE_DATA_BUFFER *rdb = (REPARSE_DATA_BUFFER*)malloc(sizeof(REPARSE_DATA_BUFFER)+ sizeof(ULONG)+sizeof(USHORT)+0xffff);\n        if (rdb)\n        {\n            DWORD outBufferSize = sizeof(REPARSE_DATA_BUFFER) + sizeof(ULONG) + sizeof(USHORT) + 0xffff;\n            DWORD bytesRet = 0;\n            if (DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, 0, rdb, outBufferSize, &bytesRet, NULL))\n                wprintf(L"DeviceIoControl succeed! printfname = %s\\n", rdb->MountPointReparseBuffer.PathBuffer[rdb->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR)]);\n            else\n                wprintf(L"error code = %d\\n", GetLastError());\n            free(rdb);\n            rdb = NULL;\n        }\n        else\n            printf("malloc failed\\n");\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

在此输入图像描述

\n\n

基本上没有可用的信息。此外,关闭“Files On Demand”将删除重分析点。

\n\n

编辑:

\n\n

与 不同的是GetFileAttributesFindFirstFile可以获得该FILE_ATTRIBUTE_REPARSE_POINT属性。根据文档重新分析点标签

\n\n
\n

要检索重分析点标记,请使用该FindFirstFile函数。\n 如果dwFileAttributes成员包含\n FILE_ATTRIBUTE_REPARSE_POINT属性,则dwReserved0\n 成员指定重分析点。

\n
\n\n

更新:

\n\n

与相关工程师确认后,由于云文件导致的此问题将隐藏重新解析信息,下面是相同的文档\n要解决此问题,有两种方法。

\n\n
    \n
  1. 将执行文件放在%systemroot%下
  2. \n
  3. 在调用重新解析相关活动之前调用以下代码行
  4. \n
\n\n

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

\n\n
typedef NTSYSAPI CHAR(*PGNSI)(CHAR Mode);\n#define PHCM_EXPOSE_PLACEHOLDERS    ((CHAR)2)\nHMODULE hmod = LoadLibrary(L"ntdll.dll");\nif (hmod == NULL)\n{\n    wprintf(L"LoadLibrary failed with %u\\n", GetLastError());\n    return 0;\n}\n\nPGNSI pGNSI;\npGNSI = (PGNSI)GetProcAddress(hmod,"RtlSetProcessPlaceholderCompatibilityMode");\nif (pGNSI == NULL)\n{\n    wprintf(L"GetProcAddress failed with %u\\n", GetLastError());\n    return 0;\n}\nCHAR c = pGNSI(PHCM_EXPOSE_PLACEHOLDERS);\n
Run Code Online (Sandbox Code Playgroud)\n\n

文档:

\n\n

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

\n\n
\n

与使用重分析点的应用程序的兼容性

\n\n

云文件 API 使用重解析点实现占位符系统。关于重分析点的一个常见误解是它们与符号链接相同。这种误解偶尔会反映在应用程序实现中,因此,许多现有应用程序在遇到任何重新分析点时都会遇到错误。

\n\n

为了缓解此兼容性问题,云文件 API 始终对除同步引擎和主映像驻留在 %systemroot% 下的进程之外的所有应用程序隐藏其重分析点。正确理解重分析点的应用程序可以强制平台使用 RtlSetProcessPlaceholderCompatibilityMode RtlSetThreadProcessPlaceholderCompatibilityMode 公开云文件 API 重分析点。

\n
\n