3 * Copyright (C) 2002, 2014 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19 * COPYRIGHT: See COPYING in the top level directory
20 * PROJECT: ReactOS kernel
21 * FILE: drivers/filesystem/ntfs/fcb.c
22 * PURPOSE: NTFS filesystem driver
23 * PROGRAMMERS: Eric Kohl
24 * Pierre Schweitzer (pierre@reactos.org)
25 * Hervé Poussineau (hpoussin@reactos.org)
28 /* INCLUDES *****************************************************************/
35 /* FUNCTIONS ****************************************************************/
39 NtfsGetNextPathElement(PWCHAR FileName
)
41 if (*FileName
== L
'\0')
46 while (*FileName
!= L
'\0' && *FileName
!= L
'\\')
57 NtfsWSubString(PWCHAR pTarget
,
61 wcsncpy(pTarget
, pSource
, pLength
);
62 pTarget
[pLength
] = L
'\0';
67 NtfsCreateFCB(PCWSTR FileName
,
73 ASSERT(Vcb
->Identifier
.Type
== NTFS_TYPE_VCB
);
75 Fcb
= ExAllocateFromNPagedLookasideList(&NtfsGlobalData
->FcbLookasideList
);
76 RtlZeroMemory(Fcb
, sizeof(NTFS_FCB
));
78 Fcb
->Identifier
.Type
= NTFS_TYPE_FCB
;
79 Fcb
->Identifier
.Size
= sizeof(NTFS_TYPE_FCB
);
85 wcscpy(Fcb
->PathName
, FileName
);
86 if (wcsrchr(Fcb
->PathName
, '\\') != 0)
88 Fcb
->ObjectName
= wcsrchr(Fcb
->PathName
, '\\');
92 Fcb
->ObjectName
= Fcb
->PathName
;
96 ExInitializeResourceLite(&Fcb
->MainResource
);
98 Fcb
->RFCB
.Resource
= &(Fcb
->MainResource
);
105 NtfsDestroyFCB(PNTFS_FCB Fcb
)
108 ASSERT(Fcb
->Identifier
.Type
== NTFS_TYPE_FCB
);
110 ExDeleteResourceLite(&Fcb
->MainResource
);
112 ExFreeToNPagedLookasideList(&NtfsGlobalData
->FcbLookasideList
, Fcb
);
117 NtfsFCBIsDirectory(PNTFS_FCB Fcb
)
119 return ((Fcb
->Entry
.FileAttributes
& NTFS_FILE_TYPE_DIRECTORY
) == NTFS_FILE_TYPE_DIRECTORY
);
124 NtfsFCBIsReparsePoint(PNTFS_FCB Fcb
)
126 return ((Fcb
->Entry
.FileAttributes
& NTFS_FILE_TYPE_REPARSE
) == NTFS_FILE_TYPE_REPARSE
);
131 NtfsFCBIsRoot(PNTFS_FCB Fcb
)
133 return (wcscmp(Fcb
->PathName
, L
"\\") == 0);
138 NtfsGrabFCB(PNTFS_VCB Vcb
,
143 DPRINT("grabbing FCB at %p: %S, refCount:%d\n",
148 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
150 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
155 NtfsReleaseFCB(PNTFS_VCB Vcb
,
160 DPRINT("releasing FCB at %p: %S, refCount:%d\n",
165 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
167 if (Fcb
->RefCount
<= 0 && !NtfsFCBIsDirectory(Fcb
))
169 RemoveEntryList(&Fcb
->FcbListEntry
);
170 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
171 CcUninitializeCacheMap(Fcb
->FileObject
, NULL
, NULL
);
176 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
182 NtfsAddFCBToTable(PNTFS_VCB Vcb
,
187 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
189 InsertTailList(&Vcb
->FcbListHead
, &Fcb
->FcbListEntry
);
190 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
195 NtfsGrabFCBFromTable(PNTFS_VCB Vcb
,
200 PLIST_ENTRY current_entry
;
202 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
204 if (FileName
== NULL
|| *FileName
== 0)
206 DPRINT("Return FCB for stream file object\n");
207 Fcb
= Vcb
->StreamFileObject
->FsContext
;
209 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
213 current_entry
= Vcb
->FcbListHead
.Flink
;
214 while (current_entry
!= &Vcb
->FcbListHead
)
216 Fcb
= CONTAINING_RECORD(current_entry
, NTFS_FCB
, FcbListEntry
);
218 DPRINT("Comparing '%S' and '%S'\n", FileName
, Fcb
->PathName
);
219 if (_wcsicmp(FileName
, Fcb
->PathName
) == 0)
222 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
226 //FIXME: need to compare against short name in FCB here
228 current_entry
= current_entry
->Flink
;
231 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
238 NtfsFCBInitializeCache(PNTFS_VCB Vcb
,
241 PFILE_OBJECT FileObject
;
245 FileObject
= IoCreateStreamFileObject(NULL
, Vcb
->StorageDevice
);
247 newCCB
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(NTFS_CCB
), TAG_CCB
);
250 return STATUS_INSUFFICIENT_RESOURCES
;
253 RtlZeroMemory(newCCB
, sizeof(NTFS_CCB
));
255 newCCB
->Identifier
.Type
= NTFS_TYPE_CCB
;
256 newCCB
->Identifier
.Size
= sizeof(NTFS_TYPE_CCB
);
258 FileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
259 FileObject
->FsContext
= Fcb
;
260 FileObject
->FsContext2
= newCCB
;
261 newCCB
->PtrFileObject
= FileObject
;
262 Fcb
->FileObject
= FileObject
;
265 Status
= STATUS_SUCCESS
;
268 CcInitializeCacheMap(FileObject
,
269 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
271 &(NtfsGlobalData
->CacheMgrCallbacks
),
274 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
276 FileObject
->FsContext2
= NULL
;
277 ExFreePoolWithTag(newCCB
, TAG_CCB
);
278 ObDereferenceObject(FileObject
);
279 Fcb
->FileObject
= NULL
;
280 return _SEH2_GetExceptionCode();
284 ObDereferenceObject(FileObject
);
285 Fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
292 NtfsMakeRootFCB(PNTFS_VCB Vcb
)
295 PFILE_RECORD_HEADER MftRecord
;
296 PFILENAME_ATTRIBUTE FileName
;
298 MftRecord
= ExAllocatePoolWithTag(NonPagedPool
,
299 Vcb
->NtfsInfo
.BytesPerFileRecord
,
301 if (MftRecord
== NULL
)
306 if (!NT_SUCCESS(ReadFileRecord(Vcb
, NTFS_FILE_ROOT
, MftRecord
)))
308 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
312 FileName
= GetFileNameFromRecord(MftRecord
, NTFS_FILE_NAME_WIN32
);
315 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
319 Fcb
= NtfsCreateFCB(L
"\\", Vcb
);
322 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
326 memcpy(&Fcb
->Entry
, FileName
, FIELD_OFFSET(FILENAME_ATTRIBUTE
, NameLength
));
327 Fcb
->Entry
.NameType
= FileName
->NameType
;
328 Fcb
->Entry
.NameLength
= 0;
329 Fcb
->Entry
.Name
[0] = UNICODE_NULL
;
332 Fcb
->RFCB
.FileSize
.QuadPart
= FileName
->DataSize
;
333 Fcb
->RFCB
.ValidDataLength
.QuadPart
= FileName
->DataSize
;
334 Fcb
->RFCB
.AllocationSize
.QuadPart
= FileName
->AllocatedSize
;
335 Fcb
->MFTIndex
= NTFS_FILE_ROOT
;
336 Fcb
->LinkCount
= MftRecord
->LinkCount
;
338 NtfsFCBInitializeCache(Vcb
, Fcb
);
339 NtfsAddFCBToTable(Vcb
, Fcb
);
340 NtfsGrabFCB(Vcb
, Fcb
);
342 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
349 NtfsOpenRootFCB(PNTFS_VCB Vcb
)
353 Fcb
= NtfsGrabFCBFromTable(Vcb
, L
"\\");
356 Fcb
= NtfsMakeRootFCB(Vcb
);
365 NtfsGetDirEntryName(PDEVICE_EXTENSION DeviceExt
,
369 * FUNCTION: Retrieves the file name, be it in short or long file name format
372 if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 0)
376 else if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 1)
382 if (DeviceExt
->CdInfo
.JolietLevel
== 0)
386 for (i
= 0; i
< Record
->FileIdLength
&& Record
->FileId
[i
] != ';'; i
++)
387 Name
[i
] = (WCHAR
)Record
->FileId
[i
];
392 NtfsSwapString(Name
, Record
->FileId
, Record
->FileIdLength
);
396 DPRINT("Name '%S'\n", Name
);
402 NtfsMakeFCBFromDirEntry(PNTFS_VCB Vcb
,
403 PNTFS_FCB DirectoryFCB
,
404 PUNICODE_STRING Name
,
405 PFILE_RECORD_HEADER Record
,
409 WCHAR pathName
[MAX_PATH
];
410 PFILENAME_ATTRIBUTE FileName
;
411 PSTANDARD_INFORMATION StdInfo
;
414 DPRINT1("NtfsMakeFCBFromDirEntry(%p, %p, %wZ, %p, %p)\n", Vcb
, DirectoryFCB
, Name
, Record
, fileFCB
);
416 FileName
= GetBestFileNameFromRecord(Record
);
419 return STATUS_OBJECT_NAME_NOT_FOUND
; // Not sure that's the best here
422 if (DirectoryFCB
&& Name
)
424 if (Name
->Buffer
[0] != 0 && wcslen(DirectoryFCB
->PathName
) +
425 sizeof(WCHAR
) + Name
->Length
/ sizeof(WCHAR
) > MAX_PATH
)
427 return STATUS_OBJECT_NAME_INVALID
;
430 wcscpy(pathName
, DirectoryFCB
->PathName
);
431 if (!NtfsFCBIsRoot(DirectoryFCB
))
433 wcscat(pathName
, L
"\\");
435 wcscat(pathName
, Name
->Buffer
);
439 RtlCopyMemory(pathName
, FileName
->Name
, FileName
->NameLength
* sizeof (WCHAR
));
440 pathName
[FileName
->NameLength
] = UNICODE_NULL
;
443 rcFCB
= NtfsCreateFCB(pathName
, Vcb
);
446 return STATUS_INSUFFICIENT_RESOURCES
;
449 memcpy(&rcFCB
->Entry
, FileName
, FIELD_OFFSET(FILENAME_ATTRIBUTE
, NameLength
));
450 rcFCB
->Entry
.NameType
= FileName
->NameType
;
451 rcFCB
->RFCB
.FileSize
.QuadPart
= FileName
->DataSize
;
452 rcFCB
->RFCB
.ValidDataLength
.QuadPart
= FileName
->DataSize
;
453 rcFCB
->RFCB
.AllocationSize
.QuadPart
= FileName
->AllocatedSize
;
455 StdInfo
= GetStandardInformationFromRecord(Record
);
458 rcFCB
->Entry
.FileAttributes
|= StdInfo
->FileAttribute
;
461 NtfsFCBInitializeCache(Vcb
, rcFCB
);
463 rcFCB
->MFTIndex
= MFTIndex
;
464 rcFCB
->LinkCount
= Record
->LinkCount
;
465 NtfsAddFCBToTable(Vcb
, rcFCB
);
468 return STATUS_SUCCESS
;
473 NtfsAttachFCBToFileObject(PNTFS_VCB Vcb
,
475 PFILE_OBJECT FileObject
)
479 newCCB
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(NTFS_CCB
), TAG_CCB
);
482 return STATUS_INSUFFICIENT_RESOURCES
;
485 RtlZeroMemory(newCCB
, sizeof(NTFS_CCB
));
487 newCCB
->Identifier
.Type
= NTFS_TYPE_CCB
;
488 newCCB
->Identifier
.Size
= sizeof(NTFS_TYPE_CCB
);
490 FileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
491 FileObject
->FsContext
= Fcb
;
492 FileObject
->FsContext2
= newCCB
;
493 newCCB
->PtrFileObject
= FileObject
;
496 if (!(Fcb
->Flags
& FCB_CACHE_INITIALIZED
))
500 CcInitializeCacheMap(FileObject
,
501 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
503 &(NtfsGlobalData
->CacheMgrCallbacks
),
506 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
508 FileObject
->FsContext2
= NULL
;
509 ExFreePoolWithTag(newCCB
, TAG_CCB
);
510 return _SEH2_GetExceptionCode();
514 Fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
517 //DPRINT("file open: fcb:%x file size: %d\n", Fcb, Fcb->Entry.DataLengthL);
519 return STATUS_SUCCESS
;
524 NtfsDirFindFile(PNTFS_VCB Vcb
,
525 PNTFS_FCB DirectoryFcb
,
530 ULONGLONG CurrentDir
;
532 PFILE_RECORD_HEADER FileRecord
;
533 PNTFS_ATTR_CONTEXT DataContext
;
536 DPRINT1("NtfsDirFindFile(%p, %p, %S, %p)\n", Vcb
, DirectoryFcb
, FileToFind
, FoundFCB
);
539 RtlInitUnicodeString(&File
, FileToFind
);
540 CurrentDir
= DirectoryFcb
->MFTIndex
;
542 Status
= NtfsLookupFileAt(Vcb
, &File
, &FileRecord
, &DataContext
, &MFTIndex
, CurrentDir
);
543 if (!NT_SUCCESS(Status
))
548 Status
= NtfsMakeFCBFromDirEntry(Vcb
, DirectoryFcb
, &File
, FileRecord
, MFTIndex
, FoundFCB
);
549 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
556 NtfsGetFCBForFile(PNTFS_VCB Vcb
,
557 PNTFS_FCB
*pParentFCB
,
559 const PWSTR pFileName
)
562 WCHAR pathName
[MAX_PATH
];
563 WCHAR elementName
[MAX_PATH
];
564 PWCHAR currentElement
;
568 DPRINT("NtfsGetFCBForFile(%p, %p, %p, '%S')\n",
575 // FCB = NtfsOpenRootFCB(Vcb);
577 // *pParentFCB = NULL;
580 /* Trivial case, open of the root directory on volume */
581 if (pFileName
[0] == L
'\0' || wcscmp(pFileName
, L
"\\") == 0)
583 DPRINT("returning root FCB\n");
585 FCB
= NtfsOpenRootFCB(Vcb
);
589 return (FCB
!= NULL
) ? STATUS_SUCCESS
: STATUS_OBJECT_PATH_NOT_FOUND
;
593 currentElement
= pFileName
+ 1;
594 wcscpy (pathName
, L
"\\");
595 FCB
= NtfsOpenRootFCB (Vcb
);
600 /* Parse filename and check each path element for existance and access */
601 while (NtfsGetNextPathElement(currentElement
) != 0)
603 /* Skip blank directory levels */
604 if ((NtfsGetNextPathElement(currentElement
) - currentElement
) == 0)
610 DPRINT("Parsing, currentElement:%S\n", currentElement
);
611 DPRINT(" parentFCB:%p FCB:%p\n", parentFCB
, FCB
);
613 /* Descend to next directory level */
616 NtfsReleaseFCB(Vcb
, parentFCB
);
620 /* fail if element in FCB is not a directory */
621 if (!NtfsFCBIsDirectory(FCB
))
623 DPRINT("Element in requested path is not a directory\n");
625 NtfsReleaseFCB(Vcb
, FCB
);
630 return STATUS_OBJECT_PATH_NOT_FOUND
;
635 /* Extract next directory level into dirName */
636 NtfsWSubString(pathName
,
638 NtfsGetNextPathElement(currentElement
) - pFileName
);
639 DPRINT(" pathName:%S\n", pathName
);
641 FCB
= NtfsGrabFCBFromTable(Vcb
, pathName
);
644 NtfsWSubString(elementName
,
646 NtfsGetNextPathElement(currentElement
) - currentElement
);
647 DPRINT(" elementName:%S\n", elementName
);
649 Status
= NtfsDirFindFile(Vcb
, parentFCB
, elementName
, &FCB
);
650 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
652 *pParentFCB
= parentFCB
;
654 currentElement
= NtfsGetNextPathElement(currentElement
);
655 if (*currentElement
== L
'\0' || NtfsGetNextPathElement(currentElement
+ 1) == 0)
657 return STATUS_OBJECT_NAME_NOT_FOUND
;
661 return STATUS_OBJECT_PATH_NOT_FOUND
;
664 else if (!NT_SUCCESS(Status
))
666 NtfsReleaseFCB(Vcb
, parentFCB
);
674 currentElement
= NtfsGetNextPathElement(currentElement
);
677 *pParentFCB
= parentFCB
;
681 return STATUS_SUCCESS
;
686 NtfsReadFCBAttribute(PNTFS_VCB Vcb
,
694 PFILE_RECORD_HEADER FileRecord
;
695 PNTFS_ATTR_CONTEXT AttrCtxt
;
696 ULONGLONG AttrLength
;
698 FileRecord
= ExAllocatePoolWithTag(NonPagedPool
,
699 Vcb
->NtfsInfo
.BytesPerFileRecord
,
701 if (FileRecord
== NULL
)
703 return STATUS_INSUFFICIENT_RESOURCES
;
706 Status
= ReadFileRecord(Vcb
, pFCB
->MFTIndex
, FileRecord
);
707 if (!NT_SUCCESS(Status
))
709 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
713 Status
= FindAttribute(Vcb
, FileRecord
, Type
, Name
, NameLength
, &AttrCtxt
);
714 if (!NT_SUCCESS(Status
))
716 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
720 AttrLength
= AttributeDataLength(&AttrCtxt
->Record
);
721 *Data
= ExAllocatePoolWithTag(NonPagedPool
, AttrLength
, TAG_NTFS
);
724 ReleaseAttributeContext(AttrCtxt
);
725 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
726 return STATUS_INSUFFICIENT_RESOURCES
;
729 ReadAttribute(Vcb
, AttrCtxt
, 0, *Data
, AttrLength
);
731 ReleaseAttributeContext(AttrCtxt
);
732 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
734 return STATUS_SUCCESS
;