Função CfGetPlaceholderRangeInfoForHydration (cfapi.h)

Obtém informações de intervalo sobre um arquivo ou pasta de espaço reservado. Essas informações de intervalo são idênticas ao que CfGetPlaceholderRangeInfo retorna. No entanto, ele não usa um fileHandle como um parâmetro. Em vez disso, ele usa ConnectionKey, TransferKey e FileId para identificar o arquivo e o fluxo para quais informações de intervalo estão sendo solicitadas.

A plataforma fornece ConnectionKey, TransferKey e FileId para todas as funções de retorno de chamada registradas por meio do CfConnectSyncRoot e o provedor pode usar esses parâmetros para obter informações de intervalo sobre um espaço reservado do CF_CALLBACK_TYPE_FETCH_DATA retorno de chamada sem exigir que ele abra um identificador para o arquivo.

Se o arquivo não for um espaço reservado para arquivos de nuvem, a API falhará. Em caso de êxito, as informações de intervalo são retornadas de acordo com o InfoClass específico solicitado.

Observação

Essa API só estará disponível se o PlatformVersion.IntegrationNumber obtido de CfGetPlatformInfo for 0x600 ou superior.

Sintaxe

HRESULT CfGetPlaceholderRangeInfoForHydration(
  [in]            CF_CONNECTION_KEY               ConnectionKey,
  [in]            CF_TRANSFER_KEY                 TransferKey,
  [in]            LARGE_INTEGER                   FileId,
  [in]            CF_PLACEHOLDER_RANGE_INFO_CLASS InfoClass,
  [in]            LARGE_INTEGER                   StartingOffset,
  [in]            LARGE_INTEGER                   RangeLength,
  [out]           PVOID                           InfoBuffer,
  [in]            DWORD                           InfoBufferSize,
  [out, optional] PDWORD                          InfoBufferWritten
);

Parâmetros

[in] ConnectionKey

Um identificador opaco criado por CfConnectSyncRoot para uma raiz de sincronização gerenciada pelo provedor de sincronização. Ele é retornado também em CF_CALLBACK_INFO no retorno de chamada CF_CALLBACK_TYPE_FETCH_DATA e outros retornos de chamada.

[in] TransferKey

O identificador opaco para o arquivo de espaço reservado para o qual CF_CALLBACK_TYPE_FETCH_DATA retorno de chamada foi invocado. Ele também é retornado em CF_CALLBACK_INFO no retorno de chamada CF_CALLBACK_TYPE_FETCH_DATA . Como alternativa, isso poderá ser obtido por CfGetTransferKey se a API não estiver sendo invocada de CF_CALLBACK_TYPE_FETCH_DATA Retorno de chamada.

[in] FileId

Uma ID exclusiva de todo o volume mantida pelo sistema de arquivos de 64 bits do arquivo/diretório de espaço reservado a ser atendido. Assim como TransferKey, isso é retornado em CF_CALLBACK_INFO no CF_CALLBACK_TYPE_FETCH_DATA e outros retornos de chamada para que o provedor não precise recuperá-lo novamente.

[in] InfoClass

Tipos do intervalo de dados de espaço reservado. O valor pode ser um dos seguintes:

Valor Descrição
CF_PLACEHOLDER_RANGE_INFO_ONDISK Dados em disco são dados que são físicos presentes no arquivo. Esse é um super conjunto de outros tipos de intervalos.
CF_PLACEHOLDER_RANGE_INFO_VALIDATED Os dados validados são um subconjunto dos dados em disco que estão atualmente em sincronia com a nuvem.
CF_PLACEHOLDER_RANGEINFO_MODIFIED Os dados modificados são um subconjunto dos dados em disco que atualmente não estão em sincronia com a nuvem (ou seja, modificados ou acrescentados).)

[in] StartingOffset

Deslocamento do ponto de partida do intervalo de dados. StartingOffset e RangeLength especificam um intervalo no arquivo de espaço reservado cujas informações, conforme descrito pelo parâmetro InfoClass , são solicitadas

[in] RangeLength

Comprimento do intervalo de dados. Um provedor pode especificar CF_EOF para RangeLength indicar que o intervalo para o qual as informações são solicitadas é de StartingOffset até o final do arquivo.

[out] InfoBuffer

Ponteiro para um buffer que receberá os dados. O buffer é uma matriz de estruturas de CF_FILE_RANGE , que são pares de deslocamento/comprimento, descrevendo os intervalos solicitados.

[in] InfoBufferSize

O comprimento do InfoBuffer em bytes.

[out, optional] InfoBufferWritten

