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