Skip to content

Commit

Permalink
Kernel - Use the Rtl API to store DokanFCB in a Adelson-Velsky/Landis…
Browse files Browse the repository at this point in the history
… (AVL) table

Store the DokanFCB in an AVL tree instead of a simple list which becomes slower when there is more context being opened on different files. Moving from O(n) to O(log n).

* The AVL tree is built by using the result of the RtlCompareUnicodeString between the two Fcb FileNames.
* The AVL allocation callback is using a new lookaside list for allocating the AVL Node that contains the Avl Node header and a DokanFCB pointer of the inserted object that is allocated by another lookaside list. This allows us to manage the lifetime of the object outside the Avl table.
* A new log macro was added when VCB is only accessible to limit the compare callback logging to the VCB instance (since we do not have the IRP RequestContext).
* The FCB lookup and insert code were merged as the Avl insert API is doing a lookup to return the matching existing item or the new one inserted.
  • Loading branch information
Liryna committed Jan 22, 2022
1 parent 9ed0c33 commit 6bd609b
Show file tree
Hide file tree
Showing 9 changed files with 265 additions and 153 deletions.
10 changes: 2 additions & 8 deletions sys/create.c
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,6 @@ Return Value:
BOOLEAN eventContextConsumed = FALSE;
DWORD disposition = 0;
BOOLEAN fcbLocked = FALSE;
BOOLEAN caseInSensitive = TRUE;
DOKAN_INIT_LOGGER(logger, RequestContext->DeviceObject->DriverObject,
IRP_MJ_CREATE);

Expand Down Expand Up @@ -600,9 +599,6 @@ Return Value:
__leave;
}

caseInSensitive =
!(RequestContext->Dcb->MountOptions & DOKAN_EVENT_CASE_SENSITIVE);

