HResult VirtualizationInstance::StartVirtualizing()

in ProjectedFSLib.Managed.API/VirtualizationInstance.cpp [450:704]


HResult VirtualizationInstance::StartVirtualizing(IRequiredCallbacks^ requiredCallbacks)
{
    if (m_virtualizationContextGc != nullptr)
    {
        return HResult::AlreadyInitialized;
    }

    // Store the provider's implementation of the required callbacks.
    m_requiredCallbacks = requiredCallbacks;

    // Create a handle to this VirtualizationInstance object so that it can be passed to the native
    // lib as the InstanceContext parameter to PrjStartVirtualizing().
    m_virtualizationContextGc = new gcroot<VirtualizationInstance^>();
    *(m_virtualizationContextGc) = this;

    HRESULT startHr = S_OK;
    if (m_apiHelper->UseBetaApi)
    {
        // Query the file system for sector alignment info that CreateWriteBuffer() will need.
        FindBytesPerSectorAndAlignment();

        PRJ_COMMAND_CALLBACKS callbacks;
        m_apiHelper->_PrjCommandCallbacksInit(sizeof(callbacks), &callbacks);

        // Set the native wrappers for the required callbacks.
        callbacks.PrjStartDirectoryEnumeration = reinterpret_cast<PRJ_START_DIRECTORY_ENUMERATION_CB*>(PrjStartDirectoryEnumerationCB);
        callbacks.PrjEndDirectoryEnumeration = reinterpret_cast<PRJ_END_DIRECTORY_ENUMERATION_CB*>(PrjEndDirectoryEnumerationCB);
        callbacks.PrjGetDirectoryEnumeration = reinterpret_cast<PRJ_GET_DIRECTORY_ENUMERATION_CB*>(PrjGetDirectoryEnumerationCB);
        callbacks.PrjGetPlaceholderInformation = reinterpret_cast<PRJ_GET_PLACEHOLDER_INFORMATION_CB*>(PrjGetPlaceholderInformationCB);
        callbacks.PrjGetFileStream = reinterpret_cast<PRJ_GET_FILE_STREAM_CB*>(PrjGetFileStreamCB);

        // Set native wrappers for the optional callbacks if the provider has an implementation for them.

        if (OnQueryFileName != nullptr)
        {
            callbacks.PrjQueryFileName = reinterpret_cast<PRJ_QUERY_FILE_NAME_CB*>(PrjQueryFileNameCB);
        }

        if (OnCancelCommand != nullptr)
        {
            callbacks.PrjCancelCommand = reinterpret_cast<PRJ_CANCEL_COMMAND_CB*>(PrjCancelCommandCB);
        }

        // We set the native wrapper for the notify callback if the provider set at least one OnNotify* property.
        if ((OnNotifyFileOpened != nullptr) ||
            (OnNotifyNewFileCreated != nullptr) ||
            (OnNotifyFileOverwritten != nullptr) ||
            (OnNotifyPreDelete != nullptr) ||
            (OnNotifyPreRename != nullptr) ||
            (OnNotifyPreCreateHardlink != nullptr) ||
            (OnNotifyFileRenamed != nullptr) ||
            (OnNotifyHardlinkCreated != nullptr) ||
            (OnNotifyFileHandleClosedNoModification != nullptr) ||
            (OnNotifyFileHandleClosedFileModifiedOrDeleted != nullptr) ||
            (OnNotifyFilePreConvertToFull != nullptr))
        {
            callbacks.PrjNotifyOperation = reinterpret_cast<PRJ_NOTIFY_OPERATION_CB*>(PrjNotifyOperationCB);
        }

        pin_ptr<const WCHAR> rootPath = PtrToStringChars(m_virtualizationRootPath);

        // Use a temp location to avoid e0158.
        pin_ptr<PRJ_NAMESPACE_VIRTUALIZATION_CONTEXT> tempHandle = &(m_virtualizationContext);
        auto instanceHandle = reinterpret_cast<PRJ_VIRTUALIZATIONINSTANCE_HANDLE*>(tempHandle);

        VIRTUALIZATION_INST_EXTENDED_PARAMETERS extendedParameters;
        memset(&extendedParameters, 0, sizeof(VIRTUALIZATION_INST_EXTENDED_PARAMETERS));

        extendedParameters.Size = sizeof(VIRTUALIZATION_INST_EXTENDED_PARAMETERS);
        extendedParameters.Flags = m_enableNegativePathCache ? PRJ_FLAG_INSTANCE_NEGATIVE_PATH_CACHE : 0;
        extendedParameters.PoolThreadCount = m_poolThreadCount;
        extendedParameters.ConcurrentThreadCount = m_concurrentThreadCount;

        if (m_notificationMappings->Count > 0)
        {
            std::unique_ptr<PRJ_NOTIFICATION_MAPPING[]> nativeNotificationMappings;
            // "Pinning pointers can only be declared as non-static local variables on the stack."
            // https://docs.microsoft.com/en-us/cpp/windows/pin-ptr-cpp-cli
            // And so lets keep a copy of the paths alive rather than trying to pin the incoming strings
            std::vector<std::wstring> notificationPaths;

            notificationPaths.reserve(m_notificationMappings->Count);
            nativeNotificationMappings.reset(new PRJ_NOTIFICATION_MAPPING[m_notificationMappings->Count]);

            int index = 0;
            for each(NotificationMapping^ mapping in m_notificationMappings)
            {
                nativeNotificationMappings[index].NotificationBitMask = static_cast<PRJ_NOTIFY_TYPES>(mapping->NotificationMask);

                pin_ptr<const WCHAR> path = PtrToStringChars(mapping->NotificationRoot);
                notificationPaths.push_back(std::wstring(path));
                nativeNotificationMappings[index].NotificationRoot = notificationPaths.rbegin()->c_str();
                ++index;
            }

            extendedParameters.NotificationMappings = nativeNotificationMappings.get();
            extendedParameters.NumNotificationMappingsCount = m_notificationMappings->Count;

            startHr = m_apiHelper->_PrjStartVirtualizationInstanceEx(rootPath,
                                                                     &callbacks,
                                                                     m_virtualizationContextGc,
                                                                     &extendedParameters,
                                                                     instanceHandle);
        }
        else
        {
            // The caller didn't provide any notification mappings.  Use the non-Ex Start routine to get
            // ProjFS to supply the default notification mask.
            startHr = m_apiHelper->_PrjStartVirtualizationInstance(rootPath,
                                                                   &callbacks,
                                                                   extendedParameters.Flags,
                                                                   0, // ProjFS will default to PRJ_DEFAULT_NOTIFICATION_MASK defined in prjlibp.h
                                                                   extendedParameters.PoolThreadCount,
                                                                   extendedParameters.ConcurrentThreadCount,
                                                                   m_virtualizationContextGc,
                                                                   instanceHandle);
        }
    }
    else
    {
        PRJ_CALLBACKS callbacks;
        memset(&callbacks, 0, sizeof(PRJ_CALLBACKS));

        // Set native wrappers for the required callbacks.
        callbacks.StartDirectoryEnumerationCallback = reinterpret_cast<PRJ_START_DIRECTORY_ENUMERATION_CB*>(PrjStartDirectoryEnumerationCB);
        callbacks.EndDirectoryEnumerationCallback = reinterpret_cast<PRJ_END_DIRECTORY_ENUMERATION_CB*>(PrjEndDirectoryEnumerationCB);
        callbacks.GetDirectoryEnumerationCallback = reinterpret_cast<PRJ_GET_DIRECTORY_ENUMERATION_CB*>(PrjGetDirectoryEnumerationCB);
        callbacks.GetPlaceholderInfoCallback = reinterpret_cast<PRJ_GET_PLACEHOLDER_INFO_CB*>(PrjGetPlaceholderInfoCB);
        callbacks.GetFileDataCallback = reinterpret_cast<PRJ_GET_FILE_DATA_CB*>(PrjGetFileDataCB);

        // Set native wrappers for the optional callbacks if the provider has an implementation for them.

        if (OnQueryFileName != nullptr)
        {
            callbacks.QueryFileNameCallback = reinterpret_cast<PRJ_QUERY_FILE_NAME_CB*>(PrjQueryFileNameCB);
        }

        if (OnCancelCommand != nullptr)
        {
            callbacks.CancelCommandCallback = reinterpret_cast<PRJ_CANCEL_COMMAND_CB*>(PrjCancelCommandCB);
        }

        // We set the native wrapper for the notification callback if the provider set at least one OnNotify* property.
        if ((OnNotifyFileOpened != nullptr) ||
            (OnNotifyNewFileCreated != nullptr) ||
            (OnNotifyFileOverwritten != nullptr) ||
            (OnNotifyPreDelete != nullptr) ||
            (OnNotifyPreRename != nullptr) ||
            (OnNotifyPreCreateHardlink != nullptr) ||
            (OnNotifyFileRenamed != nullptr) ||
            (OnNotifyHardlinkCreated != nullptr) ||
            (OnNotifyFileHandleClosedNoModification != nullptr) ||
            (OnNotifyFileHandleClosedFileModifiedOrDeleted != nullptr) ||
            (OnNotifyFilePreConvertToFull != nullptr))
        {
            callbacks.NotificationCallback = reinterpret_cast<PRJ_NOTIFICATION_CB*>(PrjNotificationCB);
        }

        pin_ptr<const WCHAR> rootPath = PtrToStringChars(m_virtualizationRootPath);
        pin_ptr<PRJ_NAMESPACE_VIRTUALIZATION_CONTEXT> namespaceCtx = &(m_virtualizationContext);

        PRJ_STARTVIRTUALIZING_OPTIONS startOptions;
        memset(&startOptions, 0, sizeof(PRJ_STARTVIRTUALIZING_OPTIONS));

        startOptions.Flags = m_enableNegativePathCache ? PRJ_FLAG_USE_NEGATIVE_PATH_CACHE : PRJ_FLAG_NONE;
        startOptions.PoolThreadCount = m_poolThreadCount;
        startOptions.ConcurrentThreadCount = m_concurrentThreadCount;

        // These need to stay in scope until we call PrjStartVirtualizing
        std::unique_ptr<PRJ_NOTIFICATION_MAPPING[]> nativeNotificationMappings;
        std::vector<std::wstring> notificationPaths;

        if (m_notificationMappings->Count > 0)
        {
            // "Pinning pointers can only be declared as non-static local variables on the stack."
            // https://docs.microsoft.com/en-us/cpp/windows/pin-ptr-cpp-cli
            // And so lets keep a copy of the paths alive rather than trying to pin the incoming strings

            notificationPaths.reserve(m_notificationMappings->Count);
            nativeNotificationMappings.reset(new PRJ_NOTIFICATION_MAPPING[m_notificationMappings->Count]);

            int index = 0;
            for each(NotificationMapping^ mapping in m_notificationMappings)
            {
                nativeNotificationMappings[index].NotificationBitMask = static_cast<PRJ_NOTIFY_TYPES>(mapping->NotificationMask);

                pin_ptr<const WCHAR> path = PtrToStringChars(mapping->NotificationRoot);
                notificationPaths.push_back(std::wstring(path));
                nativeNotificationMappings[index].NotificationRoot = notificationPaths.rbegin()->c_str();
                ++index;
            }

            startOptions.NotificationMappings = nativeNotificationMappings.get();
            startOptions.NotificationMappingsCount = m_notificationMappings->Count;
        }
        else
        {
            // ProjFS will supply a default notification mask for the root if there are no mappings.
            startOptions.NotificationMappingsCount = 0;
        }

        startHr = m_apiHelper->_PrjStartVirtualizing(rootPath,
                                                     &callbacks,
                                                     m_virtualizationContextGc,
                                                     &startOptions,
                                                     namespaceCtx);
    }

    if (FAILED(startHr))
    {
        return static_cast<HResult>(startHr);
    }

    // Store the virtualization instance ID.
    GUID instanceId = {};
    Guid^ instanceIDRef;
    HRESULT getInfoHr = S_OK;
    if (m_apiHelper->UseBetaApi)
    {
        getInfoHr = m_apiHelper->_PrjGetVirtualizationInstanceIdFromHandle(reinterpret_cast<PRJ_VIRTUALIZATIONINSTANCE_HANDLE>(m_virtualizationContext),
                                                                           &instanceId);
    }
    else
    {
        PRJ_VIRTUALIZATION_INSTANCE_INFO instanceInfo = {};
        getInfoHr = m_apiHelper->_PrjGetVirtualizationInstanceInfo(m_virtualizationContext,
                                                                   &instanceInfo);
        instanceId = instanceInfo.InstanceID;
    }

    // If we couldn't get the instance ID, shut the instance down and return error (this is super
    // unlikely; it could only happen if somehow the instance successfully started above, but its
    // info is corrupt).
    if (FAILED(getInfoHr))
    {
        StopVirtualizing();
        return static_cast<HResult>(getInfoHr);
    }

    instanceIDRef = gcnew Guid(instanceId.Data1,
                               instanceId.Data2,
                               instanceId.Data3,
                               instanceId.Data4[0],
                               instanceId.Data4[1],
                               instanceId.Data4[2],
                               instanceId.Data4[3],
                               instanceId.Data4[4],
                               instanceId.Data4[5],
                               instanceId.Data4[6],
                               instanceId.Data4[7]);

    m_virtualizationInstanceID = *instanceIDRef;

    return HResult::Ok;
}