共用方式為


處理檢視變更

當虛擬化根目錄下的檔案和目錄開啟時,提供者會在磁碟上建立佔位符,而且當檔案被讀取時,佔位符會被填充內容。 這些佔位元代表在建立備份存放區時的狀態。 這些快取項目會結合列舉中提供者所投射的項目,構成用戶端對底層存儲的「視圖」。 提供者不時可能會想要更新客戶端的檢視,無論是因為備份存放區中的變更,還是因為使用者採取明確的動作來變更其檢視。

如果提供者積極地更新檢視,當後端存放區變更時,建議檢視的變更應該較少發生。 這是因為已開啟的項目的視圖變更,因此已在磁碟上建立了佔位元,因此需要更新或刪除磁碟上的項目,以反映視圖變更。 頻繁的更新可能會導致額外的磁碟活動對系統效能造成負面影響。

項目版本管理

為了支持維護多個檢視,ProjFS 會提供 PRJ_PLACEHOLDER_VERSION_INFO 結構。 提供者會在呼叫 PrjMarkDirectoryAsPlaceholder中使用這個獨立結構,並內嵌在 PRJ_PLACEHOLDER_INFOPRJ_CALLBACK_DATA 結構中。 PRJ_PLACEHOLDER_VERSION_INFOContentID 欄位是提供者儲存檔案或目錄版本資訊的地方,可存儲最多 128 個位元組。 然後,提供者可以在內部將此值與識別特定檢視的一些值產生關聯。

例如,虛擬化原始檔控制存放庫的提供者可能會選擇使用檔案內容的哈希做為 ContentID,而且可能會使用時間戳來識別不同時間點的存放庫檢視。 時間戳可能是提交到資料庫的時間。

使用時間戳索引存放庫的範例,使用專案 ContentID 的一般流程可能是:

  1. 用戶端會藉由指定要呈現存放庫內容的時間戳來建立檢視。 這會透過提供者所實作的一些介面來完成,而不是透過 ProjFS API。 例如,提供者可能有命令行工具,可用來告訴提供者使用者所需的檢視。
  2. 當用戶端為虛擬化檔案或目錄建立句柄時,提供者會使用來自後備存儲的文件系統元數據為其建立佔位符。 它所使用的特定元數據集是由與所需檢視時間戳相關聯的 ContentID 值所識別。 當提供者呼叫 PrjWritePlaceholderInfo 以撰寫佔位符資訊時,它會在 placeholderInfo 自變數的 VersionInfo 成員中提供 ContentID。 然後提供者應該記錄該檔案或目錄的佔位元已在此檢視中建立。
  3. 當用戶端從佔位元讀取時,會叫用提供者的 PRJ_GET_FILE_DATA_CB 回呼。 callbackData 參數的 VersionInfo 成員包含在 PrjWritePlaceholderInfo 中建立檔案佔位符時提供者指定的 ContentID。 提供者會使用該 ContentID,從其備份存放區擷取檔案的正確內容。
  4. 用戶端藉由指定新的時間戳來要求檢視變更。 針對在上一個檢視中建立的每個佔位元,如果新檢視中存在不同版本的檔案,提供者可能會藉由呼叫 PrjUpdateFileIfNeeded,將磁碟上的佔位符取代為更新的佔位符,其 ContentID 與新的時間戳相關聯。 或者,提供者可以藉由呼叫 PrjDeleteFile 來刪除佔位符,以便將檔案的新檢視投影在列舉中。 如果檔案不存在於新檢視中,提供者必須藉由呼叫 PrjDeleteFile來刪除它。

請注意,提供者必須確保用戶端列舉目錄時,提供者會提供對應至客戶端檢視的內容。 例如,提供者可以使用 callbackData 參數中的 VersionInfo 成員,透過 PRJ_START_DIRECTORY_ENUMERATION_CBPRJ_GET_DIRECTORY_ENUMERATION_CB 回呼來即時擷取目錄內容。 或者,它可以在其備份存放區中建置該檢視的目錄結構,做為建立檢視的一部分,然後直接列舉該結構。

每當對於佔位或部分檔案的回調被觸發時,提供者在建立該項目時指定的版本資訊,將包含在回調的 callbackData 參數的 VersionInfo 成員中。

更新視圖

以下是範例函式,說明提供者如何在使用者指定新的時間戳時更新本機檢視。 函式會檢索提供者針對使用者剛剛變更的檢視所建立的佔位符清單,並根據新檢視中每個佔位符的狀態更新本機快取狀態。 為了清楚起見,此例程會省略處理文件系統的某些複雜度。 例如,在舊檢視中,指定的目錄可能已存在一些檔案或目錄,但在新的檢視中,目錄(及其內容)可能已不存在。 為了避免在這類情況下發生錯誤,提供者應該以深度優先的方式更新虛擬化根目錄下的檔案和目錄狀態。

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