mirror of
https://github.com/reactos/reactos.git
synced 2024-08-26 05:08:39 +00:00
![Hermès Bélusca-Maïto](/assets/img/avatar_default.png)
- Cache the RootFcb so that its cleanup can be handled separately during dismounting. - Force volume dismount at cleanup if the VCB_DISMOUNT_PENDING flag is set. - Actually dismount a volume if its VCB has been flagged as not good, or if we force dismounting. NOTE: In their *CheckForDismount() function, our 3rd-party FS drivers as well as MS' fastfat, perform a comparison check of the current VCB's VPB ReferenceCount with some sort of "dangling"/"residual" open count. It seems to be related to the fact that the volume root directory as well as auxiliary data stream(s) are still opened, and only these are allowed to be opened at that moment. After analysis it appears that for the ReactOS' fastfat, this number is equal to "3". - On dismounting, cleanup and destroy the RootFcb, VolumeFcb and the FATFileObject. Then cleanup the SpareVPB or the IoVPB members, and finish by removing the dismounted volume from the VolumeListEntry and cleaning up the notify synchronization object and the resources. - During dismounting, and on shutdown, flush the volume before resetting its dirty bit. - On shutdown, after volume flushing, try to unmount it without forcing. - Release the VCB resources only when we actually dismount the volume in VfatCheckForDismount(). - Initialize first the notify list and the synchronization object, before sending the FSRTL_VOLUME_MOUNT notification. - If we failed at mounting a volume but its VCB's FATFileObject was already initialized, first call CcUninitializeCacheMap() on it before dereferencing it. - Send FSRTL_VOLUME_LOCK, FSRTL_VOLUME_LOCK_FAILED and FSRTL_VOLUME_UNLOCK notifications during volume locking (and failure) and volume unlocking. - Flush the volume before locking it, and clean its dirty bit if needed. NOTE: In addition to checking for VCB_CLEAR_DIRTY, we also check for the presence of the VCB_IS_DIRTY flag before cleaning up the dirty bit: this allows us to not re-clean the bit if it has been previously cleaned. This is needed for instance in this scenario: - The volume is locked (it gets flushed and the dirty bit is possibly cleared); - The volume then gets formatted with a completely different FS, that possibly clears up the first sector (e.g. BTRFS ignores 1st sector); - The volume is then dismounted: if we didn't check whether VCB_IS_DIRTY was set prior to resetting it, we could attempt clearing it again! But now that the volume's filesystem has been completely changed, we would then try to modify the dirty bit on an erroneous position on disk! That's why it should not be touched in this case during dismounting. - The volume is unlocked (same comment as above), and later can be detected as being BTRFS.
1084 lines
30 KiB
C
1084 lines
30 KiB
C
/*
|
|
* FILE: drivers/filesystems/fastfat/fcb.c
|
|
* PURPOSE: Routines to manipulate FCBs.
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS kernel
|
|
* PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
|
|
* Rex Jolliff (rex@lvcablemodem.com)
|
|
* Herve Poussineau (reactos@poussine.freesurf.fr)
|
|
* Pierre Schweitzer (pierre@reactos.org)
|
|
*/
|
|
|
|
/* ------------------------------------------------------- INCLUDES */
|
|
|
|
#include "vfat.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
#ifdef __GNUC__
|
|
#include <wctype.h> /* towlower prototype */
|
|
#endif
|
|
|
|
/* -------------------------------------------------------- DEFINES */
|
|
|
|
#ifdef KDBG
|
|
extern UNICODE_STRING DebugFile;
|
|
#endif
|
|
|
|
/* -------------------------------------------------------- PUBLICS */
|
|
|
|
static
|
|
ULONG
|
|
vfatNameHash(
|
|
ULONG hash,
|
|
PUNICODE_STRING NameU)
|
|
{
|
|
PWCHAR last;
|
|
PWCHAR curr;
|
|
register WCHAR c;
|
|
|
|
// LFN could start from "."
|
|
//ASSERT(NameU->Buffer[0] != L'.');
|
|
curr = NameU->Buffer;
|
|
last = NameU->Buffer + NameU->Length / sizeof(WCHAR);
|
|
|
|
while(curr < last)
|
|
{
|
|
c = towlower(*curr++);
|
|
hash = (hash + (c << 4) + (c >> 4)) * 11;
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
VOID
|
|
vfatSplitPathName(
|
|
PUNICODE_STRING PathNameU,
|
|
PUNICODE_STRING DirNameU,
|
|
PUNICODE_STRING FileNameU)
|
|
{
|
|
PWCHAR pName;
|
|
USHORT Length = 0;
|
|
pName = PathNameU->Buffer + PathNameU->Length / sizeof(WCHAR) - 1;
|
|
while (*pName != L'\\' && pName >= PathNameU->Buffer)
|
|
{
|
|
pName--;
|
|
Length++;
|
|
}
|
|
ASSERT(*pName == L'\\' || pName < PathNameU->Buffer);
|
|
if (FileNameU)
|
|
{
|
|
FileNameU->Buffer = pName + 1;
|
|
FileNameU->Length = FileNameU->MaximumLength = Length * sizeof(WCHAR);
|
|
}
|
|
if (DirNameU)
|
|
{
|
|
DirNameU->Buffer = PathNameU->Buffer;
|
|
DirNameU->Length = (pName + 1 - PathNameU->Buffer) * sizeof(WCHAR);
|
|
DirNameU->MaximumLength = DirNameU->Length;
|
|
}
|
|
}
|
|
|
|
static
|
|
VOID
|
|
vfatInitFcb(
|
|
PVFATFCB Fcb,
|
|
PUNICODE_STRING NameU)
|
|
{
|
|
USHORT PathNameBufferLength;
|
|
|
|
if (NameU)
|
|
PathNameBufferLength = NameU->Length + sizeof(WCHAR);
|
|
else
|
|
PathNameBufferLength = 0;
|
|
|
|
Fcb->PathNameBuffer = ExAllocatePoolWithTag(NonPagedPool, PathNameBufferLength, TAG_FCB);
|
|
if (!Fcb->PathNameBuffer)
|
|
{
|
|
/* FIXME: what to do if no more memory? */
|
|
DPRINT1("Unable to initialize FCB for filename '%wZ'\n", NameU);
|
|
KeBugCheckEx(FAT_FILE_SYSTEM, (ULONG_PTR)Fcb, (ULONG_PTR)NameU, 0, 0);
|
|
}
|
|
|
|
Fcb->RFCB.NodeTypeCode = NODE_TYPE_FCB;
|
|
Fcb->RFCB.NodeByteSize = sizeof(VFATFCB);
|
|
|
|
Fcb->PathNameU.Length = 0;
|
|
Fcb->PathNameU.Buffer = Fcb->PathNameBuffer;
|
|
Fcb->PathNameU.MaximumLength = PathNameBufferLength;
|
|
Fcb->ShortNameU.Length = 0;
|
|
Fcb->ShortNameU.Buffer = Fcb->ShortNameBuffer;
|
|
Fcb->ShortNameU.MaximumLength = sizeof(Fcb->ShortNameBuffer);
|
|
Fcb->DirNameU.Buffer = Fcb->PathNameU.Buffer;
|
|
if (NameU && NameU->Length)
|
|
{
|
|
RtlCopyUnicodeString(&Fcb->PathNameU, NameU);
|
|
vfatSplitPathName(&Fcb->PathNameU, &Fcb->DirNameU, &Fcb->LongNameU);
|
|
}
|
|
else
|
|
{
|
|
Fcb->DirNameU.Buffer = Fcb->LongNameU.Buffer = NULL;
|
|
Fcb->DirNameU.MaximumLength = Fcb->DirNameU.Length = 0;
|
|
Fcb->LongNameU.MaximumLength = Fcb->LongNameU.Length = 0;
|
|
}
|
|
RtlZeroMemory(&Fcb->FCBShareAccess, sizeof(SHARE_ACCESS));
|
|
Fcb->OpenHandleCount = 0;
|
|
}
|
|
|
|
PVFATFCB
|
|
vfatNewFCB(
|
|
PDEVICE_EXTENSION pVCB,
|
|
PUNICODE_STRING pFileNameU)
|
|
{
|
|
PVFATFCB rcFCB;
|
|
|
|
DPRINT("'%wZ'\n", pFileNameU);
|
|
|
|
rcFCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->FcbLookasideList);
|
|
if (rcFCB == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
RtlZeroMemory(rcFCB, sizeof(VFATFCB));
|
|
vfatInitFcb(rcFCB, pFileNameU);
|
|
if (vfatVolumeIsFatX(pVCB))
|
|
rcFCB->Attributes = &rcFCB->entry.FatX.Attrib;
|
|
else
|
|
rcFCB->Attributes = &rcFCB->entry.Fat.Attrib;
|
|
rcFCB->Hash.Hash = vfatNameHash(0, &rcFCB->PathNameU);
|
|
rcFCB->Hash.self = rcFCB;
|
|
rcFCB->ShortHash.self = rcFCB;
|
|
ExInitializeResourceLite(&rcFCB->PagingIoResource);
|
|
ExInitializeResourceLite(&rcFCB->MainResource);
|
|
FsRtlInitializeFileLock(&rcFCB->FileLock, NULL, NULL);
|
|
ExInitializeFastMutex(&rcFCB->LastMutex);
|
|
rcFCB->RFCB.PagingIoResource = &rcFCB->PagingIoResource;
|
|
rcFCB->RFCB.Resource = &rcFCB->MainResource;
|
|
rcFCB->RFCB.IsFastIoPossible = FastIoIsNotPossible;
|
|
InitializeListHead(&rcFCB->ParentListHead);
|
|
|
|
return rcFCB;
|
|
}
|
|
|
|
static
|
|
VOID
|
|
vfatDelFCBFromTable(
|
|
PDEVICE_EXTENSION pVCB,
|
|
PVFATFCB pFCB)
|
|
{
|
|
ULONG Index;
|
|
ULONG ShortIndex;
|
|
HASHENTRY* entry;
|
|
|
|
Index = pFCB->Hash.Hash % pVCB->HashTableSize;
|
|
ShortIndex = pFCB->ShortHash.Hash % pVCB->HashTableSize;
|
|
|
|
if (pFCB->Hash.Hash != pFCB->ShortHash.Hash)
|
|
{
|
|
entry = pVCB->FcbHashTable[ShortIndex];
|
|
if (entry->self == pFCB)
|
|
{
|
|
pVCB->FcbHashTable[ShortIndex] = entry->next;
|
|
}
|
|
else
|
|
{
|
|
while (entry->next->self != pFCB)
|
|
{
|
|
entry = entry->next;
|
|
}
|
|
entry->next = pFCB->ShortHash.next;
|
|
}
|
|
}
|
|
entry = pVCB->FcbHashTable[Index];
|
|
if (entry->self == pFCB)
|
|
{
|
|
pVCB->FcbHashTable[Index] = entry->next;
|
|
}
|
|
else
|
|
{
|
|
while (entry->next->self != pFCB)
|
|
{
|
|
entry = entry->next;
|
|
}
|
|
entry->next = pFCB->Hash.next;
|
|
}
|
|
|
|
RemoveEntryList(&pFCB->FcbListEntry);
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
vfatMakeFullName(
|
|
PVFATFCB directoryFCB,
|
|
PUNICODE_STRING LongNameU,
|
|
PUNICODE_STRING ShortNameU,
|
|
PUNICODE_STRING NameU)
|
|
{
|
|
PWCHAR PathNameBuffer;
|
|
USHORT PathNameLength;
|
|
|
|
PathNameLength = directoryFCB->PathNameU.Length + max(LongNameU->Length, ShortNameU->Length);
|
|
if (!vfatFCBIsRoot(directoryFCB))
|
|
{
|
|
PathNameLength += sizeof(WCHAR);
|
|
}
|
|
|
|
if (PathNameLength > LONGNAME_MAX_LENGTH * sizeof(WCHAR))
|
|
{
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
PathNameBuffer = ExAllocatePoolWithTag(NonPagedPool, PathNameLength + sizeof(WCHAR), TAG_FCB);
|
|
if (!PathNameBuffer)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
NameU->Buffer = PathNameBuffer;
|
|
NameU->Length = 0;
|
|
NameU->MaximumLength = PathNameLength;
|
|
|
|
RtlCopyUnicodeString(NameU, &directoryFCB->PathNameU);
|
|
if (!vfatFCBIsRoot(directoryFCB))
|
|
{
|
|
RtlAppendUnicodeToString(NameU, L"\\");
|
|
}
|
|
if (LongNameU->Length > 0)
|
|
{
|
|
RtlAppendUnicodeStringToString(NameU, LongNameU);
|
|
}
|
|
else
|
|
{
|
|
RtlAppendUnicodeStringToString(NameU, ShortNameU);
|
|
}
|
|
NameU->Buffer[NameU->Length / sizeof(WCHAR)] = 0;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
vfatDestroyCCB(
|
|
PVFATCCB pCcb)
|
|
{
|
|
if (pCcb->SearchPattern.Buffer)
|
|
{
|
|
ExFreePoolWithTag(pCcb->SearchPattern.Buffer, TAG_SEARCH);
|
|
}
|
|
ExFreeToNPagedLookasideList(&VfatGlobalData->CcbLookasideList, pCcb);
|
|
}
|
|
|
|
VOID
|
|
vfatDestroyFCB(
|
|
PVFATFCB pFCB)
|
|
{
|
|
#ifdef KDBG
|
|
if (DebugFile.Buffer != NULL && FsRtlIsNameInExpression(&DebugFile, &pFCB->LongNameU, FALSE, NULL))
|
|
{
|
|
DPRINT1("Destroying: %p (%wZ) %d\n", pFCB, &pFCB->PathNameU, pFCB->RefCount);
|
|
}
|
|
#endif
|
|
|
|
FsRtlUninitializeFileLock(&pFCB->FileLock);
|
|
|
|
if (!vfatFCBIsRoot(pFCB) &&
|
|
!BooleanFlagOn(pFCB->Flags, FCB_IS_FAT) && !BooleanFlagOn(pFCB->Flags, FCB_IS_VOLUME))
|
|
{
|
|
RemoveEntryList(&pFCB->ParentListEntry);
|
|
}
|
|
ExFreePool(pFCB->PathNameBuffer);
|
|
ExDeleteResourceLite(&pFCB->PagingIoResource);
|
|
ExDeleteResourceLite(&pFCB->MainResource);
|
|
ASSERT(IsListEmpty(&pFCB->ParentListHead));
|
|
ExFreeToNPagedLookasideList(&VfatGlobalData->FcbLookasideList, pFCB);
|
|
}
|
|
|
|
BOOLEAN
|
|
vfatFCBIsRoot(
|
|
PVFATFCB FCB)
|
|
{
|
|
return FCB->PathNameU.Length == sizeof(WCHAR) && FCB->PathNameU.Buffer[0] == L'\\' ? TRUE : FALSE;
|
|
}
|
|
|
|
VOID
|
|
#ifndef KDBG
|
|
vfatGrabFCB(
|
|
#else
|
|
_vfatGrabFCB(
|
|
#endif
|
|
PDEVICE_EXTENSION pVCB,
|
|
PVFATFCB pFCB
|
|
#ifdef KDBG
|
|
,
|
|
PCSTR File,
|
|
ULONG Line,
|
|
PCSTR Func
|
|
#endif
|
|
)
|
|
{
|
|
#ifdef KDBG
|
|
if (DebugFile.Buffer != NULL && FsRtlIsNameInExpression(&DebugFile, &pFCB->LongNameU, FALSE, NULL))
|
|
{
|
|
DPRINT1("Inc ref count (%d, oc: %d) for: %p (%wZ) at: %s(%d) %s\n", pFCB->RefCount, pFCB->OpenHandleCount, pFCB, &pFCB->PathNameU, File, Line, Func);
|
|
}
|
|
#else
|
|
DPRINT("Grabbing FCB at %p: %wZ, refCount:%d\n",
|
|
pFCB, &pFCB->PathNameU, pFCB->RefCount);
|
|
#endif
|
|
|
|
ASSERT(ExIsResourceAcquiredExclusive(&pVCB->DirResource));
|
|
|
|
ASSERT(!BooleanFlagOn(pFCB->Flags, FCB_IS_FAT));
|
|
ASSERT(pFCB != pVCB->VolumeFcb && !BooleanFlagOn(pFCB->Flags, FCB_IS_VOLUME));
|
|
ASSERT(pFCB->RefCount > 0);
|
|
++pFCB->RefCount;
|
|
}
|
|
|
|
VOID
|
|
#ifndef KDBG
|
|
vfatReleaseFCB(
|
|
#else
|
|
_vfatReleaseFCB(
|
|
#endif
|
|
PDEVICE_EXTENSION pVCB,
|
|
PVFATFCB pFCB
|
|
#ifdef KDBG
|
|
,
|
|
PCSTR File,
|
|
ULONG Line,
|
|
PCSTR Func
|
|
#endif
|
|
)
|
|
{
|
|
PVFATFCB tmpFcb;
|
|
|
|
#ifdef KDBG
|
|
if (DebugFile.Buffer != NULL && FsRtlIsNameInExpression(&DebugFile, &pFCB->LongNameU, FALSE, NULL))
|
|
{
|
|
DPRINT1("Dec ref count (%d, oc: %d) for: %p (%wZ) at: %s(%d) %s\n", pFCB->RefCount, pFCB->OpenHandleCount, pFCB, &pFCB->PathNameU, File, Line, Func);
|
|
}
|
|
#else
|
|
DPRINT("Releasing FCB at %p: %wZ, refCount:%d\n",
|
|
pFCB, &pFCB->PathNameU, pFCB->RefCount);
|
|
#endif
|
|
|
|
ASSERT(ExIsResourceAcquiredExclusive(&pVCB->DirResource));
|
|
|
|
while (pFCB)
|
|
{
|
|
ULONG RefCount;
|
|
|
|
ASSERT(!BooleanFlagOn(pFCB->Flags, FCB_IS_FAT));
|
|
ASSERT(pFCB != pVCB->VolumeFcb && !BooleanFlagOn(pFCB->Flags, FCB_IS_VOLUME));
|
|
ASSERT(pFCB->RefCount > 0);
|
|
RefCount = --pFCB->RefCount;
|
|
|
|
if (RefCount == 1 && BooleanFlagOn(pFCB->Flags, FCB_CACHE_INITIALIZED))
|
|
{
|
|
PFILE_OBJECT tmpFileObject;
|
|
tmpFileObject = pFCB->FileObject;
|
|
|
|
pFCB->FileObject = NULL;
|
|
CcUninitializeCacheMap(tmpFileObject, NULL, NULL);
|
|
ClearFlag(pFCB->Flags, FCB_CACHE_INITIALIZED);
|
|
ObDereferenceObject(tmpFileObject);
|
|
}
|
|
|
|
if (RefCount == 0)
|
|
{
|
|
ASSERT(pFCB->OpenHandleCount == 0);
|
|
tmpFcb = pFCB->parentFcb;
|
|
vfatDelFCBFromTable(pVCB, pFCB);
|
|
vfatDestroyFCB(pFCB);
|
|
}
|
|
else
|
|
{
|
|
tmpFcb = NULL;
|
|
}
|
|
pFCB = tmpFcb;
|
|
}
|
|
}
|
|
|
|
static
|
|
VOID
|
|
vfatAddFCBToTable(
|
|
PDEVICE_EXTENSION pVCB,
|
|
PVFATFCB pFCB)
|
|
{
|
|
ULONG Index;
|
|
ULONG ShortIndex;
|
|
|
|
ASSERT(pFCB->Hash.Hash == vfatNameHash(0, &pFCB->PathNameU));
|
|
Index = pFCB->Hash.Hash % pVCB->HashTableSize;
|
|
ShortIndex = pFCB->ShortHash.Hash % pVCB->HashTableSize;
|
|
|
|
InsertTailList(&pVCB->FcbListHead, &pFCB->FcbListEntry);
|
|
|
|
pFCB->Hash.next = pVCB->FcbHashTable[Index];
|
|
pVCB->FcbHashTable[Index] = &pFCB->Hash;
|
|
if (pFCB->Hash.Hash != pFCB->ShortHash.Hash)
|
|
{
|
|
pFCB->ShortHash.next = pVCB->FcbHashTable[ShortIndex];
|
|
pVCB->FcbHashTable[ShortIndex] = &pFCB->ShortHash;
|
|
}
|
|
if (pFCB->parentFcb)
|
|
{
|
|
vfatGrabFCB(pVCB, pFCB->parentFcb);
|
|
}
|
|
}
|
|
|
|
static
|
|
VOID
|
|
vfatInitFCBFromDirEntry(
|
|
PDEVICE_EXTENSION Vcb,
|
|
PVFATFCB Fcb,
|
|
PVFAT_DIRENTRY_CONTEXT DirContext)
|
|
{
|
|
ULONG Size;
|
|
|
|
RtlCopyMemory(&Fcb->entry, &DirContext->DirEntry, sizeof (DIR_ENTRY));
|
|
RtlCopyUnicodeString(&Fcb->ShortNameU, &DirContext->ShortNameU);
|
|
Fcb->Hash.Hash = vfatNameHash(0, &Fcb->PathNameU);
|
|
if (vfatVolumeIsFatX(Vcb))
|
|
{
|
|
Fcb->ShortHash.Hash = Fcb->Hash.Hash;
|
|
}
|
|
else
|
|
{
|
|
Fcb->ShortHash.Hash = vfatNameHash(0, &Fcb->DirNameU);
|
|
Fcb->ShortHash.Hash = vfatNameHash(Fcb->ShortHash.Hash, &Fcb->ShortNameU);
|
|
}
|
|
|
|
if (vfatFCBIsDirectory(Fcb))
|
|
{
|
|
ULONG FirstCluster, CurrentCluster;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
Size = 0;
|
|
FirstCluster = vfatDirEntryGetFirstCluster(Vcb, &Fcb->entry);
|
|
if (FirstCluster == 1)
|
|
{
|
|
Size = Vcb->FatInfo.rootDirectorySectors * Vcb->FatInfo.BytesPerSector;
|
|
}
|
|
else if (FirstCluster != 0)
|
|
{
|
|
CurrentCluster = FirstCluster;
|
|
while (CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
|
|
{
|
|
Size += Vcb->FatInfo.BytesPerCluster;
|
|
Status = NextCluster(Vcb, FirstCluster, &CurrentCluster, FALSE);
|
|
}
|
|
}
|
|
}
|
|
else if (vfatVolumeIsFatX(Vcb))
|
|
{
|
|
Size = Fcb->entry.FatX.FileSize;
|
|
}
|
|
else
|
|
{
|
|
Size = Fcb->entry.Fat.FileSize;
|
|
}
|
|
Fcb->dirIndex = DirContext->DirIndex;
|
|
Fcb->startIndex = DirContext->StartIndex;
|
|
if (vfatVolumeIsFatX(Vcb) && !vfatFCBIsRoot(Fcb))
|
|
{
|
|
ASSERT(DirContext->DirIndex >= 2 && DirContext->StartIndex >= 2);
|
|
Fcb->dirIndex = DirContext->DirIndex-2;
|
|
Fcb->startIndex = DirContext->StartIndex-2;
|
|
}
|
|
Fcb->RFCB.FileSize.QuadPart = Size;
|
|
Fcb->RFCB.ValidDataLength.QuadPart = Size;
|
|
Fcb->RFCB.AllocationSize.QuadPart = ROUND_UP_64(Size, Vcb->FatInfo.BytesPerCluster);
|
|
}
|
|
|
|
NTSTATUS
|
|
vfatSetFCBNewDirName(
|
|
PDEVICE_EXTENSION pVCB,
|
|
PVFATFCB Fcb,
|
|
PVFATFCB ParentFcb)
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING NewNameU;
|
|
|
|
/* Get full path name */
|
|
Status = vfatMakeFullName(ParentFcb, &Fcb->LongNameU, &Fcb->ShortNameU, &NewNameU);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
/* Delete old name */
|
|
if (Fcb->PathNameBuffer)
|
|
{
|
|
ExFreePoolWithTag(Fcb->PathNameBuffer, TAG_FCB);
|
|
}
|
|
Fcb->PathNameU = NewNameU;
|
|
|
|
/* Delete from table */
|
|
vfatDelFCBFromTable(pVCB, Fcb);
|
|
|
|
/* Split it properly */
|
|
Fcb->PathNameBuffer = Fcb->PathNameU.Buffer;
|
|
Fcb->DirNameU.Buffer = Fcb->PathNameU.Buffer;
|
|
vfatSplitPathName(&Fcb->PathNameU, &Fcb->DirNameU, &Fcb->LongNameU);
|
|
Fcb->Hash.Hash = vfatNameHash(0, &Fcb->PathNameU);
|
|
if (vfatVolumeIsFatX(pVCB))
|
|
{
|
|
Fcb->ShortHash.Hash = Fcb->Hash.Hash;
|
|
}
|
|
else
|
|
{
|
|
Fcb->ShortHash.Hash = vfatNameHash(0, &Fcb->DirNameU);
|
|
Fcb->ShortHash.Hash = vfatNameHash(Fcb->ShortHash.Hash, &Fcb->ShortNameU);
|
|
}
|
|
|
|
vfatAddFCBToTable(pVCB, Fcb);
|
|
vfatReleaseFCB(pVCB, ParentFcb);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
vfatUpdateFCB(
|
|
PDEVICE_EXTENSION pVCB,
|
|
PVFATFCB Fcb,
|
|
PVFAT_DIRENTRY_CONTEXT DirContext,
|
|
PVFATFCB ParentFcb)
|
|
{
|
|
NTSTATUS Status;
|
|
PVFATFCB OldParent;
|
|
|
|
DPRINT("vfatUpdateFCB(%p, %p, %p, %p)\n", pVCB, Fcb, DirContext, ParentFcb);
|
|
|
|
/* Get full path name */
|
|
Status = vfatMakeFullName(ParentFcb, &DirContext->LongNameU, &DirContext->ShortNameU, &Fcb->PathNameU);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
/* Delete old name */
|
|
if (Fcb->PathNameBuffer)
|
|
{
|
|
ExFreePoolWithTag(Fcb->PathNameBuffer, TAG_FCB);
|
|
}
|
|
|
|
/* Delete from table */
|
|
vfatDelFCBFromTable(pVCB, Fcb);
|
|
|
|
/* Split it properly */
|
|
Fcb->PathNameBuffer = Fcb->PathNameU.Buffer;
|
|
Fcb->DirNameU.Buffer = Fcb->PathNameU.Buffer;
|
|
vfatSplitPathName(&Fcb->PathNameU, &Fcb->DirNameU, &Fcb->LongNameU);
|
|
|
|
/* Save old parent */
|
|
OldParent = Fcb->parentFcb;
|
|
RemoveEntryList(&Fcb->ParentListEntry);
|
|
|
|
/* Reinit FCB */
|
|
vfatInitFCBFromDirEntry(pVCB, Fcb, DirContext);
|
|
|
|
if (vfatFCBIsDirectory(Fcb))
|
|
{
|
|
CcFlushCache(&Fcb->SectionObjectPointers, NULL, 0, NULL);
|
|
}
|
|
Fcb->parentFcb = ParentFcb;
|
|
InsertTailList(&ParentFcb->ParentListHead, &Fcb->ParentListEntry);
|
|
vfatAddFCBToTable(pVCB, Fcb);
|
|
|
|
/* If we moved across directories, dereference our old parent
|
|
* We also dereference in case we're just renaming since AddFCBToTable references it
|
|
*/
|
|
vfatReleaseFCB(pVCB, OldParent);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
PVFATFCB
|
|
vfatGrabFCBFromTable(
|
|
PDEVICE_EXTENSION pVCB,
|
|
PUNICODE_STRING PathNameU)
|
|
{
|
|
PVFATFCB rcFCB;
|
|
ULONG Hash;
|
|
UNICODE_STRING DirNameU;
|
|
UNICODE_STRING FileNameU;
|
|
PUNICODE_STRING FcbNameU;
|
|
|
|
HASHENTRY* entry;
|
|
|
|
DPRINT("'%wZ'\n", PathNameU);
|
|
|
|
ASSERT(PathNameU->Length >= sizeof(WCHAR) && PathNameU->Buffer[0] == L'\\');
|
|
Hash = vfatNameHash(0, PathNameU);
|
|
|
|
entry = pVCB->FcbHashTable[Hash % pVCB->HashTableSize];
|
|
if (entry)
|
|
{
|
|
vfatSplitPathName(PathNameU, &DirNameU, &FileNameU);
|
|
}
|
|
|
|
while (entry)
|
|
{
|
|
if (entry->Hash == Hash)
|
|
{
|
|
rcFCB = entry->self;
|
|
DPRINT("'%wZ' '%wZ'\n", &DirNameU, &rcFCB->DirNameU);
|
|
if (RtlEqualUnicodeString(&DirNameU, &rcFCB->DirNameU, TRUE))
|
|
{
|
|
if (rcFCB->Hash.Hash == Hash)
|
|
{
|
|
FcbNameU = &rcFCB->LongNameU;
|
|
}
|
|
else
|
|
{
|
|
FcbNameU = &rcFCB->ShortNameU;
|
|
}
|
|
/* compare the file name */
|
|
DPRINT("'%wZ' '%wZ'\n", &FileNameU, FcbNameU);
|
|
if (RtlEqualUnicodeString(&FileNameU, FcbNameU, TRUE))
|
|
{
|
|
vfatGrabFCB(pVCB, rcFCB);
|
|
return rcFCB;
|
|
}
|
|
}
|
|
}
|
|
entry = entry->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
PVFATFCB
|
|
vfatMakeRootFCB(
|
|
PDEVICE_EXTENSION pVCB)
|
|
{
|
|
PVFATFCB FCB;
|
|
ULONG FirstCluster, CurrentCluster, Size = 0;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
UNICODE_STRING NameU = RTL_CONSTANT_STRING(L"\\");
|
|
|
|
ASSERT(pVCB->RootFcb == NULL);
|
|
|
|
FCB = vfatNewFCB(pVCB, &NameU);
|
|
if (vfatVolumeIsFatX(pVCB))
|
|
{
|
|
memset(FCB->entry.FatX.Filename, ' ', 42);
|
|
FCB->entry.FatX.FileSize = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
|
|
FCB->entry.FatX.Attrib = FILE_ATTRIBUTE_DIRECTORY;
|
|
FCB->entry.FatX.FirstCluster = 1;
|
|
Size = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
|
|
}
|
|
else
|
|
{
|
|
memset(FCB->entry.Fat.ShortName, ' ', 11);
|
|
FCB->entry.Fat.FileSize = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
|
|
FCB->entry.Fat.Attrib = FILE_ATTRIBUTE_DIRECTORY;
|
|
if (pVCB->FatInfo.FatType == FAT32)
|
|
{
|
|
CurrentCluster = FirstCluster = pVCB->FatInfo.RootCluster;
|
|
FCB->entry.Fat.FirstCluster = (unsigned short)(FirstCluster & 0xffff);
|
|
FCB->entry.Fat.FirstClusterHigh = (unsigned short)(FirstCluster >> 16);
|
|
|
|
while (CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
|
|
{
|
|
Size += pVCB->FatInfo.BytesPerCluster;
|
|
Status = NextCluster (pVCB, FirstCluster, &CurrentCluster, FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FCB->entry.Fat.FirstCluster = 1;
|
|
Size = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
|
|
}
|
|
}
|
|
FCB->ShortHash.Hash = FCB->Hash.Hash;
|
|
FCB->RefCount = 2;
|
|
FCB->dirIndex = 0;
|
|
FCB->RFCB.FileSize.QuadPart = Size;
|
|
FCB->RFCB.ValidDataLength.QuadPart = Size;
|
|
FCB->RFCB.AllocationSize.QuadPart = Size;
|
|
FCB->RFCB.IsFastIoPossible = FastIoIsNotPossible;
|
|
|
|
vfatFCBInitializeCacheFromVolume(pVCB, FCB);
|
|
vfatAddFCBToTable(pVCB, FCB);
|
|
|
|
/* Cache it */
|
|
pVCB->RootFcb = FCB;
|
|
|
|
return FCB;
|
|
}
|
|
|
|
PVFATFCB
|
|
vfatOpenRootFCB(
|
|
PDEVICE_EXTENSION pVCB)
|
|
{
|
|
PVFATFCB FCB;
|
|
UNICODE_STRING NameU = RTL_CONSTANT_STRING(L"\\");
|
|
|
|
FCB = vfatGrabFCBFromTable(pVCB, &NameU);
|
|
if (FCB == NULL)
|
|
{
|
|
FCB = vfatMakeRootFCB(pVCB);
|
|
}
|
|
ASSERT(FCB == pVCB->RootFcb);
|
|
|
|
return FCB;
|
|
}
|
|
|
|
NTSTATUS
|
|
vfatMakeFCBFromDirEntry(
|
|
PVCB vcb,
|
|
PVFATFCB directoryFCB,
|
|
PVFAT_DIRENTRY_CONTEXT DirContext,
|
|
PVFATFCB *fileFCB)
|
|
{
|
|
PVFATFCB rcFCB;
|
|
UNICODE_STRING NameU;
|
|
NTSTATUS Status;
|
|
|
|
Status = vfatMakeFullName(directoryFCB, &DirContext->LongNameU, &DirContext->ShortNameU, &NameU);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
rcFCB = vfatNewFCB(vcb, &NameU);
|
|
vfatInitFCBFromDirEntry(vcb, rcFCB, DirContext);
|
|
|
|
rcFCB->RefCount = 1;
|
|
rcFCB->parentFcb = directoryFCB;
|
|
InsertTailList(&directoryFCB->ParentListHead, &rcFCB->ParentListEntry);
|
|
vfatAddFCBToTable(vcb, rcFCB);
|
|
*fileFCB = rcFCB;
|
|
|
|
ExFreePoolWithTag(NameU.Buffer, TAG_FCB);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
vfatAttachFCBToFileObject(
|
|
PDEVICE_EXTENSION vcb,
|
|
PVFATFCB fcb,
|
|
PFILE_OBJECT fileObject)
|
|
{
|
|
PVFATCCB newCCB;
|
|
|
|
#ifdef KDBG
|
|
if (DebugFile.Buffer != NULL && FsRtlIsNameInExpression(&DebugFile, &fcb->LongNameU, FALSE, NULL))
|
|
{
|
|
DPRINT1("Attaching %p to %p (%d)\n", fcb, fileObject, fcb->RefCount);
|
|
}
|
|
#endif
|
|
|
|
newCCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->CcbLookasideList);
|
|
if (newCCB == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
RtlZeroMemory(newCCB, sizeof (VFATCCB));
|
|
|
|
fileObject->SectionObjectPointer = &fcb->SectionObjectPointers;
|
|
fileObject->FsContext = fcb;
|
|
fileObject->FsContext2 = newCCB;
|
|
fileObject->Vpb = vcb->IoVPB;
|
|
DPRINT("file open: fcb:%p PathName:%wZ\n", fcb, &fcb->PathNameU);
|
|
|
|
#ifdef KDBG
|
|
fcb->Flags &= ~FCB_CLEANED_UP;
|
|
fcb->Flags &= ~FCB_CLOSED;
|
|
#endif
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
vfatDirFindFile(
|
|
PDEVICE_EXTENSION pDeviceExt,
|
|
PVFATFCB pDirectoryFCB,
|
|
PUNICODE_STRING FileToFindU,
|
|
PVFATFCB *pFoundFCB)
|
|
{
|
|
NTSTATUS status;
|
|
PVOID Context = NULL;
|
|
PVOID Page = NULL;
|
|
BOOLEAN First = TRUE;
|
|
VFAT_DIRENTRY_CONTEXT DirContext;
|
|
/* This buffer must have a size of 260 characters, because
|
|
vfatMakeFCBFromDirEntry can copy 20 name entries with 13 characters. */
|
|
WCHAR LongNameBuffer[260];
|
|
WCHAR ShortNameBuffer[13];
|
|
BOOLEAN FoundLong = FALSE;
|
|
BOOLEAN FoundShort = FALSE;
|
|
BOOLEAN IsFatX = vfatVolumeIsFatX(pDeviceExt);
|
|
|
|
ASSERT(pDeviceExt);
|
|
ASSERT(pDirectoryFCB);
|
|
ASSERT(FileToFindU);
|
|
|
|
DPRINT("vfatDirFindFile(VCB:%p, dirFCB:%p, File:%wZ)\n",
|
|
pDeviceExt, pDirectoryFCB, FileToFindU);
|
|
DPRINT("Dir Path:%wZ\n", &pDirectoryFCB->PathNameU);
|
|
|
|
DirContext.DirIndex = 0;
|
|
DirContext.LongNameU.Buffer = LongNameBuffer;
|
|
DirContext.LongNameU.Length = 0;
|
|
DirContext.LongNameU.MaximumLength = sizeof(LongNameBuffer);
|
|
DirContext.ShortNameU.Buffer = ShortNameBuffer;
|
|
DirContext.ShortNameU.Length = 0;
|
|
DirContext.ShortNameU.MaximumLength = sizeof(ShortNameBuffer);
|
|
DirContext.DeviceExt = pDeviceExt;
|
|
|
|
while (TRUE)
|
|
{
|
|
status = VfatGetNextDirEntry(pDeviceExt,
|
|
&Context,
|
|
&Page,
|
|
pDirectoryFCB,
|
|
&DirContext,
|
|
First);
|
|
First = FALSE;
|
|
if (status == STATUS_NO_MORE_ENTRIES)
|
|
{
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
return status;
|
|
}
|
|
|
|
DPRINT(" Index:%u longName:%wZ\n",
|
|
DirContext.DirIndex, &DirContext.LongNameU);
|
|
|
|
if (!ENTRY_VOLUME(IsFatX, &DirContext.DirEntry))
|
|
{
|
|
if (DirContext.LongNameU.Length == 0 ||
|
|
DirContext.ShortNameU.Length == 0)
|
|
{
|
|
DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
|
|
if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION)
|
|
{
|
|
ASSERT(DirContext.LongNameU.Length != 0 &&
|
|
DirContext.ShortNameU.Length != 0);
|
|
}
|
|
DirContext.DirIndex++;
|
|
continue;
|
|
}
|
|
FoundLong = RtlEqualUnicodeString(FileToFindU, &DirContext.LongNameU, TRUE);
|
|
if (FoundLong == FALSE)
|
|
{
|
|
FoundShort = RtlEqualUnicodeString(FileToFindU, &DirContext.ShortNameU, TRUE);
|
|
}
|
|
if (FoundLong || FoundShort)
|
|
{
|
|
status = vfatMakeFCBFromDirEntry(pDeviceExt,
|
|
pDirectoryFCB,
|
|
&DirContext,
|
|
pFoundFCB);
|
|
CcUnpinData(Context);
|
|
return status;
|
|
}
|
|
}
|
|
DirContext.DirIndex++;
|
|
}
|
|
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
NTSTATUS
|
|
vfatGetFCBForFile(
|
|
PDEVICE_EXTENSION pVCB,
|
|
PVFATFCB *pParentFCB,
|
|
PVFATFCB *pFCB,
|
|
PUNICODE_STRING pFileNameU)
|
|
{
|
|
NTSTATUS status;
|
|
PVFATFCB FCB = NULL;
|
|
PVFATFCB parentFCB;
|
|
UNICODE_STRING NameU;
|
|
UNICODE_STRING RootNameU = RTL_CONSTANT_STRING(L"\\");
|
|
UNICODE_STRING FileNameU;
|
|
WCHAR NameBuffer[260];
|
|
PWCHAR curr, prev, last;
|
|
ULONG Length;
|
|
|
|
DPRINT("vfatGetFCBForFile (%p,%p,%p,%wZ)\n",
|
|
pVCB, pParentFCB, pFCB, pFileNameU);
|
|
|
|
RtlInitEmptyUnicodeString(&FileNameU, NameBuffer, sizeof(NameBuffer));
|
|
|
|
parentFCB = *pParentFCB;
|
|
|
|
if (parentFCB == NULL)
|
|
{
|
|
/* Passed-in name is the full name */
|
|
RtlCopyUnicodeString(&FileNameU, pFileNameU);
|
|
|
|
// Trivial case, open of the root directory on volume
|
|
if (RtlEqualUnicodeString(&FileNameU, &RootNameU, FALSE))
|
|
{
|
|
DPRINT("returning root FCB\n");
|
|
|
|
FCB = vfatOpenRootFCB(pVCB);
|
|
*pFCB = FCB;
|
|
*pParentFCB = NULL;
|
|
|
|
return (FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND;
|
|
}
|
|
|
|
/* Check for an existing FCB */
|
|
FCB = vfatGrabFCBFromTable(pVCB, &FileNameU);
|
|
if (FCB)
|
|
{
|
|
*pFCB = FCB;
|
|
*pParentFCB = FCB->parentFcb;
|
|
vfatGrabFCB(pVCB, *pParentFCB);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
last = curr = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
|
|
while (*curr != L'\\' && curr > FileNameU.Buffer)
|
|
{
|
|
curr--;
|
|
}
|
|
|
|
if (curr > FileNameU.Buffer)
|
|
{
|
|
NameU.Buffer = FileNameU.Buffer;
|
|
NameU.MaximumLength = NameU.Length = (curr - FileNameU.Buffer) * sizeof(WCHAR);
|
|
FCB = vfatGrabFCBFromTable(pVCB, &NameU);
|
|
if (FCB)
|
|
{
|
|
Length = (curr - FileNameU.Buffer) * sizeof(WCHAR);
|
|
if (Length != FCB->PathNameU.Length)
|
|
{
|
|
if (FileNameU.Length + FCB->PathNameU.Length - Length > FileNameU.MaximumLength)
|
|
{
|
|
vfatReleaseFCB(pVCB, FCB);
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
RtlMoveMemory(FileNameU.Buffer + FCB->PathNameU.Length / sizeof(WCHAR),
|
|
curr, FileNameU.Length - Length);
|
|
FileNameU.Length += (USHORT)(FCB->PathNameU.Length - Length);
|
|
curr = FileNameU.Buffer + FCB->PathNameU.Length / sizeof(WCHAR);
|
|
last = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
|
|
}
|
|
RtlCopyMemory(FileNameU.Buffer, FCB->PathNameU.Buffer, FCB->PathNameU.Length);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FCB = NULL;
|
|
}
|
|
|
|
if (FCB == NULL)
|
|
{
|
|
FCB = vfatOpenRootFCB(pVCB);
|
|
curr = FileNameU.Buffer;
|
|
}
|
|
|
|
parentFCB = NULL;
|
|
prev = curr;
|
|
}
|
|
else
|
|
{
|
|
/* Make absolute path */
|
|
RtlCopyUnicodeString(&FileNameU, &parentFCB->PathNameU);
|
|
curr = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
|
|
if (*curr != L'\\')
|
|
{
|
|
RtlAppendUnicodeToString(&FileNameU, L"\\");
|
|
curr++;
|
|
}
|
|
ASSERT(*curr == L'\\');
|
|
RtlAppendUnicodeStringToString(&FileNameU, pFileNameU);
|
|
|
|
FCB = parentFCB;
|
|
parentFCB = NULL;
|
|
prev = curr;
|
|
last = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
|
|
}
|
|
|
|
while (curr <= last)
|
|
{
|
|
if (parentFCB)
|
|
{
|
|
vfatReleaseFCB(pVCB, parentFCB);
|
|
parentFCB = NULL;
|
|
}
|
|
// fail if element in FCB is not a directory
|
|
if (!vfatFCBIsDirectory(FCB))
|
|
{
|
|
DPRINT ("Element in requested path is not a directory\n");
|
|
|
|
vfatReleaseFCB(pVCB, FCB);
|
|
FCB = NULL;
|
|
*pParentFCB = NULL;
|
|
*pFCB = NULL;
|
|
|
|
return STATUS_OBJECT_PATH_NOT_FOUND;
|
|
}
|
|
parentFCB = FCB;
|
|
if (prev < curr)
|
|
{
|
|
Length = (curr - prev) * sizeof(WCHAR);
|
|
if (Length != parentFCB->LongNameU.Length)
|
|
{
|
|
if (FileNameU.Length + parentFCB->LongNameU.Length - Length > FileNameU.MaximumLength)
|
|
{
|
|
vfatReleaseFCB(pVCB, parentFCB);
|
|
*pParentFCB = NULL;
|
|
*pFCB = NULL;
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
RtlMoveMemory(prev + parentFCB->LongNameU.Length / sizeof(WCHAR), curr,
|
|
FileNameU.Length - (curr - FileNameU.Buffer) * sizeof(WCHAR));
|
|
FileNameU.Length += (USHORT)(parentFCB->LongNameU.Length - Length);
|
|
curr = prev + parentFCB->LongNameU.Length / sizeof(WCHAR);
|
|
last = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
|
|
}
|
|
RtlCopyMemory(prev, parentFCB->LongNameU.Buffer, parentFCB->LongNameU.Length);
|
|
}
|
|
curr++;
|
|
prev = curr;
|
|
while (*curr != L'\\' && curr <= last)
|
|
{
|
|
curr++;
|
|
}
|
|
NameU.Buffer = FileNameU.Buffer;
|
|
NameU.Length = (curr - NameU.Buffer) * sizeof(WCHAR);
|
|
NameU.MaximumLength = FileNameU.MaximumLength;
|
|
DPRINT("%wZ\n", &NameU);
|
|
FCB = vfatGrabFCBFromTable(pVCB, &NameU);
|
|
if (FCB == NULL)
|
|
{
|
|
NameU.Buffer = prev;
|
|
NameU.MaximumLength = NameU.Length = (curr - prev) * sizeof(WCHAR);
|
|
status = vfatDirFindFile(pVCB, parentFCB, &NameU, &FCB);
|
|
if (status == STATUS_OBJECT_NAME_NOT_FOUND)
|
|
{
|
|
*pFCB = NULL;
|
|
if (curr > last)
|
|
{
|
|
*pParentFCB = parentFCB;
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
else
|
|
{
|
|
vfatReleaseFCB(pVCB, parentFCB);
|
|
*pParentFCB = NULL;
|
|
return STATUS_OBJECT_PATH_NOT_FOUND;
|
|
}
|
|
}
|
|
else if (!NT_SUCCESS(status))
|
|
{
|
|
vfatReleaseFCB(pVCB, parentFCB);
|
|
*pParentFCB = NULL;
|
|
*pFCB = NULL;
|
|
|
|
return status;
|
|
}
|
|
}
|
|
}
|
|
|
|
*pParentFCB = parentFCB;
|
|
*pFCB = FCB;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|