Aracılığıyla paylaş


Dosya Verileri Sağlama

Sağlayıcı ilk kez bir sanallaştırma kökü oluşturduğunda yerel sistemde boş olur. Başka bir ifadeyle, yedekleme veri deposundaki öğelerin hiçbiri henüz diskte önbelleğe alınmamıştır. Öğeler açıldıktan sonra ProjFS, bu öğelerin yerel dosya sisteminde oluşturulması için yer tutuculara izin vermek üzere sağlayıcıdan bilgi istemektedir. Öğe içeriğine erişildikçe, ProjFS bu içerikleri sağlayıcıdan istemektedir. Sonuç olarak, kullanıcının perspektifinden sanallaştırılmış dosyalar ve dizinler, yerel dosya sisteminde zaten bulunan normal dosyalara ve dizinlere benzer şekilde görünür.

Yer Tutucu Oluşturma

Uygulama sanallaştırılmış bir dosyaya tanıtıcı açmayı denediğinde, ProjFS yolun diskte henüz var olmayan her öğesi için PRJ_GET_PLACEHOLDER_INFO_CB geri çağırmayı çağırır. Örneğin, bir uygulama C:\virtRoot\dir1\dir2\file.txtaçmaya çalışırsa ancak diskte yalnızca C:\virtRoot\dir1 yolu varsa, sağlayıcı C:\virtRoot\dir1\dir2için bir geri çağırma alır ve ardından C:\virtRoot\dir1\dir2\file.txtiçin .

ProjFS sağlayıcının PRJ_GET_PLACEHOLDER_INFO_CB geri çağırmasını çağırdığında, sağlayıcı aşağıdaki eylemleri gerçekleştirir:

  1. Sağlayıcı, istenen adın kendi yedekleme deposunda mevcut olup olmadığını belirler. Sağlayıcı, istenen adın yedekleme deposunda mevcut olup olmadığını belirlemek için yedekleme deposunda arama yaparken karşılaştırma yordamı olarak PrjFileNameCompare kullanmalıdır. Aksi takdirde, sağlayıcı geri çağırma işlevinden HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) döndürür.

  2. İstenen ad yedekleme deposunda mevcutsa, sağlayıcı PRJ_PLACEHOLDER_INFO bir yapıyı öğenin dosya sistemi meta verileriyle doldurur ve verileri ProjFS'ye göndermek için PrjWritePlaceholderInfoçağırır. ProjFS bu bilgileri kullanarak öğenin yerel dosya sisteminde bir yer tutucu oluşturur.

    ProjFS, sağlayıcı tarafından PRJ_PLACEHOLDER_INFO'daki FileBasicInfo.FileAttributes üyesine ayarlanan FILE_ATTRIBUTE bayraklarını, FILE_ATTRIBUTE_DIRECTORY hariç, kullanır; FileBasicInfo.FileAttributes üyesindeki FILE_ATTRIBUTE_DIRECTORY için doğru değeri, sağlanan FileBasicInfo.IsDirectory üyesinin değerine göre belirler.

    Destek deposu sembolik bağlantıları destekliyorsa, sağlayıcının yer tutucu verileri ProjFS'ye göndermek için PrjWritePlaceholderInfo2 kullanması gerekir. PrjWritePlaceholderInfo2, sağlayıcının yer tutucunun sembolik bir bağlantı olduğunu ve hedefinin ne olduğunu belirtmesini sağlayan ek bir arabellek girişini destekler. Aksi takdirde PrjWritePlaceholderInfoiçin yukarıda açıklandığı gibi davranır. Aşağıdaki örnekte, sembolik bağlantılar için destek sağlamak üzere PrjWritePlaceholderInfo2 nasıl kullanılacağı gösterilmektedir.

    PrjWritePlaceholderInfo2'nin Windows 10, sürüm 2004 itibarıyla desteklendiğini unutmayın. Sağlayıcı, örneğin GetProcAddresskullanarak PrjWritePlaceholderInfo2varlığını araştırmalıdır.

