Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115/* * ReactOS kernel * Copyright (C) 2011-2012 ReactOS Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: drivers/filesystem/mountmgr/mountmgr.c * PURPOSE: Mount Manager - remote/local database handler * PROGRAMMER: Pierre Schweitzer (pierre.schweitzer@reactos.org) */
#include "mntmgr.h"
#define NDEBUG#include <debug.h>
PWSTR DatabasePath = L"\\Registry\\Machine\\System\\MountedDevices";PWSTR OfflinePath = L"\\Registry\\Machine\\System\\MountedDevices\\Offline";
UNICODE_STRING RemoteDatabase = RTL_CONSTANT_STRING(L"\\System Volume Information\\MountPointManagerRemoteDatabase");
/* * @implemented */LONGGetRemoteDatabaseSize(IN HANDLE Database){ NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; FILE_STANDARD_INFORMATION StandardInfo;
/* Just query the size */ Status = ZwQueryInformationFile(Database, &IoStatusBlock, &StandardInfo, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation); if (NT_SUCCESS(Status)) { return StandardInfo.EndOfFile.LowPart; }
return 0;}
/* * @implemented */NTSTATUSAddRemoteDatabaseEntry(IN HANDLE Database, IN PDATABASE_ENTRY Entry){ LARGE_INTEGER Size; IO_STATUS_BLOCK IoStatusBlock;
/* Get size to append data */ Size.QuadPart = GetRemoteDatabaseSize(Database);
return ZwWriteFile(Database, NULL, NULL, NULL, &IoStatusBlock, Entry, Entry->EntrySize, &Size, NULL);}
/* * @implemented */NTSTATUSCloseRemoteDatabase(IN HANDLE Database){ return ZwClose(Database);}
/* * @implemented */NTSTATUSTruncateRemoteDatabase(IN HANDLE Database, IN LONG NewSize){ NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; FILE_END_OF_FILE_INFORMATION EndOfFile; FILE_ALLOCATION_INFORMATION Allocation;
EndOfFile.EndOfFile.QuadPart = NewSize; Allocation.AllocationSize.QuadPart = NewSize;
/* First set EOF */ Status = ZwSetInformationFile(Database, &IoStatusBlock, &EndOfFile, sizeof(FILE_END_OF_FILE_INFORMATION), FileEndOfFileInformation); if (NT_SUCCESS(Status)) { /* And then, properly set allocation information */ Status = ZwSetInformationFile(Database, &IoStatusBlock, &Allocation, sizeof(FILE_ALLOCATION_INFORMATION), FileAllocationInformation); }
return Status;}
/* * @implemented */PDATABASE_ENTRYGetRemoteDatabaseEntry(IN HANDLE Database, IN LONG StartingOffset){ NTSTATUS Status; ULONG EntrySize; PDATABASE_ENTRY Entry; LARGE_INTEGER ByteOffset; IO_STATUS_BLOCK IoStatusBlock;
/* Get the entry at the given position */ ByteOffset.QuadPart = StartingOffset; Status = ZwReadFile(Database, NULL, NULL, NULL, &IoStatusBlock, &EntrySize, sizeof(EntrySize), &ByteOffset, NULL); if (!NT_SUCCESS(Status)) { return NULL; }
/* If entry doesn't exist, truncate database */ if (!EntrySize) { TruncateRemoteDatabase(Database, StartingOffset); return NULL; }
/* Allocate the entry */ Entry = AllocatePool(EntrySize); if (!Entry) { return NULL; }
/* Effectively read the entry */ Status = ZwReadFile(Database, NULL, NULL, NULL, &IoStatusBlock, Entry, EntrySize, &ByteOffset, NULL); /* If it fails or returns inconsistent data, drop it (= truncate) */ if (!NT_SUCCESS(Status) || (IoStatusBlock.Information != EntrySize) || (EntrySize < sizeof(DATABASE_ENTRY)) ) { TruncateRemoteDatabase(Database, StartingOffset); FreePool(Entry); return NULL; }
/* Validate entry */ if (MAX(Entry->SymbolicNameOffset + Entry->SymbolicNameLength, Entry->UniqueIdOffset + Entry->UniqueIdLength) > (LONG)EntrySize) { TruncateRemoteDatabase(Database, StartingOffset); FreePool(Entry); return NULL; }
return Entry;}
/* * @implemented */NTSTATUSWriteRemoteDatabaseEntry(IN HANDLE Database, IN LONG Offset, IN PDATABASE_ENTRY Entry){ NTSTATUS Status; LARGE_INTEGER ByteOffset; IO_STATUS_BLOCK IoStatusBlock;
ByteOffset.QuadPart = Offset; Status = ZwWriteFile(Database, NULL, NULL, NULL, &IoStatusBlock, Entry, Entry->EntrySize, &ByteOffset, NULL); if (NT_SUCCESS(Status)) { if (IoStatusBlock.Information < Entry->EntrySize) { Status = STATUS_INSUFFICIENT_RESOURCES; } }
return Status;}
/* * @implemented */NTSTATUSDeleteRemoteDatabaseEntry(IN HANDLE Database, IN LONG StartingOffset){ ULONG EndSize; PVOID TmpBuffer; NTSTATUS Status; ULONG DatabaseSize; PDATABASE_ENTRY Entry; IO_STATUS_BLOCK IoStatusBlock; LARGE_INTEGER EndEntriesOffset;
/* First, get database size */ DatabaseSize = GetRemoteDatabaseSize(Database); if (!DatabaseSize) { return STATUS_INVALID_PARAMETER; }
/* Then, get the entry to remove */ Entry = GetRemoteDatabaseEntry(Database, StartingOffset); if (!Entry) { return STATUS_INVALID_PARAMETER; }
/* Validate parameters: ensure we won't get zero or negative size */ if (Entry->EntrySize + StartingOffset >= DatabaseSize) { /* If we get invalid parameters, truncate the whole database * starting the wrong entry. We can't rely on the rest */ FreePool(Entry); return TruncateRemoteDatabase(Database, StartingOffset); }
/* Now, get the size of the remaining entries (those after the one to remove) */ EndSize = DatabaseSize - Entry->EntrySize - StartingOffset; /* Allocate a buffer big enough to hold them */ TmpBuffer = AllocatePool(EndSize); if (!TmpBuffer) { FreePool(Entry); return STATUS_INSUFFICIENT_RESOURCES; }
/* Get the offset of the entry right after the one to delete */ EndEntriesOffset.QuadPart = Entry->EntrySize + StartingOffset; /* We don't need the entry any more */ FreePool(Entry);
/* Read the ending entries */ Status = ZwReadFile(Database, NULL, NULL, NULL, &IoStatusBlock, TmpBuffer, EndSize, &EndEntriesOffset, NULL); if (!NT_SUCCESS(Status)) { FreePool(TmpBuffer); return Status; }
/* Ensure nothing went wrong - we don't want to corrupt the DB */ if (IoStatusBlock.Information != EndSize) { FreePool(TmpBuffer); return STATUS_INVALID_PARAMETER; }
/* Remove the entry */ Status = TruncateRemoteDatabase(Database, StartingOffset + EndSize); if (!NT_SUCCESS(Status)) { FreePool(TmpBuffer); return Status; }
/* Now, shift the ending entries to erase the entry */ EndEntriesOffset.QuadPart = StartingOffset; Status = ZwWriteFile(Database, NULL, NULL, NULL, &IoStatusBlock, TmpBuffer, EndSize, &EndEntriesOffset, NULL);
FreePool(TmpBuffer);
return Status;}
/* * @implemented */NTSTATUSNTAPIDeleteFromLocalDatabaseRoutine(IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext){ PMOUNTDEV_UNIQUE_ID UniqueId = Context;
UNREFERENCED_PARAMETER(ValueType); UNREFERENCED_PARAMETER(EntryContext);
/* Ensure it matches, and delete */ if ((UniqueId->UniqueIdLength == ValueLength) && (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) == ValueLength)) { RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, DatabasePath, ValueName); }
return STATUS_SUCCESS;}
/* * @implemented */VOIDDeleteFromLocalDatabase(IN PUNICODE_STRING SymbolicLink, IN PMOUNTDEV_UNIQUE_ID UniqueId){ RTL_QUERY_REGISTRY_TABLE QueryTable[2];
RtlZeroMemory(QueryTable, sizeof(QueryTable)); QueryTable[0].QueryRoutine = DeleteFromLocalDatabaseRoutine; QueryTable[0].Name = SymbolicLink->Buffer;
RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, DatabasePath, QueryTable, UniqueId, NULL);}
/* * @implemented */NTSTATUSWaitForRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension){ NTSTATUS Status; LARGE_INTEGER Timeout;
/* Wait for 7 minutes */ Timeout.QuadPart = 0xFA0A1F00; Status = KeWaitForSingleObject(&(DeviceExtension->RemoteDatabaseLock), Executive, KernelMode, FALSE, &Timeout); if (Status != STATUS_TIMEOUT) { return Status; }
return STATUS_IO_TIMEOUT;}
/* * @implemented */VOIDReleaseRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension){ KeReleaseSemaphore(&(DeviceExtension->RemoteDatabaseLock), IO_NO_INCREMENT, 1, FALSE);}
/* * @implemented */NTSTATUSNTAPIQueryUniqueIdQueryRoutine(IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext){ PMOUNTDEV_UNIQUE_ID IntUniqueId; PMOUNTDEV_UNIQUE_ID * UniqueId;
UNREFERENCED_PARAMETER(ValueName); UNREFERENCED_PARAMETER(ValueType); UNREFERENCED_PARAMETER(EntryContext);
/* Sanity check */ if (ValueLength >= 0x10000) { return STATUS_SUCCESS; }
/* Allocate the Unique ID */ IntUniqueId = AllocatePool(sizeof(UniqueId) + ValueLength); if (IntUniqueId) { /* Copy data & return */ IntUniqueId->UniqueIdLength = (USHORT)ValueLength; RtlCopyMemory(&(IntUniqueId->UniqueId), ValueData, ValueLength);
UniqueId = Context; *UniqueId = IntUniqueId; }
return STATUS_SUCCESS;}
/* * @implemented */NTSTATUSQueryUniqueIdFromMaster(IN PDEVICE_EXTENSION DeviceExtension, IN PUNICODE_STRING SymbolicName, OUT PMOUNTDEV_UNIQUE_ID * UniqueId){ NTSTATUS Status; PDEVICE_INFORMATION DeviceInformation; RTL_QUERY_REGISTRY_TABLE QueryTable[2];
/* Query the unique ID */ RtlZeroMemory(QueryTable, sizeof(QueryTable)); QueryTable[0].QueryRoutine = QueryUniqueIdQueryRoutine; QueryTable[0].Name = SymbolicName->Buffer;
*UniqueId = NULL; RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, DatabasePath, QueryTable, UniqueId, NULL); /* Unique ID found, no need to go farther */ if (*UniqueId) { return STATUS_SUCCESS; }
/* Otherwise, find associate device information */ Status = FindDeviceInfo(DeviceExtension, SymbolicName, FALSE, &DeviceInformation); if (!NT_SUCCESS(Status)) { return Status; }
*UniqueId = AllocatePool(DeviceInformation->UniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID)); if (!*UniqueId) { return STATUS_INSUFFICIENT_RESOURCES; }
/* Return this unique ID (better than nothing) */ (*UniqueId)->UniqueIdLength = DeviceInformation->UniqueId->UniqueIdLength; RtlCopyMemory(&((*UniqueId)->UniqueId), &(DeviceInformation->UniqueId->UniqueId), (*UniqueId)->UniqueIdLength);
return STATUS_SUCCESS;}
/* * @implemented */NTSTATUSWriteUniqueIdToMaster(IN PDEVICE_EXTENSION DeviceExtension, IN PDATABASE_ENTRY DatabaseEntry){ NTSTATUS Status; PWCHAR SymbolicName; PLIST_ENTRY NextEntry; UNICODE_STRING SymbolicString; PDEVICE_INFORMATION DeviceInformation;
/* Create symbolic name from database entry */ SymbolicName = AllocatePool(DatabaseEntry->SymbolicNameLength + sizeof(WCHAR)); if (!SymbolicName) { return STATUS_INSUFFICIENT_RESOURCES; }
RtlCopyMemory(SymbolicName, (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset), DatabaseEntry->SymbolicNameLength); SymbolicName[DatabaseEntry->SymbolicNameLength / sizeof(WCHAR)] = UNICODE_NULL;
/* Associate the unique ID with the name from remote database */ Status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, DatabasePath, SymbolicName, REG_BINARY, (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), DatabaseEntry->UniqueIdLength); FreePool(SymbolicName);
/* Reget symbolic name */ SymbolicString.Length = DatabaseEntry->SymbolicNameLength; SymbolicString.MaximumLength = DatabaseEntry->SymbolicNameLength; SymbolicString.Buffer = (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset);
/* Find the device using this unique ID */ for (NextEntry = DeviceExtension->DeviceListHead.Flink; NextEntry != &(DeviceExtension->DeviceListHead); NextEntry = NextEntry->Flink) { DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
if (DeviceInformation->UniqueId->UniqueIdLength != DatabaseEntry->UniqueIdLength) { continue; }
if (RtlCompareMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), DeviceInformation->UniqueId->UniqueId, DatabaseEntry->UniqueIdLength) == DatabaseEntry->UniqueIdLength) { break; } }
/* If found, create a mount point */ if (NextEntry != &(DeviceExtension->DeviceListHead)) { MountMgrCreatePointWorker(DeviceExtension, &SymbolicString, &(DeviceInformation->DeviceName)); }
return Status;}
/* * @implemented */VOIDNTAPIReconcileThisDatabaseWithMasterWorker(IN PVOID Parameter){ ULONG Offset; NTSTATUS Status; PFILE_OBJECT FileObject; PDEVICE_OBJECT DeviceObject; PMOUNTDEV_UNIQUE_ID UniqueId; PDATABASE_ENTRY DatabaseEntry; HANDLE DatabaseHandle, Handle; IO_STATUS_BLOCK IoStatusBlock; OBJECT_ATTRIBUTES ObjectAttributes; PDEVICE_INFORMATION ListDeviceInfo; PLIST_ENTRY Entry, EntryInfo, NextEntry; PASSOCIATED_DEVICE_ENTRY AssociatedDevice; BOOLEAN HardwareErrors, Restart, FailedFinding; WCHAR FileNameBuffer[0x8], SymbolicNameBuffer[100]; UNICODE_STRING ReparseFile, FileName, SymbolicName, VolumeName; FILE_REPARSE_POINT_INFORMATION ReparsePointInformation, SavedReparsePointInformation; PDEVICE_EXTENSION DeviceExtension = ((PRECONCILE_WORK_ITEM_CONTEXT)Parameter)->DeviceExtension; PDEVICE_INFORMATION DeviceInformation = ((PRECONCILE_WORK_ITEM_CONTEXT)Parameter)->DeviceInformation;
/* We're unloading, do nothing */ if (Unloading) { return; }
/* Lock remote DB */ if (!NT_SUCCESS(WaitForRemoteDatabaseSemaphore(DeviceExtension))) { return; }
/* Recheck for unloading */ if (Unloading) { goto ReleaseRDS; }
/* Find the DB to reconcile */ KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL); for (Entry = DeviceExtension->DeviceListHead.Flink; Entry != &DeviceExtension->DeviceListHead; Entry = Entry->Flink) { ListDeviceInfo = CONTAINING_RECORD(Entry, DEVICE_INFORMATION, DeviceListEntry); if (ListDeviceInfo == DeviceInformation) { break; } }
/* If not found, or if removable, bail out */ if (Entry == &DeviceExtension->DeviceListHead || DeviceInformation->Removable) { KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); goto ReleaseRDS; }
/* Get our device object */ Status = IoGetDeviceObjectPointer(&ListDeviceInfo->DeviceName, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject); if (!NT_SUCCESS(Status)) { KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); goto ReleaseRDS; }
/* Mark mounted only if not unloading */ if (!(DeviceObject->Flags & DO_UNLOAD_PENDING)) { InterlockedExchangeAdd(&ListDeviceInfo->MountState, 1); }
ObDereferenceObject(FileObject);
/* Force default: no DB, and need for reconcile */ DeviceInformation->NeedsReconcile = TRUE; DeviceInformation->NoDatabase = TRUE; FailedFinding = FALSE;
/* Remove any associated device that refers to the DB to reconcile */ for (Entry = DeviceExtension->DeviceListHead.Flink; Entry != &DeviceExtension->DeviceListHead; Entry = Entry->Flink) { ListDeviceInfo = CONTAINING_RECORD(Entry, DEVICE_INFORMATION, DeviceListEntry);
EntryInfo = ListDeviceInfo->AssociatedDevicesHead.Flink; while (EntryInfo != &ListDeviceInfo->AssociatedDevicesHead) { AssociatedDevice = CONTAINING_RECORD(EntryInfo, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry); NextEntry = EntryInfo->Flink;
if (AssociatedDevice->DeviceInformation == DeviceInformation) { RemoveEntryList(&AssociatedDevice->AssociatedDevicesEntry); FreePool(AssociatedDevice->String.Buffer); FreePool(AssociatedDevice); }
EntryInfo = NextEntry; } }
/* Open the remote database */ DatabaseHandle = OpenRemoteDatabase(DeviceInformation, FALSE);
/* Prepare a string with reparse point index */ ReparseFile.Length = 0; ReparseFile.MaximumLength = DeviceInformation->DeviceName.Length + ReparseIndex.Length + sizeof(UNICODE_NULL); ReparseFile.Buffer = AllocatePool(ReparseFile.MaximumLength); if (!ReparseFile.Buffer) { if (DatabaseHandle != 0) { CloseRemoteDatabase(DatabaseHandle); } KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); goto ReleaseRDS; }
RtlAppendUnicodeStringToString(&ReparseFile, &DeviceInformation->DeviceName); RtlAppendUnicodeStringToString(&ReparseFile, &ReparseIndex); ReparseFile.Buffer[ReparseFile.Length / sizeof(WCHAR)] = UNICODE_NULL;
InitializeObjectAttributes(&ObjectAttributes, &ReparseFile, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
/* Open reparse point directory */ HardwareErrors = IoSetThreadHardErrorMode(FALSE); Status = ZwOpenFile(&Handle, FILE_GENERIC_READ, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_ALERT); IoSetThreadHardErrorMode(HardwareErrors);
FreePool(ReparseFile.Buffer);
if (!NT_SUCCESS(Status)) { if (DatabaseHandle != 0) { TruncateRemoteDatabase(DatabaseHandle, 0); CloseRemoteDatabase(DatabaseHandle); } KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); goto ReleaseRDS; }
/* Query reparse point information * We only pay attention to mout point */ RtlZeroMemory(FileNameBuffer, sizeof(FileNameBuffer)); FileName.Buffer = FileNameBuffer; FileName.Length = sizeof(FileNameBuffer); FileName.MaximumLength = sizeof(FileNameBuffer); ((PULONG)FileNameBuffer)[0] = IO_REPARSE_TAG_MOUNT_POINT; Status = ZwQueryDirectoryFile(Handle, NULL, NULL, NULL, &IoStatusBlock, &ReparsePointInformation, sizeof(FILE_REPARSE_POINT_INFORMATION), FileReparsePointInformation, TRUE, &FileName, FALSE); if (!NT_SUCCESS(Status)) { ZwClose(Handle); if (DatabaseHandle != 0) { TruncateRemoteDatabase(DatabaseHandle, 0); CloseRemoteDatabase(DatabaseHandle); } KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); goto ReleaseRDS; }
/* If we failed to open the remote DB previously, * retry this time allowing migration (and thus, creation if required) */ if (DatabaseHandle == 0) { DatabaseHandle = OpenRemoteDatabase(DeviceInformation, TRUE); if (DatabaseHandle == 0) { KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); goto ReleaseRDS; } }
KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
/* Reset all the references to our DB entries */ Offset = 0; for (;;) { DatabaseEntry = GetRemoteDatabaseEntry(DatabaseHandle, Offset); if (DatabaseEntry == NULL) { break; }
DatabaseEntry->EntryReferences = 0; Status = WriteRemoteDatabaseEntry(DatabaseHandle, Offset, DatabaseEntry); if (!NT_SUCCESS(Status)) { FreePool(DatabaseEntry); goto CloseReparse; }
Offset += DatabaseEntry->EntrySize; FreePool(DatabaseEntry); }
/* Init string for QueryVolumeName call */ SymbolicName.MaximumLength = sizeof(SymbolicNameBuffer); SymbolicName.Length = 0; SymbolicName.Buffer = SymbolicNameBuffer; Restart = TRUE;
/* Start looping on reparse points */ for (;;) { RtlCopyMemory(&SavedReparsePointInformation, &ReparsePointInformation, sizeof(FILE_REPARSE_POINT_INFORMATION)); Status = ZwQueryDirectoryFile(Handle, NULL, NULL, NULL, &IoStatusBlock, &ReparsePointInformation, sizeof(FILE_REPARSE_POINT_INFORMATION), FileReparsePointInformation, TRUE, Restart ? &FileName : NULL, Restart); /* Restart only once */ if (Restart) { Restart = FALSE; } else { /* If we get the same one, we're done, bail out */ if (ReparsePointInformation.FileReference == SavedReparsePointInformation.FileReference && ReparsePointInformation.Tag == SavedReparsePointInformation.Tag) { break; } }
/* If querying failed, or if onloading, or if not returning mount points, bail out */ if (!NT_SUCCESS(Status) || Unloading || ReparsePointInformation.Tag != IO_REPARSE_TAG_MOUNT_POINT) { break; }
/* Get the volume name associated to the mount point */ Status = QueryVolumeName(Handle, &ReparsePointInformation, 0, &SymbolicName, &VolumeName); if (!NT_SUCCESS(Status)) { continue; }
/* Browse the DB to find the name */ Offset = 0; for (;;) { UNICODE_STRING DbName;
DatabaseEntry = GetRemoteDatabaseEntry(DatabaseHandle, Offset); if (DatabaseEntry == NULL) { break; }
DbName.MaximumLength = DatabaseEntry->SymbolicNameLength; DbName.Length = DbName.MaximumLength; DbName.Buffer = (PWSTR)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset); /* Found, we're done! */ if (RtlEqualUnicodeString(&DbName, &SymbolicName, TRUE)) { break; }
Offset += DatabaseEntry->EntrySize; FreePool(DatabaseEntry); }
/* If we found the mount point.... */ if (DatabaseEntry != NULL) { /* If it was referenced, reference it once more and update to remote */ if (DatabaseEntry->EntryReferences) { ++DatabaseEntry->EntryReferences; Status = WriteRemoteDatabaseEntry(DatabaseHandle, Offset, DatabaseEntry); if (!NT_SUCCESS(Status)) { goto FreeDBEntry; }
FreePool(DatabaseEntry); } else { /* Query the Unique ID associated to that mount point in case it changed */ KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL); Status = QueryUniqueIdFromMaster(DeviceExtension, &SymbolicName, &UniqueId); if (!NT_SUCCESS(Status)) { /* If we failed doing so, reuse the old Unique ID and push it to master */ Status = WriteUniqueIdToMaster(DeviceExtension, DatabaseEntry); if (!NT_SUCCESS(Status)) { goto ReleaseDeviceLock; }
/* And then, reference & write the entry */ ++DatabaseEntry->EntryReferences; Status = WriteRemoteDatabaseEntry(DatabaseHandle, Offset, DatabaseEntry); if (!NT_SUCCESS(Status)) { goto ReleaseDeviceLock; }
KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); FreePool(DatabaseEntry); } /* If the Unique ID didn't change */ else if (UniqueId->UniqueIdLength == DatabaseEntry->UniqueIdLength && RtlCompareMemory(UniqueId->UniqueId, (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), UniqueId->UniqueIdLength) == UniqueId->UniqueIdLength) { /* Reference the entry, and update to remote */ ++DatabaseEntry->EntryReferences; Status = WriteRemoteDatabaseEntry(DatabaseHandle, Offset, DatabaseEntry); if (!NT_SUCCESS(Status)) { goto FreeUniqueId; }
FreePool(UniqueId); KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); FreePool(DatabaseEntry); } /* Would, by chance, the Unique ID be present elsewhere? */ else if (IsUniqueIdPresent(DeviceExtension, DatabaseEntry)) { /* Push the ID to master */ Status = WriteUniqueIdToMaster(DeviceExtension, DatabaseEntry); if (!NT_SUCCESS(Status)) { goto FreeUniqueId; }
/* And then, reference & write the entry */ ++DatabaseEntry->EntryReferences; Status = WriteRemoteDatabaseEntry(DatabaseHandle, Offset, DatabaseEntry); if (!NT_SUCCESS(Status)) { goto FreeUniqueId; }
FreePool(UniqueId); KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); FreePool(DatabaseEntry); } else { /* OK, at that point, we're facing a totally unknown unique ID * So, get rid of the old entry, and recreate a new one with * the know unique ID */ Status = DeleteRemoteDatabaseEntry(DatabaseHandle, Offset); if (!NT_SUCCESS(Status)) { goto FreeUniqueId; }
FreePool(DatabaseEntry); /* Allocate a new entry big enough */ DatabaseEntry = AllocatePool(UniqueId->UniqueIdLength + SymbolicName.Length + sizeof(DATABASE_ENTRY)); if (DatabaseEntry == NULL) { goto FreeUniqueId; }
/* Configure it */ DatabaseEntry->EntrySize = UniqueId->UniqueIdLength + SymbolicName.Length + sizeof(DATABASE_ENTRY); DatabaseEntry->EntryReferences = 1; DatabaseEntry->SymbolicNameOffset = sizeof(DATABASE_ENTRY); DatabaseEntry->SymbolicNameLength = SymbolicName.Length; DatabaseEntry->UniqueIdOffset = SymbolicName.Length + sizeof(DATABASE_ENTRY); DatabaseEntry->UniqueIdLength = UniqueId->UniqueIdLength; RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset), SymbolicName.Buffer, DatabaseEntry->SymbolicNameLength); RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), UniqueId->UniqueId, UniqueId->UniqueIdLength);
/* And write it remotely */ Status = AddRemoteDatabaseEntry(DatabaseHandle, DatabaseEntry); if (!NT_SUCCESS(Status)) { FreePool(DatabaseEntry); goto FreeUniqueId; }
FreePool(UniqueId); FreePool(DatabaseEntry); KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); } } } else { /* We failed finding it remotely * So, let's allocate a new remote DB entry */ KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL); /* To be able to do so, we need the device Unique ID, ask master */ Status = QueryUniqueIdFromMaster(DeviceExtension, &SymbolicName, &UniqueId); KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); if (NT_SUCCESS(Status)) { /* Allocate a new entry big enough */ DatabaseEntry = AllocatePool(UniqueId->UniqueIdLength + SymbolicName.Length + sizeof(DATABASE_ENTRY)); if (DatabaseEntry != NULL) { /* Configure it */ DatabaseEntry->EntrySize = UniqueId->UniqueIdLength + SymbolicName.Length + sizeof(DATABASE_ENTRY); DatabaseEntry->EntryReferences = 1; DatabaseEntry->SymbolicNameOffset = sizeof(DATABASE_ENTRY); DatabaseEntry->SymbolicNameLength = SymbolicName.Length; DatabaseEntry->UniqueIdOffset = SymbolicName.Length + sizeof(DATABASE_ENTRY); DatabaseEntry->UniqueIdLength = UniqueId->UniqueIdLength; RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset), SymbolicName.Buffer, DatabaseEntry->SymbolicNameLength); RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), UniqueId->UniqueId, UniqueId->UniqueIdLength);
/* And write it remotely */ Status = AddRemoteDatabaseEntry(DatabaseHandle, DatabaseEntry); FreePool(DatabaseEntry); FreePool(UniqueId);
if (!NT_SUCCESS(Status)) { goto FreeVolume; } } else { FreePool(UniqueId); } } }
/* Find info about the device associated associated with the mount point */ KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL); Status = FindDeviceInfo(DeviceExtension, &SymbolicName, FALSE, &ListDeviceInfo); if (!NT_SUCCESS(Status)) { FailedFinding = TRUE; FreePool(VolumeName.Buffer); } else { /* Associate the device with the currrent DB */ AssociatedDevice = AllocatePool(sizeof(ASSOCIATED_DEVICE_ENTRY)); if (AssociatedDevice == NULL) { FreePool(VolumeName.Buffer); } else { AssociatedDevice->DeviceInformation = DeviceInformation; AssociatedDevice->String.Length = VolumeName.Length; AssociatedDevice->String.MaximumLength = VolumeName.MaximumLength; AssociatedDevice->String.Buffer = VolumeName.Buffer; InsertTailList(&ListDeviceInfo->AssociatedDevicesHead, &AssociatedDevice->AssociatedDevicesEntry); }
/* If we don't have to skip notifications, notify */ if (!ListDeviceInfo->SkipNotifications) { PostOnlineNotification(DeviceExtension, &ListDeviceInfo->SymbolicName); } }
KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); }
/* We don't need mount points any longer */ ZwClose(Handle);
/* Look for the DB again */ KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL); for (Entry = DeviceExtension->DeviceListHead.Flink; Entry != &DeviceExtension->DeviceListHead; Entry = Entry->Flink) { ListDeviceInfo = CONTAINING_RECORD(Entry, DEVICE_INFORMATION, DeviceListEntry); if (ListDeviceInfo == DeviceInformation) { break; } }
if (Entry == &DeviceExtension->DeviceListHead) { ListDeviceInfo = NULL; }
/* Start the pruning loop */ Offset = 0; for (;;) { /* Get the entry */ DatabaseEntry = GetRemoteDatabaseEntry(DatabaseHandle, Offset); if (DatabaseEntry == NULL) { break; }
/* It's not referenced anylonger? Prune it */ if (DatabaseEntry->EntryReferences == 0) { Status = DeleteRemoteDatabaseEntry(DatabaseHandle, Offset); if (!NT_SUCCESS(Status)) { FreePool(DatabaseEntry); KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE); goto CloseRDB; } } /* Update the Unique IDs to reflect the changes we might have done previously */ else { if (ListDeviceInfo != NULL) { UpdateReplicatedUniqueIds(ListDeviceInfo, DatabaseEntry); }
Offset += DatabaseEntry->EntrySize; }
FreePool(DatabaseEntry); }
/* We do have a DB now :-) */ if (ListDeviceInfo != NULL && !FailedFinding) { DeviceInformation->NoDatabase = FALSE; }
KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
goto CloseRDB;
FreeUniqueId: FreePool(UniqueId);ReleaseDeviceLock: KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);FreeDBEntry: FreePool(DatabaseEntry);FreeVolume: FreePool(VolumeName.Buffer);CloseReparse: ZwClose(Handle);CloseRDB: CloseRemoteDatabase(DatabaseHandle);ReleaseRDS: ReleaseRemoteDatabaseSemaphore(DeviceExtension); return;}
/* * @implemented */VOIDNTAPIWorkerThread(IN PDEVICE_OBJECT DeviceObject, IN PVOID Context){ ULONG i; KEVENT Event; KIRQL OldIrql; NTSTATUS Status; HANDLE SafeEvent; PLIST_ENTRY Entry; LARGE_INTEGER Timeout; PRECONCILE_WORK_ITEM WorkItem; PDEVICE_EXTENSION DeviceExtension; OBJECT_ATTRIBUTES ObjectAttributes;
UNREFERENCED_PARAMETER(DeviceObject);
InitializeObjectAttributes(&ObjectAttributes, &SafeVolumes, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); KeInitializeEvent(&Event, NotificationEvent, FALSE); Timeout.QuadPart = -10000000LL; /* Wait for 1 second */
/* Wait as long as possible for clearance from autochk * We will write remote databases only if it is safe * to access volumes. * First, given we start before SMSS, wait for the * event creation. */ i = 0; do { /* If we started to shutdown, stop waiting forever and jump to last attempt */ if (Unloading) { i = 999; } else { /* Attempt to open the event */ Status = ZwOpenEvent(&SafeEvent, EVENT_ALL_ACCESS, &ObjectAttributes); if (NT_SUCCESS(Status)) { break; }
/* Wait a bit to give SMSS a chance to create the event */ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &Timeout); }
++i; } while (i < 1000);
/* We managed to open the event, wait until autochk signals it */ if (i < 1000) { do { Status = ZwWaitForSingleObject(SafeEvent, FALSE, &Timeout); } while (Status == STATUS_TIMEOUT && !Unloading);
ZwClose(SafeEvent); }
DeviceExtension = Context;
InterlockedExchange(&(DeviceExtension->WorkerThreadStatus), 1);
/* Acquire workers lock */ KeWaitForSingleObject(&(DeviceExtension->WorkerSemaphore), Executive, KernelMode, FALSE, NULL);
KeAcquireSpinLock(&(DeviceExtension->WorkerLock), &OldIrql);
/* Ensure there are workers */ while (!IsListEmpty(&(DeviceExtension->WorkerQueueListHead))) { /* Unqueue a worker */ Entry = RemoveHeadList(&(DeviceExtension->WorkerQueueListHead)); WorkItem = CONTAINING_RECORD(Entry, RECONCILE_WORK_ITEM, WorkerQueueListEntry);
KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
/* Call it */ WorkItem->WorkerRoutine(WorkItem->Context);
IoFreeWorkItem(WorkItem->WorkItem); FreePool(WorkItem);
if (InterlockedDecrement(&(DeviceExtension->WorkerReferences)) < 0) { return; }
KeWaitForSingleObject(&(DeviceExtension->WorkerSemaphore), Executive, KernelMode, FALSE, NULL); KeAcquireSpinLock(&(DeviceExtension->WorkerLock), &OldIrql); } KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
InterlockedDecrement(&(DeviceExtension->WorkerReferences));
/* Reset event */ KeSetEvent(&UnloadEvent, IO_NO_INCREMENT, FALSE);}
/* * @implemented */NTSTATUSQueueWorkItem(IN PDEVICE_EXTENSION DeviceExtension, IN PRECONCILE_WORK_ITEM WorkItem, IN PVOID Context){ KIRQL OldIrql;
WorkItem->Context = Context;
/* When called, lock is already acquired */
/* If noone (-1 as references), start to work */ if (InterlockedIncrement(&(DeviceExtension->WorkerReferences)) == 0) { IoQueueWorkItem(WorkItem->WorkItem, WorkerThread, DelayedWorkQueue, DeviceExtension); }
/* Otherwise queue worker for delayed execution */ KeAcquireSpinLock(&(DeviceExtension->WorkerLock), &OldIrql); InsertTailList(&(DeviceExtension->WorkerQueueListHead), &(WorkItem->WorkerQueueListEntry)); KeReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
KeReleaseSemaphore(&(DeviceExtension->WorkerSemaphore), IO_NO_INCREMENT, 1, FALSE);
return STATUS_SUCCESS;}
/* * @implemented */NTSTATUSQueryVolumeName(IN HANDLE RootDirectory, IN PFILE_REPARSE_POINT_INFORMATION ReparsePointInformation, IN PUNICODE_STRING FileName OPTIONAL, OUT PUNICODE_STRING SymbolicName, OUT PUNICODE_STRING VolumeName){ HANDLE Handle; NTSTATUS Status; ULONG NeededLength; IO_STATUS_BLOCK IoStatusBlock; OBJECT_ATTRIBUTES ObjectAttributes; PFILE_NAME_INFORMATION FileNameInfo; PREPARSE_DATA_BUFFER ReparseDataBuffer;
if (!FileName) { UNICODE_STRING Reference;
Reference.Length = Reference.MaximumLength = sizeof(ReparsePointInformation->FileReference); Reference.Buffer = (PWSTR)&(ReparsePointInformation->FileReference); InitializeObjectAttributes(&ObjectAttributes, &Reference, OBJ_KERNEL_HANDLE, RootDirectory, NULL); } else { InitializeObjectAttributes(&ObjectAttributes, FileName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL); }
/* Open volume */ Status = ZwOpenFile(&Handle, SYNCHRONIZE | FILE_READ_ATTRIBUTES, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, (FileName) ? FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT : FILE_OPEN_BY_FILE_ID | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT); if (!NT_SUCCESS(Status)) { return Status; }
/* Get the reparse point data */ ReparseDataBuffer = AllocatePool(MAXIMUM_REPARSE_DATA_BUFFER_SIZE); if (!ReparseDataBuffer) { ZwClose(Handle); return STATUS_INSUFFICIENT_RESOURCES; }
Status = ZwFsControlFile(Handle, 0, NULL, NULL, &IoStatusBlock, FSCTL_GET_REPARSE_POINT, NULL, 0, ReparseDataBuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE); if (!NT_SUCCESS(Status)) { FreePool(ReparseDataBuffer); ZwClose(Handle); return Status; }
/* Check that name can fit in buffer */ if (ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength + sizeof(UNICODE_NULL) > SymbolicName->MaximumLength) { FreePool(ReparseDataBuffer); ZwClose(Handle); return STATUS_BUFFER_TOO_SMALL; }
/* Copy symbolic name */ SymbolicName->Length = ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength; RtlCopyMemory(SymbolicName->Buffer, (PWSTR)((ULONG_PTR)ReparseDataBuffer->MountPointReparseBuffer.PathBuffer + ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameOffset), ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength);
FreePool(ReparseDataBuffer);
/* Name has to \ terminated */ if (SymbolicName->Buffer[SymbolicName->Length / sizeof(WCHAR) - 1] != L'\\') { ZwClose(Handle); return STATUS_INVALID_PARAMETER; }
/* So that we can delete it, and match mountmgr requirements */ SymbolicName->Length -= sizeof(WCHAR); SymbolicName->Buffer[SymbolicName->Length / sizeof(WCHAR)] = UNICODE_NULL;
/* Also ensure it's really a volume name... */ if (!MOUNTMGR_IS_VOLUME_NAME(SymbolicName)) { ZwClose(Handle); return STATUS_INVALID_PARAMETER; }
/* Now prepare to really get the name */ FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + 2 * sizeof(WCHAR)); if (!FileNameInfo) { ZwClose(Handle); return STATUS_INSUFFICIENT_RESOURCES; }
Status = ZwQueryInformationFile(Handle, &IoStatusBlock, FileNameInfo, sizeof(FILE_NAME_INFORMATION) + 2 * sizeof(WCHAR), FileNameInformation); if (Status == STATUS_BUFFER_OVERFLOW) { /* As expected... Reallocate with proper size */ NeededLength = FileNameInfo->FileNameLength; FreePool(FileNameInfo);
FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + NeededLength); if (!FileNameInfo) { ZwClose(Handle); return STATUS_INSUFFICIENT_RESOURCES; }
/* And query name */ Status = ZwQueryInformationFile(Handle, &IoStatusBlock, FileNameInfo, sizeof(FILE_NAME_INFORMATION) + NeededLength, FileNameInformation); }
ZwClose(Handle);
if (!NT_SUCCESS(Status)) { return Status; }
/* Return the volume name */ VolumeName->Length = (USHORT)FileNameInfo->FileNameLength; VolumeName->MaximumLength = (USHORT)FileNameInfo->FileNameLength + sizeof(WCHAR); VolumeName->Buffer = AllocatePool(VolumeName->MaximumLength); if (!VolumeName->Buffer) { return STATUS_INSUFFICIENT_RESOURCES; }
RtlCopyMemory(VolumeName->Buffer, FileNameInfo->FileName, FileNameInfo->FileNameLength); VolumeName->Buffer[FileNameInfo->FileNameLength / sizeof(WCHAR)] = UNICODE_NULL;
FreePool(FileNameInfo);
return STATUS_SUCCESS;}
/* * @implemented */VOIDOnlineMountedVolumes(IN PDEVICE_EXTENSION DeviceExtension, IN PDEVICE_INFORMATION DeviceInformation){ HANDLE Handle; NTSTATUS Status; BOOLEAN RestartScan; IO_STATUS_BLOCK IoStatusBlock; OBJECT_ATTRIBUTES ObjectAttributes; PDEVICE_INFORMATION VolumeDeviceInformation; WCHAR FileNameBuffer[0x8], SymbolicNameBuffer[0x64]; UNICODE_STRING ReparseFile, FileName, SymbolicName, VolumeName; FILE_REPARSE_POINT_INFORMATION ReparsePointInformation, SavedReparsePointInformation;
/* Removable devices don't have remote database on them */ if (DeviceInformation->Removable) { return; }
/* Prepare a string with reparse point index */ ReparseFile.Length = 0; ReparseFile.MaximumLength = DeviceInformation->DeviceName.Length + ReparseIndex.Length + sizeof(UNICODE_NULL); ReparseFile.Buffer = AllocatePool(ReparseFile.MaximumLength); if (!ReparseFile.Buffer) { return; }
RtlAppendUnicodeStringToString(&ReparseFile, &DeviceInformation->DeviceName); RtlAppendUnicodeStringToString(&ReparseFile, &ReparseIndex); ReparseFile.Buffer[ReparseFile.Length / sizeof(WCHAR)] = UNICODE_NULL;
InitializeObjectAttributes(&ObjectAttributes, &ReparseFile, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
/* Open reparse point */ Status = ZwOpenFile(&Handle, FILE_GENERIC_READ, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_ALERT | FILE_OPEN_REPARSE_POINT); FreePool(ReparseFile.Buffer); if (!NT_SUCCESS(Status)) { DeviceInformation->NoDatabase = FALSE; return; }
/* Query reparse point information * We only pay attention to mout point */ RtlZeroMemory(FileNameBuffer, sizeof(FileNameBuffer)); FileName.Buffer = FileNameBuffer; FileName.Length = sizeof(FileNameBuffer); FileName.MaximumLength = sizeof(FileNameBuffer); ((PULONG)FileNameBuffer)[0] = IO_REPARSE_TAG_MOUNT_POINT; Status = ZwQueryDirectoryFile(Handle, NULL, NULL, NULL, &IoStatusBlock, &ReparsePointInformation, sizeof(FILE_REPARSE_POINT_INFORMATION), FileReparsePointInformation, TRUE, &FileName, FALSE); if (!NT_SUCCESS(Status)) { ZwClose(Handle); return; }
RestartScan = TRUE;
/* Query mount points */ while (TRUE) { SymbolicName.Length = 0; SymbolicName.MaximumLength = sizeof(SymbolicNameBuffer); SymbolicName.Buffer = SymbolicNameBuffer; RtlCopyMemory(&SavedReparsePointInformation, &ReparsePointInformation, sizeof(FILE_REPARSE_POINT_INFORMATION));
Status = ZwQueryDirectoryFile(Handle, NULL, NULL, NULL, &IoStatusBlock, &ReparsePointInformation, sizeof(FILE_REPARSE_POINT_INFORMATION), FileReparsePointInformation, TRUE, (RestartScan) ? &FileName : NULL, RestartScan); if (!RestartScan) { if (ReparsePointInformation.FileReference == SavedReparsePointInformation.FileReference && ReparsePointInformation.Tag == SavedReparsePointInformation.Tag) { break; } } else { RestartScan = FALSE; }
if (!NT_SUCCESS(Status) || ReparsePointInformation.Tag != IO_REPARSE_TAG_MOUNT_POINT) { break; }
/* Get the volume name associated to the mount point */ Status = QueryVolumeName(Handle, &ReparsePointInformation, NULL, &SymbolicName, &VolumeName); if (!NT_SUCCESS(Status)) { continue; }
FreePool(VolumeName.Buffer);
/* Get its information */ Status = FindDeviceInfo(DeviceExtension, &SymbolicName, FALSE, &VolumeDeviceInformation); if (!NT_SUCCESS(Status)) { DeviceInformation->NoDatabase = TRUE; continue; }
/* If notification are enabled, mark it online */ if (!DeviceInformation->SkipNotifications) { PostOnlineNotification(DeviceExtension, &VolumeDeviceInformation->SymbolicName); } }
ZwClose(Handle);}
/* * @implemented */VOIDReconcileThisDatabaseWithMaster(IN PDEVICE_EXTENSION DeviceExtension, IN PDEVICE_INFORMATION DeviceInformation){ PRECONCILE_WORK_ITEM WorkItem;
/* Removable devices don't have remote database */ if (DeviceInformation->Removable) { return; }
/* Allocate a work item */ WorkItem = AllocatePool(sizeof(RECONCILE_WORK_ITEM)); if (!WorkItem) { return; }
WorkItem->WorkItem = IoAllocateWorkItem(DeviceExtension->DeviceObject); if (!WorkItem->WorkItem) { FreePool(WorkItem); return; }
/* And queue it */ WorkItem->WorkerRoutine = ReconcileThisDatabaseWithMasterWorker; WorkItem->DeviceExtension = DeviceExtension; WorkItem->DeviceInformation = DeviceInformation; QueueWorkItem(DeviceExtension, WorkItem, &(WorkItem->DeviceExtension));
/* If the worker thread isn't started yet, automatic drive letter is * enabled but automount disabled, manually set mounted volumes online. * Otherwise, they will be set online during database reconciliation. */ if (DeviceExtension->WorkerThreadStatus == 0 && DeviceExtension->AutomaticDriveLetter && DeviceExtension->NoAutoMount) { OnlineMountedVolumes(DeviceExtension, DeviceInformation); }}
/* * @implemented */VOIDReconcileAllDatabasesWithMaster(IN PDEVICE_EXTENSION DeviceExtension){ PLIST_ENTRY NextEntry; PDEVICE_INFORMATION DeviceInformation;
/* Browse all the devices */ for (NextEntry = DeviceExtension->DeviceListHead.Flink; NextEntry != &(DeviceExtension->DeviceListHead); NextEntry = NextEntry->Flink) { DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry); /* If it's not removable, then, it might have a database to sync */ if (!DeviceInformation->Removable) { ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation); } }}
/* * @implemented */VOIDNTAPICreateRemoteDatabaseWorker(IN PDEVICE_OBJECT DeviceObject, IN PVOID Context){ NTSTATUS Status; HANDLE Database = 0; UNICODE_STRING DatabaseName; PMIGRATE_WORK_ITEM WorkItem; IO_STATUS_BLOCK IoStatusBlock; OBJECT_ATTRIBUTES ObjectAttributes; PDEVICE_INFORMATION DeviceInformation;
UNREFERENCED_PARAMETER(DeviceObject);
/* Extract context */ WorkItem = Context; DeviceInformation = WorkItem->DeviceInformation;
/* Reconstruct appropriate string */ DatabaseName.Length = 0; DatabaseName.MaximumLength = DeviceInformation->DeviceName.Length + RemoteDatabase.Length + sizeof(UNICODE_NULL); DatabaseName.Buffer = AllocatePool(DatabaseName.MaximumLength); if (DatabaseName.Buffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; }
/* Create the required folder (in which the database will be stored * \System Volume Information at root of the volume */ Status = RtlCreateSystemVolumeInformationFolder(&(DeviceInformation->DeviceName)); if (!NT_SUCCESS(Status)) { goto Cleanup; }
/* Finish initiating strings */ RtlAppendUnicodeStringToString(&DatabaseName, &DeviceInformation->DeviceName); RtlAppendUnicodeStringToString(&DatabaseName, &RemoteDatabase); DatabaseName.Buffer[DatabaseName.Length / sizeof(WCHAR)] = UNICODE_NULL;
/* Create database */ InitializeObjectAttributes(&ObjectAttributes, &DatabaseName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
Status = IoCreateFile(&Database, SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | FILE_WRITE_EA | FILE_READ_EA | FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA, &ObjectAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, 0, FILE_CREATE, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT, NULL, 0, CreateFileTypeNone, NULL, IO_STOP_ON_SYMLINK | IO_NO_PARAMETER_CHECKING); if (!NT_SUCCESS(Status)) { if (Status == STATUS_STOPPED_ON_SYMLINK) { DPRINT1("Attempt to exploit CVE-2015-1769. See CORE-10216\n"); }
Database = 0; goto Cleanup; }
Cleanup: if (DatabaseName.Buffer) { FreePool(DatabaseName.Buffer); }
if (NT_SUCCESS(Status)) { DeviceInformation->Migrated = 1; } else if (Database != 0) { ZwClose(Database); }
IoFreeWorkItem(WorkItem->WorkItem);
WorkItem->WorkItem = NULL; WorkItem->Status = Status; WorkItem->Database = Database;
KeSetEvent(WorkItem->Event, 0, FALSE);}
/* * @implemented */NTSTATUSCreateRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation, IN OUT PHANDLE Database){ KEVENT Event; NTSTATUS Status; PMIGRATE_WORK_ITEM WorkItem;
KeInitializeEvent(&Event, NotificationEvent, FALSE);
/* Allocate a work item dedicated to migration */ WorkItem = AllocatePool(sizeof(MIGRATE_WORK_ITEM)); if (!WorkItem) { *Database = 0; return STATUS_INSUFFICIENT_RESOURCES; }
RtlZeroMemory(WorkItem, sizeof(MIGRATE_WORK_ITEM)); WorkItem->Event = &Event; WorkItem->DeviceInformation = DeviceInformation; WorkItem->WorkItem = IoAllocateWorkItem(DeviceInformation->DeviceExtension->DeviceObject); if (!WorkItem->WorkItem) { FreePool(WorkItem); *Database = 0; return STATUS_INSUFFICIENT_RESOURCES; }
/* And queue it */ IoQueueWorkItem(WorkItem->WorkItem, CreateRemoteDatabaseWorker, DelayedWorkQueue, WorkItem);
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = WorkItem->Status;
*Database = (NT_SUCCESS(Status) ? WorkItem->Database : 0);
FreePool(WorkItem); return Status;}
/* * @implemented */HANDLEOpenRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation, IN BOOLEAN MigrateDatabase){ HANDLE Database; NTSTATUS Status; BOOLEAN PreviousMode; IO_STATUS_BLOCK IoStatusBlock; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING DeviceRemoteDatabase;
Database = 0;
/* Get database name */ DeviceRemoteDatabase.Length = 0; DeviceRemoteDatabase.MaximumLength = DeviceInformation->DeviceName.Length + RemoteDatabase.Length + sizeof(UNICODE_NULL); DeviceRemoteDatabase.Buffer = AllocatePool(DeviceRemoteDatabase.MaximumLength); if (!DeviceRemoteDatabase.Buffer) { return 0; }
RtlAppendUnicodeStringToString(&DeviceRemoteDatabase, &DeviceInformation->DeviceName); RtlAppendUnicodeStringToString(&DeviceRemoteDatabase, &RemoteDatabase); DeviceRemoteDatabase.Buffer[DeviceRemoteDatabase.Length / sizeof(WCHAR)] = UNICODE_NULL;
/* Open database */ InitializeObjectAttributes(&ObjectAttributes, &DeviceRemoteDatabase, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
/* Disable hard errors */ PreviousMode = IoSetThreadHardErrorMode(FALSE);
Status = IoCreateFile(&Database, SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | FILE_WRITE_EA | FILE_READ_EA | FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA, &ObjectAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, 0, (!MigrateDatabase || DeviceInformation->Migrated == 0) ? FILE_OPEN_IF : FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT, NULL, 0, CreateFileTypeNone, NULL, IO_STOP_ON_SYMLINK | IO_NO_PARAMETER_CHECKING); if (Status == STATUS_STOPPED_ON_SYMLINK) { DPRINT1("Attempt to exploit CVE-2015-1769. See CORE-10216\n"); }
/* If base it to be migrated and was opened successfully, go ahead */ if (MigrateDatabase && NT_SUCCESS(Status)) { CreateRemoteDatabase(DeviceInformation, &Database); }
IoSetThreadHardErrorMode(PreviousMode); FreePool(DeviceRemoteDatabase.Buffer);
return Database;}
/* * @implemented */VOIDChangeRemoteDatabaseUniqueId(IN PDEVICE_INFORMATION DeviceInformation, IN PMOUNTDEV_UNIQUE_ID OldUniqueId, IN PMOUNTDEV_UNIQUE_ID NewUniqueId){ LONG Offset = 0; HANDLE Database; PDATABASE_ENTRY Entry, NewEntry; NTSTATUS Status = STATUS_SUCCESS;
/* Open the remote database */ Database = OpenRemoteDatabase(DeviceInformation, FALSE); if (!Database) { return; }
/* Get all the entries */ do { Entry = GetRemoteDatabaseEntry(Database, Offset); if (!Entry) { break; }
/* Not the correct entry, skip it */ if (Entry->UniqueIdLength != OldUniqueId->UniqueIdLength) { Offset += Entry->EntrySize; FreePool(Entry); continue; }
/* Not the correct entry, skip it */ if (RtlCompareMemory(OldUniqueId->UniqueId, (PVOID)((ULONG_PTR)Entry + Entry->UniqueIdOffset), Entry->UniqueIdLength) != Entry->UniqueIdLength) { Offset += Entry->EntrySize; FreePool(Entry); continue; }
/* Here, we have the correct entry */ NewEntry = AllocatePool(Entry->EntrySize + NewUniqueId->UniqueIdLength - OldUniqueId->UniqueIdLength); if (!NewEntry) { Offset += Entry->EntrySize; FreePool(Entry); continue; }
/* Recreate the entry from the previous one */ NewEntry->EntrySize = Entry->EntrySize + NewUniqueId->UniqueIdLength - OldUniqueId->UniqueIdLength; NewEntry->EntryReferences = Entry->EntryReferences; NewEntry->SymbolicNameOffset = sizeof(DATABASE_ENTRY); NewEntry->SymbolicNameLength = Entry->SymbolicNameLength; NewEntry->UniqueIdOffset = Entry->SymbolicNameLength + sizeof(DATABASE_ENTRY); NewEntry->UniqueIdLength = NewUniqueId->UniqueIdLength; RtlCopyMemory((PVOID)((ULONG_PTR)NewEntry + NewEntry->SymbolicNameOffset), (PVOID)((ULONG_PTR)Entry + Entry->SymbolicNameOffset), NewEntry->SymbolicNameLength); RtlCopyMemory((PVOID)((ULONG_PTR)NewEntry + NewEntry->UniqueIdOffset), NewUniqueId->UniqueId, NewEntry->UniqueIdLength);
/* Delete old entry */ Status = DeleteRemoteDatabaseEntry(Database, Offset); if (!NT_SUCCESS(Status)) { FreePool(Entry); FreePool(NewEntry); break; }
/* And replace with new one */ Status = AddRemoteDatabaseEntry(Database, NewEntry); FreePool(Entry); FreePool(NewEntry); } while (NT_SUCCESS(Status));
CloseRemoteDatabase(Database);
return;}
/* * @implemented */NTSTATUSNTAPIDeleteDriveLetterRoutine(IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext){ PMOUNTDEV_UNIQUE_ID UniqueId; UNICODE_STRING RegistryEntry;
UNREFERENCED_PARAMETER(EntryContext);
if (ValueType != REG_BINARY) { return STATUS_SUCCESS; }
UniqueId = Context;
/* First ensure we have the correct data */ if (UniqueId->UniqueIdLength != ValueLength) { return STATUS_SUCCESS; }
if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength) { return STATUS_SUCCESS; }
RtlInitUnicodeString(&RegistryEntry, ValueName);
/* Then, it's a drive letter, erase it */ if (IsDriveLetter(&RegistryEntry)) { RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, DatabasePath, ValueName); }
return STATUS_SUCCESS;}
/* * @implemented */VOIDDeleteRegistryDriveLetter(IN PMOUNTDEV_UNIQUE_ID UniqueId){ RTL_QUERY_REGISTRY_TABLE QueryTable[2];
RtlZeroMemory(QueryTable, sizeof(QueryTable)); QueryTable[0].QueryRoutine = DeleteDriveLetterRoutine;
RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, DatabasePath, QueryTable, UniqueId, NULL);}
/* * @implemented */NTSTATUSNTAPIDeleteNoDriveLetterEntryRoutine(IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext){ PMOUNTDEV_UNIQUE_ID UniqueId = Context;
UNREFERENCED_PARAMETER(EntryContext);
/* Ensure we have correct input */ if (ValueName[0] != L'#' || ValueType != REG_BINARY || UniqueId->UniqueIdLength != ValueLength) { return STATUS_SUCCESS; }
/* And then, if unique ID matching, delete entry */ if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength) { RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, DatabasePath, ValueName); }
return STATUS_SUCCESS;}
/* * @implemented */VOIDDeleteNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId){ RTL_QUERY_REGISTRY_TABLE QueryTable[2];
RtlZeroMemory(QueryTable, sizeof(QueryTable)); QueryTable[0].QueryRoutine = DeleteNoDriveLetterEntryRoutine;
RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, DatabasePath, QueryTable, UniqueId, NULL);}