Add a per-directory cache of short filenames allowing us to generate them

uniquely.  dir /x now shows properly unambiguous short filenames.
Also, we weren't FsRtlEnterFilesystem ing in directory operations.  Fix that
too.

svn path=/trunk/; revision=39374
This commit is contained in:
Art Yerkes 2009-02-04 03:39:23 +00:00
parent b6eeb280a5
commit 37d6382770
4 changed files with 164 additions and 53 deletions

View file

@ -162,8 +162,6 @@ typedef struct
PFILE_OBJECT StreamFileObject;
CDINFO CdInfo;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION, VCB, *PVCB;
@ -173,6 +171,14 @@ typedef struct
#define MAX_PATH 260
typedef struct _CDFS_SHORT_NAME
{
LIST_ENTRY Entry;
LARGE_INTEGER StreamOffset;
UNICODE_STRING Name;
WCHAR NameBuffer[13];
} CDFS_SHORT_NAME, *PCDFS_SHORT_NAME;
typedef struct _FCB
{
FSRTL_COMMON_FCB_HEADER RFCB;
@ -201,6 +207,9 @@ typedef struct _FCB
ULONG Flags;
DIR_RECORD Entry;
ERESOURCE NameListResource;
LIST_ENTRY ShortNameList;
} FCB, *PFCB;
@ -224,8 +233,6 @@ typedef struct _CCB
#define TAG_CCB TAG('I', 'C', 'C', 'B')
typedef struct
{
PDRIVER_OBJECT DriverObject;
@ -389,6 +396,12 @@ VOID
CdfsFileFlagsToAttributes(PFCB Fcb,
PULONG FileAttributes);
VOID
CdfsShortNameCacheGet
(PFCB DirectoryFcb,
PLARGE_INTEGER StreamOffset,
PUNICODE_STRING LongName,
PUNICODE_STRING ShortName);
/* rw.c */

View file

@ -31,7 +31,7 @@
#include "cdfs.h"
#define NDEBUG
//#define NDEBUG
#include <debug.h>
/* FUNCTIONS ****************************************************************/
@ -176,9 +176,7 @@ CdfsFindFile(PDEVICE_EXTENSION DeviceExt,
PVOID Context = NULL;
ULONG DirSize;
PDIR_RECORD Record;
LARGE_INTEGER StreamOffset;
BOOLEAN HasSpaces;
GENERATE_NAME_CONTEXT NameContext;
LARGE_INTEGER StreamOffset, OffsetOfEntry;
DPRINT("FindFile(Parent %x, FileToFind '%wZ', DirIndex: %d)\n",
Parent, FileToFind, pDirIndex ? *pDirIndex : 0);
@ -276,8 +274,9 @@ CdfsFindFile(PDEVICE_EXTENSION DeviceExt,
DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n",
Record->RecordLength, Record->ExtAttrRecordLength, Record->FileIdLength);
Status = CdfsGetEntryName(DeviceExt, &Context, &Block, &StreamOffset,
DirSize, (PVOID*)&Record, name, &DirIndex, &Offset);
Status = CdfsGetEntryName
(DeviceExt, &Context, &Block, &StreamOffset,
DirSize, (PVOID*)&Record, name, &DirIndex, &Offset);
if (Status == STATUS_NO_MORE_ENTRIES)
{
@ -296,27 +295,8 @@ CdfsFindFile(PDEVICE_EXTENSION DeviceExt,
ShortName.MaximumLength = 26;
ShortName.Buffer = ShortNameBuffer;
if ((RtlIsNameLegalDOS8Dot3(&LongName, NULL, &HasSpaces) == FALSE) ||
(HasSpaces == TRUE))
{
RtlZeroMemory(&NameContext, sizeof(GENERATE_NAME_CONTEXT));
/* FIXME: check if the generated filename already exists
* and generate a new one if this is the case */
/* Build short name */
RtlGenerate8dot3Name(&LongName,
FALSE,
&NameContext,
&ShortName);
}
else
{
/* copy short name */
RtlUpcaseUnicodeString(&ShortName,
&LongName,
FALSE);
}
OffsetOfEntry.QuadPart = StreamOffset.QuadPart + Offset;
CdfsShortNameCacheGet(Parent, &OffsetOfEntry, &LongName, &ShortName);
DPRINT("ShortName '%wZ'\n", &ShortName);
@ -773,6 +753,7 @@ CdfsDirectoryControl(PDEVICE_OBJECT DeviceObject,
NTSTATUS Status;
DPRINT("CdfsDirectoryControl() called\n");
FsRtlEnterFileSystem();
Stack = IoGetCurrentIrpStackLocation(Irp);
@ -798,6 +779,7 @@ CdfsDirectoryControl(PDEVICE_OBJECT DeviceObject,
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
FsRtlExitFileSystem();
return(Status);
}

View file

@ -30,7 +30,7 @@
#include "cdfs.h"
#define NDEBUG
//#define NDEBUG
#include <debug.h>
/* FUNCTIONS ****************************************************************/
@ -92,9 +92,11 @@ CdfsCreateFCB(PCWSTR FileName)
ExInitializeResourceLite(&Fcb->PagingIoResource);
ExInitializeResourceLite(&Fcb->MainResource);
ExInitializeResourceLite(&Fcb->NameListResource);
Fcb->RFCB.PagingIoResource = &Fcb->PagingIoResource;
Fcb->RFCB.Resource = &Fcb->MainResource;
Fcb->RFCB.IsFastIoPossible = FastIoIsNotPossible;
InitializeListHead(&Fcb->ShortNameList);
return(Fcb);
}
@ -103,9 +105,18 @@ CdfsCreateFCB(PCWSTR FileName)
VOID
CdfsDestroyFCB(PFCB Fcb)
{
PLIST_ENTRY Entry;
ExDeleteResourceLite(&Fcb->PagingIoResource);
ExDeleteResourceLite(&Fcb->MainResource);
while (!IsListEmpty(&Fcb->ShortNameList))
{
Entry = Fcb->ShortNameList.Flink;
RemoveEntryList(Entry);
ExFreePool(Entry);
}
ExFreePool(Fcb);
}
@ -458,16 +469,13 @@ CdfsDirFindFile(PDEVICE_EXTENSION DeviceExt,
ULONG BlockOffset;
NTSTATUS Status;
LARGE_INTEGER StreamOffset;
LARGE_INTEGER StreamOffset, OffsetOfEntry;
PVOID Context;
WCHAR ShortNameBuffer[13];
UNICODE_STRING ShortName;
UNICODE_STRING LongName;
UNICODE_STRING FileToFindUpcase;
BOOLEAN HasSpaces;
GENERATE_NAME_CONTEXT NameContext;
ASSERT(DeviceExt);
ASSERT(DirectoryFcb);
@ -533,22 +541,8 @@ CdfsDirFindFile(PDEVICE_EXTENSION DeviceExt,
ShortName.Buffer = ShortNameBuffer;
memset(ShortNameBuffer, 0, 26);
if ((RtlIsNameLegalDOS8Dot3(&LongName, NULL, &HasSpaces) == FALSE) ||
(HasSpaces == TRUE))
{
/* Build short name */
RtlGenerate8dot3Name(&LongName,
FALSE,
&NameContext,
&ShortName);
}
else
{
/* copy short name */
RtlUpcaseUnicodeString(&ShortName,
&LongName,
FALSE);
}
OffsetOfEntry.QuadPart = StreamOffset.QuadPart + Offset;
CdfsShortNameCacheGet(DirectoryFcb, &OffsetOfEntry, &LongName, &ShortName);
DPRINT("ShortName '%wZ'\n", &ShortName);

View file

@ -30,7 +30,7 @@
#include "cdfs.h"
#define NDEBUG
//#define NDEBUG
#include <debug.h>
/* FUNCTIONS ****************************************************************/
@ -95,4 +95,126 @@ CdfsFileFlagsToAttributes(PFCB Fcb,
((Fcb->Entry.FileFlags & FILE_FLAG_READONLY) ? FILE_ATTRIBUTE_READONLY : 0);
}
/* Writes a number into a string, ending at the target position. */
static PWCHAR
CdfsWriteNumberInShortName
(PWCHAR EndOfNumberTarget,
ULONG Number)
{
while (Number)
{
*EndOfNumberTarget = '0' + (Number % 10);
EndOfNumberTarget--;
Number /= 10;
}
return EndOfNumberTarget;
}
VOID
CdfsShortNameCacheGet
(PFCB DirectoryFcb,
PLARGE_INTEGER StreamOffset,
PUNICODE_STRING LongName,
PUNICODE_STRING ShortName)
{
BOOLEAN HasSpaces;
PWCHAR LastDot, Scan;
ULONG Number = 1;
PLIST_ENTRY Entry;
PCDFS_SHORT_NAME ShortNameEntry;
GENERATE_NAME_CONTEXT Context = { };
DPRINT("CdfsShortNameCacheGet(%I64u,%wZ)\n", StreamOffset->QuadPart, LongName);
/* Get the name list resource */
ExAcquireResourceExclusiveLite(&DirectoryFcb->NameListResource, TRUE);
/* Try to find the name in our cache */
for (Entry = DirectoryFcb->ShortNameList.Flink;
Entry != &DirectoryFcb->ShortNameList;
Entry = Entry->Flink)
{
ShortNameEntry = CONTAINING_RECORD(Entry, CDFS_SHORT_NAME, Entry);
if (ShortNameEntry->StreamOffset.QuadPart == StreamOffset->QuadPart)
{
/* Cache hit */
RtlCopyMemory
(ShortName->Buffer, ShortNameEntry->Name.Buffer,
ShortNameEntry->Name.Length);
ShortName->Length = ShortNameEntry->Name.Length;
ExReleaseResourceLite(&DirectoryFcb->NameListResource);
DPRINT("Yield short name %wZ from cache\n", ShortName);
return;
}
}
/* Cache miss */
if ((RtlIsNameLegalDOS8Dot3(LongName, NULL, &HasSpaces) == FALSE) ||
(HasSpaces == TRUE))
{
RtlGenerate8dot3Name(LongName, FALSE, &Context, ShortName);
}
else
{
/* copy short name */
RtlUpcaseUnicodeString
(ShortName,
LongName,
FALSE);
}
DPRINT("Initial Guess %wZ\n", ShortName);
/* Find the part that'll be numberified */
LastDot = &ShortName->Buffer[(ShortName->Length / sizeof(WCHAR)) - 1];
for (Scan = ShortName->Buffer;
Scan - ShortName->Buffer < ShortName->Length;
Scan++)
if (*Scan == '.') LastDot = Scan - 1;
/* Make it unique by scanning the cache and bumping */
/* Note that incrementing the ambiguous name is enough, since we add new
* entries at the tail. We'll scan over all collisions. */
/* XXX could perform better. */
for (Entry = DirectoryFcb->ShortNameList.Flink;
Entry != &DirectoryFcb->ShortNameList;
Entry = Entry->Flink)
{
ShortNameEntry = CONTAINING_RECORD(Entry, CDFS_SHORT_NAME, Entry);
if (RtlCompareUnicodeString
(ShortName,
&ShortNameEntry->Name,
TRUE) == 0) /* Match */
{
Scan = CdfsWriteNumberInShortName(LastDot, ++Number);
*Scan = '~';
DPRINT("Collide; try %wZ\n", ShortName);
}
}
/* We've scanned over all entries and now have a unique one. Cache it. */
ShortNameEntry = ExAllocatePool(PagedPool, sizeof(CDFS_SHORT_NAME));
if (!ShortNameEntry)
{
/* We couldn't cache it, but we can return it. We run the risk of
* generating a non-unique name later. */
ExReleaseResourceLite(&DirectoryFcb->NameListResource);
DPRINT1("Couldn't cache potentially clashing 8.3 name %wZ\n", ShortName);
return;
}
ShortNameEntry->StreamOffset = *StreamOffset;
ShortNameEntry->Name.Buffer = ShortNameEntry->NameBuffer;
ShortNameEntry->Name.Length = ShortName->Length;
ShortNameEntry->Name.MaximumLength = sizeof(ShortNameEntry->NameBuffer);
RtlCopyMemory
(ShortNameEntry->NameBuffer,
ShortName->Buffer,
ShortName->Length);
InsertTailList(&DirectoryFcb->ShortNameList, &ShortNameEntry->Entry);
ExReleaseResourceLite(&DirectoryFcb->NameListResource);
DPRINT("Returning short name %wZ for long name %wZ\n", ShortName, LongName);
}
/* EOF */