HRESULT
MyGetPlaceholderInfoCallback(
    _In_ const PRJ_CALLBACK_DATA* callbackData
    )
{
    // MyGetItemInfo is a routine the provider might implement to get
    // information from its backing store for a given file path.  The first
    // parameter is an _In_ parameter that supplies the name to look for.
    // If the item exists the routine provides the file information in the
    // remaining parameters, all of which are annotated _Out_:
    // * 2nd parameter: the name as it appears in the backing store
    // * 3rd-9th parameters: basic file info
    // * 10th parameter: if the item is a symbolic link, a pointer to a 
    //   NULL-terminated string identifying the link's target
    //
    // Note that the routine returns the name that is in the backing
    // store.  This is because the input file path may not be in the same
    // case as what is in the backing store.  The provider should create
    // the placeholder with the name it has in the backing store.
    //
    // Note also that this example does not provide anything beyond basic
    // file information and a possible symbolic link target.
    HRESULT hr;
    WCHAR* backingStoreName = NULL;
    WCHAR* symlinkTarget = NULL;
    PRJ_PLACEHOLDER_INFO placeholderInfo = {};
    hr = MyGetItemInfo(callbackData->FilePathName,
                       &backingStoreName,
                       &placeholderInfo.FileBasicInfo.IsDirectory,
                       &placeholderInfo.FileBasicInfo.FileSize,
                       &placeholderInfo.FileBasicInfo.CreationTime,
                       &placeholderInfo.FileBasicInfo.LastAccessTime,
                       &placeholderInfo.FileBasicInfo.LastWriteTime,
                       &placeholderInfo.FileBasicInfo.ChangeTime,
                       &placeholderInfo.FileBasicInfo.FileAttributes,
                       &symlinkTarget);

    if (FAILED(hr))
    {
        // If callbackData->FilePathName doesn't exist in our backing store then
        // MyGetItemInfo should HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND).
        // If this is some other error, e.g. E_OUTOFMEMORY because MyGetItemInfo
        // couldn't allocate space for backingStoreName, we return that.
        return hr;
    }

    // If the file path is for a symbolic link, pass that in with the placeholder info.
    if (symlinkTarget != NULL)
    {
        PRJ_EXTENDED_INFO extraInfo = {};

        extraInfo.InfoType = PRJ_EXT_INFO_SYMLINK;
        extraInfo.Symlink.TargetName = symlinkTarget;

        // If this returns an error we'll want to return that error from the callback.
        hr = PrjWritePlaceholderInfo2(callbackData->NamespaceVirtualizationContext,
                                      backingStoreName,
                                      &placeholderInfo,
                                      sizeof(placeholderInfo),
                                      &extraInfo);
    }
    else
    {
        // If this returns an error we'll want to return that error from the callback.
        hr = PrjWritePlaceholderInfo(callbackData->NamespaceVirtualizationContext,
                                     backingStoreName,
                                     &placeholderInfo,
                                     sizeof(placeholderInfo));
    }

    free(backingStoreName);

    if (symlinkTarget != NULL)
    {
        free(symlinkTarget);
    }

    return hr;
}

Dosya İçeriği Sağlama

ProjFS'nin sanallaştırılmış bir dosyanın veri içerdiğinden emin olması gerektiğinde (örneğin, bir uygulama dosyadan okumaya çalıştığında), ProjFS sağlayıcının dosyanın içeriğini sağlamasını istemek için söz konusu öğenin PRJ_GET_FILE_DATA_CB geri çağırmasını çağırır. Sağlayıcı, dosyanın verilerini kendi yedekleme deposundan alır ve verileri yerel dosya sistemine göndermek için PrjWriteFileDatakullanır.

