/*
* ReactOS kernel
- * Copyright (C) 2002 ReactOS Team
+ * Copyright (C) 2002, 2014 ReactOS Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* PROJECT: ReactOS kernel
* FILE: drivers/filesystem/ntfs/fcb.c
* PURPOSE: NTFS filesystem driver
- * PROGRAMMER: Eric Kohl
+ * PROGRAMMERS: Eric Kohl
+ * Pierre Schweitzer (pierre@reactos.org)
+ * Hervé Poussineau (hpoussin@reactos.org)
*/
/* INCLUDES *****************************************************************/
#define NDEBUG
#include <debug.h>
-/* GLOBALS *****************************************************************/
-
-
-
-/* MACROS *******************************************************************/
-
-#define TAG_FCB 'BCFI'
-
-
-
/* FUNCTIONS ****************************************************************/
static
-PWCHAR
-NtfsGetNextPathElement(PWCHAR FileName)
+PCWSTR
+NtfsGetNextPathElement(PCWSTR FileName)
{
if (*FileName == L'\0')
{
static
VOID
NtfsWSubString(PWCHAR pTarget,
- const PWCHAR pSource,
+ PCWSTR pSource,
size_t pLength)
{
wcsncpy(pTarget, pSource, pLength);
PNTFS_FCB
NtfsCreateFCB(PCWSTR FileName,
+ PCWSTR Stream,
PNTFS_VCB Vcb)
{
PNTFS_FCB Fcb;
ASSERT(Vcb);
ASSERT(Vcb->Identifier.Type == NTFS_TYPE_VCB);
- Fcb = ExAllocatePoolWithTag(NonPagedPool, sizeof(NTFS_FCB), TAG_FCB);
+ Fcb = ExAllocateFromNPagedLookasideList(&NtfsGlobalData->FcbLookasideList);
RtlZeroMemory(Fcb, sizeof(NTFS_FCB));
Fcb->Identifier.Type = NTFS_TYPE_FCB;
}
}
+ if (Stream)
+ {
+ wcscpy(Fcb->Stream, Stream);
+ }
+ else
+ {
+ Fcb->Stream[0] = UNICODE_NULL;
+ }
+
ExInitializeResourceLite(&Fcb->MainResource);
Fcb->RFCB.Resource = &(Fcb->MainResource);
ExDeleteResourceLite(&Fcb->MainResource);
- ExFreePool(Fcb);
+ ExFreeToNPagedLookasideList(&NtfsGlobalData->FcbLookasideList, Fcb);
}
BOOLEAN
NtfsFCBIsDirectory(PNTFS_FCB Fcb)
{
- UNREFERENCED_PARAMETER(Fcb);
-// return(Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY);
-// return(Fcb->Entry.FileFlags & 0x02);
- return TRUE;
+ return ((Fcb->Entry.FileAttributes & NTFS_FILE_TYPE_DIRECTORY) == NTFS_FILE_TYPE_DIRECTORY);
+}
+
+
+BOOLEAN
+NtfsFCBIsReparsePoint(PNTFS_FCB Fcb)
+{
+ return ((Fcb->Entry.FileAttributes & NTFS_FILE_TYPE_REPARSE) == NTFS_FILE_TYPE_REPARSE);
}
+BOOLEAN
+NtfsFCBIsCompressed(PNTFS_FCB Fcb)
+{
+ return ((Fcb->Entry.FileAttributes & NTFS_FILE_TYPE_COMPRESSED) == NTFS_FILE_TYPE_COMPRESSED);
+}
+
BOOLEAN
NtfsFCBIsRoot(PNTFS_FCB Fcb)
{
if (Fcb->RefCount <= 0 && !NtfsFCBIsDirectory(Fcb))
{
RemoveEntryList(&Fcb->FcbListEntry);
+ KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
CcUninitializeCacheMap(Fcb->FileObject, NULL, NULL);
NtfsDestroyFCB(Fcb);
}
-
- KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
+ else
+ {
+ KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
+ }
}
Fcb->Vcb = Vcb;
Status = STATUS_SUCCESS;
- CcInitializeCacheMap(FileObject,
- (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
- FALSE,
- &(NtfsGlobalData->CacheMgrCallbacks),
- Fcb);
+ _SEH2_TRY
+ {
+ CcInitializeCacheMap(FileObject,
+ (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
+ FALSE,
+ &(NtfsGlobalData->CacheMgrCallbacks),
+ Fcb);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ FileObject->FsContext2 = NULL;
+ ExFreePoolWithTag(newCCB, TAG_CCB);
+ ObDereferenceObject(FileObject);
+ Fcb->FileObject = NULL;
+ return _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
ObDereferenceObject(FileObject);
Fcb->Flags |= FCB_CACHE_INITIALIZED;
NtfsMakeRootFCB(PNTFS_VCB Vcb)
{
PNTFS_FCB Fcb;
+ PFILE_RECORD_HEADER MftRecord;
+ PFILENAME_ATTRIBUTE FileName;
- Fcb = NtfsCreateFCB(L"\\", Vcb);
+ MftRecord = ExAllocatePoolWithTag(NonPagedPool,
+ Vcb->NtfsInfo.BytesPerFileRecord,
+ TAG_NTFS);
+ if (MftRecord == NULL)
+ {
+ return NULL;
+ }
-// memset(Fcb->entry.Filename, ' ', 11);
+ if (!NT_SUCCESS(ReadFileRecord(Vcb, NTFS_FILE_ROOT, MftRecord)))
+ {
+ ExFreePoolWithTag(MftRecord, TAG_NTFS);
+ return NULL;
+ }
-// Fcb->Entry.DataLengthL = Vcb->CdInfo.RootSize;
-// Fcb->Entry.ExtentLocationL = Vcb->CdInfo.RootStart;
-// Fcb->Entry.FileFlags = 0x02; // FILE_ATTRIBUTE_DIRECTORY;
+ FileName = GetFileNameFromRecord(Vcb, MftRecord, NTFS_FILE_NAME_WIN32);
+ if (!FileName)
+ {
+ ExFreePoolWithTag(MftRecord, TAG_NTFS);
+ return NULL;
+ }
+
+ Fcb = NtfsCreateFCB(L"\\", NULL, Vcb);
+ if (!Fcb)
+ {
+ ExFreePoolWithTag(MftRecord, TAG_NTFS);
+ return NULL;
+ }
+
+ memcpy(&Fcb->Entry, FileName, FIELD_OFFSET(FILENAME_ATTRIBUTE, NameLength));
+ Fcb->Entry.NameType = FileName->NameType;
+ Fcb->Entry.NameLength = 0;
+ Fcb->Entry.Name[0] = UNICODE_NULL;
Fcb->RefCount = 1;
Fcb->DirIndex = 0;
- Fcb->RFCB.FileSize.QuadPart = PAGE_SIZE;//Vcb->CdInfo.RootSize;
- Fcb->RFCB.ValidDataLength.QuadPart = PAGE_SIZE;//Vcb->CdInfo.RootSize;
- Fcb->RFCB.AllocationSize.QuadPart = PAGE_SIZE;//Vcb->CdInfo.RootSize;
+ Fcb->RFCB.FileSize.QuadPart = FileName->DataSize;
+ Fcb->RFCB.ValidDataLength.QuadPart = FileName->DataSize;
+ Fcb->RFCB.AllocationSize.QuadPart = FileName->AllocatedSize;
+ Fcb->MFTIndex = NTFS_FILE_ROOT;
+ Fcb->LinkCount = MftRecord->LinkCount;
NtfsFCBInitializeCache(Vcb, Fcb);
NtfsAddFCBToTable(Vcb, Fcb);
NtfsGrabFCB(Vcb, Fcb);
+ ExFreePoolWithTag(MftRecord, TAG_NTFS);
+
return Fcb;
}
}
-#if 0
-static VOID
-NtfsGetDirEntryName(PDEVICE_EXTENSION DeviceExt,
- PDIR_RECORD Record,
- PWSTR Name)
-/*
- * FUNCTION: Retrieves the file name, be it in short or long file name format
- */
+NTSTATUS
+NtfsMakeFCBFromDirEntry(PNTFS_VCB Vcb,
+ PNTFS_FCB DirectoryFCB,
+ PUNICODE_STRING Name,
+ PCWSTR Stream,
+ PFILE_RECORD_HEADER Record,
+ ULONGLONG MFTIndex,
+ PNTFS_FCB * fileFCB)
{
- if (Record->FileIdLength == 1 && Record->FileId[0] == 0)
+ WCHAR pathName[MAX_PATH];
+ PFILENAME_ATTRIBUTE FileName;
+ PSTANDARD_INFORMATION StdInfo;
+ PNTFS_FCB rcFCB;
+ ULONGLONG Size, AllocatedSize;
+
+ DPRINT1("NtfsMakeFCBFromDirEntry(%p, %p, %wZ, %p, %p, %p)\n", Vcb, DirectoryFCB, Name, Stream, Record, fileFCB);
+
+ FileName = GetBestFileNameFromRecord(Vcb, Record);
+ if (!FileName)
{
- wcscpy(Name, L".");
+ return STATUS_OBJECT_NAME_NOT_FOUND; // Not sure that's the best here
}
- else if (Record->FileIdLength == 1 && Record->FileId[0] == 1)
+
+ if (DirectoryFCB && Name)
{
- wcscpy(Name, L"..");
+ if (Name->Buffer[0] != 0 && wcslen(DirectoryFCB->PathName) +
+ sizeof(WCHAR) + Name->Length / sizeof(WCHAR) > MAX_PATH)
+ {
+ return STATUS_OBJECT_NAME_INVALID;
+ }
+
+ wcscpy(pathName, DirectoryFCB->PathName);
+ if (!NtfsFCBIsRoot(DirectoryFCB))
+ {
+ wcscat(pathName, L"\\");
+ }
+ wcscat(pathName, Name->Buffer);
}
- else
+ else
{
- if (DeviceExt->CdInfo.JolietLevel == 0)
- {
- ULONG i;
-
- for (i = 0; i < Record->FileIdLength && Record->FileId[i] != ';'; i++)
- Name[i] = (WCHAR)Record->FileId[i];
- Name[i] = 0;
- }
- else
- {
- NtfsSwapString(Name, Record->FileId, Record->FileIdLength);
- }
+ RtlCopyMemory(pathName, FileName->Name, FileName->NameLength * sizeof (WCHAR));
+ pathName[FileName->NameLength] = UNICODE_NULL;
}
- DPRINT("Name '%S'\n", Name);
-}
-
-
-NTSTATUS
-NtfsMakeFCBFromDirEntry(PVCB Vcb,
- PFCB DirectoryFCB,
- PWSTR Name,
- PDIR_RECORD Record,
- PFCB * fileFCB)
-{
- WCHAR pathName[MAX_PATH];
- PFCB rcFCB;
- ULONG Size;
+ Size = NtfsGetFileSize(Vcb, Record, (Stream ? Stream : L""), (Stream ? wcslen(Stream) : 0), &AllocatedSize);
- if (Name [0] != 0 && wcslen (DirectoryFCB->PathName) +
- sizeof(WCHAR) + wcslen (Name) > MAX_PATH)
+ rcFCB = NtfsCreateFCB(pathName, Stream, Vcb);
+ if (!rcFCB)
{
- return(STATUS_OBJECT_NAME_INVALID);
+ return STATUS_INSUFFICIENT_RESOURCES;
}
- wcscpy(pathName, DirectoryFCB->PathName);
- if (!NtfsFCBIsRoot(DirectoryFCB))
- {
- wcscat(pathName, L"\\");
- }
+ memcpy(&rcFCB->Entry, FileName, FIELD_OFFSET(FILENAME_ATTRIBUTE, NameLength));
+ rcFCB->Entry.NameType = FileName->NameType;
+ rcFCB->RFCB.FileSize.QuadPart = Size;
+ rcFCB->RFCB.ValidDataLength.QuadPart = Size;
+ rcFCB->RFCB.AllocationSize.QuadPart = AllocatedSize;
- if (Name[0] != 0)
+ StdInfo = GetStandardInformationFromRecord(Vcb, Record);
+ if (StdInfo != NULL)
{
- wcscat(pathName, Name);
+ rcFCB->Entry.FileAttributes |= StdInfo->FileAttribute;
}
- else
- {
- WCHAR entryName[MAX_PATH];
- NtfsGetDirEntryName(Vcb, Record, entryName);
- wcscat(pathName, entryName);
- }
+ NtfsFCBInitializeCache(Vcb, rcFCB);
+ rcFCB->RefCount = 1;
+ rcFCB->MFTIndex = MFTIndex;
+ rcFCB->LinkCount = Record->LinkCount;
+ NtfsAddFCBToTable(Vcb, rcFCB);
+ *fileFCB = rcFCB;
- rcFCB = NtfsCreateFCB(pathName, Vcb);
- memcpy(&rcFCB->Entry, Record, sizeof(DIR_RECORD));
-
- Size = rcFCB->Entry.DataLengthL;
-
- rcFCB->RFCB.FileSize.QuadPart = Size;
- rcFCB->RFCB.ValidDataLength.QuadPart = Size;
- rcFCB->RFCB.AllocationSize.QuadPart = ROUND_UP(Size, BLOCKSIZE);
-// DPRINT1("%S %d %d\n", longName, Size, (ULONG)rcFCB->RFCB.AllocationSize.QuadPart);
- NtfsFCBInitializeCache(Vcb, rcFCB);
- rcFCB->RefCount++;
- NtfsAddFCBToTable(Vcb, rcFCB);
- *fileFCB = rcFCB;
-
- return(STATUS_SUCCESS);
+ return STATUS_SUCCESS;
}
-#endif
NTSTATUS
if (!(Fcb->Flags & FCB_CACHE_INITIALIZED))
{
- CcInitializeCacheMap(FileObject,
- (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
- FALSE,
- NULL,
- NULL);
+ _SEH2_TRY
+ {
+ CcInitializeCacheMap(FileObject,
+ (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
+ FALSE,
+ &(NtfsGlobalData->CacheMgrCallbacks),
+ Fcb);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ FileObject->FsContext2 = NULL;
+ ExFreePoolWithTag(newCCB, TAG_CCB);
+ return _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
Fcb->Flags |= FCB_CACHE_INITIALIZED;
}
NtfsDirFindFile(PNTFS_VCB Vcb,
PNTFS_FCB DirectoryFcb,
PWSTR FileToFind,
+ BOOLEAN CaseSensitive,
PNTFS_FCB *FoundFCB)
{
-#if 0
- WCHAR TempName[2];
- WCHAR Name[256];
- PVOID Block;
- ULONG FirstSector;
- ULONG DirSize;
- PDIR_RECORD Record;
- ULONG Offset;
- ULONG BlockOffset;
- NTSTATUS Status;
-
- LARGE_INTEGER StreamOffset;
- PVOID Context;
-
- ASSERT(DeviceExt);
- ASSERT(DirectoryFcb);
- ASSERT(FileToFind);
-
- DPRINT("NtfsDirFindFile(VCB:%08x, dirFCB:%08x, File:%S)\n",
- DeviceExt,
- DirectoryFcb,
- FileToFind);
- DPRINT("Dir Path:%S\n", DirectoryFcb->PathName);
-
- /* default to '.' if no filename specified */
- if (wcslen(FileToFind) == 0)
- {
- TempName[0] = L'.';
- TempName[1] = 0;
- FileToFind = TempName;
- }
-
- DirSize = DirectoryFcb->Entry.DataLengthL;
- StreamOffset.QuadPart = (LONGLONG)DirectoryFcb->Entry.ExtentLocationL * (LONGLONG)BLOCKSIZE;
-
- if(!CcMapData(DeviceExt->StreamFileObject, &StreamOffset,
- BLOCKSIZE, TRUE, &Context, &Block))
- {
- DPRINT("CcMapData() failed\n");
- return(STATUS_UNSUCCESSFUL);
- }
-
- Offset = 0;
- BlockOffset = 0;
- Record = (PDIR_RECORD)Block;
- while(TRUE)
- {
- if (Record->RecordLength == 0)
- {
- DPRINT("RecordLength == 0 Stopped!\n");
- break;
- }
-
- DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n",
- Record->RecordLength, Record->ExtAttrRecordLength, Record->FileIdLength);
-
- NtfsGetDirEntryName(DeviceExt, Record, Name);
- DPRINT("Name '%S'\n", Name);
-
- if (wstrcmpjoki(Name, FileToFind))
- {
- DPRINT("Match found, %S\n", Name);
- Status = NtfsMakeFCBFromDirEntry(DeviceExt,
- DirectoryFcb,
- Name,
- Record,
- FoundFCB);
-
- CcUnpinData(Context);
-
- return(Status);
- }
-
- Offset += Record->RecordLength;
- BlockOffset += Record->RecordLength;
- Record = (PDIR_RECORD)(Block + BlockOffset);
- if (BlockOffset >= BLOCKSIZE || Record->RecordLength == 0)
- {
- DPRINT("Map next sector\n");
- CcUnpinData(Context);
- StreamOffset.QuadPart += BLOCKSIZE;
- Offset = ROUND_UP(Offset, BLOCKSIZE);
- BlockOffset = 0;
-
- if (!CcMapData(DeviceExt->StreamFileObject,
- &StreamOffset,
- BLOCKSIZE, TRUE,
- &Context, &Block))
- {
- DPRINT("CcMapData() failed\n");
- return(STATUS_UNSUCCESSFUL);
- }
- Record = (PDIR_RECORD)(Block + BlockOffset);
- }
-
- if (Offset >= DirSize)
- break;
- }
-
- CcUnpinData(Context);
-#else
- UNREFERENCED_PARAMETER(Vcb);
- UNREFERENCED_PARAMETER(DirectoryFcb);
- UNREFERENCED_PARAMETER(FileToFind);
- UNREFERENCED_PARAMETER(FoundFCB);
-#endif
- return STATUS_OBJECT_NAME_NOT_FOUND;
+ NTSTATUS Status;
+ ULONGLONG CurrentDir;
+ UNICODE_STRING File;
+ PFILE_RECORD_HEADER FileRecord;
+ ULONGLONG MFTIndex;
+ PWSTR Colon, OldColon;
+ PNTFS_ATTR_CONTEXT DataContext;
+ USHORT Length = 0;
+
+ DPRINT1("NtfsDirFindFile(%p, %p, %S, %s, %p)\n",
+ Vcb,
+ DirectoryFcb,
+ FileToFind,
+ CaseSensitive ? "TRUE" : "FALSE",
+ FoundFCB);
+
+ *FoundFCB = NULL;
+ RtlInitUnicodeString(&File, FileToFind);
+ CurrentDir = DirectoryFcb->MFTIndex;
+
+ Colon = wcsrchr(FileToFind, L':');
+ if (Colon != NULL)
+ {
+ Length = File.Length;
+ File.Length = (Colon - FileToFind) * sizeof(WCHAR);
+
+ if (_wcsicmp(Colon + 1, L"$DATA") == 0)
+ {
+ OldColon = Colon;
+ Colon[0] = UNICODE_NULL;
+ Colon = wcsrchr(FileToFind, L':');
+ if (Colon != NULL)
+ {
+ Length = File.Length;
+ File.Length = (Colon - FileToFind) * sizeof(WCHAR);
+ }
+ else
+ {
+ Colon = OldColon;
+ Colon[0] = L':';
+ }
+ }
+
+ /* Skip colon */
+ ++Colon;
+ DPRINT1("Will now look for file '%wZ' with stream '%S'\n", &File, Colon);
+ }
+
+ Status = NtfsLookupFileAt(Vcb, &File, CaseSensitive, &FileRecord, &MFTIndex, CurrentDir);
+ if (!NT_SUCCESS(Status))
+ {
+ return Status;
+ }
+
+ if (Length != 0)
+ {
+ File.Length = Length;
+ }
+
+ if ((FileRecord->Flags & FRH_DIRECTORY) && Colon != 0)
+ {
+ return STATUS_INVALID_PARAMETER;
+ }
+ else if (Colon != 0)
+ {
+ Status = FindAttribute(Vcb, FileRecord, AttributeData, Colon, wcslen(Colon), &DataContext, NULL);
+ if (!NT_SUCCESS(Status))
+ {
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ ReleaseAttributeContext(DataContext);
+ }
+
+ Status = NtfsMakeFCBFromDirEntry(Vcb, DirectoryFcb, &File, Colon, FileRecord, MFTIndex, FoundFCB);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+
+ return Status;
}
NtfsGetFCBForFile(PNTFS_VCB Vcb,
PNTFS_FCB *pParentFCB,
PNTFS_FCB *pFCB,
- const PWSTR pFileName)
+ PCWSTR pFileName,
+ BOOLEAN CaseSensitive)
{
NTSTATUS Status;
WCHAR pathName [MAX_PATH];
WCHAR elementName [MAX_PATH];
- PWCHAR currentElement;
+ PCWSTR currentElement;
PNTFS_FCB FCB;
PNTFS_FCB parentFCB;
- DPRINT("NtfsGetFCBForFile(%p, %p, %p, '%S')\n",
+ DPRINT("NtfsGetFCBForFile(%p, %p, %p, '%S', %s)\n",
Vcb,
pParentFCB,
pFCB,
- pFileName);
+ pFileName,
+ CaseSensitive ? "TRUE" : "FALSE");
/* Dummy code */
// FCB = NtfsOpenRootFCB(Vcb);
parentFCB = NULL;
- /* Parse filename and check each path element for existance and access */
+ /* Parse filename and check each path element for existence and access */
while (NtfsGetNextPathElement(currentElement) != 0)
{
/* Skip blank directory levels */
NtfsGetNextPathElement(currentElement) - currentElement);
DPRINT(" elementName:%S\n", elementName);
- Status = NtfsDirFindFile(Vcb, parentFCB, elementName, &FCB);
+ Status = NtfsDirFindFile(Vcb, parentFCB, elementName, CaseSensitive, &FCB);
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
{
*pParentFCB = parentFCB;
return STATUS_SUCCESS;
}
+
+NTSTATUS
+NtfsReadFCBAttribute(PNTFS_VCB Vcb,
+ PNTFS_FCB pFCB,
+ ULONG Type,
+ PCWSTR Name,
+ ULONG NameLength,
+ PVOID * Data)
+{
+ NTSTATUS Status;
+ PFILE_RECORD_HEADER FileRecord;
+ PNTFS_ATTR_CONTEXT AttrCtxt;
+ ULONGLONG AttrLength;
+
+ FileRecord = ExAllocatePoolWithTag(NonPagedPool,
+ Vcb->NtfsInfo.BytesPerFileRecord,
+ TAG_NTFS);
+ if (FileRecord == NULL)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = ReadFileRecord(Vcb, pFCB->MFTIndex, FileRecord);
+ if (!NT_SUCCESS(Status))
+ {
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ return Status;
+ }
+
+ Status = FindAttribute(Vcb, FileRecord, Type, Name, NameLength, &AttrCtxt, NULL);
+ if (!NT_SUCCESS(Status))
+ {
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ return Status;
+ }
+
+ AttrLength = AttributeDataLength(&AttrCtxt->Record);
+ *Data = ExAllocatePoolWithTag(NonPagedPool, AttrLength, TAG_NTFS);
+ if (*Data == NULL)
+ {
+ ReleaseAttributeContext(AttrCtxt);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ ReadAttribute(Vcb, AttrCtxt, 0, *Data, AttrLength);
+
+ ReleaseAttributeContext(AttrCtxt);
+ ExFreePoolWithTag(FileRecord, TAG_NTFS);
+
+ return STATUS_SUCCESS;
+}
+
/* EOF */