Поделиться через


Уведомления об операциях файловой системы

Помимо обязательных обратных вызовов для обработки перечисления, возврата метаданных файла и содержимого файла, поставщик может при необходимости зарегистрировать обратный вызов PRJ_NOTIFICATION_CB в вызове PrjStartVirtualizing. Этот обратный вызов получает уведомления об операциях файловой системы, выполняемых с файлами и каталогами под корнем виртуализации экземпляра. Некоторые уведомления являются уведомлениями после операции, которые сообщают поставщику о том, что операция только что завершена. Другие уведомления — это уведомления перед операцией. Это означает, что поставщик получает уведомление перед выполнением операции. В уведомлении перед операцией поставщик может наказать вето на операцию, возвращая код ошибки из обратного вызова. Это приводит к сбою операции с возвращенным кодом ошибки.

Как указать уведомления для получения

Если поставщик предоставляет реализацию PRJ_NOTIFICATION_CB при запуске экземпляра виртуализации, он также должен указать, какие уведомления он должен получать. Уведомления, для которых поставщик может зарегистрировать, определяются в перечислении PRJ_NOTIFICATION .

Когда поставщик указывает уведомления, которые он хочет получить, он делает это с помощью массива из одной или нескольких PRJ_NOTIFICATION_MAPPING структур. Они определяют "сопоставления уведомлений". Сопоставление уведомлений — это связывание между каталогом, называемым корнем уведомлений, и набором уведомлений, выраженным в виде битовой маски, которую ProjFS должен отправлять для этого каталога и его потомков. Сопоставление уведомлений также можно установить для одного файла. Файл или каталог, указанные в сопоставлении уведомлений, не обязательно должны существовать в момент, когда поставщик вызывает PrjStartVirtualizing. Поставщик может указать любой путь, и сопоставление будет применяться к нему, если оно когда-либо будет создано.

Если поставщик указывает несколько сопоставлений уведомлений, а некоторые из них являются потомками других, сопоставления должны указываться по убыванию. Сопоставления уведомлений на более глубоких уровнях переопределяют сопоставления более высокого уровня для своих потомков.

Если поставщик не указывает набор сопоставлений уведомлений, ProjFS по умолчанию отправляет его PRJ_NOTIFY_FILE_OPENED, PRJ_NOTIFY_NEW_FILE_CREATED и PRJ_NOTIFY_FILE_OVERWRITTEN для всех файлов и каталогов под корнем виртуализации.

В следующем фрагменте кода показано, как зарегистрировать набор уведомлений при запуске экземпляра виртуализации.

PRJ_CALLBACKS callbackTable;

// Supply required callbacks.
callbackTable.StartDirectoryEnumerationCallback = MyStartEnumCallback;
callbackTable.EndDirectoryEnumerationCallback = MyEndEnumCallback;
callbackTable.GetDirectoryEnumerationCallback = MyGetEnumCallback;
callbackTable.GetPlaceholderInfoCallback = MyGetPlaceholderInfoCallback;
callbackTable.GetFileDataCallback = MyGetFileDataCallback;

// The rest of the callbacks are optional.  We want notifications, so specify
// our notification callback.
callbackTable.QueryFileNameCallback = nullptr;
callbackTable.NotificationCallback = MyNotificationCallback;
callbackTable.CancelCommandCallback = nullptr;

// Assume the provider has created a new virtualization root at C:\VirtRoot and
// initialized it with this layout:
//
//     C:\VirtRoot
//     +--- baz
//     \--- foo
//          +--- subdir1
//          \--- subdir2
//
// The provider wants these notifications:
// * Notification of new file/directory creates for all locations within the
//   virtualization root that are not subject to one of the following more
//   specific notification mappings.
// * Notification of new file/directory creates, file opens, post-renames, and
//   pre- and post-deletes for C:\VirtRoot\foo
// * No notifications for C:\VirtRoot\foo\subdir1
PRJ_STARTVIRTUALIZING_OPTIONS startOpts = {};
PRJ_NOTIFICATION_MAPPING notificationMappings[3];

// Configure default notifications - notify of new files for most of the tree.
notificationMappings[0].NotificationRoot = L"";
notificationMappings[0].NotificationBitMask = PRJ_NOTIFY_NEW_FILE_CREATED;

