Не удается получить информацию о точке повторной обработки для папки OneDrive

Я использую приведенный ниже код для получения информации о точке повторной обработки в своем приложении. Это отлично работает для символических ссылок и соединений, но не работает с «Не точкой повторной обработки» для папки 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

Было бы здорово определить, как заставить этот код работать. Очень расстраивает то, что папки, которые подтверждены как имеющие точки повторной обработки, получают сообщение об ошибке, что это не так.

Я также пробовал это на С++, но получаю ту же ошибку.


person Randall Deetz    schedule 03.12.2019    source источник
comment
В наши дни OneDrive не является точкой повторной обработки.   -  person Jonathan Potter    schedule 03.12.2019
comment
@ Джонатан Поттер, если это не так, то что это? У него установлен атрибут повторной обработки, и для OneDrive зарезервирован целый ряд тегов повторной обработки.   -  person Randall Deetz    schedule 03.12.2019
comment
На моей машине не установлен атрибут повторной обработки (Windows 10 1909). Какая версия Windows у вас установлена?   -  person Jonathan Potter    schedule 03.12.2019
comment
Если у вас отключен параметр «Файлы по запросу» для OneDrive, точки повторной обработки удаляются.   -  person Randall Deetz    schedule 03.12.2019
comment
У меня это включено.   -  person Jonathan Potter    schedule 04.12.2019


Ответы (1)


Протестировано с некоторыми соответствующими 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" вывод всегда следующий:

введите здесь описание изображения

Информации в принципе нет. Кроме того, отключение «Файлы по запросу» удалит точки повторной обработки.

РЕДАКТИРОВАТЬ:

В отличие от GetFileAttributes, FindFirstFile может получить этот атрибут FILE_ATTRIBUTE_REPARSE_POINT. Согласно документу Теги точки повторной обработки:

Чтобы получить тег точки повторной обработки, используйте функцию 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%. Приложения, которые правильно понимают точки повторной обработки, могут заставить платформу предоставлять точки повторной обработки API облачных файлов с помощью RtlSetProcessPlaceholderCompatibilityMode или RtlSetThreadProcessPlaceholderCompatibilityMode.

person Drake Wu    schedule 04.12.2019
comment
Большое спасибо за Ваш ответ. Это очень важно для моего проекта. Это сбивает с толку, поскольку ваш код показывает, что в папке OneDrive нет атрибута точки повторной обработки. Когда я делаю dir /aL в родительской папке OneDrive, OneDrive отображается в списке. Согласно вашему снимку экрана, папка OneDrive имеет тег повторной обработки 0x9000701a (IO_REPARSE_TAG_CLOUD_7). Я предполагаю, что мой вопрос заключается в том, как мне прочитать теги повторной обработки для OneDrive и записать их в резервную копию? Если FSCTL_GET_REPARSE_POINT и FSCTL_SET_REPARSE_POINT не работают, какие команды мне нужно использовать? - person Randall Deetz; 05.12.2019
comment
Чтобы получить тег точки повторной обработки, используйте функцию FindFirstFile. Если элемент dwFileAttributes включает атрибут FILE_ATTRIBUTE_REPARSE_POINT, то элемент dwReserved0 указывает точку повторной обработки. - person Drake Wu; 05.12.2019
comment
Я попробовал это, и это не удалось, потому что в папке OneDrive не установлен FILE_ATTRIBUTE_REPARSE_POINT. Мне нужно иметь возможность прочитать тег повторной обработки, а также все метаданные, как показано на снимке экрана fsutil для папки OneDrive, а затем перенести их в резервную копию. Раньше это было очень просто, когда все это можно было успешно сделать с помощью вызовов FSCTL. - person Randall Deetz; 05.12.2019
comment
Я проконсультируюсь с соответствующими инженерами и отвечу здесь, если будут какие-либо обновления. - person Drake Wu; 06.12.2019
comment
Спасибо, Дрейк. Основываясь на вашем последнем обновлении, теперь я могу видеть информацию о повторной обработке файлов и папок OneDrive. - person Randall Deetz; 22.01.2020