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


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

Помимо обязательных обратных вызовов для обработки перечисления, возврата метаданных файла и содержимого файла поставщик может при необходимости зарегистрировать обратный вызов 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 с флагом dwCreationDisposition CREATE_ALWAYS.
  • 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.

Параметр notificationParameters PRJ_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;
}