ProjFS bu geri çağırmayı çağırdığında, callbackData parametresinin FilePathName üyesi, dosyanın yer tutucusu oluşturulduğunda sahip olduğu adını sağlar. Yani, dosya yer tutucusu oluşturulduktan sonra yeniden adlandırıldıysa, geri çağırma işlevi mevcut (yeniden adlandırıldıktan sonraki) adı değil, özgün (yeniden adlandırılmadan önceki) adını sağlar. Gerekirse sağlayıcı, hangi dosyanın verilerinin istendiğini belirlemek için callbackData parametresinin VersionInfo üyesini kullanabilir.

PRJ_CALLBACK_DATA VersionInfo üyesinin nasıl kullanılabileceğini öğrenmek için PRJ_PLACEHOLDER_VERSION_INFO belgelerine ve Görünüm Değişikliklerini İşleme konusuna bakın.

Sağlayıcının, PRJ_GET_FILE_DATA_CB geri çağırmada istenen veri aralığını, her biri istenen aralığın bir bölümünü sağlayan PrjWriteFileDataiçin birden çok çağrıya bölmesine izin verilir. Ancak sağlayıcının geri çağırmayı tamamlamadan önce istenen aralığın tamamını sağlaması gerekir. Örneğin, geri arama isteği byteOffset 0'dan uzunluğunda 10.485.760 olan bir veri uzunluğu için 10 MB veri isterse, sağlayıcı verileri, her biri 1 MB gönderilecek şekilde PrjWriteFileDataçağrılarına ayırarak sağlamayı seçebilir.

Sağlayıcı, istenen aralıktan daha fazlasını, dosyanın uzunluğuna kadar sağlamakta serbesttir. Sağlayıcının sağladığı aralık istenen aralığı kapsamalıdır. Örneğin, geri arama bayt ofset 4096'dan uzunluk 1 052 672 olan 1 MB veri talep ederse ve dosyanın toplam boyutu 10 MB ise, sağlayıcı 0 uzaklığından başlayarak 2 MB veri döndürmeyi seçebilir.

Arabellek Hizalaması Ile İlgili Dikkat Edilmesi Gerekenler

ProjFS, verileri yerel dosya sistemine yazmasını gerektiren çağıranın FILE_OBJECT kullanır. Ancak ProjFS, FILE_OBJECT'nin arabelleğe alınan veya arabelleklenmemiş G/Ç için açıldığını denetleyemez. FILE_OBJECT arabelleklenmemiş I/O için açıldıysa, dosyadaki okuma ve yazma işlemleri belirli hizalama gereksinimlerine uymalıdır. Sağlayıcı iki şey yaparak bu hizalama gereksinimlerini karşılayabilir:

  1. PrjAllocateAlignedBuffer kullanarak PrjWriteFileData'ın arabellek parametresini geçirmek üzere arabelleği ayırın.
  2. PrjWriteFileDatabyteOffset ve uzunluğu parametrelerinin depolama cihazının hizalama gereksiniminin tamsayı katları olduğundan emin olun (uzunluğu parametresinin, baytOffset + uzunluğu dosyanın sonuna eşit olması durumunda bu gereksinimi karşılaması gerekmediğini unutmayın). Sağlayıcı, depolama cihazının hizalama gereksinimini almak için PrjGetVirtualizationInstanceInfo kullanabilir.

ProjFS uygun hizalamayı hesaplamak için sağlayıcıya bırakır. Bunun nedeni, PRJ_GET_FILE_DATA_CB bir geri çağırmayı işlerken sağlayıcının geri çağırmayı tamamlamadan önce istenen verileri birden çok PrjWriteFileData çağrısında döndürmeyi seçebilmesidir.

Sağlayıcı, PrjWriteFileData tek bir çağrı kullanarak ya dosyanın tamamını yazacaksa, yani byteOffset = 0'dan uzunluk = dosyanın boyutuna kadar ya da PRJ_GET_FILE_DATA_CB geri çağırmada istenen tam aralığı döndürecekse, sağlayıcının herhangi bir hizalama hesaplaması yapması gerekmez. Ancak, yine de PrjAllocateAlignedBuffer kullanmalıdır, böylece arabellek depolama cihazının hizalama gereksinimlerini karşılar.

