You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1831 lines
48 KiB
1831 lines
48 KiB
/*
|
|
**++
|
|
**
|
|
** Copyright (c) 2000-2001 Microsoft Corporation
|
|
**
|
|
** Module Name:
|
|
**
|
|
** wrtrrsm.cpp
|
|
**
|
|
**
|
|
** Abstract:
|
|
**
|
|
** Writer shim module for RSM
|
|
**
|
|
**
|
|
** Author:
|
|
**
|
|
** Brian Berkowitz [brianb]
|
|
**
|
|
**
|
|
** Revision History:
|
|
**
|
|
** X-11 MCJ Michael C. Johnson 19-Sep-2000
|
|
** 215218: Wildcard name of log files returned by OnIdentify()
|
|
** 215390: Incorporate multiple '.' fix in MatchFileName from NtBackup
|
|
**
|
|
** X-10 MCJ Michael C. Johnson 19-Sep-2000
|
|
** 176860: Add the missing calling convention specifiers
|
|
**
|
|
** X-9 MCJ Michael C. Johnson 21-Aug-2000
|
|
** Added copyright and edit history
|
|
** 161899: Don't add a component for a database file in the
|
|
** exclude list.
|
|
** 165873: Remove trailing '\' from metadata file paths
|
|
** 165913: Deallocate memory on class destruction
|
|
**
|
|
**
|
|
**--
|
|
*/
|
|
|
|
#include <stdafx.h>
|
|
|
|
#include <esent.h>
|
|
|
|
#include <vss.h>
|
|
#include <vswriter.h>
|
|
|
|
#include <jetwriter.h>
|
|
|
|
|
|
#include "ijetwriter.h"
|
|
#include "vs_inc.hxx"
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Standard foo for file name aliasing. This code block must be after
|
|
// all includes of VSS header files.
|
|
//
|
|
#ifdef VSS_FILE_ALIAS
|
|
#undef VSS_FILE_ALIAS
|
|
#endif
|
|
#define VSS_FILE_ALIAS "JTWIJTWC"
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// local functions
|
|
|
|
|
|
#define UMAX(_a, _b) ((_a) > (_b) ? (_a) : (_b))
|
|
#define EXECUTEIF(_bSuccess, _fn) ((_bSuccess) ? (_fn) : (_bSuccess))
|
|
#define GET_STATUS_FROM_BOOL(_bSucceed) ((_bSucceed) ? NOERROR : HRESULT_FROM_WIN32 (GetLastError()))
|
|
|
|
|
|
typedef struct _ExpandedPathInfo
|
|
{
|
|
LIST_ENTRY leQueueHead;
|
|
PWCHAR pwszOriginalFilePath;
|
|
PWCHAR pwszOriginalFileName;
|
|
PWCHAR pwszExpandedFilePath;
|
|
PWCHAR pwszExpandedFileName;
|
|
bool bRecurseIntoSubdirectories;
|
|
} EXPANDEDPATHINFO, *PEXPANDEDPATHINFO, **PPEXPANDEDPATHINFO;
|
|
|
|
|
|
|
|
static void RemoveAnyTrailingSeparator (PCHAR szPath)
|
|
{
|
|
ULONG ulPathLength = strlen (szPath);
|
|
|
|
if ('\\' == szPath [ulPathLength - 1])
|
|
{
|
|
szPath [ulPathLength - 1] = '\0';
|
|
}
|
|
}
|
|
|
|
|
|
static void RemoveAnyTrailingSeparator (PWCHAR wszPath)
|
|
{
|
|
ULONG ulPathLength = wcslen (wszPath);
|
|
|
|
if (L'\\' == wszPath [ulPathLength - 1])
|
|
{
|
|
wszPath [ulPathLength - 1] = UNICODE_NULL;
|
|
}
|
|
}
|
|
|
|
|
|
static bool ConvertName (PCHAR szSourceName,
|
|
ULONG ulTargetBufferLengthInChars,
|
|
PWCHAR wszTargetBuffer)
|
|
{
|
|
bool bSucceeded = true;
|
|
|
|
|
|
wszTargetBuffer [0] = '\0';
|
|
|
|
|
|
/*
|
|
** Only need to do the conversion for non-zero length
|
|
** strings. Returning a zero length string for a zero length
|
|
** argument is an ok thing to do.
|
|
*/
|
|
if ('\0' != szSourceName [0])
|
|
{
|
|
bSucceeded = (0 != MultiByteToWideChar (CP_OEMCP,
|
|
0,
|
|
szSourceName,
|
|
-1,
|
|
wszTargetBuffer,
|
|
ulTargetBufferLengthInChars));
|
|
}
|
|
|
|
|
|
return (bSucceeded);
|
|
} /* ConvertName () */
|
|
|
|
|
|
static bool ConvertNameAndSeparateFilePaths (PCHAR pszSourcePath,
|
|
ULONG ulTargetBufferLength,
|
|
PWCHAR pwszTargetPath,
|
|
PWCHAR& pwszTargetFileSpec)
|
|
{
|
|
bool bSucceeded;
|
|
PWCHAR pwchLastSlash;
|
|
|
|
|
|
bSucceeded = ConvertName (pszSourcePath, ulTargetBufferLength, pwszTargetPath);
|
|
|
|
if (bSucceeded)
|
|
{
|
|
/*
|
|
** Scan backwards from the end of the target path, zap the
|
|
** end-most '\' and point the file spec at the character
|
|
** following where the '\' used to be.
|
|
*/
|
|
pwchLastSlash = wcsrchr (pwszTargetPath, L'\\');
|
|
|
|
bSucceeded = (NULL != pwchLastSlash);
|
|
}
|
|
|
|
|
|
if (bSucceeded)
|
|
{
|
|
pwszTargetFileSpec = pwchLastSlash + 1;
|
|
|
|
*pwchLastSlash = UNICODE_NULL;
|
|
}
|
|
|
|
|
|
return (bSucceeded);
|
|
} /* ConvertNameAndSeparateFilePaths () */
|
|
|
|
|
|
|
|
/*
|
|
** This routine breaks out the next path and filespec from a list of
|
|
** filespecs. The expected format of the input string is
|
|
**
|
|
** path\[filespec] [/s]
|
|
**
|
|
**
|
|
** The list can contain an arbitrary number of filespecs each
|
|
** separated by a semi-colon.
|
|
*/
|
|
static bool DetermineNextPathWorker (LPCWSTR pwszFileList,
|
|
LPCWSTR& pwszReturnedCursor,
|
|
ULONG& ulReturnedDirectoryStart,
|
|
ULONG& ulReturnedDirectoryLength,
|
|
ULONG& ulReturnedFilenameStart,
|
|
ULONG& ulReturnedFilenameLength,
|
|
bool& bReturnedRecurseIntoSubdirectories,
|
|
bool& bReturnedFoundSpec)
|
|
{
|
|
bool bSucceeded = true;
|
|
bool bFoundSpec = false;
|
|
ULONG ulPathNameLength;
|
|
ULONG ulFileNameLength;
|
|
ULONG ulIndex;
|
|
ULONG ulIndexSubDirectory = 0;
|
|
ULONG ulIndexLastDirectorySeparator = 0;
|
|
ULONG ulIndexFirstCharInSpec = 0;
|
|
ULONG ulIndexLastCharInSpec = 0;
|
|
const ULONG ulLengthFileList = wcslen (pwszFileList);
|
|
|
|
|
|
/*
|
|
** The format of the string we are expecting is "filename.ext /s
|
|
** ;nextname", ie a list of semi-colon separated names with an
|
|
** optional trailing '/s'. There can be an arbitrary number of
|
|
** spaces before the '/' and before the ';': these will be
|
|
** stripped out and discarded. So we start by scanning for the
|
|
** first '/' or ';' characters.
|
|
**
|
|
** Look for a ';' first to determine the end point.
|
|
*/
|
|
if ((NULL == pwszFileList) ||
|
|
(UNICODE_NULL == pwszFileList [0]))
|
|
{
|
|
bFoundSpec = false;
|
|
}
|
|
|
|
else if (( L';' == pwszFileList [0]) ||
|
|
( L'/' == pwszFileList [0]) ||
|
|
((L'\\' == pwszFileList [0]) && (UNICODE_NULL == pwszFileList [1])))
|
|
{
|
|
bSucceeded = false;
|
|
bFoundSpec = false;
|
|
}
|
|
|
|
else
|
|
{
|
|
bFoundSpec = true;
|
|
}
|
|
|
|
|
|
if (bSucceeded && bFoundSpec)
|
|
{
|
|
while (L' ' == pwszFileList [ulIndexFirstCharInSpec])
|
|
{
|
|
ulIndexFirstCharInSpec++;
|
|
}
|
|
|
|
|
|
for (ulIndex = ulIndexFirstCharInSpec; ulIndex < ulLengthFileList; ulIndex++)
|
|
{
|
|
if ((UNICODE_NULL == pwszFileList [ulIndex]) ||
|
|
(L';' == pwszFileList [ulIndex]))
|
|
{
|
|
/*
|
|
** We found the end of this specification
|
|
*/
|
|
break;
|
|
}
|
|
|
|
else if (L'\\' == pwszFileList [ulIndex])
|
|
{
|
|
/*
|
|
** Found a backslash? Record it's location. We'll want
|
|
** this later when determining what the file name is
|
|
** and so on.
|
|
*/
|
|
ulIndexLastDirectorySeparator = ulIndex;
|
|
}
|
|
|
|
else if ((L'/' == pwszFileList [ulIndex]) &&
|
|
(L's' == towlower (pwszFileList [ulIndex + 1])))
|
|
{
|
|
ulIndexSubDirectory = ulIndex;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
ulIndexLastCharInSpec = (0 == ulIndexSubDirectory) ? ulIndex - 1 : ulIndexSubDirectory - 1;
|
|
|
|
while (L' ' == pwszFileList [ulIndexLastCharInSpec])
|
|
{
|
|
ulIndexLastCharInSpec--;
|
|
}
|
|
|
|
|
|
_ASSERTE (ulIndex > ulIndexSubDirectory);
|
|
_ASSERTE (ulIndexSubDirectory == 0 ||
|
|
ulIndexSubDirectory > ulIndexLastCharInSpec);
|
|
_ASSERTE (ulIndexLastCharInSpec >= ulIndexLastDirectorySeparator);
|
|
_ASSERTE (ulIndexLastDirectorySeparator > ulIndexFirstCharInSpec);
|
|
|
|
|
|
/*
|
|
** We may have an illegal spec here with a missing '\'. Come
|
|
** on folks, there ought to be at least one. one measly '\' is
|
|
** all I'm after.
|
|
*/
|
|
bSucceeded = (0 < ulIndexLastDirectorySeparator);
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bSucceeded)
|
|
{
|
|
if (bFoundSpec)
|
|
{
|
|
ulPathNameLength = ulIndexLastDirectorySeparator - ulIndexFirstCharInSpec;
|
|
ulFileNameLength = ulIndexLastCharInSpec - ulIndexLastDirectorySeparator;
|
|
|
|
|
|
pwszReturnedCursor = (UNICODE_NULL == pwszFileList [ulIndex])
|
|
? &pwszFileList [ulIndex]
|
|
: &pwszFileList [ulIndex + 1];
|
|
|
|
ulReturnedDirectoryStart = ulIndexFirstCharInSpec;
|
|
ulReturnedDirectoryLength = ulPathNameLength;
|
|
ulReturnedFilenameStart = ulIndexLastDirectorySeparator + 1;
|
|
ulReturnedFilenameLength = ulFileNameLength;
|
|
|
|
bReturnedRecurseIntoSubdirectories = (0 != ulIndexSubDirectory);
|
|
bReturnedFoundSpec = true;
|
|
}
|
|
|
|
else
|
|
{
|
|
pwszReturnedCursor = pwszFileList;
|
|
|
|
ulReturnedDirectoryStart = 0;
|
|
ulReturnedDirectoryLength = 0;
|
|
ulReturnedFilenameStart = 0;
|
|
ulReturnedFilenameLength = 0;
|
|
|
|
bReturnedRecurseIntoSubdirectories = false;
|
|
bReturnedFoundSpec = false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return (bSucceeded);
|
|
} /* DetermineNextPathWorker () */
|
|
|
|
|
|
|
|
static bool DetermineNextPathLengths (LPCWSTR pwszFileList,
|
|
ULONG& ulReturnedLengthDirectory,
|
|
ULONG& ulReturnedLengthFilename,
|
|
bool& bReturnedRecurseIntoSubdirectories,
|
|
bool& bReturnedFoundSpec)
|
|
{
|
|
bool bSucceeded;
|
|
LPCWSTR pwszUpdatedCursor;
|
|
ULONG ulIndexDirectoryStart;
|
|
ULONG ulIndexFilenameStart;
|
|
|
|
|
|
bSucceeded = DetermineNextPathWorker (pwszFileList,
|
|
pwszUpdatedCursor,
|
|
ulIndexDirectoryStart,
|
|
ulReturnedLengthDirectory,
|
|
ulIndexFilenameStart,
|
|
ulReturnedLengthFilename,
|
|
bReturnedRecurseIntoSubdirectories,
|
|
bReturnedFoundSpec);
|
|
|
|
return (bSucceeded);
|
|
} /* DetermineNextPathLengths () */
|
|
|
|
|
|
static bool DetermineNextPath (LPCWSTR pwszFileList,
|
|
LPCWSTR& pwszReturnedCursor,
|
|
ULONG ulLengthBufferDirectory,
|
|
PWCHAR pwszBufferDirectory,
|
|
ULONG ulLengthBufferFilename,
|
|
PWCHAR pwszBufferFilename,
|
|
bool& bReturnedRecurseIntoSubdirectories,
|
|
bool& bReturnedFoundSpec)
|
|
{
|
|
bool bSucceeded = true;
|
|
bool bRecurseIntoSubdirectories;
|
|
bool bFoundSpec;
|
|
bool bWildcardFilename;
|
|
LPCWSTR pwszUpdatedCursor;
|
|
ULONG ulLengthDirectory;
|
|
ULONG ulLengthFilename;
|
|
ULONG ulIndexDirectoryStart;
|
|
ULONG ulIndexFilenameStart;
|
|
|
|
|
|
bSucceeded = DetermineNextPathWorker (pwszFileList,
|
|
pwszUpdatedCursor,
|
|
ulIndexDirectoryStart,
|
|
ulLengthDirectory,
|
|
ulIndexFilenameStart,
|
|
ulLengthFilename,
|
|
bRecurseIntoSubdirectories,
|
|
bFoundSpec);
|
|
|
|
if (bSucceeded && bFoundSpec)
|
|
{
|
|
if ((ulLengthBufferDirectory < ((sizeof (WCHAR) * ulLengthDirectory) + sizeof (UNICODE_NULL))) ||
|
|
(ulLengthBufferFilename < ((sizeof (WCHAR) * ulLengthFilename) + sizeof (UNICODE_NULL))))
|
|
{
|
|
/*
|
|
** Oops, buffer overflow would occur if we were to proceed
|
|
** with the copy.
|
|
*/
|
|
bSucceeded = false;
|
|
}
|
|
}
|
|
|
|
|
|
if (bSucceeded)
|
|
{
|
|
bReturnedRecurseIntoSubdirectories = bRecurseIntoSubdirectories;
|
|
bReturnedFoundSpec = bFoundSpec;
|
|
pwszReturnedCursor = pwszUpdatedCursor;
|
|
|
|
|
|
if (bFoundSpec)
|
|
{
|
|
/*
|
|
** Everything up to, but excluding the last directory
|
|
** separator is the path. Everything after the last directory
|
|
** separator up to and including the last char is the
|
|
** filespec. If the filespec is zero length, then add the '*'
|
|
** wildcard.
|
|
*/
|
|
bWildcardFilename = (0 == ulLengthFilename);
|
|
|
|
ulLengthFilename += bWildcardFilename ? 1 : 0;
|
|
|
|
|
|
memcpy (pwszBufferDirectory,
|
|
&pwszFileList [ulIndexDirectoryStart],
|
|
sizeof (WCHAR) * ulLengthDirectory);
|
|
|
|
memcpy (pwszBufferFilename,
|
|
(bWildcardFilename) ? L"*" : &pwszFileList [ulIndexFilenameStart],
|
|
sizeof (WCHAR) * ulLengthFilename);
|
|
|
|
pwszBufferDirectory [ulLengthDirectory] = UNICODE_NULL;
|
|
pwszBufferFilename [ulLengthFilename] = UNICODE_NULL;
|
|
}
|
|
}
|
|
|
|
|
|
return (bSucceeded);
|
|
} /* DetermineNextPath () */
|
|
|
|
|
|
|
|
static bool ValidateIncludeExcludeList (LPCWSTR pwszFileList)
|
|
{
|
|
LPCWSTR pwszCursor = pwszFileList;
|
|
bool bSucceeded = true;
|
|
bool bFoundFiles = true;
|
|
bool bRecurseIntoSubdirectories;
|
|
ULONG ulIndexDirectoryStart;
|
|
ULONG ulIndexFilenameStart;
|
|
ULONG ulLengthDirectory;
|
|
ULONG ulLengthFilename;
|
|
|
|
while (bSucceeded && bFoundFiles)
|
|
{
|
|
bSucceeded = EXECUTEIF (bSucceeded, (DetermineNextPathWorker (pwszCursor,
|
|
pwszCursor,
|
|
ulIndexDirectoryStart,
|
|
ulLengthDirectory,
|
|
ulIndexFilenameStart,
|
|
ulLengthFilename,
|
|
bRecurseIntoSubdirectories,
|
|
bFoundFiles)));
|
|
}
|
|
|
|
|
|
return (bSucceeded);
|
|
} /* ValidateIncludeExcludeList () */
|
|
|
|
|
|
/*
|
|
** Based on MatchFname() from \nt\base\fs\utils\ntback50\be\bsdmatch.cpp
|
|
*/
|
|
static bool MatchFilename (LPCWSTR pwszPattern, /* I - file name (with wildcards) */
|
|
LPCWSTR pwszFilename) /* I - file name (without wildcards ) */
|
|
{
|
|
ULONG ulIndexPattern; /* index for pwszPattern */
|
|
ULONG ulIndexFilename; /* index for pwszFilename */
|
|
ULONG ulLengthPattern;
|
|
const ULONG ulLengthFilename = wcslen (pwszFilename);
|
|
bool bSucceeded = true;
|
|
PWCHAR pwszNameBufferAllocated = NULL; /* allocated temp name buffer */
|
|
PWCHAR pwszNameBufferTemp; /* pointer to one of the above */
|
|
PWCHAR pwchTemp;
|
|
WCHAR pwszNameBufferStatic [256]; /* static temp name buffer */
|
|
WCHAR wchSavedChar ;
|
|
|
|
|
|
ulIndexFilename = 0;
|
|
|
|
if (wcscmp (pwszPattern, L"*") && wcscmp (pwszPattern, L"*.*"))
|
|
{
|
|
bool bTryWithDot = false;
|
|
|
|
do
|
|
{
|
|
if (bTryWithDot)
|
|
{
|
|
/*
|
|
** Size of name_buff minus a null, minus a dot for the
|
|
** "bTryWithDot" code below. If the name is longer than the
|
|
** static buffer, allocate one from the heap.
|
|
*/
|
|
if (((ulLengthFilename + 2) * sizeof (WCHAR)) > sizeof (pwszNameBufferStatic))
|
|
{
|
|
pwszNameBufferAllocated = new WCHAR [ulLengthFilename + 2];
|
|
pwszNameBufferTemp = pwszNameBufferAllocated;
|
|
}
|
|
else
|
|
{
|
|
pwszNameBufferTemp = pwszNameBufferStatic;
|
|
}
|
|
|
|
if (pwszNameBufferTemp != NULL)
|
|
{
|
|
wcscpy (pwszNameBufferTemp, pwszFilename);
|
|
wcscat (pwszNameBufferTemp, L".");
|
|
pwszFilename = pwszNameBufferTemp;
|
|
ulIndexFilename = 0;
|
|
bSucceeded = true;
|
|
}
|
|
|
|
bTryWithDot = false;
|
|
}
|
|
|
|
else if (wcschr (pwszFilename, L'.') == NULL)
|
|
{
|
|
bTryWithDot = true;
|
|
}
|
|
|
|
|
|
for (ulIndexPattern = 0; (pwszPattern [ulIndexPattern] != 0) && (bSucceeded) ; ulIndexPattern++)
|
|
{
|
|
switch (pwszPattern [ulIndexPattern])
|
|
{
|
|
case L'*':
|
|
while (pwszPattern [ulIndexPattern + 1] != UNICODE_NULL)
|
|
{
|
|
if (pwszPattern [ulIndexPattern + 1] == L'?')
|
|
{
|
|
if (pwszFilename [++ulIndexFilename] == UNICODE_NULL)
|
|
{
|
|
break ;
|
|
}
|
|
}
|
|
|
|
else if (pwszPattern [ulIndexPattern + 1] != L'*')
|
|
{
|
|
break ;
|
|
}
|
|
|
|
ulIndexPattern++ ;
|
|
}
|
|
|
|
pwchTemp = wcspbrk (&pwszPattern [ulIndexPattern + 1], L"*?");
|
|
|
|
if (pwchTemp != NULL)
|
|
{
|
|
wchSavedChar = *pwchTemp;
|
|
*pwchTemp = UNICODE_NULL;
|
|
|
|
ulLengthPattern = wcslen (&pwszPattern [ulIndexPattern + 1]);
|
|
|
|
while (pwszFilename [ulIndexFilename] &&
|
|
_wcsnicmp (&pwszFilename [ulIndexFilename],
|
|
&pwszPattern [ulIndexPattern + 1],
|
|
ulLengthPattern))
|
|
{
|
|
ulIndexFilename++;
|
|
}
|
|
|
|
ulIndexPattern += ulLengthPattern;
|
|
|
|
*pwchTemp = wchSavedChar;
|
|
|
|
if (pwszFilename [ulIndexFilename] == UNICODE_NULL)
|
|
{
|
|
bSucceeded = false;
|
|
}
|
|
else
|
|
{
|
|
ulIndexFilename++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pwszPattern [ulIndexPattern + 1] == UNICODE_NULL)
|
|
{
|
|
ulIndexFilename = wcslen (pwszFilename);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
pwchTemp = wcschr (&pwszFilename [ulIndexFilename],
|
|
pwszPattern [ulIndexPattern + 1]);
|
|
|
|
if (pwchTemp != NULL)
|
|
{
|
|
ulIndexFilename += (ULONG)(pwchTemp - &pwszFilename [ulIndexFilename]);
|
|
}
|
|
else
|
|
{
|
|
bSucceeded = false;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
case L'?' :
|
|
if (pwszFilename [ulIndexFilename] != UNICODE_NULL)
|
|
{
|
|
ulIndexFilename++;
|
|
}
|
|
break;
|
|
|
|
|
|
default:
|
|
if (pwszFilename [ulIndexFilename] == UNICODE_NULL)
|
|
{
|
|
bSucceeded = false;
|
|
}
|
|
|
|
else if (towupper (pwszFilename [ulIndexFilename]) != towupper (pwszPattern [ulIndexPattern]))
|
|
{
|
|
ULONG ulIndexPreviousStar = ulIndexPattern;
|
|
|
|
|
|
/*
|
|
** Set the index back to the last '*'
|
|
*/
|
|
bSucceeded = false;
|
|
|
|
do
|
|
{
|
|
if (pwszPattern [ulIndexPreviousStar] == L'*')
|
|
{
|
|
ulIndexPattern = ulIndexPreviousStar;
|
|
ulIndexFilename++;
|
|
bSucceeded = true;
|
|
break;
|
|
}
|
|
} while (ulIndexPreviousStar-- > 0);
|
|
}
|
|
else
|
|
{
|
|
ulIndexFilename++;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
if (pwszFilename [ulIndexFilename] != UNICODE_NULL)
|
|
{
|
|
bSucceeded = false;
|
|
}
|
|
|
|
} while ((!bSucceeded) && (bTryWithDot));
|
|
}
|
|
|
|
|
|
delete [] pwszNameBufferAllocated;
|
|
|
|
|
|
return (bSucceeded);
|
|
} /* MatchFilename () */
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// class CVssIJetWriter
|
|
//
|
|
// logical path == instance name
|
|
// component name == dbfilename (minus the extension?)
|
|
// caption == display name
|
|
//
|
|
//
|
|
// add db and slv files as database files
|
|
// add the per-instance log file to each database even though is is the same each time.
|
|
|
|
|
|
|
|
STDMETHODCALLTYPE CVssIJetWriter::~CVssIJetWriter()
|
|
{
|
|
PostProcessIncludeExcludeLists (true );
|
|
PostProcessIncludeExcludeLists (false);
|
|
|
|
delete m_wszWriterName;
|
|
delete m_wszFilesToInclude;
|
|
delete m_wszFilesToExclude;
|
|
}
|
|
|
|
|
|
|
|
BOOL CVssIJetWriter::CheckExcludedFileListForMatch (LPCWSTR pwszDatabaseFilePath,
|
|
LPCWSTR pwszDatabaseFileSpec)
|
|
{
|
|
BOOL bMatchFound = false;
|
|
PLIST_ENTRY pleElement = m_leFilesToExcludeEntries.Flink;
|
|
UNICODE_STRING ucsExcludedFilePath;
|
|
UNICODE_STRING ucsDatabaseFilePath;
|
|
PEXPANDEDPATHINFO pepnPathInfomation;
|
|
|
|
|
|
RtlInitUnicodeString (&ucsDatabaseFilePath, pwszDatabaseFilePath);
|
|
|
|
|
|
while ((&m_leFilesToExcludeEntries != pleElement) && !bMatchFound)
|
|
{
|
|
pepnPathInfomation = (PEXPANDEDPATHINFO)((PBYTE) pleElement - offsetof (EXPANDEDPATHINFO, leQueueHead));
|
|
|
|
RtlInitUnicodeString (&ucsExcludedFilePath,
|
|
pepnPathInfomation->pwszExpandedFilePath);
|
|
|
|
|
|
if (pepnPathInfomation->bRecurseIntoSubdirectories)
|
|
{
|
|
bMatchFound = RtlPrefixUnicodeString (&ucsExcludedFilePath,
|
|
&ucsDatabaseFilePath,
|
|
true);
|
|
}
|
|
else
|
|
{
|
|
bMatchFound = RtlEqualUnicodeString (&ucsExcludedFilePath, &ucsDatabaseFilePath, true) &&
|
|
MatchFilename (pepnPathInfomation->pwszExpandedFileName, pwszDatabaseFileSpec);
|
|
}
|
|
|
|
|
|
|
|
pleElement = pleElement->Flink;
|
|
}
|
|
|
|
|
|
|
|
|
|
return (bMatchFound);
|
|
} /* CVssIJetWriter::CheckExcludedFileListForMatch () */
|
|
|
|
|
|
|
|
|
|
bool CVssIJetWriter::ProcessJetInstance (JET_INSTANCE_INFO *pInstanceInfo)
|
|
{
|
|
JET_ERR jetStatus;
|
|
HRESULT hrStatus;
|
|
DWORD dwStatus;
|
|
bool bSucceeded;
|
|
bool bRestoreMetadata = false;
|
|
bool bNotifyOnBackupComplete = false;
|
|
bool bSelectable = false;
|
|
bool bIncludeComponent;
|
|
CHAR szPathShortName [MAX_PATH];
|
|
CHAR szPathFullName [MAX_PATH];
|
|
WCHAR wszInstanceName [MAX_PATH];
|
|
WCHAR wszDatabaseName [MAX_PATH];
|
|
WCHAR wszDatabaseDisplayName [MAX_PATH];
|
|
WCHAR wszDatabaseFilePath [MAX_PATH];
|
|
WCHAR wszDatabaseSLVFilePath [MAX_PATH];
|
|
WCHAR wszLogFilePath [MAX_PATH];
|
|
WCHAR wszLogFileName [MAX_PATH];
|
|
WCHAR wszCheckpointFilePath [MAX_PATH];
|
|
WCHAR wszCheckpointFileName [MAX_PATH];
|
|
|
|
PWCHAR pwszDatabaseFileName = L"";
|
|
PWCHAR pwszDatabaseSLVFileName = L"";
|
|
|
|
|
|
|
|
|
|
/*
|
|
** A valid instance will have an instance Id, but if it's not
|
|
** actually being used for anything it may well not have a name,
|
|
** any log or database files.
|
|
**
|
|
** See if we can get hold of the name of the log file for this
|
|
** instance.
|
|
*/
|
|
bSucceeded = (JET_errSuccess <= JetGetSystemParameter (pInstanceInfo->hInstanceId,
|
|
JET_sesidNil,
|
|
JET_paramLogFilePath,
|
|
NULL,
|
|
szPathShortName,
|
|
sizeof (szPathShortName)));
|
|
|
|
if (bSucceeded)
|
|
{
|
|
dwStatus = GetFullPathNameA (szPathShortName,
|
|
sizeof (szPathFullName),
|
|
szPathFullName,
|
|
NULL);
|
|
|
|
bSucceeded = (dwStatus > 0);
|
|
}
|
|
|
|
|
|
if (bSucceeded)
|
|
{
|
|
RemoveAnyTrailingSeparator (szPathFullName);
|
|
|
|
bSucceeded = ConvertName (szPathFullName,
|
|
MAX_PATH,
|
|
wszLogFilePath);
|
|
}
|
|
|
|
|
|
BsDebugTrace (0,
|
|
DEBUG_TRACE_VSS_SHIM,
|
|
(L"CVssIJetWriter::ProcessJetInstance - "
|
|
L"%s calling JetGetSystemParameter() with instance Log file path '%S' (shortname) or '%s' full name",
|
|
bSucceeded ? L"Succeeded" : L"FAILED",
|
|
szPathShortName,
|
|
wszLogFilePath));
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
** Ok, now get the SystemPath which we will need to construct the
|
|
** path for the checkpoint file.
|
|
*/
|
|
bSucceeded = (JET_errSuccess <= JetGetSystemParameter (pInstanceInfo->hInstanceId,
|
|
JET_sesidNil,
|
|
JET_paramSystemPath,
|
|
NULL,
|
|
szPathShortName,
|
|
sizeof (szPathShortName)));
|
|
|
|
if (bSucceeded)
|
|
{
|
|
dwStatus = GetFullPathNameA (szPathShortName,
|
|
sizeof (szPathFullName),
|
|
szPathFullName,
|
|
NULL);
|
|
|
|
bSucceeded = (dwStatus > 0);
|
|
}
|
|
|
|
|
|
if (bSucceeded)
|
|
{
|
|
RemoveAnyTrailingSeparator (szPathFullName);
|
|
|
|
bSucceeded = ConvertName (szPathFullName,
|
|
MAX_PATH,
|
|
wszCheckpointFilePath);
|
|
}
|
|
|
|
|
|
BsDebugTrace (0,
|
|
DEBUG_TRACE_VSS_SHIM,
|
|
(L"CVssIJetWriter::ProcessJetInstance - "
|
|
L"%s calling JetGetSystemParameter() with checkpoint file path '%S' (shortname) or '%s' full name",
|
|
bSucceeded ? L"Succeeded" : L"FAILED",
|
|
szPathShortName,
|
|
wszCheckpointFilePath));
|
|
|
|
|
|
|
|
/*
|
|
** Ok, now get the base name which we will need to construct the
|
|
** file spec for the log and checkpoint files. Note that we expect
|
|
** this to be just 3 chars long.
|
|
*/
|
|
bSucceeded = (JET_errSuccess <= JetGetSystemParameter (pInstanceInfo->hInstanceId,
|
|
JET_sesidNil,
|
|
JET_paramBaseName,
|
|
NULL,
|
|
szPathShortName,
|
|
sizeof (szPathShortName)));
|
|
|
|
if (bSucceeded)
|
|
{
|
|
/*
|
|
** Convert to wide char ensuring that we leave a little room
|
|
** for the "*.log"/".chk" strings to be appended to form the
|
|
** log file spec and the checkpoint file specs respectively.
|
|
*/
|
|
bSucceeded = ConvertName (szPathShortName,
|
|
MAX_PATH - sizeof ("*.log"),
|
|
wszCheckpointFileName);
|
|
}
|
|
|
|
|
|
if (bSucceeded)
|
|
{
|
|
wcscpy (wszLogFileName, wszCheckpointFileName);
|
|
|
|
|
|
wcscat (wszCheckpointFileName, L".chk" );
|
|
wcscat (wszLogFileName, L"*.log");
|
|
}
|
|
|
|
|
|
BsDebugTrace (0,
|
|
DEBUG_TRACE_VSS_SHIM,
|
|
(L"CVssIJetWriter::ProcessJetInstance - "
|
|
L"%s calling JetGetSystemParameter() for base name '%S' to form LogFileName '%s' and CheckpointFileName '%s'",
|
|
bSucceeded ? L"Succeeded" : L"FAILED",
|
|
szPathShortName,
|
|
wszLogFileName,
|
|
wszCheckpointFileName));
|
|
|
|
|
|
|
|
|
|
if (bSucceeded && (pInstanceInfo->cDatabases > 0))
|
|
{
|
|
/*
|
|
** Ok, we think we have an instance that is actually being
|
|
** used for something. so go ahead and construct a 'component'
|
|
** for it.
|
|
*/
|
|
if ((NULL == pInstanceInfo->szInstanceName) ||
|
|
('\0' == pInstanceInfo->szInstanceName [0]))
|
|
{
|
|
/*
|
|
** We seem to have a NULL pointer or a zero length
|
|
** string. Just set to a zero length unicode string.
|
|
*/
|
|
wszInstanceName [0] = UNICODE_NULL;
|
|
}
|
|
|
|
else
|
|
{
|
|
bSucceeded = ConvertName (pInstanceInfo->szInstanceName,
|
|
MAX_PATH,
|
|
wszInstanceName);
|
|
}
|
|
|
|
|
|
|
|
for (ULONG ulDatabase = 0; bSucceeded && (ulDatabase < pInstanceInfo->cDatabases); ulDatabase++)
|
|
{
|
|
bSucceeded = ConvertNameAndSeparateFilePaths (pInstanceInfo->szDatabaseFileName [ulDatabase],
|
|
MAX_PATH,
|
|
wszDatabaseFilePath,
|
|
pwszDatabaseFileName);
|
|
|
|
|
|
/*
|
|
** Convert the database display name to unicode but allow
|
|
** for a possible NULL pointer or a non-zero length file
|
|
** spec.
|
|
*/
|
|
if (bSucceeded)
|
|
{
|
|
if ((NULL == pInstanceInfo->szDatabaseDisplayName [ulDatabase]) ||
|
|
('\0' == pInstanceInfo->szDatabaseDisplayName [ulDatabase][0]))
|
|
{
|
|
wszDatabaseDisplayName [0] = UNICODE_NULL;
|
|
}
|
|
else
|
|
{
|
|
bSucceeded = ConvertName (pInstanceInfo->szDatabaseDisplayName [ulDatabase],
|
|
MAX_PATH,
|
|
wszDatabaseDisplayName);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Convert the SLV filename to unicode but allow for a
|
|
** possible NULL pointer or a non-zero length file spec.
|
|
*/
|
|
if (bSucceeded)
|
|
{
|
|
if ((NULL == pInstanceInfo->szDatabaseSLVFileName [ulDatabase]) ||
|
|
('\0' == pInstanceInfo->szDatabaseSLVFileName [ulDatabase][0]))
|
|
{
|
|
wszDatabaseSLVFilePath [0] = UNICODE_NULL;
|
|
pwszDatabaseSLVFileName = wszDatabaseSLVFilePath;
|
|
}
|
|
else
|
|
{
|
|
bSucceeded = ConvertNameAndSeparateFilePaths (pInstanceInfo->szDatabaseSLVFileName [ulDatabase],
|
|
MAX_PATH,
|
|
wszDatabaseSLVFilePath,
|
|
pwszDatabaseSLVFileName);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
** We've now done all the name conversions to unicode so
|
|
** add a component and the log and database files where
|
|
** they're available.
|
|
*/
|
|
if (bSucceeded)
|
|
{
|
|
bIncludeComponent = !CheckExcludedFileListForMatch (wszDatabaseFilePath,
|
|
pwszDatabaseFileName);
|
|
}
|
|
|
|
|
|
if (bSucceeded && bIncludeComponent)
|
|
{
|
|
PWCHAR pwchLastDot = wcsrchr (pwszDatabaseFileName, L'.');
|
|
ULONG ulDatabaseNameLength = (ULONG) (pwchLastDot - pwszDatabaseFileName);
|
|
|
|
wcsncpy (wszDatabaseName, pwszDatabaseFileName, ulDatabaseNameLength);
|
|
wszDatabaseName [ulDatabaseNameLength] = '\0';
|
|
|
|
|
|
|
|
hrStatus = m_pIMetadata->AddComponent (VSS_CT_DATABASE,
|
|
wszInstanceName,
|
|
wszDatabaseName,
|
|
wszDatabaseDisplayName,
|
|
NULL,
|
|
0,
|
|
bRestoreMetadata,
|
|
bNotifyOnBackupComplete,
|
|
bSelectable);
|
|
|
|
bSucceeded = SUCCEEDED (hrStatus);
|
|
|
|
BsDebugTrace (0,
|
|
DEBUG_TRACE_VSS_SHIM,
|
|
(L"CVssIJetWriter::ProcessJetInstance - "
|
|
L"%s adding component '%s' for database '%s' with display name '%s'",
|
|
bSucceeded ? L"Succeeded" : L"FAILED",
|
|
wszInstanceName,
|
|
wszDatabaseName,
|
|
wszDatabaseDisplayName));
|
|
}
|
|
|
|
|
|
|
|
if (bSucceeded && bIncludeComponent)
|
|
{
|
|
hrStatus = m_pIMetadata->AddDatabaseFiles (wszInstanceName,
|
|
wszDatabaseName,
|
|
wszDatabaseFilePath,
|
|
pwszDatabaseFileName);
|
|
|
|
bSucceeded = SUCCEEDED (hrStatus);
|
|
|
|
BsDebugTrace (0,
|
|
DEBUG_TRACE_VSS_SHIM,
|
|
(L"CVssIJetWriter::ProcessJetInstance - "
|
|
L"%s adding database files for instance '%s', database '%s', database file '%s\\%s'",
|
|
bSucceeded ? L"Succeeded" : L"FAILED",
|
|
wszInstanceName,
|
|
wszDatabaseName,
|
|
wszDatabaseFilePath,
|
|
pwszDatabaseFileName));
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
** May not have an SLV file so only add it if we have a
|
|
** non-zero length file spec
|
|
*/
|
|
if (bSucceeded && bIncludeComponent && (UNICODE_NULL != pwszDatabaseSLVFileName [0]))
|
|
{
|
|
hrStatus = m_pIMetadata->AddDatabaseFiles (wszInstanceName,
|
|
wszDatabaseName,
|
|
wszDatabaseSLVFilePath,
|
|
pwszDatabaseSLVFileName);
|
|
|
|
bSucceeded = SUCCEEDED (hrStatus);
|
|
|
|
BsDebugTrace (0,
|
|
DEBUG_TRACE_VSS_SHIM,
|
|
(L"CVssIJetWriter::ProcessJetInstance - "
|
|
L"%s adding SLV file for instance '%s', database '%s', SLV file '%s\\%s'",
|
|
bSucceeded ? L"Succeeded" : L"FAILED",
|
|
wszInstanceName,
|
|
wszDatabaseName,
|
|
wszDatabaseSLVFilePath,
|
|
pwszDatabaseSLVFileName));
|
|
}
|
|
|
|
|
|
/*
|
|
** May not have an instance log file so only add it if we
|
|
** have a non-zero length file path
|
|
*/
|
|
if (bSucceeded && bIncludeComponent && (UNICODE_NULL != wszLogFilePath [0]))
|
|
{
|
|
hrStatus = m_pIMetadata->AddDatabaseLogFiles (wszInstanceName,
|
|
wszDatabaseName,
|
|
wszLogFilePath,
|
|
wszLogFileName);
|
|
|
|
bSucceeded = SUCCEEDED (hrStatus);
|
|
|
|
BsDebugTrace (0,
|
|
DEBUG_TRACE_VSS_SHIM,
|
|
(L"CVssIJetWriter::ProcessJetInstance - "
|
|
L"%s adding log file for instance '%s', database '%s', log file '%s\\%s'",
|
|
bSucceeded ? L"Succeeded" : L"FAILED",
|
|
wszInstanceName,
|
|
wszDatabaseName,
|
|
wszLogFilePath,
|
|
wszLogFileName));
|
|
}
|
|
|
|
|
|
/*
|
|
** May not have a checkpoint file so only add it if we
|
|
** have a non-zero length file path
|
|
*/
|
|
if (bSucceeded && bIncludeComponent && (UNICODE_NULL != wszCheckpointFilePath [0]))
|
|
{
|
|
hrStatus = m_pIMetadata->AddDatabaseLogFiles (wszInstanceName,
|
|
wszDatabaseName,
|
|
wszCheckpointFilePath,
|
|
wszCheckpointFileName);
|
|
|
|
bSucceeded = SUCCEEDED (hrStatus);
|
|
|
|
BsDebugTrace (0,
|
|
DEBUG_TRACE_VSS_SHIM,
|
|
(L"CVssIJetWriter::ProcessJetInstance - "
|
|
L"%s adding checkpoint file for instance '%s', database '%s', checkpoint file '%s\\%s'",
|
|
bSucceeded ? L"Succeeded" : L"FAILED",
|
|
wszInstanceName,
|
|
wszDatabaseName,
|
|
wszCheckpointFilePath,
|
|
wszCheckpointFileName));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return (bSucceeded);
|
|
} /* CVssIJetWriter::ProcessJetInstance () */
|
|
|
|
|
|
|
|
bool CVssIJetWriter::PreProcessIncludeExcludeLists (bool bProcessingIncludeList)
|
|
{
|
|
/*
|
|
** Parse the m_wszFilesToInclude and m_wszFilesToExclude adding
|
|
** and enty to the appropriate list as necessary. This will
|
|
** minimize the number of passes over the un-processed lists.
|
|
*/
|
|
ULONG ulPathLength;
|
|
ULONG ulNameLength;
|
|
bool bRecurseIntoSubdirectories;
|
|
bool bSucceeded = true;
|
|
bool bFoundFiles = true;
|
|
PEXPANDEDPATHINFO pepnPathInfomation = NULL;
|
|
PWCHAR pwszCursor = bProcessingIncludeList
|
|
? m_wszFilesToInclude
|
|
: m_wszFilesToExclude;
|
|
|
|
|
|
|
|
while (bSucceeded && bFoundFiles)
|
|
{
|
|
bSucceeded = DetermineNextPathLengths (pwszCursor,
|
|
ulPathLength,
|
|
ulNameLength,
|
|
bRecurseIntoSubdirectories,
|
|
bFoundFiles);
|
|
|
|
|
|
if (bSucceeded && bFoundFiles)
|
|
{
|
|
pepnPathInfomation = new EXPANDEDPATHINFO;
|
|
|
|
bSucceeded = (NULL != pepnPathInfomation);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
** We either failed and/or found no files. In either case
|
|
** there is no point in continuing.
|
|
*/
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
if (bSucceeded)
|
|
{
|
|
InitializeListHead (&pepnPathInfomation->leQueueHead);
|
|
|
|
|
|
if (0 == ulNameLength)
|
|
{
|
|
/*
|
|
** If the filename component is zero length, then it
|
|
** will be turned into a "*" so add a character to the
|
|
** buffer to make room.
|
|
*/
|
|
ulNameLength++;
|
|
}
|
|
|
|
/*
|
|
** Allow extra space for terminating UNICODE_NULL
|
|
*/
|
|
ulPathLength++;
|
|
ulNameLength++;
|
|
|
|
|
|
pepnPathInfomation->pwszExpandedFilePath = NULL;
|
|
pepnPathInfomation->pwszExpandedFileName = NULL;
|
|
pepnPathInfomation->pwszOriginalFilePath = new WCHAR [ulPathLength];
|
|
pepnPathInfomation->pwszOriginalFileName = new WCHAR [ulNameLength];
|
|
|
|
bSucceeded = ((NULL != pepnPathInfomation->pwszOriginalFilePath) &&
|
|
(NULL != pepnPathInfomation->pwszOriginalFileName));
|
|
}
|
|
|
|
|
|
if (bSucceeded)
|
|
{
|
|
bSucceeded = DetermineNextPath (pwszCursor,
|
|
pwszCursor,
|
|
ulPathLength * sizeof (WCHAR),
|
|
pepnPathInfomation->pwszOriginalFilePath,
|
|
ulNameLength * sizeof (WCHAR),
|
|
pepnPathInfomation->pwszOriginalFileName,
|
|
pepnPathInfomation->bRecurseIntoSubdirectories,
|
|
bFoundFiles);
|
|
|
|
BS_ASSERT (bFoundFiles && L"Second attempt to locate files failed unexpectedly");
|
|
}
|
|
|
|
|
|
if (bSucceeded)
|
|
{
|
|
ulPathLength = ExpandEnvironmentStringsW (pepnPathInfomation->pwszOriginalFilePath, NULL, 0);
|
|
ulNameLength = ExpandEnvironmentStringsW (pepnPathInfomation->pwszOriginalFileName, NULL, 0);
|
|
|
|
bSucceeded = (0 < ulPathLength) && (0 < ulNameLength);
|
|
}
|
|
|
|
|
|
if (bSucceeded)
|
|
{
|
|
pepnPathInfomation->pwszExpandedFilePath = new WCHAR [ulPathLength];
|
|
pepnPathInfomation->pwszExpandedFileName = new WCHAR [ulNameLength];
|
|
|
|
bSucceeded = ((NULL != pepnPathInfomation->pwszExpandedFilePath) &&
|
|
(NULL != pepnPathInfomation->pwszExpandedFileName));
|
|
}
|
|
|
|
|
|
if (bSucceeded)
|
|
{
|
|
ExpandEnvironmentStringsW (pepnPathInfomation->pwszOriginalFilePath,
|
|
pepnPathInfomation->pwszExpandedFilePath,
|
|
ulPathLength);
|
|
|
|
|
|
ExpandEnvironmentStringsW (pepnPathInfomation->pwszOriginalFileName,
|
|
pepnPathInfomation->pwszExpandedFileName,
|
|
ulNameLength);
|
|
}
|
|
|
|
|
|
if (bSucceeded)
|
|
{
|
|
InsertTailList (bProcessingIncludeList ? &m_leFilesToIncludeEntries : &m_leFilesToExcludeEntries,
|
|
&pepnPathInfomation->leQueueHead);
|
|
|
|
pepnPathInfomation = NULL;
|
|
}
|
|
|
|
|
|
|
|
if (NULL != pepnPathInfomation)
|
|
{
|
|
delete [] pepnPathInfomation->pwszOriginalFilePath;
|
|
delete [] pepnPathInfomation->pwszOriginalFileName;
|
|
delete [] pepnPathInfomation->pwszExpandedFilePath;
|
|
delete [] pepnPathInfomation->pwszExpandedFileName;
|
|
delete pepnPathInfomation;
|
|
|
|
pepnPathInfomation = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
return (bSucceeded);
|
|
} /* CVssIJetWriter::PreProcessIncludeExcludeLists () */
|
|
|
|
|
|
|
|
bool CVssIJetWriter::ProcessIncludeExcludeLists (bool bProcessingIncludeList)
|
|
{
|
|
/*
|
|
** parse the m_wszFilesToInclude and m_wszFilesToExclude
|
|
** calling the m_pIMetadata->IncludeFiles() and/or
|
|
** m_pIMetadata->ExcludeFiles() routines as necessary
|
|
*/
|
|
HRESULT hrStatus;
|
|
bool bSucceeded = true;
|
|
const PLIST_ENTRY pleQueueHead = bProcessingIncludeList ? &m_leFilesToIncludeEntries : &m_leFilesToExcludeEntries;
|
|
PLIST_ENTRY pleElement = pleQueueHead->Flink;
|
|
PEXPANDEDPATHINFO pepnPathInfomation;
|
|
|
|
|
|
|
|
while (bSucceeded && (pleQueueHead != pleElement))
|
|
{
|
|
pepnPathInfomation = (PEXPANDEDPATHINFO)((PBYTE) pleElement - offsetof (EXPANDEDPATHINFO, leQueueHead));
|
|
|
|
|
|
if (bProcessingIncludeList)
|
|
{
|
|
hrStatus = m_pIMetadata->AddIncludeFiles (pepnPathInfomation->pwszOriginalFilePath,
|
|
pepnPathInfomation->pwszOriginalFileName,
|
|
pepnPathInfomation->bRecurseIntoSubdirectories,
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
hrStatus = m_pIMetadata->AddExcludeFiles (pepnPathInfomation->pwszOriginalFilePath,
|
|
pepnPathInfomation->pwszOriginalFileName,
|
|
pepnPathInfomation->bRecurseIntoSubdirectories);
|
|
}
|
|
|
|
|
|
bSucceeded = SUCCEEDED (hrStatus);
|
|
|
|
pleElement = pleElement->Flink;
|
|
}
|
|
|
|
|
|
return (bSucceeded);
|
|
} /* CVssIJetWriter::ProcessIncludeExcludeLists () */
|
|
|
|
|
|
|
|
void CVssIJetWriter::PostProcessIncludeExcludeLists (bool bProcessingIncludeList)
|
|
{
|
|
PEXPANDEDPATHINFO pepnPathInfomation;
|
|
PLIST_ENTRY pleElement;
|
|
const PLIST_ENTRY pleQueueHead = bProcessingIncludeList
|
|
? &m_leFilesToIncludeEntries
|
|
: &m_leFilesToExcludeEntries;
|
|
|
|
|
|
while (!IsListEmpty (pleQueueHead))
|
|
{
|
|
pleElement = RemoveHeadList (pleQueueHead);
|
|
|
|
BS_ASSERT (NULL != pleElement);
|
|
|
|
|
|
pepnPathInfomation = (PEXPANDEDPATHINFO)((PBYTE) pleElement - offsetof (EXPANDEDPATHINFO, leQueueHead));
|
|
|
|
delete [] pepnPathInfomation->pwszOriginalFilePath;
|
|
delete [] pepnPathInfomation->pwszOriginalFileName;
|
|
delete [] pepnPathInfomation->pwszExpandedFilePath;
|
|
delete [] pepnPathInfomation->pwszExpandedFileName;
|
|
delete pepnPathInfomation;
|
|
}
|
|
} /* CVssIJetWriter::PostProcessIncludeExcludeLists () */
|
|
|
|
|
|
|
|
bool STDMETHODCALLTYPE CVssIJetWriter::OnIdentify (IN IVssCreateWriterMetadata *pMetadata)
|
|
{
|
|
CVssFunctionTracer ft(VSSDBG_GEN, L"CVssIJetWriter::OnIdentify");
|
|
|
|
JET_ERR jetStatus;
|
|
HRESULT hrStatus;
|
|
bool bSucceeded = true;
|
|
ULONG ulInstanceInfoCount = 0;
|
|
JET_INSTANCE_INFO *pInstanceInfo;
|
|
|
|
|
|
m_pIMetadata = pMetadata;
|
|
|
|
|
|
/*
|
|
** Set up list of include and exclude files. ready for use in
|
|
** filtering Jet databases and adding include/exclude files lists.
|
|
*/
|
|
bSucceeded = EXECUTEIF (bSucceeded, (PreProcessIncludeExcludeLists (true )));
|
|
bSucceeded = EXECUTEIF (bSucceeded, (PreProcessIncludeExcludeLists (false)));
|
|
|
|
bSucceeded = EXECUTEIF (bSucceeded, (JET_errSuccess <= JetGetInstanceInfo (&ulInstanceInfoCount,
|
|
&pInstanceInfo)));
|
|
|
|
for (ULONG ulInstanceIndex = 0; ulInstanceIndex < ulInstanceInfoCount; ulInstanceIndex++)
|
|
{
|
|
bSucceeded = EXECUTEIF (bSucceeded, (ProcessJetInstance (pInstanceInfo + ulInstanceIndex)));
|
|
}
|
|
|
|
|
|
bSucceeded = EXECUTEIF (bSucceeded, (ProcessIncludeExcludeLists (true )));
|
|
bSucceeded = EXECUTEIF (bSucceeded, (ProcessIncludeExcludeLists (false)));
|
|
bSucceeded = EXECUTEIF (bSucceeded, (m_pwrapper->OnIdentify (pMetadata)));
|
|
|
|
|
|
|
|
PostProcessIncludeExcludeLists (true );
|
|
PostProcessIncludeExcludeLists (false);
|
|
|
|
|
|
m_pIMetadata = NULL;
|
|
|
|
return (bSucceeded);
|
|
} /* CVssIJetWriter::OnIdentify () */
|
|
|
|
|
|
|
|
bool STDMETHODCALLTYPE CVssIJetWriter::OnPrepareBackup (IN IVssWriterComponents *pIVssWriterComponents)
|
|
{
|
|
CVssFunctionTracer ft(VSSDBG_GEN, L"CVssIJetWriter::OnPrepareBackup");
|
|
|
|
bool bSucceeded;
|
|
|
|
|
|
bSucceeded = m_pwrapper->OnPrepareBackupBegin (pIVssWriterComponents);
|
|
|
|
bSucceeded = EXECUTEIF (bSucceeded, (m_pwrapper->OnPrepareBackupEnd (pIVssWriterComponents, bSucceeded)));
|
|
|
|
return (bSucceeded);
|
|
} /* CVssIJetWriter::OnPrepareBackup () */
|
|
|
|
|
|
|
|
|
|
bool STDMETHODCALLTYPE CVssIJetWriter::OnBackupComplete (IN IVssWriterComponents *pIVssWriterComponents)
|
|
{
|
|
CVssFunctionTracer ft(VSSDBG_GEN, L"CVssIJetWriter::OnBackupComplete");
|
|
|
|
bool bSucceeded;
|
|
|
|
|
|
bSucceeded = m_pwrapper->OnBackupCompleteBegin (pIVssWriterComponents);
|
|
|
|
bSucceeded = EXECUTEIF (bSucceeded, (m_pwrapper->OnBackupCompleteEnd (pIVssWriterComponents, bSucceeded)));
|
|
|
|
return (bSucceeded);
|
|
} /* CVssIJetWriter::OnBackupComplete () */
|
|
|
|
|
|
|
|
|
|
bool STDMETHODCALLTYPE CVssIJetWriter::OnPrepareSnapshot()
|
|
{
|
|
CVssFunctionTracer ft(VSSDBG_GEN, L"CVssIJetWriter::OnPrepareSnapshot");
|
|
|
|
if (!m_pwrapper->OnPrepareSnapshotBegin())
|
|
return false;
|
|
|
|
// go to Jet level directly
|
|
bool fSuccess = JET_errSuccess <= JetOSSnapshotPrepare( &m_idJet , 0 );
|
|
return m_pwrapper->OnPrepareSnapshotEnd(fSuccess);
|
|
} /* CVssIJetWriter::OnPrepareSnapshot () */
|
|
|
|
|
|
|
|
bool STDMETHODCALLTYPE CVssIJetWriter::OnFreeze()
|
|
{
|
|
CVssFunctionTracer ft(VSSDBG_GEN, L"CVssIJetWriter::OnFreeze");
|
|
|
|
unsigned long cInstanceInfo = 0;
|
|
JET_INSTANCE_INFO * aInstanceInfo = NULL;
|
|
bool fDependence = true;
|
|
|
|
if (!m_pwrapper->OnFreezeBegin())
|
|
return false;
|
|
|
|
|
|
// we need to freeze at Jet level, then check from this DLL the dependencies
|
|
// (as here we hagve the snapshot object implementation and COM registration)
|
|
|
|
if ( JET_errSuccess > JetOSSnapshotFreeze( m_idJet , &cInstanceInfo, &aInstanceInfo, 0 ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// return false if some instances are only partialy affected
|
|
fDependence = FCheckVolumeDependencies(cInstanceInfo, aInstanceInfo);
|
|
(void)JetFreeBuffer( (char *)aInstanceInfo );
|
|
|
|
if ( !fDependence )
|
|
{
|
|
JET_ERR err;
|
|
// on error, stop the snapshot, return false
|
|
err = JetOSSnapshotThaw( m_idJet , 0 );
|
|
// shell we check for time-out error here ?
|
|
// (debugging may result in time-out error the call)
|
|
BS_ASSERT ( JET_errSuccess == err );
|
|
}
|
|
|
|
return m_pwrapper->OnFreezeEnd(fDependence);
|
|
} /* CVssIJetWriter::OnFreeze () */
|
|
|
|
|
|
|
|
bool STDMETHODCALLTYPE CVssIJetWriter::OnThaw()
|
|
{
|
|
CVssFunctionTracer ft(VSSDBG_GEN, L"CVssIJetWriter::OnThaw");
|
|
|
|
bool fSuccess1 = m_pwrapper->OnThawBegin();
|
|
// go to Jet level directly. It will eventualy return timeout errors
|
|
bool fSuccess2 = JET_errSuccess <= JetOSSnapshotThaw( m_idJet , 0 );
|
|
return fSuccess1 && m_pwrapper->OnThawEnd(fSuccess2);
|
|
} /* CVssIJetWriter::OnThaw () */
|
|
|
|
bool STDMETHODCALLTYPE CVssIJetWriter::OnPostSnapshot
|
|
(
|
|
IN IVssWriterComponents *pIVssWriterComponents
|
|
)
|
|
{
|
|
return m_pwrapper->OnPostSnapshot(pIVssWriterComponents);
|
|
}
|
|
|
|
|
|
|
|
bool STDMETHODCALLTYPE CVssIJetWriter::OnAbort()
|
|
{
|
|
CVssFunctionTracer ft(VSSDBG_GEN, L"CVssIJetWriter::OnAbort");
|
|
|
|
m_pwrapper->OnAbortBegin();
|
|
JetOSSnapshotThaw( m_idJet , 0 );
|
|
m_pwrapper->OnAbortEnd();
|
|
return true;
|
|
} /* CVssIJetWriter::OnAbort () */
|
|
|
|
bool STDMETHODCALLTYPE CVssIJetWriter::OnPreRestore
|
|
(
|
|
IN IVssWriterComponents *pIVssWriterComponents
|
|
)
|
|
{
|
|
CVssFunctionTracer ft(VSSDBG_GEN, L"CVssIJetWriter::OnPostRestore");
|
|
|
|
if (!m_pwrapper->OnPreRestoreBegin(pIVssWriterComponents))
|
|
return false;
|
|
|
|
// go to Jet level directly
|
|
// BUGBUG - need to add the correct Jet restore call/code here (MCJ)
|
|
// bool fSuccess = JET_errSuccess <= JetRestore ( ??? &m_idJet , 0 );
|
|
bool fSuccess = TRUE;
|
|
return m_pwrapper->OnPreRestoreEnd(pIVssWriterComponents, fSuccess);
|
|
} /* CVssIJetWriter::OnPreRestore () */
|
|
|
|
|
|
|
|
|
|
bool STDMETHODCALLTYPE CVssIJetWriter::OnPostRestore
|
|
(
|
|
IN IVssWriterComponents *pIVssWriterComponents
|
|
)
|
|
{
|
|
CVssFunctionTracer ft(VSSDBG_GEN, L"CVssIJetWriter::OnPostRestore");
|
|
|
|
if (!m_pwrapper->OnPostRestoreBegin(pIVssWriterComponents))
|
|
return false;
|
|
|
|
// go to Jet level directly
|
|
// BUGBUG - need to add the correct Jet restore call/code here (MCJ)
|
|
// bool fSuccess = JET_errSuccess <= JetRestore ( ??? &m_idJet , 0 );
|
|
bool fSuccess = TRUE;
|
|
return m_pwrapper->OnPostRestoreEnd(pIVssWriterComponents, fSuccess);
|
|
} /* CVssIJetWriter::OnPostRestore () */
|
|
|
|
|
|
|
|
bool CVssIJetWriter::FCheckPathVolumeDependencies(const char * szPath) const
|
|
{
|
|
// use static variable in order to avoid alloc/free
|
|
WCHAR wszPath[MAX_PATH];
|
|
|
|
if (MultiByteToWideChar(CP_OEMCP, 0, szPath, -1, wszPath, MAX_PATH ) == 0 )
|
|
{
|
|
BS_ASSERT( ERROR_INSUFFICIENT_BUFFER != GetLastError() );
|
|
return false;
|
|
}
|
|
|
|
// use standart Writer call to check the affected path
|
|
return IsPathAffected(wszPath);
|
|
} /* CVssIJetWriter::FCheckPathVolumeDependencies () */
|
|
|
|
|
|
|
|
// all or nothing check: all path in instance are affected or none !
|
|
//
|
|
bool CVssIJetWriter::FCheckInstanceVolumeDependencies (const JET_INSTANCE_INFO * pInstanceInfo) const
|
|
{
|
|
BS_ASSERT(pInstanceInfo);
|
|
|
|
JET_ERR err = JET_errSuccess;
|
|
bool fAffected = false;
|
|
char szPath[ MAX_PATH ];
|
|
|
|
|
|
// check first system and log path
|
|
err = JetGetSystemParameter( pInstanceInfo->hInstanceId, JET_sesidNil, JET_paramLogFilePath, NULL, szPath, sizeof( szPath ) );
|
|
if ( JET_errSuccess > err )
|
|
return false;
|
|
|
|
fAffected = FCheckPathVolumeDependencies( szPath );
|
|
|
|
err = JetGetSystemParameter
|
|
(
|
|
pInstanceInfo->hInstanceId,
|
|
JET_sesidNil,
|
|
JET_paramSystemPath,
|
|
NULL,
|
|
szPath,
|
|
sizeof(szPath)
|
|
);
|
|
|
|
if (JET_errSuccess > err)
|
|
return false;
|
|
|
|
|
|
fAffected = !(fAffected ^ FCheckPathVolumeDependencies(szPath));
|
|
if (!fAffected)
|
|
return false;
|
|
|
|
for (ULONG_PTR iDatabase = 0;
|
|
iDatabase < pInstanceInfo->cDatabases;
|
|
iDatabase++)
|
|
{
|
|
char * szFile = pInstanceInfo->szDatabaseFileName[iDatabase];
|
|
|
|
BS_ASSERT(szFile); // we always have a db file name
|
|
fAffected = !(fAffected ^ FCheckPathVolumeDependencies(szFile));
|
|
if (!fAffected)
|
|
return false;
|
|
|
|
szFile = pInstanceInfo->szDatabaseSLVFileName[iDatabase];
|
|
|
|
// if no SLV file, go to next database
|
|
if (!szFile)
|
|
continue;
|
|
|
|
fAffected = !(fAffected ^ FCheckPathVolumeDependencies(szFile));
|
|
if ( !fAffected )
|
|
return false;
|
|
}
|
|
|
|
// all set !
|
|
return true;
|
|
} /* CVssIJetWriter::FCheckInstanceVolumeDependencies () */
|
|
|
|
|
|
|
|
bool CVssIJetWriter::FCheckVolumeDependencies
|
|
(
|
|
unsigned long cInstanceInfo,
|
|
JET_INSTANCE_INFO * aInstanceInfo
|
|
) const
|
|
{
|
|
bool fResult = true;
|
|
|
|
// check each instance
|
|
while (cInstanceInfo && fResult)
|
|
{
|
|
cInstanceInfo--;
|
|
fResult = FCheckInstanceVolumeDependencies (aInstanceInfo + cInstanceInfo);
|
|
}
|
|
|
|
return fResult;
|
|
} /* CVssIJetWriter::FCheckVolumeDependencies () */
|
|
|
|
|
|
|
|
// internal method to assign basic members
|
|
HRESULT CVssIJetWriter::InternalInitialize (IN VSS_ID idWriter,
|
|
IN LPCWSTR wszWriterName,
|
|
IN bool bSystemService,
|
|
IN bool bBootableSystemState,
|
|
IN LPCWSTR wszFilesToInclude,
|
|
IN LPCWSTR wszFilesToExclude)
|
|
{
|
|
HRESULT hrStatus = NOERROR;
|
|
|
|
|
|
CVssWriter::Initialize (idWriter,
|
|
wszWriterName,
|
|
bBootableSystemState
|
|
? VSS_UT_BOOTABLESYSTEMSTATE
|
|
: (bSystemService
|
|
? VSS_UT_SYSTEMSERVICE
|
|
: VSS_UT_USERDATA),
|
|
VSS_ST_TRANSACTEDDB,
|
|
VSS_APP_BACK_END);
|
|
|
|
|
|
m_idWriter = idWriter;
|
|
m_bSystemService = bSystemService;
|
|
m_bBootableSystemState = bBootableSystemState;
|
|
m_wszWriterName = _wcsdup(wszWriterName);
|
|
m_wszFilesToInclude = _wcsdup(wszFilesToInclude);
|
|
m_wszFilesToExclude = _wcsdup(wszFilesToExclude);
|
|
|
|
if ((NULL == m_wszWriterName) ||
|
|
(NULL == m_wszFilesToInclude) ||
|
|
(NULL == m_wszFilesToExclude))
|
|
{
|
|
delete m_wszWriterName;
|
|
delete m_wszFilesToInclude;
|
|
delete m_wszFilesToExclude;
|
|
|
|
m_wszWriterName = NULL;
|
|
m_wszFilesToInclude = NULL;
|
|
m_wszFilesToExclude = NULL;
|
|
|
|
hrStatus = E_OUTOFMEMORY;
|
|
}
|
|
|
|
|
|
return (hrStatus);
|
|
} /* CVssIJetWriter::InternalInitialize () */
|
|
|
|
|
|
|
|
// do initialization
|
|
HRESULT STDMETHODCALLTYPE CVssIJetWriter::Initialize (IN VSS_ID idWriter, // id of writer
|
|
IN LPCWSTR wszWriterName, // writer name
|
|
IN bool bSystemService, // is this a system service
|
|
IN bool bBootableSystemState, // is this writer part of bootable system state
|
|
IN LPCWSTR wszFilesToInclude, // additional files to include
|
|
IN LPCWSTR wszFilesToExclude, // additional files to exclude
|
|
IN CVssJetWriter *pWriter, // writer wrapper class
|
|
OUT void **ppInstance) // output instance
|
|
{
|
|
CVssFunctionTracer ft(VSSDBG_GEN, L"CVssIJetWriter::Initialize");
|
|
|
|
try
|
|
{
|
|
// check parameters
|
|
if (ppInstance == NULL)
|
|
{
|
|
ft.Throw (VSSDBG_GEN, E_INVALIDARG, L"NULL output parameter.");
|
|
}
|
|
|
|
// change null pointer to null strings for files to include
|
|
// and files to exclude
|
|
if (wszFilesToInclude == NULL)
|
|
wszFilesToInclude = L"";
|
|
|
|
if (wszFilesToExclude == NULL)
|
|
wszFilesToExclude = L"";
|
|
|
|
|
|
if (!ValidateIncludeExcludeList (wszFilesToInclude))
|
|
{
|
|
ft.Throw (VSSDBG_GEN, E_INVALIDARG, L"Bad FilesToInclude list.");
|
|
}
|
|
|
|
if (!ValidateIncludeExcludeList (wszFilesToExclude))
|
|
{
|
|
ft.Throw (VSSDBG_GEN, E_INVALIDARG, L"Bad FilesToExclude list.");
|
|
}
|
|
|
|
|
|
|
|
// null output parameter
|
|
*ppInstance = NULL;
|
|
|
|
// create instance
|
|
PVSSIJETWRITER pInstance = new CVssIJetWriter;
|
|
|
|
// create instance
|
|
ft.ThrowIf (NULL == pInstance,
|
|
VSSDBG_GEN,
|
|
E_OUTOFMEMORY,
|
|
L"FAILED creating CVssIJetWriter object due to allocation failure.");
|
|
|
|
|
|
|
|
// call internal initialization
|
|
ft.hr = pInstance->InternalInitialize (idWriter,
|
|
wszWriterName,
|
|
bSystemService,
|
|
bBootableSystemState,
|
|
wszFilesToInclude,
|
|
wszFilesToExclude);
|
|
|
|
ft.ThrowIf (ft.HrFailed(),
|
|
VSSDBG_GEN,
|
|
ft.hr,
|
|
L"FAILED during internal initialisation of CVssIJetWriter object");
|
|
|
|
|
|
|
|
// Subscribe the object.
|
|
ft.hr = pInstance->Subscribe();
|
|
|
|
ft.ThrowIf (ft.HrFailed(),
|
|
VSSDBG_GEN,
|
|
ft.hr,
|
|
L"FAILED during internal initialisation of CVssIJetWriter object");
|
|
|
|
|
|
|
|
((CVssIJetWriter *) pInstance)->m_pwrapper = pWriter;
|
|
*ppInstance = (void *) pInstance;
|
|
} VSS_STANDARD_CATCH(ft)
|
|
|
|
|
|
return (ft.hr);
|
|
} /* CVssIJetWriter::Initialize () */
|
|
|
|
|
|
|
|
void STDMETHODCALLTYPE CVssIJetWriter::Uninitialize(IN PVSSIJETWRITER pInstance)
|
|
{
|
|
CVssFunctionTracer ft(VSSDBG_GEN, L"CVssIJetWriter::Uninitialize");
|
|
|
|
try
|
|
{
|
|
CVssIJetWriter *pWriter = (CVssIJetWriter *) pInstance;
|
|
// Unsubscribe the object.
|
|
|
|
BS_ASSERT(pWriter);
|
|
|
|
pWriter->Unsubscribe();
|
|
delete pWriter;
|
|
}
|
|
VSS_STANDARD_CATCH(ft)
|
|
} /* CVssIJetWriter::Uninitialize () */
|
|
|