Condividi tramite


Gestione delle modifiche delle visualizzazioni

Quando vengono aperti file e directory nella radice di virtualizzazione, il provider crea segnaposto su disco e, man mano che i file vengono letti, i segnaposto vengono idratati con il contenuto. Questi segnaposto rappresentano lo stato dell'archivio di backup al momento della creazione. Questi elementi memorizzati nella cache, combinati con gli elementi proiettati dal provider nelle enumerazioni, costituiscono la "visualizzazione" del client dell'archivio di backup. A volte il provider potrebbe voler aggiornare la visualizzazione del client, a causa delle modifiche apportate all'archivio di backup o a causa di un'azione esplicita eseguita dall'utente per modificare la visualizzazione.

Se il provider aggiorna la visualizzazione in modo proattivo, poiché l'archivio di backup cambia, è consigliabile che le modifiche alla visualizzazione siano relativamente frequenti. Ciò è dovuto al fatto che una modifica di visualizzazione a un elemento aperto e pertanto è stato creato un segnaposto sul disco, richiede che l'elemento su disco venga aggiornato o eliminato per riflettere la modifica della visualizzazione. Gli aggiornamenti frequenti possono comportare un'attività aggiuntiva del disco che può influire negativamente sulle prestazioni del sistema.

Controllo delle versioni degli elementi

Per supportare la gestione di più viste, ProjFS fornisce lo struct PRJ_PLACEHOLDER_VERSION_INFO . Un provider usa questo struct autonomo nelle chiamate a PrjMarkDirectoryAsPlaceholder ed è incorporato nei PRJ_PLACEHOLDER_INFO e negli struct PRJ_CALLBACK_DATA . PRJ_PLACEHOLDER_VERSION_INFO. Il campo ContentID è il percorso in cui il provider archivia fino a 128 byte di informazioni sulla versione per un file o una directory. Il provider può quindi associare internamente questo valore a un valore che identifica una vista specifica.

Ad esempio, un provider che virtualizza un repository del controllo del codice sorgente potrebbe scegliere di usare un hash del contenuto di un file per fungere da ContentID e potrebbe usare timestamp per identificare le visualizzazioni del repository in vari momenti. I timestamp potrebbero essere i tempi in cui sono stati eseguiti i commit nel repository.

Usando l'esempio di un repository con indicizzazione timestamp, un flusso tipico per l'uso di ContentID di un elemento potrebbe essere:

  1. Il client stabilisce una visualizzazione specificando il timestamp in corrispondenza del quale presentare il contenuto del repository. Questa operazione verrà eseguita tramite un'interfaccia implementata dal provider, non tramite un'API ProjFS. Ad esempio, il provider può avere uno strumento da riga di comando che può essere usato per indicare al provider quale vista l'utente desidera.
  2. Quando il client crea un handle in un file o una directory virtualizzata, il provider crea un segnaposto, usando i metadati del file system dall'archivio di backup. Il set specifico di metadati utilizzato viene identificato dal valore ContentID associato al timestamp della visualizzazione desiderata. Quando il provider chiama PrjWritePlaceholderInfo per scrivere le informazioni sul segnaposto, fornisce contentID nel membro VersionInfo dell'argomento placeholderInfo . Il provider deve quindi registrare che in questa vista è stato creato un segnaposto per tale file o directory.
  3. Quando il client legge da un segnaposto, viene richiamato il callback PRJ_GET_FILE_DATA_CB del provider. Il membro VersionInfo del parametro callbackData contiene contentID il provider specificato in PrjWritePlaceholderInfo al momento della creazione del segnaposto del file. Il provider usa tale ContentID per recuperare il contenuto corretto del file dall'archivio di backup.
  4. Il client richiede una modifica di visualizzazione specificando un nuovo timestamp. Per ogni segnaposto creato nella visualizzazione precedente, se esiste una versione diversa del file nella nuova visualizzazione, il provider può sostituire il segnaposto su disco con un elemento aggiornato il cui ContentID è associato al nuovo timestamp chiamando PrjUpdateFileIfNeeded. In alternativa, il provider può eliminare il segnaposto chiamando PrjDeleteFile in modo che la nuova visualizzazione del file venga proiettata nelle enumerazioni. Se il file non esiste nella nuova visualizzazione, il provider deve eliminarlo chiamando PrjDeleteFile.

Si noti che il provider deve assicurarsi che quando il client enumera una directory, il provider fornirà il contenuto corrispondente alla visualizzazione del client. Ad esempio, il provider potrebbe usare il membro VersionInfo del parametro callbackData del PRJ_START_DIRECTORY_ENUMERATION_CB e PRJ_GET_DIRECTORY_ENUMERATION_CB callback per recuperare il contenuto della directory in tempo reale. In alternativa, potrebbe creare una struttura di directory per tale visualizzazione nell'archivio di backup come parte della definizione della vista e quindi enumerare semplicemente tale struttura.

Ogni volta che viene richiamato un callback del provider per un segnaposto o un file parziale, le informazioni sulla versione specificate dal provider specificato durante la creazione dell'elemento vengono fornite nel membro VersionInfo del parametro callbackData del callback del callback .

Aggiornamento della visualizzazione

Di seguito è riportata una funzione di esempio che illustra come un provider potrebbe aggiornare la visualizzazione locale quando l'utente specifica un nuovo timestamp. La funzione recupera un elenco dei segnaposto creati dal provider per la visualizzazione da cui l'utente è appena passato e aggiorna lo stato della cache locale in base allo stato di ogni segnaposto nella nuova visualizzazione. Per maggiore chiarezza, questa routine omette alcune complessità di gestione del file system. Nella visualizzazione precedente, ad esempio, una determinata directory potrebbe essere presente con alcuni file o directory, ma nella nuova visualizzazione che la directory (e il relativo contenuto) potrebbero non esistere più. Per evitare di riscontrare errori in tale situazione, il provider deve aggiornare lo stato del file e delle directory sotto la radice di virtualizzazione in modo approfondito.

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;
}