// Override default notifications - notify of new files, opened files, and file
// deletes for C:\VirtRoot\foo and its descendants.
notificationMappings[1].NotificationRoot = L"foo";
notificationMappings[1].NotificationBitMask = PRJ_NOTIFY_NEW_FILE_CREATED |
                                              PRJ_NOTIFY_FILE_OPENED |
                                              PRJ_NOTIFY_PRE_DELETE |
                                              PRJ_NOTIFY_FILE_HANDLE_CLOSED_FILE_DELETED |
                                              PRJ_NOTIFY_FILE_RENAMED;

// Override all notifications for C:\VirtRoot\foo\subdir1 and its descendants.
notificationMappings[2].NotificationRoot = L"foo\\subdir1";
notificationMappings[2].NotificationBitMask = PRJ_NOTIFY_SUPPRESS_NOTIFICATIONS;

startOpts.NotificationMappings = notificationMappings;
startOpts.NotificationMappingsCount = ARRAYSIZE(notificationMapping);

// Start the instance and provide our notification mappings.
HRESULT hr;
PRJ_NAMESPACE_VIRTUALIZATION_CONTEXT instanceHandle;
hr = PrjStartVirtualizing(rootName,
                          &callbackTable,
                          nullptr,
                          &startOpts,
                          &instanceHandle);
if (FAILED(hr))
{
    wprintf(L"Failed to start the virtualization instance (0x%08x)\n", hr);
    return;
}

Получение уведомлений

ProjFS отправляет уведомления об операциях файловой системы, вызывая обратный вызов PRJ_NOTIFICATION_CB поставщика. Это делается для каждой операции, для которых зарегистрирован поставщик. Если, как в приведенном выше примере, поставщик зарегистрирован для PRJ_NOTIFY_NEW_FILE_CREATED | PRJ_NOTIFY_FILE_OPENED | PRJ_NOTIFY_PRE_DELETE | PRJ_NOTIFY_FILE_HANDLE_CLOSED_FILE_DELETED | PRJ_NOTIFY_FILE_RENAMED и приложение создало новый файл в корневом каталоге уведомлений, ProjFS будет вызывать обратный вызов PRJ_NOTIFICATION_CB с именем пути к новому файлу и параметром уведомления , равным PRJ_NOTIFICATION_NEW_FILE_CREATED.

ProjFS отправляет уведомления из следующего списка перед выполнением связанной операции с файловой системой. Если поставщик возвращает код сбоя из обратного вызова PRJ_NOTIFICATION_CB , операция файловой системы завершится ошибкой и вернет код сбоя, указанный поставщиком.

  • PRJ_NOTIFICATION_PRE_DELETE — файл будет удален.
  • PRJ_NOTIFICATION_PRE_RENAME — файл будет переименован.
  • PRJ_NOTIFICATION_PRE_SET_HARDLINK — для файла будет создана жесткая ссылка.
  • PRJ_NOTIFICATION_FILE_PRE_CONVERT_TO_FULL — файл должен быть развернут из заполнителя в полный файл, что указывает на то, что его содержимое, скорее всего, будет изменено.

ProjFS отправляет уведомления из следующего списка после завершения связанной операции файловой системы.

  • PRJ_NOTIFICATION_FILE_OPENED — открыт существующий файл.
    • Несмотря на то, что это уведомление отправляется после открытия файла, поставщик может вернуть код сбоя. В ответ ProjFS отменит открытие и возвратит вызывающей ошибке.
  • PRJ_NOTIFICATION_NEW_FILE_CREATED — создан новый файл.
  • PRJ_NOTIFICATION_FILE_OVERWRITTEN — существующий файл перезаписан, например путем вызова CreateFileW с флагом CREATE_ALWAYSdwCreationDisposition .
  • PRJ_NOTIFICATION_FILE_RENAMED — файл переименован.
  • PRJ_NOTIFICATION_HARDLINK_CREATED — для файла создана жесткая ссылка .
  • PRJ_NOTIFICATION_FILE_HANDLE_CLOSED_NO_MODIFICATION — дескриптор файла закрыт, содержимое файла не было изменено и не было удалено.
  • PRJ_NOTIFICATION_FILE_HANLDE_CLOSED_FILE_MODIFIED — дескриптор файла закрыт, а содержимое файла изменено.
  • PRJ_NOTIFICATION_FILE_HANDLE_CLOSED_FILE_DELETED — дескриптор файла был закрыт, и файл был удален при закрытии дескриптора.