Ara belleğe alınmış ve alınmamış I/O hakkında daha fazla bilgi için Dosya Arabelleğe Alma konusuna bakın.

//  BlockAlignTruncate(): Aligns P on the previous V boundary (V must be != 0).
#define BlockAlignTruncate(P,V) ((P) & (0-((UINT64)(V))))

// This sample illustrates both returning the entire requested range in a
// single call to PrjWriteFileData(), and splitting it up into smaller 
// units.  Note that the provider must return all the requested data before
// completing the PRJ_GET_FILE_DATA_CB callback with S_OK.
HRESULT
MyGetFileDataCallback(
    _In_ const PRJ_CALLBACK_DATA* callbackData,
    _In_ UINT64 byteOffset,
    _In_ UINT32 length
    )
{
    HRESULT hr;

    // For the purposes of this sample our provider has a 1 MB limit to how
    // much data it can return at once (perhaps its backing store imposes such
    // a limit).
    UINT64 writeStartOffset;
    UINT32 writeLength;
    if (length <= 1024*1024)
    {
        // The range requested in the callback is less than 1MB, so we can return
        // the data in a single call, without doing any alignment calculations.
        writeStartOffset = byteOffset;
        writeLength = length;
    }
    else
    {
        // The range requested is more than 1MB.  Retrieve the device alignment
        // and calculate a transfer size that conforms to the device alignment and
        // is <= 1MB.
        PRJ_VIRTUALIZATION_INSTANCE_INFO instanceInfo;
        UINT32 infoSize = sizeof(instanceInfo);
        hr = PrjGetVirtualizationInstanceInfo(callbackData->NamespaceVirtualizationContext,
                                              &infoSize,
                                              &instanceInfo);

        if (FAILED(hr))
        {
            return hr;
        }

        // The first transfer will start at the beginning of the requested range,
        // which is guaranteed to have the correct alignment.
        writeStartOffset = byteOffset;

        // Ensure our transfer size is aligned to the device alignment, and is
        // no larger than 1 MB (note this assumes the device alignment is less
        // than 1 MB).
        UINT64 writeEndOffset = BlockAlignTruncate(writeStartOffset + 1024*1024,
                                                   instanceInfo->WriteAlignment);
        assert(writeEndOffset > 0);
        assert(writeEndOffset > writeStartOffset);

        writeLength = writeEndOffset - writeStartOffset;
    }

    // Allocate a buffer that adheres to the needed memory alignment.
    void* writeBuffer = NULL;
    writeBuffer = PrjAllocateAlignedBuffer(callbackData->NamespaceVirtualizationContext,
                                           writeLength);

    if (writeBuffer == NULL)
    {
        return E_OUTOFMEMORY;
    }

    do
    {
        // MyGetFileDataFromStore is a routine the provider might implement to copy
        // data for the specified file from the provider's backing store to a
        // buffer.  The routine finds the file located at callbackData->FilePathName
        // and copies writeLength bytes of its data, starting at writeStartOffset,
        // to the buffer pointed to by writeBuffer.
        hr = MyGetFileDataFromStore(callbackData->FilePathName,
                                    writeStartOffset,
                                    writeLength,
                                    writeBuffer);

        if (FAILED(hr))
        {
            PrjFreeAlignedBuffer(writeBuffer);
            return hr;
        }

        // Write the data to the file in the local file system.
        hr = PrjWriteFileData(callbackData->NamespaceVirtualizationContext,
                              callbackData->DataStreamId,
                              writeBuffer,
                              writeStartOffset,
                              writeLength);

        if (FAILED(hr))
        {
            PrjFreeAlignedBuffer(writeBuffer);
            return hr;
        }

        // The length parameter to the callback is guaranteed to be either
        // correctly aligned or to result in a write to the end of the file.
        length -= writeLength;
        if (length < writeLength)
        {
            writeLength = length;
        }
    }
    while (writeLength > 0);

    PrjFreeAlignedBuffer(writeBuffer);
    return hr;
}