Esempio n. 1
0
PSTORAGE_DESCRIPTOR_HEADER
pStorageQueryProperty(
	HANDLE hDevice,
	STORAGE_PROPERTY_ID PropertyId,
	STORAGE_QUERY_TYPE  QueryType)
{
	STORAGE_PROPERTY_QUERY spquery;
	::ZeroMemory(&spquery, sizeof(spquery));
	spquery.PropertyId = PropertyId;
	spquery.QueryType = QueryType;

	DWORD bufferLength = sizeof(STORAGE_DESCRIPTOR_HEADER);
	XTL::AutoProcessHeapPtr<STORAGE_DESCRIPTOR_HEADER> buffer =
		static_cast<STORAGE_DESCRIPTOR_HEADER*>(
			::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, bufferLength));
	if (buffer.IsInvalid())
	{
		return NULL;
	}

	DWORD returnedLength;
	BOOL fSuccess = ::DeviceIoControl(
		hDevice, IOCTL_STORAGE_QUERY_PROPERTY, 
		&spquery, sizeof(spquery),
		buffer, bufferLength,
		&returnedLength, NULL);
	if (!fSuccess)
	{
		XTLTRACE_ERR("IOCTL_STORAGE_QUERY_PROPERTY(HEADER) failed.\n");
		return NULL;
	}

	// We only retrived the header, now we reallocate the buffer
	// required for the actual query
	bufferLength = buffer->Size; 
	XTL::AutoProcessHeapPtr<STORAGE_DESCRIPTOR_HEADER> newBuffer =
		static_cast<STORAGE_DESCRIPTOR_HEADER*>(
			::HeapReAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, buffer, bufferLength));
	if (newBuffer.IsInvalid())
	{
		return NULL;
	}
	// set the buffer with the new buffer
	buffer.Detach();
	buffer = newBuffer.Detach();
	
	// now we can query the actual property with the proper size
	fSuccess = ::DeviceIoControl(
		hDevice, IOCTL_STORAGE_QUERY_PROPERTY, 
		&spquery, sizeof(spquery),
		buffer, bufferLength,
		&returnedLength, NULL);
	if (!fSuccess)
	{
		XTLTRACE_ERR("IOCTL_STORAGE_QUERY_PROPERTY(DATA) failed.\n");
		return NULL;
	}

	return buffer.Detach();
}
Esempio n. 2
0
LPSTR
pReadEulaFromFile(
	MSIHANDLE hInstall)
{
	XTL::AutoProcessHeapPtr<TCHAR> sourceDir;

	UINT ret = pMsiGetProperty(hInstall, _T("SourceDir"), &sourceDir, NULL);
	
	if (ERROR_SUCCESS != ret)
	{
		pMsiLogMessage(
			hInstall, 
			_T("EULACA: MsiGetProperty(SourceDir) failed, error=0x%X"),
			ret);

		return NULL;
	}

	//
	// EULA from the property EulaFileName
	//

	XTL::AutoProcessHeapPtr<CHAR> eulaText = 
		pReadEulaFromFileEx(hInstall, sourceDir, _T("EulaFileName"));

	if (NULL == static_cast<LPSTR>(eulaText))
	{
		// 
		// try again with EulaFallbackFileName
		//
		eulaText = pReadEulaFromFileEx(
			hInstall, sourceDir, _T("EulaFallbackFileName"));

		if (NULL == static_cast<LPSTR>(eulaText))
		{
			//
			// Once more with EulaFallbackFileName2
			//
			eulaText = pReadEulaFromFileEx(
				hInstall, sourceDir, _T("EulaFallbackFileName2"));
		}
	}

	//
	// If eulaText is still NULL, every attempt has been failed.
	//
	if (NULL == static_cast<LPSTR>(eulaText))
	{
		pMsiLogMessage(
			hInstall, 
			_T("EULACA: EULA files are not available!"));

		return NULL;
	}

	//
	// Now we have the EULA Text!
	//
	return eulaText.Detach();
}
Esempio n. 3
0
PDRIVE_LAYOUT_INFORMATION_EX
pDiskGetDriveLayoutEx(HANDLE hDevice)
{
	DWORD returnedLength; // always returns 0
	DWORD bufferLength = sizeof(DRIVE_LAYOUT_INFORMATION_EX);

	XTL::AutoProcessHeapPtr<DRIVE_LAYOUT_INFORMATION_EX> buffer =
		static_cast<DRIVE_LAYOUT_INFORMATION_EX*>(
			::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, bufferLength));

	BOOL fSuccess = ::DeviceIoControl(
		hDevice, IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
		NULL, 0,
		buffer, bufferLength,
		&returnedLength, NULL);
	while (!fSuccess && ERROR_INSUFFICIENT_BUFFER == ::GetLastError())
	{
		// To determine the size of output buffer that is required, caller
		// should send this IOCTL request in a loop. Every time the
		// storage stack rejects the IOCTL with an error message
		// indicating that the buffer was too small, caller should double
		// the buffer size.
		bufferLength += bufferLength;
		XTL::AutoProcessHeapPtr<DRIVE_LAYOUT_INFORMATION_EX> newBuffer = 
			static_cast<DRIVE_LAYOUT_INFORMATION_EX*>(
				::HeapReAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, buffer, bufferLength));
		if (newBuffer.IsInvalid())
		{
			// buffer is still valid in case of failure
			// buffer will be released on return.
			return NULL;
		}
		// buffer points to the non-valid memory, so invalidate it now
		buffer.Detach();
		// transfer the data from newBuffer to buffer
		buffer = newBuffer.Detach();
		// query again
		fSuccess = ::DeviceIoControl(
			 hDevice, IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
			 NULL, 0,
			 buffer, bufferLength,
			 &returnedLength, NULL);
	}
	// returns the buffer
	return buffer.Detach();
}
Esempio n. 4
0
LPSTR
pReadEulaFromFileEx(
	MSIHANDLE hInstall,
	LPCTSTR SourceDir,
	LPCTSTR PropertyName)
{
	TCHAR fullPath[MAX_PATH];

	XTL::AutoProcessHeapPtr<TCHAR> eulaFileName;

	UINT ret = pMsiGetProperty(hInstall, PropertyName, &eulaFileName, NULL);

	if (ERROR_SUCCESS != ret)
	{
		pMsiLogMessage(
			hInstall, 
			_T("EULACA: MsiGetProperty(%s) failed, error=0x%X"),
			PropertyName,
			ret);

		return NULL;
	}

	StringCchCopy(fullPath, MAX_PATH, SourceDir);
	StringCchCat(fullPath, MAX_PATH, eulaFileName);

	pMsiLogMessage(
		hInstall, 
		_T("EULACA: EulaFile=%s"),
		fullPath);

	//
	// RTF file is an ANSI text file not unicode.
	// RTF has the CodePage tag inside, so we don't have to 
	// worry about displaying non-English text.
	// 

	XTL::AutoProcessHeapPtr<CHAR> eulaText = pReadTextFromFile(fullPath);

	if (NULL == (LPSTR) eulaText)
	{
		pMsiLogMessage(
			hInstall, 
			_T("EULACA: ReadTextFromFile(%s) failed, error=0x%X"),
			fullPath,
			GetLastError());

		return NULL;
	}

	pMsiLogMessage(
		hInstall, 
		_T("EULACA: Read EULA text from %s"),
		fullPath);

	return eulaText.Detach();
}
Esempio n. 5
0
LPSTR
pReadTextFromFile(
	LPCTSTR szFileName)
{
	XTL::AutoFileHandle hFile = CreateFile(
		szFileName, 
		GENERIC_READ, 
		FILE_SHARE_READ, 
		NULL, 
		OPEN_EXISTING, 
		FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
		NULL);
	
	if (INVALID_HANDLE_VALUE == static_cast<HANDLE>(hFile))
	{
		return NULL;
	}
	
	LARGE_INTEGER fileSize;
	BOOL fSuccess = GetFileSizeEx(hFile, &fileSize);

	if (!fSuccess)
	{
		return NULL;
	}
	
	DWORD cbToRead = fileSize.LowPart;

	XTL::AutoProcessHeapPtr<CHAR> lpBuffer = (LPSTR) 
		::HeapAlloc(
			GetProcessHeap(), 
			HEAP_ZERO_MEMORY, 
			cbToRead);

	if (NULL == static_cast<LPSTR>(lpBuffer))
	{
		return NULL;
	}

	DWORD cbRead;

	fSuccess = ReadFile(
		hFile,
		lpBuffer,
		cbToRead,
		&cbRead,
		NULL);

	if (!fSuccess)
	{
		return NULL;
	}		 
	
	return lpBuffer.Detach();
}
Esempio n. 6
0
LPTSTR
pGetVolumeInstIdListForDisk(
    LPCTSTR DiskInstId)
{
    // Disk and volumes are removal relations.
    XTL::AutoProcessHeapPtr<TCHAR> VolumeInstIdList;
    CONFIGRET ret = pCMGetDeviceIDList(
                        &VolumeInstIdList,
                        DiskInstId,
                        CM_GETIDLIST_FILTER_REMOVALRELATIONS);
    if (CR_SUCCESS != ret)
    {
        XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR,
                  "pCMGetDeviceIDList failed, cret=0x%X\n", ret);
        ::SetLastError(ConfigRetToWin32Error(ret));
        return NULL;
    }
    return VolumeInstIdList.Detach();
}
Esempio n. 7
0
HRESULT
pGetVolumeMountPointForPathW(
	__in LPCWSTR Path,
	__out LPWSTR* MountPoint)
{
	if (NULL == MountPoint)
	{
		return E_POINTER;
	}

	*MountPoint = NULL;

	// TODO: It is possible to be the path is more than MAX_PATH
	// in case of supporting unicode file names up to 65534
	DWORD mountPointLength = MAX_PATH;
	XTL::AutoProcessHeapPtr<TCHAR> mountPoint = 
		static_cast<TCHAR*>(
			::HeapAlloc(
				::GetProcessHeap(), 
				HEAP_ZERO_MEMORY,
				mountPointLength * sizeof(WCHAR)));

	if (mountPoint.IsInvalid())
	{
		return E_OUTOFMEMORY;
	}

	if (!GetVolumePathName(Path, mountPoint, mountPointLength))
	{
		HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
		XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR,
			"GetVolumePathName(%ls) failed, hr=0x%X\n",
			Path, hr);
		return hr;
	}

	XTLTRACE2(NdasVolTrace, TRACE_LEVEL_INFORMATION,
		"Path(%ls) is mounted from %s.\n", Path, mountPoint);

	*MountPoint = mountPoint.Detach();

	return S_OK;
}
Esempio n. 8
0
LPTSTR
pGetDeviceSymbolicLinkList(
    LPCGUID InterfaceClassGuid,
    DEVINSTID DevInstId,
    ULONG Flags /*= CM_GET_DEVICE_INTERFACE_LIST_PRESENT*/)
{
    XTL::AutoProcessHeapPtr<TCHAR> SymbolicLinkList;
    CONFIGRET ret = pCMGetDeviceInterfaceList(
                        &SymbolicLinkList,
                        const_cast<LPGUID>(InterfaceClassGuid),
                        DevInstId,
                        Flags);
    if (CR_SUCCESS != ret)
    {
        XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR,
                  "pCMGetDeviceInterfaceList failed, cret=0x%X\n", ret);
        ::SetLastError(ConfigRetToWin32Error(ret));
        return NULL;
    }
    return SymbolicLinkList.Detach();
}
Esempio n. 9
0
//
// Returns the symbolic link name of the disk.
//
// Caller should free the non-null returned pointer
// with HeapFree(GetProcessHeap(),...)
//
LPTSTR
pGetChildDevInstIdsForNdasScsiLocation(
    const NDAS_SCSI_LOCATION* NdasScsiLocation)
{
    DEVINST NdasScsiDevInst;
    if (!pFindNdasScsiDevInst(&NdasScsiDevInst, NdasScsiLocation->SlotNo))
    {
        XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR,
                  "FindNdasScsiDevInst failed, error=0x%X\n", GetLastError());
        return NULL;
    }

    TCHAR NdasScsiInstanceId[MAX_DEVICE_ID_LEN];
    CONFIGRET ret = ::CM_Get_Device_ID(
                        NdasScsiDevInst,
                        NdasScsiInstanceId,
                        RTL_NUMBER_OF(NdasScsiInstanceId),
                        0);
    if (CR_SUCCESS != ret)
    {
        XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR,
                  "CM_Get_Device_ID failed, cret=0x%X\n", ret);
        return NULL;
    }

    XTL::AutoProcessHeapPtr<TCHAR> ChildDevInstIdList;
    ret = pCMGetDeviceIDList(
              &ChildDevInstIdList,
              NdasScsiInstanceId,
              CM_GETIDLIST_FILTER_BUSRELATIONS);
    if (CR_SUCCESS != ret)
    {
        XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR,
                  "pCMGetDeviceIDList failed, cret=0x%X\n", ret);
        ::SetLastError(ConfigRetToWin32Error(ret));
        return NULL;
    }

    return ChildDevInstIdList.Detach();
}
Esempio n. 10
0
LPTSTR
pMsiGetProperty(
    MSIHANDLE hInstall, 
    LPCTSTR szPropertyName,
    LPDWORD pcch)
{
	HANDLE hHeap = ::GetProcessHeap();
	XTL::AutoProcessHeapPtr<TCHAR> pszValue;
    DWORD cchValue = 0;
    
    UINT msiret = MsiGetProperty(hInstall, szPropertyName, _T(""), &cchValue);
    
    if (ERROR_SUCCESS != msiret && ERROR_MORE_DATA != msiret)
    {
        return NULL;
    }
    
    if (ERROR_MORE_DATA == msiret)
    {
        ++cchValue;
        pszValue = (LPTSTR) HeapAlloc(GetProcessHeap(), 0, cchValue * sizeof(TCHAR));
        if (NULL == (LPTSTR) pszValue)
        {
            return NULL;
        }
        msiret = MsiGetProperty(hInstall, szPropertyName, pszValue, &cchValue);
        if (ERROR_SUCCESS != msiret)
        {
            return NULL;
        }
    }

    if (NULL != pcch)
    {
        *pcch = cchValue;
    }
    
    return pszValue.Detach();
}
Esempio n. 11
0
LPTSTR
pMsiGetSourcePath(
    MSIHANDLE hInstall, 
    LPCTSTR szFolder,
    LPDWORD pcch)
{
    XTL::AutoProcessHeapPtr<TCHAR> pszValue;
    DWORD cchValue = 0;

    UINT msiret = MsiGetSourcePath(hInstall, szFolder, _T(""), &cchValue);

    if (ERROR_MORE_DATA == msiret)
    {
        ++cchValue;
        pszValue = (LPTSTR) HeapAlloc(
            GetProcessHeap(), 
            0, 
            cchValue * sizeof(TCHAR));

        if (NULL == (LPTSTR) pszValue)
        {
            return NULL;
        }

        msiret = MsiGetSourcePath(hInstall, szFolder, pszValue, &cchValue);
    }

    if (ERROR_SUCCESS != msiret)
    {
        return NULL;
    }

    if (NULL != pcch)
    {
        *pcch = cchValue;
    }
    
    return pszValue.Detach();
}
Esempio n. 12
0
LPTSTR
pGetDiskForNdasScsiLocationEx(
    const NDAS_SCSI_LOCATION* NdasScsiLocation,
    BOOL SymbolicLinkOrDevInstId)
{
    // Get the child device instances of the NDAS SCSI pdo
    XTL::AutoProcessHeapPtr<TCHAR> ChildDevInstIdList =
        pGetChildDevInstIdsForNdasScsiLocation(NdasScsiLocation);

    // Find the disk of TargetID and LUN for all child device instances
    for (LPCTSTR ChildDevInstId = ChildDevInstIdList;
            ChildDevInstId && *ChildDevInstId;
            ChildDevInstId += ::lstrlen(ChildDevInstId) + 1)
    {
        XTLTRACE2(NdasVolTrace, TRACE_LEVEL_VERBOSE,
                  _T("ChildDevInstId:%s\n"), ChildDevInstId);

        // Ensure that the child really is present (not ghosted)
        DEVINST ChildDevInst;
        CONFIGRET ret = ::CM_Locate_DevNode(
                            &ChildDevInst,
                            const_cast<DEVINSTID>(ChildDevInstId),
                            CM_LOCATE_DEVNODE_NORMAL);
        if (CR_SUCCESS != ret)
        {
            XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR,
                      "CM_Locate_DevNode failed, cret=0x%X\n", ret);
            continue;
        }

        // Query the Disk Class Interface, if there are no disk class
        // interface, it is not of a disk class.
        XTL::AutoProcessHeapPtr<TCHAR> SymLinkList;
        ret = pCMGetDeviceInterfaceList(
                  &SymLinkList,
                  &DiskClassGuid,
                  const_cast<DEVINSTID>(ChildDevInstId),
                  CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
        if (CR_SUCCESS != ret)
        {
            XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR,
                      "pCMGetDeviceInterfaceList failed, cret=0x%X\n", ret);
            continue;
        }

        // Returned list contains the list of the device file names (or
        // symbolic links) which we can open with CreateFile for IO_CTLs
        for (LPCTSTR DeviceName = SymLinkList;
                DeviceName && *DeviceName;
                DeviceName += ::lstrlen(DeviceName) + 1)
        {
            XTLTRACE2(NdasVolTrace, TRACE_LEVEL_VERBOSE,
                      _T("Device:%s\n"), DeviceName);
            XTL::AutoFileHandle hDevice = ::CreateFile(
                                              DeviceName,
                                              GENERIC_READ,
                                              FILE_SHARE_READ | FILE_SHARE_WRITE,
                                              NULL,
                                              OPEN_EXISTING,
                                              0,
                                              NULL);
            if (hDevice.IsInvalid())
            {
                XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR,
                          _T("CreateFile(%s) failed, error=0x%X\n"),
                          DeviceName, GetLastError());
                continue;
            }
            // Query the SCSI Address and compare TargetID and LUN to
            // compare them with those of NDAS SCSI location.
            SCSI_ADDRESS scsiAddress;
            if (!pScsiGetAddress(hDevice, &scsiAddress))
            {
                XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR,
                          _T("GetScsiAddress(%s) failed, error=0x%X\n"),
                          DeviceName, GetLastError());
                continue;
            }
            if (NdasScsiLocation->TargetID == scsiAddress.TargetId &&
                    NdasScsiLocation->LUN == scsiAddress.Lun)
            {
                // We found the target disk, and we create a buffer to
                // return. If SymbolicLinkOrDevInstId is non-zero (TRUE),
                // we will returns SymbolicLink, otherwise, we will return the DevInstId
                if (SymbolicLinkOrDevInstId)
                {
                    DWORD TargetLength = (::lstrlen(DeviceName) + 1) * sizeof(TCHAR);
                    XTL::AutoProcessHeapPtr<TCHAR> TargetDeviceName =
                        static_cast<LPTSTR>(
                            ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, TargetLength));
                    if (TargetDeviceName.IsInvalid())
                    {
                        XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR,
                                  "HeapAlloc failed, byets=%d\n", TargetLength);
                        return NULL;
                    }
                    ::CopyMemory(TargetDeviceName, DeviceName, TargetLength);
                    return TargetDeviceName.Detach();
                }
                else
                {
                    DWORD TargetLength = (::lstrlen(ChildDevInstId) + 1) * sizeof(TCHAR);
                    XTL::AutoProcessHeapPtr<TCHAR> TargetDevInstId =
                        static_cast<LPTSTR>(
                            ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, TargetLength));
                    if (TargetDevInstId.IsInvalid())
                    {
                        XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR,
                                  "HeapAlloc failed, byets=%d\n", TargetLength);
                        return NULL;
                    }
                    ::CopyMemory(TargetDevInstId, ChildDevInstId, TargetLength);
                    return TargetDevInstId.Detach();
                }
            }
        }
    }

    return NULL;
}
Esempio n. 13
0
CONFIGRET
pCMGetDeviceIDList(
    LPTSTR* RelDevInstIdList,
    LPCTSTR DevInstId,
    ULONG Flags)
{
    XTLTRACE2(NdasVolTrace, TRACE_LEVEL_INFORMATION,
              _T("DevInstId=%s,Flags=%08X\n"), DevInstId, Flags);

    *RelDevInstIdList = NULL;

    ULONG bufferLength;
    CONFIGRET ret = ::CM_Get_Device_ID_List_Size(
                        &bufferLength,
                        DevInstId,
                        Flags);
    if (CR_SUCCESS != ret)
    {
        XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR,
                  "CM_Get_Device_ID_List_Size failed with cret=0x%X.\n", ret);
        return ret;
    }

    XTLTRACE2(NdasVolTrace, TRACE_LEVEL_VERBOSE,
              "RequiredBufferSize=%d chars\n", bufferLength);
    if (0 == bufferLength)
    {
        TCHAR* empty = static_cast<TCHAR*>(
                           ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, 2 * sizeof(TCHAR)));
        *RelDevInstIdList = empty;
        return CR_SUCCESS;
    }

    XTL::AutoProcessHeapPtr<TCHAR> buffer = static_cast<TCHAR*>(
            ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, bufferLength * sizeof(TCHAR)));
    if (buffer.IsInvalid())
    {
        XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR,
                  "HeapAlloc failed, bytes=%d\n", bufferLength * sizeof(TCHAR));
        return CR_OUT_OF_MEMORY;
    }

    ret = ::CM_Get_Device_ID_List(
              DevInstId,
              buffer,
              bufferLength,
              Flags);
    if (CR_SUCCESS != ret)
    {
        XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR,
                  "CM_Get_Device_ID_List failed with cret=0x%X.\n", ret);
        return ret;
    }

    *RelDevInstIdList = buffer.Detach();

    XTLTRACE2(NdasVolTrace, TRACE_LEVEL_INFORMATION,
              _T("RelDevInstIdList=%s\n"), *RelDevInstIdList);

    return CR_SUCCESS;
}
Esempio n. 14
0
HRESULT
pGetVolumeDeviceNameForMountPointW(
	__in LPCWSTR VolumeMountPoint,
	__out LPWSTR* VolumeDeviceName)
{
	// The lpszVolumeMountPoint parameter may be a drive letter with 
	// appended backslash (\), such as "D:\". Alternatively, it may be 
	// a path to a volume mount point, again with appended backslash (\), 
	// such as "c:\mnt\edrive\".

	// A reasonable size for the buffer to accommodate the largest possible 
	// volume name is 50 characters --> wrong 100

	HRESULT hr;

	XTLASSERT(NULL != VolumeDeviceName);
	if (NULL == VolumeDeviceName)
	{
		return E_POINTER;
	}

	*VolumeDeviceName = NULL;

	const DWORD MAX_VOLUMENAME_LEN = 50;
	DWORD volumeDeviceNameLength = MAX_VOLUMENAME_LEN;
	
	XTL::AutoProcessHeapPtr<TCHAR> volumeDeviceName = 
		static_cast<TCHAR*>(
			HeapAlloc(
				GetProcessHeap(), 
				HEAP_ZERO_MEMORY, 
				volumeDeviceNameLength * sizeof(TCHAR)));

	if (volumeDeviceName.IsInvalid())
	{
		XTLTRACE2(NdasVolTrace, 0,
			"HeapAlloc for %d bytes failed.\n", volumeDeviceNameLength);
		return E_OUTOFMEMORY;
	}

	BOOL success = GetVolumeNameForVolumeMountPoint(
		VolumeMountPoint, 
		volumeDeviceName, 
		volumeDeviceNameLength);

	if (!success)
	{
		hr = HRESULT_FROM_WIN32(GetLastError());
		XTLTRACE2(NdasVolTrace, 0,
			"GetVolumeNameForVolumeMountPoint(%ls) failed, hr=0x%X\n", 
			VolumeMountPoint, hr);
		return hr;
	}

	// Volume Name is a format of \\?\Volume{XXXX}\ with trailing backslash
	// Volume device name is that of \\.\Volume{XXXX} without trailing backslash

	_ASSERTE(_T('\\') == volumeDeviceName[0]);
	_ASSERTE(_T('\\') == volumeDeviceName[1]);
	_ASSERTE(_T('?')  == volumeDeviceName[2]);
	_ASSERTE(_T('\\') == volumeDeviceName[3]);

	if (_T('\\') == volumeDeviceName[0] &&
		_T('\\') == volumeDeviceName[1] &&
		_T('?') == volumeDeviceName[2] &&
		_T('\\') == volumeDeviceName[3])
	{
		// replace ? to .
		volumeDeviceName[2] = _T('.');
	}

	// remove trailing backslash
	pRemoveTrailingBackslash(volumeDeviceName);

	XTLTRACE2(NdasVolTrace, 2, 
		"VolumeMountPoint(%ls)=>Volume(%ls)\n", 
		VolumeMountPoint, volumeDeviceName);

	*VolumeDeviceName = volumeDeviceName.Detach();

	return S_OK;
}
Esempio n. 15
0
CNdasUnitDevice*
CNdasUnitDeviceCreator::CreateUnitDiskDevice()
{
    //
    // Read DIB
    //
    // Read DIB should success, even if the unit disk does not contain
    // the DIB. If it fails, there should be some communication error.
    //

    XTL::AutoProcessHeapPtr<NDAS_DIB_V2> pDIBv2;
    XTL::AutoProcessHeapPtr<BLOCK_ACCESS_CONTROL_LIST> pBACL;

    //
    // ReadDIB is to allocate pDIBv2
    //
    BOOL fSuccess = ReadDIB(&pDIBv2);

    // attach to auto heap

    if (!fSuccess)
    {
        XTLTRACE2(NDASSVC_NDASUNITDEVICE, TRACE_LEVEL_ERROR,
                  "ReadDIB failed, error=0x%X\n", GetLastError());

        XTLTRACE2(NDASSVC_NDASUNITDEVICE, TRACE_LEVEL_ERROR,
                  "Creating unit disk device instance failed.\n");

        return _CreateUnknownUnitDiskDevice(NDAS_UNITDEVICE_ERROR_HDD_READ_FAILURE);
    }

    if(0 != pDIBv2->BACLSize)
    {
        fSuccess = ReadBACL(&pBACL, pDIBv2->BACLSize);
        if (!fSuccess)
        {
            XTLTRACE2(NDASSVC_NDASUNITDEVICE, TRACE_LEVEL_ERROR,
                      "ReadBACL failed, error=0x%X\n", GetLastError());

            XTLTRACE2(NDASSVC_NDASUNITDEVICE, TRACE_LEVEL_ERROR,
                      "Creating unit disk device instance failed.\n");

            return _CreateUnknownUnitDiskDevice(NDAS_UNITDEVICE_ERROR_HDD_READ_FAILURE);
        }
    }

    NDAS_UNITDEVICE_DISK_TYPE diskType = pGetNdasUnitDiskTypeFromDIBv2(pDIBv2);

    if (NDAS_UNITDEVICE_DISK_TYPE_UNKNOWN == diskType)
    {
        //
        // Error! Invalid media type
        //
        XTLTRACE2(NDASSVC_NDASUNITDEVICE, TRACE_LEVEL_ERROR,
                  "Media type in DIBv2 is invalid, mediaType=0x%X\n", pDIBv2->iMediaType);

        //
        // we should create generic unknown unit disk device
        //
        return _CreateUnknownUnitDiskDevice(NDAS_UNITDEVICE_ERROR_HDD_UNKNOWN_LDTYPE);
    }

    // return NULL;
    LPVOID pAddTargetInfo = NULL;
    NDAS_RAID_META_DATA Rmd = {0};

    if (NMT_RAID1 == pDIBv2->iMediaType ||
            NMT_RAID4 == pDIBv2->iMediaType)
    {
        //
        // These types are not used anymore, however,
        // for compatibility reasons, we retain these codes
        //
        pAddTargetInfo = HeapAlloc(
                             GetProcessHeap(),
                             HEAP_ZERO_MEMORY,
                             sizeof(NDAS_LURN_RAID_INFO_V1));

        PNDAS_LURN_RAID_INFO_V1 raidInfo =
            static_cast<PNDAS_LURN_RAID_INFO_V1>(pAddTargetInfo);
        raidInfo->SectorsPerBit = pDIBv2->iSectorsPerBit;
        raidInfo->SectorBitmapStart = m_udinfo.SectorCount.QuadPart - 0x0f00;
        raidInfo->SectorInfo = m_udinfo.SectorCount.QuadPart - 0x0002;
        raidInfo->SectorLastWrittenSector = m_udinfo.SectorCount.QuadPart - 0x1000;

    }
    else if (NMT_RAID1R2 == pDIBv2->iMediaType ||
             NMT_RAID4R2 == pDIBv2->iMediaType ||
             NMT_RAID1R3 == pDIBv2->iMediaType ||
             NMT_RAID4R3 == pDIBv2->iMediaType)
    {
        //
        // do not allocate with "new INFO_RAID"
        // destructor will delete with "HeapFree"
        //

        pAddTargetInfo = HeapAlloc(
                             GetProcessHeap(),
                             HEAP_ZERO_MEMORY,
                             sizeof(NDAS_LURN_RAID_INFO_V2));

        PNDAS_LURN_RAID_INFO_V2 pIR =
            reinterpret_cast<PNDAS_LURN_RAID_INFO_V2>(pAddTargetInfo);

        fSuccess = m_devComm.ReadDiskBlock(
                       reinterpret_cast<PBYTE>(&Rmd),
                       NDAS_BLOCK_LOCATION_RMD,
                       1);
        if (!fSuccess)
        {
            XTLTRACE2(NDASSVC_NDASUNITDEVICE, TRACE_LEVEL_ERROR, "Reading RMD failed: ");
            XTLTRACE2(NDASSVC_NDASUNITDEVICE, TRACE_LEVEL_ERROR, "Creating unit disk device instance failed.\n");
            return _CreateUnknownUnitDiskDevice(NDAS_UNITDEVICE_ERROR_HDD_READ_FAILURE);
        }
        if (Rmd.Signature != NDAS_RAID_META_DATA_SIGNATURE) {
            XTLTRACE2(NDASSVC_NDASUNITDEVICE, TRACE_LEVEL_ERROR, "RMD signature mismatch.");
            XTLTRACE2(NDASSVC_NDASUNITDEVICE, TRACE_LEVEL_ERROR, "Creating unit disk device instance failed.\n");
            return _CreateUnknownUnitDiskDevice(NDAS_UNITDEVICE_ERROR_HDD_UNKNOWN_LDTYPE);
        }

        pIR->SectorsPerBit = pDIBv2->iSectorsPerBit;
        pIR->nSpareDisk = pDIBv2->nSpareCount;
        // To fix: Use RaidSetId and ConfigSetId from NdasOpGetRaidInfo
        ::CopyMemory(&pIR->RaidSetId, &Rmd.RaidSetId, sizeof(pIR->RaidSetId));
        ::CopyMemory(&pIR->ConfigSetId, &Rmd.ConfigSetId, sizeof(pIR->ConfigSetId));
    }

    UINT64 ulUserBlocks = static_cast<UINT64>(pDIBv2->sizeUserSpace);
    DWORD ldSequence = pDIBv2->iSequence;
    NDAS_LOGICALDEVICE_GROUP ldGroup = {0};
    ldGroup.Type = pGetNdasLogicalDeviceTypeFromDIBv2(pDIBv2);
    ldGroup.nUnitDevices = pDIBv2->nDiskCount + pDIBv2->nSpareCount;
    ldGroup.nUnitDevicesSpare = pDIBv2->nSpareCount;
//	::CopyMemory(&ldGroup.RaidSetId, &Rmd.RaidSetId, sizeof(GUID));
//	::CopyMemory(&ldGroup.ConfigSetId, &Rmd.ConfigSetId, sizeof(GUID));
    for(DWORD i=0; i<MAX_NDAS_LOGICALDEVICE_GROUP_MEMBER; i++) {
        ldGroup.DeviceHwVersions[i] = pDIBv2->UnitDiskInfos[i].HwVersion;
    }

    //
    // Consistency check for DIB
    //
    // 1. DIB should contain an entry for this unit device
    //    wich m_pDevice->GetDeviceId() and m_dwUnitNo;
    //
    // Otherwise, DIB is invalid and reported as Single
    //

    for(DWORD i = 0; i < ldGroup.nUnitDevices; i++)
    {
        CopyMemory(
            &ldGroup.UnitDevices[i].DeviceId,
            pDIBv2->UnitDisks[i].MACAddr,
            sizeof(NDAS_DEVICE_ID));
        ldGroup.UnitDevices[i].UnitNo = pDIBv2->UnitDisks[i].UnitNumber;
    }

    //
    // Read CONTENT_ENCRYPT
    //
    // Read CONTENT_ENCRYPT should success, even if the unit disk does not contain
    // the CONTENT_ENCRYPT. If it fails, there should be some communication error.
    //

    NDAS_CONTENT_ENCRYPT_BLOCK ceb = {0};
    NDAS_CONTENT_ENCRYPT ce = {0};

    fSuccess = ReadContentEncryptBlock(&ceb);

    if (!fSuccess)
    {
        XTLTRACE2(NDASSVC_NDASUNITDEVICE, TRACE_LEVEL_ERROR,
                  "ReadContentEncryptBlock failed, error=0x%X\n", GetLastError());

        XTLTRACE2(NDASSVC_NDASUNITDEVICE, TRACE_LEVEL_ERROR,
                  "Creating unit disk device instance failed.\n");

        (VOID) ::NdasLogEventError2(EVT_NDASSVC_ERROR_CEB_READ_FAILURE);

        return _CreateUnknownUnitDiskDevice(NDAS_UNITDEVICE_ERROR_HDD_READ_FAILURE);
    }

    UINT uiRet = ::NdasEncVerifyContentEncryptBlock(&ceb);

    if (NDASENC_ERROR_CEB_INVALID_SIGNATURE == uiRet)
    {
        // No Content Encryption
        // Safe to ignore
        ce.Method = NDAS_CONTENT_ENCRYPT_METHOD_NONE;
    }
    else if (NDASENC_ERROR_CEB_INVALID_CRC == uiRet)
    {
        // No Content Encryption
        // Safe to ignore
        ce.Method = NDAS_CONTENT_ENCRYPT_METHOD_NONE;
    }
    else if (NDASENC_ERROR_CEB_UNSUPPORTED_REVISION == uiRet)
    {
        // Error !
        (VOID) ::NdasLogEventError2(EVT_NDASSVC_ERROR_CEB_UNSUPPORTED_REVISION, uiRet);
        // return NULL;
        return _CreateUnknownUnitDiskDevice(NDAS_UNITDEVICE_ERROR_HDD_ECKEY_FAILURE);
    }
    else if (NDASENC_ERROR_CEB_INVALID_KEY_LENGTH == uiRet)
    {
        // No Content Encryption
        (VOID) ::NdasLogEventError2(EVT_NDASSVC_ERROR_CEB_INVALID_KEY_LENGTH, uiRet);
        ce.Method = NDAS_CONTENT_ENCRYPT_METHOD_NONE;
    }
    else if (ERROR_SUCCESS != uiRet)
    {
        // No Content Encryption
        ce.Method = NDAS_CONTENT_ENCRYPT_METHOD_NONE;
    }
    else
    {
        //
        // We consider the case that ENCRYPTION is not NONE.
        //

        if (NDAS_CONTENT_ENCRYPT_METHOD_NONE != ceb.Method)
        {
            BYTE SysKey[16] = {0};
            DWORD cbSysKey = sizeof(SysKey);

            uiRet = ::NdasEncGetSysKey(cbSysKey, SysKey, &cbSysKey);

            if (ERROR_SUCCESS != uiRet)
            {
                (VOID) ::NdasLogEventError2(EVT_NDASSVC_ERROR_GET_SYS_KEY, uiRet);
                // return NULL;
                return _CreateUnknownUnitDiskDevice(NDAS_UNITDEVICE_ERROR_HDD_ECKEY_FAILURE);
            }

            uiRet = ::NdasEncVerifyFingerprintCEB(SysKey, cbSysKey, &ceb);

            if (ERROR_SUCCESS != uiRet)
            {
                (VOID) ::NdasLogEventError2(EVT_NDASSVC_ERROR_SYS_KEY_MISMATCH, uiRet);
                // return NULL;
                return _CreateUnknownUnitDiskDevice(NDAS_UNITDEVICE_ERROR_HDD_ECKEY_FAILURE);
            }

            //
            // Create Content Encrypt
            //
            ce.Method = ceb.Method;
            ce.KeyLength = ceb.KeyLength;
            ce.Key;

            uiRet = ::NdasEncCreateContentEncryptKey(
                        SysKey,
                        cbSysKey,
                        ceb.Key,
                        ceb.KeyLength,
                        ce.Key,
                        sizeof(ce.Key));

            if (ERROR_SUCCESS != uiRet)
            {
                (VOID) ::NdasLogEventError2(EVT_NDASSVC_ERROR_KEY_GENERATION_FAILURE, uiRet);
                // return NULL;
                return _CreateUnknownUnitDiskDevice(NDAS_UNITDEVICE_ERROR_HDD_ECKEY_FAILURE);
            }

        }

    }

    CNdasUnitDiskDevice* pUnitDiskDevice =
        new CNdasUnitDiskDevice(
        m_pDevice,
        m_dwUnitNo,
        diskType,
        m_udinfo,
        ldGroup,
        ldSequence,
        ulUserBlocks,
        pAddTargetInfo,
        ce,
        pDIBv2,
        pBACL);

    if (NULL == pUnitDiskDevice)
    {
        return NULL;
    }

    // We should detach pDIBv2 from being freeed here
    // pUnitDiskDevice will take care of it at dtor
    pDIBv2.Detach();
    pBACL.Detach();
    return pUnitDiskDevice;
}
Esempio n. 16
0
//
// Returns the symbolic link name list of the volume.
// Each entry is null-terminated and the last entry is terminated
// by an additional null character
//
// Caller should free the non-null returned pointer
// with HeapFree(GetProcessHeap(),...)
//
LPTSTR
pGetVolumesForNdasScsiLocation(
    const NDAS_SCSI_LOCATION* NdasScsiLocation)
{
    // Get the disk instance id of the location
    XTL::AutoProcessHeapPtr<TCHAR> DiskInstId =
        pGetDiskForNdasScsiLocationEx(NdasScsiLocation, FALSE);

    if (DiskInstId.IsInvalid())
    {
        XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR,
                  "GetDiskDevInstId failed, error=0x%X\n", GetLastError());
        return NULL;
    }

    // Get the volume instance id list of the disk
    XTL::AutoProcessHeapPtr<TCHAR> VolumeInstIdList =
        pGetVolumeInstIdListForDisk(DiskInstId);
    if (VolumeInstIdList.IsInvalid())
    {
        XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR,
                  "GetVolumeInstIdList failed, error=0x%X\n", GetLastError());
        return NULL;
    }

    // Get the volume names of the disk (via interface query)
    // Preallocate the buffer up to MAX_PATH
    DWORD bufferLength = MAX_PATH * sizeof(TCHAR);
    DWORD bufferRemaining = bufferLength;
    XTL::AutoProcessHeapPtr<TCHAR> buffer = static_cast<LPTSTR>(
            ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, bufferLength));
    LPTSTR bufferNext = static_cast<LPTSTR>(buffer); // direct resource access (non-const)

    for (LPCTSTR VolumeInstId = VolumeInstIdList;
            VolumeInstId && *VolumeInstId;
            VolumeInstId += ::lstrlen(VolumeInstId) + 1)
    {
        // Actually returned buffer is a list of null-terminated strings
        // but we queried for a specific instance id and for the specific
        // class, where only one or zero volume name may return.
        // So the return type is just a single string.
        XTL::AutoProcessHeapPtr<TCHAR> VolumeName =
            pGetDeviceSymbolicLinkList(
                &GUID_DEVINTERFACE_VOLUME,
                const_cast<LPTSTR>(VolumeInstId),
                CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
        if (VolumeName.IsInvalid())
        {
            // may not be a volume
            XTLTRACE2(NdasVolTrace, TRACE_LEVEL_INFORMATION,
                      _T("%s is not a volume device\n"), VolumeInstId);
            continue;
        }
        for (LPCTSTR VN = VolumeName; VN && *VN; VN += ::lstrlen(VN) + 1)
        {
            XTLTRACE2(NdasVolTrace, TRACE_LEVEL_INFORMATION,
                      _T("VolumeName=%s\n"), VN);
        }

        XTLTRACE2(NdasVolTrace, TRACE_LEVEL_INFORMATION,
                  _T("VolumeName=%s\n"), VolumeName);

        DWORD volumeNameLength = ::lstrlen(VolumeName);
        DWORD bufferRequired =  (volumeNameLength + 1) * sizeof(TCHAR);
        if (bufferRemaining < bufferRequired)
        {
            XTLTRACE2(NdasVolTrace, TRACE_LEVEL_INFORMATION,
                      "Reallocation\n");
            XTL::AutoProcessHeapPtr<TCHAR> newBuffer = static_cast<LPTSTR>(
                        ::HeapReAlloc(
                            ::GetProcessHeap(),
                            HEAP_ZERO_MEMORY,
                            buffer,
                            bufferLength + bufferRequired - bufferRemaining));
            if (newBuffer.IsInvalid())
            {
                XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR,
                          "HeapReAlloc failed, bytes=%d\n",
                          bufferLength + bufferLength - bufferRemaining);
                return NULL;
            }
            size_t bufferNextOffset =
                reinterpret_cast<BYTE*>(bufferNext) -
                reinterpret_cast<BYTE*>(*(&buffer));
            // discard the old buffer and attach it to the new buffer
            buffer.Detach();
            buffer = newBuffer.Detach();
            bufferNext = reinterpret_cast<TCHAR*>(
                             reinterpret_cast<BYTE*>(static_cast<LPTSTR>(buffer)) + bufferNextOffset);
        }
        ::CopyMemory(bufferNext, VolumeName, bufferRequired);
        bufferNext += volumeNameLength + 1;
        bufferRemaining -= bufferRequired;
    }

    return buffer.Detach();
}
Esempio n. 17
0
CONFIGRET
pCMGetDeviceInterfaceList(
    LPTSTR* SymbolicLinkNameList,
    LPCGUID InterfaceClassGuid,
    DEVINSTID DevInstId,
    ULONG Flags /*= CM_GET_DEVICE_INTERFACE_LIST_PRESENT*/)
{
    *SymbolicLinkNameList = NULL;

    ULONG bufferLength;
    CONFIGRET ret = ::CM_Get_Device_Interface_List_Size(
                        &bufferLength,
                        const_cast<LPGUID>(InterfaceClassGuid),
                        DevInstId,
                        Flags);
    if (CR_SUCCESS != ret)
    {
        XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR,
                  "CM_Get_Device_Interface_List_Size failed, cret=0x%X.\n", ret);
        return ret;
    }

    XTLTRACE2(NdasVolTrace, TRACE_LEVEL_VERBOSE,
              "RequiredBufferSize=%d chars\n", bufferLength);

    if (0 == bufferLength)
    {
        TCHAR* empty = static_cast<TCHAR*>(
                           ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, 2 * sizeof(TCHAR)));
        *SymbolicLinkNameList = empty;
        return CR_SUCCESS;
    }

    XTL::AutoProcessHeapPtr<TCHAR> buffer = static_cast<TCHAR*>(
            ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, bufferLength * sizeof(TCHAR)));
    if (buffer.IsInvalid())
    {
        XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR,
                  "HeapAlloc failed, bytes=%d\n", bufferLength * sizeof(TCHAR));
        return CR_OUT_OF_MEMORY;
    }

    ret = ::CM_Get_Device_Interface_List(
              const_cast<LPGUID>(InterfaceClassGuid),
              DevInstId,
              buffer,
              bufferLength,
              Flags);
    if (CR_SUCCESS != ret)
    {
        XTLTRACE2(NdasVolTrace, TRACE_LEVEL_ERROR,
                  "CM_Get_Device_Interface_List failed, cret=%x.\n", ret);
        return ret;
    }

    *SymbolicLinkNameList = buffer.Detach();
    XTLTRACE2(NdasVolTrace, TRACE_LEVEL_INFORMATION,
              _T("SymbolicLinkNameList=%s\n"), *SymbolicLinkNameList);

    return CR_SUCCESS;
}
Esempio n. 18
0
PVOLUME_DISK_EXTENTS
pVolumeGetVolumeDiskExtents(
	HANDLE hVolume)
{
	DWORD returnedLength;
	DWORD bufferLength = sizeof(VOLUME_DISK_EXTENTS);

	XTL::AutoProcessHeapPtr<VOLUME_DISK_EXTENTS> buffer =
		static_cast<PVOLUME_DISK_EXTENTS>(
			::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, bufferLength));

	if (buffer.IsInvalid())
	{
		XTLTRACE2_ERR(NdasVolTrace, 0, 
			"HeapAlloc (%d bytes) failed.\n", bufferLength);
		return NULL;
	}

	BOOL fSuccess = ::DeviceIoControl(
		hVolume, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
		NULL, 0,
		buffer, bufferLength,
		&returnedLength, NULL);

	if (!fSuccess && ERROR_MORE_DATA == ::GetLastError())
	{
		//
		// An extent is a contiguous run of sectors on one disk. When the
		// number of extents returned is greater than one (1), the error
		// code ERROR_MORE_DATA is returned. You should call
		// DeviceIoControl again, allocating enough buffer space based on
		// 
		// the value of NumberOfDiskExtents after the first DeviceIoControl call.

		bufferLength = sizeof(VOLUME_DISK_EXTENTS) - sizeof(DISK_EXTENT) + 
			sizeof(DISK_EXTENT) * buffer->NumberOfDiskExtents;

		XTL::AutoProcessHeapPtr<VOLUME_DISK_EXTENTS> newBuffer = 
			static_cast<PVOLUME_DISK_EXTENTS>(
				::HeapReAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, buffer, bufferLength));
		if (newBuffer.IsInvalid())
		{
			// buffer is still valid in case of failure
			// buffer will be released on return.
			XTLTRACE2_ERR(NdasVolTrace, 0, 
				"HeapReAlloc (%d bytes) failed.\n", bufferLength);
			return NULL;
		}
		// buffer points to the non-valid memory, so invalidate it now
		buffer.Detach();
		// transfer the data from newBuffer to buffer
		buffer = newBuffer.Detach();
		// query again
		fSuccess = ::DeviceIoControl(
			hVolume, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
			NULL, 0,
			buffer, bufferLength,
			&returnedLength, NULL);
	}

	if (!fSuccess)
	{
		XTLTRACE2_ERR(NdasVolTrace, 0, 
			"DeviceIoControl(IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS) failed.\n");
		return NULL;
	}

	// returns the buffer
	return buffer.Detach();
}