|
|
/*++
Copyright (c) 2001 Microsoft Corporation
Abstract:
module common.cxx | Implementation of SnapshotWriter common code
Author:
Michael C. Johnson [mikejohn] 03-Feb-2000 Stefan R. Steiner [SSteiner] 27-Jul-2001
Description: Contains general code and definitions used by the Shim writer and the various other writers.
Revision History:
reuvenl 05/02/2002 Incorporated code from common.h/cpp into this module
--*/
#include "stdafx.h"
#include "vssmsg.h"
#include "wrtcommon.hxx"
#include <accctrl.h>
#include <aclapi.h>
////////////////////////////////////////////////////////////////////////
// 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 "WSHCOMNC"
// Definitions for internal static functions
static HRESULT ConstructSecurityAttributes ( IN OUT PSECURITY_ATTRIBUTES psaSecurityAttributes, IN BOOL bIncludeBackupOperator );
static VOID CleanupSecurityAttributes( IN PSECURITY_ATTRIBUTES psaSecurityAttributes );
static BOOL FixupFileInfo( IN LPCWSTR pwszPathName, IN LPSECURITY_ATTRIBUTES lpSecurityAttributes, IN DWORD dwExtraAttributes);
static PWCHAR const GetStringFromStateCode (DWORD dwState);
static DWORD const GetControlCodeFromTargetState (const DWORD dwTargetState);
static DWORD const GetNormalisedState (DWORD dwCurrentState);
static HRESULT WaitForServiceToEnterState (SC_HANDLE shService, DWORD dwMaxDelayInMilliSeconds, const DWORD dwDesiredState);
/*
** The first group of (overloaded) routines manipulate UNICODE_STRING ** strings. The rules which apply here are: ** ** 1) The Buffer field points to an array of characters (WCHAR) with ** the length of the buffer being specified in the MaximumLength ** field. If the Buffer field is non-NULL it must point to a valid ** buffer capable of holding at least one character. If the Buffer ** field is NULL, the MaximumLength and Length fields must both be ** zero. ** ** 2) Any valid string in the buffer is always terminated with a ** UNICODE_NULL. ** ** 3) the MaximumLength describes the length of the buffer measured in ** bytes. This value must be even. ** ** 4) The Length field describes the number of valid characters in the ** buffer measured in BYTES, excluding the termination ** character. Since the string must always have a termination ** character ('\0'), the maximum value of Length is MaximumLength - 2. ** ** ** The routines available are:- ** ** StringInitialise () ** StringTruncate () ** StringSetLength () ** StringAllocate () ** StringFree () ** StringCreateFromString () ** StringAppendString () ** StringCreateFromExpandedString () ** */
/*
**++ ** ** Routine Description: ** ** ** Arguments: ** ** ** Side Effects: ** ** ** Return Value: ** ** Any HRESULT ** **-- */
HRESULT StringInitialise (PUNICODE_STRING pucsString) { pucsString->Buffer = NULL; pucsString->Length = 0; pucsString->MaximumLength = 0;
return (NOERROR); } /* StringInitialise () */
HRESULT StringInitialise (PUNICODE_STRING pucsString, LPCWSTR pwszString) { return (StringInitialise (pucsString, (PWCHAR) pwszString)); }
HRESULT StringInitialise (PUNICODE_STRING pucsString, PWCHAR pwszString) { HRESULT hrStatus = NOERROR; ULONG ulStringLength = wcslen (pwszString) * sizeof (WCHAR);
if (ulStringLength >= (MAXUSHORT - sizeof (UNICODE_NULL))) { hrStatus = HRESULT_FROM_WIN32 (ERROR_BAD_LENGTH); } else { pucsString->Buffer = pwszString; pucsString->Length = (USHORT) ulStringLength; pucsString->MaximumLength = (USHORT) (ulStringLength + sizeof (UNICODE_NULL)); }
return (hrStatus); } /* StringInitialise () */
HRESULT StringTruncate (PUNICODE_STRING pucsString, USHORT usSizeInChars) { HRESULT hrStatus = NOERROR; USHORT usNewLength = (USHORT)(usSizeInChars * sizeof (WCHAR));
if (usNewLength > pucsString->Length) { hrStatus = HRESULT_FROM_WIN32 (ERROR_BAD_LENGTH); } else { pucsString->Buffer [usSizeInChars] = UNICODE_NULL; pucsString->Length = usNewLength; }
return (hrStatus); } /* StringTruncate () */
HRESULT StringSetLength (PUNICODE_STRING pucsString) { HRESULT hrStatus = NOERROR; ULONG ulStringLength = wcslen (pucsString->Buffer) * sizeof (WCHAR);
if (ulStringLength >= (MAXUSHORT - sizeof (UNICODE_NULL))) { hrStatus = HRESULT_FROM_WIN32 (ERROR_BAD_LENGTH); } else { pucsString->Length = (USHORT) ulStringLength; pucsString->MaximumLength = (USHORT) UMAX (pucsString->MaximumLength, pucsString->Length + sizeof (UNICODE_NULL)); }
return (hrStatus); } /* StringSetLength () */
HRESULT StringAllocate (PUNICODE_STRING pucsString, USHORT usMaximumStringLengthInBytes) { HRESULT hrStatus = NOERROR; LPVOID pvBuffer = NULL; SIZE_T cActualLength = 0;
pvBuffer = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, usMaximumStringLengthInBytes);
hrStatus = GET_STATUS_FROM_POINTER (pvBuffer);
if (SUCCEEDED (hrStatus)) { pucsString->Buffer = (PWCHAR)pvBuffer; pucsString->Length = 0; pucsString->MaximumLength = usMaximumStringLengthInBytes;
cActualLength = HeapSize (GetProcessHeap (), 0, pvBuffer);
if ((cActualLength <= MAXUSHORT) && (cActualLength > usMaximumStringLengthInBytes)) { pucsString->MaximumLength = (USHORT) cActualLength; } }
return (hrStatus); } /* StringAllocate () */
HRESULT StringFree (PUNICODE_STRING pucsString) { HRESULT hrStatus = NOERROR; BOOL bSucceeded;
if (NULL != pucsString->Buffer) { bSucceeded = HeapFree (GetProcessHeap (), 0, pucsString->Buffer);
hrStatus = GET_STATUS_FROM_BOOL (bSucceeded); }
if (SUCCEEDED (hrStatus)) { pucsString->Buffer = NULL; pucsString->Length = 0; pucsString->MaximumLength = 0; }
return (hrStatus); } /* StringFree () */
HRESULT StringCreateFromString (PUNICODE_STRING pucsNewString, PUNICODE_STRING pucsOriginalString) { HRESULT hrStatus = NOERROR;
hrStatus = StringAllocate (pucsNewString, pucsOriginalString->MaximumLength);
if (SUCCEEDED (hrStatus)) { memcpy (pucsNewString->Buffer, pucsOriginalString->Buffer, pucsOriginalString->Length);
pucsNewString->Length = pucsOriginalString->Length;
pucsNewString->Buffer [pucsNewString->Length / sizeof (WCHAR)] = UNICODE_NULL; }
return (hrStatus); } /* StringCreateFromString () */
HRESULT StringCreateFromString (PUNICODE_STRING pucsNewString, LPCWSTR pwszOriginalString) { HRESULT hrStatus = NOERROR; ULONG ulStringLength = wcslen (pwszOriginalString) * sizeof (WCHAR);
if (ulStringLength >= (MAXUSHORT - sizeof (UNICODE_NULL))) { hrStatus = HRESULT_FROM_WIN32 (ERROR_BAD_LENGTH); }
if (SUCCEEDED (hrStatus)) { hrStatus = StringAllocate (pucsNewString, (USHORT) (ulStringLength + sizeof (UNICODE_NULL))); }
if (SUCCEEDED (hrStatus)) { memcpy (pucsNewString->Buffer, pwszOriginalString, ulStringLength);
pucsNewString->Length = (USHORT) ulStringLength;
pucsNewString->Buffer [pucsNewString->Length / sizeof (WCHAR)] = UNICODE_NULL; }
return (hrStatus); } /* StringCreateFromString () */
HRESULT StringCreateFromString (PUNICODE_STRING pucsNewString, PUNICODE_STRING pucsOriginalString, DWORD dwExtraChars) { HRESULT hrStatus = NOERROR; ULONG ulStringLength = pucsOriginalString->MaximumLength + (dwExtraChars * sizeof (WCHAR));
if (ulStringLength >= (MAXUSHORT - sizeof (UNICODE_NULL))) { hrStatus = HRESULT_FROM_WIN32 (ERROR_BAD_LENGTH); }
if (SUCCEEDED (hrStatus)) { hrStatus = StringAllocate (pucsNewString, (USHORT) (ulStringLength + sizeof (UNICODE_NULL))); }
if (SUCCEEDED (hrStatus)) { memcpy (pucsNewString->Buffer, pucsOriginalString->Buffer, pucsOriginalString->Length);
pucsNewString->Length = pucsOriginalString->Length;
pucsNewString->Buffer [pucsNewString->Length / sizeof (WCHAR)] = UNICODE_NULL; }
return (hrStatus); } /* StringCreateFromString () */
HRESULT StringCreateFromString (PUNICODE_STRING pucsNewString, LPCWSTR pwszOriginalString, DWORD dwExtraChars) { HRESULT hrStatus = NOERROR; ULONG ulStringLength = (wcslen (pwszOriginalString) + dwExtraChars) * sizeof (WCHAR);
if (ulStringLength >= (MAXUSHORT - sizeof (UNICODE_NULL))) { hrStatus = HRESULT_FROM_WIN32 (ERROR_BAD_LENGTH); }
if (SUCCEEDED (hrStatus)) { hrStatus = StringAllocate (pucsNewString, (USHORT) (ulStringLength + sizeof (UNICODE_NULL))); }
if (SUCCEEDED (hrStatus)) { memcpy (pucsNewString->Buffer, pwszOriginalString, wcslen(pwszOriginalString)*sizeof(WCHAR));
pucsNewString->Length = (USHORT) (wcslen(pwszOriginalString)*sizeof(WCHAR));
pucsNewString->Buffer [pucsNewString->Length / sizeof (WCHAR)] = UNICODE_NULL; }
return (hrStatus); } /* StringCreateFromString () */
HRESULT StringAppendString (PUNICODE_STRING pucsTarget, PUNICODE_STRING pucsSource) { HRESULT hrStatus = NOERROR;
if (pucsSource->Length > (pucsTarget->MaximumLength - pucsTarget->Length - sizeof (UNICODE_NULL))) { hrStatus = HRESULT_FROM_WIN32 (ERROR_BAD_LENGTH); } else { memmove (&pucsTarget->Buffer [pucsTarget->Length / sizeof (WCHAR)], pucsSource->Buffer, pucsSource->Length + sizeof (UNICODE_NULL));
pucsTarget->Length += pucsSource->Length; }
/*
** There should be no reason in this code using this routine to ** have to deal with a short buffer so trap potential problems. */ BS_ASSERT (SUCCEEDED (hrStatus));
return (hrStatus); } /* StringAppendString () */
HRESULT StringAppendString (PUNICODE_STRING pucsTarget, PWCHAR pwszSource) { HRESULT hrStatus = NOERROR; UNICODE_STRING ucsSource;
StringInitialise (&ucsSource, pwszSource);
hrStatus = StringAppendString (pucsTarget, &ucsSource);
/*
** There should be no reason in this code using this routine to ** have to deal with a short buffer so trap potential problems. */ BS_ASSERT (SUCCEEDED (hrStatus));
return (hrStatus); } /* StringAppendString () */
HRESULT StringCreateFromExpandedString (PUNICODE_STRING pucsNewString, LPCWSTR pwszOriginalString) { return (StringCreateFromExpandedString (pucsNewString, pwszOriginalString, 0)); }
HRESULT StringCreateFromExpandedString (PUNICODE_STRING pucsNewString, PUNICODE_STRING pucsOriginalString) { return (StringCreateFromExpandedString (pucsNewString, pucsOriginalString->Buffer, 0)); }
HRESULT StringCreateFromExpandedString (PUNICODE_STRING pucsNewString, PUNICODE_STRING pucsOriginalString, DWORD dwExtraChars) { return (StringCreateFromExpandedString (pucsNewString, pucsOriginalString->Buffer, dwExtraChars)); }
HRESULT StringCreateFromExpandedString (PUNICODE_STRING pucsNewString, LPCWSTR pwszOriginalString, DWORD dwExtraChars) { HRESULT hrStatus = NOERROR; DWORD dwStringLength;
/*
** Remember, ExpandEnvironmentStringsW () includes the terminating null in the response. */ dwStringLength = ExpandEnvironmentStringsW (pwszOriginalString, NULL, 0) + dwExtraChars;
hrStatus = GET_STATUS_FROM_BOOL (0 != dwStringLength);
if (SUCCEEDED (hrStatus) && ((dwStringLength * sizeof (WCHAR)) > MAXUSHORT)) { hrStatus = HRESULT_FROM_WIN32 (ERROR_BAD_LENGTH); }
if (SUCCEEDED (hrStatus)) { hrStatus = StringAllocate (pucsNewString, (USHORT)(dwStringLength * sizeof (WCHAR))); }
if (SUCCEEDED (hrStatus)) { /*
** Note that if the expanded string has gotten bigger since we ** allocated the buffer then too bad, we may not get all the ** new translation. Not that we really expect these expanded ** strings to have changed any time recently. */ dwStringLength = ExpandEnvironmentStringsW (pwszOriginalString, pucsNewString->Buffer, pucsNewString->MaximumLength / sizeof (WCHAR));
hrStatus = GET_STATUS_FROM_BOOL (0 != dwStringLength);
if (SUCCEEDED (hrStatus)) { pucsNewString->Length = (USHORT) ((dwStringLength - 1) * sizeof (WCHAR)); } }
return (hrStatus); } /* StringCreateFromExpandedString () */
/*
**++ ** ** Routine Description: ** ** Closes a standard Win32 handle and set it to INVALID_HANDLE_VALUE. ** Safe to be called multiple times on the same handle or on a handle ** initialised to INVALID_HANDLE_VALUE or NULL. ** ** ** Arguments: ** ** phHandle Address of the handle to be closed ** ** ** Side Effects: ** ** ** Return Value: ** ** Any HRESULT from CloseHandle() ** **-- */
HRESULT CommonCloseHandle (PHANDLE phHandle) { CVssFunctionTracer ft(VSSDBG_WRTCMN, L"CommonCloseHandle"); HRESULT hrStatus = NOERROR; BOOL bSucceeded;
if ((INVALID_HANDLE_VALUE != *phHandle) && (NULL != *phHandle)) { bSucceeded = CloseHandle (*phHandle);
hrStatus = GET_STATUS_FROM_BOOL (bSucceeded);
if (SUCCEEDED (hrStatus)) { *phHandle = INVALID_HANDLE_VALUE; } }
return (hrStatus); } /* CommonCloseHandle () */
#define VALID_PATH( path ) ( ( (wcslen(path) >= 2) && ( path[0] == DIR_SEP_CHAR ) && ( path[1] == DIR_SEP_CHAR ) ) || \
( (wcslen(path) >= 3) && iswalpha( path[0] ) && ( path[1] == L':' ) && ( path[2] == DIR_SEP_CHAR ) ) )
/*++
** ** Routine Description: ** ** Creates any number of directories along a path. Only works for ** full path names with no relative elements in it. Other than that ** it works identically as CreateDirectory() works and sets the same ** error codes except it doesn't return an error if the complete ** path already exists. ** ** Arguments: ** ** pwszPathName - The path with possible directory components to create. ** ** lpSecurityAttributes - ** ** Return Value: ** ** TRUE - Sucessful ** FALSE - GetLastError() can return one of these (and others): ** ERROR_ALREADY_EXISTS - when something other than a file exists somewhere in the path. ** ERROR_BAD_PATHNAME - when \\servername alone is specified in the pathname ** ERROR_ACCESS_DENIED - when x:\ alone is specified in the pathname and x: exists ** ERROR_PATH_NOT_FOUND - when x: alone is specified in the pathname and x: doesn't exist. ** Should not get this error code for any other reason. ** ERROR_INVALID_NAME - when pathname doesn't start with either x:\ or \\ ** **-- */
BOOL VsCreateDirectories ( IN LPCWSTR pwszPathName, IN LPSECURITY_ATTRIBUTES lpSecurityAttributes, IN DWORD dwExtraAttributes) { DWORD dwObjAttribs, dwRetPreserve; BOOL bRet;
CVssFunctionTracer ft(VSSDBG_WRTCMN, L"VsCreateDirectories"); /*
** Make sure the path starts with the valid path prefixes */ if (!VALID_PATH (pwszPathName)) { SetLastError (ERROR_INVALID_NAME); return FALSE; }
/*
** Save away the current last error code. */ dwRetPreserve = GetLastError ();
/*
** Now test for the most common case, the directory already exists. This ** is the performance path. */
dwObjAttribs = GetFileAttributesW (pwszPathName); if ((dwObjAttribs != 0xFFFFFFFF) && (dwObjAttribs & FILE_ATTRIBUTE_DIRECTORY)) { /*
** Don't return an error if the directory already exists. ** This is the one case where this function differs from ** CreateDirectory(). Notice that even though another type of ** file may exist with this pathname, no error is returned yet ** since I want the error to come from CreateDirectory() to ** get CreateDirectory() error behavior. ** ** Since we're successful restore the last error code. */
if (FixupFileInfo(pwszPathName, lpSecurityAttributes, dwExtraAttributes) == TRUE) { SetLastError (dwRetPreserve); return TRUE; }
return FALSE; }
/*
** Now try to create the directory using the full path. Even ** though we might already know it exists as something other than ** a directory, get the error from CreateDirectory() instead of ** having to try to reverse engineer all possible errors that ** CreateDirectory() can return in the above code. ** ** It is probably the second most common case that when this ** function is called that only the last component in the ** directory doesn't exist. Let's try to make it. ** ** Note that it appears if a UNC path is given with a number of ** non-existing path components the remote server automatically ** creates all of those components when CreateDirectory is called. ** Therefore, the next call is almost always successful when the ** path is a UNC. */ bRet = CreateDirectoryW (pwszPathName, lpSecurityAttributes);
if (bRet) { SetFileAttributesW (pwszPathName, dwExtraAttributes);
/*
** Set it back to the last error code */ SetLastError (dwRetPreserve); return TRUE; }
else if (GetLastError () == ERROR_ALREADY_EXISTS) { /*
** Looks like someone created the name while we weren't ** looking. Check to see if it's a directory and return ** success if so, otherwise return the error that ** CreateDirectoryW() set. */ dwObjAttribs = GetFileAttributesW (pwszPathName);
if ((dwObjAttribs != 0xFFFFFFFF) && (dwObjAttribs & FILE_ATTRIBUTE_DIRECTORY)) { /*
** It's a directory. Declare victory. ** ** Restore the last error code */
if (FixupFileInfo(pwszPathName, lpSecurityAttributes, dwExtraAttributes) == TRUE) { SetLastError (dwRetPreserve); return TRUE; } return FALSE; } else { SetLastError (ERROR_ALREADY_EXISTS);
return FALSE; } } else if (GetLastError () != ERROR_PATH_NOT_FOUND ) { return FALSE; }
/*
** Allocate memory to hold the string while processing the path. ** The passed in string is a const. */ PWCHAR pwszTempPath = (PWCHAR) malloc ((wcslen (pwszPathName) + 1) * sizeof (WCHAR));
BS_ASSERT (pwszTempPath != NULL);
wcscpy (pwszTempPath, pwszPathName);
/*
** Appears some components in the path don't exist. Now try to ** create the components. */ PWCHAR pwsz, pwszSlash;
/*
** First skip the drive letter part or the \\server\sharename ** part and get to the first slash before the first level ** directory component. */ if (pwszTempPath [1] == L':') { /*
** Path in the form of x:\..., skip first 2 chars */ pwsz = pwszTempPath + 2; } else { /*
** Path should be in form of \\servername\sharename. Can be ** \\?\d: Search to first slash after sharename ** ** First search to first char of the share name */ pwsz = pwszTempPath + 2;
while ((*pwsz != L'\0') && (*pwsz != DIR_SEP_CHAR)) { ++pwsz; }
/*
** Eat up all continuous slashes and get to first char of the ** share name */ while (*pwsz == DIR_SEP_CHAR) { ++pwsz; }
if (*pwsz == L'\0') { /*
** This shouldn't have happened since the CreateDirectory ** call should have caught it. Oh, well, deal with it. */ SetLastError (ERROR_BAD_PATHNAME);
free (pwszTempPath);
return FALSE; }
/*
** Now at first char of share name, let's search for first ** slash after the share name to get to the (first) shash in ** front the first level directory. */ while ((*pwsz != L'\0') && (*pwsz != DIR_SEP_CHAR)) { ++pwsz; } }
/*
** Eat up all continuous slashes before the first level directory */ while (*pwsz == DIR_SEP_CHAR) { ++pwsz; }
/*
** Now at first char of the first level directory, let's search ** for first slash after the directory. */ while ((*pwsz != L'\0') && (*pwsz != DIR_SEP_CHAR)) { ++pwsz; }
/*
** If pwsz is pointing to a null char, that means only the first ** level directory needs to be created. Fall through to the leaf ** node create directory. */ while (*pwsz != L'\0') { pwszSlash = pwsz; // Keep pointer to the separator
/*
** Eat up all continuous slashes. */ while (*pwsz == DIR_SEP_CHAR) { ++pwsz; }
if (*pwsz == L'\0') { /*
** There were just slashes at the end of the path. Break ** out of loop, let the leaf node CreateDirectory create ** the last directory. */ break; }
/*
** Terminate the directory path at the current level. */ *pwszSlash = L'\0';
dwObjAttribs = GetFileAttributesW (pwszTempPath);
if ((dwObjAttribs == 0XFFFFFFFF) || ((dwObjAttribs & FILE_ATTRIBUTE_DIRECTORY) == 0)) { bRet = CreateDirectoryW (pwszTempPath, lpSecurityAttributes);
if (bRet) { SetFileAttributesW (pwszTempPath, dwExtraAttributes); } else { if (ERROR_ALREADY_EXISTS != GetLastError ()) { /*
** Restore the slash. */ *pwszSlash = DIR_SEP_CHAR;
free (pwszTempPath); return FALSE; } else { /*
** Looks like someone created the name whilst we ** weren't looking. Check to see if it's a ** directory and continue if so, otherwise return ** the error that CreateDirectoryW() set. */ dwObjAttribs = GetFileAttributesW (pwszTempPath);
if ((dwObjAttribs == 0xFFFFFFFF) || ((dwObjAttribs & FILE_ATTRIBUTE_DIRECTORY) == 0)) { /*
** It's not what we recognise as a ** directory. Declare failure. Set the error ** code to that which CreateDirectoryW() ** returned, restore the slash, free the ** buffer and get out of here. */ SetLastError (ERROR_ALREADY_EXISTS);
*pwszSlash = DIR_SEP_CHAR;
free (pwszTempPath);
return FALSE; }
/*
** If someone deletes the directory right before FixupFileInfo is called, the function will fail, and we'll return a ** partially-constructed directory tree. This is not expected to happen (and the way we use these functions, only an ** Administrator can do this), so it's not worth the effort to retry on failure */ if (FixupFileInfo(pwszTempPath, lpSecurityAttributes, dwExtraAttributes) == FALSE) { free(pwszTempPath);
return FALSE; } } } }
/*
** Restore the slash. */ *pwszSlash = DIR_SEP_CHAR;
/*
** Now at first char of the next level directory, let's search ** for first slash after the directory. */ while ((*pwsz != L'\0') && (*pwsz != DIR_SEP_CHAR)) { ++pwsz; } }
free (pwszTempPath);
pwszTempPath = NULL;
/*
** Now make the last directory. */ dwObjAttribs = GetFileAttributesW (pwszPathName);
if ((dwObjAttribs == 0xFFFFffff) || ((dwObjAttribs & FILE_ATTRIBUTE_DIRECTORY) == 0)) { bRet = CreateDirectoryW (pwszPathName, lpSecurityAttributes);
if (bRet) { SetFileAttributesW (pwszPathName, dwExtraAttributes); } else { // someone sneakily created the directory here
dwObjAttribs = GetFileAttributesW (pwszPathName); if ((dwObjAttribs != 0xFFFFFFFF) && ((dwObjAttribs & FILE_ATTRIBUTE_DIRECTORY) != 0)) { if (FixupFileInfo(pwszPathName, lpSecurityAttributes, dwExtraAttributes) == FALSE) return FALSE; } else return FALSE; } } else if ( (dwObjAttribs != 0xFFFFFFFF) && ((dwObjAttribs & FILE_ATTRIBUTE_DIRECTORY) != 0)) // or here
{ if (FixupFileInfo(pwszPathName, lpSecurityAttributes, dwExtraAttributes) == FALSE) return FALSE; }
SetLastError (dwRetPreserve); // Set back old last error code
return TRUE; }
/*
**++ ** ** Routine Description: ** ** Changes the attributes and security information ** for a file or a directory. ** ** Arguments: ** ** pwszPathName The directory path to change ** lpSecurityAttributes The new security information for the directory ** dwExtraAttributes The new attributes for the directory ** ** ** Side Effects: ** ** None ** ** ** Return Value: ** ** TRUE -- Success FALSE -- Failure. GetLastError will contain the appropriate error code except ** for the case where we tried this on something that isn't a directory ** **-- */
BOOL FixupFileInfo( IN LPCWSTR pwszPathName, IN LPSECURITY_ATTRIBUTES lpSecurityAttributes, IN DWORD dwExtraAttributes) { CVssFunctionTracer ft(VSSDBG_WRTCMN, L"FixupFileInfo");
// try and push down the required attributes
if (SetFileAttributes(pwszPathName, dwExtraAttributes) == FALSE) { ft.hr = HRESULT_FROM_WIN32(GetLastError()); return FALSE; }
// gather security information from the security descriptor
PSID pOwner = NULL; PSID pGroup = NULL; PACL pDacl = NULL; PACL pSacl = NULL; BOOL ownerDefaulted, groupDefaulted, DaclDefaulted, SaclDefaulted, DaclPresent, SaclPresent; SECURITY_DESCRIPTOR_CONTROL control; DWORD revision = 0; if ((GetSecurityDescriptorOwner(lpSecurityAttributes->lpSecurityDescriptor, &pOwner, &ownerDefaulted) == FALSE) || (GetSecurityDescriptorGroup(lpSecurityAttributes->lpSecurityDescriptor, &pGroup, &groupDefaulted) == FALSE) || (GetSecurityDescriptorDacl(lpSecurityAttributes->lpSecurityDescriptor, &DaclPresent, &pDacl, &DaclDefaulted) == FALSE) || (GetSecurityDescriptorSacl(lpSecurityAttributes->lpSecurityDescriptor, &SaclPresent, &pSacl, &SaclDefaulted) == FALSE) || (GetSecurityDescriptorControl(lpSecurityAttributes->lpSecurityDescriptor, &control, &revision) == FALSE)) { ft.hr = HRESULT_FROM_WIN32(GetLastError()); return FALSE; } SECURITY_INFORMATION securityInformation; securityInformation = ((pDacl != NULL) ? DACL_SECURITY_INFORMATION : 0) | ((pDacl != NULL && ((control & SE_DACL_PROTECTED) != 0)) ? PROTECTED_DACL_SECURITY_INFORMATION : 0) | ((pSacl != NULL) ? SACL_SECURITY_INFORMATION : 0) |
((pSacl != NULL && ((control & SE_SACL_PROTECTED) != 0)) ? PROTECTED_SACL_SECURITY_INFORMATION : 0) |
((pOwner != NULL) ? OWNER_SECURITY_INFORMATION : 0) | ((pGroup != NULL) ? GROUP_SECURITY_INFORMATION : 0);
// push down the new security information to the file
ft.hr = SetNamedSecurityInfo(const_cast<LPWSTR>(pwszPathName), SE_FILE_OBJECT, securityInformation, pOwner, pGroup, pDacl, pSacl);
return ft.HrSucceeded(); }
/*
** The next set of rountes are used to change the state of SCM ** controlled services, typically between RUNNING and either PAUSED or ** STOPPED. ** ** The initial collection are for manipulating the states, control ** codes and getting the string equivalents to be used for tracing ** purposes. ** ** The major routines is VsServiceChangeState(). This is called ** specifying the reuiqred state for the service and after some ** validation, it makes the appropriate request of the SCM and calls ** WaitForServiceToEnterState() to wait until the services reaches the ** desired state, or it times out. */
static PWCHAR const GetStringFromStateCode (DWORD dwState) { PWCHAR pwszReturnedString = NULL;
switch (dwState) { case 0: pwszReturnedString = L"UnSpecified"; break; case SERVICE_STOPPED: pwszReturnedString = L"Stopped"; break; case SERVICE_START_PENDING: pwszReturnedString = L"StartPending"; break; case SERVICE_STOP_PENDING: pwszReturnedString = L"StopPending"; break; case SERVICE_RUNNING: pwszReturnedString = L"Running"; break; case SERVICE_CONTINUE_PENDING: pwszReturnedString = L"ContinuePending"; break; case SERVICE_PAUSE_PENDING: pwszReturnedString = L"PausePending"; break; case SERVICE_PAUSED: pwszReturnedString = L"Paused"; break; default: pwszReturnedString = L"UNKKNOWN STATE"; break; }
return (pwszReturnedString); } /* GetStringFromStateCode () */
static DWORD const GetControlCodeFromTargetState (const DWORD dwTargetState) { DWORD dwServiceControlCode;
switch (dwTargetState) { case SERVICE_STOPPED: dwServiceControlCode = SERVICE_CONTROL_STOP; break; case SERVICE_PAUSED: dwServiceControlCode = SERVICE_CONTROL_PAUSE; break; case SERVICE_RUNNING: dwServiceControlCode = SERVICE_CONTROL_CONTINUE; break; default: dwServiceControlCode = 0; break; }
return (dwServiceControlCode); } /* GetControlCodeFromTargetState () */
static DWORD const GetNormalisedState (DWORD dwCurrentState) { DWORD dwNormalisedState;
switch (dwCurrentState) { case SERVICE_STOPPED: case SERVICE_STOP_PENDING: dwNormalisedState = SERVICE_STOPPED; break;
case SERVICE_START_PENDING: case SERVICE_CONTINUE_PENDING: case SERVICE_RUNNING: dwNormalisedState = SERVICE_RUNNING; break;
case SERVICE_PAUSED: case SERVICE_PAUSE_PENDING: dwNormalisedState = SERVICE_PAUSED; break;
default: dwNormalisedState = 0; break; }
return (dwNormalisedState); } /* GetNormalisedState () */
/*
**++ ** ** Routine Description: ** ** Wait for the specified service to enter the specified ** state. The routine polls the serivce for it's current state ** every dwServiceStatePollingIntervalInMilliSeconds milliseconds ** to see if the service has reached the desired state. If the ** repeated delay eventually reaches the timeout period the ** routine stops polling and returns a failure status. ** ** NOTE: since this routine just sleeps between service state ** interrogations, it effectively stalls from the point of view ** of the caller. ** ** ** Arguments: ** ** shService handle to the service being manipulated ** dwMaxDelayInMilliSeconds timeout period ** dwDesiredState state to move the service into ** ** ** Side Effects: ** ** ** Return Value: ** ** HRESULT for ERROR_TIMOUT if the service did not reach the required state in the required time ** **-- */
static HRESULT WaitForServiceToEnterState (SC_HANDLE shService, DWORD dwMaxDelayInMilliSeconds, const DWORD dwDesiredState) { CVssFunctionTracer ft (VSSDBG_WRTCMN, L"WaitForServiceToEnterState");
DWORD dwRemainingDelay = dwMaxDelayInMilliSeconds; DWORD dwInitialState; const DWORD dwServiceStatePollingIntervalInMilliSeconds = 100; BOOL bSucceeded; SERVICE_STATUS sSStat;
try { bSucceeded = QueryServiceStatus (shService, &sSStat);
ft.hr = GET_STATUS_FROM_BOOL (bSucceeded);
dwInitialState = sSStat.dwCurrentState;
ft.Trace (VSSDBG_WRTCMN, L"Initial QueryServiceStatus returned: 0x%08X with current state '%s' and desired state '%s'", ft.hr, GetStringFromStateCode (dwInitialState), GetStringFromStateCode (dwDesiredState));
while ((dwDesiredState != sSStat.dwCurrentState) && (dwRemainingDelay > 0)) { Sleep (UMIN (dwServiceStatePollingIntervalInMilliSeconds, dwRemainingDelay));
dwRemainingDelay -= (UMIN (dwServiceStatePollingIntervalInMilliSeconds, dwRemainingDelay));
if (0 == dwRemainingDelay) { ft.Throw (VSSDBG_WRTCMN, HRESULT_FROM_WIN32 (ERROR_TIMEOUT), L"Exceeded maximum delay (%dms)", dwMaxDelayInMilliSeconds); }
bSucceeded = QueryServiceStatus (shService, &sSStat);
ft.ThrowIf (!bSucceeded, VSSDBG_WRTCMN, GET_STATUS_FROM_BOOL (bSucceeded), L"QueryServiceStatus shows '%s' as current state", GetStringFromStateCode (sSStat.dwCurrentState)); }
ft.Trace (VSSDBG_WRTCMN, L"Service state change from '%s' to '%s' took %u milliseconds", GetStringFromStateCode (dwInitialState), GetStringFromStateCode (sSStat.dwCurrentState), dwMaxDelayInMilliSeconds - dwRemainingDelay); } VSS_STANDARD_CATCH (ft);
return (ft.hr); } /* WaitForServiceToEnterState () */
/*
**++ ** ** Routine Description: ** ** Changes the state of a service if appropriate. ** ** ** Arguments: ** ** pwszServiceName The real service name, i.e. cisvc ** dwRequestedState the state code for the state we wish to enter ** pdwReturnedOldState pointer to location to receive current service state. ** Can be NULL of current state not required ** pbReturnedStateChanged pointer to location to receive flag indicating if ** service changed state. Pointer can be NULL if flag ** value not required. ** ** ** Return Value: ** ** Any HRESULT resulting from faiure communication with the ** SCM (Service Control Manager). ** **-- */
HRESULT VsServiceChangeState (LPCWSTR pwszServiceName, DWORD dwRequestedState, PDWORD pdwReturnedOldState, PBOOL pbReturnedStateChanged) { CVssFunctionTracer ft (VSSDBG_WRTCMN, L"VsServiceChangeState");
SC_HANDLE shSCManager = NULL; SC_HANDLE shSCService = NULL; DWORD dwOldState = 0; BOOL bSucceeded; SERVICE_STATUS sSStat; const DWORD dwNormalisedRequestedState = GetNormalisedState (dwRequestedState);
ft.Trace (VSSDBG_WRTCMN, L"Service '%s' requested to change to state '%s' (normalised to '%s')", pwszServiceName, GetStringFromStateCode (dwRequestedState), GetStringFromStateCode (dwNormalisedRequestedState));
RETURN_VALUE_IF_REQUIRED (pbReturnedStateChanged, FALSE);
try { /*
** Connect to the local service control manager */ shSCManager = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
ft.hr = GET_STATUS_FROM_HANDLE (shSCManager);
ft.ThrowIf (ft.HrFailed (), VSSDBG_WRTCMN, ft.hr, L"Called OpenSCManager()");
/*
** Get a handle to the service */ shSCService = OpenService (shSCManager, pwszServiceName, SERVICE_ALL_ACCESS);
ft.hr = GET_STATUS_FROM_HANDLE (shSCService);
/*
** If it's an invalid name or the service doesn't exist then ** fail gracefully. For all other failures do the normal ** thing. Oh yes, if on the off-chance we should happen to ** succeed, carry on. */ if ((HRESULT_FROM_WIN32 (ERROR_INVALID_NAME) == ft.hr) || (HRESULT_FROM_WIN32 (ERROR_SERVICE_DOES_NOT_EXIST) == ft.hr)) { ft.Trace (VSSDBG_WRTCMN, L"'%s' service not found", pwszServiceName); }
else if (ft.HrFailed ()) { /*
** See if the service doesn't exist */ ft.Throw (VSSDBG_WRTCMN, E_FAIL, L"ERROR - OpenService() returned: %d", ft.hr); }
else { /*
** Now query the service to see what state it is in at the moment. */ bSucceeded = QueryServiceStatus (shSCService, &sSStat);
ft.ThrowIf (!bSucceeded, VSSDBG_WRTCMN, GET_STATUS_FROM_BOOL (bSucceeded), L"QueryServiceStatus shows '%s' as current state", GetStringFromStateCode (sSStat.dwCurrentState));
dwOldState = sSStat.dwCurrentState;
/*
** Now we decide what to do. ** If we are already in the requested state, we do nothing. ** If we are stopped and are requested to pause, we do nothing ** otherwise we make the attempt to change state. */ if (dwNormalisedRequestedState == dwOldState) { /*
** We are already in the requested state, so do ** nothing. We should even tell folk of that. We're ** proud to be doing nothing. */ ft.Trace (VSSDBG_WRTCMN, L"'%s' service is already in requested state: doing nothing", pwszServiceName);
RETURN_VALUE_IF_REQUIRED (pdwReturnedOldState, dwOldState); }
else if ((SERVICE_STOPPED == sSStat.dwCurrentState) && (SERVICE_PAUSED == dwNormalisedRequestedState)) { /*
** Do nothing. Just log the fact and move on. */ ft.Trace (VSSDBG_WRTCMN, L"Asked to PAUSE the '%s' service which is already STOPPED", pwszServiceName);
RETURN_VALUE_IF_REQUIRED (pdwReturnedOldState, dwOldState); }
else { /*
** We want a state which is different from the one ** we're in at the moment. Generally this just means ** calling ControlService() asking for the new state ** except if the service is currently stopped. If ** that's so, then we call StartService() */ if (SERVICE_STOPPED == sSStat.dwCurrentState) { /*
** Call StartService to get the ball rolling */ bSucceeded = StartService (shSCService, 0, NULL); }
else { bSucceeded = ControlService (shSCService, GetControlCodeFromTargetState (dwNormalisedRequestedState), &sSStat); }
ft.ThrowIf (!bSucceeded, VSSDBG_WRTCMN, GET_STATUS_FROM_BOOL (bSucceeded), (SERVICE_STOPPED == sSStat.dwCurrentState) ? L"StartService attempting '%s' to '%s', now at '%s'" : L"ControlService attempting '%s' to '%s', now at '%s'", GetStringFromStateCode (dwOldState), GetStringFromStateCode (dwNormalisedRequestedState), GetStringFromStateCode (sSStat.dwCurrentState));
RETURN_VALUE_IF_REQUIRED (pdwReturnedOldState, dwOldState); RETURN_VALUE_IF_REQUIRED (pbReturnedStateChanged, TRUE);
ft.hr = WaitForServiceToEnterState (shSCService, 15000, dwNormalisedRequestedState);
if (ft.HrFailed ()) { ft.Throw (VSSDBG_WRTCMN, ft.hr, L"WaitForServiceToEnterState() failed with 0x%08X", ft.hr); }
} } } VSS_STANDARD_CATCH (ft);
/*
** Now close the service and service control manager handles */ if (NULL != shSCService) CloseServiceHandle (shSCService); if (NULL != shSCManager) CloseServiceHandle (shSCManager);
return (ft.hr); } /* VsServiceChangeState () */
/*
**++ ** ** Routine Description: ** ** Deletes all the sub-directories and files in the specified ** directory and then deletes the directory itself. ** ** If the directory does not exist then simply return S_OK. ** ** Arguments: ** ** pucsDirectoryPath The directory path to clear out ** ** ** Side Effects: ** ** None ** ** ** Return Value: ** ** Out of memory or any HRESULT from ** ** RemoveDirectory() ** DeleteFile() ** FindFirstFile() ** **-- */
HRESULT RemoveDirectoryTree ( IN LPCWSTR pwszDirectoryPath ) { HANDLE hFileScan = INVALID_HANDLE_VALUE; DWORD dwSubDirectoriesEntered = 0; INT iCurrentPathCursor = 0; BOOL bSucceeded; WIN32_FIND_DATAW FileFindData; CBsString cwsCurrentPath;
CVssFunctionTracer ft(VSSDBG_WRTCMN, L"RemoveDirectoryTree");
try { ft.Trace( VSSDBG_WRTCMN, L"Recursive delete of the '%s' directory", pwszDirectoryPath ); cwsCurrentPath = pwszDirectoryPath; iCurrentPathCursor = cwsCurrentPath.ReverseFind( DIR_SEP_CHAR ) + 1;
while ( 1 ) { if ( HandleInvalid ( hFileScan ) ) { /*
** No valid scan handle so start a new scan */ ft.Trace( VSSDBG_WRTCMN, L"FindFirstFileW( %s, ... )", cwsCurrentPath.c_str() ); hFileScan = FindFirstFileW( cwsCurrentPath, &FileFindData); if ( ( hFileScan == INVALID_HANDLE_VALUE ) && ( ( ::GetLastError() == ERROR_FILE_NOT_FOUND ) || ( ::GetLastError() == ERROR_PATH_NOT_FOUND ) ) ) { // Directory is empty or does not exists
ft.hr = S_OK; break; } ft.hr = GET_STATUS_FROM_HANDLE( hFileScan ); ft.CheckForError(VSSDBG_WRTCMN, L"RemoveDirectoryTree - FindFirstFileW");
cwsCurrentPath.GetBufferSetLength( iCurrentPathCursor ); cwsCurrentPath += FileFindData.cFileName; } else { /*
** Continue with the existing scan */ bSucceeded = FindNextFileW( hFileScan, &FileFindData ); ft.hr = GET_STATUS_FROM_BOOL( bSucceeded );
if ( HRESULT_FROM_WIN32( ERROR_NO_MORE_FILES ) == ft.hr ) { FindClose( hFileScan ); hFileScan = INVALID_HANDLE_VALUE;
if ( dwSubDirectoriesEntered > 0 ) { /*
** This is a scan of a sub-directory that is now ** complete so delete the sub-directory itself. */ cwsCurrentPath.GetBufferSetLength( iCurrentPathCursor - 1 ); ft.Trace( VSSDBG_WRTCMN, L"RemoveDirectoryW( %s, ... )", cwsCurrentPath.c_str() );
bSucceeded = RemoveDirectoryW( cwsCurrentPath ); ft.hr = GET_STATUS_FROM_BOOL( bSucceeded ); ft.CheckForError(VSSDBG_WRTCMN, L"RemoveDirectoryTree - RemoveDirectoryW");
dwSubDirectoriesEntered--; }
if ( 0 == dwSubDirectoriesEntered ) { /*
** We are back to where we started except that the ** requested directory is now gone. Time to leave. */ ft.hr = S_OK; break; } else { /*
** Move back up one directory level, reset the cursor ** and prepare the path buffer to begin a new scan. */ iCurrentPathCursor = cwsCurrentPath.ReverseFind( DIR_SEP_CHAR ) + 1; cwsCurrentPath.GetBufferSetLength( iCurrentPathCursor); cwsCurrentPath += "\\*"; }
/*
** No files to be processed on this pass so go back and try to ** find another or leave the loop as we've finished the task. */ continue; }
ft.CheckForError( VSSDBG_WRTCMN, L"RemoveDirectoryTree - FindNextFileW" ); cwsCurrentPath.GetBufferSetLength( iCurrentPathCursor ); cwsCurrentPath += FileFindData.cFileName; }
if (FileFindData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { SetFileAttributesW (cwsCurrentPath, FileFindData.dwFileAttributes ^ (FILE_ATTRIBUTE_READONLY)); }
if ( !NameIsDotOrDotDot( FileFindData.cFileName ) ) { if ( ( FileFindData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ) || !( FileFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ) { ft.Trace( VSSDBG_WRTCMN, L"DeleteFileW( %s, ... )", cwsCurrentPath.c_str() ); bSucceeded = DeleteFileW( cwsCurrentPath ); ft.hr = GET_STATUS_FROM_BOOL( bSucceeded ); ft.CheckForError( VSSDBG_WRTCMN, L"RemoveDirectoryTree - DeleteFileW" ); } else { ft.Trace( VSSDBG_WRTCMN, L"RemoveDirectoryW( %s, ... )", cwsCurrentPath.c_str() ); bSucceeded = RemoveDirectoryW( cwsCurrentPath ); ft.hr = GET_STATUS_FROM_BOOL( bSucceeded );
if ( HRESULT_FROM_WIN32( ERROR_DIR_NOT_EMPTY ) == ft.hr ) { ft.Trace( VSSDBG_WRTCMN, L"Dir not empty after calling RemoveDirectoryW( %s, ... )", cwsCurrentPath.c_str() ); /*
** The directory wasn't empty so move down one level, ** close the old scan and start a new one. */ FindClose (hFileScan); hFileScan = INVALID_HANDLE_VALUE; cwsCurrentPath += DIR_SEP_STRING L"*"; iCurrentPathCursor = cwsCurrentPath.GetLength() - 1; dwSubDirectoriesEntered++; } else { ft.CheckForError( VSSDBG_WRTCMN, L"RemoveDirectoryTree - RemoveDirectoryW" ); } } } } } VSS_STANDARD_CATCH(ft)
if (!HandleInvalid (hFileScan)) FindClose (hFileScan);
return( ft.hr ); } /* RemoveDirectoryTree () */
/*
**++ ** ** Routine Description: ** ** Routines to construct and cleanup a security descriptor which ** can be applied to limit access to an object to member of ** either the Administrators or Backup Operators group. ** ** ** Arguments: ** ** psaSecurityAttributes Pointer to a SecurityAttributes ** structure which has already been ** setup to point to a blank ** security descriptor ** ** eSaType What we are building the SA for ** ** bIncludeBackupOperator Whether or not to include an ACE to ** grant BackupOperator access ** ** ** Return Value: ** ** Any HRESULT from ** InitializeSecurityDescriptor() ** AllocateAndInitializeSid() ** SetEntriesInAcl() ** SetSecurityDescriptorDacl() ** **-- */
static HRESULT ConstructSecurityAttributes ( IN OUT PSECURITY_ATTRIBUTES psaSecurityAttributes, IN BOOL bIncludeBackupOperator ) { DWORD dwStatus; DWORD dwAccessMask = 0; BOOL bSucceeded; PSID psidBackupOperators = NULL; PSID psidAdministrators = NULL; PACL paclDiscretionaryAcl = NULL; SID_IDENTIFIER_AUTHORITY sidNtAuthority = SECURITY_NT_AUTHORITY; EXPLICIT_ACCESS eaExplicitAccess [2];
CVssFunctionTracer ft(VSSDBG_WRTCMN, L"ConstructSecurityAttributes");
try { dwAccessMask = FILE_ALL_ACCESS;
/*
** Initialise the security descriptor. */ bSucceeded = ::InitializeSecurityDescriptor ( psaSecurityAttributes->lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION );
ft.hr = GET_STATUS_FROM_BOOL( bSucceeded ); ft.CheckForError( VSSDBG_WRTCMN, L"InitializeSecurityDescriptor" );
if ( bIncludeBackupOperator ) { /*
** Create a SID for the Backup Operators group. */ bSucceeded = ::AllocateAndInitializeSid ( &sidNtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_BACKUP_OPS, 0, 0, 0, 0, 0, 0, &psidBackupOperators );
ft.hr = GET_STATUS_FROM_BOOL ( bSucceeded ); ft.CheckForError( VSSDBG_WRTCMN, L"AllocateAndInitializeSid" ); }
/*
** Create a SID for the Administrators group. */ bSucceeded = ::AllocateAndInitializeSid ( &sidNtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidAdministrators);
ft.hr = GET_STATUS_FROM_BOOL (bSucceeded); ft.CheckForError( VSSDBG_WRTCMN, L"AllocateAndInitializeSid" );
/*
** Initialize the array of EXPLICIT_ACCESS structures for an ** ACEs we are setting. ** ** The first ACE allows the Backup Operators group full access ** and the second, allowa the Administrators group full ** access. */ eaExplicitAccess[0].grfAccessPermissions = dwAccessMask; eaExplicitAccess[0].grfAccessMode = SET_ACCESS; eaExplicitAccess[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; eaExplicitAccess[0].Trustee.pMultipleTrustee = NULL; eaExplicitAccess[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; eaExplicitAccess[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; eaExplicitAccess[0].Trustee.TrusteeType = TRUSTEE_IS_ALIAS; eaExplicitAccess[0].Trustee.ptstrName = (LPTSTR) psidAdministrators;
if ( bIncludeBackupOperator ) { eaExplicitAccess[1].grfAccessPermissions = dwAccessMask; eaExplicitAccess[1].grfAccessMode = SET_ACCESS; eaExplicitAccess[1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; eaExplicitAccess[1].Trustee.pMultipleTrustee = NULL; eaExplicitAccess[1].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; eaExplicitAccess[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; eaExplicitAccess[1].Trustee.TrusteeType = TRUSTEE_IS_ALIAS; eaExplicitAccess[1].Trustee.ptstrName = (LPTSTR) psidBackupOperators; }
/*
** Create a new ACL that contains the new ACEs. */ dwStatus = ::SetEntriesInAcl( bIncludeBackupOperator ? 2 : 1, eaExplicitAccess, NULL, &paclDiscretionaryAcl );
ft.hr = HRESULT_FROM_WIN32 (dwStatus); ft.CheckForError( VSSDBG_WRTCMN, L"SetEntriesInAcl" ); /*
** Add the ACL to the security descriptor. */ bSucceeded = ::SetSecurityDescriptorDacl ( psaSecurityAttributes->lpSecurityDescriptor, TRUE, paclDiscretionaryAcl, FALSE );
ft.hr = GET_STATUS_FROM_BOOL (bSucceeded); ft.CheckForError( VSSDBG_WRTCMN, L"SetSecurityDescriptorDacl" );
paclDiscretionaryAcl = NULL;
bSucceeded = ::SetSecurityDescriptorControl ( psaSecurityAttributes->lpSecurityDescriptor, SE_DACL_PROTECTED, SE_DACL_PROTECTED); ft.hr = GET_STATUS_FROM_BOOL (bSucceeded); ft.CheckForError( VSSDBG_WRTCMN, L"SetSecurityDescriptorControl" ); } VSS_STANDARD_CATCH( ft );
/*
** Clean up any left over junk. */ if ( NULL != psidAdministrators ) FreeSid ( psidAdministrators ); if ( NULL != psidBackupOperators ) FreeSid ( psidBackupOperators ); if ( NULL != paclDiscretionaryAcl ) LocalFree (paclDiscretionaryAcl ); return ( ft.hr ); } /* ConstructSecurityAttributes () */
static VOID CleanupSecurityAttributes( IN PSECURITY_ATTRIBUTES psaSecurityAttributes ) { CVssFunctionTracer ft(VSSDBG_WRTCMN, L"CleanupSecurityAttributes");
BOOL bSucceeded; BOOL bDaclPresent = FALSE; BOOL bDaclDefaulted = TRUE; PACL paclDiscretionaryAcl = NULL;
try { bSucceeded = ::GetSecurityDescriptorDacl( psaSecurityAttributes->lpSecurityDescriptor, &bDaclPresent, &paclDiscretionaryAcl, &bDaclDefaulted );
if ( bSucceeded && bDaclPresent && !bDaclDefaulted && ( NULL != paclDiscretionaryAcl ) ) { LocalFree( paclDiscretionaryAcl ); } } VSS_STANDARD_CATCH( ft ); } /* CleanupSecurityAttributes () */
/*
**++ ** ** Routine Description: ** ** Creates a new target directory specified by the target path ** member variable if not NULL. It will create any necessary ** parent directories too. ** ** NOTE: already exists type errors are ignored. ** ** ** Arguments: ** ** pwszTargetPath directory to create and apply security attributes ** ** ** Return Value: ** ** Any HRESULT resulting from memory allocation or directory creation attempts. **-- */
HRESULT CreateTargetPath( IN LPCWSTR pwszTargetPath ) { CVssFunctionTracer ft(VSSDBG_WRTCMN, L"CreateTargetPath");
ACL DiscretionaryAcl; SECURITY_ATTRIBUTES saSecurityAttributes; SECURITY_DESCRIPTOR sdSecurityDescriptor; BOOL bSucceeded; DWORD dwFileAttributes = 0; const DWORD dwExtraAttributes = FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
try { if ( NULL != pwszTargetPath ) { /*
** We really want a no access acl on this directory but ** because of various problems with the EventLog and ** ConfigDir writers we will settle for admin or backup ** operator access only. The only possible accessor is ** Backup which is supposed to have the SE_BACKUP_NAME ** priv which will effectively bypass the ACL. No one else ** needs to see this stuff. */ saSecurityAttributes.nLength = sizeof (saSecurityAttributes); saSecurityAttributes.lpSecurityDescriptor = &sdSecurityDescriptor; saSecurityAttributes.bInheritHandle = FALSE;
ft.hr = ::ConstructSecurityAttributes( &saSecurityAttributes, FALSE ); ft.CheckForError( VSSDBG_WRTCMN, L"ConstructSecurityAttributes" );
CBsString expandedTarget; ft.hr = GET_STATUS_FROM_BOOL(expandedTarget.ExpandEnvironmentStrings(pwszTargetPath)); ft.CheckForError(VSSDBG_WRTCMN, L"ExpandEnvironmentStrings"); bSucceeded = ::VsCreateDirectories ( expandedTarget, &saSecurityAttributes, dwExtraAttributes );
ft.hr = GET_STATUS_FROM_BOOL( bSucceeded ); if ( ft.hr == HRESULT_FROM_WIN32( ERROR_ALREADY_EXISTS ) ) { ft.hr = S_OK; }
::CleanupSecurityAttributes( &saSecurityAttributes ); ft.CheckForError( VSSDBG_WRTCMN, L"VsCreateDirectories" ); } } VSS_STANDARD_CATCH( ft ); return( ft.hr ); } /* CreateTargetPath () */
/*
**++ ** ** Routine Description: ** ** Deletes all the files present in the directory pointed at by the target ** path member variable if not NULL. It will also remove the target directory ** itself, eg for a target path of c:\dir1\dir2 all files under dir2 will be ** removed and then dir2 itself will be deleted. ** ** ** Arguments: ** ** pwszTargetPath ** ** ** Return Value: ** ** Any HRESULT resulting from memory allocation or file and ** directory deletion attempts. **-- */
HRESULT CleanupTargetPath (LPCWSTR pwszTargetPath) { CVssFunctionTracer ft(VSSDBG_WRTCMN, L"CleanupTargetPath"); HRESULT hrStatus = NOERROR; DWORD dwFileAttributes = 0; BOOL bSucceeded; WCHAR wszTempBuffer [50]; UNICODE_STRING ucsTargetPath; UNICODE_STRING ucsTargetPathAlternateName;
StringInitialise (&ucsTargetPath); StringInitialise (&ucsTargetPathAlternateName);
if (NULL != pwszTargetPath) { hrStatus = StringCreateFromExpandedString (&ucsTargetPath, pwszTargetPath, MAX_PATH);
if (SUCCEEDED (hrStatus)) { hrStatus = StringCreateFromString (&ucsTargetPathAlternateName, &ucsTargetPath, MAX_PATH); }
if (SUCCEEDED (hrStatus)) { dwFileAttributes = GetFileAttributesW (ucsTargetPath.Buffer);
hrStatus = GET_STATUS_FROM_BOOL ( -1 != dwFileAttributes);
if ((HRESULT_FROM_WIN32 (ERROR_FILE_NOT_FOUND) == hrStatus) || (HRESULT_FROM_WIN32 (ERROR_PATH_NOT_FOUND) == hrStatus)) { hrStatus = NOERROR; dwFileAttributes = 0; }
else if (SUCCEEDED (hrStatus)) { /*
** If there is a file there then blow it away, or if it's ** a directory, blow it and all it's contents away. This ** is our directory and no one but us gets to play there. */ hrStatus = RemoveDirectoryTree (CBsString(&ucsTargetPath));
if (FAILED (hrStatus)) { srand ((unsigned) time (NULL));
_itow (rand (), wszTempBuffer, 16);
StringAppendString (&ucsTargetPathAlternateName, wszTempBuffer);
bSucceeded = MoveFileW (ucsTargetPath.Buffer, ucsTargetPathAlternateName.Buffer);
if (bSucceeded) { BsDebugTraceAlways (0, DEBUG_TRACE_VSSAPI, (L"VSSAPI::CleanupTargetPath: " L"FAILED to delete %s with status 0x%08X so renamed to %s", ucsTargetPath.Buffer, hrStatus, ucsTargetPathAlternateName.Buffer)); } else { BsDebugTraceAlways (0, DEBUG_TRACE_VSSAPI, (L"VSSAPI::CleanupTargetPath: " L"FAILED to delete %s with status 0x%08X and " L"FAILED to rename to %s with status 0x%08X", ucsTargetPath.Buffer, hrStatus, ucsTargetPathAlternateName.Buffer, GET_STATUS_FROM_BOOL (bSucceeded))); } } } } }
StringFree (&ucsTargetPathAlternateName); StringFree (&ucsTargetPath);
return (hrStatus); } /* CleanupTargetPath () */
/*
**++ ** ** Routine Description: ** ** Moves the contents of the source directory to the target directory. ** ** Arguments: ** ** pwszSourceDirectoryPath Source directory for the files to be moved ** pwszTargetDirectoryPath Target directory for the files to be moved ** ** ** Side Effects: ** ** An intermediate error can leave directory in a partial moved ** state where some of the files have been moved but not all. ** ** ** Return Value: ** ** Any HRESULT from FindFirstFile() etc or from MoveFileEx() ** ** Remarks - copied from the wrtrshim\src\common.cpp file and ** switched to using CBsStrings. **-- */
HRESULT MoveFilesInDirectory ( IN CBsString cwsSourceDirectoryPath, IN CBsString cwsTargetDirectoryPath ) { CVssFunctionTracer ft( VSSDBG_WRTCMN, L"MoveFilesInDirectory" );
HANDLE hFileScan = INVALID_HANDLE_VALUE;
try { WIN32_FIND_DATA sFileInformation; if ( cwsSourceDirectoryPath.Tail() != DIR_SEP_CHAR ) cwsSourceDirectoryPath += DIR_SEP_CHAR; if ( cwsTargetDirectoryPath.Tail() != DIR_SEP_CHAR ) cwsTargetDirectoryPath += DIR_SEP_CHAR;
hFileScan = ::FindFirstFileW ( cwsSourceDirectoryPath + L'*', &sFileInformation );
ft.hr = GET_STATUS_FROM_BOOL( INVALID_HANDLE_VALUE != hFileScan ); ft.CheckForError( VSSDBG_WRTCMN, L"FindFirstFileW" );
BOOL bMoreFiles; do { if ( !NameIsDotOrDotDot( sFileInformation.cFileName ) ) { BOOL bSucceeded; CBsString cwsSourceFile = cwsSourceDirectoryPath + sFileInformation.cFileName; CBsString cwsTargetFile = cwsTargetDirectoryPath + sFileInformation.cFileName;
ft.Trace( VSSDBG_WRTCMN, L"Moving '%s' to '%s'", cwsSourceFile.c_str(), cwsTargetFile.c_str() ); bSucceeded = ::MoveFileExW( cwsSourceDirectoryPath + sFileInformation.cFileName, cwsTargetDirectoryPath + sFileInformation.cFileName, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING );
ft.hr = GET_STATUS_FROM_BOOL( bSucceeded ); ft.CheckForError( VSSDBG_WRTCMN, L"MoveFileExW" ); }
bMoreFiles = ::FindNextFileW( hFileScan, &sFileInformation ); } while ( bMoreFiles );
/*
** If the last move operation was successful determine the ** reason for terminating the scan. No need to report an ** error if all that happened was that we have finished ** what we were asked to do. */ ft.hr = GET_STATUS_FROM_FILESCAN( bMoreFiles ); ft.CheckForError( VSSDBG_WRTCMN, L"FindNextFileW" ); } VSS_STANDARD_CATCH( ft );
if ( hFileScan != INVALID_HANDLE_VALUE ) ::FindClose( hFileScan ); return( ft.hr ); }
/*
**++ ** ** Routine Description: ** ** Checks a path against an array of pointers to volume names to ** see if path is affected by any of the volumes in the array ** ** ** Arguments: ** ** pwszPath Path to be checked ** ulVolumeCount Number of volumes in volume array ** ppwszVolumeNamesArray address of the array ** pbReturnedFoundInVolumeArray pointer to a location to store the ** result of the check ** ** ** Side Effects: ** ** None ** ** ** Return Value: ** ** Any HRESULT from:- ** GetVolumePathNameW() ** GetVolumeNameForVolumeMountPoint() ** **-- */
HRESULT IsPathInVolumeArray (IN LPCWSTR pwszPath, IN const ULONG ulVolumeCount, IN LPCWSTR *ppwszVolumeNamesArray, OUT PBOOL pbReturnedFoundInVolumeArray) { CVssFunctionTracer ft(VSSDBG_WRTCMN, L"IsPathInVolumeArray"); HRESULT hrStatus = NOERROR; BOOL bFound = FALSE; BOOL bContinue = TRUE; ULONG ulIndex; WCHAR wszVolumeName [MAX_VOLUMENAME_LENGTH]; UNICODE_STRING ucsVolumeMountPoint;
StringInitialise (&ucsVolumeMountPoint);
if ((0 == ulVolumeCount) || (NULL == pbReturnedFoundInVolumeArray)) { BS_ASSERT (false);
bContinue = FALSE; }
if (bContinue) { /*
** We need a string that is at least as big as the supplied ** path. */ hrStatus = StringAllocate (&ucsVolumeMountPoint, wcslen (pwszPath) * sizeof (WCHAR));
bContinue = SUCCEEDED (hrStatus); }
if (bContinue) { /*
** Get the volume mount point */ bContinue = GetVolumePathNameW (pwszPath, ucsVolumeMountPoint.Buffer, ucsVolumeMountPoint.MaximumLength / sizeof (WCHAR));
hrStatus = GET_STATUS_FROM_BOOL (bContinue); }
if (bContinue) { /*
** Get the volume name */ bContinue = GetVolumeNameForVolumeMountPointW (ucsVolumeMountPoint.Buffer, wszVolumeName, SIZEOF_ARRAY (wszVolumeName));
hrStatus = GET_STATUS_FROM_BOOL (bContinue); }
if (bContinue) { /*
** Search to see if that volume is within snapshotted volumes */ for (ulIndex = 0; !bFound && (ulIndex < ulVolumeCount); ulIndex++) { BS_ASSERT (NULL != ppwszVolumeNamesArray [ulIndex]);
if (0 == wcscmp (wszVolumeName, ppwszVolumeNamesArray [ulIndex])) { bFound = TRUE; } } }
RETURN_VALUE_IF_REQUIRED (pbReturnedFoundInVolumeArray, bFound);
StringFree (&ucsVolumeMountPoint);
return (hrStatus); } /* IsPathInVolumeArray () */
/*
**++ ** ** Routine Description: ** ** Routine to classify the many assorted internal writer errors ** into one of the narrow set of responses a writer is permitted ** to send back to the requestor. ** ** ** Arguments: ** ** hrStatus HRESULT to be classified ** ** ** Return Value: ** ** One of the following list depending upon the supplied status. ** ** VSS_E_WRITERERROR_OUTOFRESOURCES ** VSS_E_WRITERERROR_RETRYABLE ** VSS_E_WRITERERROR_NONRETRYABLE ** VSS_E_WRITERERROR_TIMEOUT ** VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT ** ** **-- */
const HRESULT ClassifyWriterFailure (HRESULT hrWriterFailure) { BOOL bStatusUpdated;
return (ClassifyWriterFailure (hrWriterFailure, bStatusUpdated)); } /* ClassifyWriterFailure () */
/*
**++ ** ** Routine Description: ** ** Routine to classify the many assorted internal writer errors ** into one of the narrow set of responses a writer is permitted ** to send back to the requestor. ** ** ** Arguments: ** ** hrStatus HRESULT to be classified ** bStatusUpdated TRUE if the status is re-mapped ** ** ** Return Value: ** ** One of the following list depending upon the supplied status. ** ** VSS_E_WRITERERROR_OUTOFRESOURCES ** VSS_E_WRITERERROR_RETRYABLE ** VSS_E_WRITERERROR_NONRETRYABLE ** VSS_E_WRITERERROR_TIMEOUT ** VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT ** ** **-- */
const HRESULT ClassifyWriterFailure (HRESULT hrWriterFailure, BOOL &bStatusUpdated) { HRESULT hrStatus;
switch (hrWriterFailure) { case NOERROR: case VSS_E_WRITERERROR_OUTOFRESOURCES: case VSS_E_WRITERERROR_RETRYABLE: case VSS_E_WRITERERROR_NONRETRYABLE: case VSS_E_WRITERERROR_TIMEOUT: case VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT: /*
** These are ok as they are so no need to transmogrify them. */ hrStatus = hrWriterFailure; bStatusUpdated = FALSE; break;
case E_OUTOFMEMORY: case HRESULT_FROM_WIN32 (ERROR_NOT_ENOUGH_MEMORY): case HRESULT_FROM_WIN32 (ERROR_NO_MORE_SEARCH_HANDLES): case HRESULT_FROM_WIN32 (ERROR_NO_MORE_USER_HANDLES): case HRESULT_FROM_WIN32 (ERROR_NO_LOG_SPACE): case HRESULT_FROM_WIN32 (ERROR_DISK_FULL): hrStatus = VSS_E_WRITERERROR_OUTOFRESOURCES; bStatusUpdated = TRUE; break;
case HRESULT_FROM_WIN32 (ERROR_NOT_READY): hrStatus = VSS_E_WRITERERROR_RETRYABLE; bStatusUpdated = TRUE; break;
case HRESULT_FROM_WIN32 (ERROR_TIMEOUT): hrStatus = VSS_E_WRITERERROR_TIMEOUT; bStatusUpdated = TRUE; break;
case E_UNEXPECTED: case E_INVALIDARG: // equal to HRESULT_FROM_WIN32 (ERROR_INVALID_PARAMETER)
case E_ACCESSDENIED: case HRESULT_FROM_WIN32 (ERROR_PATH_NOT_FOUND): case HRESULT_FROM_WIN32 (ERROR_FILE_NOT_FOUND): case HRESULT_FROM_WIN32 (ERROR_PRIVILEGE_NOT_HELD): case HRESULT_FROM_WIN32 (ERROR_NOT_LOCKED): case HRESULT_FROM_WIN32 (ERROR_LOCKED):
default: hrStatus = VSS_E_WRITERERROR_NONRETRYABLE; bStatusUpdated = TRUE; break; }
return (hrStatus); } /* ClassifyWriterFailure () */
/*
**++ ** ** Routine Description: ** ** Routine to classify the many assorted internal shim errors ** into one of the narrow set of responses a writer is permitted ** to send back to the requestor. ** ** ** Arguments: ** ** hrStatus HRESULT to be classified ** ** ** Return Value: ** ** One of the following list depending upon the supplied status. ** ** E_OUTOFMEMORY ** E_ACCESSDENIED ** E_INVALIDARG ** E_UNEXPECTED ** VSS_E_WRITERERROR_OUTOFRESOURCES ** VSS_E_WRITERERROR_RETRYABLE ** VSS_E_WRITERERROR_NONRETRYABLE ** VSS_E_WRITERERROR_TIMEOUT ** VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT **-- */
const HRESULT ClassifyShimFailure (HRESULT hrWriterFailure) { BOOL bStatusUpdated;
return (ClassifyShimFailure (hrWriterFailure, bStatusUpdated)); } /* ClassifyShimFailure () */
/*
**++ ** ** Routine Description: ** ** Routine to classify the many assorted internal shim errors ** into one of the narrow set of responses a writer is permitted ** to send back to the requestor. ** ** ** Arguments: ** ** hrStatus HRESULT to be classified ** bStatusUpdated TRUE if the status is re-mapped ** ** ** Return Value: ** ** One of the following list depending upon the supplied status. ** ** E_OUTOFMEMORY ** E_ACCESSDENIED ** E_INVALIDARG ** E_UNEXPECTED ** VSS_E_BAD_STATE ** VSS_E_SNAPSHOT_SET_IN_PROGRESS ** VSS_E_WRITERERROR_OUTOFRESOURCES ** VSS_E_WRITERERROR_RETRYABLE ** VSS_E_WRITERERROR_NONRETRYABLE ** VSS_E_WRITERERROR_TIMEOUT ** VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT **-- */
const HRESULT ClassifyShimFailure (HRESULT hrWriterFailure, BOOL &bStatusUpdated) { HRESULT hrStatus;
switch (hrWriterFailure) { case NOERROR: case E_OUTOFMEMORY: case E_ACCESSDENIED: case E_INVALIDARG: // equal to HRESULT_FROM_WIN32 (ERROR_INVALID_PARAMETER)
case E_UNEXPECTED: case VSS_E_BAD_STATE: case VSS_E_SNAPSHOT_SET_IN_PROGRESS: case VSS_E_WRITERERROR_RETRYABLE: case VSS_E_WRITERERROR_NONRETRYABLE: case VSS_E_WRITERERROR_TIMEOUT: case VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT: case VSS_E_WRITERERROR_OUTOFRESOURCES: /*
** These are ok as they are so no need to transmogrify them. */ hrStatus = hrWriterFailure; bStatusUpdated = FALSE; break;
case HRESULT_FROM_WIN32 (ERROR_NOT_LOCKED): hrStatus = VSS_E_BAD_STATE; bStatusUpdated = TRUE; break;
case HRESULT_FROM_WIN32 (ERROR_LOCKED): hrStatus = VSS_E_SNAPSHOT_SET_IN_PROGRESS; bStatusUpdated = TRUE; break;
case HRESULT_FROM_WIN32 (ERROR_NOT_ENOUGH_MEMORY): case HRESULT_FROM_WIN32 (ERROR_NO_MORE_SEARCH_HANDLES): case HRESULT_FROM_WIN32 (ERROR_NO_MORE_USER_HANDLES): case HRESULT_FROM_WIN32 (ERROR_NO_LOG_SPACE): case HRESULT_FROM_WIN32 (ERROR_DISK_FULL): hrStatus = E_OUTOFMEMORY; bStatusUpdated = TRUE; break;
case HRESULT_FROM_WIN32 (ERROR_PRIVILEGE_NOT_HELD): hrStatus = E_ACCESSDENIED; bStatusUpdated = TRUE; break;
case HRESULT_FROM_WIN32 (ERROR_TIMEOUT): case HRESULT_FROM_WIN32 (ERROR_PATH_NOT_FOUND): case HRESULT_FROM_WIN32 (ERROR_FILE_NOT_FOUND): case HRESULT_FROM_WIN32 (ERROR_NOT_READY):
default: hrStatus = E_UNEXPECTED; bStatusUpdated = TRUE; break; }
return (hrStatus); } /* ClassifyShimFailure () */
/*
**++ ** ** Routine Description: ** ** Routine to classify the many assorted internal shim or shim ** writer errors into one of the narrow set of responses we are ** permitted to send back to the requestor. ** ** The determination is made to classify either as a shim error ** or as a writer error based upon whether or not a writer name ** is supplied. If it is supplied then the assumption is made ** that this is a writer failure and so the error is classified ** accordingly. ** ** Note that this is a worker routine for the LogFailure() macro ** and the two are intended to be used in concert. ** ** ** Arguments: ** ** pft Pointer to a Function trace class ** pwszNameWriter The name of the applicable writer or NULL or L"" ** pwszNameCalledRoutine The name of the routine that returned the failure status ** ** ** Side Effects: ** ** hr field of *pft updated ** ** ** Return Value: ** ** One of the following list depending upon the supplied status. ** ** E_OUTOFMEMORY ** E_ACCESSDENIED ** E_INVALIDARG ** E_UNEXPECTED ** VSS_E_BAD_STATE ** VSS_E_SNAPSHOT_SET_IN_PROGRESS ** VSS_E_WRITERERROR_OUTOFRESOURCES ** VSS_E_WRITERERROR_RETRYABLE ** VSS_E_WRITERERROR_NONRETRYABLE ** VSS_E_WRITERERROR_TIMEOUT ** VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT ** **-- */
HRESULT LogFailureWorker (CVssFunctionTracer *pft, LPCWSTR pwszNameWriter, LPCWSTR pwszNameCalledRoutine) { if (pft->HrFailed ()) { BOOL bStatusRemapped; HRESULT hrStatusClassified = ((NULL == pwszNameWriter) || (L'\0' == pwszNameWriter [0])) ? ClassifyShimFailure (pft->hr, bStatusRemapped) : ClassifyWriterFailure (pft->hr, bStatusRemapped);
if (bStatusRemapped) { if (((NULL == pwszNameCalledRoutine) || (L'\0' == pwszNameCalledRoutine [0])) && ((NULL == pwszNameWriter) || (L'\0' == pwszNameWriter [0]))) { pft->LogError (VSS_ERROR_SHIM_GENERAL_FAILURE, VSSDBG_WRTCMN << pft->hr << hrStatusClassified);
pft->Trace (VSSDBG_WRTCMN, L"FAILED with status 0x%08lX (converted to 0x%08lX)", pft->hr, hrStatusClassified); }
else if ((NULL == pwszNameCalledRoutine) || (L'\0' == pwszNameCalledRoutine [0])) { pft->LogError (VSS_ERROR_SHIM_WRITER_GENERAL_FAILURE, VSSDBG_WRTCMN << pft->hr << hrStatusClassified << pwszNameWriter);
pft->Trace (VSSDBG_WRTCMN, L"FAILED in writer %s with status 0x%08lX (converted to 0x%08lX)", pwszNameWriter, pft->hr, hrStatusClassified); }
else if ((NULL == pwszNameWriter) || (L'\0' == pwszNameWriter [0])) { pft->LogError (VSS_ERROR_SHIM_FAILED_SYSTEM_CALL, VSSDBG_WRTCMN << pft->hr << hrStatusClassified << pwszNameCalledRoutine);
pft->Trace (VSSDBG_WRTCMN, L"FAILED calling routine %s with status 0x%08lX (converted to 0x%08lX)", pwszNameCalledRoutine, pft->hr, hrStatusClassified); }
else { pft->LogError (VSS_ERROR_SHIM_WRITER_FAILED_SYSTEM_CALL, VSSDBG_WRTCMN << pft->hr << hrStatusClassified << pwszNameWriter << pwszNameCalledRoutine);
pft->Trace (VSSDBG_WRTCMN, L"FAILED in writer %s calling routine %s with status 0x%08lX (converted to 0x%08lX)", pwszNameWriter, pwszNameCalledRoutine, pft->hr, hrStatusClassified); }
pft->hr = hrStatusClassified; } }
return (pft->hr); } /* LogFailureWorker () */
|