Дополнительные сведения о каждом уведомлении см. в документации по PRJ_NOTIFICATION.

Параметр notificationParametersPRJ_NOTIFICATION_CB обратного вызова задает дополнительные параметры для определенных уведомлений. Если поставщик получает PRJ_NOTIFICATION_FILE_OPENED, PRJ_NOTIFICATION_NEW_FILE_CREATED, PRJ_NOTIFICATION_FILE_OVERWRITTEN или PRJ_NOTIFICATION_FILE_RENAMED уведомление, поставщик может указать новый набор уведомлений для файла. Дополнительные сведения см. в документации по PRJ_NOTIFICATION_CB.

В следующем примере показаны простые примеры того, как поставщик может обрабатывать уведомления, зарегистрированные в приведенном выше фрагменте кода.

#include <windows.h>

HRESULT
MyNotificationCallback(
    _In_ const PRJ_CALLBACK_DATA* callbackData,
    _In_ BOOLEAN isDirectory,
    _In_ PRJ_NOTIFICATION notification,
    _In_opt_z_ PCWSTR destinationFileName,
    _Inout_ PRJ_NOTIFICATION_PARAMETERS* operationParameters
    )
{
    HRESULT hr = S_OK;

    switch (notification)
    {
    case PRJ_NOTIFY_NEW_FILE_CREATED:

        wprintf(L"A new %ls was created at [%ls].\n",
                isDirectory ? L"directory" : L"file",
                callbackData->FilePathName);

        // Let's stop getting notifications inside newly-created directories.
        if (isDirectory)
        {
            operationParameters->PostCreate.NotificationMask = PRJ_NOTIFY_SUPPRESS_NOTIFICATIONS;
        }
        break;

    case PRJ_NOTIFY_FILE_OPENED:

        wprintf(L"Handle opened to %ls [%ls].\n",
                isDirectory ? L"directory": L"file",
                callbackData->FilePathName);
        break;

    case PRJ_NOTIFY_PRE_DELETE:

        wprintf(L"Attempt to delete %ls [%ls]: ",
                isDirectory ? L"directory": L"file",
                callbackData->FilePathName);

        // MyAllowDelete is a routine the provider might implement to decide
        // whether to allow a given file/directory to be deleted.
        if (!MyAllowDelete(callbackData->FilePathName, isDirectory))
        {
            wprintf(L"DENIED\n");
            hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
        }
        else
        {
            wprintf(L"ALLOWED\n");
        }
        break;

    case PRJ_NOTIFY_FILE_HANDLE_CLOSED_FILE_DELETED:

        wprintf(L"The %ls [%ls] was deleted.\n",
                isDirectory ? L"directory": L"file",
                callbackData->FilePathName);
        break;

    case PRJ_NOTIFY_FILE_RENAMED:

        if (wcslen(callbackData->FilePathName) == 0)
        {
            // If callbackData->FilePathName is an empty string, then the item
            // that was renamed was originally in a location not under the
            // virtualization root.
            wprintf(L"A %ls was renamed into the virtualization root,\n",
                    isDirectory ? L"directory": L"file");
            wprintf(L"Its new location is [%ls].\n",
                    destinationFileName);
        }
        else if (wcslen(destinationFileName) == 0)
        {
            // If destinationFileName is an empty string, then the item that was
            // renamed is now in a location not under the virtualization root.
            wprintf(L"A %ls was renamed out of the virtualization root.\n",
                    isDirectory ? L"directory": L"file");
            wprintf(L"Its original location was [%ls].\n",
                    callbackData->FilePathName);
        }
        else
        {
            // If both names are specified, both the new and old names are under
            // the virtualization root (it is never the case that nether name is
            // specified).
            wprintf(L"A %ls [%ls] was renamed.\n",
                    isDirectory ? L"directory": L"file",
                    callbackData->FilePathName);
            wprintf(L"Its new name is [%ls].\n", destinationFileName);
        }
        break;

    default:
        wprintf(L"Unrecognized notification: 0x%08x");
    }

    // Note that this may be a failure code from MyAllowDelete().
    return hr;
}