Recebe o número de bytes retornados no InfoBuffer.

Retornar valor

Se essa função for bem-sucedida, ela retornará S_OK. Caso contrário, ele retornará um código de erro HRESULT. Alguns códigos de erro comuns são listados na tabela a seguir:

Código do erro Significado
HRESULT_FROM_WIN32( ERROR_HANDLE_EOF ) Isso significa que StartingOffset>= a posição do final do arquivo.
HRESULT_FROM_WIN32( ERROR_MORE_DATA ) Isso implica que a próxima entrada de CF_FILE_RANGE não se encaixa no buffer fornecido. O chamador deve verificar se alguma entrada é recebida ou não usando o valor InfoBufferWritten retornado.

Comentários

Embora já exista uma API para consultar intervalos de arquivos hidratados de um espaço reservado, uma nova API foi necessária para melhorar a confiabilidade da plataforma.

A API existente, CfGetPlaceholderRangeInfo, requer um identificador aberto para um arquivo e, em seguida, dispara um FSCTL_HSM_CONTROL usando esse identificador. Provedores/Mecanismos de Sincronização normalmente usam essa API para avaliar quais partes do arquivo não são hidratadas do contexto de um retorno de chamada CF_CALLBACK_TYPE_FETCH_DATA invocado pelo filtro para hidratar o arquivo para satisfazer uma E/S.

Um minifiltro na pilha de E/S pode emitir a verificação de dados no arquivo quando o mecanismo de provedor/sincronização tenta abrir um identificador para o arquivo a ser passado como um parâmetro para CfGetPlaceholderRangeInfo. Como alternativa, um minifiltro pode bloquear o FSCTL_HSM_CONTROL que o CfGetPlaceholderRangeInfo dispara internamente.

O filtro cldflt foi projetado para invocar apenas uma CF_CALLBACK_TYPE_FETCH_DATA retorno de chamada por intervalo de arquivos necessário para hidratar o arquivo. Como resultado de qualquer um dos casos acima, a verificação de dados está presa atrás do CF_CALLBACK_TYPE_FETCH_DATA original ou o CF_CALLBACK_TYPE_FETCH_DATA está preso atrás do FSCTL bloqueado. Isso causa um deadlock no caminho da hidratação.

Portanto, essa API é necessária. Ele executa a mesma funcionalidade que CfGetPlaceholderRangeInfo, mas se comunica com o filtro diretamente usando portas de mensagem de filtro ignorando a pilha de E/S intermediária. Portanto, nenhum minifiltro intermediário pode obstruir o CreateFile ou o FSCTL_HSM_CONTROL.

Observe que o chamador sempre tem a ConnectionKey obtida por meio de CfConnectSyncRoot. Ele pode obter TransferKey por meio de CfGetTransferKey e obter FileId usando GetFileInformationByHandle. Mas essa abordagem precisa de um identificador para ser aberta ao arquivo e, portanto, não é diferente de usar CfGetPlaceholderRangeInfo.

Para resumir, quando as informações de intervalo são necessárias do contexto de um retorno de chamada CF_CALLBACK_TYPE_FETCH_DATA , essa API deve ser usada. Em todos os outros casos, incluindo quando o provedor deseja hidratar o arquivo sem ser solicitado pelo filtro, CfGetPlaceholderRangeInfo deve ser usado. A plataforma não pode reconhecer qual API é chamada em um contexto específico e, portanto, o ônus está no provedor/Mecanismo de Sincronização para fazer a coisa certa.

Exemplos

Este é um exemplo simples em que a função passa um InfoBuffer suficiente para recuperar apenas uma entrada CF_FILE_RANGE por vez. Na prática, o chamador pode passar um InfoBuffer que pode corresponder a várias entradas CF_FILE_RANGE por invocação da API. O código de erro HRESULT_FROM_WIN32( ERROR_MORE_DATA ) pode ser usado para passar um buffer maior, se necessário.

#include <cfapi.h>

// ******************************************************************************************************
// From within the CF_CALLBACK_TYPE_FETCH_DATA Callback, the provider can use
// g_PlatformInfo.IntegrationNumber to see if the new API is supported. If it is, the provider can pass
// ConnectionKey, TransferKey and FileId along with other parameters to obtain information about file
// ranges which have already been hydrated.
// *******************************************************************************************************

// The provider could obtain file ranges that are hydrated like this:
std::vector<CF_FILE_RANGE> hydratedRanges = GetFileRangesFromCallback( CallbackInfo->ConnectionKey,
                                                                       CallbackInfo->TransferKey,
                                                                       CallbackInfo->FileId,
                                                                       CF_PLACEHOLDER_RANGE_INFO_ONDISK
                                                                       0,
                                                                       CF_EOF);

