Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Gdy dostawca najpierw tworzy katalog główny wirtualizacji, jest pusty w systemie lokalnym. Oznacza to, że żaden z elementów w zapasowej bazie danych nie został jeszcze zapisany na dysku w pamięci podręcznej. W miarę otwierania elementów program ProjFS żąda informacji od dostawcy, aby umożliwić tworzenie symboli zastępczych dla tych elementów w lokalnym systemie plików. W miarę uzyskiwania dostępu do zawartości elementu program ProjFS żąda tej zawartości od dostawcy. Wynikiem jest to, że z perspektywy użytkownika zwirtualizowane pliki i katalogi wydają się podobne do normalnych plików i katalogów, które już znajdują się w lokalnym systemie plików.
Tworzenie symbolu zastępczego
Gdy aplikacja próbuje otworzyć dojście do zwirtualizowanego pliku, program ProjFS wywołuje wywołanie zwrotne PRJ_GET_PLACEHOLDER_INFO_CB dla każdego elementu ścieżki, który jeszcze nie istnieje na dysku. Jeśli na przykład aplikacja spróbuje otworzyć C:\virtRoot\dir1\dir2\file.txt, ale tylko ścieżka C:\virtRoot\dir1 istnieje na dysku, dostawca otrzyma wywołanie zwrotne dla C:\virtRoot\dir1\dir2, a następnie dla C:\virtRoot\dir1\dir2\file.txt.
Gdy ProjFS wywołuje wywołanie zwrotne PRJ_GET_PLACEHOLDER_INFO_CB dostawcy, dostawca wykonuje następujące czynności:
Dostawca określa, czy żądana nazwa istnieje w magazynie zapasowym. Dostawca powinien użyć PrjFileNameCompare jako procedury porównania podczas przeszukiwania magazynu kopii zapasowych w celu określenia, czy żądana nazwa istnieje w magazynie zapasowym. Jeśli tak nie jest, dostawca zwraca HRESULT_FROM_WIN32 (ERROR_FILE_NOT_FOUND) z wywołania zwrotnego.
Jeśli żądana nazwa istnieje w magazynie zapasowym, dostawca wypełnia strukturę PRJ_PLACEHOLDER_INFO metadanymi systemu plików elementu i wywołuje funkcję PrjWritePlaceholderInfo, aby wysłać dane do systemu ProjFS. ProjFS użyje tych informacji, aby utworzyć placeholder w lokalnym systemie plików dla tego elementu.
ProjFS używa ustawionych przez dostawcę flag FILE_ATTRIBUTE w członkowskim elemencie FileBasicInfo.FileAttributes w PRJ_PLACEHOLDER_INFO, z wyjątkiem FILE_ATTRIBUTE_DIRECTORY; wartość dla FILE_ATTRIBUTE_DIRECTORY zostanie poprawnie ustawiona w członkowskim elemencie FileBasicInfo.FileAttributes zgodnie z podaną wartością dla FileBasicInfo.IsDirectory.
Jeśli magazyn zapasowy obsługuje linki symboliczne, dostawca musi użyć PrjWritePlaceholderInfo2, aby wysłać dane zastępcze do systemu plików ProjFS. PrjWritePlaceholderInfo2 obsługuje dodatkowe dane wejściowe buforu, które umożliwiają dostawcy określenie, że symbol zastępczy jest łączem symbolicznym i jego celem. W przeciwnym razie zachowuje się zgodnie z powyższym opisem dla PrjWritePlaceholderInfo. Poniższy przykład ilustruje sposób użycia PrjWritePlaceholderInfo2 w celu zapewnienia obsługi linków symbolicznych.
Należy pamiętać, że PrjWritePlaceholderInfo2 jest obsługiwana w systemie Windows 10 w wersji 2004. Dostawca powinien sprawdzić istnienie PrjWritePlaceholderInfo2, na przykład przy użyciu GetProcAddress.
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;
}
Dostarczanie zawartości pliku
Gdy system plików ProjFS musi upewnić się, że zwirtualizowany plik zawiera dane, na przykład gdy aplikacja próbuje odczytać z pliku, projFS wywołuje wywołanie zwrotne PRJ_GET_FILE_DATA_CB dla tego elementu, aby zażądać, aby dostawca podać zawartość pliku. Dostawca pobiera dane pliku z magazynu zapasowego i używa PrjWriteFileData do wysyłania danych do lokalnego systemu plików.
Gdy ProjFS wywołuje tę funkcję zwrotną, element członkowski FilePathName parametru callbackData podaje nazwę pliku w chwili utworzenia jego symbolu zastępczego. Oznacza to, że jeśli nazwa pliku została zmieniona od momentu utworzenia jego symbolu zastępczego, wywołanie zwrotne dostarcza oryginalną nazwę (wstępnie zmienioną nazwę), a nie bieżącą (po zmianie nazwy). W razie potrzeby dostawca może użyć członu VersionInfo parametru callbackData, aby określić, które dane pliku są żądane.
Aby uzyskać więcej informacji na temat sposobu użycia członka VersionInfo w PRJ_CALLBACK_DATA, zobacz dokumentację dotyczącą PRJ_PLACEHOLDER_VERSION_INFO oraz temat Obsługa zmian widoku.
Dostawca może podzielić zakres danych żądanych w wywołaniu zwrotnym PRJ_GET_FILE_DATA_CB na wiele wywołań do PrjWriteFileData, z których każdy dostarcza część żądanego zakresu. Jednak dostawca musi podać cały żądany zakres przed ukończeniem wywołania zwrotnego. Jeśli na przykład wywołanie zwrotne żąda 10 MB danych z byteOffset 0 dla długości 10 485 760, dostawca może wybrać podanie danych w 10 wywołaniach do PrjWriteFileData, z których każdy wysyła 1 MB.
Dostawca może również dostarczać więcej danych niż żądany zakres, aż do długości pliku. Zakres dostaw dostawcy musi obejmować żądany zakres. Jeśli na przykład wywołanie zwrotne żąda 1 MB danych z byteOffset 4096 dla długości 1052 672, a plik ma całkowity rozmiar 10 MB, dostawca może zwrócić 2 MB danych rozpoczynających się od przesunięcia 0.
Zagadnienia dotyczące wyrównania buforu
System plików ProjFS używa FILE_OBJECT od wywołującego, który wymaga danych do zapisania ich w lokalnym systemie plików. Jednak ProjFS nie może kontrolować, czy FILE_OBJECT został otwarty dla buforowanego lub niebuforowanego I/O. Jeśli FILE_OBJECT został otwarty w trybie niebuforowanym, odczyty i zapisy w pliku muszą być zgodne z pewnymi wymaganiami dotyczącymi wyrównania. Dostawca może spełnić te wymagania dotyczące wyrównania, wykonując dwie czynności:
- Użyj PrjAllocateAlignedBuffer, aby przydzielić bufor do przekazania parametru PrjWriteFileDatabuforu.
- Upewnij się, że parametry byteOffset i długość parametru PrjWriteFileData są całkowitymi wielokrotnościami wymagań wyrównania urządzenia pamięci masowej (należy pamiętać, że parametr długość nie musi spełniać tego wymagania, jeśli byteOffset + długość jest równa końcowi pliku). Dostawca może użyć PrjGetVirtualizationInstanceInfo, aby pobrać wymaganie wyrównania urządzenia pamięci masowej.
Program ProjFS pozostawia dostawcy obliczenie właściwego wyrównania. Dzieje się tak dlatego, że podczas przetwarzania wywołania zwrotnego PRJ_GET_FILE_DATA_CB dostawca może zdecydować się na zwrócenie żądanych danych za pośrednictwem wielu wywołań PrjWriteFileData, z których każde zwraca część całkowitej ilości żądanych danych przed zakończeniem wywołania zwrotnego.
Jeśli dostawca użyje pojedynczego wywołania do PrjWriteFileData zapisać cały plik, tj. z byteOffset = 0 do długości = rozmiar pliku lub zwrócić dokładny zakres żądany w wywołaniu zwrotnym PRJ_GET_FILE_DATA_CB, dostawca nie musi wykonywać żadnych obliczeń wyrównania. Należy jednak nadal używać PrjAllocateAlignedBuffer, aby upewnić się, że bufor spełnia wymagania dotyczące wyrównania urządzenia pamięci masowej.
Jeśli chcesz uzyskać więcej informacji na temat buforowanych vs. niebuforowanych operacji we/wy, zobacz temat Buforowanie plików.
// 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;
}