Baca dalam bahasa Inggris

Bagikan melalui


Menangani Perubahan Tampilan

Saat file dan direktori di bawah akar virtualisasi dibuka, penyedia membuat tempat penampung pada disk, dan saat file dibaca tempat penampung dihidrasi dengan konten. Tempat penampung ini mewakili status penyimpanan cadangan pada saat mereka dibuat. Item yang di-cache ini, dikombinasikan dengan item yang diproyeksikan oleh penyedia dalam enumerasi, merupakan "tampilan" klien dari penyimpanan backing. Dari waktu ke waktu penyedia mungkin ingin memperbarui tampilan klien, baik karena perubahan di penyimpanan pendukung, atau karena tindakan eksplisit yang diambil oleh pengguna untuk mengubah tampilan mereka.

Jika penyedia memperbarui tampilan secara proaktif, karena penyimpanan pendukung berubah, disarankan agar perubahan tampilan relatif jarang. Ini karena perubahan tampilan pada item yang telah dibuka, dan karena itu memiliki tempat penampung yang dibuat pada disk, mengharuskan item pada disk diperbarui atau dihapus untuk mencerminkan perubahan tampilan. Pembaruan yang sering dapat mengakibatkan aktivitas disk tambahan yang dapat berdampak negatif pada performa sistem.

Penerapan Versi Item

Untuk mendukung mempertahankan beberapa tampilan, ProjFS menyediakan struct PRJ_PLACEHOLDER_VERSION_INFO . Penyedia menggunakan struktur ini mandiri dalam panggilan ke PrjMarkDirectoryAsPlaceholder, dan disematkan dalam struktur PRJ_PLACEHOLDER_INFO dan PRJ_CALLBACK_DATA . PRJ_PLACEHOLDER_VERSION_INFO. Bidang ContentID adalah tempat penyedia menyimpan hingga 128 byte informasi versi untuk file atau direktori. Penyedia kemudian dapat secara internal mengaitkan nilai ini ke beberapa nilai yang mengidentifikasi tampilan tertentu.

Misalnya, penyedia yang memvirtualisasi repositori kontrol sumber mungkin memilih untuk menggunakan hash konten file untuk berfungsi sebagai ContentID, dan mungkin menggunakan tanda waktu untuk mengidentifikasi tampilan repositori di berbagai titik waktu. Tanda waktu mungkin merupakan waktu yang berkomitmen pada repositori dilakukan.

Menggunakan contoh repositori yang diindeks tanda waktu, alur umum untuk menggunakan ContentID item mungkin:

  1. Klien membuat tampilan dengan menentukan tanda waktu untuk menyajikan konten repositori. Ini akan dilakukan melalui beberapa antarmuka yang diimplementasikan oleh penyedia, bukan melalui ProjFS API. Misalnya, penyedia mungkin memiliki alat baris perintah yang dapat digunakan untuk memberi tahu penyedia yang melihat keinginan pengguna.
  2. Ketika klien membuat handel ke file atau direktori virtual, penyedia membuat tempat penampung untuk itu, menggunakan metadata sistem file dari penyimpanan backing. Kumpulan metadata tertentu yang digunakannya diidentifikasi oleh nilai ContentID yang terkait dengan tanda waktu tampilan yang diinginkan. Ketika penyedia memanggil PrjWritePlaceholderInfo untuk menulis informasi tempat penampung, penyediaan ContentID di anggota VersionInfo dari argumen placeholderInfo . Penyedia kemudian harus merekam bahwa tempat penampung untuk file atau direktori tersebut dibuat dalam tampilan ini.
  3. Ketika klien membaca dari tempat penampung, panggilan balik PRJ_GET_FILE_DATA_CB penyedia dipanggil. Anggota VersionInfo parameter callbackData berisi ContentID yang ditentukan penyedia dalam PrjWritePlaceholderInfo saat membuat tempat penampung file. Penyedia menggunakan ContentID tersebut untuk mengambil isi file yang benar dari penyimpanan cadangannya.
  4. Klien meminta perubahan tampilan dengan menentukan tanda waktu baru. Untuk setiap tempat penampung yang dibuat penyedia dalam tampilan sebelumnya, jika versi yang berbeda dari file tersebut ada dalam tampilan baru, penyedia dapat mengganti tempat penampung pada disk dengan yang diperbarui yang ContentID-nya terkait dengan tanda waktu baru dengan memanggil PrjUpdateFileIfNeeded. Atau, penyedia dapat menghapus tempat penampung dengan memanggil PrjDeleteFile sehingga tampilan baru file akan diproyeksikan dalam enumerasi. Jika file tidak ada dalam tampilan baru, penyedia harus menghapusnya dengan memanggil PrjDeleteFile.

Perhatikan bahwa penyedia harus memastikan bahwa ketika klien menghitung direktori, penyedia akan menyediakan konten yang sesuai dengan tampilan klien. Misalnya, penyedia dapat menggunakan anggota VersionInfo dari parameter callbackData dari panggilan balik PRJ_START_DIRECTORY_ENUMERATION_CB dan PRJ_GET_DIRECTORY_ENUMERATION_CB untuk mengambil konten direktori dengan cepat. Atau itu dapat membangun struktur direktori untuk tampilan itu di penyimpanan backing-nya sebagai bagian dari membangun tampilan, dan kemudian hanya menghitung struktur itu.

Setiap kali panggilan balik penyedia dipanggil untuk tempat penampung atau file parsial, informasi versi yang ditentukan penyedia saat membuat item disediakan di anggota VersionInfo dari parameter callbackData panggilan balik .

Memperbarui Tampilan

Di bawah ini adalah fungsi sampel yang mengilustrasikan bagaimana penyedia dapat memperbarui tampilan lokal saat pengguna menentukan tanda waktu baru. Fungsi ini mengambil daftar tempat penampung yang dibuat penyedia untuk tampilan yang baru saja diubah pengguna, dan memperbarui status cache lokal berdasarkan status setiap tempat penampung dalam tampilan baru. Untuk kejelasan, rutinitas ini menghilangkan kompleksitas tertentu dalam menangani sistem file. Misalnya, dalam tampilan lama direktori tertentu mungkin ada dengan beberapa file atau direktori di dalamnya, tetapi dalam tampilan baru direktori tersebut (dan kontennya) mungkin tidak ada lagi. Untuk menghindari terjadinya kesalahan dalam situasi seperti itu, penyedia harus memperbarui status file dan direktori di bawah akar virtualisasi dengan cara yang mengutamakan kedalaman.

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