Leaked source code of windows server 2003
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.
 
 
 
 
 
 

2665 lines
76 KiB

/*++
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 () */