/*++ © 1998 Seagate Software, Inc. All rights reserved. Module Name: MTFSessn.cpp Abstract: CMTFSession class Author: Brian Dodd [brian] 25-Nov-1997 Revision History: --*/ #include "stdafx.h" #include "engine.h" #include "MTFSessn.h" // // !!!!! VERY IMPORTANT !!!!! // // WIN32_STREAM_ID_SIZE -- Size of WIN32_STREAM_ID (20) != sizeof(WIN32_STREAM_ID) (24) // due to WIN32_STREAM_ID being a variable sized structure -- // See below. #define WIN32_STREAM_ID_SIZE 20 /***************************************************************************** DETAIL DESCRIPTION OVERVIEW ======== This class uses MTF API calls to create a data set. A data set is created by writing a series of DBLKs which may optionally be followed by data streams. For each DBLK, the MTF API defines a corresponding xxxx_DBLK_INFO struct which is filled in by the client. This can be done field by field or by using an MTF API function which automatically fills in the structure with default information. The xxxx_DBLK_INFO structs are then passed to MTF_WriteXXXXDblk functions which use the information in the struct to format a buffer, which can then be written to the data set, using the Stream I/O Write function. DATA SET FORMAT =============== A data set is created by writing DBLKs and Streams in the following order: TAPE DBLK -- describes the media FILEMARK SSET DBLK -- start of set describing the data set VOLB DBLK -- describs the volume being stored for each directory and parent directory DIRB DBLK -- one for each directory/sub-directory, starting with the root STREAM -- may contain security info for the directory for each file to backup FILE DBLK -- one for each file, followed by one or more streams, describing STREAM security info, as well as the file data streams themselves STREAM FILEMARK ESET DBLK -- indicates the end of the data set FILEMARK -- terminates the data set FUNCTIONS OVERVIEW ================== The MTF session maintains various information about the data set being created. This member data is then used by the following routines. InitCommonHeader() -- Initializes the common header which is used in all DBLKS is stored DoTapeDblk() -- writes the TAPE DBLK DoSSETDblk() -- writes the SSET DBLK DoVolumeDblk() -- writes the VOLB DBLK DoParentDirectories() -- writes DIRB DBLKs and Streams for the directory to backed up and each of its parent directories DoDirectoryDblk() -- writes a single DIRB DBLK and associated security stream DoDataSet() -- writes FILE and DIRB DBLKs and associated data streams for everything in the directory. Recurses for sub-directories DoFileDblk() -- writes a FILE DBLK DoDataStream() -- writes data stream(s) associated with a file or directory DoEndOfDataSet() -- writes ESET DBLK and surrounding FILEMARKS NOTES AND WARNINGS ================== o Directory names are stored in DIRB DBLKs as "DIR\SUBDIR1\...\SUBDIRn\" (no vol, and with a trailing \) where as filenames are stored in FILE DBLKS just as "FILENAME.EXT" o In DIRB's, the backslashes between directory names are covnerted to L'\0'!!!!! (the mtf api takes care of this -- give it names with slashes!!!!) o All strings are assumed to be WCHAR by the MTF API o We assume here that __uint64 is supported by the compiler. *****************************************************************************/ static USHORT iCountMTFs = 0; // Count of existing objects CMTFSession::CMTFSession(void) { WsbTraceIn(OLESTR("CMTFSession::CMTFSession"), OLESTR("")); // public m_pStream = NULL; memset(&m_sHints, 0, sizeof(MVR_REMOTESTORAGE_HINTS)); // private m_nCurrentBlockId = 0; m_nDirectoryId = 0; m_nFileId = 0; m_nFormatLogicalAddress = 0; m_nPhysicalBlockAddress = 0; m_nBlockSize = 0; m_pSoftFilemarks = NULL; memset (&m_sHeaderInfo, 0, sizeof(MTF_DBLK_HDR_INFO)); memset (&m_sSetInfo, 0, sizeof(MTF_DBLK_SSET_INFO)); memset (&m_sVolInfo, 0, sizeof(MTF_DBLK_VOLB_INFO)); m_pBuffer = NULL; m_pRealBuffer = NULL; m_nBufUsed = 0; m_nBufSize = 0; m_nStartOfPad = 0; m_bUseFlatFileStructure = FALSE; m_bUseSoftFilemarks = FALSE; m_bUseCaseSensitiveSearch = FALSE; m_bCommitFile = FALSE; m_bSetInitialized = FALSE; memset (&m_SaveBasicInformation, 0, sizeof(FILE_BASIC_INFORMATION)); m_pvReadContext = NULL; // Create an MTFApi object m_pMTFApi = new CMTFApi; iCountMTFs++; WsbTraceOut(OLESTR("CMTFSession::CMTFSession"),OLESTR("Count is <%d>"), iCountMTFs); } CMTFSession::~CMTFSession(void) { WsbTraceIn(OLESTR("CMTFSession::~CMTFSession"), OLESTR("")); if (m_pMTFApi) { delete m_pMTFApi; m_pMTFApi = NULL; } if (m_pSoftFilemarks) { WsbFree(m_pSoftFilemarks); m_pSoftFilemarks = NULL; } if (m_pRealBuffer) { WsbFree(m_pRealBuffer); m_pBuffer = NULL; m_pRealBuffer = NULL; } iCountMTFs--; WsbTraceOut(OLESTR("CMTFSession::~CMTFSession"),OLESTR("Count is <%d>"), iCountMTFs); } //////////////////////////////////////////////////////////////////////////////// // // Local Methods // HRESULT CMTFSession::SetBlockSize( UINT32 blockSize) /*++ Routine Description: Defines the physical block size for the session. This is used for various PBA calculations. The value should only be set once per session. Arguments: blockSize - The new block size for the session. Return Value: S_OK - Success. --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CMTFSession::SetBlockSize"), OLESTR("<%d>"), blockSize); ULONG bufferSize = 0; try { WsbAssert(blockSize > 0, E_INVALIDARG); WsbAssert(!(blockSize % 512), E_INVALIDARG); m_nBlockSize = blockSize; // **MTF API CALL** // The MTF API needs to know the alignment factor!!!!!!! // WsbAssert(m_pMTFApi != NULL, E_OUTOFMEMORY); if (!(blockSize % 1024)) { m_pMTFApi->MTF_SetAlignmentFactor((UINT16) 1024); } else { // We already checked that the block size is a multiple of 512.... m_pMTFApi->MTF_SetAlignmentFactor((UINT16) 512); } ULONG defaultBufferSize = RMS_DEFAULT_BUFFER_SIZE; DWORD size; OLECHAR tmpString[256]; if (SUCCEEDED(WsbGetRegistryValueString(NULL, RMS_REGISTRY_STRING, RMS_PARAMETER_BUFFER_SIZE, tmpString, 256, &size))) { // Get the value. LONG val = wcstol(tmpString, NULL, 10); if (val > 0) { defaultBufferSize = val; } } ULONG nBlocks = defaultBufferSize/m_nBlockSize; nBlocks = (nBlocks < 2) ? 2 : nBlocks; bufferSize = nBlocks * m_nBlockSize; // Make sure we work with a virtual address aligned with sector size m_pRealBuffer = (BYTE *)WsbAlloc(bufferSize+m_nBlockSize); if (m_pRealBuffer) { if ((ULONG_PTR)m_pRealBuffer % m_nBlockSize) { m_pBuffer = m_pRealBuffer - ((ULONG_PTR)m_pRealBuffer % m_nBlockSize) + m_nBlockSize; } else { m_pBuffer = m_pRealBuffer; } } else { m_pBuffer = NULL; } WsbTrace(OLESTR("CMTFSession::SetBlockSize: Real Buffer Ptr = %I64X , Use Buffer Ptr = %I64X\n"), (UINT64)m_pRealBuffer, (UINT64)m_pBuffer); if (m_pBuffer) { m_nBufSize = bufferSize; m_nBufUsed = 0; } else { m_nBufSize = 0; m_nBufUsed = bufferSize; } WsbAssertPointer(m_pBuffer); WsbTraceAlways( OLESTR("Using buffer size of %d bytes for data transfers.\n"), bufferSize); } WsbCatch(hr); WsbTraceOut(OLESTR("CMTFSession::SetBlockSize"), OLESTR("hr = <%ls>, Alignment = %d, BufferSize = %d"), WsbHrAsString(hr), m_pMTFApi->MTF_GetAlignmentFactor(), bufferSize); return hr; } HRESULT CMTFSession::SetUseFlatFileStructure( BOOL val) /*++ Routine Description: Unconditionally sets the session flags to the value specified. Arguments: val - The new value for the UseFlatFileStructure flag. Return Value: S_OK - Success. --*/ { m_bUseFlatFileStructure = val; return S_OK; } HRESULT CMTFSession::SetUseCaseSensitiveSearch( BOOL val) /*++ Routine Description: Unconditionally sets the session flag to the value specified. Arguments: val - The new value for the CaseSensitiveSearch flag. Return Value: S_OK - Success. --*/ { m_bUseCaseSensitiveSearch = val; return S_OK; } HRESULT CMTFSession::SetCommitFile( BOOL val) /*++ Routine Description: Unconditionally sets the session flag to the value specified. Arguments: val - The new value for the CommitFile flag. Return Value: S_OK - Success. --*/ { m_bCommitFile = val; return S_OK; } HRESULT CMTFSession::SetUseSoftFilemarks( BOOL val) /*++ Routine Description: Unconditionally sets the session flags to the value specified. Arguments: val - The new value for the UseSoftFilemarks flag. Return Value: S_OK - Success. --*/ { HRESULT hr = S_OK; try { m_bUseSoftFilemarks = val; if (TRUE == m_bUseSoftFilemarks) { WsbAssert(NULL == m_pSoftFilemarks, E_UNEXPECTED); // Make sure the block size was initialized. WsbAssert(m_nBlockSize > 0, E_UNEXPECTED); // Allocate a block of memory for the soft filemark array m_pSoftFilemarks = (MTF_DBLK_SFMB_INFO *) WsbAlloc(m_nBlockSize); WsbAffirmPointer(m_pSoftFilemarks); memset(m_pSoftFilemarks, 0 , m_nBlockSize); // **MTF API CALL** m_pSoftFilemarks->uNumberOfFilemarkEntries = m_pMTFApi->MTF_GetMaxSoftFilemarkEntries(m_nBlockSize); WsbAssert(m_pSoftFilemarks->uNumberOfFilemarkEntries > 0, E_UNEXPECTED); } else { if (m_pSoftFilemarks) { WsbFree(m_pSoftFilemarks); m_pSoftFilemarks = NULL; } } } WsbCatch(hr); return hr; } HRESULT CMTFSession::InitCommonHeader(void) /*++ Routine Description: Sets up the common header. m_sHeaderInfo is set for unicode, NT & no block attributes Arguments: None. Return Value: S_OK - Success. --*/ { // Init Common header // **MTF API CALL** m_pMTFApi->MTF_SetDblkHdrDefaults(&m_sHeaderInfo); m_sHeaderInfo.uBlockAttributes = 0; m_sHeaderInfo.uOSID = MTF_OSID_DOS; // MTF_OSID_NT or MTF_OSID_DOS m_sHeaderInfo.uStringType = MTF_STRING_UNICODE_STR; return S_OK; } HRESULT CMTFSession::DoTapeDblk( IN WCHAR *szLabel, IN ULONG maxIdSize, IN OUT BYTE *pIdentifier, IN OUT ULONG *pIdSize, IN OUT ULONG *pIdType) /*++ Routine Description: Formats and Writes a TAPE DBLK. TAPE DBLK and FILEMARK are written to beginning of tape, disk, or file. Arguments: szLabel - The media label. Return Value: S_OK - Success. --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CMTFSession::DoTapeDblk"), OLESTR("<%ls>"), szLabel); try { MvrInjectError(L"Inject.CMTFSession::DoTapeDblk.0"); WsbAssertPointer(m_pBuffer); if ( maxIdSize > 0 ) { WsbAssertPointer( pIdentifier ); WsbAssertPointer( pIdSize ); WsbAssertPointer( pIdType ); } MTF_DBLK_TAPE_INFO sTapeInfo; // **MTF API STRUCT ** -- info for TAPE (void) InitCommonHeader(); // **MTF API CALL** // First set defaults for the info struct // // Note: Tthis sets the alignment factor to that set previously by // MTF_SetAlignmentFactor() m_pMTFApi->MTF_SetTAPEDefaults(&sTapeInfo); // Set the values of the MTF_DBLK_TAPE_INFO struct to suit our application // Set SFMB size, this should be used during restoration on media types that use SFM sTapeInfo.uSoftFilemarkBlockSize = (UINT16)(m_nBlockSize / 512); // The family id should be a unique value for each piece of media. Although not // guaranteed to be unique, the time function should provide something close enough. time_t tTime; sTapeInfo.uTapeFamilyId = (unsigned int) time(&tTime); sTapeInfo.uTapeAttributes |= MTF_TAPE_MEDIA_LABEL; if (TRUE == m_bUseSoftFilemarks) { sTapeInfo.uTapeAttributes |= MTF_TAPE_SOFT_FILEMARK; } sTapeInfo.uTapeSequenceNumber = 1; sTapeInfo.szTapeDescription = szLabel; sTapeInfo.uSoftwareVendorId = REMOTE_STORAGE_MTF_VENDOR_ID; // // Get remaining information from the label // CWsbBstrPtr tempLabel = szLabel; WCHAR delim[] = OLESTR("|"); WCHAR *token; int index=0; token = wcstok( (WCHAR *)tempLabel, delim ); while( token != NULL ) { index++; switch ( index ) { case 1: // Tag case 2: // Version case 3: // Vendor break; case 4: // Vendor Product ID sTapeInfo.szSoftwareName = token; break; case 5: // Creation Time Stamp { int iYear, iMonth, iDay, iHour, iMinute, iSecond; swscanf( token, L"%d/%d/%d.%d:%d:%d", &iYear, &iMonth, &iDay, &iHour, &iMinute, &iSecond ); // **MTF API CALL** sTapeInfo.sTapeDate = m_pMTFApi->MTF_CreateDateTime( iYear, iMonth, iDay, iHour, iMinute, iSecond ); } break; case 6: // Cartridge Label sTapeInfo.szTapeName = token; break; case 7: // Side case 8: // Media ID case 9: // Media Domain ID default: // Vendor specific of the form L"VS:Name=Value" break; } token = wcstok( NULL, delim ); } // These are zero for the tape dblk m_sHeaderInfo.uControlBlockId = 0; m_sHeaderInfo.uFormatLogicalAddress = 0; WsbTrace(OLESTR("Writing Tape Header (TAPE)\n")); // Sets the current position to beginning of data. WsbAffirmHr(SpaceToBOD()); // **MTF API CALL** // Provide the MTF_DBLK_HDR_INFO and MTF_DBLK_TAPE_INFO structs to this // function. The result is an MTF formatted TAPE DBLK in m_pBuffer. WsbAssert(0 == m_nBufUsed, MVR_E_LOGIC_ERROR); WsbAssertNoError(m_pMTFApi->MTF_WriteTAPEDblk(&m_sHeaderInfo, &sTapeInfo, m_pBuffer, m_nBufSize, &m_nBufUsed)); WsbTrace(OLESTR("Tape Header uses %lu of %lu bytes\n"), (ULONG)m_nBufUsed, (ULONG)m_nBufSize); // Save the on media identifier if (maxIdSize > 0) { *pIdSize = (maxIdSize > (ULONG)m_nBufUsed) ? (ULONG)m_nBufUsed : maxIdSize; *pIdType = (LONG) RmsOnMediaIdentifierMTF; memcpy(pIdentifier, m_pBuffer, *pIdSize); } WsbAffirmHr(PadToNextPBA()); // Write a filemark. This will flush the device buffer. WsbAffirmHr(WriteFilemarks(1)); } WsbCatch(hr); WsbTraceOut(OLESTR("CMTFSession::DoTapeDblk"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return hr; } HRESULT CMTFSession::DoSSETDblk( IN WCHAR *szSessionName, IN WCHAR *szSessionDescription, IN MTFSessionType type, IN USHORT nDataSetNumber) /*++ Routine Description: Formats and Writes a SSET DBLK. The SSET is the first DBLK written to a data set. Arguments: szSessionName - Session name. szSessionDescription - Session description that is displayed by the Backup program type - Specifies the data set type: Transfer, copy , normal, differential, incremental, daily, etc. nDataSetNumber - The data set number. Return Value: S_OK - Success. --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CMTFSession::DoSSETDblk"), OLESTR("<%ls> <%ls> <%d> <%d>"), szSessionName, szSessionDescription, type, nDataSetNumber); try { MvrInjectError(L"Inject.CMTFSession::DoSSETDblk.0"); WsbAssertPointer(m_pBuffer); UINT64 curPos; size_t nBufUsed = 0; // Reset Control Block info. m_nCurrentBlockId = 0; m_nDirectoryId = 0; m_nFileId = 0; (void) InitCommonHeader(); // Init SSET block // **MTF API CALL** m_pMTFApi->MTF_SetSSETDefaults(&m_sSetInfo); m_bSetInitialized = TRUE; // // Find out our account information // CWsbStringPtr accountName; WsbAffirmHr(WsbGetServiceInfo(APPID_RemoteStorageEngine, NULL, &accountName)); // // Set the values of the MTF_DBLK_SSET_INFO struct... // // First select the type of data set we are creating. switch (type) { case MTFSessionTypeTransfer: m_sSetInfo.uSSETAttributes = MTF_SSET_TRANSFER; break; case MTFSessionTypeCopy: m_sSetInfo.uSSETAttributes = MTF_SSET_COPY; break; case MTFSessionTypeNormal: m_sSetInfo.uSSETAttributes = MTF_SSET_NORMAL; break; case MTFSessionTypeDifferential: m_sSetInfo.uSSETAttributes = MTF_SSET_DIFFERENTIAL; break; case MTFSessionTypeIncremental: m_sSetInfo.uSSETAttributes = MTF_SSET_INCREMENTAL; break; case MTFSessionTypeDaily: m_sSetInfo.uSSETAttributes = MTF_SSET_DAILY; break; default: WsbThrow(E_INVALIDARG); break; } m_sSetInfo.uDataSetNumber = nDataSetNumber; m_sSetInfo.uSoftwareVendorId = REMOTE_STORAGE_MTF_VENDOR_ID; m_sSetInfo.szDataSetName = szSessionName; m_sSetInfo.szDataSetDescription = szSessionDescription; m_sSetInfo.szUserName = accountName; WsbAffirmHr(GetCurrentPBA(&curPos)); // utility fn below m_sSetInfo.uPhysicalBlockAddress = curPos; m_sSetInfo.uPhysicalBlockAddress += 1 ; // MTF is one based, devices are zero based. m_sSetInfo.uSoftwareVerMjr = REMOTE_STORAGE_MTF_SOFTWARE_VERSION_MJ; m_sSetInfo.uSoftwareVerMnr = REMOTE_STORAGE_MTF_SOFTWARE_VERSION_MN; // Save the PBA for the data set m_nPhysicalBlockAddress = m_sSetInfo.uPhysicalBlockAddress -1; WsbAssert(m_nPhysicalBlockAddress > 0, E_UNEXPECTED); // Someting went wrong! m_nFormatLogicalAddress = 0; // The Control Block ID field is used for error recovery. The // Control Block ID value for an SSET DBLK should be zero. All // subsequent DBLKs within the Data Set will have a Control Block // ID one greater than the previous DBLK’s Control Block ID. // Values for this field are only defined for DBLKs within a Data // Set from the SSET to the last DBLK occurring prior to the ESET. WsbAssert(0 == m_nCurrentBlockId, E_UNEXPECTED); m_sHeaderInfo.uControlBlockId = m_nCurrentBlockId++; // Increment them here after for every dblk we write m_sHeaderInfo.uFormatLogicalAddress = 0; WsbTrace(OLESTR("Writing Start of Set (SSET) @ PBA %I64u\n"), m_nPhysicalBlockAddress); // **MTF API CALL** -- // Provide the MTF_DBLK_HDR_INFO and MTF_DBLK_SSET_INFO structs to // this function. The result is an MTF formatted SSET DBLK in m_pBuffer. WsbAssert(0 == m_nBufUsed, MVR_E_LOGIC_ERROR); WsbAssertNoError(m_pMTFApi->MTF_WriteSSETDblk(&m_sHeaderInfo, &m_sSetInfo, m_pBuffer, m_nBufSize, &m_nBufUsed)); // We pass in FALSE to make sure we don't actually touch tape. The SSET is the // first DBLK written in the data set so we have plenty of transfer buffer for // the DBLKs to follow. // // This routine is called when the application starts a new data set, but // we don't wan't to fail if we're going to get device errors, that will come // later. WsbAffirmHr(PadToNextFLA(FALSE)); m_sHints.DataSetStart.QuadPart = m_nPhysicalBlockAddress * m_nBlockSize; } WsbCatch(hr); WsbTraceOut(OLESTR("CMTFSession::DoSSETDblk"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return hr; } HRESULT CMTFSession::DoVolumeDblk( IN WCHAR *szPath) /*++ Routine Description: Formats and Writes a VOLB DBLK. Arguments: szPath - Full pathname containing name of volume. Return Value: S_OK - Success. --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CMTFSession::DoVolumeDblk"), OLESTR("<%ls>"), WsbAbbreviatePath(szPath, 120)); try { MvrInjectError(L"Inject.CMTFSession::DoVolumeDblk.0"); WsbAssertPointer(m_pBuffer); WsbAssertPointer(szPath); CWsbStringPtr szVolume; size_t nMoreBufUsed; szVolume = szPath; WsbAffirm(0 != (WCHAR *)szVolume, E_OUTOFMEMORY); WsbAssert(m_nBlockSize > 0, MVR_E_LOGIC_ERROR); // Make sure we are aligned with a FLA (i.e. the last DBLK was properly padded). // It won't be if we are having problems writing to tape. UINT16 uAlignmentFactor = m_pMTFApi->MTF_GetAlignmentFactor(); WsbAffirm(0 == (m_nBufUsed % uAlignmentFactor), E_ABORT); UINT64 fla = m_nFormatLogicalAddress + m_nBufUsed/uAlignmentFactor; UINT64 pba = m_nPhysicalBlockAddress + (fla*uAlignmentFactor/m_nBlockSize); WsbTrace(OLESTR("%ls (VOLB) @ FLA %I64u (%I64u, %I64u)\n"), WsbAbbreviatePath(szPath, 120), fla, pba, fla % (m_nBlockSize/uAlignmentFactor)); // **MTF API CALL** // Sets the MTF_VOLB_DBLK_INFO struct using Win32 GetVolumeInformation data m_pMTFApi->MTF_SetVOLBForDevice(&m_sVolInfo, szVolume); // Increment the blockid and alignment index values that we keep in // our common block header structure. m_sHeaderInfo.uControlBlockId = m_nCurrentBlockId++; m_sHeaderInfo.uFormatLogicalAddress = fla; WsbAffirmHr(FlushBuffer(m_pBuffer, &m_nBufUsed)); // **MTF API CALL** // Provide the MTF_DBLK_HDR_INFO and MTF_DBLK_VOLB_INFO structs to // this function. The result is an MTF formatted VOLB DBLK in m_pBuffer. nMoreBufUsed = 0; WsbAssertNoError(m_pMTFApi->MTF_WriteVOLBDblk(&m_sHeaderInfo, &m_sVolInfo, m_pBuffer+m_nBufUsed, m_nBufSize-m_nBufUsed, &nMoreBufUsed)); m_nBufUsed += nMoreBufUsed; // Output VOLB to the data set. WsbAffirmHr(PadToNextFLA(TRUE)); } WsbCatch(hr); WsbTraceOut(OLESTR("CMTFSession::DoVolumeDblk"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return hr; } HRESULT CMTFSession::DoParentDirectories( IN WCHAR *szPath) /*++ Routine Description: Formats and writes the parent DIRB Dblks for the given pathname. Arguments: szPath - Full pathname of directory. Return Value: S_OK - Success. Note: In order for both stickyName and driveLetter-colon path formats to work properly, both with and without writing separate DIRBs for the parent directories, THE EXISTENCE AND PLACEMENT OF THE PATH MANIPULATION CODE (APPEND/PREPEND, ETC.) IS CRUCIAL!!! --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CMTFSession::DoParentDirectories"), OLESTR("<%ls>"), WsbAbbreviatePath(szPath, 120)); HANDLE hSearchHandle = INVALID_HANDLE_VALUE; try { MvrInjectError(L"Inject.CMTFSession::DoParentDirectories.0"); WsbAssertPointer( szPath ); WIN32_FIND_DATAW obFindData; CWsbStringPtr path; CWsbStringPtr nameSpace; WCHAR *szDirs; WCHAR *token; DWORD additionalSearchFlags = 0; additionalSearchFlags |= (m_bUseCaseSensitiveSearch) ? FIND_FIRST_EX_CASE_SENSITIVE : 0; nameSpace = szPath; nameSpace.GiveTo(&szDirs); // First we need to do a DIRB dblk for the root directory nameSpace = wcstok(szDirs, OLESTR("\\")); // pop off "Volume{......}" or "driveLetter:" WsbAffirmHr(nameSpace.Append(OLESTR("\\"))); // ** WIN32 API Call - gets directory info for the root directory // For the root directory only, we need to call GetFileInformationByHandle instead of // ..FindFirstFileEx. FindFirst doesn't return root dir info since the root has no parent path = nameSpace; WsbAffirmHr(path.Prepend(OLESTR("\\\\?\\"))); WsbAffirm(0 != (WCHAR *)path, E_OUTOFMEMORY); BY_HANDLE_FILE_INFORMATION obGetFileInfoData; memset(&obGetFileInfoData, 0, sizeof(BY_HANDLE_FILE_INFORMATION)); WsbAffirmHandle(hSearchHandle = CreateFile(path, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL)); WsbAffirmStatus(GetFileInformationByHandle(hSearchHandle, &obGetFileInfoData)); FindClose(hSearchHandle); hSearchHandle = INVALID_HANDLE_VALUE; // At time of this writing CreateTime for root dir is bogus. (bills 10/20/98). WsbTrace(OLESTR("Create Time = <%ls>\n"), WsbFiletimeAsString(FALSE, obGetFileInfoData.ftCreationTime)); WsbTrace(OLESTR("Last Access Time = <%ls>\n"), WsbFiletimeAsString(FALSE, obGetFileInfoData.ftLastAccessTime)); WsbTrace(OLESTR("Last Write Time = <%ls>\n"), WsbFiletimeAsString(FALSE, obGetFileInfoData.ftLastWriteTime)); // copy info from GetFileInformationByHandle call (BY_HANDLE_FILE_INFORMATION struct) // .. into obFindData (WIN32_FIND_DATAW struct) for DoDirectoryDblk call. memset(&obFindData, 0, sizeof(WIN32_FIND_DATAW)); obFindData.dwFileAttributes = obGetFileInfoData.dwFileAttributes; obFindData.ftCreationTime = obGetFileInfoData.ftCreationTime; obFindData.ftLastAccessTime = obGetFileInfoData.ftLastAccessTime; obFindData.ftLastWriteTime = obGetFileInfoData.ftLastWriteTime; WsbAffirmHr(DoDirectoryDblk(nameSpace, &obFindData)); // Now do the same for each succeeding directory in the path using strtok token = wcstok(0, OLESTR("\\")); // pop off first subdir for ( ; token; token = wcstok(0, OLESTR("\\"))) { nameSpace.Append(token); path = nameSpace; path.Prepend(OLESTR("\\\\?\\")); WsbAssertHandle(hSearchHandle = FindFirstFileEx((WCHAR *) path, FindExInfoStandard, &obFindData, FindExSearchLimitToDirectories, 0, additionalSearchFlags)); if ( obFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { nameSpace.Append(OLESTR("\\")); // add in \ to dir WsbAffirmHr(DoDirectoryDblk((WCHAR *) nameSpace, &obFindData)); } FindClose(hSearchHandle); hSearchHandle = INVALID_HANDLE_VALUE; } nameSpace.TakeFrom(szDirs, 0); // cleanup } WsbCatch(hr); if (hSearchHandle != INVALID_HANDLE_VALUE) { FindClose(hSearchHandle); } WsbTraceOut(OLESTR("CMTFSession::DoParentDirectories"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return hr; } HRESULT CMTFSession::DoDataSet( IN WCHAR *szPath) /*++ Routine Description: Recurses through all the items contained in the directory specified by path and backs them up calling DoFileDblk and DoDirectoryDblk Arguments: szPath - Full pathname of directory. Return Value: S_OK - Success. MVR_E_NOT_FOUND - Object not found. --*/ { HRESULT hr = MVR_E_NOT_FOUND; WsbTraceIn(OLESTR("CMTFSession::DoDataSet"), OLESTR("<%ls>"), WsbAbbreviatePath(szPath, 120)); HANDLE hSearchHandle = INVALID_HANDLE_VALUE; try { MvrInjectError(L"Inject.CMTFSession::DoDataSet.0"); WsbAssertPointer( szPath ); WIN32_FIND_DATAW obFindData; BOOL bMoreFiles; CWsbStringPtr nameSpace; CWsbStringPtr pathname; // check if the specification is for file(s): nameSpace = c:\dir\test*.* or c:\dir\test1.tst nameSpace = szPath; WsbAffirmHr(nameSpace.Prepend(OLESTR("\\\\?\\"))); DWORD additionalSearchFlags = 0; additionalSearchFlags |= (m_bUseCaseSensitiveSearch) ? FIND_FIRST_EX_CASE_SENSITIVE : 0; WsbAssertHandle(hSearchHandle = FindFirstFileEx((WCHAR *) nameSpace, FindExInfoStandard, &obFindData, FindExSearchNameMatch, 0, additionalSearchFlags)); for (bMoreFiles = TRUE; hSearchHandle != INVALID_HANDLE_VALUE && bMoreFiles; bMoreFiles = FindNextFileW(hSearchHandle, &obFindData)) { // Skip all directories if ((obFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { // Not a dir CWsbStringPtr path; WCHAR *end; LONG numChar; // use the szPath to get the pathname, then append the filename pathname = szPath; WsbAffirm(0 != (WCHAR *)pathname, E_OUTOFMEMORY); end = wcsrchr((WCHAR *)pathname, L'\\'); WsbAssert(end != NULL, MVR_E_INVALIDARG); numChar = (LONG)(end - (WCHAR *)pathname + 1); WsbAssert(numChar > 0, E_UNEXPECTED); WsbAffirmHr(path.Alloc(numChar + MAX_PATH)); wcsncpy((WCHAR *)path, (WCHAR *)pathname, numChar); ((WCHAR *)path)[numChar] = L'\0'; path.Append(obFindData.cFileName); WsbAffirmHr(hr = DoFileDblk((WCHAR *)path, &obFindData)); } } // close search handle after processing all the files FindClose(hSearchHandle); hSearchHandle = INVALID_HANDLE_VALUE; // process all files for this directory: nameSpace = c:\dir nameSpace = szPath; nameSpace.Append(OLESTR("\\*.*")); nameSpace.Prepend(OLESTR("\\\\?\\")); hSearchHandle = FindFirstFileEx((WCHAR *) nameSpace, FindExInfoStandard, &obFindData, FindExSearchNameMatch, 0, additionalSearchFlags); for (bMoreFiles = TRUE; hSearchHandle != INVALID_HANDLE_VALUE && bMoreFiles; bMoreFiles = FindNextFileW(hSearchHandle, &obFindData)) { if ((obFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { // use the szPath to get the pathname, then append the filename pathname = szPath; pathname.Append(OLESTR("\\")); pathname.Append(obFindData.cFileName); WsbAffirmHr(hr = DoFileDblk((WCHAR *)pathname, &obFindData)); } } // close search handle after processing all the files FindClose(hSearchHandle); hSearchHandle = INVALID_HANDLE_VALUE; // process all directories in this directory: nameSpace = c:\dir nameSpace = szPath; nameSpace.Append(OLESTR("\\*.*")); nameSpace.Prepend(OLESTR("\\\\?\\")); hSearchHandle = FindFirstFileEx((WCHAR *) nameSpace, FindExInfoStandard, &obFindData, FindExSearchNameMatch, 0, additionalSearchFlags); for (bMoreFiles = TRUE; hSearchHandle != INVALID_HANDLE_VALUE && bMoreFiles; bMoreFiles = FindNextFileW(hSearchHandle, &obFindData)) { // Recursively handle any directories other than . and .. if (((obFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) && (wcscmp(obFindData.cFileName, OLESTR(".")) != 0) && (wcscmp(obFindData.cFileName, OLESTR("..")) != 0)) { // append the directory name to pathname pathname = szPath; pathname.Append(OLESTR("\\")); pathname.Append(obFindData.cFileName); pathname.Append(OLESTR("\\")); WsbAffirmHr(hr = DoDirectoryDblk((WCHAR *) pathname, &obFindData)); // append the directory name to pathname and process pathname = szPath; pathname.Append(OLESTR("\\")); pathname.Append(obFindData.cFileName); WsbAffirmHr(DoDataSet((WCHAR *) pathname)); } } } WsbCatch(hr); // close search handle after processing all the directories if (hSearchHandle != INVALID_HANDLE_VALUE) { FindClose(hSearchHandle); } WsbTraceOut(OLESTR("CMTFSession::DoDataSet"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return hr; } HRESULT CMTFSession::DoDirectoryDblk( IN WCHAR *szPath, IN WIN32_FIND_DATAW *pFindData) /*++ Routine Description: Writes out a DIRB DBLK and calls DoStream to write out associated stream data. Arguments: szPath - Full pathname of directory. pFindData - WIN32 information about the directiory. Return Value: S_OK - Success. Note: In order for both stickyName and driveLetter-colon path formats to work properly, both with and without writing separate DIRBs for the parent directories, THE EXISTENCE AND PLACEMENT OF THE PATH MANIPULATION CODE (APPEND/PREPEND, ETC.) IS CRUCIAL!!! --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CMTFSession::DoDirectoryDblk"), OLESTR("")); HANDLE hStream = INVALID_HANDLE_VALUE; try { MvrInjectError(L"Inject.CMTFSession::DoDirectoryDblk.0"); WsbAssertPointer(m_pBuffer); WsbAssertPointer(szPath); MTF_DBLK_DIRB_INFO sDIRB; // **MTF API STRUCT ** -- info for DIRB PWCHAR pSlash; size_t nMoreBufUsed; WCHAR *end; WsbAssert(m_nBlockSize > 0, MVR_E_LOGIC_ERROR); // Make sure we are aligned with a FLA (i.e. the last DBLK was properly padded). // It won't be if we are having problems writing to tape. UINT16 uAlignmentFactor = m_pMTFApi->MTF_GetAlignmentFactor(); WsbAffirm(0 == (m_nBufUsed % uAlignmentFactor), E_ABORT); UINT64 fla = m_nFormatLogicalAddress + m_nBufUsed/uAlignmentFactor; UINT64 pba = m_nPhysicalBlockAddress + (fla*uAlignmentFactor/m_nBlockSize); WsbTrace(OLESTR("%ls (DIRB) @ FLA %I64u (%I64u, %I64u)\n"), WsbAbbreviatePath(szPath, 120), fla, pba, fla % (m_nBlockSize/uAlignmentFactor)); CWsbStringPtr path = szPath; // tack on trailing backslash if not already there end = wcsrchr((WCHAR *)path, L'\0'); WsbAssert(end != NULL, MVR_E_INVALIDARG); // Something went wrong! if(*(end-1) != L'\\') { path.Append(OLESTR("\\")); } // Get a handle to the directory. If this fails we need to skip everything else. WsbAffirmHr(OpenStream(path, &hStream)); // **MTF API CALL** // automatically fill in the MTF_DIRB_DBLK_INFO structure using // information in the pFindData structure // // if we are getting something in the form of "C:\", // then we want to send the name along as just "\" // otherwise // we want to send the full path, but omit the volume ("C:\") // thus the "+3" pSlash = wcschr(path, L'\\'); WsbAssert(pSlash != NULL, MVR_E_INVALIDARG); // Something went wrong! pSlash++; // Look for the second one pSlash = wcschr(pSlash, L'\\'); if (NULL == pSlash) { // It's just the volume name and nothing more m_pMTFApi->MTF_SetDIRBFromFindData(&sDIRB, OLESTR("\\"), pFindData); } else { pSlash = wcschr(path, L'\\'); // point to first backslash (beginning of path) m_pMTFApi->MTF_SetDIRBFromFindData(&sDIRB, pSlash + 1, pFindData); } // Check if we need to set the Backup Date field for the DIRB if (m_sSetInfo.uSSETAttributes & MTF_SSET_NORMAL) { time_t tTime; time(&tTime); sDIRB.sBackupDate = m_pMTFApi->MTF_CreateDateTimeFromTM(gmtime(&tTime)); } // make sure to mark and update the directory id as well as the // control block id and alignment is already correct sDIRB.uDirectoryId = ++m_nDirectoryId; m_sHeaderInfo.uControlBlockId = m_nCurrentBlockId++; m_sHeaderInfo.uFormatLogicalAddress = fla; // Add in OS Specific data MTF_DIRB_OS_NT_0 sOSNT; switch ( m_sHeaderInfo.uOSID ) { case MTF_OSID_NT: sOSNT.uDirectoryAttributes = sDIRB.uDirectoryAttributes; m_sHeaderInfo.pvOSData = &sOSNT; m_sHeaderInfo.uOSDataSize = sizeof(sOSNT); break; default: break; } WsbAffirmHr(FlushBuffer(m_pBuffer, &m_nBufUsed)); // **MTF API CALL** // provide the MTF_DBLK_HDR_INFO and MTF_DBLK_DIRB_INFO structs // to this function. The result is an MTF formatted DIRB DBLK in // m_pBuffer. nMoreBufUsed = 0; WsbAssertNoError(m_pMTFApi->MTF_WriteDIRBDblk(&m_sHeaderInfo, &sDIRB, m_pBuffer+m_nBufUsed, m_nBufSize-m_nBufUsed, &nMoreBufUsed)); m_nBufUsed += nMoreBufUsed; // **MTF API CALL** // output the name stream, if required. if ( sDIRB.uDirectoryAttributes & MTF_DIRB_PATH_IN_STREAM ) { nMoreBufUsed = 0; if ( m_sVolInfo.uVolumeAttributes & MTF_VOLB_DEV_DRIVE ) { WsbAssertNoError(m_pMTFApi->MTF_WriteNameStream(MTF_PATH_NAME_STREAM, szPath + 3, m_pBuffer+m_nBufUsed, m_nBufSize-m_nBufUsed, &nMoreBufUsed)); m_nBufUsed += nMoreBufUsed; } else if ( m_sVolInfo.uVolumeAttributes & MTF_VOLB_DEV_OS_SPEC ) { if ( 0 == _wcsnicmp( m_sVolInfo.szDeviceName, OLESTR("Volume{"), 7 )) { WsbAssertNoError(m_pMTFApi->MTF_WriteNameStream(MTF_PATH_NAME_STREAM, szPath + 45, m_pBuffer+m_nBufUsed, m_nBufSize-m_nBufUsed, &nMoreBufUsed)); m_nBufUsed += nMoreBufUsed; } else { // unrecognized operating system specific format WsbThrow(MVR_E_INVALIDARG); } } else { // UNC path - unsupported WsbThrow(MVR_E_INVALIDARG); } } // Now, instead of padding this out, we call this funciton to write // out the stream which will write out the current contents of the // buffer as well. When this call returns, the current contents of // the buffer as well as the associated data stream will have been // written to media. // Note: Data may still remain in the device buffer, or the // local m_pBuffer if the file doesn't pad to a block // boundary, and the device buffer is not flushed. WsbAffirmHr(DoDataStream(hStream)); } WsbCatch(hr); if (INVALID_HANDLE_VALUE != hStream) { CloseStream(hStream); hStream = INVALID_HANDLE_VALUE; } WsbTraceOut(OLESTR("CMTFSession::DoDirectoryDblk"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return hr; } HRESULT CMTFSession::DoFileDblk( IN WCHAR *szPath, IN WIN32_FIND_DATAW *pFindData) /*++ Routine Description: Writes out a FILE DBLK and calls DoStream to write out associated stream data Arguments: szPath - Full pathname of file. pFindData - WIN32 information about the file. Return Value: S_OK - Success. --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CMTFSession::DoFileDblk"), OLESTR("")); HANDLE hStream = INVALID_HANDLE_VALUE; try { MvrInjectError(L"Inject.CMTFSession::DoFileDblk.0"); WsbAssertPointer(m_pBuffer); WsbAssertPointer(szPath); MTF_DBLK_FILE_INFO sFILE; // **MTF API STRUCT ** -- info for FILE size_t nMoreBufUsed; UINT16 uAlignmentFactor = m_pMTFApi->MTF_GetAlignmentFactor(); WsbAssert(m_nBlockSize > 0, MVR_E_LOGIC_ERROR); // Make sure we are aligned with a FLA (i.e. the last DBLK was properly padded). // It won't be if we are having problems writing to tape. WsbAffirm(0 == (m_nBufUsed % uAlignmentFactor), E_ABORT); UINT64 fla = m_nFormatLogicalAddress + m_nBufUsed/uAlignmentFactor; UINT64 pba = m_nPhysicalBlockAddress + (fla*uAlignmentFactor/m_nBlockSize); WsbTrace(OLESTR("%ls (FILE) @ FLA %I64u (%I64u, %I64u)\n"), WsbAbbreviatePath(szPath, 120), fla, pba, fla % (m_nBlockSize/uAlignmentFactor)); // Get a handle to the directory. If this fails we need to skip everything else. WsbAffirmHr(OpenStream(szPath, &hStream)); // Initialize the hints set for each file. m_sHints.FileStart.QuadPart = fla * uAlignmentFactor; m_sHints.FileSize.QuadPart = 0; m_sHints.DataStart.QuadPart = 0; m_sHints.DataSize.QuadPart = 0; m_sHints.VerificationType = MVR_VERIFICATION_TYPE_NONE; m_sHints.VerificationData.QuadPart = 0; m_sHints.DatastreamCRCType = WSB_CRC_CALC_NONE; m_sHints.DatastreamCRC.QuadPart = 0; m_sHints.FileUSN.QuadPart = 0; if (m_bUseFlatFileStructure) { // For HSM we rename the file to it's logical address swprintf( pFindData->cFileName, L"%08x", fla ); } // **MTF API CALL** // automatically fill in the MTF_FILE_DBLK_INFO structure using // information in the pFindData structure m_pMTFApi->MTF_SetFILEFromFindData(&sFILE, pFindData); // Check if we need to set the Backup Date field for the FILE DBLK if ((m_sSetInfo.uSSETAttributes & MTF_SSET_NORMAL) |(m_sSetInfo.uSSETAttributes & MTF_SSET_DIFFERENTIAL) |(m_sSetInfo.uSSETAttributes & MTF_SSET_INCREMENTAL) |(m_sSetInfo.uSSETAttributes & MTF_SSET_DAILY)){ time_t tTime; time(&tTime); sFILE.sBackupDate = m_pMTFApi->MTF_CreateDateTimeFromTM(gmtime(&tTime)); } // make sure to mark and update the file id as well as the control // block id and alignment is already correct sFILE.uDirectoryId = m_nDirectoryId; sFILE.uFileId = ++m_nFileId; m_sHeaderInfo.uControlBlockId = m_nCurrentBlockId++; m_sHeaderInfo.uFormatLogicalAddress = fla; // Add in OS Specific data MTF_FILE_OS_NT_0 sOSNT; switch ( m_sHeaderInfo.uOSID ) { case MTF_OSID_NT: sOSNT.uFileAttributes = sFILE.uFileAttributes; sOSNT.uShortNameOffset = 0; sOSNT.uShortNameSize = 0; sOSNT.lLink = 0; sOSNT.uReserved = 0; m_sHeaderInfo.pvOSData = &sOSNT; m_sHeaderInfo.uOSDataSize = sizeof(sOSNT); break; default: break; } WsbAffirmHr(FlushBuffer(m_pBuffer, &m_nBufUsed)); // **MTF API CALL** // Provide the MTF_DBLK_HDR_INFO and MTF_DBLK_FILE_INFO structs // to this function. The result is an MTF formatted FILE DBLK in // m_pBuffer. nMoreBufUsed = 0; WsbAssertNoError(m_pMTFApi->MTF_WriteFILEDblk(&m_sHeaderInfo, &sFILE, m_pBuffer+m_nBufUsed, m_nBufSize-m_nBufUsed, &nMoreBufUsed)); m_nBufUsed += nMoreBufUsed; // Like the directory, instead of padding this out, we call this // funciton to write out the stream which will write out the current // contents of the buffer as well. When this call returns, the // current contents of the buffer as well as the associated data // stream will have been written to media. // Note: Data may still remain in the device buffer, or the // local m_pBuffer if the file doesn't pad to a block // boundary, and the device buffer is not flushed. hr = DoDataStream(hStream); if ( hr != S_OK) { // unable to copy the file to target media. WsbTraceAlways( OLESTR("Unable to store file %ls. reason = %s\n"), WsbAbbreviatePath(szPath, 120), WsbHrAsString(hr)); WsbThrow(hr); } else { // Make sure we are alinged with a FLA (i.e. the last stream was properly padded). WsbAssert(0 == (m_nBufUsed % uAlignmentFactor), MVR_E_LOGIC_ERROR); m_sHints.FileSize.QuadPart = m_nFormatLogicalAddress * uAlignmentFactor + m_nBufUsed - m_sHints.FileStart.QuadPart; } } WsbCatch(hr); if (INVALID_HANDLE_VALUE != hStream) { CloseStream(hStream); hStream = INVALID_HANDLE_VALUE; } WsbTraceOut(OLESTR("CMTFSession::DoFileDblk"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return hr; } HRESULT CMTFSession::OpenStream( IN WCHAR *szPath, OUT HANDLE *pStreamHandle) /*++ Routine Description: Opens the file to backup in "backup read" mode, and returns stream handle for the file specified. Arguments: szPath - Full pathname of file. hStream - Returned stream handle. Return Value: S_OK - Success. --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CMTFSession::OpenStream"), OLESTR("<%ls>"), WsbAbbreviatePath(szPath, 120)); HANDLE hStream = INVALID_HANDLE_VALUE; try { MvrInjectError(L"Inject.CMTFSession::OpenStream.0"); WsbAssertPointer(szPath); WsbAssertPointer(pStreamHandle); *pStreamHandle = INVALID_HANDLE_VALUE; FILE_BASIC_INFORMATION basicInformation; IO_STATUS_BLOCK IoStatusBlock; NTSTATUS ccode; // ** WIN32 File API Call - open the file for backup read. This can be more involved if // the app needs to be run by someone without the proper authority to // backup certain files.... // We also ask for GENERIC_WRITE so we can set the attributes to prevent the // modification of dates. DWORD posixFlag = (m_bUseCaseSensitiveSearch) ? FILE_FLAG_POSIX_SEMANTICS : 0; CWsbStringPtr name = szPath; WsbAffirmHr(name.Prepend(OLESTR("\\\\?\\"))); WsbAffirm(0 != (WCHAR *)name, E_OUTOFMEMORY); WsbAffirmHandle(hStream = CreateFileW((WCHAR *) name, GENERIC_READ | FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT | posixFlag, NULL)); // // Prevent modification of file dates // // ** NT System Call - query for file information WsbAffirmNtStatus(NtQueryInformationFile(hStream, &IoStatusBlock, (PVOID)&basicInformation, sizeof( basicInformation ), FileBasicInformation)); m_SaveBasicInformation = basicInformation; basicInformation.CreationTime.QuadPart = -1; basicInformation.LastAccessTime.QuadPart = -1; basicInformation.LastWriteTime.QuadPart = -1; basicInformation.ChangeTime.QuadPart = -1; // ** NT System Call - set file information WsbAffirmNtStatus(ccode = NtSetInformationFile( hStream, &IoStatusBlock, (PVOID)&basicInformation, sizeof( basicInformation ), FileBasicInformation)); if (pStreamHandle) { *pStreamHandle = hStream; } } WsbCatchAndDo(hr, if (INVALID_HANDLE_VALUE != hStream) { CloseHandle( hStream ); hStream = INVALID_HANDLE_VALUE; } ); WsbTraceOut(OLESTR("CMTFSession::OpenStream"), OLESTR("hr = <%ls>, handle = <0x%08x>"), WsbHrAsString(hr), hStream); return hr; } HRESULT CMTFSession::CloseStream( IN HANDLE hStream) /*++ Routine Description: Close stream handle and performs cleanup. Arguments: hStream - Stream handle to close Return Value: S_OK - Success. --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CMTFSession::CloseStream"), OLESTR("<0x%08x>"), hStream); try { if (INVALID_HANDLE_VALUE != hStream) { // // Cleanup from a partial backup read. We're setting bAbort=TRUE // to free resources used by BackupRead() // if (m_pvReadContext) { (void) BackupRead(hStream, NULL, 0, NULL, TRUE, FALSE, &m_pvReadContext); m_pvReadContext = NULL; } (void) CloseHandle( hStream ); hStream = INVALID_HANDLE_VALUE; } } WsbCatch(hr); WsbTraceOut(OLESTR("CMTFSession::CloseStream"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return hr; } HRESULT CMTFSession::DoDataStream( IN HANDLE hStream) /*++ Routine Description: Uses WIN32 BackupRead to read streams associated with a file and then write them out to the data set. BackupRead opens a file and successively reads data streams from that file. Each data stream is preceeded by a WIN32_STREAM_ID struct. Arguments: hStream - File handle. Return Value: S_OK - Success. Algorithm: - with buffer, current_buf_position do: - while there are more streams loop - read next stream header using BackupRead - exit loop when no next stream - use stream header to append format MTF STREAM HEADER to buffer - flush as much of buffer as possible to the data set. - while entire stream not read loop - read as much of current stream as possible into remainder of buffer - flush as much of buffer as possible to the data set. - end loop this stream not read - end loop more streams - flush as much of the buffer to the data set - pad buffer out to next alignment factor - flush as much of the buffer to the data set --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CMTFSession::DoDataStream"), OLESTR("<0x%08x>"), hStream); try { MvrInjectError(L"Inject.CMTFSession::DoDataStream.0"); UINT16 uAlignmentFactor = m_pMTFApi->MTF_GetAlignmentFactor(); UINT64 fla = m_nFormatLogicalAddress + m_nBufUsed/uAlignmentFactor; WsbTrace(OLESTR("CMTFSession::DoDataStream - Start: FLA = %I64u\n"), fla); WIN32_STREAM_ID sStreamHeader; // comes back from Win32 BackupRead ULONG nThisRead; // number of bytes to read ULONG nBytesRead; // number of bytes read UINT64 nStreamBytesToRead; // total number bytes that we need to read UINT64 nStreamBytesRead; // total number bytes that have been read USHORT nStreamCount = 0; // current stream number MTF_STREAM_INFO sSTREAM; size_t nMoreBufUsed; BOOL bReadStatus = FALSE; // Prepare to calculate the CRC for the unnamed datastream BYTE* pCurrent; BYTE* pStart; ULONG datastreamCRC; BOOL doDatastreamCRC; memset(&sStreamHeader, 0, sizeof(WIN32_STREAM_ID)); INITIALIZE_CRC(datastreamCRC); WsbTrace(OLESTR("CMTFSession::DoDataStream initialzed CRC is <%lu> for <0x%08x>\n"), datastreamCRC, hStream); m_sHints.DatastreamCRCType = WSB_CRC_CALC_NONE; WsbTrace(OLESTR("CMTFSession::DoDataStream - Start While\n")); while(1) { // We want to do a CRC on the unnamed datastream doDatastreamCRC = FALSE; nBytesRead = 0; try { MvrInjectError(L"Inject.CMTFSession::DoDataStream.BackupRead.1.0"); // ** WIN32 File API Call - Backup read returns the file as a sequence of streams each // preceed by a WIN32_STREAM_ID struct. Note that this structure is a // variable size -- depending on the length of the name of the stream. // In any case, we are guaranteed at least 20 bytes of it // (WIN32_STREAM_ID_SIZE) nStreamCount++; WsbAffirmStatus(BackupRead(hStream, (BYTE *) &sStreamHeader, WIN32_STREAM_ID_SIZE, &nBytesRead, FALSE, TRUE, &m_pvReadContext)); MvrInjectError(L"Inject.CMTFSession::DoDataStream.BackupRead.1.1"); } catch (HRESULT catchHr) { // // CORRUPT FILE PROCESSING for stream header // hr = catchHr; WsbLogEvent(MVR_E_ERROR_IO_DEVICE, 0, NULL, WsbHrAsString(hr), NULL); // Write SPAD WsbAffirmHr(PadToNextFLA(TRUE)); // Make sure we are aligned with a FLA (i.e. the last DBLK was properly padded). // It won't be if we are having problems writing to tape. WsbAssert(0 == (m_nBufUsed % uAlignmentFactor), MVR_E_LOGIC_ERROR); UINT64 fla = m_nFormatLogicalAddress + m_nBufUsed/uAlignmentFactor; UINT64 pba = m_nPhysicalBlockAddress + (fla*uAlignmentFactor/m_nBlockSize); WsbTrace(OLESTR("%ls (CFIL) @ FLA %I64u (%I64u, %I64u)\n"), fla, pba, fla % (m_nBlockSize/uAlignmentFactor)); // Write a corrupt file (CFIL) DBLK MTF_DBLK_CFIL_INFO sCFILInfo; m_pMTFApi->MTF_SetCFILDefaults( &sCFILInfo ); sCFILInfo.uCorruptStreamNumber = nStreamCount; sCFILInfo.uStreamOffset = 0; m_sHeaderInfo.uControlBlockId = m_nCurrentBlockId++; m_sHeaderInfo.uFormatLogicalAddress = fla; WsbAssertNoError(m_pMTFApi->MTF_WriteCFILDblk(&m_sHeaderInfo, &sCFILInfo, m_pBuffer+m_nBufUsed, m_nBufSize-m_nBufUsed, &nMoreBufUsed)); m_nBufUsed += nMoreBufUsed; WsbAffirmHr(FlushBuffer(m_pBuffer, &m_nBufUsed)); WsbThrow(hr); }; if (nBytesRead < WIN32_STREAM_ID_SIZE) break; // **MTF API CALL** // now use the info in the stream header to fill in an mtf stream // header using the mtf call then write the resulting info to the // buffer. // BMD Note: special conditional code added on third arg for named data streams m_pMTFApi->MTF_SetSTREAMFromStreamId( &sSTREAM, &sStreamHeader, (sStreamHeader.dwStreamNameSize) ? sStreamHeader.dwStreamNameSize + 4 : 0 ); WsbAffirmHr(FlushBuffer(m_pBuffer, &m_nBufUsed)); // **MTF API CALL** // Write out the stream header. nMoreBufUsed = 0; WsbAssertNoError(m_pMTFApi->MTF_WriteStreamHeader(&sSTREAM, m_pBuffer+m_nBufUsed, m_nBufSize-m_nBufUsed, &nMoreBufUsed)); m_nBufUsed += nMoreBufUsed; // BMD Note: we need to put the size of the stream name in the MTF stream // right after the header. We'll write the name itself as part of the stream. // // ?? Should this be in MTF_WriteStreamHeader ?? if ( sStreamHeader.dwStreamNameSize ) { *(DWORD UNALIGNED *)(m_pBuffer + m_nBufUsed) = sStreamHeader.dwStreamNameSize; m_nBufUsed += sizeof( DWORD ); } // Save away the "STAN" stream start byte address, and size. // This is the one we recall. if ( 0 == memcmp( sSTREAM.acStreamId, "STAN", 4 ) ) { // This is an unnamed data stream, so there's no stream name. m_sHints.VerificationData.QuadPart = sSTREAM.uCheckSum; m_sHints.VerificationType = MVR_VERIFICATION_TYPE_HEADER_CRC; m_sHints.DataStart.QuadPart = m_nFormatLogicalAddress * uAlignmentFactor + m_nBufUsed - m_sHints.FileStart.QuadPart; m_sHints.DataSize.QuadPart = sSTREAM.uStreamLength; doDatastreamCRC = TRUE; m_sHints.DatastreamCRCType = WSB_CRC_CALC_MICROSOFT_32; } // the above stream should always fit... WsbAssert(m_nBufUsed < m_nBufSize, MVR_E_LOGIC_ERROR); // try to flush as many BLOCK SIZE chunks out of the buffer as possible WsbAffirmHr(FlushBuffer(m_pBuffer, &m_nBufUsed)); // now, while there is more data in the stream, read the rest of // the stream, or how ever much will fit into the buffer nStreamBytesToRead = m_pMTFApi->MTF_CreateUINT64(sStreamHeader.Size.LowPart, sStreamHeader.Size.HighPart) + sStreamHeader.dwStreamNameSize; nStreamBytesRead = 0; WsbTrace(OLESTR("CMTFSession::DoDataStream - Start Do\n")); do { nThisRead = 0; // we read as many bytes as will fit into our buffer, up to // the end of the stream min doesn't work well here... if (nStreamBytesToRead < (m_nBufSize - m_nBufUsed)) nThisRead = (ULONG) nStreamBytesToRead; else nThisRead = (ULONG)(m_nBufSize - m_nBufUsed); try { MvrInjectError(L"Inject.CMTFSession::DoDataStream.BackupRead.2.0"); // ** WIN32 File API Call - read nThisRead bytes, bail out if the read failed or // no bytes were read (assume done) bReadStatus = FALSE; bReadStatus = BackupRead(hStream, m_pBuffer + m_nBufUsed, nThisRead, &nBytesRead, FALSE, TRUE, &m_pvReadContext); nStreamBytesRead += nBytesRead; WsbAffirmStatus(bReadStatus); MvrInjectError(L"Inject.CMTFSession::DoDataStream.BackupRead.2.1"); } catch (HRESULT catchHr) { // // CORRUPT FILE PROCESSING for stream data // hr = catchHr; WsbLogEvent(MVR_E_ERROR_IO_DEVICE, 0, NULL, WsbHrAsString(hr), NULL); // Go to the last good byte m_nBufUsed += nBytesRead; // Pad to fill up size of file while( nStreamBytesRead < nStreamBytesToRead ) { for( ; (m_nBufUsed < m_nBufSize) && (nStreamBytesRead < nStreamBytesToRead); ++m_nBufUsed, ++nStreamBytesRead ) { m_pBuffer[m_nBufUsed] = 0; } WsbAffirmHr(FlushBuffer(m_pBuffer, &m_nBufUsed)); } WsbAffirmHr(FlushBuffer(m_pBuffer, &m_nBufUsed)); // Align on 4-byte boundary for( ; m_nBufUsed % 4; ++m_nBufUsed ){ m_pBuffer[m_nBufUsed] = 0; } // Write SPAD WsbAffirmHr(PadToNextFLA(TRUE)); // Make sure we are aligned with a FLA (i.e. the last DBLK was properly padded). // It won't be if we are having problems writing to tape. WsbAssert(0 == (m_nBufUsed % uAlignmentFactor), MVR_E_LOGIC_ERROR); UINT64 fla = m_nFormatLogicalAddress + m_nBufUsed/uAlignmentFactor; UINT64 pba = m_nPhysicalBlockAddress + (fla*uAlignmentFactor/m_nBlockSize); WsbTrace(OLESTR("%ls (CFIL) @ FLA %I64u (%I64u, %I64u)\n"), fla, pba, fla % (m_nBlockSize/uAlignmentFactor)); // Write a corrupt file (CFIL) DBLK MTF_DBLK_CFIL_INFO sCFILInfo; m_pMTFApi->MTF_SetCFILDefaults( &sCFILInfo ); sCFILInfo.uCorruptStreamNumber = nStreamCount; sCFILInfo.uStreamOffset = nStreamBytesRead; m_sHeaderInfo.uControlBlockId = m_nCurrentBlockId++; m_sHeaderInfo.uFormatLogicalAddress = fla; WsbAssertNoError(m_pMTFApi->MTF_WriteCFILDblk(&m_sHeaderInfo, &sCFILInfo, m_pBuffer+m_nBufUsed, m_nBufSize-m_nBufUsed, &nMoreBufUsed)); m_nBufUsed += nMoreBufUsed; WsbAffirmHr(FlushBuffer(m_pBuffer, &m_nBufUsed)); WsbThrow(hr); }; if (nBytesRead == 0) break; nStreamBytesToRead -= nBytesRead; pStart = m_pBuffer + m_nBufUsed; m_nBufUsed += nBytesRead; HRESULT hrCRC = S_OK; if (TRUE == doDatastreamCRC ) { for (pCurrent = pStart; (pCurrent < (pStart + nBytesRead)) && (S_OK == hr); pCurrent++) { hrCRC = WsbCRCReadFile(pCurrent, &datastreamCRC); if (S_OK != hrCRC) { WsbThrow(MVR_E_CANT_CALC_DATASTREAM_CRC); } } } // At this point we've got stuff in the buffer that might need // to be flushed so, try to do that WsbAffirmHr(FlushBuffer(m_pBuffer, &m_nBufUsed)); } while (nStreamBytesToRead > 0); WsbTrace(OLESTR("CMTFSession::DoDataStream - End Do\n")); // Okay. At this point we're done with the stream. As much as // possible was actually written out to the data set by FlushBuffer, but // some probably still remains in the buffer. It will get flushed // later on... At this point we need to align on a four byte // boundary. Once we do this, we can start all over again with // the next stream (if none, then we bail out of this loop) for( ; m_nBufUsed % 4; ++m_nBufUsed ) m_pBuffer[m_nBufUsed] = 0; } WsbTrace(OLESTR("CMTFSession::DoDataStream - End While\n")); // Finish off the unnamed datastream CRC stuff FINIALIZE_CRC(datastreamCRC); WsbTrace(OLESTR("CMTFSession::DoDataStream finalized CRC is <%lu>\n"), datastreamCRC); if (WSB_CRC_CALC_NONE != m_sHints.DatastreamCRCType) { // We have a CRC that we want to save in the hints. m_sHints.DatastreamCRC.QuadPart = datastreamCRC; } IO_STATUS_BLOCK IoStatusBlock; NTSTATUS ccode; // ** NT System Call - set file information // This call fixes the access time that can be changed by the BackupRead call above // When BackupRead is fixed this line should be removed. RAID 121023. // // IMPORTANT NOTE: This changes the USN, and must be done before we save the USN. // // TODO: See if we still need this HRESULT infoHr = S_OK; try { WsbAffirmNtStatus(ccode = NtSetInformationFile( hStream, &IoStatusBlock, (PVOID)&m_SaveBasicInformation, sizeof( m_SaveBasicInformation ), FileBasicInformation)); } WsbCatch(infoHr); // Get the USN of the file before we close it // // Before we close the file, get the USN // LONGLONG lUsn; if (S_OK == WsbGetUsnFromFileHandle(hStream, TRUE, &lUsn)) { m_sHints.FileUSN.QuadPart = lUsn; } else { // If we can't get the USN, then just set it to 0 // which is invalid. Don't stop things. m_sHints.FileUSN.QuadPart = 0; } // Now, were done with all of the streams. If there is data left // in the buffer, we need to pad out to the next alignment block boundary and // flush the buffer. WsbAffirmHr(FlushBuffer(m_pBuffer, &m_nBufUsed)); if (m_bCommitFile) { WsbTrace(OLESTR("CMTFSession::DoDataStream - Commit\n")); // Pad and Flush to next physical block WsbAffirmHr(PadToNextPBA()); // Now flush the device buffer. WsbAffirmNoError(WriteFilemarks(0)); } else { // Pad and Flush to next format logical block WsbAffirmHr(PadToNextFLA(TRUE)); } // Make sure we are aligned with a FLA (i.e. the last DBLK/stream was properly padded). WsbAssert(0 == (m_nBufUsed % uAlignmentFactor), MVR_E_LOGIC_ERROR); fla = m_nFormatLogicalAddress + m_nBufUsed/uAlignmentFactor; WsbTrace(OLESTR("CMTFSession::DoDataStream - End: FLA = %I64u\n"), fla);\ } WsbCatch(hr); WsbTraceOut(OLESTR("CMTFSession::DoDataStream"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return hr; } HRESULT CMTFSession::DoEndOfDataSet( IN USHORT nDataSetNumber) /*++ Routine Description: Formats and Writes an ESET DBLK. The end of data set sequence starts with a filemark (which terminates the file data), followed by an ESET, then a final filemark. Arguments: nDataSetNumber - The data set number. Used only in error recover. Otherwise The original data set number is used. Return Value: S_OK - Success. --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CMTFSession::DoEndOfDataSet"), OLESTR("<%d>"), nDataSetNumber); try { MvrInjectError(L"Inject.CMTFSession::DoEndOfDataSet.0"); WsbAssertPointer(m_pBuffer); MTF_DBLK_ESET_INFO sESET; // **MTF API STRUCT ** -- info for ESET size_t nMoreBufUsed; UINT16 uAlignmentFactor = m_pMTFApi->MTF_GetAlignmentFactor(); WsbAssert(m_nBlockSize > 0, MVR_E_LOGIC_ERROR); // // We can enter this routine in error recovery mode if // we need to write out an ESET at the end of a previously // written data set. In this case the Initialization flag // will be FALSE. // if (! m_bSetInitialized) { // This block of code is special to error recovery. (void) InitCommonHeader(); // Since we use the Init SSET block to retrieve ESET info // we need to initialize it. // **MTF API CALL** m_pMTFApi->MTF_SetSSETDefaults(&m_sSetInfo); // Reset the set attributes and DataSetNumber. m_sSetInfo.uSSETAttributes = 0; // TODO: This should match the original set attribute m_sSetInfo.uDataSetNumber = nDataSetNumber; // Can't be anyting in the buffer if we are only writing // out the ESET. WsbAssert(0 == m_nBufUsed, MVR_E_LOGIC_ERROR); } if (m_nBufUsed > 0) { // Write out an ESPB if we have something in the buffer. This conditional covers // the error recovery case where a missing ESET is detected. In this case we // don't have enough info to write an ESBP, and were already on a physical block // boundary, so we skip the ESPB. // Make sure we are aligned with a FLA (i.e. the last DBLK was properly padded). // It won't be if we are having problems writing to tape. WsbAffirm(0 == (m_nBufUsed % uAlignmentFactor), E_ABORT); UINT64 fla = m_nFormatLogicalAddress + m_nBufUsed/uAlignmentFactor; UINT64 pba = m_nPhysicalBlockAddress + (fla*uAlignmentFactor/m_nBlockSize); WsbTrace(OLESTR("Writing End of Set Pad (ESPB) @ FLA %I64u (%I64u, %I64u)\n"), fla, pba, fla % (m_nBlockSize/uAlignmentFactor)); // TODO: Not sure all the error cases are handled, here. What if we // end the set before completing the last I/O transfer. May need // to add code to write out CFIL. // Increment the BlockId and alignment index values that we keep in // our common block header structure. m_sHeaderInfo.uControlBlockId = m_nCurrentBlockId++; m_sHeaderInfo.uFormatLogicalAddress = fla; WsbAffirmHr(FlushBuffer(m_pBuffer, &m_nBufUsed)); // **MTF API CALL** // Write ESPB to pad the backup set to a phyical block boundary. nMoreBufUsed = 0; WsbAssertNoError(m_pMTFApi->MTF_WriteESPBDblk(&m_sHeaderInfo, m_pBuffer+m_nBufUsed, m_nBufSize-m_nBufUsed, &nMoreBufUsed)); m_nBufUsed += nMoreBufUsed; // Write out the ESPB DBLK and SPAD. WsbAffirmHr(PadToNextPBA()); } // Write a filemark to begin the end of data set sequence. This will flush the device buffer. WsbAffirmHr(WriteFilemarks(1)); // **MTF API CALL** // First set defaults for the info struct m_pMTFApi->MTF_SetESETDefaults(&sESET); sESET.uESETAttributes = m_sSetInfo.uSSETAttributes; sESET.uDataSetNumber = m_sSetInfo.uDataSetNumber; m_sHeaderInfo.uControlBlockId = m_nCurrentBlockId++; m_sHeaderInfo.uFormatLogicalAddress = 0; UINT64 curPos = 0; WsbAffirmHr(GetCurrentPBA(&curPos)); // From the stream I/O model WsbTrace(OLESTR("Writing End of Set (ESET) @ PBA %I64u\n"), curPos); // **MTF API CALL** // Provide the MTF_DBLK_HDR_INFO and MTF_DBLK_SSET_INFO structs to // this function. The result is an MTF formatted SSET DBLK in m_pBuffer. WsbAssert(0 == m_nBufUsed, MVR_E_LOGIC_ERROR); WsbAssertNoError(m_pMTFApi->MTF_WriteESETDblk(&m_sHeaderInfo, &sESET, m_pBuffer, m_nBufSize, &m_nBufUsed)); // Write out the ESET DBLK and SPAD. WsbAffirmHr(PadToNextPBA()); // NOTE: The PadToNextPBA() is a placeholder. // The On Media Catalog would be generated and written after the ESET DBLK and SPAD. // If we ever implement a catalog, we need to change the previous PadToNextPBA() to // PadToNextPLA(); // Write a filemark. This will flush the device buffer. WsbAffirmHr(WriteFilemarks(1)); } WsbCatch(hr); WsbTraceOut(OLESTR("CMTFSession::DoEndOfDataSet"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return hr; } HRESULT CMTFSession::ExtendLastPadToNextPBA(void) /*++ Routine Description: Re-writes the last SPAD in the transfer buffer to align with the next physical block boundary. This routine shoud only be used before flushing the device buffer to guarantee data is written to the physical device. Arguments: None. Return Value: S_OK - Success. Comments: !!! Not for CMTFSession internal use !!! --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CMTFSession::ExtendLastPadToNextPBA"), OLESTR("")); try { MvrInjectError(L"Inject.CMTFSession::ExtendLastPadToNextPBA.0"); WsbAssertPointer(m_pBuffer); // // The start of the SPAD could be in last part of a previous // block that was flushed. In this case the transfer buffer // contains the remaning portion of the SPAD, and the // SPAD cannot be extended so we simply return. // // If we hit EOM while in the middle of a file transfer, the // last thing in the transfer buffer won't be a SPAD. No SPAD // is indicated by m_nStartOfPad == 0. // if ((m_nBufUsed > 0) && (m_nStartOfPad > 0) && (m_nStartOfPad < m_nBufUsed)) { MTF_STREAM_INFO sSTREAM; // Verify that there's an SPAD within the valid part of the buffer. // Make sure our last pad pointer is at an SPAD. WsbAffirmNoError(m_pMTFApi->MTF_ReadStreamHeader(&sSTREAM, &m_pBuffer[m_nStartOfPad])); WsbAssert((0 == memcmp(sSTREAM.acStreamId, "SPAD", 4)), MVR_E_LOGIC_ERROR); // Now, make sure we aren't going to overwrite anything other than a trailing SPAD. WsbAssert(m_nBufUsed == (m_nStartOfPad + sizeof(MTF_STREAM_INFO) + sSTREAM.uStreamLength), MVR_E_LOGIC_ERROR); // Reset the amount of buffer used to the start of the current SPAD // in preparation for overwrite of SPAD to a physical block boundary. m_nBufUsed = m_nStartOfPad; WsbAffirmHr(PadToNextPBA()); } // Flush the device buffer. WsbAffirmHr(WriteFilemarks(0)); } WsbCatch(hr); WsbTraceOut(OLESTR("CMTFSession::ExtendLastPadToNextPBA"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return hr; } /*** Note: "Skip" methods used for Recovery assume that you may read FLA size blocks rather than PBA size block. Therefore, they muse be used only for files opened without the FILE_FLAG_NO_BUFFERING flag. If we come to the point where we must read only sector-size blocks, then some of this code should be enhanced! ***/ HRESULT CMTFSession::SkipOverTapeDblk(void) /*++ Routine Description: Skips over a TAPE DBLK and the following FILEMARK. Expects to find a full or partial TAPE DBLK but no other data. Arguments: None. Return Value: S_OK - Success. MVR_E_NOT_FOUND - Block is missing or cut in the middle --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CMTFSession::SkipOverTapeDblk"), OLESTR("")); try { ULONG bytesRead = 0; ULONG bytesToRead = m_nBlockSize; UINT64 fileMarkPos; // Read TAPE DBLK WsbAffirmHr(SetCurrentPBA(0)); WsbAffirmHr(ReadFromDataSet (m_pBuffer, bytesToRead, &bytesRead)); if (bytesRead < bytesToRead) { // incomplete block WsbThrow(MVR_E_NOT_FOUND); } // Check block MTF_DBLK_HDR_INFO sHdrInfo; m_pMTFApi->MTF_DBLK_HDR_INFO_ReadFromBuffer(&sHdrInfo, m_pBuffer); WsbAffirm(0 == memcmp(sHdrInfo.acBlockType, MTF_ID_TAPE, 4), MVR_E_UNKNOWN_MEDIA); // Next block should be a FILEMARK WsbAffirmHr(GetCurrentPBA(&fileMarkPos)); bytesRead = 0; WsbAffirmHr(ReadFromDataSet (m_pBuffer, bytesToRead, &bytesRead)); if (bytesRead < bytesToRead) { // incomplete block WsbThrow(MVR_E_NOT_FOUND); } // Check block m_pMTFApi->MTF_DBLK_HDR_INFO_ReadFromBuffer(&sHdrInfo, m_pBuffer); WsbAffirm(0 == memcmp(sHdrInfo.acBlockType, MTF_ID_SFMB, 4), MVR_E_INCONSISTENT_MEDIA_LAYOUT); // Keep Soft File Marks array updated if (TRUE == m_bUseSoftFilemarks) { m_pMTFApi->MTF_InsertSoftFilemark(m_pSoftFilemarks, (UINT32)fileMarkPos); } } WsbCatch(hr); WsbTraceOut(OLESTR("CMTFSession::SkipOverTapeDblk"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return hr; } HRESULT CMTFSession::SkipOverSSETDblk(OUT USHORT* pDataSetNumber) /*++ Routine Description: Skips over a SSET DBLK Expects to find a full or partial SSET DBLK but no other data. Arguments: pDataSetNumber - Data set number taken from the skipped block Return Value: S_OK - Success. MVR_E_NOT_FOUND - Block is missing or cut in the middle --*/ { HRESULT hr = S_OK; LARGE_INTEGER startBlockPosition = {0,0}; LARGE_INTEGER currentBlockPosition = {0,0}; WsbTraceIn(OLESTR("CMTFSession::SkipOverSSETDblk"), OLESTR("")); try { UINT16 uAlignmentFactor = m_pMTFApi->MTF_GetAlignmentFactor(); ULONG bytesRead = 0; ULONG bytesToRead = uAlignmentFactor; LARGE_INTEGER zero = {0,0}; m_nFormatLogicalAddress = 0; WsbAffirmHr(m_pStream->Seek(zero, STREAM_SEEK_CUR, (ULARGE_INTEGER *)&startBlockPosition)); // Read SSET DBLK WsbAffirmHr(m_pStream->Read(m_pBuffer, bytesToRead, &bytesRead)); if (bytesRead < bytesToRead) { // incomplete block WsbThrow(MVR_E_NOT_FOUND); } // Check block and get set number MTF_DBLK_HDR_INFO sHdrInfo; MTF_DBLK_SSET_INFO sSsetInfo; m_pMTFApi->MTF_ReadSSETDblk(&sHdrInfo, &sSsetInfo, m_pBuffer); WsbAffirm(0 == memcmp(sHdrInfo.acBlockType, MTF_ID_SSET, 4), MVR_E_INCONSISTENT_MEDIA_LAYOUT); *pDataSetNumber = m_sSetInfo.uDataSetNumber; // Skip over rest of the block WsbAffirmHr(SkipOverStreams(startBlockPosition.QuadPart + sHdrInfo.uOffsetToFirstStream)); WsbAffirmHr(m_pStream->Seek(zero, STREAM_SEEK_CUR, (ULARGE_INTEGER *)¤tBlockPosition)); m_nFormatLogicalAddress += (currentBlockPosition.QuadPart - startBlockPosition.QuadPart) / uAlignmentFactor; } WsbCatchAndDo(hr, // Seek back to the beginning of the block (void) m_pStream->Seek(startBlockPosition, STREAM_SEEK_SET, NULL); ); WsbTraceOut(OLESTR("CMTFSession::SkipOverSSETDblk"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return hr; } HRESULT CMTFSession::SkipToDataSet(void) /*++ Routine Description: Skips to the beginning of the next FILE DBLK Expects to find 0 to n other blocks such as DIRB DBLK. In case of a partial last block, stream pointer is set to the beginning of the partial block Arguments: None. Return Value: S_OK - Success. MVR_S_SETMARK_DETECTED - No more data sets (i.e. end-of-data-set detected) MVR_E_NOT_FOUND - Block is missing or cut in the middle --*/ { HRESULT hr = S_OK; LARGE_INTEGER startBlockPosition = {0,0}; LARGE_INTEGER currentBlockPosition = {0,0}; BOOL bIdRead = FALSE; WsbTraceIn(OLESTR("CMTFSession::SkipToDataSet"), OLESTR("")); try { UINT16 uAlignmentFactor = m_pMTFApi->MTF_GetAlignmentFactor(); ULONG bytesRead = 0; ULONG bytesToRead = uAlignmentFactor; LARGE_INTEGER zero = {0,0}; while (TRUE) { bIdRead = FALSE; // keep current position, before block starts WsbAffirmHr(m_pStream->Seek(zero, STREAM_SEEK_CUR, (ULARGE_INTEGER *)&startBlockPosition)); // Read block header WsbAffirmHr(m_pStream->Read(m_pBuffer, bytesToRead, &bytesRead)); if (bytesRead < bytesToRead) { // incomplete block WsbThrow(MVR_E_NOT_FOUND); } // Check block MTF_DBLK_HDR_INFO sHdrInfo; m_pMTFApi->MTF_DBLK_HDR_INFO_ReadFromBuffer(&sHdrInfo, m_pBuffer); m_nFormatLogicalAddress = sHdrInfo.uFormatLogicalAddress; m_nCurrentBlockId = sHdrInfo.uControlBlockId + 1; bIdRead = TRUE; if ((0 == memcmp(sHdrInfo.acBlockType, MTF_ID_VOLB, 4)) || (0 == memcmp(sHdrInfo.acBlockType, MTF_ID_DIRB, 4))) { // Just skip following streams WsbAffirmHr(SkipOverStreams(startBlockPosition.QuadPart + sHdrInfo.uOffsetToFirstStream)); WsbAffirmHr(m_pStream->Seek(zero, STREAM_SEEK_CUR, (ULARGE_INTEGER *)¤tBlockPosition)); m_nFormatLogicalAddress += (currentBlockPosition.QuadPart - startBlockPosition.QuadPart) / uAlignmentFactor; } else if (0 == memcmp(sHdrInfo.acBlockType, MTF_ID_FILE, 4)) { WsbAffirmHr(m_pStream->Seek(startBlockPosition, STREAM_SEEK_SET, NULL)); break; } else if (0 == memcmp(sHdrInfo.acBlockType, MTF_ID_SFMB, 4)) { // end of data-set reached, no ESPB block, must be alligned with PBA WsbAffirmHr(m_pStream->Seek(startBlockPosition, STREAM_SEEK_SET, NULL)); WsbAssert(0 == (startBlockPosition.QuadPart % m_nBlockSize), MVR_E_INCONSISTENT_MEDIA_LAYOUT); hr = MVR_S_SETMARK_DETECTED; break; } else if (0 == memcmp(sHdrInfo.acBlockType, MTF_ID_ESPB, 4)) { // last block in data-set found. Make sure it is complete WsbAffirmHr(SkipOverStreams(startBlockPosition.QuadPart + sHdrInfo.uOffsetToFirstStream)); WsbAffirmHr(m_pStream->Seek(zero, STREAM_SEEK_CUR, (ULARGE_INTEGER *)¤tBlockPosition)); WsbAssert(0 == (currentBlockPosition.QuadPart % m_nBlockSize), MVR_E_INCONSISTENT_MEDIA_LAYOUT); m_nFormatLogicalAddress += (currentBlockPosition.QuadPart - startBlockPosition.QuadPart) / uAlignmentFactor; hr = MVR_S_SETMARK_DETECTED; break; } else { // unexpected data WsbThrow(MVR_E_INCONSISTENT_MEDIA_LAYOUT); } } } WsbCatchAndDo(hr, // Seek back to the end of the last complete & valid block (void) m_pStream->Seek(startBlockPosition, STREAM_SEEK_SET, NULL); if (bIdRead) { m_nCurrentBlockId--; } ); WsbTraceOut(OLESTR("CMTFSession::SkipToDataSet"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return hr; } HRESULT CMTFSession::SkipOverDataSet(void) /*++ Routine Description: Skips over one FILE DBLK, including all of its data streams Expects to find a FILE DBLK. In case of a partial block, stream pointer is set back to the beginning of the block Arguments: None. Return Value: S_OK - Success. MVR_E_NOT_FOUND - Block is missing or cut in the middle --*/ { HRESULT hr = S_OK; LARGE_INTEGER startBlockPosition = {0,0}; LARGE_INTEGER currentBlockPosition = {0,0}; WsbTraceIn(OLESTR("CMTFSession::SkipOverDataSet"), OLESTR("")); try { UINT16 uAlignmentFactor = m_pMTFApi->MTF_GetAlignmentFactor(); ULONG bytesRead = 0; ULONG bytesToRead = uAlignmentFactor; LARGE_INTEGER zero = {0,0}; // keep current position, before block starts WsbAffirmHr(m_pStream->Seek(zero, STREAM_SEEK_CUR, (ULARGE_INTEGER *)&startBlockPosition)); // Read block header WsbAffirmHr(m_pStream->Read(m_pBuffer, bytesToRead, &bytesRead)); if (bytesRead < bytesToRead) { // incomplete block WsbThrow(MVR_E_NOT_FOUND); } // Check block MTF_DBLK_HDR_INFO sHdrInfo; m_pMTFApi->MTF_DBLK_HDR_INFO_ReadFromBuffer(&sHdrInfo, m_pBuffer); if (0 == memcmp(sHdrInfo.acBlockType, MTF_ID_FILE, 4)) { WsbAffirmHr(SkipOverStreams(startBlockPosition.QuadPart + sHdrInfo.uOffsetToFirstStream)); WsbAffirmHr(m_pStream->Seek(zero, STREAM_SEEK_CUR, (ULARGE_INTEGER *)¤tBlockPosition)); m_nFormatLogicalAddress += (currentBlockPosition.QuadPart - startBlockPosition.QuadPart) / uAlignmentFactor; } else { // unexpected data WsbThrow(MVR_E_INCONSISTENT_MEDIA_LAYOUT); } } WsbCatchAndDo(hr, // Seek back to the beginning of the block (void) m_pStream->Seek(startBlockPosition, STREAM_SEEK_SET, NULL); m_nCurrentBlockId--; ); WsbTraceOut(OLESTR("CMTFSession::SkipOverDataSet"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return hr; } HRESULT CMTFSession::SkipOverEndOfDataSet(void) /*++ Routine Description: Skips over one the sequence FILEMARK + ESET DBLK + FILEMARK Expects to find a FILE MARK, even if ESPB exists, it should have been already skipped. In case of a partial sequence, stream pointer is set back to the beginning of the sequence Arguments: None. Return Value: S_OK - Success. (It really means that the file is valid & complete) MVR_E_NOT_FOUND - Sequence is missing or cut in the middle --*/ { HRESULT hr = S_OK; LARGE_INTEGER startBlockPosition = {0,0}; UINT64 nFormatLogicalAddress = m_nFormatLogicalAddress; WsbTraceIn(OLESTR("CMTFSession::SkipOverEndOfDataSet"), OLESTR("")); try { UINT16 uAlignmentFactor = m_pMTFApi->MTF_GetAlignmentFactor(); ULONG bytesRead = 0; ULONG bytesToRead = m_nBlockSize; LARGE_INTEGER zero = {0,0}; // keep current position, before block starts WsbAffirmHr(m_pStream->Seek(zero, STREAM_SEEK_CUR, (ULARGE_INTEGER *)&startBlockPosition)); // Read block header m_nFormatLogicalAddress = startBlockPosition.QuadPart / uAlignmentFactor; WsbAffirmHr(ReadFromDataSet (m_pBuffer, bytesToRead, &bytesRead)); if (bytesRead < bytesToRead) { // incomplete block WsbThrow(MVR_E_NOT_FOUND); } // Check block, must be a FILE MARK MTF_DBLK_HDR_INFO sHdrInfo; m_pMTFApi->MTF_DBLK_HDR_INFO_ReadFromBuffer(&sHdrInfo, m_pBuffer); WsbAffirm(0 == memcmp(sHdrInfo.acBlockType, MTF_ID_SFMB, 4), MVR_E_INCONSISTENT_MEDIA_LAYOUT); // Read next block bytesRead = 0; WsbAffirmHr(ReadFromDataSet (m_pBuffer, bytesToRead, &bytesRead)); if (bytesRead < bytesToRead) { // incomplete block WsbThrow(MVR_E_NOT_FOUND); } // Check block, must be a ESET DBLK m_pMTFApi->MTF_DBLK_HDR_INFO_ReadFromBuffer(&sHdrInfo, m_pBuffer); WsbAffirm(0 == memcmp(sHdrInfo.acBlockType, MTF_ID_ESET, 4), MVR_E_INCONSISTENT_MEDIA_LAYOUT); // Read next block bytesRead = 0; WsbAffirmHr(ReadFromDataSet (m_pBuffer, bytesToRead, &bytesRead)); if (bytesRead < bytesToRead) { // incomplete block WsbThrow(MVR_E_NOT_FOUND); } // Check block, must be a FILEMARK m_pMTFApi->MTF_DBLK_HDR_INFO_ReadFromBuffer(&sHdrInfo, m_pBuffer); WsbAffirm(0 == memcmp(sHdrInfo.acBlockType, MTF_ID_SFMB, 4), MVR_E_INCONSISTENT_MEDIA_LAYOUT); } WsbCatchAndDo(hr, // Seek back to the beginning of the block (void) m_pStream->Seek(startBlockPosition, STREAM_SEEK_SET, NULL); m_nFormatLogicalAddress = nFormatLogicalAddress; ); WsbTraceOut(OLESTR("CMTFSession::SkipOverEndOfDataSet"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return hr; } HRESULT CMTFSession::PrepareForEndOfDataSet(void) /*++ Routine Description: Write an ESPB block in case that last complete fla is NOT aligned with pba File position should be aligned with pba after the method ends Arguments: None. Return Value: S_OK - Success. --*/ { HRESULT hr = S_OK; LARGE_INTEGER startBlockPosition = {0,0}; LARGE_INTEGER zero = {0,0}; UINT64 nRemainder; UINT64 nFormatLogicalAddress = m_nFormatLogicalAddress; WsbTraceIn(OLESTR("CMTFSession::PrepareForEndOfDataSet"), OLESTR("")); try { // ESPB block should be written only if: // 1. Physical Block size is larger than MTF Logical Block size // 2. Current location is not aligned with pba (it already must be aligned with fla) UINT16 uAlignmentFactor = m_pMTFApi->MTF_GetAlignmentFactor(); if (m_nBlockSize != uAlignmentFactor) { WsbAffirmHr(m_pStream->Seek(zero, STREAM_SEEK_CUR, (ULARGE_INTEGER *)&startBlockPosition)); nRemainder = startBlockPosition.QuadPart % m_nBlockSize; if (0 != nRemainder) { size_t nSizeUsed = 0; size_t nBufUsed = 0; ULONG bytesWritten = 0; ULONG bytesToWrite; WsbTrace(OLESTR("Writing ESPB for Recovery, completing a remainder of %I64u bytes (%I64u fla) to pba\n"), nRemainder, (nRemainder / uAlignmentFactor)); (void) InitCommonHeader(); m_sHeaderInfo.uControlBlockId = m_nCurrentBlockId++; m_sHeaderInfo.uFormatLogicalAddress = m_nFormatLogicalAddress; // **MTF API CALL** WsbAssertNoError(m_pMTFApi->MTF_WriteESPBDblk(&m_sHeaderInfo, m_pBuffer+m_nBufUsed, m_nBufSize, &nSizeUsed)); WsbAssertNoError(m_pMTFApi->MTF_PadToNextPhysicalBlockBoundary(m_pBuffer, m_nBlockSize, nSizeUsed, m_nBufSize, &nBufUsed)); // Write data and flush bytesToWrite = (ULONG)(m_nBlockSize - nRemainder); WsbAffirmHr(m_pStream->Write(m_pBuffer, bytesToWrite, &bytesWritten)); WsbAffirm((bytesWritten == bytesToWrite), E_FAIL); WsbAffirmHr(m_pStream->Commit(0)); // Flush the device buffers m_nFormatLogicalAddress += bytesWritten / uAlignmentFactor; } } } WsbCatchAndDo(hr, // Seek back to the beginning of the block (void) m_pStream->Seek(startBlockPosition, STREAM_SEEK_SET, NULL); m_nCurrentBlockId--; ); WsbTraceOut(OLESTR("CMTFSession::PrepareForEndOfDataSet"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return hr; } HRESULT CMTFSession::SkipOverStreams(IN UINT64 uOffsetToFirstStream) /*++ Routine Description: Skips over all streams of current block Expects to find a SPAD stream as the last one (if data is not truncated) Arguments: uOffsetToFirstStream - Offset to the beginning of the first stream (absolute position) Return Value: S_OK - Success. MVR_E_NOT_FOUND - Stream is missing or cut in the middle --*/ { HRESULT hr = S_OK; LARGE_INTEGER startStreamPosition = {0,0}; WsbTraceIn(OLESTR("CMTFSession::SkipOverStreams"), OLESTR("")); try { UINT16 uAlignmentFactor = m_pMTFApi->MTF_GetAlignmentFactor(); ULONG bytesRead = 0; ULONG bytesToRead = (ULONG)sizeof(MTF_STREAM_INFO); UINT64 uStreamLength; LARGE_INTEGER skipToPosition = {0,0}; LARGE_INTEGER endPosition = {0,0}; LARGE_INTEGER zero = {0,0}; BOOL bMoreStreams = TRUE; // Keep end position WsbAffirmHr(m_pStream->Seek(zero, STREAM_SEEK_END, (ULARGE_INTEGER *)&endPosition)); // Seek to begining of first stream skipToPosition.QuadPart = uOffsetToFirstStream; WsbAffirmHr(m_pStream->Seek(skipToPosition, STREAM_SEEK_SET, NULL)); while (bMoreStreams) { // keep current position, before stream starts startStreamPosition.QuadPart = skipToPosition.QuadPart; // Read stream header WsbAffirmHr(m_pStream->Read(m_pBuffer, bytesToRead, &bytesRead)); if (bytesRead < bytesToRead) { // incomplete stream WsbThrow(MVR_E_NOT_FOUND); } MTF_STREAM_INFO sHdrInfo; m_pMTFApi->MTF_ReadStreamHeader(&sHdrInfo, m_pBuffer); if (0 == memcmp(sHdrInfo.acStreamId, MTF_PAD_STREAM, 4)) { bMoreStreams = FALSE; } // Skip to the next stream uStreamLength = sHdrInfo.uStreamLength + sizeof(MTF_STREAM_INFO); if (uStreamLength % 4) { uStreamLength = uStreamLength - (uStreamLength % 4) + 4; } WsbAffirmHr(m_pStream->Seek(zero, STREAM_SEEK_CUR, (ULARGE_INTEGER *)&skipToPosition)); skipToPosition.QuadPart = skipToPosition.QuadPart + uStreamLength - bytesToRead; if (skipToPosition.QuadPart > endPosition.QuadPart) { // incomplete block WsbThrow(MVR_E_NOT_FOUND); } WsbAffirmHr(m_pStream->Seek(skipToPosition, STREAM_SEEK_SET, NULL)); } // If we got here, SPAD was found and skipped hence we must be FLA alligned WsbAssert(0 == (skipToPosition.QuadPart % uAlignmentFactor), MVR_E_INCONSISTENT_MEDIA_LAYOUT); } WsbCatchAndDo(hr, // Seek back to the end of the last complete & valid stream (void) m_pStream->Seek(startStreamPosition, STREAM_SEEK_SET, NULL); ); WsbTraceOut(OLESTR("CMTFSession::SkipOverStreams"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return hr; } HRESULT CMTFSession::PadToNextPBA(void) /*++ Routine Description: Writes an SPAD to the transfer buffer upto the next physical block boundary. Arguments: None. Return Value: S_OK - Success. --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CMTFSession::PadToNextPBA"), OLESTR("")); try { MvrInjectError(L"Inject.CMTFSession::PadToNextPBA.0"); WsbAssertPointer(m_pBuffer); // **MTF API CALL ** // Write an SPAD out to the next physical block boundary. WsbAssertNoError(m_pMTFApi->MTF_PadToNextPhysicalBlockBoundary(m_pBuffer, m_nBlockSize, m_nBufUsed, m_nBufSize, &m_nBufUsed)); // At this point our buffer should be padded out to // the next physical block boundary, which means it is // ready to be written in its entirety to the target // media. // Write out the data and SPAD stream. WsbAffirmHr(FlushBuffer(m_pBuffer, &m_nBufUsed)); // Everything in the buffer should be written out when // the buffer is aligned on a physical block boundary. WsbAssert(0 == m_nBufUsed, E_UNEXPECTED); } WsbCatch(hr); WsbTraceOut(OLESTR("CMTFSession::PadToNextPBA"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return hr; } HRESULT CMTFSession::PadToNextFLA( BOOL flush) /*++ Routine Description: Writes an SPAD to the transfer buffer upto format logical block boundary. Arguments: flush - if TRUE, the transfer buffer is flushed. Return Value: S_OK - Success. --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CMTFSession::PadToNextFLA"), OLESTR("<%ls>"), WsbBoolAsString(flush)); try { MvrInjectError(L"Inject.CMTFSession::PadToNextFLA.0"); WsbAssertPointer(m_pBuffer); size_t startOfPad; // **MTF API CALL ** // Write an SPAD out to the next alignment block boundary. startOfPad = m_nBufUsed; WsbAssertNoError(m_pMTFApi->MTF_PadToNextAlignmentFactor(m_pBuffer, m_nBufUsed, m_nBufSize, &m_nBufUsed)); if (flush) { // Write out data and SPAD stream. WsbAffirmHr(FlushBuffer(m_pBuffer, &m_nBufUsed)); } // Reset the location of the last SPAD within the buffer. // Note: The value is only valid of m_nStartOfPad < m_nBufUsed. m_nStartOfPad = (m_nBufUsed > 0) ? startOfPad % m_nBlockSize : 0; } WsbCatch(hr); WsbTraceOut(OLESTR("CMTFSession::PadToNextFLA"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return hr; } HRESULT CMTFSession::WriteToDataSet( IN BYTE *pBuffer, IN ULONG nBytesToWrite, OUT ULONG *pBytesWritten) /*++ Routine Description: Used to write all MTF data. Format Logical Address is updated to the current offset. Arguments: pBuffer - Data buffer. nBytesToWrite - number of bytes to write in buffer. pBytesWritten - Bytes written. Return Value: S_OK - Success. --*/ { HRESULT hr = S_OK; try { MvrInjectError(L"Inject.CMTFSession::WriteToDataSet.0"); WsbAssertPointer(m_pStream); WsbAssertPointer(pBuffer); WsbAssertPointer(pBytesWritten); *pBytesWritten = 0; // Make sure that we are asked to write only full blocks WsbAssert(!(nBytesToWrite % m_nBlockSize), MVR_E_LOGIC_ERROR); try { WsbAffirmHr(m_pStream->Write(pBuffer, nBytesToWrite, pBytesWritten)); } WsbCatch(hr); // Making sure that we are writing only full blocks if (*pBytesWritten != nBytesToWrite) { WsbTraceAlways(OLESTR("Asked to write %lu bytes but wrote only %lu bytes. Write hr = <%ls>\n"), nBytesToWrite, *pBytesWritten, WsbHrAsString(hr)); if (SUCCEEDED(hr)) { // Write "succeeded" buy didn't write all the bytes (full disk scenario): // Shouldn't happen since caller is expected to verify that there's enough free space in advance. hr = E_FAIL; } } // Update the total number of alignment factors m_nFormatLogicalAddress += *pBytesWritten / (m_pMTFApi->MTF_GetAlignmentFactor()); } WsbCatch(hr); return hr; } HRESULT CMTFSession::ReadFromDataSet ( IN BYTE *pBuffer, IN ULONG nBytesToRead, OUT ULONG *pBytesRead) /*++ Routine Description: Used to read all MTF data. Format Logical Address is updated to the current offset. Arguments: pBuffer - Data buffer. nBytesToRead - number of bytes to read into buffer. pBytesRead - Bytes read. Return Value: S_OK - Success. --*/ { HRESULT hr = S_OK; try { MvrInjectError(L"Inject.CMTFSession::ReadFromDataSet.0"); WsbAssertPointer(m_pStream); WsbAssertPointer(pBuffer); WsbAssertPointer(pBytesRead); // We need to set hr. MVR_S_FILEMARK_DETECTED, MVR_S_SETMARK_DETECTED are Okay. hr = m_pStream->Read(pBuffer, nBytesToRead, pBytesRead); // update the total number of alignment factors m_nFormatLogicalAddress += *pBytesRead / (m_pMTFApi->MTF_GetAlignmentFactor()); // Now test hr WsbAffirmHr(hr); // Make sure that we read only full blocks WsbAssert(!(*pBytesRead % m_nBlockSize), MVR_E_LOGIC_ERROR); } WsbCatch(hr); return hr; } HRESULT CMTFSession::FlushBuffer( IN BYTE *pBuffer, IN OUT size_t *pBufPosition) /*++ Routine Description: Writes as much of the buffer as possible out to the device. Any remaining data not written out is moved to the front of the buffer, and *pBufPosition is updated accordingly Arguments: pBuffer - Data buffer. pBufPosition - Number of bytes to write in buffer. On output holds the number of bytes still in the buffer. Return Value: S_OK - Success. --*/ { HRESULT hr = S_OK; ULONG uPosition = (ULONG)(*pBufPosition); try { MvrInjectError(L"Inject.CMTFSession::FlushBuffer.0"); // If the buffer has more than a physical block of bytes in it, dump as many as // possible to the device, then move the remaining data to the head of the buffer if (uPosition >= m_nBlockSize) { ULONG nBlocksToWrite; ULONG nBytesWritten = 0; // Determine the number of physical blocks to write nBlocksToWrite = uPosition / m_nBlockSize; try { // Write the data to the data set WsbAffirmHr(WriteToDataSet(pBuffer, nBlocksToWrite * m_nBlockSize, &nBytesWritten)); } WsbCatch(hr); // Adjust the buffer position and slide the unwritten data down in the buffer WsbAssert(uPosition >= nBytesWritten, E_UNEXPECTED); uPosition -= nBytesWritten; memmove(pBuffer, pBuffer + nBytesWritten, uPosition); // Invalidate the pad start location after any flush. This is reset in PadToNextFLA(). m_nStartOfPad = 0; } } WsbCatch(hr); // Set output *pBufPosition = (size_t)uPosition; return hr; } HRESULT CMTFSession::WriteFilemarks( IN ULONG nCount) /*++ Routine Description: Writes count filemarks at the current location. Arguments: nCount - Number of Filemarks to write. Return Value: S_OK - Success. --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CMTFSession::WriteFilemarks"), OLESTR("<%u>"), nCount); try { MvrInjectError(L"Inject.CMTFSession::WriteFilemarks.0"); WsbAssertPointer(m_pStream); WsbAssertPointer(m_pBuffer); UINT16 uAlignmentFactor = m_pMTFApi->MTF_GetAlignmentFactor(); if ( nCount > 0) { // Can't write a filemark with data still in the transfer buffer if nCount > 0! WsbAssert(0 == m_nBufUsed, MVR_E_LOGIC_ERROR); UINT64 pba = 0; UINT64 curPos = 0; WsbAffirmHr(GetCurrentPBA(&curPos)); // From the stream I/O model if ( m_nPhysicalBlockAddress > 0 ) { // Make sure the FLA aligns with a PBA! WsbAssert(0 == (m_nFormatLogicalAddress*uAlignmentFactor) % m_nBlockSize, MVR_E_LOGIC_ERROR); // Provided there's nothing in the transfer buffer, this is an accurate calc. pba = m_nPhysicalBlockAddress + ((m_nFormatLogicalAddress*uAlignmentFactor)/m_nBlockSize); // Make sure we are where we think we are. WsbAssert(curPos == pba, MVR_E_LOGIC_ERROR); } else { // // We skip the consistency check for the case were we're writing filemarks // through the session model and m_nPhysicalBlockAddress is uninitialzed. // This happens we we are writing an ESET sequence in dataset recovery code. // pba = curPos; } if (TRUE == m_bUseSoftFilemarks) { LONG n = nCount; if (n > 0) { UINT32 pba32 = (UINT32) pba; // Soft Filemark support only handles 2^32 * 1 KByte media (16 TBytes using 1 KByte logical Blocks) // Some day this won't be enough... and we'll know! WsbAssert((UINT64)pba32 == pba, E_UNEXPECTED); // One last check... Can't write out more filemarks, at one time, than can be stored in // the filemark table. WsbAssert(nCount < m_pSoftFilemarks->uNumberOfFilemarkEntries, E_UNEXPECTED); while(n-- > 0) { // **MTF API CALL** m_pMTFApi->MTF_InsertSoftFilemark(m_pSoftFilemarks, pba32++); // **MTF API CALL** WsbAssertNoError(m_pMTFApi->MTF_WriteSFMBDblk(&m_sHeaderInfo, m_pSoftFilemarks, m_pBuffer, m_nBufSize, &m_nBufUsed)); // Write out the SFMB DBLK. WsbAffirmHr(FlushBuffer(m_pBuffer, &m_nBufUsed)); // Everything should be written to media after a filemark! WsbAssert(0 == m_nBufUsed, MVR_E_LOGIC_ERROR); // PBA counter should never roll over! WsbAssert(pba32 > 0, E_UNEXPECTED); }; } WsbAffirmHr(m_pStream->Commit(0)); // Flush the device buffers // NOTE: The total number of alignment factors is updated via FlushBuffer(), // so we don't need to do it here. } else { // We use the IStream::Commit interface to write out the filemark. // This is not a perfect match in that the nCount parameter is supposed to // be a commit flag, not filemark count. Zero flushes device buffers // without writing a filemark. WsbAffirmHr(m_pStream->Commit(nCount)); // update the total number of alignment factors m_nFormatLogicalAddress += (nCount * m_nBlockSize) / uAlignmentFactor; } } else { // 0 == nCount implies flush device buffers. // // We skip all consistency checks since it is // is always safe to flush device buffers. // WsbAffirmHr(m_pStream->Commit(0)); // Flush the device buffers } } WsbCatch(hr); WsbTraceOut(OLESTR("CMTFSession::WriteFilemarks"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return hr; } HRESULT CMTFSession::GetCurrentPBA( OUT UINT64 *pPosition) /*++ Routine Description: Returns the current physical block address relative the current partition. Arguments: pPostion - Receives the current physical block address. Return Value: S_OK - Success. --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CMTFSession::GetCurrentPBA"), OLESTR("")); ULARGE_INTEGER position = {0xffffffff,0xffffffff}; try { MvrInjectError(L"Inject.CMTFSession::GetCurrentPBA.0"); WsbAssertPointer(m_pStream); WsbAssertPointer(pPosition); LARGE_INTEGER zero = {0,0}; // Gets the current position. WsbAffirmHr(m_pStream->Seek(zero, STREAM_SEEK_CUR, &position)); position.QuadPart = position.QuadPart / m_nBlockSize; *pPosition = position.QuadPart; } WsbCatch(hr); WsbTraceOut(OLESTR("CMTFSession::GetCurrentPBA"), OLESTR("hr = <%ls>, pos=%I64u"), WsbHrAsString(hr), position); return hr; } HRESULT CMTFSession::SetCurrentPBA( IN UINT64 position) /*++ Routine Description: Returns the current physical block address relative the current partition. Arguments: postion - The physical block address to position to. Return Value: S_OK - Success. --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CMTFSession::SetCurrentPBA"), OLESTR("<%I64u>"), position); try { WsbAssertPointer(m_pStream); LARGE_INTEGER seekTo; seekTo.QuadPart = position * m_nBlockSize; // Move to the specified position. WsbAffirmHr(m_pStream->Seek(seekTo, STREAM_SEEK_SET, NULL)); m_nPhysicalBlockAddress = position; } WsbCatch(hr); WsbTraceOut(OLESTR("CMTFSession::SetCurrentPBA"), OLESTR("hr = <%ls>, pos=%I64u"), WsbHrAsString(hr), m_nPhysicalBlockAddress); return hr; } HRESULT CMTFSession::SpaceToEOD(void) /*++ Routine Description: Positions the media to the end of data of the current partition. Arguments: None. Return Value: S_OK - Success. --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CMTFSession::SpaceToEOD"), OLESTR("")); UINT64 curPos = 0xffffffffffffffff; try { MvrInjectError(L"Inject.CMTFSession::SpaceToEOD.0"); WsbAssertPointer(m_pStream); LARGE_INTEGER zero = {0,0}; // Sets the current position to the end of data. WsbAffirmHr(m_pStream->Seek(zero, STREAM_SEEK_END, NULL)); WsbAffirmHr(GetCurrentPBA(&curPos)); m_nPhysicalBlockAddress = curPos; } WsbCatch(hr); WsbTraceOut(OLESTR("CMTFSession::SpaceToEOD"), OLESTR("hr = <%ls>, pos=%I64u"), WsbHrAsString(hr), curPos); return hr; } HRESULT CMTFSession::SpaceToBOD(void) /*++ Routine Description: Posotions the media to the beginnning of the current partition. Arguments: None. Return Value: S_OK - Success. --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CMTFSession::SpaceToBOD"), OLESTR("")); UINT64 curPos = 0xffffffffffffffff; try { MvrInjectError(L"Inject.CMTFSession::SpaceToBOD.0"); WsbAssertPointer(m_pStream); LARGE_INTEGER zero = {0,0}; // Sets the current position to the beginning of data. WsbAffirmHr(m_pStream->Seek(zero, STREAM_SEEK_SET, NULL)); WsbAffirmHr(GetCurrentPBA(&curPos)); m_nPhysicalBlockAddress = curPos; } WsbCatch(hr); WsbTraceOut(OLESTR("CMTFSession::SpaceToBOD"), OLESTR("hr = <%ls>, pos=%I64u"), WsbHrAsString(hr), curPos); return hr; } HRESULT CMTFSession::ReadTapeDblk(OUT WCHAR **pszLabel) /*++ Routine Description: Skips over a SSET DBLK Expects to find a full or partial SSET DBLK but no other data. Arguments: pszLabel - Pointer to a buffer to hold the RSS tape label. Reallocated as necessary Return Value: S_OK - Success. MVR_E_UNKNOWN_MEDIA - No TAPE DBLK or not RSS TAPE --*/ { HRESULT hr = S_OK; ULONG bytesRead = 0; WsbTraceIn(OLESTR("CMTFSession::ReadTapeDblk"), OLESTR("")); try { ULARGE_INTEGER position = {0,0}; LARGE_INTEGER zero = {0,0}; // The MTF labels are < 1024 bytes. We need to read 1024 bytes + the filemark // (1 block), 3x the min block size covers all cases. // The MTFSession work buffer is at least 2 blocks ULONG nBlocks = (3*512)/m_nBlockSize; nBlocks = (nBlocks < 2) ? 2 : nBlocks; ULONG bytesToRead = nBlocks * m_nBlockSize; WsbAssertPointer(m_pBuffer); memset(m_pBuffer, 0, bytesToRead); // Sets the current position to the beginning of data. WsbAffirmHr(m_pStream->Seek(zero, STREAM_SEEK_SET, &position)); // Read upto first Filemark. WsbAffirmHr(m_pStream->Read(m_pBuffer, bytesToRead, &bytesRead)); MTF_DBLK_HDR_INFO sHdrInfo; MTF_DBLK_TAPE_INFO sTapeInfo; m_pMTFApi->MTF_ReadTAPEDblk(&sHdrInfo, &sTapeInfo, m_pBuffer); // Is this a MTF Tape? WsbAffirm(0 == memcmp(sHdrInfo.acBlockType, MTF_ID_TAPE, 4), MVR_E_UNKNOWN_MEDIA); // Now try to identify it as one of ours, // using the following criteria: // 1) It has a UNICODE tape name and tape description and software name. // 2) It has our Vendor Id (accept both old Win2K id and current id). WsbAffirm(sHdrInfo.uStringType == MTF_STRING_UNICODE_STR, MVR_E_UNKNOWN_MEDIA); WsbAffirm(sTapeInfo.szTapeName, MVR_E_UNKNOWN_MEDIA); WsbAffirm(sTapeInfo.szTapeDescription, MVR_E_UNKNOWN_MEDIA); WsbAffirm(sTapeInfo.szSoftwareName, MVR_E_UNKNOWN_MEDIA); WsbAffirm((REMOTE_STORAGE_MTF_VENDOR_ID == sTapeInfo.uSoftwareVendorId) || (REMOTE_STORAGE_WIN2K_MTF_VENDOR_ID == sTapeInfo.uSoftwareVendorId), MVR_E_UNKNOWN_MEDIA); CWsbStringPtr label = sTapeInfo.szTapeDescription; *pszLabel = NULL; WsbAffirmHr(label.CopyTo(pszLabel)); } WsbCatchAndDo(hr, // Trace the illegal buffer where the RSS TAPE DBLK should reside if (m_pBuffer) { WsbTraceBuffer(bytesRead, m_pBuffer); } ); WsbTraceOut(OLESTR("CMTFSession::ReadTapeDblk"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return hr; }