cfapi: cloud operation is invalid

Viewed 647

I'm making a Cloud Sync Engines Supports Placeholder based on CloudMirror. And got problem on CF_CALLBACK_TYPE_FETCH_DATA

When i double click file (placeholder) in window explorer, app trigger FILE_ATTRIBUTE_PINNED and Hydrating file. And then cfapi call FETCH_DATA and read asynchronous file (my app work look same with CloudMirror).
But i got HRESULT return from CfExecute is 0x8007017c the cloud operation is invalid. Debug look all value is true

Then how to resolve it, thank.

enter image description here

#define CHUNKSIZE 4096
#define FIELD_SIZE( type, field ) ( sizeof( ( (type*)0 )->field ) )
#define CF_SIZE_OF_OP_PARAM( field )( FIELD_OFFSET( CF_OPERATION_PARAMETERS, field ) + FIELD_SIZE( CF_OPERATION_PARAMETERS, field ) )
    struct READ_COMPLETION_CONTEXT
    {
        OVERLAPPED Overlapped;
        LARGE_INTEGER CallbackInfo_FileSize;
        CF_CONNECTION_KEY CallbackInfo_ConnectionKey;
        CF_TRANSFER_KEY CallbackInfo_TransferKey;
        HANDLE PipeHandle{ 0 };
        LARGE_INTEGER StartOffset;
        LARGE_INTEGER RemainingLength;
        ULONG BufferSize;

        WCHAR* FullPath{ nullptr };
        BYTE* Buffer{ nullptr };
        ~READ_COMPLETION_CONTEXT()
        {
            if (FullPath) delete FullPath;
            if (Buffer) delete Buffer;
            if (PipeHandle) CloseHandle(PipeHandle);
        }
        void Cancel()
        {
            TransferData(
                CallbackInfo_ConnectionKey, 
                CallbackInfo_TransferKey,
                NULL,
                StartOffset,
                RemainingLength,
                STATUS_UNSUCCESSFUL);
        }
    };
    void CALLBACK FETCH_DATA(_In_ CONST CF_CALLBACK_INFO* callbackInfo, _In_ CONST CF_CALLBACK_PARAMETERS* callbackParameters)
    {
        try
        {
            //...
                if (DownloadItem(/*call to c++\cli for stream download and copy async to pipe server*/))
                {
                    std::wstring pipename(L"\\\\.\\pipe\\");
                    pipename.append(ci->Id);
                    HANDLE hpipe = CreateFile(pipename.c_str(),
                        GENERIC_READ,
                        0,                  // no sharing 
                        NULL,               // default security attributes
                        OPEN_EXISTING,
                        FILE_FLAG_OVERLAPPED,
                        NULL);              // no template file 
                    if (hpipe != INVALID_HANDLE_VALUE)
                    {
                        if (GetLastError() != ERROR_PIPE_BUSY)
                        {
                            READ_COMPLETION_CONTEXT* readContext = new READ_COMPLETION_CONTEXT();
                            DWORD chunkBufferSize = (ULONG)min(callbackParameters->FetchData.RequiredLength.QuadPart, CHUNKSIZE);

                            std::wstring fullClientPath(callbackInfo->VolumeDosName);
                            fullClientPath.append(callbackInfo->NormalizedPath);
                            readContext->Overlapped.Offset = callbackParameters->FetchData.RequiredFileOffset.LowPart;
                            readContext->Overlapped.OffsetHigh = callbackParameters->FetchData.RequiredFileOffset.HighPart;
                            readContext->CallbackInfo_FileSize = callbackInfo->FileSize;
                            readContext->CallbackInfo_ConnectionKey = callbackInfo->ConnectionKey;
                            readContext->CallbackInfo_TransferKey = callbackInfo->TransferKey;
                            readContext->PipeHandle = hpipe;
                            readContext->StartOffset = callbackParameters->FetchData.RequiredFileOffset;
                            readContext->RemainingLength = callbackParameters->FetchData.RequiredLength;
                            readContext->BufferSize = chunkBufferSize;
                            readContext->FullPath = Utilities::WStringToWCHARP(fullClientPath);
                            readContext->Buffer = new BYTE[chunkBufferSize];

                            if (ReadFileEx(hpipe, readContext->Buffer, chunkBufferSize, &readContext->Overlapped, OverlappedCompletionRoutine)) 
                                if (GetLastError() == S_OK) return;
                            delete readContext;
                        }
                        else CloseHandle(hpipe);
                    }
                }               
            }
        
        catch (...)
        {        
        }
        TransferData(
            callbackInfo->ConnectionKey,
            callbackInfo->TransferKey,
            NULL,
            callbackParameters->FetchData.RequiredFileOffset,
            callbackParameters->FetchData.RequiredLength,
            STATUS_UNSUCCESSFUL);
    }
    void CALLBACK CANCEL_FETCH_DATA(_In_ CONST CF_CALLBACK_INFO* callbackInfo,_In_ CONST CF_CALLBACK_PARAMETERS* callbackParameters)
    {

    }

    HRESULT TransferData(
        _In_ CF_CONNECTION_KEY connectionKey,
        _In_ LARGE_INTEGER transferKey,
        _In_reads_bytes_opt_(length.QuadPart) LPCVOID transferData,
        _In_ LARGE_INTEGER startingOffset,
        _In_ LARGE_INTEGER length,
        _In_ NTSTATUS completionStatus)
    {
        CF_OPERATION_INFO opInfo = { 0 };
        CF_OPERATION_PARAMETERS opParams = { 0 };

        opInfo.StructSize = sizeof(opInfo);
        opInfo.Type = CF_OPERATION_TYPE_TRANSFER_DATA;
        opInfo.ConnectionKey = connectionKey;
        opInfo.TransferKey = transferKey;
        opParams.ParamSize = CF_SIZE_OF_OP_PARAM(TransferData);
        opParams.TransferData.CompletionStatus = completionStatus;
        opParams.TransferData.Buffer = transferData;
        opParams.TransferData.Offset = startingOffset;
        opParams.TransferData.Length = length;
        HRESULT hresult = CfExecute(&opInfo, &opParams);
        return hresult;
    }

    void WINAPI OverlappedCompletionRoutine(
        _In_ DWORD errorCode,
        _In_ DWORD numberOfBytesTransfered,
        _Inout_ LPOVERLAPPED overlapped)
    {
        READ_COMPLETION_CONTEXT* readContext = (READ_COMPLETION_CONTEXT*)overlapped;
        if (errorCode == 0 && !GetOverlappedResult(readContext->PipeHandle, overlapped, &numberOfBytesTransfered, TRUE)) errorCode = GetLastError();

        if (errorCode != 0)
        {
            readContext->Cancel();
            delete readContext;
            return;
        }

        assert(numberOfBytesTransfered != 0);

        LONGLONG total = readContext->CallbackInfo_FileSize.QuadPart;
        LONGLONG completed = readContext->StartOffset.QuadPart + numberOfBytesTransfered;

        Utilities::ApplyTransferStateToFile(readContext->FullPath,
            readContext->CallbackInfo_ConnectionKey,
            readContext->CallbackInfo_TransferKey,
            total,
            completed);

        HRESULT hresult = TransferData(
            readContext->CallbackInfo_ConnectionKey,
            readContext->CallbackInfo_TransferKey,
            errorCode == 0 ? readContext->Buffer : NULL,
            readContext->StartOffset,
            Utilities::LongLongToLargeInteger(numberOfBytesTransfered),
            errorCode);
        if (hresult != S_OK)
        {
            readContext->Cancel();
            delete readContext;
            winrt::check_hresult(hresult);
            return;
        }

        readContext->StartOffset.QuadPart += numberOfBytesTransfered;
        readContext->RemainingLength.QuadPart -= numberOfBytesTransfered;

        if (readContext->RemainingLength.QuadPart > 0)
        {
            DWORD bytesToRead = (DWORD)(min(readContext->RemainingLength.QuadPart, readContext->BufferSize));
            readContext->Overlapped.Offset = readContext->StartOffset.LowPart;
            readContext->Overlapped.OffsetHigh = readContext->StartOffset.HighPart;

            if (!ReadFileEx(readContext->PipeHandle, readContext->Buffer, bytesToRead, &readContext->Overlapped, OverlappedCompletionRoutine))
            {
                readContext->Cancel();
                delete readContext;
            }
        }
        else delete readContext;//done
    }

Edit: After test, fake byteread = 4096 it running successful. Then, how much the min limit of data transfer?

2 Answers

My question in another forum

The chuck size seems required a multiple of 4096. Define this size to (4096*N) will solve the 0x8007017c error.

This is suspected to the issue of offset/length alignment during transfer data operation. This is what the API spec says about these parameters.

OpParams.TransferData.Offset and OpParams.TransferData.Length describe a range in the placeholder to which the sync provider is transferring the data. There is no requirement that the sync provider return all data as requested in one shot. It is also OK for a sync provider to return more data than requested. As an example, the sync provider can decide to over-read, for performance or other reasons. The sync provider can also perform multiple TRANSFER_DATA operations repeatedly as a response to the same FETCH_DATA callback. The only requirement is that both offset and length are 4KB aligned unless the range described ends on the logical file size (EoF), in which case, the length is not required to be 4KB aligned as long as the resulting range ends on or beyond the logical file size.

Related