Add a per-directory cache of short filenames allowing us to generate them
authorArt Yerkes <art.yerkes@gmail.com>
Wed, 4 Feb 2009 03:39:23 +0000 (03:39 +0000)
committerArt Yerkes <art.yerkes@gmail.com>
Wed, 4 Feb 2009 03:39:23 +0000 (03:39 +0000)
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

reactos/drivers/filesystems/cdfs/cdfs.h
reactos/drivers/filesystems/cdfs/dirctl.c
reactos/drivers/filesystems/cdfs/fcb.c
reactos/drivers/filesystems/cdfs/misc.c

index e8b164f..744fa73 100644 (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 */
 
index a54eb52..bf43c51 100644 (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);
 }
index 76ee526..39102b4 100644 (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);
 
index 9aca5fc..1aa700f 100644 (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 */