Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3752 lines
110 KiB

/*++
© 1998 Seagate Software, Inc. All rights reserved.
Module Name:
NtFileIo.cpp
Abstract:
CNtFileIo class
Author:
Brian Dodd [brian] 25-Nov-1997
Revision History:
--*/
#include "stdafx.h"
#include "NtFileIo.h"
#include "engine.h"
#include "wsbfmt.h"
#include "Mll.h"
#include "ntmsapi.h"
#include "aclapi.h"
int CNtFileIo::s_InstanceCount = 0;
////////////////////////////////////////////////////////////////////////////////
//
// CComObjectRoot Implementation
//
#pragma optimize("g", off)
STDMETHODIMP
CNtFileIo::FinalConstruct(void)
/*++
Implements:
CComObjectRoot::FinalConstruct
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::FinalConstruct"), OLESTR(""));
try {
WsbAffirmHr(CComObjectRoot::FinalConstruct());
(void) CoCreateGuid( &m_ObjectId );
m_pSession = NULL;
m_DataSetNumber = 0;
m_hFile = INVALID_HANDLE_VALUE;
m_DeviceName = MVR_UNDEFINED_STRING;
m_Flags = 0;
m_LastVolume = OLESTR("");
m_LastPath = OLESTR("");
m_ValidLabel = TRUE;
m_StreamName = MVR_UNDEFINED_STRING;
m_Mode = 0;
m_StreamOffset.QuadPart = 0;
m_StreamSize.QuadPart = 0;
m_isLocalStream = FALSE;
m_OriginalAttributes = 0;
m_BlockSize = DefaultBlockSize;
} WsbCatch(hr);
s_InstanceCount++;
WsbTraceAlways(OLESTR("CNtFileIo::s_InstanceCount += %d\n"), s_InstanceCount);
WsbTraceOut(OLESTR("CNtFileIo::FinalConstruct"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CNtFileIo::FinalRelease(void)
/*++
Implements:
CComObjectRoot::FinalRelease
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::FinalRelease"), OLESTR(""));
try {
(void) CloseStream(); // in case anything is left open
CComObjectRoot::FinalRelease();
} WsbCatch(hr);
s_InstanceCount--;
WsbTraceAlways(OLESTR("CNtFileIo::s_InstanceCount -= %d\n"), s_InstanceCount);
WsbTraceOut(OLESTR("CNtFileIo::FinalRelease"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
#pragma optimize("", on)
HRESULT
CNtFileIo::CompareTo(
IN IUnknown *pCollectable,
OUT SHORT *pResult)
/*++
Implements:
CRmsComObject::CompareTo
--*/
{
HRESULT hr = E_FAIL;
SHORT result = 1;
WsbTraceIn( OLESTR("CNtFileIo::CompareTo"), OLESTR("") );
try {
// Validate arguments - Okay if pResult is NULL
WsbAssertPointer( pCollectable );
// We need the IRmsComObject interface to get the value of the object.
CComQIPtr<IDataMover, &IID_IDataMover> pObject = pCollectable;
WsbAssertPointer( pObject );
GUID objectId;
// Get objectId.
WsbAffirmHr( pObject->GetObjectId( &objectId ));
if ( m_ObjectId == objectId ) {
// Object IDs match
hr = S_OK;
result = 0;
}
else {
hr = S_FALSE;
result = 1;
}
}
WsbCatch( hr );
if ( SUCCEEDED(hr) && (0 != pResult) ){
*pResult = result;
}
WsbTraceOut( OLESTR("CNtFileIo::CompareTo"),
OLESTR("hr = <%ls>, result = <%ls>"),
WsbHrAsString( hr ), WsbPtrToShortAsString( pResult ) );
return hr;
}
HRESULT
CNtFileIo::IsEqual(
IUnknown* pObject
)
/*++
Implements:
IWsbCollectable::IsEqual().
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::IsEqual"), OLESTR(""));
hr = CompareTo(pObject, NULL);
WsbTraceOut(OLESTR("CNtFileIo::IsEqual"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return(hr);
}
////////////////////////////////////////////////////////////////////////////////
//
// ISupportErrorInfo Implementation
//
STDMETHODIMP
CNtFileIo::InterfaceSupportsErrorInfo(
IN REFIID riid)
/*++
Implements:
ISupportErrorInfo::InterfaceSupportsErrorInfo
--*/
{
static const IID* arr[] =
{
&IID_IDataMover,
&IID_IStream,
};
for (int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
{
if (InlineIsEqualGUID(*arr[i],riid))
return S_OK;
}
return S_FALSE;
}
////////////////////////////////////////////////////////////////////////////////
//
// IDataMover Implementation
//
STDMETHODIMP
CNtFileIo::GetObjectId(
OUT GUID *pObjectId)
/*++
Implements:
IRmsComObject::GetObjectId
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::GetObjectId"), OLESTR(""));
UNREFERENCED_PARAMETER(pObjectId);
try {
WsbAssertPointer( pObjectId );
*pObjectId = m_ObjectId;
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::GetObjectId"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CNtFileIo::BeginSession(
IN BSTR remoteSessionName,
IN BSTR remoteSessionDescription,
IN SHORT remoteDataSet,
IN DWORD options)
/*++
Implements:
IDataMover::BeginSession
Notes:
Each Mover session is written as a single MTF file data set. To create a consistant
MTF data set we copy the MediaLabel data and use it for the TAPE DBLK for
each data set generated.
--*/
{
HRESULT hr = S_OK;
CComPtr<IStream> pStream;
WsbTraceIn(OLESTR("CNtFileIo::BeginSession"), OLESTR("<%ls> <%ls> <%d> <0x%08x>"),
remoteSessionName, remoteSessionDescription, remoteDataSet, options);
try {
if (!(options & MVR_SESSION_METADATA)) {
WsbAssert(remoteDataSet > 0, MVR_E_INVALIDARG);
}
WsbAffirm(TRUE == m_ValidLabel, E_ABORT);
ULARGE_INTEGER nil = {0,0};
CWsbBstrPtr label, tempLabel;
const ULONG maxIdSize = 1024;
BYTE identifier[maxIdSize];
ULONG idSize;
ULONG idType;
DWORD mode;
// We need to read the label and use this label for each dataset created.
// One data set per session. One data set per remote file.
WsbAffirmHr(ReadLabel(&label));
tempLabel = label;
WsbAssertHr(VerifyLabel(tempLabel));
// Try recovery, that is look for an indication for an incomplete data-set remote files
// We continue even if Recovery fails since each data-set is kept in a separate file
// Note: This code should be protected with CS when we support multiple migration to the SAME media
(void) DoRecovery ();
// Create the remote stream used for the entire session.
// Use given remote session name as the remote file name
mode = MVR_MODE_WRITE;
if (options & MVR_SESSION_METADATA) {
mode |= MVR_FLAG_SAFE_STORAGE;
}
WsbAffirmHr(CreateRemoteStream(remoteSessionName, mode, L"",L"",nil,nil,nil,nil,nil,0,nil, &pStream));
WsbAssertPointer(pStream);
// Create the Recovery indicator (avoid creating for safe-storage files)
// Note: the Recovery indicator just indicates that a Recovery may be required
if (! (mode & MVR_FLAG_SAFE_STORAGE)) {
WsbAssert(m_StreamName != MVR_UNDEFINED_STRING, MVR_E_LOGIC_ERROR);
WsbAffirmHr(CreateRecoveryIndicator(m_StreamName));
}
// Write the TAPE DBLK and filemark
WsbAffirmHr(m_pSession->DoTapeDblk(label, maxIdSize, identifier, &idSize, &idType));
m_DataSetNumber = remoteDataSet;
// Convert session option type bits to MTFSessionType
MTFSessionType type;
switch (options & MVR_SESSION_TYPES) {
case MVR_SESSION_TYPE_TRANSFER:
type = MTFSessionTypeTransfer;
break;
case MVR_SESSION_TYPE_COPY:
type = MTFSessionTypeCopy;
break;
case MVR_SESSION_TYPE_NORMAL:
type = MTFSessionTypeNormal;
break;
case MVR_SESSION_TYPE_DIFFERENTIAL:
type = MTFSessionTypeDifferential;
break;
case MVR_SESSION_TYPE_INCREMENTAL:
type = MTFSessionTypeIncremental;
break;
case MVR_SESSION_TYPE_DAILY:
type = MTFSessionTypeDaily;
break;
default:
type = MTFSessionTypeCopy;
break;
}
// Write the SSET DBLK
WsbAffirmHr(m_pSession->DoSSETDblk(remoteSessionName, remoteSessionDescription, type, remoteDataSet));
} WsbCatchAndDo(hr,
if (pStream) {
(void) CloseStream();
}
);
WsbTraceOut(OLESTR("CNtFileIo::BeginSession"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CNtFileIo::EndSession(void)
/*++
Implements:
IDataMover::EndSession
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::EndSession"), OLESTR(""));
try {
WsbAffirm(TRUE == m_ValidLabel, E_ABORT);
// Write the trailing filemark, ESET DBLK, and filemark
WsbAffirmHr(m_pSession->DoEndOfDataSet(m_DataSetNumber));
} WsbCatch(hr);
(void) CloseStream();
if (! (m_Mode & MVR_FLAG_SAFE_STORAGE)) {
WsbAssert(m_StreamName != MVR_UNDEFINED_STRING, MVR_E_LOGIC_ERROR);
(void) DeleteRecoveryIndicator(m_StreamName);
}
// If Safe Storage flag is indicated, copy the temporary backup file to the dataset file
// We copy by delete & rename (instead of copy) so if the dataset file exists, it is consistent
if ((m_Mode & MVR_FLAG_SAFE_STORAGE) && (m_Mode & MVR_MODE_WRITE || m_Mode & MVR_MODE_APPEND)) {
CWsbBstrPtr datasetName;
int nLen, nExtLen;
DWORD dwStatus;
// Build dataset name
nLen = wcslen(m_StreamName);
nExtLen = wcslen(MVR_SAFE_STORAGE_FILETYPE);
WsbAffirmHr(datasetName.TakeFrom(NULL, nLen - nExtLen + wcslen(MVR_DATASET_FILETYPE) + 1));
wcsncpy(datasetName, m_StreamName, nLen-nExtLen);
wcscpy(&(datasetName[nLen-nExtLen]), MVR_DATASET_FILETYPE);
// No need to flush bedore Copy since flush-buffers always follows writing FILEMARKs
if (! DeleteFile(datasetName)) {
// DeleteFile may fail with NOT_FOUND if the dataset file is created for the first time
dwStatus = GetLastError();
if (ERROR_FILE_NOT_FOUND != dwStatus) {
WsbAffirmNoError(dwStatus);
}
}
WsbAffirmStatus(MoveFile(m_StreamName, datasetName));
}
// Clear internal data (such that another Mover Session could be started)
m_Flags = 0;
m_LastVolume = OLESTR("");
m_LastPath = OLESTR("");
m_ValidLabel = TRUE;
m_isLocalStream = FALSE;
m_OriginalAttributes = 0;
WsbTraceOut(OLESTR("CNtFileIo::EndSession"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CNtFileIo::StoreData(
IN BSTR localName,
IN ULARGE_INTEGER localDataStart,
IN ULARGE_INTEGER localDataSize,
IN DWORD flags,
OUT ULARGE_INTEGER* pRemoteDataSetStart,
OUT ULARGE_INTEGER* pRemoteFileStart,
OUT ULARGE_INTEGER* pRemoteFileSize,
OUT ULARGE_INTEGER* pRemoteDataStart,
OUT ULARGE_INTEGER* pRemoteDataSize,
OUT DWORD* pRemoteVerificationType,
OUT ULARGE_INTEGER* pRemoteVerificationData,
OUT DWORD* pDatastreamCRCType,
OUT ULARGE_INTEGER* pDatastreamCRC,
OUT ULARGE_INTEGER* pUsn)
/*++
Implements:
IDataMover::StoreData
--*/
{
HRESULT hr = S_OK;
HANDLE hSearchHandle = INVALID_HANDLE_VALUE;
HANDLE hFile = INVALID_HANDLE_VALUE;
WsbTraceIn(OLESTR("CNtFileIo::StoreData"), OLESTR("<%ls> <%I64u> <%I64u> <0x%08x>"),
WsbAbbreviatePath((WCHAR *) localName, 120), localDataStart.QuadPart, localDataSize.QuadPart, flags);
WsbTraceAlways(OLESTR("CNtFileIo::StoreData - Begin\n"));
try {
MvrInjectError(L"Inject.CNtFileIo::StoreData.0");
WsbAssertPointer(m_pSession);
WsbAffirm(TRUE == m_ValidLabel, E_ABORT);
// Default is to perform non-case sensitive searches.
// So knock down the posix flag.
m_Flags &= ~MVR_FLAG_POSIX_SEMANTICS;
// Default is to not commit after each file.
// So knock down the commit flag.
m_Flags &= ~MVR_FLAG_COMMIT_FILE;
// Default is to write one DIRB containing all directory info
// instead of writing a DIRB for each directory level.
// So knock down the write parent dir info flag.
m_Flags &= ~MVR_FLAG_WRITE_PARENT_DIR_INFO;
m_Flags |= flags;
m_Flags |= MVR_MODE_WRITE;
// Unconditionally set the case sensitive flag for each file.
// We allow this flag to be set on a per file basis
WsbTrace(OLESTR("Posix Semantics Flag: <%ls>\n"), WsbBoolAsString(MVR_FLAG_POSIX_SEMANTICS & m_Flags));
WsbAffirmHr(m_pSession->SetUseCaseSensitiveSearch(MVR_FLAG_POSIX_SEMANTICS & m_Flags));
// This tells the session object to pad to a block boundary and flush the device
// after the file is written.
WsbTrace(OLESTR("Commit Flag: <%ls>\n"), WsbBoolAsString(MVR_FLAG_COMMIT_FILE & m_Flags));
WsbAffirmHr(m_pSession->SetCommitFile(MVR_FLAG_COMMIT_FILE & m_Flags));
WsbTrace(OLESTR("ParentDirInfo Flag: <%ls>\n"), WsbBoolAsString(MVR_FLAG_WRITE_PARENT_DIR_INFO & m_Flags));
if ((MVR_FLAG_BACKUP_SEMANTICS & m_Flags) || (MVR_FLAG_HSM_SEMANTICS & m_Flags)) {
// Compare the volume and path with the last ones written to tape.
CWsbStringPtr pathname;
WCHAR *end;
LONG numChar;
pathname = localName;
// strip off the path and file name
end = wcschr((WCHAR *)pathname, L'\\');
WsbAssert(end != NULL, MVR_E_INVALIDARG);
numChar =(LONG)(end - (WCHAR *)pathname + 1); // keep the trailing backslash
WsbAssert(numChar > 0, E_UNEXPECTED);
((WCHAR *)pathname)[numChar] = L'\0';
// We do a case sensitive search if using Posix semantics.
WsbTrace(OLESTR("Comparing with last volume: <%ls>\n"), WsbAbbreviatePath((WCHAR *) m_LastVolume, 120));
if ( ((MVR_FLAG_POSIX_SEMANTICS & ~m_Flags)) && (0 != _wcsicmp((WCHAR *) m_LastVolume, (WCHAR *) pathname)) ||
((MVR_FLAG_POSIX_SEMANTICS & m_Flags) && (0 != wcscmp((WCHAR *) m_LastVolume, (WCHAR *) pathname))) ) {
// write the VOLB DBLK
WsbAffirmHr(m_pSession->DoVolumeDblk(pathname));
m_LastVolume = pathname;
}
pathname = localName;
// strip off the file name
end = wcsrchr((WCHAR *)pathname, L'\\');
WsbAssert(end != NULL, MVR_E_INVALIDARG);
numChar = (LONG)(end - (WCHAR *)pathname);
WsbAssert(numChar > 0, E_UNEXPECTED);
((WCHAR *)pathname)[numChar] = L'\0';
// pathname is now in the form "Volume{guid}\dir1\...\dirn"
// or "<drive letter>:\dir1\...\dirn"
/***
m_Flags |= MVR_FLAG_WRITE_PARENT_DIR_INFO;
***/
WsbTrace(OLESTR("Comparing with last path: <%ls>\n"), WsbAbbreviatePath((WCHAR *) m_LastPath, 120));
// We do a case sensitive search if using Posix semantics.
if ( ((MVR_FLAG_POSIX_SEMANTICS & ~m_Flags)) && (0 != _wcsicmp((WCHAR *) m_LastPath, (WCHAR *) pathname)) ||
((MVR_FLAG_POSIX_SEMANTICS & m_Flags) && (0 != wcscmp((WCHAR *) m_LastPath, (WCHAR *) pathname))) ) {
if (MVR_FLAG_HSM_SEMANTICS & m_Flags) {
// We're not supporting this anymore!
WsbThrow(E_NOTIMPL);
WCHAR szRoot[16];
// We use a flat file structure for MVR_FLAG_HSM_SEMANTICS
WsbAffirmHr(m_pSession->SetUseFlatFileStructure(TRUE));
// do DIRB DBLKs for root
wcscpy(szRoot, L"X:\\");
szRoot[0] = localName[0];
WsbAffirmHr(m_pSession->DoParentDirectories(szRoot));
}
else if (MVR_FLAG_WRITE_PARENT_DIR_INFO & m_Flags) {
// do a DIRB DBLK for each directory level of the file(s) to be backed up.
WsbAffirmHr(m_pSession->DoParentDirectories(pathname));
m_LastPath = pathname;
}
else {
// do one DIRB DBLK for the whole directory structure of the file(s) to be backed up.
WIN32_FIND_DATAW obFindData;
CWsbStringPtr tempPath;
DWORD additionalSearchFlags = 0;
additionalSearchFlags |= (m_Flags & MVR_FLAG_POSIX_SEMANTICS) ? FIND_FIRST_EX_CASE_SENSITIVE : 0;
tempPath = pathname;
tempPath.Prepend(OLESTR("\\\\?\\"));
if (NULL == wcschr((WCHAR *)tempPath+4, L'\\'))
{
// no path (i.e. we're at the root)
BY_HANDLE_FILE_INFORMATION obGetFileInfoData;
memset(&obGetFileInfoData, 0, sizeof(BY_HANDLE_FILE_INFORMATION));
tempPath.Append(OLESTR("\\"));
// ** WIN32 API Calls
WsbAffirmHandle(hFile = CreateFile(tempPath, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL));
WsbAffirmStatus(GetFileInformationByHandle(hFile, &obGetFileInfoData));
// 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;
}
else {
// ** WIN32 API Call - gets file info
WsbAffirmHandle(hSearchHandle = FindFirstFileEx((WCHAR *) tempPath, FindExInfoStandard, &obFindData, FindExSearchLimitToDirectories, 0, additionalSearchFlags));
}
WsbAffirmHr(m_pSession->DoDirectoryDblk((WCHAR *) pathname, &obFindData));
if (hSearchHandle != INVALID_HANDLE_VALUE) {
FindClose(hSearchHandle);
hSearchHandle = INVALID_HANDLE_VALUE;
}
if (hFile != INVALID_HANDLE_VALUE) {
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
}
m_LastPath = pathname;
}
}
}
// The following uses code to store multiple files, but the
// RS Hints is only valid for the last file. With the current
// implementation, the HSM engine sends one file request through
// StoreData at a time. The caveat is that Posix is case
// sensitive, and therefore files created in this fashion could
// overload the same filename (ignoring case) with multiple files.
WsbAffirmHr(m_pSession->DoDataSet(localName));
*pRemoteDataSetStart = m_pSession->m_sHints.DataSetStart;
*pRemoteFileStart = m_pSession->m_sHints.FileStart;
*pRemoteFileSize = m_pSession->m_sHints.FileSize;
*pRemoteDataStart = m_pSession->m_sHints.DataStart;
*pRemoteDataSize = m_pSession->m_sHints.DataSize;
*pRemoteVerificationType = m_pSession->m_sHints.VerificationType;
*pRemoteVerificationData = m_pSession->m_sHints.VerificationData;
*pDatastreamCRCType = m_pSession->m_sHints.DatastreamCRCType;
*pDatastreamCRC = m_pSession->m_sHints.DatastreamCRC;
*pUsn = m_pSession->m_sHints.FileUSN;
} WsbCatchAndDo(hr,
if (hSearchHandle != INVALID_HANDLE_VALUE) {
FindClose(hSearchHandle);
hSearchHandle = INVALID_HANDLE_VALUE;
}
if (hFile != INVALID_HANDLE_VALUE) {
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
}
WsbLogEvent(MVR_MESSAGE_DATA_TRANSFER_ERROR, 0, NULL,
WsbAbbreviatePath((WCHAR *) localName, 120), WsbHrAsString(hr), NULL);
// All fatal device errors are converted to E_ABORT so the calling code
// can detect this general class of problem.
switch(hr) {
case MVR_E_BUS_RESET:
case MVR_E_MEDIA_CHANGED:
case MVR_E_NO_MEDIA_IN_DRIVE:
case MVR_E_DEVICE_REQUIRES_CLEANING:
case MVR_E_SHARING_VIOLATION:
case MVR_E_ERROR_IO_DEVICE:
case MVR_E_ERROR_DEVICE_NOT_CONNECTED:
case MVR_E_ERROR_NOT_READY:
hr = E_ABORT;
break;
case MVR_E_INVALID_BLOCK_LENGTH:
case MVR_E_WRITE_PROTECT:
case MVR_E_CRC:
hr = MVR_E_MEDIA_ABORT;
break;
default:
break;
}
);
WsbTraceAlways(OLESTR("CNtFileIo::StoreData - End\n"));
WsbTraceOut(OLESTR("CNtFileIo::StoreData"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CNtFileIo::RecallData (
IN BSTR /*localName*/,
IN ULARGE_INTEGER /*localDataStart*/,
IN ULARGE_INTEGER /*localDataSize*/,
IN DWORD /*options*/,
IN BSTR /*migrateFileName*/,
IN ULARGE_INTEGER /*remoteDataSetStart*/,
IN ULARGE_INTEGER /*remoteFileStart*/,
IN ULARGE_INTEGER /*remoteFileSize*/,
IN ULARGE_INTEGER /*remoteDataStart*/,
IN ULARGE_INTEGER /*remoteDataSize*/,
IN DWORD /*verificationType*/,
IN ULARGE_INTEGER /*verificationData*/)
/*++
Implements:
IDataMover::RecallData
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::RecallData"), OLESTR(""));
try {
WsbThrow( E_NOTIMPL );
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::RecallData"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CNtFileIo::FormatLabel(
IN BSTR displayName,
OUT BSTR* pLabel)
/*++
Implements:
IDataMover::FormatLabel
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::FormatLabel"), OLESTR("<%ls>"), displayName);
try {
WsbAssertPointer(pLabel);
WsbAssertPointer(displayName);
WsbAssert(wcslen((WCHAR *)displayName) > 0, E_INVALIDARG);
WsbAssertPointer(m_pCartridge);
// Media Label or Description
CWsbBstrPtr label;
// Tag
label = OLESTR("MTF Media Label"); // Required text per MTF specification.
// Version
WsbAffirmHr(label.Append(OLESTR("|")));
WsbAffirmHr(label.Append(WsbLongAsString(MTF_FORMAT_VER_MAJOR)));
WsbAffirmHr(label.Append(OLESTR(".")));
WsbAffirmHr(label.Append(WsbLongAsString(MTF_FORMAT_VER_MINOR)));
// Vendor
WsbAffirmHr(label.Append(OLESTR("|")));
WsbAffirmHr(label.Append(REMOTE_STORAGE_MTF_VENDOR_NAME));
// Vendor Product ID
WsbAffirmHr(label.Append(OLESTR("|")));
WsbAffirmHr(label.Append(REMOTE_STORAGE_MLL_SOFTWARE_NAME));
// Creation Time Stamp
WsbAffirmHr(label.Append(OLESTR("|")));
WCHAR timeStamp[128];
time_t lTime;
time(&lTime);
wcsftime(timeStamp, 128, L"%Y/%m/%d.%H:%M:%S", localtime(&lTime));
WsbAffirmHr(label.Append(timeStamp));
// Cartridge Label
WsbAffirmHr(label.Append(OLESTR("|")));
if (m_pCartridge) {
// Use barcode if available
CWsbBstrPtr barcode;
if (S_OK == m_pCartridge->GetBarcode(&barcode)) {
WsbAffirmHr(label.Append(barcode));
}
else {
WsbAffirmHr(label.Append(displayName));
}
}
else {
WsbAffirmHr(label.Append(displayName));
}
// Side
WsbAffirmHr(label.Append(OLESTR("|")));
if (m_pCartridge) {
// TODO: This is broken, we need to know if the cartridge is inverted?
if (S_OK == m_pCartridge->IsTwoSided()) {
WsbAffirmHr(label.Append(OLESTR("2")));
}
else {
WsbAffirmHr(label.Append(OLESTR("1")));
}
}
else {
WsbAffirmHr(label.Append(OLESTR("1"))); // Default
}
// Media Id
GUID cartId;
WsbAffirmHr(label.Append(OLESTR("|")));
if (m_pCartridge) {
// Use cartridge Id
if (S_OK == m_pCartridge->GetCartridgeId(&cartId)) {
WsbAffirmHr(label.Append(WsbGuidAsString(cartId)));
}
else {
WsbAffirmHr(label.Append(WsbGuidAsString(GUID_NULL)));
}
}
else {
WsbAffirmHr(label.Append(WsbGuidAsString(GUID_NULL)));
}
// Media Domain Id
GUID mediaSetId;
WsbAffirmHr(label.Append(OLESTR("|")));
if (m_pCartridge) {
// Use MediaSet Id
if (S_OK == m_pCartridge->GetMediaSetId(&mediaSetId)) {
WsbAffirmHr(label.Append(WsbGuidAsString(mediaSetId)));
}
else {
WsbAffirmHr(label.Append(WsbGuidAsString(GUID_NULL)));
}
}
else {
WsbAffirmHr(label.Append(WsbGuidAsString(GUID_NULL)));
}
// Vendor Specific
WsbAffirmHr(label.Append(OLESTR("|VS:DisplayName=")));
WsbAffirmHr(label.Append(displayName));
WsbAffirmHr(label.CopyToBstr(pLabel));
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::FormatLabel"), OLESTR("hr = <%ls>, label = <%ls>"), WsbHrAsString(hr), *pLabel);
return hr;
}
STDMETHODIMP
CNtFileIo::WriteLabel(
IN BSTR label)
/*++
Implements:
IDataMover::WriteLabel
--*/
{
CComPtr<IStream> pStream;
HRESULT hr = S_OK;
CWsbBstrPtr DirName;
PSID pAdminSID = NULL;
PSID pSystemSID = NULL;
PACL pACL = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
#define REMOTE_DIR_NUM_ACE 2
EXPLICIT_ACCESS ea[REMOTE_DIR_NUM_ACE];
SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
SECURITY_ATTRIBUTES sa;
WsbTraceIn(OLESTR("CNtFileIo::WriteLabel"), OLESTR("<%ls>"), label);
try {
WsbAssertPointer(label);
WsbAssert(wcslen((WCHAR *)label) > 0, E_INVALIDARG);
WsbAssertPointer(m_pCartridge);
const ULONG maxIdSize = 1024;
BYTE identifier[maxIdSize];
ULONG idSize;
ULONG idType;
ULARGE_INTEGER nil = {0,0};
// WriteLabel should be the first access to the remote media.
// Therefore, some media initialization is done here:
// 1) Formatting the volume
// 2) Creating RSS directory
// (We may consider moving this initialization part to rms unit)
// Initialize volume (format in case of Removable Disk)
UINT type = GetDriveType(m_DeviceName);
switch (type) {
case DRIVE_REMOVABLE: {
// Format the volume on the media
WCHAR *driveName = 0;
WsbAffirmHr(m_DeviceName.CopyTo(&driveName));
// Remove trailing backslash from drive name
int len = wcslen(driveName);
WsbAffirm(len > 0, E_UNEXPECTED);
if (driveName[len-1] == OLECHAR('\\')) {
driveName[len-1] = OLECHAR('\0');
}
// If the volume is already formatted to NTFS, perform a quick format
BOOLEAN bQuickFormat = FALSE;
BOOLEAN bNoFS = FALSE;
WCHAR fileSystemType[MAX_PATH];
if (! GetVolumeInformation((WCHAR *)m_DeviceName, NULL, 0,
NULL, NULL, NULL, fileSystemType, MAX_PATH) ) {
DWORD status = GetLastError();
if (ERROR_UNRECOGNIZED_VOLUME == status) {
status = NO_ERROR;
bNoFS = TRUE;
}
WsbAffirmNoError(status);
}
if ( (! bNoFS) && (0 == wcscmp(L"NTFS", fileSystemType)) ) {
bQuickFormat = TRUE;
WsbTrace(OLESTR("CNtFileIo::WriteLabel: Quick formatting %ls to NTFS\n"), driveName);
} else {
WsbTrace(OLESTR("CNtFileIo::WriteLabel: Full formatting %ls to NTFS\n"), driveName);
}
hr = FormatPartition(driveName, // drive name
FSTYPE_NTFS, // format to NTFS
MVR_VOLUME_LABEL, // colume label
WSBFMT_ENABLE_VOLUME_COMPRESSION, // enable compression
bQuickFormat, // Full or Quick format
TRUE, // Force format
0); // Use default allocation size
WsbTrace(OLESTR("CNtFileIo::WriteLabel: Finish formatting hr=<%ls>\n"), WsbHrAsString(hr));
if (! SUCCEEDED(hr)) {
WsbLogEvent(MVR_MESSAGE_MEDIA_FORMAT_FAILED, 0, NULL, driveName, WsbHrAsString(hr), NULL);
WsbFree(driveName);
WsbAffirmHr(hr);
}
WsbFree(driveName);
break;
}
case DRIVE_FIXED:
// Delete files from RS remote directory
WsbAffirmHr(DeleteAllData());
break;
case DRIVE_CDROM:
case DRIVE_UNKNOWN:
case DRIVE_REMOTE:
case DRIVE_RAMDISK:
default:
WsbAssertHr(E_UNEXPECTED);
break;
}
// Prepare security attribute for admin only access:
memset(ea, 0, sizeof(EXPLICIT_ACCESS) * REMOTE_DIR_NUM_ACE);
// Create a SID for the local system account
WsbAssertStatus( AllocateAndInitializeSid( &SIDAuthNT, 1,
SECURITY_LOCAL_SYSTEM_RID,
0, 0, 0, 0, 0, 0, 0,
&pSystemSID) );
// Initialize an EXPLICIT_ACCESS structure for an ACE.
// The ACE allows the Administrators group full access to the directory
ea[0].grfAccessPermissions = FILE_ALL_ACCESS;
ea[0].grfAccessMode = SET_ACCESS;
ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
ea[0].Trustee.pMultipleTrustee = NULL;
ea[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[0].Trustee.TrusteeType = TRUSTEE_IS_USER;
ea[0].Trustee.ptstrName = (LPTSTR) pSystemSID;
// Create a SID for the Administrators group.
WsbAssertStatus( AllocateAndInitializeSid( &SIDAuthNT, 2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&pAdminSID) );
// Initialize an EXPLICIT_ACCESS structure for an ACE.
// The ACE allows the Administrators group full access to the directory
ea[1].grfAccessPermissions = FILE_ALL_ACCESS;
ea[1].grfAccessMode = SET_ACCESS;
ea[1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
ea[1].Trustee.pMultipleTrustee = NULL;
ea[1].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[1].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ea[1].Trustee.ptstrName = (LPTSTR) pAdminSID;
// Create a new ACL that contains the new ACEs.
WsbAffirmNoError( SetEntriesInAcl(REMOTE_DIR_NUM_ACE, ea, NULL, &pACL));
// Initialize a security descriptor.
pSD = (PSECURITY_DESCRIPTOR) WsbAlloc(SECURITY_DESCRIPTOR_MIN_LENGTH);
WsbAffirmPointer(pSD);
WsbAffirmStatus(InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION));
// Add the ACL to the security descriptor.
WsbAffirmStatus(SetSecurityDescriptorDacl
(pSD,
TRUE, // fDaclPresent flag
pACL,
FALSE)); // not a default DACL
// Initialize a security attributes structure.
sa.nLength = sizeof (SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = pSD;
sa.bInheritHandle = FALSE;
// Create the RSS directory with Admin Only access
WsbAffirmHr(GetRemotePath(&DirName));
if (! CreateDirectory(DirName, &sa)) {
DWORD status = GetLastError();
if ((status == ERROR_ALREADY_EXISTS) || (status == ERROR_FILE_EXISTS)) {
// Directory already exists on remote media - ignore it
status = NO_ERROR;
}
WsbAffirmNoError(status);
}
// Create the remote stream. Use fixed named for the media label file
WsbAffirmHr(CreateRemoteStream(MVR_LABEL_FILENAME, MVR_MODE_WRITE, L"",L"",nil,nil,nil,nil,nil,0,nil, &pStream));
WsbAssertPointer(pStream);
// Write the TAPE DBLK and filemark
WsbAssertPointer(m_pSession);
WsbAffirmHr(m_pSession->DoTapeDblk(label, maxIdSize, identifier, &idSize, &idType));
WsbAffirmHr(CloseStream());
pStream = NULL;
// Now verify the label
CWsbBstrPtr tempLabel;
WsbAffirmHr(ReadLabel(&tempLabel));
WsbAffirmHr(VerifyLabel(tempLabel));
// Now that the tape header is written, we update the cartridge info.
if (m_pCartridge) {
WsbAffirmHr(m_pCartridge->SetOnMediaLabel(label));
WsbAffirmHr(m_pCartridge->SetBlockSize(m_BlockSize));
// For files systems we ignore the TAPE DBLK identifier, and use file system info.
NTMS_FILESYSTEM_INFO fsInfo;
DWORD filenameLength;
DWORD fileSystemFlags;
WsbAffirmStatus(GetVolumeInformation( (WCHAR *)m_DeviceName, fsInfo.VolumeName, 64,
&fsInfo.SerialNumber, &filenameLength, &fileSystemFlags, fsInfo.FileSystemType, 256));
WsbAffirmHr(m_pCartridge->SetOnMediaIdentifier((BYTE *)&fsInfo, sizeof(NTMS_FILESYSTEM_INFO), RmsOnMediaIdentifierWIN32));
}
} WsbCatchAndDo(hr,
if (pStream) {
(void) CloseStream();
}
);
// Cleanup security allocations
if (pAdminSID)
FreeSid(pAdminSID);
if (pSystemSID)
FreeSid(pSystemSID);
if (pACL)
LocalFree(pACL);
if (pSD)
WsbFree(pSD);
WsbTraceOut(OLESTR("CNtFileIo::WriteLabel"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CNtFileIo::ReadLabel(
IN OUT BSTR* pLabel)
/*++
Implements:
IDataMover::ReadLabel
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::ReadLabel"), OLESTR(""));
CComPtr<IStream> pStream;
try {
WsbAssertPointer(pLabel);
WsbAssert(m_BlockSize > 0, MVR_E_LOGIC_ERROR);
// Read the MTF TAPE DBLK, and pull out the label.
ULARGE_INTEGER nil = {0,0};
// Create remote stream of copy
WsbAffirmHr(CreateRemoteStream(MVR_LABEL_FILENAME, MVR_MODE_READ | MVR_MODE_UNFORMATTED, L"",L"",nil,nil,nil,nil,nil,0,nil, &pStream));
WsbAssertPointer(pStream);
// Read label
CWsbStringPtr label;
WsbAffirmHr(m_pSession->ReadTapeDblk(&label));
WsbAffirmHr(CloseStream());
pStream = NULL;
WsbAffirmHr(label.CopyToBstr(pLabel));
} WsbCatchAndDo(hr,
if (pStream) {
(void) CloseStream();
}
);
WsbTraceOut(OLESTR("CNtFileIo::ReadLabel"), OLESTR("hr = <%ls>, label = <%ls>"), WsbHrAsString(hr), *pLabel);
return hr;
}
STDMETHODIMP
CNtFileIo::VerifyLabel(
IN BSTR label)
/*++
Implements:
IDataMover::VerifyLabel
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::VerifyLabel"), OLESTR("<%ls>"), label);
GUID mediaId[2];
try {
WsbAssertPointer(label);
WsbAssert(wcslen((WCHAR *)label) > 0, E_INVALIDARG);
WsbAssertPointer(m_pCartridge);
//
// To verify a label we assert that the on-media Id matches the cartridge Id.
//
// From the media label we obtain the on-media Id.
//
WCHAR delim[] = OLESTR("|");
WCHAR *token;
int index = 0;
token = wcstok((WCHAR *)label, delim); // !!! This toasts the string !!!
while( token != NULL ) {
index++;
switch ( index ) {
case 1: // Tag
case 2: // Version
case 3: // Vendor
case 4: // Vendor Product ID
case 5: // Creation Time Stamp
case 6: // Cartridge Label
case 7: // Side
break;
case 8: // Media ID
WsbGuidFromString(token, &mediaId[0]);
break;
case 9: // Media Domain ID
default: // Vendor specific of the form: L"VS:Name=Value"
break;
}
token = wcstok( NULL, delim );
}
if (m_pCartridge) {
//
// Now compare on-media Id taken from the label to the cartridge's object Id.
//
WsbAffirmHr(m_pCartridge->GetCartridgeId(&mediaId[1]));
WsbAffirm(mediaId[0] == mediaId[1], MVR_E_UNEXPECTED_MEDIA_ID_DETECTED);
}
m_ValidLabel = TRUE;
} WsbCatchAndDo(hr,
m_ValidLabel = FALSE;
CWsbBstrPtr name;
CWsbBstrPtr desc;
if ( m_pCartridge ) {
m_pCartridge->GetName(&name);
m_pCartridge->GetDescription(&desc);
}
WsbLogEvent(MVR_MESSAGE_ON_MEDIA_ID_VERIFY_FAILED, 2*sizeof(GUID), mediaId,
(WCHAR *) name, (WCHAR *) desc, WsbHrAsString(hr), NULL);
);
WsbTraceOut(OLESTR("CNtFileIo::VerifyLabel"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CNtFileIo::GetDeviceName(
OUT BSTR* pName)
/*++
Implements:
IDataMover::GetDeviceName
--*/
{
HRESULT hr = S_OK;
try {
WsbAssertPointer(pName);
WsbAffirmHr(m_DeviceName.CopyToBstr(pName));
} WsbCatch( hr );
return hr;
}
STDMETHODIMP
CNtFileIo::SetDeviceName(
IN BSTR name,
IN BSTR /*unused*/)
/*++
Implements:
IDataMover::SetDeviceName
--*/
{
HRESULT hr = S_OK;
try {
WsbAssertPointer(name);
m_DeviceName = name;
} WsbCatch(hr);
return S_OK;
}
STDMETHODIMP
CNtFileIo::GetLargestFreeSpace(
OUT LONGLONG* pFreeSpace,
OUT LONGLONG* pCapacity,
IN ULONG defaultFreeSpaceLow,
IN LONG defaultFreeSpaceHigh
)
/*++
Implements:
IDataMover::GetLargestFreeSpace
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::GetLargestFreeSpace"), OLESTR(""));
UNREFERENCED_PARAMETER(defaultFreeSpaceLow);
UNREFERENCED_PARAMETER(defaultFreeSpaceHigh);
LONGLONG capacity = MAXLONGLONG;
LONGLONG remaining = MAXLONGLONG;
try {
// Note: Fot File I/O, we currentlym always go to the file system to query
// for free space and capacity and avoid internal counting like in tape.
// If we want to use internal counting (IRmsStorageInfo interface of m_pCartridge),
// then we need to maintain it by calling IncrementBytesWritten when appropriate
ULARGE_INTEGER freeSpaceForCaller;
ULARGE_INTEGER totalCapacity;
ULARGE_INTEGER totalFreeSpace;
capacity = MAXLONGLONG;
remaining = MAXLONGLONG;
try {
// WIN32 - get disk free space
WsbAffirmStatus(GetDiskFreeSpaceEx( m_DeviceName, &freeSpaceForCaller, &totalCapacity, &totalFreeSpace));
capacity = totalCapacity.QuadPart;
remaining = freeSpaceForCaller.QuadPart;
} WsbCatchAndDo(hr,
hr = MapFileError(hr);
WsbLogEvent(MVR_MESSAGE_DEVICE_ERROR, 0, NULL, WsbHrAsString(hr), NULL);
WsbThrow(hr);
);
} WsbCatch(hr);
// Fill in the return parameters
if ( pCapacity ) {
*pCapacity = capacity;
}
if ( pFreeSpace ) {
*pFreeSpace = remaining;
}
WsbTraceOut(OLESTR("CNtFileIo::GetLargestFreeSpace"), OLESTR("hr = <%ls>, free=%I64u, capacity=%I64u"), WsbHrAsString(hr), remaining, capacity);
return hr;
}
STDMETHODIMP
CNtFileIo::SetInitialOffset(
IN ULARGE_INTEGER initialOffset
)
/*++
Implements:
IDataMover::SetInitialOffset
Notes:
Set Initial stream offset (without explicitly seeking the stream to this offset)
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::SetInitialOffset"), OLESTR(""));
m_StreamOffset.QuadPart = initialOffset.QuadPart;
WsbTraceOut(OLESTR("CNtFileIo::SetInitialOffset"), OLESTR("hr = <%ls> offset = %I64u"), WsbHrAsString(hr), initialOffset.QuadPart);
return hr;
}
STDMETHODIMP
CNtFileIo::GetCartridge(
OUT IRmsCartridge** ptr
)
/*++
Implements:
IDataMover::GetCartridge
--*/
{
HRESULT hr = S_OK;
try {
WsbAssertPointer( ptr );
*ptr = m_pCartridge;
m_pCartridge->AddRef();
} WsbCatch( hr );
return hr;
}
STDMETHODIMP
CNtFileIo::SetCartridge(
IN IRmsCartridge* ptr
)
/*++
Implements:
IDataMover::SetCartridge
--*/
{
HRESULT hr = S_OK;
try {
WsbAssertPointer( ptr );
if ( m_pCartridge )
m_pCartridge = 0;
m_pCartridge = ptr;
} WsbCatch( hr );
return hr;
}
STDMETHODIMP
CNtFileIo::Cancel(void)
/*++
Implements:
IDataMover::Cancel
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::Cancel"), OLESTR(""));
try {
(void) Revert();
(void) CloseStream();
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::Cancel"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CNtFileIo::CreateLocalStream(
IN BSTR name,
IN DWORD mode,
OUT IStream** ppStream)
/*++
Implements:
IDataMover::CreateLocalStream
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::CreateLocalStream"), OLESTR(""));
try {
WsbAffirmPointer( ppStream );
WsbAffirm( mode & MVR_MODE_WRITE, E_UNEXPECTED ); // Only Recall or Restore supported this way.
FILE_BASIC_INFORMATION basicInformation;
IO_STATUS_BLOCK IoStatusBlock;
m_Mode = mode;
m_StreamName = name;
m_isLocalStream = TRUE;
m_StreamOffset.QuadPart = 0;
m_StreamSize.QuadPart = 0;
m_OriginalAttributes = GetFileAttributes(name);
if ( 0xffffffff == m_OriginalAttributes ) {
WsbAssertNoError(GetLastError());
} else if ( m_OriginalAttributes & FILE_ATTRIBUTE_READONLY ) {
//
// Set it to read/write
//
WsbAssertStatus(SetFileAttributes(m_StreamName, m_OriginalAttributes & ~FILE_ATTRIBUTE_READONLY));
}
DWORD posixFlag = (m_Mode & MVR_FLAG_POSIX_SEMANTICS) ? FILE_FLAG_POSIX_SEMANTICS : 0;
if ( m_Mode & MVR_FLAG_HSM_SEMANTICS ) {
//
// Recall - File must already exits!
//
WsbAffirmHandle(m_hFile = CreateFile(m_StreamName,
GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT | posixFlag,
NULL));
//
// Mark the USN source for this handle (So content indexing knows there is no real change)
//
WsbAffirmHr(WsbMarkUsnSource(m_hFile, m_DeviceName));
} else {
//
// Restore
//
WsbAffirmHandle(m_hFile = CreateFile(m_StreamName,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT | posixFlag,
NULL));
}
//
// Set the time flags so that when we close the handle the
// times are not updated on the file and the FileAttributes
// indicate the file is offline
//
WsbAffirmNtStatus(NtQueryInformationFile(m_hFile,
&IoStatusBlock,
(PVOID)&basicInformation,
sizeof(basicInformation),
FileBasicInformation));
basicInformation.CreationTime.QuadPart = -1;
basicInformation.LastAccessTime.QuadPart = -1;
basicInformation.LastWriteTime.QuadPart = -1;
basicInformation.ChangeTime.QuadPart = -1;
WsbAffirmNtStatus(NtSetInformationFile(m_hFile,
&IoStatusBlock,
(PVOID)&basicInformation,
sizeof(basicInformation),
FileBasicInformation));
WsbAssertHrOk(((IUnknown*) (IDataMover*) this)->QueryInterface(IID_IStream, (void **) ppStream));
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::CreateLocalStream"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CNtFileIo::CreateRemoteStream(
IN BSTR name,
IN DWORD mode,
IN BSTR remoteSessionName,
IN BSTR remoteSessionDescription,
IN ULARGE_INTEGER remoteDataSetStart,
IN ULARGE_INTEGER remoteFileStart,
IN ULARGE_INTEGER remoteFileSize,
IN ULARGE_INTEGER remoteDataStart,
IN ULARGE_INTEGER remoteDataSize,
IN DWORD remoteVerificationType,
IN ULARGE_INTEGER remoteVerificationData,
OUT IStream** ppStream)
/*++
Implements:
IDataMover::CreateRemoteStream
--*/
{
UNREFERENCED_PARAMETER(remoteSessionName);
UNREFERENCED_PARAMETER(remoteSessionDescription);
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::CreateRemoteStream"), OLESTR(""));
try {
WsbAffirmPointer( ppStream );
m_Mode = mode;
WsbAffirmHr(GetRemotePath(&m_StreamName));
// Use given name as file-name here, use remoteSessionName only if name is NULL
if (name && (0 < wcslen((WCHAR *)name))) {
WsbAffirmHr(m_StreamName.Append(name));
} else {
WsbAffirmHr(m_StreamName.Append(remoteSessionName));
}
// Add file extension
// Note: In case of safe storage, we write to a temporary file.
// After a successful store, we rename the temporary file to the real file name
if ((m_Mode & MVR_FLAG_SAFE_STORAGE) && (m_Mode & MVR_MODE_WRITE || m_Mode & MVR_MODE_APPEND)) {
WsbAffirmHr(m_StreamName.Append(MVR_SAFE_STORAGE_FILETYPE));
} else {
WsbAffirmHr(m_StreamName.Append(MVR_DATASET_FILETYPE));
}
m_StreamOffset.QuadPart = 0;
m_StreamSize.QuadPart = remoteDataSize.QuadPart;
WsbTrace(OLESTR("CNtFileIo::CreateRemoteStream: Creating <%ls>\n"), (WCHAR *)m_StreamName);
if (m_Mode & MVR_FLAG_HSM_SEMANTICS || m_Mode & MVR_MODE_READ) {
//
// File must already exists!
//
DWORD dwFlags = FILE_ATTRIBUTE_NORMAL;
if (m_Mode & MVR_FLAG_NO_CACHING) {
dwFlags |= FILE_FLAG_NO_BUFFERING;
}
WsbAffirmHandle(m_hFile = CreateFile(m_StreamName,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
dwFlags,
NULL));
} else if (m_Mode & MVR_MODE_RECOVER) {
//
// Open for R/W an already existsing file
//
WsbAffirmHandle(m_hFile = CreateFile(m_StreamName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, // cannot use FILE_FLAG_NO_BUFFERING here !!
NULL));
} else {
//
// Create Data Set or Media Label
//
WsbAffirmHandle(m_hFile = CreateFile(m_StreamName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING,
NULL));
}
// Create and initialize an MTF Session object
CComPtr<IStream> pStream;
WsbAssertHrOk(((IUnknown*) (IDataMover*) this)->QueryInterface( IID_IStream, (void **) &pStream));
WsbAssert(NULL == m_pSession, MVR_E_LOGIC_ERROR);
m_pSession = new CMTFSession();
WsbAssertPointer(m_pSession);
m_pSession->m_pStream = pStream;
m_pSession->m_sHints.DataSetStart.QuadPart = remoteDataSetStart.QuadPart;
m_pSession->m_sHints.FileStart.QuadPart = remoteFileStart.QuadPart;
m_pSession->m_sHints.FileSize.QuadPart = remoteFileSize.QuadPart;
m_pSession->m_sHints.DataStart.QuadPart = remoteDataStart.QuadPart;
m_pSession->m_sHints.DataSize.QuadPart = remoteDataSize.QuadPart;
m_pSession->m_sHints.VerificationType = remoteVerificationType;
m_pSession->m_sHints.VerificationData.QuadPart = remoteVerificationData.QuadPart;
// Set block size according to device sector size
// (On FS-based media, the sector size is fixed, therefore we ignore the cached value in the cartridge record)
DWORD dummy1, dummy2, dummy3;
WsbAffirmStatus(GetDiskFreeSpace(m_DeviceName, &dummy1, &m_BlockSize, &dummy2, &dummy3));
WsbAssert((m_BlockSize % 512) == 0, E_UNEXPECTED);
WsbTrace( OLESTR("Setting Block Size to %d bytes/block.\n"), m_BlockSize);
// Set the Block Size used for the session.
WsbAffirmHr(m_pSession->SetBlockSize(m_BlockSize));
// Set the Block Size used for the session.
WsbAffirmHr(m_pSession->SetUseSoftFilemarks(TRUE));
if (m_Mode & MVR_MODE_APPEND) {
// Sets the current position to the end of data.
LARGE_INTEGER zero = {0,0};
WsbAffirmHr(pStream->Seek(zero, STREAM_SEEK_END, NULL));
}
*ppStream = pStream;
pStream->AddRef();
} WsbCatchAndDo(hr,
(void) CloseStream();
);
WsbTraceOut(OLESTR("CNtFileIo::CreateRemoteStream"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CNtFileIo::CloseStream(void)
/*++
Implements:
IDataMover::CloseStream
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::CloseStream"), OLESTR(""));
try {
if (m_hFile != INVALID_HANDLE_VALUE) {
CloseHandle(m_hFile);
m_hFile = INVALID_HANDLE_VALUE;
}
if (m_isLocalStream) {
if (m_OriginalAttributes & FILE_ATTRIBUTE_READONLY) {
//
// Set it back to read only
WsbAssertStatus(SetFileAttributesW(m_StreamName, m_OriginalAttributes));
}
}
if (m_pSession) {
delete m_pSession;
m_pSession = NULL;
}
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::CloseStream"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CNtFileIo::Duplicate(
IN IDataMover* pDestination,
IN DWORD options,
OUT ULARGE_INTEGER* pBytesCopied,
OUT ULARGE_INTEGER* pBytesReclaimed)
/*++
Implements:
IDataMover::Duplicate
Notes:
1) The method uses an internal copy method instead of CopyFile since CopyFile makes wrong assumptions on
whether a copy is feasible based on the file-size and target volume size (ignores compression factor for example).
2) It is assumed that for RSS data-set files, only the unnamed data stream should be copied.
Otherwise, the internal copy method that Duplicate calls for each file needs to be changed.
3) The method uses the MVR_RECOVERY_FILETYPE files to mark (on the copy-media) a file that is
in the middle of copy. In case of a crash, the next time the function runs it will identify
such a case and delete the partial file.
--*/
{
ULARGE_INTEGER bytesCopied = {0,0};
ULARGE_INTEGER bytesReclaimed = {0,0};
HANDLE hSearchHandle = INVALID_HANDLE_VALUE;
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::Duplicate"), OLESTR(""));
try {
CWsbBstrPtr dirName;
CWsbBstrPtr copyDirName;
CWsbStringPtr nameSpace;
CWsbStringPtr nameSpacePrefix;
CWsbStringPtr originalFile;
CWsbStringPtr copyFile;
CWsbStringPtr specificFile;
BOOL bRefresh;
WIN32_FIND_DATA findData;
BOOL bMoreFiles = TRUE;
bRefresh = (options & MVR_DUPLICATE_REFRESH) ? TRUE : FALSE;
// Check if recovery is needed on the master media before duplicating the media
// We continue even if Recovery fails
(void) DoRecovery ();
// Get remote path of original and copy
WsbAffirmHr(GetRemotePath(&dirName));
WsbAffirmHr(pDestination->GetDeviceName(&copyDirName));
WsbAffirmHr(copyDirName.Append(MVR_RSDATA_PATH));
// Traverse directory (traverse only MTF files)
nameSpacePrefix = dirName;
WsbAffirmHr(nameSpacePrefix.Prepend(OLESTR("\\\\?\\")));
WsbAffirmHr(nameSpacePrefix.Append(OLESTR("*")));
nameSpace = nameSpacePrefix;
WsbAffirmHr(nameSpace.Append(MVR_DATASET_FILETYPE));
hSearchHandle = FindFirstFile((WCHAR *) nameSpace, &findData);
// Copy only non-existing data-set (BAG) files
while ((INVALID_HANDLE_VALUE != hSearchHandle) && bMoreFiles) {
if ( (0 == (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) &&
(0 != wcsncmp(findData.cFileName, MVR_LABEL_FILENAME, wcslen(MVR_LABEL_FILENAME))) ) {
originalFile = dirName;
WsbAffirmHr(originalFile.Append(findData.cFileName));
copyFile = copyDirName;
WsbAffirmHr(copyFile.Append(findData.cFileName));
// Test for an incomplete copy from a previous session
WsbAffirmHr(TestRecoveryIndicatorAndDeleteFile(copyFile));
// Create a recovery indicator file for crash consistency on the copy media
WsbAffirmHr(CreateRecoveryIndicator(copyFile));
// Copy
hr = InternalCopyFile(originalFile, copyFile, (! bRefresh));
// Delete the recovery indicator file
(void) DeleteRecoveryIndicator(copyFile);
if (! SUCCEEDED(hr)) {
if ( (! bRefresh) &&
((HRESULT_CODE(hr) == ERROR_ALREADY_EXISTS) || (HRESULT_CODE(hr) == ERROR_FILE_EXISTS)) ) {
// File already exists on remote media - ignore it
hr = S_OK;
}
WsbAffirmHr(hr);
} else {
// Increase counter only if a file is really copied
bytesCopied.HighPart += findData.nFileSizeHigh;
bytesCopied.LowPart += findData.nFileSizeLow;
}
}
bMoreFiles = FindNextFile(hSearchHandle, &findData);
}
if (INVALID_HANDLE_VALUE != hSearchHandle) {
FindClose(hSearchHandle);
hSearchHandle = INVALID_HANDLE_VALUE;
}
// Copy safe-storage backup files (if exist, usually they don't)
bMoreFiles = TRUE;
nameSpace = nameSpacePrefix;
WsbAffirmHr(nameSpace.Append(MVR_SAFE_STORAGE_FILETYPE));
hSearchHandle = FindFirstFile((WCHAR *) nameSpace, &findData);
while ((INVALID_HANDLE_VALUE != hSearchHandle) && bMoreFiles) {
if ( (0 == (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) ) {
originalFile = dirName;
WsbAffirmHr(originalFile.Append(findData.cFileName));
copyFile = copyDirName;
WsbAffirmHr(copyFile.Append(findData.cFileName));
WsbAffirmHr(InternalCopyFile(originalFile, copyFile, FALSE));
}
bMoreFiles = FindNextFile(hSearchHandle, &findData);
}
// Copy specific files (currently, only HSM metadata file)
specificFile = HSM_METADATA_NAME;
WsbAffirmHr(specificFile.Append(MVR_DATASET_FILETYPE));
originalFile = dirName;
WsbAffirmHr(originalFile.Append(specificFile));
copyFile = copyDirName;
WsbAffirmHr(copyFile.Append(specificFile));
hr = InternalCopyFile(originalFile, copyFile, FALSE);
if (! SUCCEEDED(hr)) {
if (HRESULT_CODE(hr) == ERROR_FILE_NOT_FOUND) {
// Original file may not exist
hr = S_OK;
}
WsbAffirmHr(hr);
}
} WsbCatch(hr);
if (INVALID_HANDLE_VALUE != hSearchHandle) {
FindClose(hSearchHandle);
hSearchHandle = INVALID_HANDLE_VALUE;
}
// Set output params
if ( pBytesCopied ) {
pBytesCopied->QuadPart = bytesCopied.QuadPart;
}
if ( pBytesReclaimed ) {
pBytesReclaimed->QuadPart = bytesReclaimed.QuadPart;
}
WsbTraceOut(OLESTR("CNtFileIo::Duplicate"), OLESTR("hr = <%ls>, bytesCopied=%I64u, bytesReclaimed=%I64u"),
WsbHrAsString(hr), bytesCopied.QuadPart, bytesReclaimed.QuadPart);
return hr;
}
STDMETHODIMP
CNtFileIo::FlushBuffers(void)
/*++
Implements:
IDataMover::FlushBuffers
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::FlushBuffers"), OLESTR(""));
try {
// Pad to the next physical block boundary and flush the filesystem buffer.
// Note: The session object calls Commit which flush the data
WsbAffirmHr(m_pSession->ExtendLastPadToNextPBA());
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::FlushBuffers"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CNtFileIo::Recover(OUT BOOL *pDeleteFile)
/*++
Implements:
IDataMover::Recover
Notes:
Recovery is done by:
1. Verifying existence of initial blocks
2. Skip to data sets (FILE DNLKs)
3. If a data set is incomplete - delete it and write FILEMARK+ESET+FILEMARK
4. If FILEMARK is found, all the data is there, just verify and complete the FILEMARK+ESET+FILEMARK
--*/
{
HRESULT hr = S_OK;
*pDeleteFile = FALSE;
WsbTraceIn(OLESTR("CNtFileIo::Recover"), OLESTR(""));
try {
USHORT nDataSetNumber = 0;
BOOL bForceEset = FALSE;
// Check first part of the file
hr = m_pSession->SkipOverTapeDblk();
if (hr == S_OK) {
hr = m_pSession->SkipOverSSETDblk(&nDataSetNumber);
}
if (hr == S_OK) {
hr = m_pSession->SkipToDataSet();
}
if (hr == S_OK) {
hr = m_pSession->SkipOverDataSet();
}
if (hr == MVR_E_NOT_FOUND) {
// File is consistent but no remote data was written or first data written was cut
// Therefore, indicate that file can be deleted altogether and exit
*pDeleteFile = TRUE;
hr = S_OK;
WsbThrow(hr);
} else {
// Verify no other unexpected error
WsbAffirmHr(hr);
}
// Skip over data sets until they are done or we find a problem
while (TRUE) {
hr = m_pSession->SkipToDataSet();
if (hr == S_OK) {
hr = m_pSession->SkipOverDataSet();
if (hr != S_OK) {
bForceEset = TRUE;
break;
}
// No more data sets
} else {
// force re-marking end-of-set unless end-of-set was detected
if (hr != MVR_S_SETMARK_DETECTED) {
bForceEset = TRUE;
}
break;
}
}
// Whatever the error is, since we collected at least one legal data set (one
// complete migrated file), continueby terminating the file properly
// TEMPORARY: in case of an 'inconsistent' error should we ignore, terminate, log event
hr = S_OK;
// Handle end of set
if (! bForceEset) {
// Verify that end-of-data-set is complete
hr = m_pSession->SkipOverEndOfDataSet();
if (hr != S_OK) {
bForceEset = TRUE;
hr = S_OK;
}
}
if (bForceEset) {
// End-of-set is missing or incomplete
WsbAffirmHr(m_pSession->PrepareForEndOfDataSet());
WsbAffirmHr(m_pSession->DoEndOfDataSet(nDataSetNumber));
WsbAffirmStatus(SetEndOfFile(m_hFile));
}
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::Recover"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
////////////////////////////////////////////////////////////////////////////////
//
// IStream Implementation
//
STDMETHODIMP
CNtFileIo::Read(
OUT void *pv,
IN ULONG cb,
OUT ULONG *pcbRead)
/*++
Implements:
IStream::Read
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::Read"), OLESTR("Bytes Requested = %u, offset = %I64u, mode = 0x%08x"), cb, m_StreamOffset.QuadPart, m_Mode);
ULONG bytesRead = 0;
ULONG bytesToRead = 0;
try {
WsbAssert(pv != 0, STG_E_INVALIDPOINTER);
WsbAssert(FALSE == m_isLocalStream, E_UNEXPECTED);
//
// Read data from disk
//
LARGE_INTEGER loc = {0,0};
if ( MVR_MODE_UNFORMATTED & m_Mode ) {
//
// Set location according to current stream offset
// (m_StreamOffset represents here the absolute location to read from)
//
loc.QuadPart = m_StreamOffset.QuadPart;
bytesToRead = cb;
}
else if ( MVR_FLAG_HSM_SEMANTICS & m_Mode ) {
//
// Set location according to session parameters
// (m_StreamOffset represents here an offset into the actual stream-to-read)
//
loc.QuadPart = ( m_pSession->m_sHints.DataSetStart.QuadPart +
m_pSession->m_sHints.FileStart.QuadPart +
m_pSession->m_sHints.DataStart.QuadPart +
m_StreamOffset.QuadPart );
bytesToRead = cb;
}
else {
WsbThrow( E_UNEXPECTED );
}
//
// Set Position
//
WsbAffirmHr(SetPosition(loc.QuadPart));
hr = ReadBuffer((BYTE *) pv, cb, &bytesRead);
if ( FAILED(hr) ) {
WsbThrow(hr)
}
else {
switch (hr) {
case MVR_S_FILEMARK_DETECTED:
case MVR_S_SETMARK_DETECTED:
m_StreamOffset.QuadPart += (unsigned _int64) m_BlockSize;
break;
}
}
m_StreamOffset.QuadPart += bytesRead;
if ( pcbRead ) {
*pcbRead = bytesRead;
}
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::Read"), OLESTR("hr = <%ls> bytes Read = %u, new offset = %I64u"), WsbHrAsString(hr), bytesRead, m_StreamOffset.QuadPart);
return hr;
}
STDMETHODIMP
CNtFileIo::Write(
IN void const *pv,
IN ULONG cb,
OUT ULONG *pcbWritten)
/*++
Implements:
IStream::Write
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::Write"), OLESTR("Bytes Requested = %u, offset = %I64u, mode = 0x%08x"),
cb, m_StreamOffset.QuadPart, m_Mode);
ULONG bytesWritten = 0;
try {
WsbAssert(pv != 0, STG_E_INVALIDPOINTER);
// Consistency Check
// UINT64 pos = m_StreamOffset.QuadPart / m_BlockSize;;
// WsbAffirmHr(EnsurePosition(pos));
// UINT64 curPos;
// WsbAffirmHr(GetPosition(&curPos));
// WsbAssert(curPos == m_StreamOffset.QuadPart / m_BlockSize, E_UNEXPECTED);
WsbAffirmHr(WriteBuffer((BYTE *) pv, cb, &bytesWritten));
if (pcbWritten) {
*pcbWritten = bytesWritten;
}
m_StreamOffset.QuadPart += bytesWritten;
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::Write"), OLESTR("hr = <%ls>, bytesWritten=%u"), WsbHrAsString(hr), bytesWritten);
return hr;
}
STDMETHODIMP
CNtFileIo::Seek(
IN LARGE_INTEGER dlibMove,
IN DWORD dwOrigin,
OUT ULARGE_INTEGER *plibNewPosition)
/*++
Implements:
IStream::Seek
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::Seek"), OLESTR("<%I64d> <%d>"), dlibMove.QuadPart, dwOrigin);
ULARGE_INTEGER newPosition;
try {
newPosition.QuadPart = dlibMove.QuadPart;
//
// Note: Somewhere it is written that FILE_BEGIN is always and
// forever same as STREAM_SEEK_CUR, etc.
//
switch ( (STREAM_SEEK)dwOrigin ) {
case STREAM_SEEK_SET:
newPosition.LowPart = SetFilePointer(m_hFile, dlibMove.LowPart, (long *)&newPosition.HighPart, FILE_BEGIN);
if (INVALID_SET_FILE_POINTER == newPosition.LowPart) {
WsbAffirmNoError(GetLastError());
}
m_StreamOffset.QuadPart = dlibMove.QuadPart;
break;
case STREAM_SEEK_CUR:
newPosition.LowPart = SetFilePointer(m_hFile, dlibMove.LowPart, (long *)&newPosition.HighPart, FILE_CURRENT);
if (INVALID_SET_FILE_POINTER == newPosition.LowPart) {
WsbAffirmNoError(GetLastError());
}
m_StreamOffset.QuadPart += dlibMove.QuadPart;
break;
case STREAM_SEEK_END:
WsbAssert(0 == dlibMove.QuadPart, STG_E_INVALIDPARAMETER);
newPosition.LowPart = SetFilePointer(m_hFile, 0, (long *)&newPosition.HighPart, FILE_END);
if (INVALID_SET_FILE_POINTER == newPosition.LowPart) {
WsbAffirmNoError(GetLastError());
}
m_StreamOffset = newPosition;
break;
default:
WsbThrow(STG_E_INVALIDFUNCTION);
}
WsbAssert(newPosition.QuadPart == m_StreamOffset.QuadPart, MVR_E_LOGIC_ERROR);
if (plibNewPosition) {
plibNewPosition->QuadPart = newPosition.QuadPart;
}
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::Seek"), OLESTR("hr = <%ls>, newPosition=%I64u"), WsbHrAsString(hr), newPosition.QuadPart);
return hr;
}
STDMETHODIMP
CNtFileIo::SetSize(
IN ULARGE_INTEGER /*libNewSize*/)
/*++
Implements:
IStream::SetSize
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::SetSize"), OLESTR(""));
try {
WsbThrow(E_NOTIMPL);
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::SetSize"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CNtFileIo::CopyTo(
IN IStream *pstm,
IN ULARGE_INTEGER cb,
OUT ULARGE_INTEGER *pcbRead,
OUT ULARGE_INTEGER *pcbWritten)
/*++
Implements:
IStream::CopyTo
Note:
A lot of the code that is implemented for Tape I/O in the Read method, is
implemented here in CopyTo, the method that alloacte the I/O buffer.
Otherwise, we would have to alloacte an internal buffer in Read and perform
double copy. In File I/O we want to avoid this for better performance.
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::CopyTo"), OLESTR("<%I64u>"), cb.QuadPart);
ULARGE_INTEGER totalBytesRead = {0,0};
ULARGE_INTEGER totalBytesWritten = {0,0};
BYTE *pBuffer = NULL;
BYTE *pRealBuffer = NULL;
try {
WsbAssert(pstm != 0, STG_E_INVALIDPOINTER);
WsbAssert(m_BlockSize > 0, MVR_E_LOGIC_ERROR);
ULONG defaultBufferSize = DefaultMinBufferSize;
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 bufferSize;
ULONG nBlocks = defaultBufferSize/m_BlockSize;
nBlocks = (nBlocks < 2) ? 2 : nBlocks;
bufferSize = nBlocks * m_BlockSize;
// Allocate buffer and make sure its virtual address is aligned with block size
pRealBuffer = (BYTE *) WsbAlloc(bufferSize+m_BlockSize);
if (pRealBuffer) {
if ((ULONG_PTR)pRealBuffer % m_BlockSize) {
pBuffer = pRealBuffer - ((ULONG_PTR)pRealBuffer % m_BlockSize) + m_BlockSize;
} else {
pBuffer = pRealBuffer;
}
} else {
pBuffer = NULL;
}
WsbAffirmPointer(pBuffer);
memset(pBuffer, 0, bufferSize);
ULONG bytesToRead;
ULONG bytesRead;
ULONG bytesWritten;
ULONG bytesToSkip;
ULONG bytesToCut;
ULARGE_INTEGER bytesToCopy;
bytesToCopy.QuadPart = cb.QuadPart;
while ((bytesToCopy.QuadPart > 0) && (S_OK == hr)) {
bytesToRead = 0;
bytesRead = 0;
bytesWritten = 0;
bytesToSkip = 0;
bytesToCut = 0;
if ((m_Mode & MVR_FLAG_NO_CACHING) ||
(MVR_VERIFICATION_TYPE_HEADER_CRC == m_pSession->m_sHints.VerificationType )) {
// Must read additional data for alignment and/or CRC check
ULARGE_INTEGER loc = {0,0};
ULONG tempMode;
ULARGE_INTEGER offsetIntoFile;
// Set absoulte offset to read from
if ( MVR_VERIFICATION_TYPE_NONE == m_pSession->m_sHints.VerificationType ) {
// No verification - no stream header
loc.QuadPart = ( m_pSession->m_sHints.DataSetStart.QuadPart +
m_pSession->m_sHints.FileStart.QuadPart +
m_pSession->m_sHints.DataStart.QuadPart +
m_StreamOffset.QuadPart );
} else if (MVR_VERIFICATION_TYPE_HEADER_CRC == m_pSession->m_sHints.VerificationType ) {
// Currently, we don't support CRC checking if you don't read from the beginning of the stream
WsbAssert(m_StreamOffset.QuadPart == 0, MVR_E_INVALIDARG);
// Position to the stream header and crc it first.
loc.QuadPart = (m_pSession->m_sHints.DataSetStart.QuadPart +
m_pSession->m_sHints.FileStart.QuadPart +
m_pSession->m_sHints.DataStart.QuadPart -
sizeof(MTF_STREAM_INFO));
bytesToSkip += sizeof(MTF_STREAM_INFO);
} else {
WsbThrow( E_UNEXPECTED );
}
// Set absolute place to read from, how many bytes to read and
// how many bytes for skipping to the actual data
offsetIntoFile.QuadPart = m_StreamOffset.QuadPart;
m_StreamOffset.QuadPart = loc.QuadPart - (loc.QuadPart % m_BlockSize);
bytesToSkip += (ULONG)(loc.QuadPart % m_BlockSize);
if (bytesToCopy.QuadPart > bufferSize) {
bytesToRead = bufferSize;
} else {
bytesToRead = bytesToCopy.LowPart;
bytesToRead += bytesToSkip;
bytesToRead = (bytesToRead < bufferSize) ? bytesToRead : bufferSize;
}
if (bytesToRead % m_BlockSize) {
// Expected only when reading the last chunk
bytesToCut = m_BlockSize - (bytesToRead % m_BlockSize);
bytesToRead = bytesToRead - (bytesToRead % m_BlockSize) + m_BlockSize;
}
// Read the aligned data in an 'unformated' Read
tempMode = m_Mode;
m_Mode |= MVR_MODE_UNFORMATTED;
hr = Read(pBuffer, bytesToRead, &bytesRead);
m_Mode = tempMode;
m_StreamOffset.QuadPart = offsetIntoFile.QuadPart;
if (FAILED(hr)) {
WsbThrow(hr);
}
if (MVR_VERIFICATION_TYPE_HEADER_CRC == m_pSession->m_sHints.VerificationType ) {
// Peform the CRC check
// If for some unexpected reason not enough bytes are read, we skip the CRC check
if (bytesToSkip <= bytesRead) {
MTF_STREAM_INFO sSTREAM;
CMTFApi::MTF_ReadStreamHeader(&sSTREAM, &(pBuffer[bytesToSkip-sizeof(MTF_STREAM_INFO)]));
try {
// Make sure it is the correct type of header
WsbAffirm((0 == memcmp(sSTREAM.acStreamId, MTF_STANDARD_DATA_STREAM, 4)), MVR_E_UNEXPECTED_DATA);
// Verify the stream header checksum
WsbAffirm((m_pSession->m_sHints.VerificationData.QuadPart == sSTREAM.uCheckSum), MVR_E_UNEXPECTED_DATA);
} catch (HRESULT catchHr) {
hr = catchHr;
// Log a detailed error
// Give as attached data the beginning of the buffer which usually contains the FILE DBLK + Stream Info
CWsbBstrPtr name;
CWsbBstrPtr desc;
if (m_pCartridge) {
m_pCartridge->GetName(&name);
m_pCartridge->GetDescription(&desc);
}
WCHAR location[32];
WCHAR offset[16];
WCHAR mark[8];
WCHAR found[16];
swprintf(found, L"0x%04x", sSTREAM.uCheckSum);
swprintf(location, L"%I64u", m_StreamOffset.QuadPart);
swprintf(offset, L"%lu", bytesToSkip - sizeof(MTF_STREAM_INFO));
swprintf(mark, L"0");
WsbLogEvent(MVR_MESSAGE_UNEXPECTED_DATA,
bytesToSkip, pBuffer,
found, (WCHAR *)name, (WCHAR *)desc,
location, offset, mark, NULL);
WsbThrow(hr);
}
}
// CRC check is done only once
m_pSession->m_sHints.VerificationType = MVR_VERIFICATION_TYPE_NONE;
}
// Set file offset, handle unexpected cases where bytesRead<bytesToRead
if (bytesToCut) {
if ((bytesToRead - bytesRead) < bytesToCut) {
bytesToCut = bytesToCut - (bytesToRead - bytesRead);
} else {
bytesToCut = 0;
}
}
if (bytesRead > bytesToSkip) {
m_StreamOffset.QuadPart += (bytesRead - (bytesToSkip+bytesToCut));
}
} else {
// May read only actual data (no alignments) - let default Read to do its job
bytesToRead = (bytesToCopy.QuadPart < bufferSize) ? bytesToCopy.LowPart : bufferSize;
hr = Read(pBuffer, bytesToRead, &bytesRead);
if (FAILED(hr)) {
WsbThrow(hr);
}
}
// Write the data in the output stream and calculate totals
if (bytesRead > (bytesToSkip+bytesToCut)) {
totalBytesRead.QuadPart += (bytesRead - (bytesToSkip+bytesToCut));
WsbAffirmHrOk(pstm->Write(pBuffer+bytesToSkip, bytesRead - (bytesToSkip+bytesToCut), &bytesWritten));
totalBytesWritten.QuadPart += bytesWritten;
bytesToCopy.QuadPart -= (bytesRead - (bytesToSkip+bytesToCut));
}
}
if (pcbRead) {
pcbRead->QuadPart = totalBytesRead.QuadPart;
}
if (pcbWritten) {
pcbWritten->QuadPart = totalBytesWritten.QuadPart;
}
} WsbCatch(hr);
if (pRealBuffer) {
WsbFree(pRealBuffer);
pRealBuffer = NULL;
pBuffer = NULL;
}
WsbTraceOut(OLESTR("CNtFileIo::CopyTo"), OLESTR("hr = <%ls>, bytesRead=%I64u, bytesWritten=%I64u"),
WsbHrAsString(hr), totalBytesRead.QuadPart, totalBytesWritten.QuadPart);
return hr;
}
STDMETHODIMP
CNtFileIo::Commit(
IN DWORD grfCommitFlags)
/*++
Implements:
IStream::Commit
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::Commit"), OLESTR(""));
try {
if (STGC_DEFAULT == grfCommitFlags) {
WsbAssertStatus(FlushFileBuffers(m_hFile));
}
else {
WsbThrow(E_NOTIMPL);
}
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::Commit"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CNtFileIo::Revert(void)
/*++
Implements:
IStream::Revert
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::Revert"), OLESTR(""));
try {
// TEMPORARY: Setting the mode to 0 currently doesn't prevent any write
// which is ongoing. We need to re-visit this issue
m_Mode = 0;
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::Revert"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CNtFileIo::LockRegion(
IN ULARGE_INTEGER /*libOffset*/,
IN ULARGE_INTEGER /*cb*/,
IN DWORD /*dwLockType*/)
/*++
Implements:
IStream::LockRegion
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::LockRegion"), OLESTR(""));
try {
WsbThrow(E_NOTIMPL);
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::LockRegion"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CNtFileIo::UnlockRegion(
IN ULARGE_INTEGER /*libOffset*/,
IN ULARGE_INTEGER /*cb*/,
IN DWORD /*dwLockType*/)
/*++
Implements:
IStream::UnlockRegion
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::UnlockRegion"), OLESTR(""));
try {
WsbThrow(E_NOTIMPL);
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::UnlockRegion"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CNtFileIo::Stat(
OUT STATSTG * /*pstatstg*/,
IN DWORD /*grfStatFlag*/)
/*++
Implements:
IStream::Stat
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::Stat"), OLESTR(""));
try {
WsbThrow(E_NOTIMPL);
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::Stat"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CNtFileIo::Clone(
OUT IStream ** /*ppstm*/)
/*++
Implements:
IStream::Clone
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::Clone"), OLESTR(""));
try {
WsbThrow(E_NOTIMPL);
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::Clone"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
////////////////////////////////////////////////////////////////////////////////
//
// Local Methods
//
HRESULT
CNtFileIo::WriteBuffer(
IN BYTE *pBuffer,
IN ULONG nBytesToWrite,
OUT ULONG *pBytesWritten)
/*++
Routine Description:
Used to write all MTF data. Guarantees full blocks are written.
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 {
if (!m_isLocalStream) {
// Must have a valid label!
WsbAffirm(TRUE == m_ValidLabel, E_ABORT);
// Making sure that we are writting only full blocks
WsbAssert(!(nBytesToWrite % m_BlockSize), MVR_E_LOGIC_ERROR);
}
try {
// ** WIN32 Tape API Call - write the data
WsbAffirmStatus(WriteFile(m_hFile, pBuffer, nBytesToWrite, pBytesWritten, 0));
} WsbCatchAndDo(hr,
hr = MapFileError(hr);
WsbLogEvent(MVR_MESSAGE_DEVICE_ERROR, 0, NULL, WsbHrAsString(hr), NULL);
);
if (!m_isLocalStream) {
// Making sure that we have written only full blocks
WsbAssert(!(*pBytesWritten % m_BlockSize), E_UNEXPECTED);
}
} WsbCatch(hr);
return hr;
}
HRESULT
CNtFileIo::ReadBuffer (
IN BYTE *pBuffer,
IN ULONG nBytesToRead,
OUT ULONG *pBytesRead)
/*++
Routine Description:
Used to read all MTF data. Guarantees full blocks are read.
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 {
// For FileSystem I/O restrictions on reading only full blocks depends on how
// the file is opened. Therefore, we don't enforce it here.
try {
// ** WIN32 Tape API Call - read the data
WsbAffirmStatus(ReadFile(m_hFile, pBuffer, nBytesToRead, pBytesRead, 0));
} WsbCatchAndDo(hr,
hr = MapFileError(hr);
if ( FAILED(hr) ) {
WsbLogEvent(MVR_MESSAGE_DEVICE_ERROR, 0, NULL, WsbHrAsString(hr), NULL);
WsbThrow(hr);
}
);
} WsbCatch(hr);
return hr;
}
HRESULT
CNtFileIo::GetPosition(
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("CNtFileIo::GetPosition"), OLESTR(""));
try {
WsbThrow(E_NOTIMPL);
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::GetPosition"), OLESTR("hr = <%ls>, pos=%I64u"), WsbHrAsString(hr), *pPosition);
return hr;
}
HRESULT
CNtFileIo::SetPosition(
IN UINT64 position)
/*++
Routine Description:
Mover to the specified 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("CNtFileIo::SetPosition"), OLESTR("<%I64u>"), position);
ULARGE_INTEGER newPosition;
try {
newPosition.QuadPart = position;
newPosition.LowPart = SetFilePointer(m_hFile, newPosition.LowPart, (long *)&newPosition.HighPart, FILE_BEGIN);
if (INVALID_SET_FILE_POINTER == newPosition.LowPart) {
WsbAffirmNoError(GetLastError());
}
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::SetPosition"), OLESTR("hr = <%ls>, pos=%I64u"), WsbHrAsString(hr), newPosition.QuadPart);
return hr;
}
HRESULT
CNtFileIo::EnsurePosition(
IN UINT64 position)
/*++
Routine Description:
Checks that the tape is positioned at the specified current physical block
address relative to the current partition. If it is not an attempt is made
to recover to the specified position.
Arguments:
postion - The physical block address to verify.
Return Value:
S_OK - Success.
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::EnsurePosition"), OLESTR("<%I64u>"), position);
try {
WsbThrow(E_NOTIMPL);
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::EnsurePosition"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
HRESULT
CNtFileIo::GetRemotePath(
OUT BSTR *pDestinationString)
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::GetRemotePath"), OLESTR(""));
try {
CWsbBstrPtr tmpString;
tmpString = m_DeviceName;
WsbAffirmHr(tmpString.Append(MVR_RSDATA_PATH));
WsbTrace(OLESTR("RemotePath is <%ls>\n"), (WCHAR *) tmpString);
// Hand over the string
WsbAffirmHr(tmpString.CopyToBstr(pDestinationString));
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::GetRemotePath"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
HRESULT
CNtFileIo::DoRecovery(void)
{
HANDLE hSearchHandle = INVALID_HANDLE_VALUE;
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::DoRecovery"), OLESTR(""));
try {
CWsbBstrPtr dirName;
CWsbStringPtr nameSpace;
CWsbStringPtr recoveredFile;
WIN32_FIND_DATA findData;
BOOL bMoreFiles = TRUE;
// Traverse remote directory for Recovery Indicator files
WsbAffirmHr(GetRemotePath(&dirName));
nameSpace = dirName;
WsbAffirmHr(nameSpace.Append(OLESTR("*")));
WsbAffirmHr(nameSpace.Append(MVR_RECOVERY_FILETYPE));
nameSpace.Prepend(OLESTR("\\\\?\\"));
hSearchHandle = FindFirstFile((WCHAR *) nameSpace, &findData);
while ((INVALID_HANDLE_VALUE != hSearchHandle) && bMoreFiles) {
CComPtr<IDataMover> pMover;
CComPtr<IStream> pStream;
CWsbBstrPtr recoveryName;
ULARGE_INTEGER nil = {0,0};
int nLen, nExtLen;
BOOL bDeleteFile = FALSE;
CWsbBstrPtr name;
CWsbBstrPtr desc;
// Prepare file name to recover
nLen = wcslen(findData.cFileName);
nExtLen = wcslen(MVR_RECOVERY_FILETYPE);
WsbAffirmHr(recoveryName.TakeFrom(NULL, nLen - nExtLen + 1));
wcsncpy(recoveryName, findData.cFileName, nLen-nExtLen);
recoveryName[nLen-nExtLen] = NULL;
// Recover - a failure to recover in file doesn't stop from trying to recover others
try {
if ( m_pCartridge ) {
m_pCartridge->GetName(&name);
m_pCartridge->GetDescription(&desc);
}
WsbLogEvent(MVR_MESSAGE_INCOMPLETE_DATA_SET_DETECTED, 0, NULL,
(WCHAR *)recoveryName, (WCHAR *) name, (WCHAR *) desc, NULL);
// Create and initializa a data mover
WsbAssertHr(CoCreateInstance(CLSID_CNtFileIo, 0, CLSCTX_SERVER, IID_IDataMover, (void **)&pMover));
WsbAffirmHr(pMover->SetDeviceName(m_DeviceName));
WsbAffirmHr(pMover->SetCartridge(m_pCartridge));
// Create the stream for Recovery
WsbAffirmHr(pMover->CreateRemoteStream(recoveryName, MVR_MODE_RECOVER | MVR_MODE_UNFORMATTED, L"",L"",nil,nil,nil,nil,nil,0,nil, &pStream));
// Perform the actual recovery over the file
WsbAffirmHr(pMover->Recover(&bDeleteFile));
(void) pMover->CloseStream();
pStream = NULL;
if (bDeleteFile) {
// Delete the remote file itself
recoveredFile = dirName;
WsbAffirmHr(recoveredFile.Append(recoveryName));
WsbAffirmHr(recoveredFile.Append(MVR_DATASET_FILETYPE));
WsbTrace(OLESTR("CNtFileIo::DoRecovery: Nothing to recover in <%ls> - Deleting file!\n"), (WCHAR *)recoveredFile);
WsbAffirmStatus(DeleteFile(recoveredFile));
}
WsbLogEvent(MVR_MESSAGE_DATA_SET_RECOVERED, 0, NULL, NULL);
} WsbCatchAndDo (hr,
if (pStream) {
(void) pMover->CloseStream();
pStream = NULL;
}
WsbLogEvent(MVR_MESSAGE_DATA_SET_NOT_RECOVERABLE, 0, NULL, WsbHrAsString(hr), NULL);
hr = S_OK;
);
// Create (for deleting) full name of indicator file
recoveredFile = dirName;
WsbAffirmHr(recoveredFile.Append(findData.cFileName));
// Get next file
bMoreFiles = FindNextFile(hSearchHandle, &findData);
// Delete indicator file (independent of the recovery result)
WsbAffirmStatus(DeleteFile(recoveredFile));
}
} WsbCatch(hr);
if (INVALID_HANDLE_VALUE != hSearchHandle) {
FindClose(hSearchHandle);
}
WsbTraceOut(OLESTR("CNtFileIo::DoRecovery"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
HRESULT
CNtFileIo::CreateRecoveryIndicator(IN WCHAR *pFileName)
/*++
Implements:
CNtFileIo::CreateRecoveryIndicator
Notes:
The method assumes that the input file name ends with MVR_DATASET_FILETYPE !!
Otherwise, it fails with E_UNEXPECTED
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::CreateRecoveryIndicator"), OLESTR(""));
try {
CWsbStringPtr recoveryName;
int nLen, nExtLen;
HANDLE hFile;
// Generate file name
nLen = wcslen(pFileName);
nExtLen = wcslen(MVR_DATASET_FILETYPE);
WsbAssert(nLen > nExtLen, E_UNEXPECTED);
WsbAssert(0 == wcscmp(&(pFileName[nLen-nExtLen]), MVR_DATASET_FILETYPE), E_UNEXPECTED);
WsbAffirmHr(recoveryName.TakeFrom(NULL, nLen - nExtLen + wcslen(MVR_RECOVERY_FILETYPE) + 1));
wcsncpy(recoveryName, pFileName, nLen-nExtLen);
wcscpy(&(recoveryName[nLen-nExtLen]), MVR_RECOVERY_FILETYPE);
//Create and immediately close the file
WsbAffirmHandle(hFile = CreateFile(recoveryName,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_HIDDEN,
NULL));
CloseHandle(hFile);
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::CreateRecoveryIndicator"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
HRESULT
CNtFileIo::DeleteRecoveryIndicator(IN WCHAR *pFileName)
/*++
Implements:
CNtFileIo::DeleteRecoveryIndicator
Notes:
The method assumes that the input file name ends with MVR_DATASET_FILETYPE !!
Otherwise, it fails with E_UNEXPECTED
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::DeleteRecoveryIndicator"), OLESTR(""));
try {
CWsbStringPtr recoveryName;
int nLen, nExtLen;
// Generate file name
nLen = wcslen(pFileName);
nExtLen = wcslen(MVR_DATASET_FILETYPE);
WsbAssert(nLen > nExtLen, E_UNEXPECTED);
WsbAssert(0 == wcscmp(&(pFileName[nLen-nExtLen]), MVR_DATASET_FILETYPE), E_UNEXPECTED);
WsbAffirmHr(recoveryName.TakeFrom(NULL, nLen - nExtLen + wcslen(MVR_RECOVERY_FILETYPE) + 1));
wcsncpy(recoveryName, pFileName, nLen-nExtLen);
wcscpy(&(recoveryName[nLen-nExtLen]), MVR_RECOVERY_FILETYPE);
//Delete the indicator file
WsbAffirmStatus(DeleteFile(recoveryName));
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::DeleteRecoveryIndicator"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
HRESULT
CNtFileIo::TestRecoveryIndicatorAndDeleteFile(IN WCHAR *pFileName)
/*++
Implements:
CNtFileIo::TestRecoveryIndicatorAndDeleteFile
Notes:
The method assumes that the input file name ends with MVR_DATASET_FILETYPE !!
Otherwise, it fails with E_UNEXPECTED
The method:
1) Test if the recovery indicator for the given file exists
2) If so, it deletes the file
3) Then, it deleted the recovery indicator
Returns:
S_OK - If found a recovery indicator and deleted successfully
S_FALSE - If didn't find a recovery indicator
--*/
{
HRESULT hr = S_FALSE;
WsbTraceIn(OLESTR("CNtFileIo::TestRecoveryIndicatorAndDeleteFile"), OLESTR(""));
try {
CWsbStringPtr recoveryName;
int nLen, nExtLen;
// Generate recovery-indicator file name
nLen = wcslen(pFileName);
nExtLen = wcslen(MVR_DATASET_FILETYPE);
WsbAssert(nLen > nExtLen, E_UNEXPECTED);
WsbAssert(0 == wcscmp(&(pFileName[nLen-nExtLen]), MVR_DATASET_FILETYPE), E_UNEXPECTED);
WsbAffirmHr(recoveryName.TakeFrom(NULL, nLen - nExtLen + wcslen(MVR_RECOVERY_FILETYPE) + 1));
wcsncpy(recoveryName, pFileName, nLen-nExtLen);
wcscpy(&(recoveryName[nLen-nExtLen]), MVR_RECOVERY_FILETYPE);
// Test recovery indicator file existance
HANDLE hSearchHandle = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA findData;
hSearchHandle = FindFirstFile(recoveryName, &findData);
if (INVALID_HANDLE_VALUE != hSearchHandle) {
FindClose(hSearchHandle);
hr = S_OK;
WsbTrace(OLESTR("CNtFileIo::TestRecoveryIndicator... : Found recovery indicator. Therefore, deleting <%ls>\n"),
pFileName);
//Delete the target file itself
WsbAffirmStatus(DeleteFile(pFileName));
//Delete the indicator file
WsbAffirmStatus(DeleteFile(recoveryName));
}
} WsbCatch(hr);
WsbTraceOut(OLESTR("CNtFileIo::TestRecoveryIndicatorAndDeleteFile"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
HRESULT
CNtFileIo::DeleteAllData(void)
{
HRESULT hr = S_OK;
HANDLE hSearchHandle = INVALID_HANDLE_VALUE;
WsbTraceIn(OLESTR("CNtFileIo::DeleteAllData"), OLESTR(""));
try {
CWsbStringPtr nameSpace;
CWsbStringPtr pathname;
WIN32_FIND_DATAW obFindData;
BOOL bMoreFiles;
CWsbBstrPtr remotePath;
WsbAffirmHr(GetRemotePath(&remotePath));
nameSpace = remotePath;
nameSpace.Append(OLESTR("*.*"));
nameSpace.Prepend(OLESTR("\\\\?\\"));
hSearchHandle = FindFirstFileEx((WCHAR *) nameSpace, FindExInfoStandard, &obFindData, FindExSearchNameMatch, 0, 0);
for (bMoreFiles = TRUE;
hSearchHandle != INVALID_HANDLE_VALUE && bMoreFiles;
bMoreFiles = FindNextFileW(hSearchHandle, &obFindData)) {
if ((obFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
// use the remotePath to get the pathname, then append the filename
pathname = remotePath;
pathname.Prepend(OLESTR("\\\\?\\"));
pathname.Append(obFindData.cFileName);
WsbAffirmStatus(DeleteFile((WCHAR *)pathname));
}
}
} WsbCatch(hr);
// close search handle after processing all the files
if (INVALID_HANDLE_VALUE != hSearchHandle) {
FindClose(hSearchHandle);
hSearchHandle = INVALID_HANDLE_VALUE;
}
WsbTraceOut(OLESTR("CNtFileIo::DeleteAllData"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
HRESULT
CNtFileIo::FormatDrive(
IN BSTR label)
/*++
Routine Description:
Formats a unit of media.
Arguments:
label - The formatted label returned from FormatLabel().
Return Value:
S_OK - Success.
E_ABORT - Operation aborted.
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::FormatDrive"), OLESTR("<%ls>"), (WCHAR *)label);
PROCESS_INFORMATION exeInfo;
STARTUPINFO startupInfo;
memset(&startupInfo, 0, sizeof(startupInfo));
startupInfo.cb = sizeof( startupInfo );
startupInfo.wShowWindow = SW_HIDE;
startupInfo.dwFlags = STARTF_USESHOWWINDOW;
try {
//
// Get volumeLabel for the FS from the label parameter
//
CWsbBstrPtr volumeLabel = MVR_UNDEFINED_STRING;
CWsbBstrPtr tempLabel = label;
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
case 4: // Vendor Product ID
case 5: // Creation Time Stamp
case 6: // Cartridge Label
case 7: // Side
case 8: // Media ID
case 9: // Media Domain ID
default: // Vendor specific of the form L"VS:Name=Value"
{
WCHAR *name = NULL;
int nameLen = 0;
// DisplayName
name = L"VS:DisplayName=";
nameLen = wcslen(name);
if( 0 == wcsncmp(token, name, nameLen) ) {
volumeLabel = &token[nameLen];
}
}
break;
}
token = wcstok( NULL, delim );
}
// Build the format command with options:
// "Format d: /fs:ntfs /force /q /x /v:ROOT_D > Null:"
OLECHAR drive[256];
(void) wcsncpy((WCHAR *)drive, (WCHAR *)m_DeviceName, 2);
drive[2] = '\0'; // TODO: Fix for no drive letter support
// NOTE: It's possible that the format command isn't where we
// think it is. The following registry entry allows
// an override.
CWsbBstrPtr formatCmd = RMS_DEFAULT_FORMAT_COMMAND;
DWORD size;
OLECHAR tmpString[256];
if (SUCCEEDED(WsbGetRegistryValueString(NULL, RMS_REGISTRY_STRING, RMS_PARAMETER_FORMAT_COMMAND, tmpString, 256, &size))) {
// Get the value.
formatCmd = tmpString;
}
WsbTrace(OLESTR("Using command: <%ls>.\n"), (WCHAR *)formatCmd);
WsbAffirmHr(formatCmd.Append(L" "));
WsbAffirmHr(formatCmd.Append(drive));
CWsbBstrPtr commandLine = formatCmd;
CWsbBstrPtr formatOpts = RMS_DEFAULT_FORMAT_OPTIONS;
if (SUCCEEDED(WsbGetRegistryValueString(NULL, RMS_REGISTRY_STRING, RMS_PARAMETER_FORMAT_OPTIONS, tmpString, 256, &size))) {
// Get the value.
formatOpts = tmpString;
}
WsbTrace(OLESTR("Using options: <%ls>.\n"), (WCHAR *)formatOpts);
DWORD formatWaitTime = RMS_DEFAULT_FORMAT_WAIT_TIME;
if (SUCCEEDED(WsbGetRegistryValueString(NULL, RMS_REGISTRY_STRING, RMS_PARAMETER_FORMAT_WAIT_TIME, tmpString, 256, &size))) {
// Get the value.
formatWaitTime = wcstol(tmpString, NULL, 10);
}
WsbTrace(OLESTR("Using wait time: %d.\n"), formatWaitTime);
int retry = 0;
do {
try {
WsbAffirm(0 == wcslen((WCHAR *)formatOpts), E_UNEXPECTED);
WsbAffirmHr(commandLine.Append(L" "));
WsbAffirmHr(commandLine.Append(formatOpts));
WsbAffirmHr(commandLine.Append(L" /v:"));
WsbAffirmHr(commandLine.Append(volumeLabel));
WsbAffirmHr(commandLine.Append(L" > Null:"));
WsbTrace(OLESTR("Using command: <%ls> to format media.\n"), (WCHAR *)commandLine);
WsbAffirmStatus(CreateProcess(0, (WCHAR *)commandLine, 0, 0, FALSE, 0, 0, 0, &startupInfo, &exeInfo));
WsbAffirmStatus(WAIT_FAILED != WaitForSingleObject(exeInfo.hProcess, 20*60*1000));
break;
} WsbCatchAndDo(hr,
retry++;
commandLine = formatCmd;
if (retry == 1) {
formatOpts = RMS_DEFAULT_FORMAT_OPTIONS_ALT1;
if (SUCCEEDED(WsbGetRegistryValueString(NULL, RMS_REGISTRY_STRING, RMS_PARAMETER_FORMAT_OPTIONS_ALT1, tmpString, 256, &size))) {
// Get the value.
formatOpts = tmpString;
}
}
else if (retry == 2) {
formatOpts = RMS_DEFAULT_FORMAT_OPTIONS_ALT2;
if (SUCCEEDED(WsbGetRegistryValueString(NULL, RMS_REGISTRY_STRING, RMS_PARAMETER_FORMAT_OPTIONS_ALT2, tmpString, 256, &size))) {
// Get the value.
formatOpts = tmpString;
}
}
else {
WsbThrow(hr);
}
WsbTrace(OLESTR("Retrying with otions: <%ls>.\n"), (WCHAR *)formatOpts);
);
} while (retry < 3);
} WsbCatch(hr);
if (FAILED(hr)) {
hr = E_ABORT;
}
WsbTraceOut(OLESTR("CNtFileIo::FormatDrive"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
HRESULT
CNtFileIo::MapFileError(
IN HRESULT hrToMap)
/*++
Routine Description:
Maps a WIN32 file error, specified as an HRESULT, to a MVR error.
Arguments:
hrToMap - WIN32 file error to map.
Return Value:
S_OK - Success.
MVR_E_BEGINNING_OF_MEDIA - The beginning of the tape or a partition was encountered.
MVR_E_BUS_RESET - The I/O bus was reset.
MVR_E_END_OF_MEDIA - The physical end of the tape has been reached.
MVR_S_FILEMARK_DETECTED - A tape access reached a filemark.
MVR_S_SETMARK_DETECTED - A tape access reached the end of a set of files.
MVR_S_NO_DATA_DETECTED - No more data is on the tape.
MVR_E_PARTITION_FAILURE - Tape could not be partitioned.
MVR_E_INVALID_BLOCK_LENGTH - When accessing a new tape of a multivolume partition, the current blocksize is incorrect.
MVR_E_DEVICE_NOT_PARTITIONED - Tape partition information could not be found when loading a tape.
MVR_E_MEDIA_CHANGED - The media in the drive may have changed.
MVR_E_NO_MEDIA_IN_DRIVE - No media in drive.
MVR_E_UNABLE_TO_LOCK_MEDIA - Unable to lock the media eject mechanism.
MVR_E_UNABLE_TO_UNLOAD_MEDIA - Unable to unload the media.
MVR_E_WRITE_PROTECT - The media is write protected.
MVR_E_CRC - Data error (cyclic redundancy check).
MVR_E_SHARING_VIOLATION - The process cannot access the file because it is being used by another process.
MVR_E_ERROR_IO_DEVICE - The request could not be performed because of an I/O device error. - Unknown error.
MVE_E_ERROR_DEVICE_NOT_CONNECTED - The device is not connected.
MVR_E_DISK_FULL - There is insufficient disk space to complete the operation.
E_ABORT - Unknown error, abort.
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CNtFileIo::MapFileError"), OLESTR("<%ls>"), WsbHrAsString(hrToMap));
try {
// The valid label flag is knocked down when the media may have changed
// or device parameters (i.e. block size) may have been reset.
switch ( hrToMap ) {
case S_OK:
break;
case HRESULT_FROM_WIN32( ERROR_BEGINNING_OF_MEDIA ):
hr = MVR_E_BEGINNING_OF_MEDIA;
break;
case HRESULT_FROM_WIN32( ERROR_BUS_RESET ):
hr = MVR_E_BUS_RESET;
m_ValidLabel = FALSE;
WsbLogEvent(MVR_MESSAGE_MEDIA_NOT_VALID, 0, NULL, WsbHrAsString(hr), NULL);
break;
case HRESULT_FROM_WIN32( ERROR_END_OF_MEDIA ):
hr = MVR_E_END_OF_MEDIA;
break;
case HRESULT_FROM_WIN32( ERROR_FILEMARK_DETECTED ): // Maps to Success
hr = MVR_S_FILEMARK_DETECTED;
break;
case HRESULT_FROM_WIN32( ERROR_SETMARK_DETECTED ): // Maps to Success
hr = MVR_S_SETMARK_DETECTED;
break;
case HRESULT_FROM_WIN32( ERROR_NO_DATA_DETECTED ): // Maps to Success
// EOD
// This happens on SpaceFilemarks() past end of data.
hr = MVR_S_NO_DATA_DETECTED;
break;
case HRESULT_FROM_WIN32( ERROR_PARTITION_FAILURE ):
hr = MVR_E_PARTITION_FAILURE;
break;
case HRESULT_FROM_WIN32( ERROR_INVALID_BLOCK_LENGTH ):
hr = MVR_E_INVALID_BLOCK_LENGTH;
break;
case HRESULT_FROM_WIN32( ERROR_DEVICE_NOT_PARTITIONED ):
hr = MVR_E_DEVICE_NOT_PARTITIONED;
break;
case HRESULT_FROM_WIN32( ERROR_MEDIA_CHANGED ):
hr = MVR_E_MEDIA_CHANGED;
m_ValidLabel = FALSE;
WsbLogEvent(MVR_MESSAGE_MEDIA_NOT_VALID, 0, NULL, WsbHrAsString(hr), NULL);
break;
case HRESULT_FROM_WIN32( ERROR_NO_MEDIA_IN_DRIVE ):
hr = MVR_E_NO_MEDIA_IN_DRIVE;
m_ValidLabel = FALSE;
WsbLogEvent(MVR_MESSAGE_MEDIA_NOT_VALID, 0, NULL, WsbHrAsString(hr), NULL);
break;
case HRESULT_FROM_WIN32( ERROR_UNABLE_TO_LOCK_MEDIA ):
hr = MVR_E_UNABLE_TO_LOCK_MEDIA;
break;
case HRESULT_FROM_WIN32( ERROR_UNABLE_TO_UNLOAD_MEDIA ):
hr = MVR_E_UNABLE_TO_UNLOAD_MEDIA;
break;
case HRESULT_FROM_WIN32( ERROR_WRITE_PROTECT ):
hr = MVR_E_WRITE_PROTECT;
break;
case HRESULT_FROM_WIN32( ERROR_CRC ):
// This is may indicate that the drive needs cleaning.
hr = MVR_E_CRC;
break;
case HRESULT_FROM_WIN32( ERROR_SHARING_VIOLATION ):
// This happens when the CreateFile fails because the device is in use by some other app.
hr = MVR_E_SHARING_VIOLATION;
break;
case HRESULT_FROM_WIN32( ERROR_IO_DEVICE ):
// This happens when the device is turned off during I/O, for example.
hr = MVR_E_ERROR_IO_DEVICE;
break;
case HRESULT_FROM_WIN32( ERROR_DEVICE_NOT_CONNECTED ):
// This happens when the device is turned off.
hr = MVR_E_ERROR_DEVICE_NOT_CONNECTED;
break;
case HRESULT_FROM_WIN32( ERROR_SEM_TIMEOUT ):
// This happens when the SCSI command does not return within the timeout period. A system error is logged for the SCSI controler (adapter).
hr = MVR_E_ERROR_DEVICE_NOT_CONNECTED;
break;
case HRESULT_FROM_WIN32( ERROR_DISK_FULL ):
// There is not enough space on the disk.
hr = MVR_E_DISK_FULL;
break;
case HRESULT_FROM_WIN32( ERROR_UNRECOGNIZED_VOLUME ):
// This happens when the volume is not formatted to any file system
hr = MVR_E_UNRECOGNIZED_VOLUME;
break;
case HRESULT_FROM_WIN32( ERROR_INVALID_HANDLE ):
// This happens after a Cancel() operation.
hr = E_ABORT;
break;
default:
WsbThrow(hrToMap);
}
} WsbCatchAndDo(hr,
WsbLogEvent(MVR_MESSAGE_UNKNOWN_DEVICE_ERROR, 0, NULL, WsbHrAsString(hr), NULL);
hr = E_ABORT;
);
WsbTraceOut(OLESTR("CNtFileIo::MapFileError"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
HRESULT
CNtFileIo::InternalCopyFile(
IN WCHAR *pOriginalFileName,
IN WCHAR *pCopyFileName,
IN BOOL bFailIfExists)
/*++
Implements:
CNtFileIo::InternalCopyFile
Notes:
1) The method copies only the unnamed data stream using Read/Write.
Currently this is sufficient for RSS .bkf files on a media, however, if we ever use
other-than-default file characteristics like named streams, per-file security attributes,
special file attributes, etc. - then we should consider using BackupRead & BackupWrite
for implementing the internal-copy
2) If caller ask for bFailIfExists=TRUE, then the method returns HRESULT_FROM_WIN32(ERROR_FILE_EXISTS)
3) In case of a failure half way through, the method deletes the partial file
--*/
{
HRESULT hr = S_OK;
HANDLE hOriginal = INVALID_HANDLE_VALUE;
HANDLE hCopy = INVALID_HANDLE_VALUE;
BYTE *pBuffer = NULL;
WsbTraceIn(OLESTR("CNtFileIo::InternalCopyFile"), OLESTR(""));
try {
// Create file on the Original media with no write-sharing - upper level should ensure
// that nobody opens the file for write while a copy-media is going on
WsbAffirmHandle(hOriginal = CreateFile(pOriginalFileName,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
NULL));
// Create the file on the Copy media with no sharing at all. Create-disposition
// depends on caller request
// Exisitng file would generate ERROR_FILE_EXISTS that should be handled by the caller
DWORD dwCreationDisposition;
dwCreationDisposition = bFailIfExists ? CREATE_NEW : CREATE_ALWAYS;
WsbAffirmHandle(hCopy = CreateFile(pCopyFileName,
GENERIC_WRITE,
0, // no sharing
NULL,
dwCreationDisposition,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
NULL));
// Allocate a buffer for the media copy
ULONG defaultBufferSize = RMS_DEFAULT_BUFFER_SIZE;
DWORD size;
OLECHAR tmpString[256];
if (SUCCEEDED(WsbGetRegistryValueString(NULL, RMS_REGISTRY_STRING, RMS_PARAMETER_COPY_BUFFER_SIZE, tmpString, 256, &size))) {
// Get the value.
LONG val = wcstol(tmpString, NULL, 10);
if (val > 0) {
defaultBufferSize = val;
}
}
pBuffer = (BYTE *)WsbAlloc(defaultBufferSize);
WsbAffirmAlloc(pBuffer);
// Read and write in chunks
// Synchronous ReadFile should signal eof by returning zero bytes read
BOOL done = FALSE;
DWORD dwBytesToRead = defaultBufferSize;
DWORD dwBytesRead, dwBytesWritten;
while (! done) {
WsbAffirmStatus(ReadFile(hOriginal, pBuffer, dwBytesToRead, &dwBytesRead, NULL));
if (dwBytesRead == 0) {
// eof
done = TRUE;
} else {
// Write to copy-file
WsbAffirmStatus(WriteFile(hCopy, pBuffer, dwBytesRead, &dwBytesWritten, NULL));
if (dwBytesWritten != dwBytesRead) {
// Fail the copy
WsbTraceAlways(OLESTR("CNtFileIo::InternalCopyFile: writing to copy-file is not completed to-write=%lu, written=%lu - Aborting!\n"),
dwBytesRead, dwBytesWritten);
WsbThrow(E_FAIL);
}
}
}
// Flush to media
WsbAffirmStatus(FlushFileBuffers(hCopy));
} WsbCatch(hr);
// Close original file
if (INVALID_HANDLE_VALUE != hOriginal) {
CloseHandle(hOriginal);
hOriginal = INVALID_HANDLE_VALUE;
}
// Close copy file
if (INVALID_HANDLE_VALUE != hCopy) {
if (! CloseHandle(hCopy)) {
DWORD dwErr = GetLastError();
WsbTrace(OLESTR("CNtFileIo::InternalCopyFile: CloseHandle for copy-file failed with error=%lu\n"), dwErr);
// Set hr only if there was a success so far...
if (SUCCEEDED(hr)) {
hr = HRESULT_FROM_WIN32(dwErr);
}
}
hCopy = INVALID_HANDLE_VALUE;
// Delete copy file on any error, including close errors
if (! SUCCEEDED(hr)) {
WsbTrace(OLESTR("CNtFileIo::InternalCopyFile: Deleting copy-file <%s> due to an error during the copy\n"),
pCopyFileName);
if (! DeleteFile(pCopyFileName)) {
DWORD dwErr = GetLastError();
WsbTrace(OLESTR("CNtFileIo::InternalCopyFile: Failed to delete copy-file, DeleteFile error=%lu\n"), dwErr);
}
}
}
if (pBuffer) {
WsbFree(pBuffer);
pBuffer = NULL;
}
WsbTraceOut(OLESTR("CNtFileIo::InternalCopyFile"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}