* LICENSE: GPL - See COPYING in the top level directory
* FILE: drivers/filesystems/fastfat/fcb.c
* PURPOSE: FCB manipulation routines.
- * PROGRAMMERS: Alexey Vlasov
+ * PROGRAMMERS: Aleksey Bragin <aleksey@reactos.org>
*/
/* INCLUDES *****************************************************************/
#define NDEBUG
#include "fastfat.h"
+#define TAG_FILENAME 'fBnF'
+
/* FUNCTIONS ****************************************************************/
-/**
- * Locates FCB by the supplied name in the cache trie of fcbs.
- *
- * @param ParentFcb
- * Supplies a pointer to the parent FCB
- *
- * @param Name
- * Supplied a name of the FCB to be located in cache.
- *
- * @return
- * Pointer to the found FCB or NULL.
- */
+FSRTL_COMPARISON_RESULT
+NTAPI
+FatiCompareNames(PSTRING NameA,
+ PSTRING NameB)
+{
+ ULONG MinimumLen, i;
+
+ /* Calc the minimum length */
+ MinimumLen = NameA->Length < NameB->Length ? NameA->Length :
+ NameB->Length;
+
+ /* Actually compare them */
+ i = (ULONG)RtlCompareMemory( NameA->Buffer, NameB->Buffer, MinimumLen );
+
+ if (i < MinimumLen)
+ {
+ /* Compare prefixes */
+ if (NameA->Buffer[i] < NameB->Buffer[i])
+ return LessThan;
+ else
+ return GreaterThan;
+ }
+
+ /* Final comparison */
+ if (NameA->Length < NameB->Length)
+ return LessThan;
+ else if (NameA->Length > NameB->Length)
+ return GreaterThan;
+ else
+ return EqualTo;
+}
+
PFCB
-FatLookupFcbByName(
- IN PFCB ParentFcb,
- IN PUNICODE_STRING Name)
+NTAPI
+FatFindFcb(PFAT_IRP_CONTEXT IrpContext,
+ PRTL_SPLAY_LINKS *RootNode,
+ PSTRING AnsiName,
+ PBOOLEAN IsDosName)
{
PFCB_NAME_LINK Node;
- PRTL_SPLAY_LINKS Links;
-
- /* Get sub-trie root node from the parent FCB */
- Links = ParentFcb->Dcb.SplayLinks;
- while (Links != NULL)
- {
- LONG Comparison;
-
- Node = CONTAINING_RECORD(Links, FCB_NAME_LINK, Links);
-
- /*
- * Compare the name stored in the node
- * and determine the direction to walk.
- */
- Comparison = RtlCompareUnicodeString(&Node->String, Name, TRUE);
- if (Comparison > 0) {
- /* Left child */
- Links = RtlLeftChild(&Node->Links);
- }
- else if (Comparison < 0)
- {
- /* Right child */
- Links = RtlRightChild(&Node->Links);
- }
- else
- {
- /* Strings are equal, we have found the node! */
- break;
- }
- }
-
- /* The case when nothing was found. */
- if (Links == NULL)
- return NULL;
-
- /* Cast node to the FCB structure. */
- return CONTAINING_RECORD(Links, FCB, FileName[Node->Type]);
+ FSRTL_COMPARISON_RESULT Comparison;
+ PRTL_SPLAY_LINKS Links;
+
+ Links = *RootNode;
+
+ while (Links)
+ {
+ Node = CONTAINING_RECORD(Links, FCB_NAME_LINK, Links);
+
+ /* Compare the prefix */
+ if (*(PUCHAR)Node->Name.Ansi.Buffer != *(PUCHAR)AnsiName->Buffer)
+ {
+ if (*(PUCHAR)Node->Name.Ansi.Buffer < *(PUCHAR)AnsiName->Buffer)
+ Comparison = LessThan;
+ else
+ Comparison = GreaterThan;
+ }
+ else
+ {
+ /* Perform real comparison */
+ Comparison = FatiCompareNames(&Node->Name.Ansi, AnsiName);
+ }
+
+ /* Do they match? */
+ if (Comparison == GreaterThan)
+ {
+ /* No, it's greater, go to the left child */
+ Links = RtlLeftChild(Links);
+ }
+ else if (Comparison == LessThan)
+ {
+ /* No, it's lesser, go to the right child */
+ Links = RtlRightChild(Links);
+ }
+ else
+ {
+ /* Exact match, balance the tree */
+ *RootNode = RtlSplay(Links);
+
+ /* Save type of the name, if needed */
+ if (IsDosName)
+ *IsDosName = Node->IsDosName;
+
+ /* Return the found fcb */
+ return Node->Fcb;
+ }
+ }
+
+ /* Nothing found */
+ return NULL;
}
-/**
- * Inserts FCB into FCBs cache trie.
- *
- * @param ParentFcb
- * Supplies a pointer to the parent FCB
- *
- * @param Fcb
- * Supplied a pointer to the being inserted FCB.
- *
- * @return
- * TRUE if the FCB was successfully inserted,
- * FASLE in the case of name collision.
- */
-BOOLEAN
-FatLinkFcbNames(
- IN PFCB ParentFcb,
- IN PFCB Fcb)
+PFCB
+NTAPI
+FatCreateFcb(IN PFAT_IRP_CONTEXT IrpContext,
+ IN PVCB Vcb,
+ IN PFCB ParentDcb,
+ IN FF_FILE *FileHandle)
{
- PFCB_NAME_LINK Name;
- PRTL_SPLAY_LINKS Links;
-
- /* None of the parameters can be NULL */
- ASSERT(ParentFcb != NULL && Fcb != NULL);
-
- /* Get root links of the parent FCB. */
- Links = ParentFcb->Dcb.SplayLinks;
-
- /*
- * Get first file name
- * (short name for FAT because it's always there.
- */
- Name = Fcb->FileName;
-
- /*
- * Check if ParentDcb links are initialized,
- * at least one child FCB is cached.
- */
- if (Links == NULL)
- {
- ParentFcb->Dcb.SplayLinks = Links = &Name->Links;
- RtlInitializeSplayLinks(Links);
-
- /* Check if we have more names to cache. */
- if ((++Name)->String.Length == 0)
- return TRUE;
- }
- /* Lookup for the insertion point in the trie. */
- do
- {
- LONG Comparison;
- PFCB_NAME_LINK Node;
- PRTL_SPLAY_LINKS PrevLinks;
-
- PrevLinks = Links;
- Node = CONTAINING_RECORD(Links, FCB_NAME_LINK, Links);
- Comparison = RtlCompareUnicodeString(&Node->String, &Name->String, TRUE);
- if (Comparison > 0) {
- Links = RtlLeftChild(&Node->Links);
- if (Links == NULL)
- {
- RtlInsertAsLeftChild(PrevLinks, &Name->Links);
- break;
- }
- }
- else if (Comparison < 0)
- {
- Links = RtlRightChild(&Node->Links);
- if (Links == NULL)
- {
- RtlInsertAsRightChild(PrevLinks, &Name->Links);
- break;
- }
- }
- else
- {
- return FALSE;
- }
-
- /* Possibly switch to the second (lfn) name and cache that. */
- } while (Name == Fcb->FileName && (++Name)->String.Length > 0);
- return TRUE;
+ PFCB Fcb;
+
+ /* Allocate it and zero it */
+ Fcb = ExAllocatePoolWithTag(NonPagedPool, sizeof(FCB), TAG_FCB);
+ RtlZeroMemory(Fcb, sizeof(FCB));
+
+ /* Set node types */
+ Fcb->Header.NodeTypeCode = FAT_NTC_FCB;
+ Fcb->Header.NodeByteSize = sizeof(FCB);
+ Fcb->Condition = FcbGood;
+
+ /* Initialize resources */
+ Fcb->Header.Resource = &Fcb->Resource;
+ ExInitializeResourceLite(Fcb->Header.Resource);
+
+ Fcb->Header.PagingIoResource = &Fcb->PagingIoResource;
+ ExInitializeResourceLite(Fcb->Header.PagingIoResource);
+
+ /* Initialize mutexes */
+ Fcb->Header.FastMutex = &Fcb->HeaderMutex;
+ ExInitializeFastMutex(&Fcb->HeaderMutex);
+ FsRtlSetupAdvancedHeader(&Fcb->Header, &Fcb->HeaderMutex);
+
+ /* Insert into parent's DCB list */
+ InsertTailList(&ParentDcb->Dcb.ParentDcbList, &Fcb->ParentDcbLinks);
+
+ /* Set backlinks */
+ Fcb->ParentFcb = ParentDcb;
+ Fcb->Vcb = Vcb;
+
+ /* Set file handle and sizes */
+ Fcb->Header.FileSize.LowPart = FileHandle->Filesize;
+ Fcb->Header.ValidDataLength.LowPart = FileHandle->Filesize;
+ Fcb->FatHandle = FileHandle;
+
+ /* Set names */
+ FatSetFcbNames(IrpContext, Fcb);
+
+ return Fcb;
}
-/**
- * Unlinks FCB from the FCBs cache trie.
- *
- * @param ParentFcb
- * Supplies a pointer to the parent FCB
- *
- * @param Fcb
- * Supplied a pointer to the being unlinked FCB.
- *
- * @return
- * VOID
- */
-VOID
-FatUnlinkFcbNames(
- IN PFCB ParentFcb,
- IN PFCB Fcb)
+PCCB
+NTAPI
+FatCreateCcb()
{
- /* See if there is an lfn and unlink that. */
- if (Fcb->FileName[FcbLongName].String.Length > 0)
- ParentFcb->Dcb.SplayLinks =
- RtlDelete(&Fcb->FileName[FcbLongName].Links);
-
- /* See if there is a short name and unlink that. */
- if (Fcb->FileName[FcbShortName].String.Length > 0)
- ParentFcb->Dcb.SplayLinks =
- RtlDelete(&Fcb->FileName[FcbShortName].Links);
+ PCCB Ccb;
+
+ /* Allocate the CCB and zero it */
+ Ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(CCB), TAG_CCB);
+ RtlZeroMemory(Ccb, sizeof(CCB));
+
+ /* Set mandatory header */
+ Ccb->NodeTypeCode = FAT_NTC_FCB;
+ Ccb->NodeByteSize = sizeof(CCB);
+
+ return Ccb;
}
-NTSTATUS
-FatCreateFcb(
- OUT PFCB* CreatedFcb,
- IN PFAT_IRP_CONTEXT IrpContext,
- IN PFCB ParentFcb,
- IN PDIR_ENTRY Dirent,
- IN PUNICODE_STRING FileName,
- IN PUNICODE_STRING LongFileName OPTIONAL)
+VOID
+NTAPI
+FatSetFullNameInFcb(PFCB Fcb,
+ PUNICODE_STRING Name)
{
- NTSTATUS Status;
- PFCB Fcb;
-
- /* Allocate FCB structure. */
- Fcb = (PFCB) ExAllocateFromNPagedLookasideList(&FatGlobalData.NonPagedFcbList);
- if (Fcb == NULL)
- return STATUS_INSUFFICIENT_RESOURCES;
- RtlZeroMemory(Fcb, sizeof(FCB));
-
- /* Setup FCB Advanced Header. */
- Fcb->Header.NodeTypeCode = FAT_NTC_FCB;
- Fcb->Header.NodeByteSize = sizeof(*Fcb);
- ExInitializeResourceLite(&Fcb->Resource);
- Fcb->Header.Resource = &Fcb->Resource;
- ExInitializeResourceLite(&Fcb->PagingIoResource);
- Fcb->Header.PagingIoResource = &Fcb->PagingIoResource;
- ExInitializeFastMutex(&Fcb->HeaderMutex);
- FsRtlSetupAdvancedHeader(&Fcb->Header, &Fcb->HeaderMutex);
- Fcb->Header.FileSize.QuadPart = Dirent->FileSize;
- Fcb->Header.ValidDataLength.QuadPart = Dirent->FileSize;
- Fcb->Header.IsFastIoPossible = FastIoIsNotPossible;
-
- /* Setup main fields. */
- FsRtlInitializeFileLock(&Fcb->Lock, NULL, NULL);
- FsRtlInitializeLargeMcb(&Fcb->Mcb, PagedPool);
- Fcb->Vcb = IrpContext->Vcb;
- Fcb->ParentFcb = ParentFcb;
- Fcb->FirstCluster = Dirent->FirstCluster
- | (Dirent->FirstClusterOfFileHi << 0x10);
-
- /* Setup basic info. */
- Fcb->BasicInfo.FileAttributes = Dirent->Attributes;
- FatQueryFileTimes(&Fcb->BasicInfo.CreationTime, Dirent);
-
- /* Setup short name since always present in FAT. */
- Fcb->FileName[FcbShortName].Type = FcbShortName;
- Fcb->FileName[FcbShortName].String.Buffer = Fcb->ShortNameBuffer;
- Fcb->FileName[FcbShortName].String.MaximumLength = 0x0c;
- Fcb->FileName[FcbShortName].String.Length = FileName->Length;
- RtlCopyMemory(Fcb->ShortNameBuffer, FileName->Buffer, FileName->Length);
-
- /* Just swap optional lfn. */
- if (ARGUMENT_PRESENT(LongFileName) && LongFileName->Length > 0)
- {
- Fcb->FileName[FcbLongName].Type = FcbLongName;
- Fcb->FileName[FcbLongName].String = *LongFileName;
- RtlZeroMemory(LongFileName, sizeof(UNICODE_STRING));
- }
-
- /* Put FCB into cache trie. */
- if (!FatLinkFcbNames(ParentFcb, Fcb))
- {
- Status = STATUS_OBJECT_NAME_COLLISION;
- goto FsdFatCreateFcbCleanup;
- }
- *CreatedFcb = Fcb;
-
- /* We are done! */
- return STATUS_SUCCESS;
-
-FsdFatCreateFcbCleanup:
- if (ARGUMENT_PRESENT(LongFileName) &&
- Fcb->FileName[FcbLongName].String.Buffer != NULL)
+ PUNICODE_STRING ParentName;
+
+ /* Make sure this FCB's name wasn't already set */
+ ASSERT(Fcb->FullFileName.Buffer == NULL);
+
+ /* First of all, check exact case name */
+ if (Fcb->ExactCaseLongName.Buffer)
{
- /* Swap lfn back to the input parameter */
- *LongFileName = Fcb->FileName[FcbLongName].String;
+ ASSERT(Fcb->ExactCaseLongName.Length != 0);
+
+ /* Use exact case name */
+ Name = &Fcb->ExactCaseLongName;
+ }
+
+ /* Treat root dir different */
+ if (FatNodeType(Fcb->ParentFcb) == FAT_NTC_ROOT_DCB)
+ {
+ /* Set lengths */
+ Fcb->FullFileName.MaximumLength = sizeof(WCHAR) + Name->Length;
+ Fcb->FullFileName.Length = Fcb->FullFileName.MaximumLength;
+
+ /* Allocate a buffer */
+ Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag(PagedPool,
+ Fcb->FullFileName.Length,
+ TAG_FILENAME);
+
+ /* Prefix with a backslash */
+ Fcb->FullFileName.Buffer[0] = L'\\';
+
+ /* Copy the name here */
+ RtlCopyMemory(&Fcb->FullFileName.Buffer[1],
+ &Name->Buffer[0],
+ Name->Length );
+ }
+ else
+ {
+ ParentName = &Fcb->ParentFcb->FullFileName;
+
+ /* Check if parent's name is set */
+ if (!ParentName->Buffer)
+ return;
+
+ /* Set lengths */
+ Fcb->FullFileName.MaximumLength =
+ ParentName->Length + sizeof(WCHAR) + Name->Length;
+ Fcb->FullFileName.Length = Fcb->FullFileName.MaximumLength;
+
+ /* Allocate a buffer */
+ Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag(PagedPool,
+ Fcb->FullFileName.Length,
+ TAG_FILENAME );
+
+ /* Copy parent's name here */
+ RtlCopyMemory(&Fcb->FullFileName.Buffer[0],
+ &ParentName->Buffer[0],
+ ParentName->Length );
+
+ /* Add a backslash */
+ Fcb->FullFileName.Buffer[ParentName->Length / sizeof(WCHAR)] = L'\\';
+
+ /* Copy given name here */
+ RtlCopyMemory(&Fcb->FullFileName.Buffer[(ParentName->Length / sizeof(WCHAR)) + 1],
+ &Name->Buffer[0],
+ Name->Length );
}
- ExFreeToNPagedLookasideList(&FatGlobalData.NonPagedFcbList, Fcb);
- return Status;
}
-NTSTATUS
-FatOpenFcb(
- OUT PFCB* Fcb,
- IN PFAT_IRP_CONTEXT IrpContext,
- IN PFCB ParentFcb,
- IN PUNICODE_STRING FileName)
+VOID
+NTAPI
+FatSetFcbNames(IN PFAT_IRP_CONTEXT IrpContext,
+ IN PFCB Fcb)
{
- FAT_FIND_DIRENT_CONTEXT Context;
- UNICODE_STRING LongFileName;
- PDIR_ENTRY Dirent;
+ FF_DIRENT DirEnt;
+ FF_ERROR Err;
+ POEM_STRING ShortName;
+ CHAR ShortNameRaw[13];
+ UCHAR EntryBuffer[32];
+ UCHAR NumLFNs;
+ PUNICODE_STRING UnicodeName;
+ OEM_STRING LongNameOem;
NTSTATUS Status;
- // TODO: _SEH_TRY {
- if (ParentFcb->Dcb.StreamFileObject == NULL)
+ /* Get the dir entry */
+ Err = FF_GetEntry(Fcb->Vcb->Ioman,
+ Fcb->FatHandle->DirEntry,
+ Fcb->FatHandle->DirCluster,
+ &DirEnt);
+
+ if (Err != FF_ERR_NONE)
+ {
+ DPRINT1("Error %d getting dirent of a file\n", Err);
+ return;
+ }
+
+ /* Read the dirent to fetch the raw short name */
+ FF_FetchEntry(Fcb->Vcb->Ioman,
+ Fcb->FatHandle->DirCluster,
+ Fcb->FatHandle->DirEntry,
+ EntryBuffer);
+ NumLFNs = (UCHAR)(EntryBuffer[0] & ~0x40);
+ RtlCopyMemory(ShortNameRaw, EntryBuffer, 11);
+
+ /* Initialize short name string */
+ ShortName = &Fcb->ShortName.Name.Ansi;
+ ShortName->Buffer = Fcb->ShortNameBuffer;
+ ShortName->Length = 0;
+ ShortName->MaximumLength = sizeof(Fcb->ShortNameBuffer);
+
+ /* Convert raw short name to a proper string */
+ Fati8dot3ToString(ShortNameRaw, FALSE, ShortName);
+
+ /* Get the long file name (if any) */
+ if (NumLFNs > 0)
{
- PFILE_OBJECT FileObject;
- PVPB Vpb;
-
- Vpb = IrpContext->Vcb->Vpb;
+ /* Prepare the oem string */
+ LongNameOem.Buffer = DirEnt.FileName;
+ LongNameOem.MaximumLength = FF_MAX_FILENAME;
+ LongNameOem.Length = strlen(DirEnt.FileName);
+
+ /* Prepare the unicode string */
+ UnicodeName = &Fcb->LongName.Name.String;
+ UnicodeName->Length = (LongNameOem.Length + 1) * sizeof(WCHAR);
+ UnicodeName->MaximumLength = UnicodeName->Length;
+ UnicodeName->Buffer = FsRtlAllocatePool(PagedPool, UnicodeName->Length);
+
+ /* Convert it to unicode */
+ Status = RtlOemStringToUnicodeString(UnicodeName, &LongNameOem, FALSE);
+ if (!NT_SUCCESS(Status))
+ {
+ ASSERT(FALSE);
+ }
+
+ RtlDowncaseUnicodeString(UnicodeName, UnicodeName, FALSE);
+ RtlUpcaseUnicodeString(UnicodeName, UnicodeName, FALSE);
+
+ DPRINT1("Converted long name: %wZ\n", UnicodeName);
+ }
- /* Create stream file object */
- FileObject = IoCreateStreamFileObject(NULL, Vpb->RealDevice);
- FileObject->Vpb = Vpb;
- FileObject->SectionObjectPointer = &ParentFcb->SectionObjectPointers;
- FileObject->FsContext = ParentFcb;
- FileObject->FsContext2 = NULL;
+ // TODO: Add names to the splay tree
+}
- /* Store it in parent fcb */
- ParentFcb->Dcb.StreamFileObject = FileObject;
+VOID
+NTAPI
+Fati8dot3ToString(IN PCHAR FileName,
+ IN BOOLEAN DownCase,
+ OUT POEM_STRING OutString)
+{
+#if 1
+ ULONG BaseLen, ExtLen;
+ CHAR *cString = OutString->Buffer;
+ ULONG i;
+ /* Calc base and ext lens */
+ for (BaseLen = 8; BaseLen > 0; BaseLen--)
+ {
+ if (FileName[BaseLen - 1] != ' ') break;
}
- /* Check if cache is initialized. */
- if (ParentFcb->Dcb.StreamFileObject->PrivateCacheMap == NULL )
+ for (ExtLen = 3; ExtLen > 0; ExtLen--)
{
- CcInitializeCacheMap(ParentFcb->Dcb.StreamFileObject,
- (PCC_FILE_SIZES) &ParentFcb->Header.AllocationSize,
- TRUE,
- &FatGlobalData.CacheMgrNoopCallbacks,
- ParentFcb);
+ if (FileName[8 + ExtLen - 1] != ' ') break;
}
- /* Page context */
- Context.Page.FileObject = ParentFcb->Dcb.StreamFileObject;
- Context.Page.EndOfData = ParentFcb->Header.FileSize;
- Context.Page.Offset.QuadPart = -1LL;
- Context.Page.Bcb = NULL;
- Context.Page.CanWait = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT);
- Context.Page.EndOfData = ParentFcb->Header.FileSize;
-
- /* Search context */
- Context.ShortName.Length = 0;
- Context.ShortName.Buffer = Context.ShortNameBuffer;
- Context.ShortName.MaximumLength = sizeof(Context.ShortNameBuffer);
- Context.FileName = FileName;
- Context.Valid8dot3Name = RtlIsNameLegalDOS8Dot3(FileName, NULL, NULL);
-
- /* Locate the dirent */
- FatFindDirent(&Context, &Dirent, &LongFileName);
-
- Status = FatCreateFcb(Fcb, IrpContext, ParentFcb, Dirent,
- &Context.ShortName, &LongFileName);
- return Status;
+ /* Process base name */
+ if (BaseLen)
+ {
+ RtlCopyMemory(cString, FileName, BaseLen);
+
+ /* Substitute the e5 thing */
+ if (cString[0] == 0x05) cString[0] = 0xe5;
+
+ /* Downcase if asked to */
+ if (DownCase)
+ {
+ /* Do it manually */
+ for (i = 0; i < BaseLen; i++)
+ {
+ if (cString[i] >= 'A' &&
+ cString[i] <= 'Z')
+ {
+ /* Lowercase it */
+ cString[i] += 'a' - 'A';
+ }
+
+ }
+ }
+ }
+
+ /* Process extension */
+ if (ExtLen)
+ {
+ /* Add the dot */
+ cString[BaseLen] = '.';
+ BaseLen++;
+
+ /* Copy the extension */
+ for (i = 0; i < ExtLen; i++)
+ {
+ cString[BaseLen + i] = FileName[8 + i];
+ }
+
+ /* Lowercase the extension if asked to */
+ if (DownCase)
+ {
+ /* Do it manually */
+ for (i = BaseLen; i < BaseLen + ExtLen; i++)
+ {
+ if (cString[i] >= 'A' &&
+ cString[i] <= 'Z')
+ {
+ /* Lowercase it */
+ cString[i] += 'a' - 'A';
+ }
+ }
+ }
+ }
+
+ /* Set the length */
+ OutString->Length = BaseLen + ExtLen;
+#else
+ RtlCopyMemory(OutString->Buffer, FileName, 11);
+ OutString->Length = strlen(FileName);
+ ASSERT(OutString->Length <= 12);
+#endif
+
+ DPRINT1("'%s', len %d\n", OutString->Buffer, OutString->Length);
}
+
+
/* EOF */