// Based on these hydratedRanges, the provider can chose to hydrate only ranges which aren’t on the disk.

// ******************************************************************************************************
// Implementation of a function that eventually calls this API.
// ******************************************************************************************************

typedef HRESULT( __stdcall* t_CfGetPlaceholderRangeInfoForHydration )(
    CF_CONNECTION_KEY ConnectionKey,
    CF_TRANSFER_KEY TransferKey,
    LARGE_INTEGER FileId,
    CF_PLACEHOLDER_RANGE_INFO_CLASS InfoClass,
    LARGE_INTEGER StartingOffset,
    LARGE_INTEGER RangeLength,
    PVOID InfoBuffer,
    DWORD InfoBufferSize,
    PDWORD InfoBufferWritten );

t_CfGetPlaceholderRangeInfoForHydration _CfGetPlaceholderRangeInfoForHydration = nullptr;

std::vector<CF_FILE_RANGE>
GetFileRangesFromCallback( CF_CONNECTION_KEY ConnectionKey,
                           CF_TRANSFER_KEY TransferKey,
                           LARGE_INTEGER FileId,
                           CF_PLACEHOLDER_RANGE_INFO_CLASS RangeInfoClass,
                           long long StartOffset,
                           long long Length,
                           PBOOLEAN UseOldAPI )
{

    long long StartOffset = 0;
    CF_FILE_RANGE fileRange;
    long long Length = 0;
    LARGE_INTEGER queryOffset = ll2li( StartOffset );
    LARGE_INTEGER queryLength = ll2li( Length );
    DWORD inforBufferWritten = 0;

    // This will contain all the hydrated ranges in the file if the function succeeds.
    std::vector<CF_FILE_RANGE> ranges;
    bool stop = false;

    CF_PLATFORM_INFO platformInfo;

    hr = (CfGetPlatformInfo( &platformInfo ));
    if(FAILED(hr)) {
        *UseOldAPI = TRUE;
        return ranges; //empty.
    }

    if (platformInfo.IntegrationNumber < 600) {
        *UseOldAPI = TRUE;
        return ranges; //empty.
    }

    wil::unique_hmodule CloudFilesApi( LoadLibrary( L"cldapi.dll" ) );
    THROW_LAST_ERROR_IF_NULL( CloudFilesApi );

    _CfGetPlaceholderRangeInfoForHydration = reinterpret_cast<t_CfGetPlaceholderRangeInfoForHydration>(
            GetProcAddress( CloudFilesApi.get(), "CfGetPlaceholderRangeInfoForHydration" ) );
    THROW_LAST_ERROR_IF_NULL( _CfGetPlaceholderRangeInfoForHydration );

    while ( !stop ) {

        hr = _CfGetPlaceholderRangeInfoForHydration ( ConnectionKey,
                                                      TransferKey,
                                                      FileId,
                                                      RangeInfoClass,
                                                      queryOffset,
                                                      queryLength,
                                                      &fileRange,
                                                      sizeof( fileRange ),
                                                      &infoBufferWritten );

        if ( hr == HRESULT_FROM_WIN32( ERROR_HANDLE_EOF ) ||
             hr == HRESULT_FROM_WIN32( ERROR_MORE_DATA ) ) {

            // We need to break the loop only if there is no more data.
            if ( hr == HRESULT_FROM_WIN32( ERROR_HANDLE_EOF ) ) {
                stop = true;
            }

            hr = S_OK;
        }

        if ( FAILED( hr ) || infoBufferWritten == 0 ) {
            return ranges;
        }

        ranges.push_back( fileRange );
        queryOffset.QuadPart = fileRange.StartingOffset.QuadPart + fileRange.Length.QuadPart;

        if ( Length != CF_EOF && queryOffset.QuadPart >= ( StartOffset + Length ) ) {
            stop = true;
        } else if ( Length != CF_EOF) {
            // Update the new query length
            queryLength.QuadPart = StartOffset + Length - queryOffset.QuadPart
        
            if ( queryLength.QuadPart <= 0 ) {
                stop = true;
            }
        }
    }

    return ranges;
}

Requisitos

Requisito Valor
Cabeçalho cfapi.h

Confira também

CF_PLACEHOLDER_RANGE_INFO_CLASS

CfGetPlaceholderRangeInfo

CfConnectSyncRoot

CfGetPlatformInfo

CfGetTransferKey