2

我正在使用下面的代码在我的应用程序中检索重解析点信息。这适用于符号链接和交汇点,但对于 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++ 中尝试过这个,但得到了同样的错误。

4

1 回答 1

3

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

使用获取的文件属性GetFileAttributes

FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY

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

FILE_ATTRIBUTE_ARCHIVE 

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

以下是测试样例:</p>

#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",输出总是如下:

在此处输入图像描述

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

编辑:

与 不同GetFileAttributesFindFirstFile可以得到这个FILE_ATTRIBUTE_REPARSE_POINT属性。根据文档Reparse Point Tags

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

更新:

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

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

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

文档:

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

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

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

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

于 2019-12-04T08:42:59.833 回答