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.
 
 
 
 
 
 

943 lines
26 KiB

/*++
Copyright (c) 1997, 1998 Microsoft Corporation
Module Name:
index.c
Abstract:
Support for SIS indices.
Authors:
Bill Bolosky, Summer, 1997
Environment:
Kernel mode
Revision History:
--*/
#include "sip.h"
BOOLEAN
SipIndicesFromReparseBuffer(
IN PREPARSE_DATA_BUFFER reparseBuffer,
OUT PCSID CSid,
OUT PLINK_INDEX LinkIndex,
OUT PLARGE_INTEGER CSFileNtfsId,
OUT PLARGE_INTEGER LinkFileNtfsId,
OUT PLONGLONG CSFileChecksum OPTIONAL,
OUT PBOOLEAN EligibleForPartialFinalCopy OPTIONAL,
OUT PBOOLEAN ReparseBufferCorrupt OPTIONAL)
/*++
Routine Description:
Take a SIS reparse buffer, check it for internal consistency and
decode it to its constituient parts.
Arguments:
reparseBuffer - the buffer to decode
CSid
LinkIndex
CSFileNtfsId
LinkFileNtfsId
CSFileChecksum - the values from the reparse point
EligibleForPartialFinalCopy - can we do a partial final copy on this file (ie., is the
reparse format version > 4?)
ReparseBufferCorrupt - are we convinced that the buffer is corrupt (rather than just
being too new a version) Meaningful only if
the return value of the function is FALSE
Return Value:
TRUE if the buffer was decoded successfully, FALSE otherwise.
--*/
{
PSI_REPARSE_BUFFER sisReparseBuffer = (PSI_REPARSE_BUFFER)reparseBuffer->GenericReparseBuffer.DataBuffer;
LONGLONG Checksum = 0;
//
// First check to be sure that we understand this reparse point format version and
// that it has the correct size.
//
if (reparseBuffer->ReparseDataLength < sizeof(ULONG)) {
//
// The reparse buffer is to small to include a version number. We guarantee that
// no SIS version will ever produce such a reparse point, so it is corrupt.
//
if (NULL != ReparseBufferCorrupt) {
*ReparseBufferCorrupt = TRUE;
}
return FALSE;
}
if (sisReparseBuffer->ReparsePointFormatVersion < 4) {
//
// It's too old to be supported. Treat it as corrupt.
//
if (NULL != ReparseBufferCorrupt) {
*ReparseBufferCorrupt = TRUE;
}
return FALSE;
}
if (sisReparseBuffer->ReparsePointFormatVersion > SIS_REPARSE_BUFFER_FORMAT_VERSION) {
//
// This buffer is from a newer version of SIS than the filter. It is non-corrupt,
// but we don't understand it.
//
if (NULL != ReparseBufferCorrupt) {
*ReparseBufferCorrupt = FALSE;
}
return FALSE;
}
//
// Now check the checksum.
//
SipComputeChecksum(
sisReparseBuffer,
sizeof(SI_REPARSE_BUFFER) - sizeof sisReparseBuffer->Checksum,
&Checksum);
if (Checksum != sisReparseBuffer->Checksum.QuadPart) {
if (NULL != ReparseBufferCorrupt) {
*ReparseBufferCorrupt = TRUE;
}
return FALSE;
}
//
// Fill in the return values from the reparse point.
//
*CSid = sisReparseBuffer->CSid;
*LinkIndex = sisReparseBuffer->LinkIndex;
*LinkFileNtfsId = sisReparseBuffer->LinkFileNtfsId;
*CSFileNtfsId = sisReparseBuffer->CSFileNtfsId;
if (NULL != CSFileChecksum) {
*CSFileChecksum = sisReparseBuffer->CSChecksum;
}
if (NULL != EligibleForPartialFinalCopy) {
*EligibleForPartialFinalCopy = (sisReparseBuffer->ReparsePointFormatVersion > 4);
}
if (NULL != ReparseBufferCorrupt) {
*ReparseBufferCorrupt = FALSE;
}
return TRUE;
}
BOOLEAN
SipIndicesIntoReparseBuffer(
OUT PREPARSE_DATA_BUFFER reparseBuffer,
IN PCSID CSid,
IN PLINK_INDEX LinkIndex,
IN PLARGE_INTEGER CSFileNtfsId,
IN PLARGE_INTEGER LinkFileNtfsId,
IN PLONGLONG CSFileChecksum,
IN BOOLEAN EligibleForPartialFinalCopy)
/*++
Routine Description:
Given the information that goes into a SIS reparse buffer, construct the
buffer. The caller must provide a sufficiently large buffer, and is
responsible for filling in the ReparseDataLength field of the buffer
with a size that corresponds to the size of the buffer (note that this is
not EQUAL to the size of the buffer, because the meaning of this field
is that it gives the length of the buffer beyond the mandatory header
portion).
Arguments:
reparseBuffer - the buffer into which to write the reparse data
CSid
LinkIndex
CSFileNtfsId
LinkFileNtfsId
CSFileChecksum - the values to go into the reparse point
Return Value:
TRUE if the buffer was encoded successfully, FALSE otherwise.
--*/
{
PSI_REPARSE_BUFFER sisReparseBuffer = (PSI_REPARSE_BUFFER)reparseBuffer->GenericReparseBuffer.DataBuffer;
//
// Check that we've got enough space.
//
if (reparseBuffer->ReparseDataLength < sizeof(SI_REPARSE_BUFFER)) {
return FALSE;
}
//
// Fill in the NTFS part of the reparse buffer.
//
reparseBuffer->ReparseTag = IO_REPARSE_TAG_SIS;
reparseBuffer->Reserved = 0xcaf; //???
//
// Fill in SIS's part of the buffer.
//
if (EligibleForPartialFinalCopy) {
sisReparseBuffer->ReparsePointFormatVersion = SIS_REPARSE_BUFFER_FORMAT_VERSION;
} else {
//
// When we go to version 6 of the reparse buffer, EligibleForPartialFinalCopy should be
// built into the reparse point. For now, we'll just use a version 4 reparse point.
//
sisReparseBuffer->ReparsePointFormatVersion = 4;
}
sisReparseBuffer->Reserved = 0xb111b010;
sisReparseBuffer->CSid = *CSid;
sisReparseBuffer->LinkIndex = *LinkIndex;
sisReparseBuffer->LinkFileNtfsId = *LinkFileNtfsId;
sisReparseBuffer->CSFileNtfsId = *CSFileNtfsId;
sisReparseBuffer->CSChecksum = *CSFileChecksum;
//
// Compute the checksum.
//
sisReparseBuffer->Checksum.QuadPart = 0;
SipComputeChecksum(
sisReparseBuffer,
sizeof(SI_REPARSE_BUFFER) - sizeof sisReparseBuffer->Checksum,
&sisReparseBuffer->Checksum.QuadPart);
//
// Indicate the size.
//
reparseBuffer->ReparseDataLength = sizeof(SI_REPARSE_BUFFER);
return TRUE;
}
NTSTATUS
SipIntegerToBase36UnicodeString(
ULONG Value,
PUNICODE_STRING String)
/*++
Routine Description:
This does what RtlIntegerToUnicodeString(Value,36,String) would do if it
handled base 36. We use the same rules for digits as are normally used
in Hex: 0-9, followed by a-z. Note that we're intentionally using Arabic
numerals and English letters here rather than something localized because
this is intended to generate filenames that are never seen by users, and
are constant regardless of the language used on the machine.
Arguments:
Value - The ULONG to be converted into a base36 string
String - A pointer to a UNICODE string to receive the result
Return Value:
success or buffer overflow
--*/
{
ULONG numChars;
ULONG ValueCopy = Value;
ULONG currentCharacter;
// First, figure out the length by seeing how many times we can divide 36 into the value
for (numChars = 0; ValueCopy != 0; ValueCopy /= 36, numChars++) {
// No loop body
}
// Special case the value 0.
if (numChars == 0) {
ASSERT(Value == 0);
if (String->MaximumLength < sizeof(WCHAR))
return STATUS_BUFFER_OVERFLOW;
String->Buffer[0] = '0';
String->Length = sizeof(WCHAR);
return STATUS_SUCCESS;
}
// If the string is too short, quit now.
if (numChars * sizeof(WCHAR) > String->MaximumLength) {
return STATUS_BUFFER_OVERFLOW;
}
// Convert the string character-by-character starting at the lowest order (and so rightmost) "digit"
ValueCopy = Value;
for (currentCharacter = 0 ; currentCharacter < numChars; currentCharacter++) {
ULONG digit = ValueCopy % 36;
ASSERT(ValueCopy != 0);
if (digit < 10) {
String->Buffer[numChars - (currentCharacter + 1)] = (WCHAR)('0' + (ValueCopy % 36));
} else {
String->Buffer[numChars - (currentCharacter + 1)] = (WCHAR)('a' + ((ValueCopy % 36) - 10));
}
ValueCopy /= 36;
}
ASSERT(ValueCopy == 0);
// Fill in the string length, and we're done
String->Length = (USHORT)(numChars * sizeof(WCHAR));
return STATUS_SUCCESS;
}
NTSTATUS
SipIndexToFileName(
IN PDEVICE_EXTENSION deviceExtension,
IN PCSID CSid,
IN ULONG appendBytes,
IN BOOLEAN mayAllocate,
OUT PUNICODE_STRING fileName
)
/*++
Routine Description:
Given an index, returns the corresponding fully qualified file name.
Arguments:
deviceExtension - device extension
CSid - The id to convert
appendBytes - A number of bytes that must be left unused at the end of fileName
mayAllocate - May we allocate a new string, or do we have to live with what we have?
fileName - A pointer to a UNICODE string to receive the result
Return Value:
success or buffer overflow
--*/
{
NTSTATUS status;
USHORT stringMaxLength;
UNICODE_STRING GUIDString[1];
BOOLEAN allocatedBufferSpace = FALSE;
//
// We generate the filename as <common store path>\<guid>.sis, where <guid> is
// the standard striung representation of the GUID for the common store file (ie.,
// its CSid).
//
stringMaxLength = (USHORT)(deviceExtension->CommonStorePathname.Length +
INDEX_MAX_NUMERIC_STRING_LENGTH +
appendBytes);
if (mayAllocate && stringMaxLength > fileName->MaximumLength) {
fileName->Buffer = ExAllocatePoolWithTag(PagedPool, stringMaxLength, ' siS');
if (!fileName->Buffer) {
return STATUS_INSUFFICIENT_RESOURCES;
}
allocatedBufferSpace = TRUE;
fileName->MaximumLength = stringMaxLength;
} else if (fileName->MaximumLength < stringMaxLength) {
return STATUS_BUFFER_OVERFLOW;
}
RtlCopyUnicodeString(fileName,&deviceExtension->CommonStorePathname);
ASSERT(fileName->Length < fileName->MaximumLength);
ASSERT(fileName->Length == deviceExtension->CommonStorePathname.Length);
status = RtlStringFromGUID(CSid,GUIDString);
if (!NT_SUCCESS(status)) {
SIS_MARK_POINT_ULONG(status);
goto Error;
}
//
// Get rid of the leading and trailing curly braces in the GUID name.
//
ASSERT(GUIDString->Buffer[0] == '{' && GUIDString->Buffer[(GUIDString->Length/sizeof(WCHAR)) - 1] == '}');
GUIDString->Buffer++;
GUIDString->Length -= 2 * sizeof(WCHAR);
status = RtlAppendUnicodeStringToString(
fileName,
GUIDString);
//
// Just for safety, undo the hacking that we did on the GUID string before freeing it.
//
GUIDString->Buffer--;
GUIDString->Length += 2 * sizeof(WCHAR);
RtlFreeUnicodeString(GUIDString);
if (!NT_SUCCESS(status)) {
SIS_MARK_POINT_ULONG(status);
goto Error;
}
status = RtlAppendUnicodeToString(fileName,L".sis");
if (!NT_SUCCESS(status)) {
SIS_MARK_POINT_ULONG(status);
goto Error;
}
return STATUS_SUCCESS;
Error:
if (allocatedBufferSpace) {
ExFreePool(fileName->Buffer);
fileName->Buffer = NULL;
}
return status;
}
BOOLEAN
SipFileNameToIndex(
IN PUNICODE_STRING fileName,
OUT PCSID CSid)
/*++
Routine Description:
Given a common store file name, returns the corresponding index. The
file name must be in the format generated by SipIndexToFileName().
Arguments:
fileName - A pointer to a UNICODE string containing the file name.
CSid - A pointer to a CSID to receive the result.
Return Value:
TRUE if successful, else FALSE
--*/
{
UNICODE_STRING substring[1];
NTSTATUS status;
#define BUFSIZE 42
WCHAR buffer[BUFSIZE];
//
// Format: "<guid>.sis", where <guid> is the standard string representation of the
// csid guid with the curly braces stripped off.
//
if (fileName->Length <= 4 * sizeof(WCHAR)) {
//
// It doesn't end in .sis, ignore it.
//
return FALSE;
}
substring->Buffer = buffer;
substring->Buffer[0] = L'{';
substring->Length = sizeof(WCHAR);
substring->MaximumLength = BUFSIZE * sizeof(WCHAR);
status = RtlAppendUnicodeStringToString(substring, fileName);
if (!NT_SUCCESS(status)) {
SIS_MARK_POINT_ULONG(status);
return FALSE;
}
substring->Length = substring->Length - 3 * sizeof(WCHAR);
substring->Buffer[(substring->Length - 1) / sizeof(WCHAR)] = L'}';
status = RtlGUIDFromString(substring, CSid);
if (!NT_SUCCESS(status)) {
SIS_MARK_POINT_ULONG(status);
return FALSE;
}
return TRUE;
}
NTSTATUS
SipOpenMaxIndexFile(
IN OUT PDEVICE_EXTENSION deviceExtension,
IN BOOLEAN create
)
/*++
Routine Description:
Open the MaxIndex file for a given volume. Must be called in the
PsInitialSystemProcess context.
Arguments:
deviceExtension - For the volume on which to open the MaxIndex file
create - May we create the file, or must it already exist?
Return Value:
status of the open
--*/
{
OBJECT_ATTRIBUTES Obja;
UNICODE_STRING fileName;
NTSTATUS status;
IO_STATUS_BLOCK Iosb;
ASSERT(deviceExtension->MaxAllocatedIndex.QuadPart == 0 || create);
fileName.Buffer = ExAllocatePoolWithTag(
NonPagedPool,
deviceExtension->CommonStorePathname.Length + 8 * sizeof(WCHAR),
' siS');
if (!fileName.Buffer) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto done;
}
fileName.MaximumLength = deviceExtension->CommonStorePathname.Length + 8 * sizeof(WCHAR);
fileName.Length = 0;
RtlCopyUnicodeString(&fileName,&deviceExtension->CommonStorePathname);
ASSERT(fileName.Length == deviceExtension->CommonStorePathname.Length);
status = RtlAppendUnicodeToString(&fileName,L"MaxIndex");
if (!NT_SUCCESS(status)) {
goto done;
}
InitializeObjectAttributes(
&Obja,
&fileName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
status = NtCreateFile(
&deviceExtension->IndexHandle,
GENERIC_READ|GENERIC_WRITE,
&Obja,
&Iosb,
NULL, // Allocation Size
0, // File Attributes
FILE_SHARE_READ,
create ? FILE_OVERWRITE_IF : FILE_OPEN,
FILE_WRITE_THROUGH,
NULL, // EA Buffer
0); // EA Length
done:
if (NULL != fileName.Buffer) {
ExFreePool(fileName.Buffer);
}
return status;
}
VOID
SipAllocateIndices(
IN PVOID Parameter)
/*++
Routine Description:
This is a worker thread routine that allocates a new chunk of indices
from the index file. Essentially, opens the index file and reads
the old value if necessary. Then, it adds the chunk size onto the
max allocated index, and writes the new value back into the file
write through. When the write completes, set the event and exit.
Arguments:
parameter - a PSI_ALLOCATE_INDICES.
Return Value:
void
--*/
{
PSI_ALLOCATE_INDICES allocateRequest = Parameter;
KIRQL OldIrql;
PDEVICE_EXTENSION deviceExtension = allocateRequest->deviceExtension;
NTSTATUS status;
IO_STATUS_BLOCK Iosb;
LARGE_INTEGER ByteOffset;
#if DBG
// Just to ensure that we don't have more then one allocator running at once, we check
// that the allocation request is really == TRUE (rather than just != 0), and then set
// it to 2.
KeAcquireSpinLock(deviceExtension->IndexSpinLock, &OldIrql);
ASSERT(deviceExtension->IndexAllocationInProgress == TRUE);
deviceExtension->IndexAllocationInProgress = 2;
KeReleaseSpinLock(deviceExtension->IndexSpinLock, OldIrql);
#endif // DBG
if (deviceExtension->IndexHandle == NULL) {
status = SipCreateEvent(
SynchronizationEvent,
&deviceExtension->IndexFileEventHandle,
&deviceExtension->IndexFileEvent);
if (!NT_SUCCESS(status)) {
SIS_MARK_POINT_ULONG(status);
goto done;
}
status = SipOpenMaxIndexFile(
deviceExtension,
(BOOLEAN) (deviceExtension->MaxAllocatedIndex.QuadPart != 0));
if (!NT_SUCCESS(status)) {
//
// We can't open the MaxIndex file. It was probably deleted
// or something. Kick off a volume check to rebuild it.
//
SIS_MARK_POINT_ULONG(status);
KeAcquireSpinLock(deviceExtension->FlagsLock, &OldIrql);
deviceExtension->Flags |= SIP_EXTENSION_FLAG_CORRUPT_MAXINDEX;
KeReleaseSpinLock(deviceExtension->FlagsLock, OldIrql);
status = STATUS_CORRUPT_SYSTEM_FILE;
//
// Do a volume check only if this is the first attempt.
//
if (deviceExtension->MaxAllocatedIndex.QuadPart == 0) {
SipCheckVolume(deviceExtension);
}
goto done;
}
}
if (deviceExtension->MaxAllocatedIndex.QuadPart == 0) {
ByteOffset.QuadPart = 0;
status = ZwReadFile(
deviceExtension->IndexHandle,
deviceExtension->IndexFileEventHandle,
NULL, // APC routine
NULL, // APC Context
&Iosb,
&deviceExtension->MaxAllocatedIndex,
sizeof(deviceExtension->MaxAllocatedIndex),
&ByteOffset,
NULL); // Key
if (status == STATUS_PENDING) {
status = KeWaitForSingleObject(deviceExtension->IndexFileEvent,Executive,KernelMode,FALSE,NULL);
ASSERT(status == STATUS_SUCCESS);
status = Iosb.Status;
}
if (!NT_SUCCESS(status) || Iosb.Information != sizeof(LONGLONG) || deviceExtension->MaxAllocatedIndex.Check) {
#if DBG
DbgPrint(
"SIS: SipAllocateIndices: ZwReadFile of MaxIndex file failed, wrong length or invalid value, 0x%x, %d\n",
status,Iosb.Information);
#endif // DBG
ZwClose(deviceExtension->IndexHandle);
deviceExtension->IndexHandle = NULL;
KeAcquireSpinLock(deviceExtension->FlagsLock, &OldIrql);
deviceExtension->Flags |= SIP_EXTENSION_FLAG_CORRUPT_MAXINDEX;
KeReleaseSpinLock(deviceExtension->FlagsLock, OldIrql);
status = STATUS_CORRUPT_SYSTEM_FILE;
SipCheckVolume(deviceExtension);
goto done;
}
deviceExtension->MaxUsedIndex = deviceExtension->MaxAllocatedIndex;
}
deviceExtension->MaxAllocatedIndex.QuadPart += 1000; // 1000 is pretty arbitrary. We can do better.
ByteOffset.QuadPart = 0;
status = ZwWriteFile(
deviceExtension->IndexHandle,
deviceExtension->IndexFileEventHandle,
NULL, // APC routine
NULL, // APC context
&Iosb,
&deviceExtension->MaxAllocatedIndex,
sizeof(deviceExtension->MaxAllocatedIndex),
&ByteOffset,
NULL); // key
if (status == STATUS_PENDING) {
status = KeWaitForSingleObject(deviceExtension->IndexFileEvent,Executive,KernelMode,FALSE,NULL);
ASSERT(status == STATUS_SUCCESS);
status = Iosb.Status;
}
if (!NT_SUCCESS(status)) {
// The write failed. Back out the allocation.
deviceExtension->MaxAllocatedIndex.QuadPart -= 1000;
#if DBG
DbgPrint("SIS: SipAllocateIndices: writing MaxIndex file failed, 0x%x\n",status);
#endif // DBG
}
done:
KeAcquireSpinLock(deviceExtension->IndexSpinLock, &OldIrql);
deviceExtension->IndexStatus = status;
deviceExtension->IndexAllocationInProgress = FALSE;
KeSetEvent(deviceExtension->IndexEvent, 0, FALSE); // we may no longer touch allocationRequest after this set
KeReleaseSpinLock(deviceExtension->IndexSpinLock, OldIrql);
return;
}
NTSTATUS
SipAllocateIndex(
IN PDEVICE_EXTENSION DeviceExtension,
OUT PLINK_INDEX Index)
/*++
Routine Description:
Allocate a new LINK_INDEX. If there are indices that have been reserved from the
file but not yet allocated, we can just grab one and return it. Otherwise, we
need to wait for a new index allocation. If one is not already in progress, we
start it and wait for it to complete.
Arguments:
deviceExtension - for the volume on which we're to allocate the index.
Index - returns the new index
Return Value:
status of the allocation.
--*/
{
KIRQL OldIrql;
BOOLEAN StartAllocator;
SI_ALLOCATE_INDICES AllocateRequest[1];
NTSTATUS status;
if (DeviceExtension->Flags & SIP_EXTENSION_FLAG_CORRUPT_MAXINDEX) {
return STATUS_CORRUPT_SYSTEM_FILE;
}
KeAcquireSpinLock(DeviceExtension->IndexSpinLock, &OldIrql);
while (TRUE) {
if (DeviceExtension->MaxAllocatedIndex.QuadPart > DeviceExtension->MaxUsedIndex.QuadPart) {
DeviceExtension->MaxUsedIndex.QuadPart++;
*Index = DeviceExtension->MaxUsedIndex;
KeReleaseSpinLock(DeviceExtension->IndexSpinLock, OldIrql);
return STATUS_SUCCESS;
}
// There are no free indices left, we have to block.
if (!DeviceExtension->IndexAllocationInProgress) {
StartAllocator = TRUE;
DeviceExtension->IndexAllocationInProgress = TRUE;
// Stop anyone from passing the barrier until the allocator runs.
KeClearEvent(DeviceExtension->IndexEvent);
} else {
StartAllocator = FALSE;
}
KeReleaseSpinLock(DeviceExtension->IndexSpinLock, OldIrql);
if (StartAllocator) {
ExInitializeWorkItem(AllocateRequest->workQueueItem, SipAllocateIndices, AllocateRequest);
AllocateRequest->deviceExtension = DeviceExtension;
ExQueueWorkItem(AllocateRequest->workQueueItem, CriticalWorkQueue);
}
status = KeWaitForSingleObject(DeviceExtension->IndexEvent, Executive, KernelMode, FALSE, NULL);
ASSERT(status == STATUS_SUCCESS);
if ((status != STATUS_SUCCESS) && !StartAllocator) {
// The reason that we check StartAllocator here is because the allocation request is
// on our stack, and we really can't return until the work item is completed. (Of course,
// the KeWaitForSingleObject should never fail in the first place...)
return status;
}
KeAcquireSpinLock(DeviceExtension->IndexSpinLock, &OldIrql);
if (!NT_SUCCESS(DeviceExtension->IndexStatus)) {
status = DeviceExtension->IndexStatus;
KeReleaseSpinLock(DeviceExtension->IndexSpinLock, OldIrql);
return status;
}
}
}
NTSTATUS
SipGetMaxUsedIndex(
IN PDEVICE_EXTENSION DeviceExtension,
OUT PLINK_INDEX Index)
/*++
Routine Description:
Return a number that's at least as big as the largest LINK_INDEX ever allocated
on this volume. Note that if it looks like we don't have any indices available
we'll kick the index allocator, because otherwise we can't be sure that the
index values are valid (they may have never been read for this volume).
Arguments:
deviceExtension - for the volume we're considering
Index - returns new index
Return Value:
status of the check. *Index is meaningful iff NT_SUCCESS(return value).
--*/
{
KIRQL OldIrql;
BOOLEAN StartAllocator;
SI_ALLOCATE_INDICES AllocateRequest[1];
NTSTATUS status;
KeAcquireSpinLock(DeviceExtension->IndexSpinLock, &OldIrql);
while (TRUE) {
if (DeviceExtension->MaxAllocatedIndex.QuadPart > DeviceExtension->MaxUsedIndex.QuadPart) {
*Index = DeviceExtension->MaxUsedIndex;
KeReleaseSpinLock(DeviceExtension->IndexSpinLock, OldIrql);
return STATUS_SUCCESS;
}
// There are no free indices left, we have to block.
if (!DeviceExtension->IndexAllocationInProgress) {
StartAllocator = TRUE;
DeviceExtension->IndexAllocationInProgress = TRUE;
// Stop anyone from passing the barrier until the allocator runs.
KeClearEvent(DeviceExtension->IndexEvent);
} else {
StartAllocator = FALSE;
}
KeReleaseSpinLock(DeviceExtension->IndexSpinLock, OldIrql);
if (StartAllocator) {
ExInitializeWorkItem(AllocateRequest->workQueueItem, SipAllocateIndices, AllocateRequest);
AllocateRequest->deviceExtension = DeviceExtension;
ExQueueWorkItem(AllocateRequest->workQueueItem, CriticalWorkQueue);
}
status = KeWaitForSingleObject(DeviceExtension->IndexEvent, Executive, KernelMode, FALSE, NULL);
ASSERT(status == STATUS_SUCCESS);
if ((status != STATUS_SUCCESS) && !StartAllocator) {
// The reason that we check StartAllocator here is because the allocation request is
// on our stack, and we really can't return until the work item is completed. (Of course,
// the KeWaitForSingleObject should never fail in the first place...)
return status;
}
KeAcquireSpinLock(DeviceExtension->IndexSpinLock, &OldIrql);
if (!NT_SUCCESS(DeviceExtension->IndexStatus)) {
status = DeviceExtension->IndexStatus;
KeReleaseSpinLock(DeviceExtension->IndexSpinLock, OldIrql);
return status;
}
}
}
NTSTATUS
SipAssureMaxIndexFileOpen(
IN PDEVICE_EXTENSION deviceExtension)
{
NTSTATUS status;
KIRQL OldIrql;
LINK_INDEX uselessIndex;
//
// Make sure that the MaxIndex file is already opened. We need to
// do this here to avoid a deadlock if someone
// tries to do a copyfile with MaxIndex as the source, which would
// otherwise deadlock. If things are messed up, this might kick off
// a volume check, but we should still fail the open.
//
if (deviceExtension->IndexHandle != NULL) {
//
// The file's already open, no need to do any work.
//
return STATUS_SUCCESS;
}
//
// The index file isn't open. Rather than trying to open it directly,
// we avoid races by just calling the index allocator. We'll throw away
// the index we get back, but they're plentiful so it's not much of a
// problem.
//
status = SipAllocateIndex(deviceExtension, &uselessIndex);
if (!NT_SUCCESS(status)) {
BOOLEAN volumeCheckPending;
SIS_MARK_POINT_ULONG(status);
//
// If we're in a volume check, transmute the error to STATUS_RETRY on the
// theory that the volume check will rebuild the MaxIndex file. If not,
// then just leave it alone.
//
KeAcquireSpinLock(deviceExtension->FlagsLock, &OldIrql);
volumeCheckPending = (deviceExtension->Flags & SIP_EXTENSION_FLAG_VCHECK_PENDING) ? TRUE : FALSE;
KeReleaseSpinLock(deviceExtension->FlagsLock, OldIrql);
if (volumeCheckPending) {
SIS_MARK_POINT();
status = STATUS_RETRY;
}
return status;
}
return status;
}