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.
 
 
 
 
 
 

2857 lines
77 KiB

/*++
Copyright (c) 1998-1999 Microsoft Corporation
Module Name:
filelist.c
Abstract:
this is the code that handles the file lists (exclusion/inclusion).
Author:
Paul McDaniel (paulmcd) 23-Jan-2000
Revision History:
--*/
#include "precomp.h"
//
// Private prototypes.
//
//
// linker commands
//
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, SrGetObjectName )
#pragma alloc_text( PAGE, SrpFindFilePartW)
#pragma alloc_text( PAGE, SrpFindFilePart )
#pragma alloc_text( PAGE, SrFindCharReverse )
#pragma alloc_text( PAGE, SrGetDestFileName )
#pragma alloc_text( PAGE, SrGetNextFileNumber )
#pragma alloc_text( PAGE, SrGetNextSeqNumber )
#pragma alloc_text( PAGE, SrGetSystemVolume )
#pragma alloc_text( PAGE, SrMarkFileBackedUp )
#pragma alloc_text( PAGE, SrHasFileBeenBackedUp )
#pragma alloc_text( PAGE, SrResetBackupHistory )
#pragma alloc_text( PAGE, SrResetHistory )
#pragma alloc_text( PAGE, SrGetVolumeDevice )
#pragma alloc_text( PAGE, SrSetFileSecurity )
#pragma alloc_text( PAGE, SrGetVolumeGuid )
#pragma alloc_text( PAGE, SrAllocateFileNameBuffer )
#pragma alloc_text( PAGE, SrFreeFileNameBuffer )
#pragma alloc_text( PAGE, SrGetNumberOfLinks )
#pragma alloc_text( PAGE, SrCheckVolume )
#pragma alloc_text( PAGE, SrCheckForRestoreLocation )
#pragma alloc_text( PAGE, SrGetMountVolume )
#pragma alloc_text( PAGE, SrCheckFreeDiskSpace )
#pragma alloc_text( PAGE, SrSetSecurityObjectAsSystem )
#pragma alloc_text( PAGE, SrCheckForMountsInPath )
#pragma alloc_text( PAGE, SrGetShortFileName )
#endif // ALLOC_PRAGMA
//
// Private globals.
//
//
// Public globals.
//
//
// Public functions.
//
NTSTATUS
SrGetObjectName(
IN PSR_DEVICE_EXTENSION pExtension OPTIONAL,
IN PVOID pObject,
OUT PUNICODE_STRING pName,
IN ULONG NameLength // size of the buffer in pName
)
{
NTSTATUS Status;
ULONG ReturnLength = 0;
PVOID Buffer = NULL;
ULONG BufferLength;
PFILE_NAME_INFORMATION NameInfo;
if (pExtension != NULL) {
ASSERT( IS_VALID_FILE_OBJECT( (PFILE_OBJECT)pObject ) &&
((PFILE_OBJECT)pObject)->Vpb != NULL );
//
// We are getting the name of a file object, so
// call SrQueryInformationFile to query the name.
//
BufferLength = NameLength + sizeof( ULONG );
Buffer = ExAllocatePoolWithTag( PagedPool,
BufferLength,
SR_FILENAME_BUFFER_TAG);
if (Buffer == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
NameInfo = Buffer;
Status = SrQueryInformationFile( pExtension->pTargetDevice,
pObject,
NameInfo,
BufferLength,
FileNameInformation,
&ReturnLength );
if (NT_SUCCESS( Status )) {
//
// We successfully got the name, so now build up the device name
// and file name into the pName buffer passed in.
//
ASSERT( pExtension->pNtVolumeName );
Status = RtlAppendUnicodeStringToString( pName,
pExtension->pNtVolumeName );
if (!NT_SUCCESS( Status )) {
goto SrGetObjectName_Cleanup;
}
if ((pName->Length + NameInfo->FileNameLength + sizeof( WCHAR )) <=
pName->MaximumLength ) {
//
// We have enough room in the buffer for the file name and
// a NULL terminator.
//
RtlCopyMemory( &pName->Buffer[pName->Length/sizeof(WCHAR)],
NameInfo->FileName,
NameInfo->FileNameLength );
pName->Length += (USHORT)NameInfo->FileNameLength;
pName->Buffer[pName->Length/sizeof( WCHAR )] = UNICODE_NULL;
} else {
Status = STATUS_BUFFER_OVERFLOW;
}
}
} else {
ULONG NameBufferLength = NameLength - sizeof( UNICODE_STRING );
ASSERT( IS_VALID_DEVICE_OBJECT( (PDEVICE_OBJECT)pObject ) );
//
// Use ObQueryNameString to get the name of the DeviceObject passed
// in, but save space to NULL-terminate the name.
//
Status = ObQueryNameString( pObject,
(POBJECT_NAME_INFORMATION) pName,
NameBufferLength - sizeof( UNICODE_NULL ),
&ReturnLength);
if (NT_SUCCESS( Status )) {
//
// ObQueryNameString sets the MaximumLength of pName to something
// it calculates, which is smaller than what we allocated. Fix this
// up here and NULL terminate the string (we've already reserved
// the space).
//
pName->MaximumLength = (USHORT)NameBufferLength;
pName->Buffer[pName->Length/sizeof( WCHAR )] = UNICODE_NULL;
}
}
SrGetObjectName_Cleanup:
if (Buffer != NULL) {
ExFreePoolWithTag( Buffer, SR_FILENAME_BUFFER_TAG );
}
RETURN( Status );
}
/***************************************************************************++
Routine Description:
Locates the file part of a fully qualified path.
Arguments:
pPath - Supplies the path to scan.
Return Value:
PSTR - The file part.
--***************************************************************************/
PWSTR
SrpFindFilePartW(
IN PWSTR pPath
)
{
PWSTR pFilePart;
PAGED_CODE();
SrTrace(FUNC_ENTRY, ("SR!SrpFindFilePartW\n"));
//
// Strip off the path from the path.
//
pFilePart = wcsrchr( pPath, L'\\' );
if (pFilePart == NULL)
{
pFilePart = pPath;
}
else
{
pFilePart++;
}
return pFilePart;
} // SrpDbgFindFilePart
/***************************************************************************++
Routine Description:
Locates the file part of a fully qualified path.
Arguments:
pPath - Supplies the path to scan.
Return Value:
PSTR - The file part.
--***************************************************************************/
PSTR
SrpFindFilePart(
IN PSTR pPath
)
{
PSTR pFilePart;
PAGED_CODE();
SrTrace(FUNC_ENTRY, ("SR!SrpFindFilePart\n"));
//
// Strip off the path from the path.
//
pFilePart = strrchr( pPath, '\\' );
if (pFilePart == NULL)
{
pFilePart = pPath;
}
else
{
pFilePart++;
}
return pFilePart;
} // SrpFindFilePart
NTSTATUS
SrFindCharReverse(
IN PWSTR pToken,
IN ULONG TokenLength,
IN WCHAR FindChar,
OUT PWSTR * ppToken,
OUT PULONG pTokenLength
)
{
NTSTATUS Status;
int i;
ULONG TokenCount;
PAGED_CODE();
//
// assume we didn't find it
//
Status = STATUS_OBJECT_NAME_NOT_FOUND;
//
// turn this into a count
//
TokenCount = TokenLength / sizeof(WCHAR);
if (TokenCount == 0 || pToken == NULL || pToken[0] == UNICODE_NULL)
goto end;
//
// start looking from the end
//
for (i = TokenCount - 1; i >= 0; i--)
{
if (pToken[i] == FindChar)
break;
}
if (i >= 0)
{
//
// found it!
//
*ppToken = pToken + i;
*pTokenLength = (TokenCount - i) * sizeof(WCHAR);
Status = STATUS_SUCCESS;
}
end:
return Status;
} // SrFindCharReverse
/***************************************************************************++
Routine Description:
This routine generates the destination file name for a file that is being
created in the restore location. This name had the extension of the file
that is being backed up with a unique file name that is generated here.
Arguments:
pExtension - The SR_DEVICE_EXTENSION for the volume on which this file
resides.
pFileName - The name of the original file that is being backed up into
the restore location. This file is in the SR's normalized form
(e.g., \\Device\HarddiskVolume1\mydir\myfile.ext)
pDestFileName - This unicode string gets filled in with the full path
and file name for the destination file in the restore location.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
SrGetDestFileName(
IN PSR_DEVICE_EXTENSION pExtension,
IN PUNICODE_STRING pFileName,
OUT PUNICODE_STRING pDestFileName
)
{
NTSTATUS Status;
PWSTR pFilePart;
ULONG FilePartLength;
ULONG NextFileNumber;
ULONG CharCount;
PAGED_CODE();
ASSERT( (pFileName != NULL) && (pFileName->Length > 0));
ASSERT( pDestFileName != NULL );
ASSERT( IS_ACTIVITY_LOCK_ACQUIRED_EXCLUSIVE( pExtension ) ||
IS_ACTIVITY_LOCK_ACQUIRED_SHARED( pExtension ) );
//
// Copy the volume name out of the device extension.
//
ASSERT( pExtension->pNtVolumeName != NULL );
Status = RtlAppendUnicodeStringToString( pDestFileName,
pExtension->pNtVolumeName );
if (!NT_SUCCESS( Status ))
{
goto SrGetDestFileName_Exit;
}
//
// and append our restore point location
//
CharCount = swprintf( &pDestFileName->Buffer[pDestFileName->Length/sizeof(WCHAR)],
RESTORE_LOCATION,
global->MachineGuid );
pDestFileName->Length += (USHORT)CharCount * sizeof(WCHAR);
//
// and the actual restore directory; we don't need to acquire a lock
// to read this because we already have the ActivityLock and this
// will prevent the value from changing.
//
CharCount = swprintf( &pDestFileName->Buffer[pDestFileName->Length/sizeof(WCHAR)],
L"\\" RESTORE_POINT_PREFIX L"%d\\",
global->FileConfig.CurrentRestoreNumber );
pDestFileName->Length += (USHORT)CharCount * sizeof(WCHAR);
//
// now get a number to use
//
Status = SrGetNextFileNumber(&NextFileNumber);
if (!NT_SUCCESS(Status))
{
goto SrGetDestFileName_Exit;
}
//
// use the "A" prefix (e.g. "A0000001.dll" )
//
swprintf( &pDestFileName->Buffer[pDestFileName->Length/sizeof(WCHAR)],
RESTORE_FILE_PREFIX L"%07d",
NextFileNumber );
pDestFileName->Length += 8 * sizeof(WCHAR);
//
// We know that the pFileName contains a fully-normalized name, so
// we just need to search for the '.' from the end of the name
// to find the proper extension.
//
pFilePart = pFileName->Buffer;
FilePartLength = pFileName->Length;
Status = SrFindCharReverse( pFilePart,
FilePartLength,
L'.',
&pFilePart,
&FilePartLength );
//
// No extension is not supported!
//
if (!NT_SUCCESS(Status))
{
goto SrGetDestFileName_Exit;
}
//
// now put the proper extension on
//
RtlCopyMemory( &pDestFileName->Buffer[pDestFileName->Length/sizeof(WCHAR)],
pFilePart,
FilePartLength );
pDestFileName->Length += (USHORT)FilePartLength;
//
// NULL terminate it
//
ASSERT(pDestFileName->Length < pDestFileName->MaximumLength);
pDestFileName->Buffer[pDestFileName->Length/sizeof(WCHAR)] = UNICODE_NULL;
SrGetDestFileName_Exit:
RETURN (Status);
} // SrGetDestFileName
NTSTATUS
SrGetNextFileNumber(
OUT PULONG pNextFileNumber
)
{
NTSTATUS Status;
PAGED_CODE();
ASSERT(pNextFileNumber != NULL);
*pNextFileNumber = InterlockedIncrement(&global->LastFileNameNumber);
if (*pNextFileNumber >= global->FileConfig.FileNameNumber)
{
//
// save out the number again
//
try {
SrAcquireGlobalLockExclusive();
//
// double check with the lock held
//
if (*pNextFileNumber >= global->FileConfig.FileNameNumber)
{
global->FileConfig.FileNameNumber += SR_FILE_NUMBER_INCREMENT;
//
// save it out
//
Status = SrWriteConfigFile();
CHECK_STATUS(Status);
}
} finally {
SrReleaseGlobalLock();
}
}
RETURN(STATUS_SUCCESS);
} // SrGetNextFileNumber
NTSTATUS
SrGetNextSeqNumber(
OUT PINT64 pNextSeqNumber
)
{
NTSTATUS Status;
PAGED_CODE();
ASSERT(pNextSeqNumber != NULL);
//
// bummer , there is no interlocked increment for 64bits
//
try {
SrAcquireGlobalLockExclusive();
*pNextSeqNumber = ++(global->LastSeqNumber);
if (*pNextSeqNumber >= global->FileConfig.FileSeqNumber)
{
//
// save out the number again
//
global->FileConfig.FileSeqNumber += SR_SEQ_NUMBER_INCREMENT;
//
// save it out
//
Status = SrWriteConfigFile();
CHECK_STATUS(Status);
}
} finally {
SrReleaseGlobalLock();
}
RETURN(STATUS_SUCCESS);
} // SrGetNextFileNumber
/***************************************************************************++
Routine Description:
Returns the string location of the system volume. Get this by using the
global cache'd system volume extension. If it can't be found (hasn't
been attached yet), it queries the os to get the match for \\SystemRoot.
Arguments:
pFileName - holds the volume path on return (has to be a contigous block)
pSystemVolumeExtension - SR's extension for the device that is attached
to the system volume.
pFileNameLength - holds the size of the buffer on in, and the size copied
on out. both in bytes.
Return Value:
NTSTATUS - completion code.
--***************************************************************************/
NTSTATUS
SrGetSystemVolume(
OUT PUNICODE_STRING pFileName,
OUT PSR_DEVICE_EXTENSION *ppSystemVolumeExtension,
IN ULONG FileNameLength
)
{
NTSTATUS Status;
HANDLE FileHandle = NULL;
IO_STATUS_BLOCK IoStatusBlock;
OBJECT_ATTRIBUTES ObjectAttributes;
PFILE_OBJECT pFileObject = NULL;
UNICODE_STRING FileName;
PDEVICE_OBJECT pFilterDevice;
PDEVICE_OBJECT pRelatedDevice;
PAGED_CODE();
ASSERT( pFileName != NULL);
ASSERT( ppSystemVolumeExtension != NULL );
*ppSystemVolumeExtension = NULL;
if (global->pSystemVolumeExtension == NULL) {
//
// don't have it cache'd, attempt to open the SystemRoot
//
RtlInitUnicodeString(&FileName, L"\\SystemRoot");
InitializeObjectAttributes( &ObjectAttributes,
&FileName,
OBJ_KERNEL_HANDLE,
NULL,
NULL );
Status = ZwCreateFile( &FileHandle,
FILE_GENERIC_READ, // DesiredAccess
&ObjectAttributes,
&IoStatusBlock,
NULL, // AllocationSize
FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL, // EaBuffer
0 ); // EaLength
if (!NT_SUCCESS(Status))
goto end;
//
// now get the file object
//
Status = ObReferenceObjectByHandle( FileHandle,
0, // DesiredAccess
*IoFileObjectType,
KernelMode,
(PVOID *) &pFileObject,
NULL );
if (!NT_SUCCESS(Status))
goto end;
//
// and get our device's extension
//
pRelatedDevice = IoGetRelatedDeviceObject( pFileObject );
if (pRelatedDevice == NULL )
{
Status = STATUS_INVALID_DEVICE_REQUEST;
goto end;
}
pFilterDevice = SrGetFilterDevice(pRelatedDevice);
if (pFilterDevice == NULL) {
//
// we are not attached to the system volume, just get the name
// This happens during unload, when writing out the config file
//
Status = SrGetObjectName( NULL,
pFileObject->Vpb->RealDevice,
pFileName,
FileNameLength );
if (!NT_SUCCESS(Status))
goto end;
//
// all done
//
goto end;
}
//
// and store it
//
global->pSystemVolumeExtension = pFilterDevice->DeviceExtension;
SrTrace( INIT, (
"sr!SrGetSystemVolume: cached system volume [%wZ]\n",
global->pSystemVolumeExtension->pNtVolumeName ));
}
ASSERT(global->pSystemVolumeExtension != NULL);
ASSERT(global->pSystemVolumeExtension->pNtVolumeName != NULL);
//
// now use the cache'd value
//
if (FileNameLength <
global->pSystemVolumeExtension->pNtVolumeName->Length) {
Status = STATUS_BUFFER_OVERFLOW;
} else {
RtlCopyUnicodeString( pFileName,
global->pSystemVolumeExtension->pNtVolumeName );
*ppSystemVolumeExtension = global->pSystemVolumeExtension;
Status = STATUS_SUCCESS;
}
end:
if (pFileObject != NULL)
{
ObDereferenceObject(pFileObject);
pFileObject = NULL;
}
if (FileHandle != NULL)
{
ZwClose(FileHandle);
FileHandle = NULL;
}
RETURN(Status);
} // SrGetSystemVolume
/***************************************************************************++
Routine Description:
This routine updates the backup history for the given file. Based on the
current event and the full file name, this routine decides whether to log
this change against the file's unnamed data stream or the data stream
currently being operation on.
Arguments:
pExtension - the SR device extension for the current volume.
pFileName - holds the path name of the file that's been backed up.
StreamNameLength - the length of the stream component of the file name
if there is one.
CurrentEvent - the event that is causing us to update the backup history
FutureEventsToIgnore - the events that should be ignored in the future.
Return Value:
Returns STATUS_SUCCESS if we are able to successfully update the backup
history, or the appropriate error code otherwise.
--***************************************************************************/
NTSTATUS
SrMarkFileBackedUp(
IN PSR_DEVICE_EXTENSION pExtension,
IN PUNICODE_STRING pFileName,
IN USHORT StreamNameLength,
IN SR_EVENT_TYPE CurrentEvent,
IN SR_EVENT_TYPE FutureEventsToIgnore
)
{
NTSTATUS Status;
ULONG_PTR Context = (ULONG_PTR) SrEventInvalid;
HASH_KEY Key;
PAGED_CODE();
ASSERT( pFileName != NULL );
//
// Make sure our pFileName is correctly constructed.
//
ASSERT( IS_VALID_SR_STREAM_STRING( pFileName, StreamNameLength ) );
//
// Set up the hash key we need to lookup.
//
Key.FileName.Length = pFileName->Length;
Key.FileName.MaximumLength = pFileName->MaximumLength;
Key.FileName.Buffer = pFileName->Buffer;
Key.StreamNameLength = RECORD_AGAINST_STREAM( CurrentEvent,
StreamNameLength );
try {
SrAcquireBackupHistoryLockExclusive( pExtension );
//
// try and find a matching entry in the hash list
//
Status = HashFindEntry( pExtension->pBackupHistory,
&Key,
(PVOID*)&Context );
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
{
//
// not found... add one
//
Status = HashAddEntry( pExtension->pBackupHistory,
&Key,
(PVOID)FutureEventsToIgnore );
if (!NT_SUCCESS(Status))
leave;
}
else if (NT_SUCCESS(Status))
{
//
// add this to the mask
//
Context |= FutureEventsToIgnore;
//
// and update the entry
//
Status = HashAddEntry( pExtension->pBackupHistory,
&Key,
(PVOID)Context );
if (Status == STATUS_DUPLICATE_OBJECTID)
{
Status = STATUS_SUCCESS;
}
else if (!NT_SUCCESS(Status))
{
leave;
}
}
}finally {
SrReleaseBackupHistoryLock( pExtension );
}
RETURN(Status);
} // SrMarkFileBackedUp
/***************************************************************************++
Routine Description:
This routine looks up in the backup history based on the name passed in
whether or not this EventType has already been backed up for this file.
With stream names, this is a little more complicated than it first appears.
If this name contains a stream, we may have to look up to see if we have
a history on the file name with and without the stream component of the
name.
Arguments:
pExtension - SR's device extension for this volume. This contains
our backup history structures.
pFileName - The file name to lookup. If the name has a stream component,
that stream component is in the buffer of this unicode string, but
the length only designates the file-only name portion.
StreamNameLength - Designates the extra bytes in addition to
pFileName->Length that specify the stream component of the name.
EventType - The current event that we are seeing on this file.
Return Value:
Returns TRUE if this file has already been backed up for this EventType,
and FALSE otherwise.
--***************************************************************************/
BOOLEAN
SrHasFileBeenBackedUp(
IN PSR_DEVICE_EXTENSION pExtension,
IN PUNICODE_STRING pFileName,
IN USHORT StreamNameLength,
IN SR_EVENT_TYPE EventType
)
{
NTSTATUS Status;
ULONG_PTR Context;
HASH_KEY Key;
SR_EVENT_TYPE EventsToIgnore = SrEventInvalid;
BOOLEAN HasBeenBackedUp = FALSE;
PAGED_CODE();
ASSERT( pFileName != NULL );
//
// Make sure our pFileName is correctly constructed.
//
ASSERT( IS_VALID_SR_STREAM_STRING( pFileName, StreamNameLength ) );
//
// Setup our hash key. We will first do a lookup on the exact match
// of the name passed in since this will be what hits the majority of the
// time.
//
Key.FileName.Length = pFileName->Length;
Key.FileName.MaximumLength = pFileName->MaximumLength;
Key.FileName.Buffer = pFileName->Buffer;
Key.StreamNameLength = StreamNameLength;
try {
SrAcquireBackupHistoryLockShared( pExtension );
//
// try and find a matching entry in the hash list
//
Status = HashFindEntry( pExtension->pBackupHistory,
&Key,
(PVOID*)&Context );
if (Status == STATUS_OBJECT_NAME_NOT_FOUND || !NT_SUCCESS(Status))
{
//
// We don't have a backup history entry for this name, so we
// don't have any recorded events to igore for this name.
//
EventsToIgnore = SrEventInvalid;
}
else
{
//
// Context contains the set of events that should be ignored
// for this name.
//
EventsToIgnore = (SR_EVENT_TYPE)Context;
}
//
// Now see if we have enough information to say with certainty whether
// or not we should ignore this operation.
//
// We have two cases here:
// 1. The name passed in has NO stream name component
// In this case, the current value of EventsToIgnore is all
// we have to make our decision. So figure out if our
// EventType is in this list and return the appropriate
// HasBeenBackedUp value.
//
// 2. The name passed in DOES have a stream name component
// In this case, if our EventType is already in the
// EventsToIgnore set, we are done. Otherwise, if this
// EventType is relevant to the file-only name, check to
// see if we have a backup history entry for that name.
//
if (StreamNameLength == 0)
{
HasBeenBackedUp = BooleanFlagOn( EventsToIgnore, EventType );
leave;
}
else
{
if (FlagOn( EventsToIgnore, EventType ))
{
HasBeenBackedUp = TRUE;
leave;
}
else
{
//
// We need to see if we have a context on the file-only portion
// of the name.
//
Key.FileName.Length = pFileName->Length;
Key.FileName.MaximumLength = pFileName->MaximumLength;
Key.FileName.Buffer = pFileName->Buffer;
Key.StreamNameLength = 0;
Status = HashFindEntry( pExtension->pBackupHistory,
&Key,
(PVOID*)&Context );
if (Status == STATUS_OBJECT_NAME_NOT_FOUND || !NT_SUCCESS(Status))
{
//
// We don't have a backup history entry for this name, so we
// don't have any recorded events to igore for this name.
//
EventsToIgnore = SrEventInvalid;
}
else
{
//
// Context contains the set of events that should be ignored
// for this name.
//
EventsToIgnore = (SR_EVENT_TYPE)Context;
}
//
// This is all we've got, so make our decision based on the
// current value of EventsToIgnore.
//
HasBeenBackedUp = BooleanFlagOn( EventsToIgnore, EventType );
}
}
}
finally
{
SrReleaseBackupHistoryLock( pExtension );
}
return HasBeenBackedUp;
} // SrHasFileBeenBackedUp
/***************************************************************************++
Routine Description:
this will clear the backup history completely. this is done when a new
restore point is created .
Arguments:
Return Value:
NTSTATUS - completion code.
--***************************************************************************/
NTSTATUS
SrResetBackupHistory(
IN PSR_DEVICE_EXTENSION pExtension,
IN PUNICODE_STRING pFileName OPTIONAL,
IN USHORT StreamNameLength OPTIONAL,
IN SR_EVENT_TYPE EventType
)
{
NTSTATUS Status;
ULONG_PTR Context;
HASH_KEY Key;
PAGED_CODE();
try
{
SrAcquireBackupHistoryLockExclusive( pExtension );
if (pFileName == NULL)
{
//
// clear them all
//
HashClearEntries(pExtension->pBackupHistory);
Status = STATUS_SUCCESS;
}
else if (StreamNameLength > 0)
{
//
// clear just this one
//
//
// Make sure our pFileName is correctly constructed.
//
ASSERT( IS_VALID_SR_STREAM_STRING( pFileName, StreamNameLength ) );
Key.FileName.Length = pFileName->Length;
Key.FileName.MaximumLength = pFileName->MaximumLength;
Key.FileName.Buffer = pFileName->Buffer;
Key.StreamNameLength = StreamNameLength;
Status = HashFindEntry( pExtension->pBackupHistory,
&Key,
(PVOID*)&Context );
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
{
//
// no entry, nothing to clear
//
Status = STATUS_SUCCESS;
leave;
}
else if (!NT_SUCCESS(Status))
{
leave;
}
//
// update/clear the existing entry
//
Context = EventType;
Status = HashAddEntry( pExtension->pBackupHistory,
&Key,
(PVOID)Context );
ASSERT(Status == STATUS_DUPLICATE_OBJECTID);
if (Status == STATUS_DUPLICATE_OBJECTID)
{
Status = STATUS_SUCCESS;
}
else if (!NT_SUCCESS(Status))
{
leave;
}
}
else
{
//
// We've got to clear all entries associated with this file name.
//
Status = HashClearAllFileEntries( pExtension->pBackupHistory,
pFileName );
}
}
finally
{
SrReleaseBackupHistoryLock( pExtension );
}
RETURN(Status);
} // SrResetBackupHistory
/***************************************************************************++
Routine Description:
this is a callback function for HashprocessEntries that is used to reset
the history on all files that match the directory prefix. this is called
when a directory is renamed, invaliding all hash entries with the
directory's new name, as they are no longer the same file.
Arguments:
Return Value:
NTSTATUS - completion code.
--***************************************************************************/
PVOID
SrResetHistory(
IN PHASH_KEY pKey,
IN PVOID pEntryContext,
PUNICODE_STRING pDirectoryName
)
{
PAGED_CODE();
//
// does this directory prefix match the key?
//
if (RtlPrefixUnicodeString(pDirectoryName, &pKey->FileName, TRUE))
{
//
// return a new context of invalid event.
//
SrTrace(HASH, ("sr!SrResetHistory: clearing %wZ\n", &pKey->FileName));
return (PVOID)(ULONG_PTR)SrEventInvalid;
}
else
{
//
// do nothing, keep the old context
//
return pEntryContext;
}
} // SrResetHistory
/***************************************************************************++
Routine Description:
returns the proper volume device object for this file object. handles
if the file is open or not.
Arguments:
Return Value:
NTSTATUS - completion code.
--***************************************************************************/
PDEVICE_OBJECT
SrGetVolumeDevice(
PFILE_OBJECT pFileObject
)
{
PAGED_CODE();
//
// is this file open?
//
if (pFileObject->Vpb != NULL)
return pFileObject->Vpb->RealDevice;
//
// otherwise is there a related file object?
//
if (pFileObject->RelatedFileObject != NULL)
{
ASSERT(pFileObject->RelatedFileObject->Vpb != NULL);
if (pFileObject->RelatedFileObject->Vpb != NULL)
return pFileObject->RelatedFileObject->Vpb->RealDevice;
}
//
// otherwise it's unopened and not a relative open
//
return pFileObject->DeviceObject;
} // SrGetVolumeDevice
/***************************************************************************++
Routine Description:
this will set the security descriptor for the object referenced by
FileHandle. it will create a DACL for either system access
(non-recursive) OR eveyone access (recursive) depending on the SystemOnly
flag. the OWNER of the file will be BUILTIN\Administrators .
Arguments:
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
SrSetFileSecurity(
IN HANDLE FileHandle,
IN BOOLEAN SystemOnly,
IN BOOLEAN SetDacl
)
{
NTSTATUS Status;
SECURITY_DESCRIPTOR SecurityDescriptor;
#define DaclLength 128
UCHAR DaclBuffer[DaclLength];
PACL pDacl = (PACL) &DaclBuffer[0];
PACE_HEADER pAce;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
SID_IDENTIFIER_AUTHORITY WorldAuthority = SECURITY_WORLD_SID_AUTHORITY;
//
// SID on the stack only has space for 1 subauthority
//
C_ASSERT(ANYSIZE_ARRAY == 1);
SID LocalSystemSid;
SID EveryoneSid;
PSID pAdminsSid = NULL;
ULONG SecurityInformation = 0;
PAGED_CODE();
try {
//
// create the security descriptor
//
Status = RtlCreateSecurityDescriptor( &SecurityDescriptor,
SECURITY_DESCRIPTOR_REVISION );
if (!NT_SUCCESS(Status))
leave;
if (SetDacl)
{
//
// construct the DACL
//
Status = RtlCreateAcl(pDacl, DaclLength, ACL_REVISION);
if (!NT_SUCCESS(Status))
leave;
//
// is this for system only access ?
//
if (SystemOnly)
{
//
// Construct the local system sid
//
Status = RtlInitializeSid(&LocalSystemSid, &NtAuthority, 1);
if (!NT_SUCCESS(Status))
leave;
*RtlSubAuthoritySid(&LocalSystemSid, 0) = SECURITY_LOCAL_SYSTEM_RID;
//
// just mark it with the local system sid, no inherit
//
Status = RtlAddAccessAllowedAce( pDacl,
ACL_REVISION,
STANDARD_RIGHTS_ALL | GENERIC_ALL,
&LocalSystemSid );
if (!NT_SUCCESS(Status))
leave;
}
else
{
//
// Construct the everyone sid
//
Status = RtlInitializeSid(&EveryoneSid, &WorldAuthority, 1);
if (!NT_SUCCESS(Status))
leave;
*RtlSubAuthoritySid(&EveryoneSid, 0) = SECURITY_WORLD_RID;
//
// mark it with the everyone sid, full inherit
//
Status = RtlAddAccessAllowedAce( pDacl,
ACL_REVISION,
STANDARD_RIGHTS_ALL | GENERIC_ALL,
&EveryoneSid );
if (!NT_SUCCESS(Status))
leave;
//
// set the flags to full inherit (LAME! should use
// RtlAddAccessAllowedAceEx but it isn't exported from ntoskrnl.lib )
//
Status = RtlGetAce(pDacl, 0, &pAce);
if (!NT_SUCCESS(Status))
leave;
pAce->AceFlags = OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
}
//
// put the dacl in the descriptor
//
Status = RtlSetDaclSecurityDescriptor( &SecurityDescriptor,
TRUE,
pDacl,
FALSE );
if (!NT_SUCCESS(Status))
leave;
//
// mark it as protected so that no parent DACL changes what we've set
//
// we should really use RtlSetControlSecurityDescriptor but it isn't
// included in ntoskrnl.lib (lame!)
//
SecurityDescriptor.Control |= SE_DACL_PROTECTED;
SecurityInformation |= DACL_SECURITY_INFORMATION;
} // if (SetDacl)
//
// construct the local admin sid
//
pAdminsSid = SR_ALLOCATE_POOL( PagedPool,
RtlLengthRequiredSid(2),
SR_SECURITY_DATA_TAG );
if (pAdminsSid == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
leave;
}
Status = RtlInitializeSid(pAdminsSid, &NtAuthority, 2);
if (!NT_SUCCESS(Status))
leave;
*RtlSubAuthoritySid(pAdminsSid, 0) = SECURITY_BUILTIN_DOMAIN_RID;
*RtlSubAuthoritySid(pAdminsSid, 1) = DOMAIN_ALIAS_RID_ADMINS;
//
// set the owner to admins
//
Status = RtlSetOwnerSecurityDescriptor( &SecurityDescriptor,
pAdminsSid,
FALSE );
if (!NT_SUCCESS(Status))
leave;
SecurityInformation |= OWNER_SECURITY_INFORMATION;
//
// now set it on the file object
//
Status = SrSetSecurityObjectAsSystem( FileHandle,
SecurityInformation,
&SecurityDescriptor );
if (!NT_SUCCESS(Status))
leave;
} finally {
Status = FinallyUnwind(SrSetFileSecurity, Status);
if (pAdminsSid != NULL)
{
SR_FREE_POOL( pAdminsSid, SR_SECURITY_DATA_TAG );
pAdminsSid = NULL;
}
}
RETURN(Status);
} // SrSetFileSecurity
/***************************************************************************++
Routine Description:
this will lookup the volume guid and return it to the caller.
Arguments:
pVolumeName - the nt name of the volume (\Device\HardDiskVolume1)
pVolumeGuid - holds the guid on retunr ( {xxx-x-x-x} )
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
SrGetVolumeGuid(
IN PUNICODE_STRING pVolumeName,
OUT PWCHAR pVolumeGuid
)
{
NTSTATUS Status;
PMOUNTMGR_MOUNT_POINT pMountPoint = NULL;
PMOUNTMGR_MOUNT_POINTS pMountPoints = NULL;
PMOUNTMGR_MOUNT_POINT pVolumePoint;
UNICODE_STRING DeviceName;
UNICODE_STRING VolumePoint;
IO_STATUS_BLOCK IoStatusBlock;
PKEVENT pEvent = NULL;
PIRP pIrp;
PDEVICE_OBJECT pDeviceObject;
PFILE_OBJECT pFileObject = NULL;
ULONG MountPointsLength;
ULONG Index;
PAGED_CODE();
ASSERT(pVolumeName != NULL);
ASSERT(pVolumeGuid != NULL);
try {
//
// bind to the volume mount point manager's device
//
RtlInitUnicodeString(&DeviceName, MOUNTMGR_DEVICE_NAME);
Status = IoGetDeviceObjectPointer( &DeviceName,
FILE_READ_ATTRIBUTES,
&pFileObject,
&pDeviceObject );
if (!NT_SUCCESS(Status))
leave;
//
// allocate some space for the input mount point (the volume name)
//
pMountPoint = SR_ALLOCATE_STRUCT_WITH_SPACE( PagedPool,
MOUNTMGR_MOUNT_POINT,
pVolumeName->Length,
SR_MOUNT_POINTS_TAG );
if (pMountPoint == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
leave;
}
RtlZeroMemory(pMountPoint, sizeof(MOUNTMGR_MOUNT_POINT));
pMountPoint->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
pMountPoint->DeviceNameLength = pVolumeName->Length;
RtlCopyMemory( pMountPoint + 1,
pVolumeName->Buffer,
pVolumeName->Length );
//
// allocate some space for the mount points we are going to query for
//
MountPointsLength = 1024 * 2;
//
// init an event for use
//
pEvent = SR_ALLOCATE_STRUCT(NonPagedPool, KEVENT, SR_KEVENT_TAG);
if (pEvent == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
leave;
}
KeInitializeEvent(pEvent, SynchronizationEvent, FALSE);
retry:
ASSERT(pMountPoints == NULL);
pMountPoints = (PMOUNTMGR_MOUNT_POINTS)SR_ALLOCATE_ARRAY( PagedPool,
UCHAR,
MountPointsLength,
SR_MOUNT_POINTS_TAG );
if (pMountPoints == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
leave;
}
//
// call into the mount manager to get all of the mount points
//
pIrp = IoBuildDeviceIoControlRequest( IOCTL_MOUNTMGR_QUERY_POINTS,
pDeviceObject,
pMountPoint, // InputBuffer
sizeof(MOUNTMGR_MOUNT_POINT)
+ pMountPoint->DeviceNameLength,
pMountPoints, // OutputBuffer
MountPointsLength,
FALSE, // InternalIoctl
pEvent,
&IoStatusBlock );
if (pIrp == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
leave;
}
//
// call the driver
//
Status = IoCallDriver(pDeviceObject, pIrp);
if (Status == STATUS_PENDING)
{
Status = KeWaitForSingleObject( pEvent,
Executive,
KernelMode,
FALSE,
NULL );
ASSERT(NT_SUCCESS(Status));
Status = IoStatusBlock.Status;
}
//
// do we need a larger buffer?
//
if (Status == STATUS_BUFFER_OVERFLOW)
{
//
// how much should we allocate? (odd IoStatusBlock isn't used - this
// was copied straight from volmount.c ).
//
MountPointsLength = pMountPoints->Size;
SR_FREE_POOL(pMountPoints, SR_MOUNT_POINTS_TAG);
pMountPoints = NULL;
//
// call the driver again!
//
goto retry;
}
else if (!NT_SUCCESS(Status))
{
leave;
}
//
// walk through all of the mount points return and find the
// volume guid name
//
for (Index = 0; Index < pMountPoints->NumberOfMountPoints; ++Index)
{
pVolumePoint = &pMountPoints->MountPoints[Index];
VolumePoint.Length = pVolumePoint->SymbolicLinkNameLength;
VolumePoint.Buffer = (PWSTR)( ((PUCHAR)pMountPoints)
+ pVolumePoint->SymbolicLinkNameOffset);
if (MOUNTMGR_IS_VOLUME_NAME(&VolumePoint))
{
//
// found it!
//
break;
}
} // for (Index = 0; pMountPoints->NumberOfMountPoints; ++Index)
//
// did we find it?
//
if (Index == pMountPoints->NumberOfMountPoints)
{
Status = STATUS_OBJECT_NAME_NOT_FOUND;
leave;
}
//
// return it!
//
ASSERT(VolumePoint.Buffer[10] == L'{');
RtlCopyMemory( pVolumeGuid,
&VolumePoint.Buffer[10],
SR_GUID_BUFFER_LENGTH );
pVolumeGuid[SR_GUID_BUFFER_LENGTH/sizeof(WCHAR)] = UNICODE_NULL;
SrTrace(NOTIFY, ("SR!SrGetVolumeGuid(%wZ, %ws)\n",
pVolumeName, pVolumeGuid ));
} finally {
//
// check for unhandled exceptions
//
Status = FinallyUnwind(SrGetVolumeGuid, Status);
if (pEvent != NULL)
{
SR_FREE_POOL(pEvent, SR_KEVENT_TAG);
pEvent = NULL;
}
if (pMountPoint != NULL)
{
SR_FREE_POOL(pMountPoint, SR_MOUNT_POINTS_TAG);
pMountPoint = NULL;
}
if (pMountPoints != NULL)
{
SR_FREE_POOL(pMountPoints, SR_MOUNT_POINTS_TAG);
pMountPoints = NULL;
}
if (pFileObject != NULL)
{
ObDereferenceObject(pFileObject);
pFileObject = NULL;
}
}
RETURN(Status);
} // SrGetVolumeGuid
//
// paulmcd: 7/2000: remove the lookaside code so that the verifier can
// catch any invalid memory accesses
//
NTSTATUS
SrAllocateFileNameBuffer (
IN ULONG TokenLength,
OUT PUNICODE_STRING * ppBuffer
)
{
PAGED_CODE();
ASSERT(ppBuffer != NULL);
//
// is the file name too big ?
//
if (TokenLength > SR_MAX_FILENAME_LENGTH)
{
RETURN(STATUS_OBJECT_PATH_SYNTAX_BAD);
}
#ifdef USE_LOOKASIDE
*ppBuffer = ExAllocateFromPagedLookasideList(
&global->FileNameBufferLookaside
);
#else
*ppBuffer = SR_ALLOCATE_STRUCT_WITH_SPACE( PagedPool,
UNICODE_STRING,
SR_MAX_FILENAME_LENGTH+sizeof(WCHAR),
SR_FILENAME_BUFFER_TAG );
#endif
if (*ppBuffer == NULL)
{
RETURN(STATUS_INSUFFICIENT_RESOURCES);
}
(*ppBuffer)->Buffer = (PWCHAR)((*ppBuffer) + 1);
(*ppBuffer)->Length = 0;
(*ppBuffer)->MaximumLength = SR_MAX_FILENAME_LENGTH;
RETURN(STATUS_SUCCESS);
} // SrAllocateFileNameBuffer
VOID
SrFreeFileNameBuffer (
IN PUNICODE_STRING pBuffer
)
{
PAGED_CODE();
ASSERT(pBuffer != NULL);
#ifdef USE_LOOKASIDE
ExFreeToPagedLookasideList(
&global->FileNameBufferLookaside,
pBuffer );
#else
SR_FREE_POOL( pBuffer,
SR_FILENAME_BUFFER_TAG );
#endif
} // SrFreeFileNameBuffer
/***************************************************************************++
Routine Description:
this routine will return the number of hardlinks outstanding on this file
Arguments:
NextDeviceObject - the device object where this query will begin.
FileObject - the object to query
pNumberOfLinks - returns the number of links
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
SrGetNumberOfLinks(
IN PDEVICE_OBJECT NextDeviceObject,
IN PFILE_OBJECT FileObject,
OUT ULONG * pNumberOfLinks
)
{
FILE_STANDARD_INFORMATION StandardInformation;
NTSTATUS Status = STATUS_SUCCESS;
ASSERT(NextDeviceObject != NULL );
ASSERT(FileObject != NULL);
ASSERT(pNumberOfLinks != NULL);
PAGED_CODE();
Status = SrQueryInformationFile( NextDeviceObject,
FileObject,
&StandardInformation,
sizeof(StandardInformation),
FileStandardInformation,
NULL );
if (!NT_SUCCESS( Status )) {
RETURN( Status );
}
*pNumberOfLinks = StandardInformation.NumberOfLinks;
RETURN( Status );
} // SrGetNumberOfLinks
/***************************************************************************++
Routine Description:
this checks the volume provided if necessary. a hash table is used
to prevent redundant checks. if it is necessary to check this volume,
this function will do so and create any needed directory structures.
this includes the volume restore location + current restore point
location.
it will fire the first write notification to any listening usermode
process.
Arguments:
pVolumeName - the name of the volume to check on
ForceCheck - force a check. this is passed as TRUE if an SrBackupFile
failed due to path not found.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
SrCheckVolume(
IN PSR_DEVICE_EXTENSION pExtension,
IN BOOLEAN ForceCheck
)
{
NTSTATUS Status;
PUNICODE_STRING pLogFileName = NULL;
BOOLEAN ReleaseLock = FALSE;
ASSERT(ExIsResourceAcquiredShared(&pExtension->ActivityLock));
PAGED_CODE();
//
// has the drive already been checked?
//
if (!ForceCheck && pExtension->DriveChecked)
{
//
// all done then
//
return STATUS_SUCCESS;
}
try {
Status = STATUS_SUCCESS;
//
// grab the lock EXCLUSIVE
//
SrAcquireGlobalLockExclusive();
ReleaseLock = TRUE;
//
// delay load our file config (we have to wait for the file systems
// to become active)
//
if (!global->FileConfigLoaded)
{
Status = SrReadConfigFile();
if (!NT_SUCCESS(Status))
leave;
global->FileConfigLoaded = TRUE;
}
} finally {
SrReleaseGlobalLock();
}
if (!NT_SUCCESS_NO_DBGBREAK( Status )) {
goto SrCheckVolume_Cleanup;
}
try {
SrAcquireLogLockExclusive( pExtension );
//
// check it again with the lock held.
//
if (!ForceCheck && pExtension->DriveChecked)
{
//
// all done then
//
leave;
}
//
// Check to make sure that the volume is enabled.
//
if (!SR_LOGGING_ENABLED(pExtension))
{
leave;
}
//
// first time we've seen this volume, need to check it
//
SrTrace( NOTIFY, ("SR!SrCheckVolume(%wZ, %d)\n",
pExtension->pNtVolumeName,
(ULONG)ForceCheck ));
//
// get the volume guid. this can't be done in SrAttachToVolume as it
// won't work at boot time as the mount mgr is not happy about being
// called that early
//
pExtension->VolumeGuid.Length = SR_GUID_BUFFER_LENGTH;
pExtension->VolumeGuid.MaximumLength = SR_GUID_BUFFER_LENGTH;
pExtension->VolumeGuid.Buffer = &pExtension->VolumeGuidBuffer[0];
Status = SrGetVolumeGuid( pExtension->pNtVolumeName,
&pExtension->VolumeGuidBuffer[0] );
if (!NT_SUCCESS(Status)) {
leave;
}
//
// look for an existing restore location
//
Status = SrCheckForRestoreLocation( pExtension );
//
// if it failed we might need to create a new restore store .
//
if (Status == STATUS_OBJECT_NAME_NOT_FOUND ||
Status == STATUS_OBJECT_PATH_NOT_FOUND)
{
//
// fire a notification for the first write on this volume
//
Status = SrFireNotification( SrNotificationVolumeFirstWrite,
pExtension,
0 );
if (!NT_SUCCESS(Status))
leave;
//
// and create a new restore location
//
Status = SrCreateRestoreLocation( pExtension );
if (!NT_SUCCESS(Status))
leave;
} else if (!NT_SUCCESS(Status)) {
leave;
}
//
// and start logging
//
if (pExtension->pLogContext == NULL)
{
Status = SrAllocateFileNameBuffer(SR_MAX_FILENAME_LENGTH, &pLogFileName);
if (!NT_SUCCESS(Status))
leave;
Status = SrGetLogFileName( pExtension->pNtVolumeName,
SR_FILENAME_BUFFER_LENGTH,
pLogFileName );
if (!NT_SUCCESS(Status))
leave;
//
// start logging!
//
Status = SrLogStart( pLogFileName,
pExtension,
&pExtension->pLogContext );
if (!NT_SUCCESS(Status))
{
leave;
}
}
//
// the drive is now checked
//
pExtension->DriveChecked = TRUE;
//
// we are all done
//
} finally {
//
// check for unhandled exceptions
//
Status = FinallyUnwind(SrCheckVolume, Status);
SrReleaseLogLock( pExtension );
}
SrCheckVolume_Cleanup:
if (pLogFileName != NULL)
{
SrFreeFileNameBuffer( pLogFileName );
pLogFileName = NULL;
}
RETURN(Status);
} // SrCheckVolume
/***************************************************************************++
Routine Description:
this will check for a restore location on the volume provided.
this consists of checking both the location + the restore point location.
it will return failure for no restore location, but will alwasy create
the restore point location as long as a good restore location is found.
Arguments:
pVolumeName - the nt name of the volume to check on
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
SrCheckForRestoreLocation(
IN PSR_DEVICE_EXTENSION pExtension
)
{
NTSTATUS Status;
HANDLE Handle = NULL;
ULONG CharCount;
PUNICODE_STRING pDirectoryName = NULL;
IO_STATUS_BLOCK IoStatusBlock;
OBJECT_ATTRIBUTES ObjectAttributes;
PFILE_RENAME_INFORMATION pRenameInformation = NULL;
PUNICODE_STRING pVolumeName;
ASSERT( IS_VALID_SR_DEVICE_EXTENSION( pExtension ));
ASSERT( IS_ACTIVITY_LOCK_ACQUIRED_EXCLUSIVE( pExtension) ||
IS_LOG_LOCK_ACQUIRED_EXCLUSIVE( pExtension ) );
PAGED_CODE();
try {
pVolumeName = pExtension->pNtVolumeName;
ASSERT( pVolumeName != NULL );
//
// need to check for an existing _restore directory?
//
RtlZeroMemory(&IoStatusBlock, sizeof(IoStatusBlock));
//
// grab a filename buffer
//
Status = SrAllocateFileNameBuffer(SR_MAX_FILENAME_LENGTH, &pDirectoryName);
if (!NT_SUCCESS(Status))
leave;
//
// create our restore point location string
//
CharCount = swprintf( pDirectoryName->Buffer,
VOLUME_FORMAT RESTORE_LOCATION,
pVolumeName,
global->MachineGuid );
pDirectoryName->Length = (USHORT)CharCount * sizeof(WCHAR);
InitializeObjectAttributes( &ObjectAttributes,
pDirectoryName,
OBJ_KERNEL_HANDLE,
NULL,
NULL );
Status = SrIoCreateFile( &Handle,
FILE_LIST_DIRECTORY | SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN, // OPEN_EXISTING
FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_FOR_BACKUP_INTENT,
NULL,
0, // EaLength
IO_IGNORE_SHARE_ACCESS_CHECK,
pExtension->pTargetDevice );
if (!NT_SUCCESS_NO_DBGBREAK(Status))
leave;
//
// the correct location existed .
// this is a good restore point to use
//
ZwClose(Handle);
Handle = NULL;
//
// is there a restore point directory
//
//
// check our current restore points sub directory; don't need to protect
// access to CurrentRestoreNumber because we are protected by the
// ActivityLock.
//
CharCount = swprintf( &pDirectoryName->Buffer[pDirectoryName->Length/sizeof(WCHAR)],
L"\\" RESTORE_POINT_PREFIX L"%d",
global->FileConfig.CurrentRestoreNumber );
pDirectoryName->Length += (USHORT)CharCount * sizeof(WCHAR);
InitializeObjectAttributes( &ObjectAttributes,
pDirectoryName,
OBJ_KERNEL_HANDLE,
NULL,
NULL );
Status = SrIoCreateFile( &Handle,
FILE_LIST_DIRECTORY | SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN, // OPEN_EXISTING
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT,
NULL,
0, // EaLength
IO_IGNORE_SHARE_ACCESS_CHECK,
pExtension->pTargetDevice );
if (!NT_SUCCESS_NO_DBGBREAK(Status))
leave;
//
// all done, it was there
//
ZwClose(Handle);
Handle = NULL;
} finally {
//
// check for unhandled exceptions
//
Status = FinallyUnwind(SrCheckForRestoreLocation, Status);
if (Handle != NULL)
{
ZwClose(Handle);
Handle = NULL;
}
if (pDirectoryName != NULL)
{
SrFreeFileNameBuffer(pDirectoryName);
pDirectoryName = NULL;
}
}
//
// don't use RETURN.. it's normal to return NOT_FOUND
//
return Status;
} // SrCheckForRestoreLocation
NTSTATUS
SrGetMountVolume(
IN PFILE_OBJECT pFileObject,
OUT PUNICODE_STRING * ppMountVolume
)
{
NTSTATUS Status;
HANDLE FileHandle = NULL;
HANDLE EventHandle = NULL;
BOOLEAN RestoreFileObjectFlag = FALSE;
IO_STATUS_BLOCK IoStatusBlock;
PREPARSE_DATA_BUFFER pReparseHeader = NULL;
OBJECT_ATTRIBUTES ObjectAttributes;
PAGED_CODE();
ASSERT(IS_VALID_FILE_OBJECT(pFileObject));
ASSERT(ppMountVolume != NULL);
try {
*ppMountVolume = NULL;
//
// get the current mount point information
//
pReparseHeader = SR_ALLOCATE_POOL( PagedPool,
MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
SR_REPARSE_HEADER_TAG );
if (pReparseHeader == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
leave;
}
RtlZeroMemory(pReparseHeader, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
//
// first turn off any synchro bit that is set. we want
// async for our calls. this does :
//
// 1) avoids the iomgr from grabbing the FileObjectLock. this
// will deadlock if it attempts to grab it.
//
if (FlagOn(pFileObject->Flags, FO_SYNCHRONOUS_IO))
{
RestoreFileObjectFlag = TRUE;
pFileObject->Flags = pFileObject->Flags ^ FO_SYNCHRONOUS_IO;
}
//
// get a handle
//
Status = ObOpenObjectByPointer( pFileObject,
OBJ_KERNEL_HANDLE,
NULL, // PassedAccessState
FILE_READ_ATTRIBUTES,
*IoFileObjectType,
KernelMode,
&FileHandle );
if (!NT_SUCCESS(Status)) {
leave;
}
InitializeObjectAttributes( &ObjectAttributes,
NULL,
OBJ_KERNEL_HANDLE,
NULL,
NULL );
Status = ZwCreateEvent( &EventHandle,
EVENT_ALL_ACCESS,
&ObjectAttributes,
SynchronizationEvent,
FALSE);
if (!NT_SUCCESS(Status)) {
leave;
}
//
// get the old mount volume name
//
Status = ZwFsControlFile( FileHandle,
EventHandle,
NULL, // ApcRoutine OPTIONAL,
NULL, // ApcContext OPTIONAL,
&IoStatusBlock,
FSCTL_GET_REPARSE_POINT,
NULL, // InputBuffer OPTIONAL,
0, // InputBufferLength,
pReparseHeader,
MAXIMUM_REPARSE_DATA_BUFFER_SIZE );
if (Status == STATUS_PENDING)
{
Status = ZwWaitForSingleObject( EventHandle, FALSE, NULL );
if (!NT_SUCCESS(Status)) {
leave;
}
Status = IoStatusBlock.Status;
}
if ((STATUS_NOT_A_REPARSE_POINT == Status) ||
!NT_SUCCESS(Status)) {
leave;
}
if (pReparseHeader->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT ||
pReparseHeader->ReparseDataLength == 0)
{
Status = STATUS_NOT_A_REPARSE_POINT;
leave;
}
//
// grab the volume name
//
Status = SrAllocateFileNameBuffer( pReparseHeader->MountPointReparseBuffer.SubstituteNameLength,
ppMountVolume );
if (!NT_SUCCESS(Status)) {
leave;
}
RtlCopyMemory( (*ppMountVolume)->Buffer,
pReparseHeader->MountPointReparseBuffer.PathBuffer,
pReparseHeader->MountPointReparseBuffer.SubstituteNameLength );
(*ppMountVolume)->Length = pReparseHeader->MountPointReparseBuffer.SubstituteNameLength;
} finally {
Status = FinallyUnwind(SrGetMountVolume, Status);
if (pReparseHeader != NULL)
{
SR_FREE_POOL(pReparseHeader, SR_REPARSE_HEADER_TAG);
pReparseHeader = NULL;
}
if (FileHandle != NULL)
{
ZwClose(FileHandle);
FileHandle = NULL;
}
if (EventHandle != NULL)
{
ZwClose(EventHandle);
EventHandle = NULL;
}
if (RestoreFileObjectFlag)
{
pFileObject->Flags = pFileObject->Flags | FO_SYNCHRONOUS_IO;
}
if (!NT_SUCCESS_NO_DBGBREAK(Status) && *ppMountVolume != NULL)
{
SrFreeFileNameBuffer(*ppMountVolume);
*ppMountVolume = NULL;
}
}
#if DBG
//
// We don't want to break if we see STATUS_NOT_A_REPARSE_POINT.
//
if (STATUS_NOT_A_REPARSE_POINT == Status) {
return Status;
}
#endif
RETURN(Status);
} // SrGetMountVolume
NTSTATUS
SrCheckFreeDiskSpace(
IN HANDLE FileHandle,
IN PUNICODE_STRING pVolumeName OPTIONAL
)
{
NTSTATUS Status;
FILE_FS_FULL_SIZE_INFORMATION FsFullSizeInformation;
IO_STATUS_BLOCK IoStatus;
UNREFERENCED_PARAMETER( pVolumeName );
PAGED_CODE();
Status = ZwQueryVolumeInformationFile( FileHandle,
&IoStatus,
&FsFullSizeInformation,
sizeof(FsFullSizeInformation),
FileFsFullSizeInformation );
if (!NT_SUCCESS(Status))
RETURN(Status);
//
// make sure there is 50mb free
//
if ((FsFullSizeInformation.ActualAvailableAllocationUnits.QuadPart *
FsFullSizeInformation.SectorsPerAllocationUnit *
FsFullSizeInformation.BytesPerSector) < SR_MIN_DISK_FREE_SPACE)
{
//
// this disk is too full for us
//
SrTrace( NOTIFY, ("sr!SrCheckFreeDiskSpace: skipping %wZ due to < 50mb free\n",
pVolumeName ));
RETURN(STATUS_DISK_FULL);
}
RETURN(STATUS_SUCCESS);
} // SrCheckFreeDiskSpace
/***************************************************************************++
Routine Description:
this is a private version of ZwSetSecurityObject. This works around
a bug in ntfs that does not allow you to change the OWNER to admin,
even though PreviousMode is KernelMode.
Arguments:
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
SrSetSecurityObjectAsSystem(
IN HANDLE Handle,
IN SECURITY_INFORMATION SecurityInformation,
IN PSECURITY_DESCRIPTOR SecurityDescriptor
)
{
NTSTATUS Status;
PACCESS_TOKEN pSystemToken;
PACCESS_TOKEN pSavedThreadToken = NULL;
BOOLEAN SavedCopyOnOpen;
BOOLEAN SavedEffectiveOnly;
BOOLEAN RevertImpersonation = FALSE;
SECURITY_IMPERSONATION_LEVEL SavedLevel;
PAGED_CODE();
try {
Status = STATUS_SUCCESS;
//
// get the system token off of the system process
//
pSystemToken = PsReferencePrimaryToken(global->pSystemProcess);
if (pSystemToken == NULL)
{
Status = STATUS_NO_TOKEN;
leave;
}
//
// get this current thread's token (if any)
//
pSavedThreadToken = PsReferenceImpersonationToken( PsGetCurrentThread(),
&SavedCopyOnOpen,
&SavedEffectiveOnly,
&SavedLevel );
//
// OK if (pSavedThreadToken == NULL)
//
//
// impersonate the system token on this thread (if we are not already)
//
if (pSavedThreadToken != pSystemToken)
{
Status = PsImpersonateClient( PsGetCurrentThread(),
pSystemToken,
TRUE, // CopyOnOpen
TRUE, // EffectiveOnly
SecurityImpersonation );
if (!NT_SUCCESS(Status))
leave;
RevertImpersonation = TRUE;
}
//
// change the OWNER now
//
Status = ZwSetSecurityObject( Handle,
SecurityInformation,
SecurityDescriptor );
if (!NT_SUCCESS(Status))
leave;
} finally {
if (RevertImpersonation)
{
NTSTATUS TempStatus;
//
// now revert the impersonation back
//
TempStatus = PsImpersonateClient( PsGetCurrentThread(),
pSavedThreadToken, // OK if NULL
SavedCopyOnOpen,
SavedEffectiveOnly,
SavedLevel );
//
// not much we can do if this fails
//
ASSERT(NT_SUCCESS(TempStatus));
}
if (pSavedThreadToken != NULL)
{
PsDereferenceImpersonationToken(pSavedThreadToken);
pSavedThreadToken = NULL;
}
if (pSystemToken != NULL)
{
ObDereferenceObject(pSystemToken);
pSystemToken = NULL;
}
}
RETURN(Status);
} // SrSetSecurityObjectAsSystem
/***************************************************************************++
Routine Description:
this is will check if the fileobject passed in really is on the volume
represented by pExtension .
if it is not (due to mount points in the path) then it returns the real
file name and the real volume it's on.
Arguments:
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
SrCheckForMountsInPath(
IN PSR_DEVICE_EXTENSION pExtension,
IN PFILE_OBJECT pFileObject,
OUT BOOLEAN * pMountInPath
)
{
PDEVICE_OBJECT pFilterDevice;
PAGED_CODE();
ASSERT(IS_VALID_SR_DEVICE_EXTENSION(pExtension));
ASSERT(IS_VALID_FILE_OBJECT(pFileObject));
ASSERT(pFileObject->Vpb != NULL && pFileObject->Vpb->DeviceObject != NULL);
*pMountInPath = FALSE;
//
// get our device extension that we have attached to this VPB
//
pFilterDevice = SrGetFilterDevice(pFileObject->Vpb->DeviceObject);
if (pFilterDevice == NULL)
{
RETURN(STATUS_INVALID_DEVICE_STATE);
}
//
// check it against the passed in extension for a match
//
if (pFilterDevice->DeviceExtension == pExtension)
{
//
// it's normal, leave early
//
RETURN(STATUS_SUCCESS);
}
//
// we went through a mount point
//
*pMountInPath = TRUE;
RETURN(STATUS_SUCCESS);
} // SrCheckForMountsInPath
/***************************************************************************++
Routine Description:
Return the short file name for the given file object
Arguments:
pExtension - The SR device extension for the volume on which this file
resides.
pFileObject - The file for which we are querying the short name.
pShortName - The unicode string that will get set to the short name.
Return Value:
Returns STATUS_SUCCESS if the shortname is retrieved successfully.
Returns STATUS_OBJECT_NAME_NOT_FOUND if this file does not have a
short name (e.g., hardlinks).
Returns STATUS_BUFFER_OVERFLOW if pShortName is not large enough to hold
the shortname returned.
--***************************************************************************/
NTSTATUS
SrGetShortFileName(
IN PSR_DEVICE_EXTENSION pExtension,
IN PFILE_OBJECT pFileObject,
OUT PUNICODE_STRING pShortName
)
{
NTSTATUS Status;
PFILE_NAME_INFORMATION pNameInfo;
CHAR buffer[sizeof(FILE_NAME_INFORMATION)+(SR_SHORT_NAME_CHARS+1)*sizeof(WCHAR)];
PAGED_CODE();
//
// make the query
//
Status = SrQueryInformationFile( pExtension->pTargetDevice,
pFileObject,
buffer,
sizeof(buffer),
FileAlternateNameInformation,
NULL );
if (STATUS_OBJECT_NAME_NOT_FOUND == Status ||
!NT_SUCCESS( Status ))
{
//
// STATUS_OBJECT_NAME_NOT_FOUND is a valid error that the caller
// should be able to deal with. Some files do not have shortname
// (e.g., hardlinks). If we hit some other error, just return that
// also and let the caller deal.
//
return Status;
}
pNameInfo = (PFILE_NAME_INFORMATION) buffer;
//
// return the short name
//
if (pShortName->MaximumLength < pNameInfo->FileNameLength /*+ sizeof(WCHAR)*/)
{
return STATUS_BUFFER_OVERFLOW;
}
//
// copy the name over
//
RtlCopyMemory( pShortName->Buffer,
pNameInfo->FileName,
pNameInfo->FileNameLength );
//
// update the length and NULL terminate
//
pShortName->Length = (USHORT)pNameInfo->FileNameLength;
//pShortName->Buffer[pShortName->Length/sizeof(WCHAR)] = UNICODE_NULL;
return STATUS_SUCCESS;
}