/*
* PROJECT: ReactOS FAT file system driver
- * LICENSE: GPL - See COPYING in the top level directory
+ * LICENSE: GNU GPLv3 as published by the Free Software Foundation
* FILE: drivers/filesystems/fastfat/fcb.c
* PURPOSE: FCB manipulation routines.
* PROGRAMMERS: Aleksey Bragin <aleksey@reactos.org>
return Ccb;
}
+IO_STATUS_BLOCK
+NTAPI
+FatiOpenExistingFcb(IN PFAT_IRP_CONTEXT IrpContext,
+ IN PFILE_OBJECT FileObject,
+ IN PVCB Vcb,
+ IN PFCB Fcb,
+ IN PACCESS_MASK DesiredAccess,
+ IN USHORT ShareAccess,
+ IN ULONG AllocationSize,
+ IN PFILE_FULL_EA_INFORMATION EaBuffer,
+ IN ULONG EaLength,
+ IN UCHAR FileAttributes,
+ IN ULONG CreateDisposition,
+ IN BOOLEAN NoEaKnowledge,
+ IN BOOLEAN DeleteOnClose,
+ IN BOOLEAN OpenedAsDos,
+ OUT PBOOLEAN OplockPostIrp)
+{
+ IO_STATUS_BLOCK Iosb = {{0}};
+ ACCESS_MASK AddedAccess = 0;
+ BOOLEAN Hidden;
+ BOOLEAN System;
+ PCCB Ccb = NULL;
+ NTSTATUS Status;
+
+ /* Acquire exclusive FCB lock */
+ (VOID)FatAcquireExclusiveFcb(IrpContext, Fcb);
+
+ *OplockPostIrp = FALSE;
+
+ /* Check if there is a batch oplock */
+ if (FsRtlCurrentBatchOplock(&Fcb->Fcb.Oplock))
+ {
+ /* Return with a special information field */
+ Iosb.Information = FILE_OPBATCH_BREAK_UNDERWAY;
+
+ /* Check the oplock */
+ Iosb.Status = FsRtlCheckOplock(&Fcb->Fcb.Oplock,
+ IrpContext->Irp,
+ IrpContext,
+ FatOplockComplete,
+ FatPrePostIrp);
+
+ if (Iosb.Status != STATUS_SUCCESS &&
+ Iosb.Status != STATUS_OPLOCK_BREAK_IN_PROGRESS)
+ {
+ /* The Irp needs to be queued */
+ *OplockPostIrp = TRUE;
+
+ /* Release the FCB and return */
+ FatReleaseFcb(IrpContext, Fcb);
+ return Iosb;
+ }
+ }
+
+ /* Validate parameters and modify access */
+ if (CreateDisposition == FILE_CREATE)
+ {
+ Iosb.Status = STATUS_OBJECT_NAME_COLLISION;
+
+ /* Release the FCB and return */
+ FatReleaseFcb(IrpContext, Fcb);
+ return Iosb;
+ }
+ else if (CreateDisposition == FILE_SUPERSEDE)
+ {
+ SetFlag(AddedAccess, DELETE & ~(*DesiredAccess));
+ *DesiredAccess |= DELETE;
+ }
+ else if ((CreateDisposition == FILE_OVERWRITE) ||
+ (CreateDisposition == FILE_OVERWRITE_IF))
+ {
+ SetFlag(AddedAccess,
+ (FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES)
+ & ~(*DesiredAccess) );
+
+ *DesiredAccess |= FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES;
+ }
+
+ // TODO: Check desired access
+
+ // TODO: Check if this file is readonly and DeleteOnClose is set
+
+ /* Validate disposition information */
+ if ((CreateDisposition == FILE_SUPERSEDE) ||
+ (CreateDisposition == FILE_OVERWRITE) ||
+ (CreateDisposition == FILE_OVERWRITE_IF))
+ {
+ // TODO: Get this attributes from the dirent
+ Hidden = FALSE;
+ System = FALSE;
+
+ if ((Hidden && !FlagOn(FileAttributes, FILE_ATTRIBUTE_HIDDEN)) ||
+ (System && !FlagOn(FileAttributes, FILE_ATTRIBUTE_SYSTEM)))
+ {
+ DPRINT1("Hidden/system attributes don't match\n");
+
+ Iosb.Status = STATUS_ACCESS_DENIED;
+
+ /* Release the FCB and return */
+ FatReleaseFcb(IrpContext, Fcb);
+ return Iosb;
+ }
+
+ // TODO: Check for write protected volume
+ }
+
+ /* Check share access */
+ Iosb.Status = IoCheckShareAccess(*DesiredAccess,
+ ShareAccess,
+ FileObject,
+ &Fcb->ShareAccess,
+ FALSE);
+ if (!NT_SUCCESS(Iosb.Status))
+ {
+ /* Release the FCB and return */
+ FatReleaseFcb(IrpContext, Fcb);
+ return Iosb;
+ }
+
+ /* Check the oplock status after checking for share access */
+ Iosb.Status = FsRtlCheckOplock(&Fcb->Fcb.Oplock,
+ IrpContext->Irp,
+ IrpContext,
+ FatOplockComplete,
+ FatPrePostIrp );
+
+ if (Iosb.Status != STATUS_SUCCESS &&
+ Iosb.Status != STATUS_OPLOCK_BREAK_IN_PROGRESS)
+ {
+ /* The Irp needs to be queued */
+ *OplockPostIrp = TRUE;
+
+ /* Release the FCB and return */
+ FatReleaseFcb(IrpContext, Fcb);
+ return Iosb;
+ }
+
+ /* Set Fast I/O flag */
+ Fcb->Header.IsFastIoPossible = FALSE; //FatiIsFastIoPossible(Fcb);
+
+ /* Make sure image is not mapped */
+ if (DeleteOnClose || FlagOn(*DesiredAccess, FILE_WRITE_DATA))
+ {
+ /* Try to flush the image section */
+ if (!MmFlushImageSection(&Fcb->SectionObjectPointers, MmFlushForWrite))
+ {
+ /* Yes, image section exists, set correct status code */
+ if (DeleteOnClose)
+ Iosb.Status = STATUS_CANNOT_DELETE;
+ else
+ Iosb.Status = STATUS_SHARING_VIOLATION;
+
+ /* Release the FCB and return */
+ FatReleaseFcb(IrpContext, Fcb);
+ return Iosb;
+ }
+ }
+
+ /* Flush the cache if it's non-cached non-pagefile access */
+ if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING) &&
+ Fcb->SectionObjectPointers.DataSectionObject &&
+ !FlagOn(Fcb->State, FCB_STATE_PAGEFILE))
+ {
+ /* Set the flag that create is in progress */
+ SetFlag(Fcb->Vcb->State, VCB_STATE_CREATE_IN_PROGRESS);
+
+ /* Flush the cache */
+ CcFlushCache(&Fcb->SectionObjectPointers, NULL, 0, NULL);
+
+ /* Acquire and release Paging I/O resource before purging the cache section
+ to let lazy writer finish */
+ ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE);
+ ExReleaseResourceLite( Fcb->Header.PagingIoResource );
+
+ /* Delete the cache section */
+ CcPurgeCacheSection(&Fcb->SectionObjectPointers, NULL, 0, FALSE);
+
+ /* Clear the flag */
+ ClearFlag(Fcb->Vcb->State, VCB_STATE_CREATE_IN_PROGRESS);
+ }
+
+ /* Check create disposition flags and branch accordingly */
+ if (CreateDisposition == FILE_OPEN ||
+ CreateDisposition == FILE_OPEN_IF)
+ {
+ DPRINT("Opening a file\n");
+
+ /* Check if we need to bother with EA */
+ if (NoEaKnowledge && FALSE /* FatIsFat32(Vcb)*/)
+ {
+ UNIMPLEMENTED;
+ }
+
+ /* Set up file object */
+ Ccb = FatCreateCcb(IrpContext);
+ FatSetFileObject(FileObject,
+ UserFileOpen,
+ Fcb,
+ Ccb);
+
+ FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
+
+ /* The file is opened */
+ Iosb.Information = FILE_OPENED;
+ goto SuccComplete;
+ }
+ else if ((CreateDisposition == FILE_SUPERSEDE) ||
+ (CreateDisposition == FILE_OVERWRITE) ||
+ (CreateDisposition == FILE_OVERWRITE_IF))
+ {
+ UNIMPLEMENTED;
+ ASSERT(FALSE);
+ }
+ else
+ {
+ /* We can't get here */
+ KeBugCheckEx(0x23, CreateDisposition, 0, 0, 0);
+ }
+
+
+SuccComplete:
+ /* If all is fine */
+ if (Iosb.Status != STATUS_PENDING &&
+ NT_SUCCESS(Iosb.Status))
+ {
+ /* Update access if needed */
+ if (AddedAccess)
+ {
+ /* Remove added access flags from desired access */
+ ClearFlag(*DesiredAccess, AddedAccess);
+
+ /* Check share access */
+ Status = IoCheckShareAccess(*DesiredAccess,
+ ShareAccess,
+ FileObject,
+ &Fcb->ShareAccess,
+ TRUE);
+
+ /* Make sure it's success */
+ ASSERT(Status == STATUS_SUCCESS);
+ }
+ else
+ {
+ /* Update the share access */
+ IoUpdateShareAccess(FileObject, &Fcb->ShareAccess);
+ }
+
+ /* Clear the delay close */
+ ClearFlag(Fcb->State, FCB_STATE_DELAY_CLOSE);
+
+ /* Increase counters */
+ Fcb->OpenCount++;
+ Vcb->OpenFileCount++;
+
+ // TODO: Handle DeleteOnClose and OpenedAsDos by storing those flags in CCB
+ }
+
+ return Iosb;
+}
+
+VOID
+NTAPI
+FatGetFcbUnicodeName(IN PFAT_IRP_CONTEXT IrpContext,
+ IN PFCB Fcb,
+ OUT PUNICODE_STRING LongName)
+{
+ FF_DIRENT DirEnt;
+ FF_ERROR Err;
+ OEM_STRING ShortName;
+ CHAR ShortNameBuf[13];
+ UCHAR EntryBuffer[32];
+ UCHAR NumLFNs;
+ OEM_STRING LongNameOem;
+ NTSTATUS Status;
+
+ /* Make sure this FCB has a FullFAT handle associated with it */
+ if (Fcb->FatHandle == NULL &&
+ FatNodeType(Fcb) == FAT_NTC_DCB)
+ {
+ /* Open the dir with FullFAT */
+ Fcb->FatHandle = FF_OpenW(Fcb->Vcb->Ioman, &Fcb->FullFileName, FF_MODE_DIR, NULL);
+ if (!Fcb->FatHandle)
+ {
+ ASSERT(FALSE);
+ }
+ }
+
+ /* 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);
+
+ /* Check if we only have a short name.
+ Convert it to unicode and return if that's the case */
+ if (NumLFNs == 0)
+ {
+ /* Initialize short name string */
+ ShortName.Buffer = ShortNameBuf;
+ ShortName.Length = 0;
+ ShortName.MaximumLength = 12;
+
+ /* Convert raw short name to a proper string */
+ Fati8dot3ToString((PCHAR)EntryBuffer, FALSE, &ShortName);
+
+ /* Convert it to unicode */
+ Status = RtlOemStringToCountedUnicodeString(LongName,
+ &ShortName,
+ FALSE);
+
+ /* Ensure conversion was successful */
+ ASSERT(Status == STATUS_SUCCESS);
+
+ /* Exit */
+ return;
+ }
+
+ /* Convert LFN from OEM to unicode and return */
+ LongNameOem.Buffer = DirEnt.FileName;
+ LongNameOem.MaximumLength = FF_MAX_FILENAME;
+ LongNameOem.Length = strlen(DirEnt.FileName);
+
+ /* Convert it to unicode */
+ Status = RtlOemStringToUnicodeString(LongName, &LongNameOem, FALSE);
+
+ /* Ensure conversion was successful */
+ ASSERT(Status == STATUS_SUCCESS);
+}
+
+
VOID
NTAPI
FatSetFullNameInFcb(PFCB Fcb,
}
}
+VOID
+NTAPI
+FatSetFullFileNameInFcb(IN PFAT_IRP_CONTEXT IrpContext,
+ IN PFCB Fcb)
+{
+ UNICODE_STRING LongName;
+ PFCB CurFcb = Fcb;
+ PFCB StopFcb;
+ PWCHAR TmpBuffer;
+ ULONG PathLength = 0;
+
+ /* Do nothing if it's already set */
+ if (Fcb->FullFileName.Buffer) return;
+
+ /* Allocate a temporary buffer */
+ LongName.Length = 0;
+ LongName.MaximumLength = FF_MAX_FILENAME * sizeof(WCHAR);
+ LongName.Buffer =
+ FsRtlAllocatePoolWithTag(PagedPool,
+ FF_MAX_FILENAME * sizeof(WCHAR),
+ TAG_FILENAME);
+
+ /* Go through all parents to calculate needed length */
+ while (CurFcb != Fcb->Vcb->RootDcb)
+ {
+ /* Does current FCB have FullFileName set? */
+ if (CurFcb != Fcb &&
+ CurFcb->FullFileName.Buffer)
+ {
+ /* Yes, just use it! */
+ PathLength += CurFcb->FullFileName.Length;
+
+ Fcb->FullFileName.Buffer =
+ FsRtlAllocatePoolWithTag(PagedPool,
+ PathLength,
+ TAG_FILENAME);
+
+ RtlCopyMemory(Fcb->FullFileName.Buffer,
+ CurFcb->FullFileName.Buffer,
+ CurFcb->FullFileName.Length);
+
+ break;
+ }
+
+ /* Sum up length of a current item */
+ PathLength += CurFcb->FileNameLength + sizeof(WCHAR);
+
+ /* Go to the parent */
+ CurFcb = CurFcb->ParentFcb;
+ }
+
+ /* Allocate FullFileName if it wasn't already allocated above */
+ if (!Fcb->FullFileName.Buffer)
+ {
+ Fcb->FullFileName.Buffer =
+ FsRtlAllocatePoolWithTag(PagedPool,
+ PathLength,
+ TAG_FILENAME);
+ }
+
+ StopFcb = CurFcb;
+
+ CurFcb = Fcb;
+ TmpBuffer = Fcb->FullFileName.Buffer + PathLength / sizeof(WCHAR);
+
+ /* Set lengths */
+ Fcb->FullFileName.Length = PathLength;
+ Fcb->FullFileName.MaximumLength = PathLength;
+
+ while (CurFcb != StopFcb)
+ {
+ /* Get its unicode name */
+ FatGetFcbUnicodeName(IrpContext,
+ CurFcb,
+ &LongName);
+
+ /* Copy it */
+ TmpBuffer -= LongName.Length / sizeof(WCHAR);
+ RtlCopyMemory(TmpBuffer, LongName.Buffer, LongName.Length);
+
+ /* Append with a backslash */
+ TmpBuffer -= 1;
+ *TmpBuffer = L'\\';
+
+ /* Go to the parent */
+ CurFcb = CurFcb->ParentFcb;
+ }
+
+ /* Free the temp buffer */
+ ExFreePool(LongName.Buffer);
+}
+
+
VOID
NTAPI
FatSetFcbNames(IN PFAT_IRP_CONTEXT IrpContext,
ASSERT(FALSE);
}
+ /* Set its length */
+ Fcb->FileNameLength = UnicodeName->Length;
+
+ /* Save case-preserved copy */
+ Fcb->ExactCaseLongName.Length = UnicodeName->Length;
+ Fcb->ExactCaseLongName.MaximumLength = UnicodeName->Length;
+ Fcb->ExactCaseLongName.Buffer =
+ FsRtlAllocatePoolWithTag(PagedPool, UnicodeName->Length, TAG_FILENAME);
+
+ RtlCopyMemory(Fcb->ExactCaseLongName.Buffer,
+ UnicodeName->Buffer,
+ UnicodeName->Length);
+
+ /* Perform a trick which is done by MS's FASTFAT driver to monocase
+ the filename */
RtlDowncaseUnicodeString(UnicodeName, UnicodeName, FALSE);
RtlUpcaseUnicodeString(UnicodeName, UnicodeName, FALSE);
- DPRINT1("Converted long name: %wZ\n", UnicodeName);
+ DPRINT("Converted long name: %wZ\n", UnicodeName);
/* Add the long unicode name link */
FatInsertName(IrpContext, &Fcb->ParentFcb->Dcb.SplayLinksUnicode, &Fcb->LongName);
/* Indicate that this FCB has a unicode long name */
SetFlag(Fcb->State, FCB_STATE_HAS_UNICODE_NAME);
}
+ else
+ {
+ /* No LFN, set exact case name to 0 length */
+ Fcb->ExactCaseLongName.Length = 0;
+ Fcb->ExactCaseLongName.MaximumLength = 0;
+
+ /* Set the length based on the short name */
+ Fcb->FileNameLength = RtlOemStringToCountedUnicodeSize(ShortName);
+ }
/* Mark the fact that names were added to splay trees*/
SetFlag(Fcb->State, FCB_STATE_HAS_NAMES);
/* Set the length */
OutString->Length = BaseLen + ExtLen;
- DPRINT1("'%s', len %d\n", OutString->Buffer, OutString->Length);
+ DPRINT("'%s', len %d\n", OutString->Buffer, OutString->Length);
}
VOID
NameLink = CONTAINING_RECORD(*RootNode, FCB_NAME_LINK, Links);
while (TRUE)
{
- /* Compare prefixes */
- Comparison = FatiCompareNames(&NameLink->Name.Ansi, &Name->Name.Ansi);
+ /* Compare the prefix */
+ if (*(PUCHAR)NameLink->Name.Ansi.Buffer != *(PUCHAR)&Name->Name.Ansi.Buffer)
+ {
+ if (*(PUCHAR)NameLink->Name.Ansi.Buffer < *(PUCHAR)&Name->Name.Ansi.Buffer)
+ Comparison = LessThan;
+ else
+ Comparison = GreaterThan;
+ }
+ else
+ {
+ /* Perform real comparison */
+ Comparison = FatiCompareNames(&NameLink->Name.Ansi, &Name->Name.Ansi);
+ }
/* Check the bad case first */
if (Comparison == EqualTo)
if (!RtlLeftChild(&NameLink->Links))
{
/* It's absent, insert here and break */
- RtlInsertAsLeftChild(&NameLink->Links, &NameLink->Links);
+ RtlInsertAsLeftChild(&NameLink->Links, &Name->Links);
break;
}
else