Auflisten von Dateien und Verzeichnissen
Wenn ein Anbieter zum ersten Mal einen Virtualisierungsstamm erstellt, ist er auf dem lokalen System leer. Das heißt, keines der Elemente im Sicherungsdatenspeicher wurde noch auf dem Datenträger zwischengespeichert. Während jedes Element geöffnet wird, wird es zwischengespeichert, aber bis ein Element geöffnet wird, ist es nur im Sicherungsdatenspeicher vorhanden.
Da ungeöffnete Elemente nicht lokal vorhanden sind, würde die normale Verzeichnisaufzählung ihre Existenz für den Benutzer nicht offenbaren. Daher muss der Anbieter an der Verzeichnisaufzählung teilnehmen, indem er Informationen zu Elementen im Sicherungsdatenspeicher an ProjFS zurückgibt. ProjFS führt die Informationen des Anbieters mit den Informationen im lokalen Dateisystem zusammen, um dem Benutzer eine einheitliche Liste der Inhalte eines Verzeichnisses zu präsentieren.
Verzeichnisaufzählungsrückrufe
Um an der Verzeichnisaufzählung teilzunehmen, muss der Anbieter die Rückrufe PRJ_START_DIRECTORY_ENUMERATION_CB, PRJ_GET_DIRECTORY_ENUMERATION_CB und PRJ_END_DIRECTORY_ENUMERATION_CB implementieren. Wenn ein Verzeichnis unter dem Virtualisierungsstamm des Anbieters aufgelistet wird, führt ProjFS die folgenden Aktionen aus:
ProjFS ruft den PRJ_START_DIRECTORY_ENUMERATION_CB Rückruf des Anbieters auf, um eine Enumerationssitzung zu starten.
Im Rahmen der Verarbeitung dieses Rückrufs sollte sich der Anbieter auf die Durchführung der Enumeration vorbereiten. Beispielsweise sollte der Speicher zugeordnet werden, den es möglicherweise benötigt, um den Fortschritt der Enumeration in seinem Sicherungsdatenspeicher nachzuverfolgen.
ProjFS identifiziert das Verzeichnis, das im FilePathName-Element des Rückrufdata-Parameters aufgezählt wird. Dies wird als Pfad relativ zum Virtualisierungsstamm angegeben. Wenn sich beispielsweise der Virtualisierungsstamm unter
C:\virtRoot
befindet und das Verzeichnis, das aufgezählt wird, istC:\virtRoot\dir1\dir2
, enthältdir1\dir2
das FilePathName-Element . Der Anbieter bereitet sich darauf vor, diesen Pfad in seinem Sicherungsdatenspeicher aufzulisten. Abhängig von den Funktionen des Sicherungsspeichers eines Anbieters kann es sinnvoll sein, die Backing Store-Enumeration im PRJ_START_DIRECTORY_ENUMERATION_CB Rückruf auszuführen.Da mehrere Enumerationen parallel im selben Verzeichnis auftreten können, verfügt jeder Enumerationsrückruf über ein enumerationId-Argument , damit der Anbieter die Rückrufaufrufe einer einzelnen Enumerationssitzung zuordnen kann.
Wenn der Anbieter S_OK aus dem PRJ_START_DIRECTORY_ENUMERATION_CB Rückruf zurückgibt, muss er alle Informationen zur Enumerationssitzung beibehalten, bis ProjFS den PRJ_END_DIRECTORY_ENUMERATION_CB Rückruf mit demselben Wert für enumerationId aufruft. Wenn der Anbieter einen Fehler von PRJ_START_DIRECTORY_ENUMERATION_CB zurückgibt, ruft ProjFS den PRJ_END_DIRECTORY_ENUMERATION_CB Rückruf für diese EnumerationId nicht auf.
ProjFS ruft die PRJ_GET_DIRECTORY_ENUMERATION_CB des Anbieters mehrmals auf, um den Verzeichnisinhalt vom Anbieter abzurufen.
Als Reaktion auf diesen Rückruf gibt der Anbieter eine sortierte Liste von Elementen aus seinem Sicherungsdatenspeicher zurück.
Der Rückruf kann einen Suchausdruck in seinem searchExpression-Parameter bereitstellen, den der Anbieter zum Festlegen des Bereichs der Ergebnisse der Enumeration verwendet. Wenn searchExpression NULL ist, gibt der Anbieter alle Einträge im Verzeichnis zurück, das aufgelistet wird. Wenn es nicht NULL ist, kann der Anbieter PrjDoesNameContainWildCards verwenden, um zu bestimmen, ob im Ausdruck Wildcards vorhanden sind. Falls vorhanden, verwendet der Anbieter PrjFileNameMatch , um zu bestimmen, ob ein Eintrag im Verzeichnis mit dem Suchausdruck übereinstimmt.
Der Anbieter sollte den Wert von searchExpression beim ersten Aufruf dieses Rückrufs erfassen. Der erfasste Wert wird bei jedem nachfolgenden Aufruf des Rückrufs in derselben Enumerationssitzung verwendet, es sei denn, PRJ_CB_DATA_FLAG_ENUM_RESTART_SCAN wird im Feld Flags des callbackData-Parameters festgelegt. In diesem Fall sollte der Wert von searchExpression erneut abgerufen werden.
Wenn in seinem Sicherungsspeicher keine übereinstimmenden Einträge vorhanden sind, gibt der Anbieter einfach S_OK aus dem Rückruf zurück. Andernfalls formatiert der Anbieter jeden übereinstimmenden Eintrag aus seinem Sicherungsspeicher in eine PRJ_FILE_BASIC_INFO-Struktur und verwendet dann PrjFillDirEntryBuffer , um den Puffer aufzufüllen, der vom dirEntryBufferHandle-Parameter des Rückrufs bereitgestellt wird. Der Anbieter fügt Einträge so lange hinzu, bis er sie alle hinzugefügt hat oder bis PrjFillDirEntryBuffer HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) zurückgibt. Anschließend gibt der Anbieter S_OK aus dem Rückruf zurück. Beachten Sie, dass der Anbieter die Einträge dem puffer dirEntryBufferHandle in der richtigen Sortierreihenfolge hinzufügen muss. Der Anbieter sollte PrjFileNameCompare verwenden, um die richtige Sortierreihenfolge zu bestimmen.
Der Anbieter muss einen Wert für das IsDirectory-Element von PRJ_FILE_BASIC_INFO angeben. Wenn IsDirectory auf FALSE festgelegt ist, muss der Anbieter auch einen Wert für das FileSize-Element angeben.
Wenn der Anbieter keine Werte für die Member CreationTime, LastAccessTime, LastWriteTime und ChangeTime angibt, verwendet ProjFS die aktuelle Systemzeit.
ProjFS verwendet alle FILE_ATTRIBUTE Flags, die der Anbieter im FileAttributes-Member festlegt, mit Ausnahme von FILE_ATTRIBUTE_DIRECTORY. Er legt den richtigen Wert für FILE_ATTRIBUTE_DIRECTORY im FileAttributes-Member gemäß dem angegebenen Wert des IsDirectory-Elements fest.
Wenn PrjFillDirEntryBuffer HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) zurückgibt, bevor dem Anbieter keine Einträge mehr hinzugefügt werden, muss der Anbieter den Eintrag nachverfolgen, den er beim Empfang des Fehlers hinzufügen wollte. Wenn ProjFS das nächste Mal den PRJ_GET_DIRECTORY_ENUMERATION_CB Rückruf für dieselbe Enumerationssitzung aufruft, muss der Anbieter das Hinzufügen von Einträgen zum dirEntryBufferHandle-Puffer mit dem Eintrag fortsetzen, den er hinzufügen wollte.
Wenn der Sicherungsspeicher symbolische Verknüpfungen unterstützt, muss der Anbieter PrjFillDirEntryBuffer2 verwenden, um den Puffer zu füllen, der vom dirEntryBufferHandle-Parameter des Rückrufs bereitgestellt wird. PrjFillDirEntryBuffer2 unterstützt eine zusätzliche Puffereingabe, mit der der Anbieter angeben kann, dass der Enumerationseintrag ein symbolischer Link und sein Ziel ist. Andernfalls verhält es sich wie oben für PrjFillDirEntryBuffer beschrieben. Im folgenden Beispiel wird PrjFillDirEntryBuffer2 verwendet, um die Unterstützung für symbolische Links zu veranschaulichen.
Beachten Sie, dass PrjFillDirEntryBuffer2 ab Windows 10 Version 2004 unterstützt wird. Ein Anbieter sollte das Vorhandensein von PrjFillDirEntryBuffer2 für instance mithilfe von GetProcAddress testen.
typedef struct MY_ENUM_ENTRY MY_ENUM_ENTRY; typedef struct MY_ENUM_ENTRY { MY_ENUM_ENTRY* Next; PWSTR Name; BOOLEAN IsDirectory; INT64 FileSize; BOOLEAN IsSymlink; PWSTR SymlinkTarget; } MY_ENUM_ENTRY; typedef struct MY_ENUM_SESSION { GUID EnumerationId; PWSTR SearchExpression; USHORT SearchExpressionMaxLen; BOOLEAN SearchExpressionCaptured; MY_ENUM_ENTRY* EnumHead; MY_ENUM_ENTRY* LastEnumEntry; BOOLEAN EnumCompleted; } MY_ENUM_SESSION; HRESULT MyGetEnumCallback( _In_ const PRJ_CALLBACK_DATA* callbackData, _In_ const GUID* enumerationId, _In_opt_z_ PCWSTR searchExpression, _In_ PRJ_DIR_ENTRY_BUFFER_HANDLE dirEntryBufferHandle ) { // MyGetEnumSession is a routine the provider might implement to find // information about the enumeration session that it first stored // when processing its PRJ_START_DIRECTORY_ENUMERATION_CB callback. // // In this example the PRJ_START_DIRECTORY_ENUMERATION_CB callback has // already retrieved a list of the items in the backing store located at // callbackData->FilePathName, sorted them using PrjFileNameCompare() // to determine collation order, and stored the list in session->EnumHead. MY_ENUM_SESSION* session = NULL; session = MyGetEnumSession(enumerationId); if (session == NULL) { return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); } if (!session->SearchExpressionCaptured || (callbackData->Flags & PRJ_CB_DATA_FLAG_ENUM_RESTART_SCAN)) { if (searchExpression != NULL) { if (wcsncpy_s(session->SearchExpression, session->SearchExpressionMaxLen, searchExpression, wcslen(searchExpression))) { // Failed to copy the search expression; perhaps the provider // could try reallocating session->SearchExpression. } } else { if (wcsncpy_s(session->SearchExpression, session->SearchExpressionMaxLen, "*", 1)) { // Failed to copy the search expression; perhaps the provider // could try reallocating session->SearchExpression. } } session->SearchExpressionCaptured = TRUE; } MY_ENUM_ENTRY* enumHead = NULL; // We have to start the enumeration from the beginning if we aren't // continuing an existing session or if the caller is requesting a restart. if (((session->LastEnumEntry == NULL) && !session->EnumCompleted) || (callbackData->Flags & PRJ_CB_DATA_FLAG_ENUM_RESTART_SCAN)) { // Ensure that if the caller is requesting a restart we reset our // bookmark of how far we got in the enumeration. session->LastEnumEntry = NULL; // In case we're restarting ensure we don't think we're done. session->EnumCompleted = FALSE; // We need to start considering items from the beginning of the list // retrieved from the backing store. enumHead = session->EnumHead; } else { // We are resuming an existing enumeration session. Note that // session->LastEnumEntry may be NULL. That is okay; it means // we got all the entries the last time this callback was invoked. // Returning S_OK without adding any new entries to the // dirEntryBufferHandle buffer signals ProjFS that the enumeration // has returned everything it can. enumHead = session->LastEnumEntry; } if (enumHead == NULL) { // There are no items to return. Remember that we've returned everything // we can. session->EnumCompleted = TRUE; } else { MY_ENUM_ENTRY* thisEntry = enumHead; int entriesAdded = 0; while (thisEntry != NULL) { // We'll insert the entry into the return buffer if it matches // the search expression captured for this enumeration session. if (PrjFileNameMatch(thisEntry->Name, session->SearchExpression)) { PRJ_FILE_BASIC_INFO fileBasicInfo = {}; fileBasicInfo.IsDirectory = thisEntry->IsDirectory; fileBasicInfo.FileSize = thisEntry->FileSize; // If our backing store says this is really a symbolic link, // set up to tell ProjFS that it is a symlink and what its // target is. PRJ_EXTENDED_INFO extraInfo = {}; if (thisEntry->IsSymlink) { extraInfo.InfoType = PRJ_EXT_INFO_SYMLINK; extraInfo.Symlink.TargetName = thisEntry->SymlinkTarget; } // Format the entry for return to ProjFS. HRESULT fillResult = PrjFillDirEntryBuffer2(dirEntryBufferHandle, thisEntry->Name, &fileBasicInfo, thisEntry->IsSymlink ? &extraInfo : NULL); if (fillResult == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) { if (entriesAdded > 0) { // We couldn't add this entry to the buffer; remember where we left // off for the next time we're called for this enumeration session. session->LastEnumEntry = thisEntry; return S_OK; } else { // The buffer should always have enough space for at least one entry, but // we could not add even one entry. Return the error. return fillResult; } } else { // Any other error is unexpected. NOTE: Be sure to check for E_INVALIDARG to // ensure you are calling PrjFillDirEntryBuffer2 correctly. return fillResult; } ++entriesAdded; } thisEntry = thisEntry->Next; } // We reached the end of the list of entries; remember that we've returned // everything we can. session->EnumCompleted = TRUE; } return S_OK; }
ProjFS ruft den PRJ_END_DIRECTORY_ENUMERATION_CB Rückruf des Anbieters auf, um die Enumerationssitzung zu beenden.
Der Anbieter sollte alle Bereinigungen durchführen, die er durchführen muss, um die Enumerationssitzung zu beenden, z. B. aufheben der Zuordnung von Arbeitsspeicher, der als Teil der Verarbeitung PRJ_START_DIRECTORY_ENUMERATION_CB.