// this memory is freed by DokanGetFCB if needed
// "+ sizeof(WCHAR)" is for the last NULL character
fileName = DokanAllocZero(fileNameLength + sizeof(WCHAR));
Expand Down Expand Up @@ -671,11 +667,9 @@ Return Value:
fileName = NULL;
__leave;
}
fcb = DokanGetFCB(RequestContext, parentDir, parentDirLength,
caseInSensitive);
fcb = DokanGetFCB(RequestContext, parentDir, parentDirLength);
} else {
fcb = DokanGetFCB(RequestContext, fileName, fileNameLength,
caseInSensitive);
fcb = DokanGetFCB(RequestContext, fileName, fileNameLength);
}
if (fcb == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
Expand Down
2 changes: 1 addition & 1 deletion sys/dokan.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ DokanFilterCallbackAcquireForCreateSection(__in PFS_FILTER_CALLBACK_DATA
}

BOOLEAN
DokanLookasideCreate(LOOKASIDE_LIST_EX *pCache, size_t cbElement) {
DokanLookasideCreate(__in LOOKASIDE_LIST_EX *pCache, __in size_t cbElement) {
NTSTATUS Status = ExInitializeLookasideListEx(
pCache, NULL, NULL, NonPagedPool, 0, cbElement, TAG, 0);

Expand Down
17 changes: 17 additions & 0 deletions sys/dokan.h
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,14 @@ typedef struct _DokanVolumeControlBlock {
PDokanDCB Dcb;
LIST_ENTRY NextFCB;

// Avl Table storing the DokanFCB instances.
RTL_AVL_TABLE FcbTable;
// Lookaside list for the FCB Avl table node containing the Avl header and a
// pointer to our DokanFCB that is allocated by the global fcb lookaside list.
LOOKASIDE_LIST_EX FCBAvlNodeLookasideList;
// Whether the lookaside list was initialized.
BOOLEAN FCBAvlNodeLookasideListInit;

// NotifySync is used by notify directory change
PNOTIFY_SYNC NotifySync;
LIST_ENTRY DirNotifyList;
Expand Down Expand Up @@ -541,6 +549,12 @@ typedef struct _DokanFileControlBlock {
// owned by the VCB and guarded by the VCB lock.
BOOLEAN GarbageCollectionGracePeriodPassed;

// The Fcb was removed from the Avl table and is waiting to be deleted when
// all existing handles are being closed. This can happen when a file is
// renamed with the destination having an open handle. NTFS denies this action
// but due to a Dokan bug, this is actually possible. Until it is fixed, we
// reproduce the behavior prior to the Avl table.
BOOLEAN ReplacedByRename;
} DokanFCB, *PDokanFCB;

#define DokanResourceLockRO(resource) \
Expand Down Expand Up @@ -1178,6 +1192,9 @@ VOID DokanCreateMountPoint(__in PDokanDCB Dcb);
VOID FlushFcb(__in PREQUEST_CONTEXT RequestContext, __in PDokanFCB fcb,
__in_opt PFILE_OBJECT fileObject);

BOOLEAN
DokanLookasideCreate(__in LOOKASIDE_LIST_EX *pCache, __in size_t cbElement);

PDEVICE_ENTRY
FindDeviceForDeleteBySessionId(PDOKAN_GLOBAL dokanGlobal, ULONG sessionId);

Expand Down
81 changes: 38 additions & 43 deletions sys/fileinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "dokan.h"
#include "util/fcb.h"
#include "util/irp_buffer_helper.h"
#include "util/str.h"

Expand Down Expand Up @@ -350,54 +351,51 @@ VOID FlushFcb(__in PREQUEST_CONTEXT RequestContext, __in PDokanFCB Fcb,
}
}

VOID FlushAllCachedFcb(__in PREQUEST_CONTEXT RequestContext,
__in PDokanFCB fcbRelatedTo,
__in_opt PFILE_OBJECT fileObject) {
PLIST_ENTRY thisEntry, nextEntry, listHead;
PDokanFCB fcb = NULL;
VOID FlushIfDescendant(__in PREQUEST_CONTEXT RequestContext,
__in PDokanFCB FcbRelatedTo, __in PDokanFCB Fcb) {
if (DokanFCBFlagsIsSet(Fcb, DOKAN_FILE_DIRECTORY)) {
DOKAN_LOG_FINE_IRP(RequestContext, "FCB=%p is directory so skip it.", Fcb);
return;
}

DOKAN_LOG_FINE_IRP(RequestContext, "Check \"%wZ\" if is related to \"%wZ\"",
&Fcb->FileName, &FcbRelatedTo->FileName);

if (fcbRelatedTo == NULL) {
if (!StartsWith(&Fcb->FileName, &FcbRelatedTo->FileName)) {
return;
}
DOKAN_LOG_FINE_IRP(RequestContext, "Flush \"%wZ\" if it is possible.",
&Fcb->FileName);
FlushFcb(RequestContext, Fcb, NULL);
}

VOID FlushAllCachedFcb(__in PREQUEST_CONTEXT RequestContext,
__in PDokanFCB FcbRelatedTo,
__in_opt PFILE_OBJECT FileObject) {
if (FcbRelatedTo == NULL) {
return;
}

if (!DokanFCBFlagsIsSet(fcbRelatedTo, DOKAN_FILE_DIRECTORY)) {
if (!DokanFCBFlagsIsSet(FcbRelatedTo, DOKAN_FILE_DIRECTORY)) {
DOKAN_LOG_FINE_IRP(
RequestContext,
"FlushAllCachedFcb file passed in. Flush only: FCB=%p FileObject=%p.",
fcbRelatedTo, fileObject);
FlushFcb(RequestContext, fcbRelatedTo, fileObject);
FcbRelatedTo, FileObject);
FlushFcb(RequestContext, FcbRelatedTo, FileObject);
return;
}

DokanVCBLockRW(fcbRelatedTo->Vcb);
DokanVCBLockRW(FcbRelatedTo->Vcb);

listHead = &fcbRelatedTo->Vcb->NextFCB;

for (thisEntry = listHead->Flink; thisEntry != listHead;
thisEntry = nextEntry) {
nextEntry = thisEntry->Flink;

fcb = CONTAINING_RECORD(thisEntry, DokanFCB, NextFCB);

if (DokanFCBFlagsIsSet(fcb, DOKAN_FILE_DIRECTORY)) {
DOKAN_LOG_FINE_IRP(RequestContext, "FCB=%p is directory so skip it.",
fcb);
continue;
}

DOKAN_LOG_FINE_IRP(RequestContext, "Check \"%wZ\" if is related to \"%wZ\"", &fcb->FileName,
&fcbRelatedTo->FileName);

if (StartsWith(&fcb->FileName, &fcbRelatedTo->FileName)) {
DOKAN_LOG_FINE_IRP(RequestContext, "Flush \"%wZ\" if it is possible.",
&fcb->FileName);
FlushFcb(RequestContext, fcb, NULL);
}

fcb = NULL;
for (PDokanFCB *fcbInTable = (PDokanFCB *)RtlEnumerateGenericTableAvl(
&RequestContext->Vcb->FcbTable, /*Restart=*/TRUE);
fcbInTable != NULL;
fcbInTable = (PDokanFCB *)RtlEnumerateGenericTableAvl(
&RequestContext->Vcb->FcbTable, /*Restart=*/FALSE)) {
FlushIfDescendant(RequestContext, FcbRelatedTo, *fcbInTable);
}

DokanVCBUnlock(fcbRelatedTo->Vcb);
DokanVCBUnlock(FcbRelatedTo->Vcb);

DOKAN_LOG_FINE_IRP(RequestContext, "Finished");
}
Expand Down Expand Up @@ -888,14 +886,11 @@ VOID DokanCompleteSetInformation(__in PREQUEST_CONTEXT RequestContext,
__leave;
}

fcb->FileName.Buffer = buffer;
ASSERT(fcb->FileName.Buffer != NULL);

RtlCopyMemory(fcb->FileName.Buffer, EventInfo->Buffer,
EventInfo->BufferLength);

fcb->FileName.Length = (USHORT)EventInfo->BufferLength;
fcb->FileName.MaximumLength = (USHORT)EventInfo->BufferLength;
RtlCopyMemory(buffer, EventInfo->Buffer, EventInfo->BufferLength);
DokanVCBLockRW(RequestContext->Vcb);
DokanRenameFcb(RequestContext, fcb, buffer,
(USHORT)EventInfo->BufferLength);
DokanVCBUnlock(RequestContext->Vcb);
DOKAN_LOG_FINE_IRP(RequestContext, "Fcb=%p renamed \"%wZ\"", fcb,
&fcb->FileName);
}
Expand Down
2 changes: 2 additions & 0 deletions sys/fscontrol.c
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,8 @@ NTSTATUS DokanMountVolume(__in PREQUEST_CONTEXT RequestContext) {
}

InitializeListHead(&vcb->NextFCB);
RtlInitializeGenericTableAvl(&vcb->FcbTable, DokanCompareFcb,
DokanAllocateFcbAvl, DokanFreeFcbAvl, vcb);

InitializeListHead(&vcb->DirNotifyList);
FsRtlNotifyInitializeSync(&vcb->NotifySync);
Expand Down
3 changes: 3 additions & 0 deletions sys/notification.c
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,9 @@ NTSTATUS DokanEventRelease(__in_opt PREQUEST_CONTEXT RequestContext,
DokanStopFcbGarbageCollectorThread(vcb);
ClearLongFlag(vcb->Flags, VCB_MOUNTED);

if (vcb->FCBAvlNodeLookasideListInit) {
ExDeleteLookasideListEx(&vcb->FCBAvlNodeLookasideList);
}
DokanCleanupAllChangeNotificationWaiters(vcb);
IoReleaseRemoveLockAndWait(&dcb->RemoveLock, RequestContext);

Expand Down
Loading

0 comments on commit 6bd609b

Please sign in to comment.