Behandeln von Ansichtsänderungen
Wenn Dateien und Verzeichnisse unter dem Virtualisierungsstamm geöffnet werden, erstellt der Anbieter Platzhalter auf dem Datenträger, und da Dateien gelesen werden, werden die Platzhalter mit Inhalt versorgt. Diese Platzhalter stellen den Zustand des Sicherungsspeichers zum Zeitpunkt der Erstellung dar. Diese zwischengespeicherten Elemente bilden in Kombination mit den vom Anbieter in Enumerationen projizierten Elementen die "Ansicht" des Clients des Sicherungsspeichers. Von Zeit zu Zeit kann der Anbieter die Ansicht des Clients aktualisieren, sei es aufgrund von Änderungen im Sicherungsspeicher oder aufgrund einer expliziten Aktion des Benutzers, um seine Ansicht zu ändern.
Wenn der Anbieter die Ansicht proaktiv aktualisiert, da sich der Sicherungsspeicher ändert, wird empfohlen, dass Ansichtsänderungen relativ selten erfolgen. Dies liegt daran, dass für eine Ansichtsänderung an einem Element, das geöffnet wurde und daher ein Platzhalter auf dem Datenträger erstellt wurde, erforderlich ist, dass das Element auf dem Datenträger entweder aktualisiert oder gelöscht werden muss, um die Ansichtsänderung widerzuspiegeln. Häufige Updates können zu zusätzlichen Datenträgeraktivitäten führen, die sich negativ auf die Systemleistung auswirken können.
Elementversionsverwaltung
Um die Verwaltung mehrerer Ansichten zu unterstützen, stellt ProjFS die PRJ_PLACEHOLDER_VERSION_INFO Struktur bereit. Ein Anbieter verwendet diese Struktur eigenständig in Aufrufen von PrjMarkDirectoryAsPlaceholder und ist in die PRJ_PLACEHOLDER_INFO - und PRJ_CALLBACK_DATA-Strukturen eingebettet. Die PRJ_PLACEHOLDER_VERSION_INFO. Im Feld ContentID speichert der Anbieter bis zu 128 Bytes an Versionsinformationen für eine Datei oder ein Verzeichnis. Der Anbieter kann diesen Wert dann intern einem Wert zuordnen, der eine bestimmte Sicht identifiziert.
Beispielsweise kann ein Anbieter, der ein Quellcodeverwaltungsrepository virtualisiert, einen Hash des Inhalts einer Datei als ContentID verwenden und Zeitstempel verwenden, um Ansichten des Repositorys zu verschiedenen Zeitpunkten zu identifizieren. Die Zeitstempel können die Zeiten sein, zu denen Commits in das Repository ausgeführt wurden.
Anhand des Beispiels eines Zeitstempel-indizierten Repositorys kann ein typischer Flow für die Verwendung der ContentID eines Elements wie folgt aussehen:
- Der Client erstellt eine Ansicht, indem er den Zeitstempel angibt, an dem der Repositoryinhalt angezeigt werden soll. Dies würde über eine vom Anbieter implementierte Schnittstelle erfolgen, nicht über eine ProjFS-API. Beispielsweise kann der Anbieter über ein Befehlszeilentool verfügen, das verwendet werden kann, um dem Anbieter mitzuteilen, welche Ansicht der Benutzer wünscht.
- Wenn der Client ein Handle für eine virtualisierte Datei oder ein virtualisiertes Verzeichnis erstellt, erstellt der Anbieter einen Platzhalter für die Datei mithilfe von Dateisystemmetadaten aus dem Sicherungsspeicher. Der bestimmte Satz von Metadaten, die verwendet wird, wird durch den ContentID-Wert identifiziert, der dem Zeitstempel der gewünschten Ansicht zugeordnet ist. Wenn der Anbieter PrjWritePlaceholderInfo aufruft, um die Platzhalterinformationen zu schreiben, stellt er die ContentID im VersionInfo-Member des arguments placeholderInfo bereit. Der Anbieter sollte dann aufzeichnen, dass in dieser Ansicht ein Platzhalter für diese Datei oder dieses Verzeichnis erstellt wurde.
- Wenn der Client aus einem Platzhalter liest, wird der PRJ_GET_FILE_DATA_CB Rückruf des Anbieters aufgerufen. Das VersionInfo-Element des callbackData-Parameters enthält die ContentID des Anbieters, der beim Erstellen des Dateiplatzhalters in PrjWritePlaceholderInfo angegeben wurde. Der Anbieter verwendet diese ContentID, um den richtigen Inhalt der Datei aus dem Sicherungsspeicher abzurufen.
- Der Client fordert eine Ansichtsänderung an, indem er einen neuen Zeitstempel angibt. Für jeden Platzhalter, den der Anbieter in der vorherigen Ansicht erstellt hat, kann der Anbieter, wenn eine andere Version dieser Datei in der neuen Ansicht vorhanden ist, den Platzhalter auf dem Datenträger durch einen aktualisierten ersetzen, dessen ContentID dem neuen Zeitstempel zugeordnet ist, indem PrjUpdateFileIfNeeded aufgerufen wird. Alternativ kann der Anbieter den Platzhalter durch Aufrufen von PrjDeleteFile löschen, sodass die neue Ansicht der Datei in Enumerationen projiziert wird. Wenn die Datei in der neuen Ansicht nicht vorhanden ist, muss der Anbieter sie durch Aufrufen von PrjDeleteFile löschen.
Beachten Sie, dass der Anbieter sicherstellen muss, dass der Anbieter, wenn der Client ein Verzeichnis aufzählt, den Inhalt bereitstellt, der der Ansicht des Clients entspricht. Beispielsweise könnte der Anbieter das VersionInfo-Element des callbackData-Parameters des PRJ_START_DIRECTORY_ENUMERATION_CB und PRJ_GET_DIRECTORY_ENUMERATION_CB Rückrufe verwenden, um Verzeichnisinhalte direkt abzurufen. Alternativ könnte sie eine Verzeichnisstruktur für diese Ansicht in ihrem Sicherungsspeicher erstellen, um die Ansicht zu erstellen, und dann einfach diese Struktur auflisten.
Wenn ein Anbieterrückruf für einen Platzhalter oder eine Teildatei aufgerufen wird, werden die Versionsinformationen, die der Anbieter beim Erstellen des Elements angegeben hat, im VersionInfo-Member des callbackData-Parameters des Rückrufs bereitgestellt.
Aktualisieren der Ansicht
Im Folgenden finden Sie eine Beispielfunktion, die veranschaulicht, wie ein Anbieter die lokale Ansicht aktualisieren kann, wenn der Benutzer einen neuen Zeitstempel angibt. Die Funktion ruft eine Liste der Platzhalter ab, die der Anbieter für die Ansicht erstellt hat, aus der der Benutzer gerade geändert hat, und aktualisiert den status des lokalen Caches basierend auf dem Status der einzelnen Platzhalter in der neuen Ansicht. Aus Gründen der Übersichtlichkeit lassen diese Routine bestimmte Komplexitäten des Umgangs mit dem Dateisystem aus. In der alten Ansicht kann beispielsweise ein bestimmtes Verzeichnis mit einigen Dateien oder Verzeichnissen vorhanden sein, aber in der neuen Ansicht ist dieses Verzeichnis (und sein Inhalt) möglicherweise nicht mehr vorhanden. Um in einer solchen Situation zu vermeiden, dass Fehler auftreten, sollte der Anbieter den Status der Datei und der Verzeichnisse unterhalb des Virtualisierungsstamms gründlich aktualisieren.
typedef struct MY_ON_DISK_PLACEHOLDER MY_ON_DISK_PLACEHOLDER;
typedef struct MY_ON_DISK_PLACEHOLDER {
MY_ON_DISK_PLACEHOLDER* Next;
PWSTR RelativePath;
} MY_ON_DISK_PLACEHOLDER;
HRESULT
MyViewUpdater(
_In_ PRJ_CALLBACK_DATA callbackData,
_In_ time_t newViewTime
)
{
HRESULT hr = S_OK;
// MyGetOnDiskPlaceholders is a routine the provider might implement to produce
// a pointer to a list of the placeholders the provider has created in the view.
MY_ON_DISK_PLACEHOLDER* placeholder = MyGetOnDiskPlaceholders();
while (placeholder != NULL)
{
// MyGetProjectedFileInfo is a routine the provider might implement to
// determine whether a given file exists in its backing store at a
// particular timestamp. If it does, the routine returns a pointer to
// a PRJ_PLACEHOLDER_INFO struct populated with information about the
// file.
PRJ_PLACEHOLDER_INFO* newViewPlaceholderInfo = NULL;
UINT32 newViewPlaceholderInfoSize = 0;
newViewPlaceholderInfo = MyGetProjectedFileInfo(placeholder->RelativePath,
newViewTime,
&newViewPlaceholderInfoSize);
if (newViewPlaceholderInfo == NULL)
{
// The file no longer exists in the new view. We want to remove its
// placeholder from the local cache.
PRJ_UPDATE_FAILURE_CAUSES deleteFailureReason;
hr = PrjDeleteFile(callbackData->NamespaceVirtualizationContext,
placeholder->RelativePath,
PRJ_UPDATE_ALLOW_DIRTY_METADATA
| PRJ_UPDATE_ALLOW_DIRTY_DATA
| PRJ_UPDATE_ALLOW_TOMBSTONE,
&deleteFailureReason);
if (hr == HRESULT_FROM_WIN32(ERROR_FILE_SYSTEM_VIRTUALIZATION_INVALID_OPERATION))
{
wprintf(L"Could not delete [%ls].\n", placeholder->RelativePath);
wprintf(L"Failure reason: 0x%x", deleteFailureReason);
return hr;
}
else
{
wprintf(L"Error deleting [%ls]: 0x%08x",
placeholder->RelativePath,
hr);
return hr;
}
// MyRemovePlaceholderData is a routine the provider might implement
// to remove an item from its list of placeholders it has created in
// the view.
MyRemovePlaceholderData(placeholder);
}
else
{
// The file exists in the new view. Its local cache state may need
// to be updated, for example if its content is different in this view.
// As an optimization, the provider may compare the file's ContentID
// in the new view (stored in newViewPlaceholderInfo->ContentId) with
// the ContentID it had in the old view. If they are the same, calling
// PrjUpdateFileIfNeeded would not be needed.
PRJ_UPDATE_FAILURE_CAUSES updateFailureReason;
hr = PrjUpdateFileIfNeeded(callbackData->NamespaceVirtualizationContext,
placeholder->RelativePath,
newViewPlaceholderInfo,
newViewPlaceholderInfoSize,
PRJ_UPDATE_ALLOW_DIRTY_METADATA
| PRJ_UPDATE_ALLOW_DIRTY_DATA
| PRJ_UPDATE_ALLOW_TOMBSTONE,
&updateFailureReason);
if (hr == HRESULT_FROM_WIN32(ERROR_FILE_SYSTEM_VIRTUALIZATION_INVALID_OPERATION))
{
wprintf(L"Could not update [%ls].\n", placeholder->RelativePath);
wprintf(L"Failure reason: 0x%x", updateFailureReason);
return hr;
}
else
{
wprintf(L"Error updating [%ls]: 0x%08x",
placeholder->RelativePath,
hr);
return hr;
}
// MyUpdatePlaceholderData is a routine the provider might implement
// to update information about a placeholder it has created in the view.
MyUpdatePlaceholderData(placeholder, newViewPlaceholderInfo);
}
placeholder = placeholder->Next;
}
return hr;
}