3753 lines
110 KiB
C++
3753 lines
110 KiB
C++
/*++
|
||
|
||
© 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(©DirName));
|
||
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;
|
||
}
|