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(PCWSTR 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
,
74 ASSERT(Vcb
->Identifier
.Type
== NTFS_TYPE_VCB
);
76 Fcb
= ExAllocateFromNPagedLookasideList(&NtfsGlobalData
->FcbLookasideList
);
77 RtlZeroMemory(Fcb
, sizeof(NTFS_FCB
));
79 Fcb
->Identifier
.Type
= NTFS_TYPE_FCB
;
80 Fcb
->Identifier
.Size
= sizeof(NTFS_TYPE_FCB
);
86 wcscpy(Fcb
->PathName
, FileName
);
87 if (wcsrchr(Fcb
->PathName
, '\\') != 0)
89 Fcb
->ObjectName
= wcsrchr(Fcb
->PathName
, '\\');
93 Fcb
->ObjectName
= Fcb
->PathName
;
99 wcscpy(Fcb
->Stream
, Stream
);
103 Fcb
->Stream
[0] = UNICODE_NULL
;
106 ExInitializeResourceLite(&Fcb
->MainResource
);
108 Fcb
->RFCB
.Resource
= &(Fcb
->MainResource
);
115 NtfsDestroyFCB(PNTFS_FCB Fcb
)
118 ASSERT(Fcb
->Identifier
.Type
== NTFS_TYPE_FCB
);
120 ExDeleteResourceLite(&Fcb
->MainResource
);
122 ExFreeToNPagedLookasideList(&NtfsGlobalData
->FcbLookasideList
, Fcb
);
127 NtfsFCBIsDirectory(PNTFS_FCB Fcb
)
129 return ((Fcb
->Entry
.FileAttributes
& NTFS_FILE_TYPE_DIRECTORY
) == NTFS_FILE_TYPE_DIRECTORY
);
134 NtfsFCBIsReparsePoint(PNTFS_FCB Fcb
)
136 return ((Fcb
->Entry
.FileAttributes
& NTFS_FILE_TYPE_REPARSE
) == NTFS_FILE_TYPE_REPARSE
);
141 NtfsFCBIsCompressed(PNTFS_FCB Fcb
)
143 return ((Fcb
->Entry
.FileAttributes
& NTFS_FILE_TYPE_COMPRESSED
) == NTFS_FILE_TYPE_COMPRESSED
);
147 NtfsFCBIsRoot(PNTFS_FCB Fcb
)
149 return (wcscmp(Fcb
->PathName
, L
"\\") == 0);
154 NtfsGrabFCB(PNTFS_VCB Vcb
,
159 DPRINT("grabbing FCB at %p: %S, refCount:%d\n",
164 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
166 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
171 NtfsReleaseFCB(PNTFS_VCB Vcb
,
176 DPRINT("releasing FCB at %p: %S, refCount:%d\n",
181 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
183 if (Fcb
->RefCount
<= 0 && !NtfsFCBIsDirectory(Fcb
))
185 RemoveEntryList(&Fcb
->FcbListEntry
);
186 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
187 CcUninitializeCacheMap(Fcb
->FileObject
, NULL
, NULL
);
192 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
198 NtfsAddFCBToTable(PNTFS_VCB Vcb
,
203 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
205 InsertTailList(&Vcb
->FcbListHead
, &Fcb
->FcbListEntry
);
206 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
211 NtfsGrabFCBFromTable(PNTFS_VCB Vcb
,
216 PLIST_ENTRY current_entry
;
218 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
220 if (FileName
== NULL
|| *FileName
== 0)
222 DPRINT("Return FCB for stream file object\n");
223 Fcb
= Vcb
->StreamFileObject
->FsContext
;
225 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
229 current_entry
= Vcb
->FcbListHead
.Flink
;
230 while (current_entry
!= &Vcb
->FcbListHead
)
232 Fcb
= CONTAINING_RECORD(current_entry
, NTFS_FCB
, FcbListEntry
);
234 DPRINT("Comparing '%S' and '%S'\n", FileName
, Fcb
->PathName
);
235 if (_wcsicmp(FileName
, Fcb
->PathName
) == 0)
238 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
242 //FIXME: need to compare against short name in FCB here
244 current_entry
= current_entry
->Flink
;
247 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
254 NtfsFCBInitializeCache(PNTFS_VCB Vcb
,
257 PFILE_OBJECT FileObject
;
261 FileObject
= IoCreateStreamFileObject(NULL
, Vcb
->StorageDevice
);
263 newCCB
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(NTFS_CCB
), TAG_CCB
);
266 return STATUS_INSUFFICIENT_RESOURCES
;
269 RtlZeroMemory(newCCB
, sizeof(NTFS_CCB
));
271 newCCB
->Identifier
.Type
= NTFS_TYPE_CCB
;
272 newCCB
->Identifier
.Size
= sizeof(NTFS_TYPE_CCB
);
274 FileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
275 FileObject
->FsContext
= Fcb
;
276 FileObject
->FsContext2
= newCCB
;
277 newCCB
->PtrFileObject
= FileObject
;
278 Fcb
->FileObject
= FileObject
;
281 Status
= STATUS_SUCCESS
;
284 CcInitializeCacheMap(FileObject
,
285 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
287 &(NtfsGlobalData
->CacheMgrCallbacks
),
290 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
292 FileObject
->FsContext2
= NULL
;
293 ExFreePoolWithTag(newCCB
, TAG_CCB
);
294 ObDereferenceObject(FileObject
);
295 Fcb
->FileObject
= NULL
;
296 return _SEH2_GetExceptionCode();
300 ObDereferenceObject(FileObject
);
301 Fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
308 NtfsMakeRootFCB(PNTFS_VCB Vcb
)
311 PFILE_RECORD_HEADER MftRecord
;
312 PFILENAME_ATTRIBUTE FileName
;
314 MftRecord
= ExAllocateFromNPagedLookasideList(&Vcb
->FileRecLookasideList
);
315 if (MftRecord
== NULL
)
320 if (!NT_SUCCESS(ReadFileRecord(Vcb
, NTFS_FILE_ROOT
, MftRecord
)))
322 ExFreeToNPagedLookasideList(&Vcb
->FileRecLookasideList
, MftRecord
);
326 FileName
= GetFileNameFromRecord(Vcb
, MftRecord
, NTFS_FILE_NAME_WIN32
);
329 ExFreeToNPagedLookasideList(&Vcb
->FileRecLookasideList
, MftRecord
);
333 Fcb
= NtfsCreateFCB(L
"\\", NULL
, Vcb
);
336 ExFreeToNPagedLookasideList(&Vcb
->FileRecLookasideList
, MftRecord
);
340 memcpy(&Fcb
->Entry
, FileName
, FIELD_OFFSET(FILENAME_ATTRIBUTE
, NameLength
));
341 Fcb
->Entry
.NameType
= FileName
->NameType
;
342 Fcb
->Entry
.NameLength
= 0;
343 Fcb
->Entry
.Name
[0] = UNICODE_NULL
;
346 Fcb
->RFCB
.FileSize
.QuadPart
= FileName
->DataSize
;
347 Fcb
->RFCB
.ValidDataLength
.QuadPart
= FileName
->DataSize
;
348 Fcb
->RFCB
.AllocationSize
.QuadPart
= FileName
->AllocatedSize
;
349 Fcb
->MFTIndex
= NTFS_FILE_ROOT
;
350 Fcb
->LinkCount
= MftRecord
->LinkCount
;
352 NtfsFCBInitializeCache(Vcb
, Fcb
);
353 NtfsAddFCBToTable(Vcb
, Fcb
);
354 NtfsGrabFCB(Vcb
, Fcb
);
356 ExFreeToNPagedLookasideList(&Vcb
->FileRecLookasideList
, MftRecord
);
363 NtfsOpenRootFCB(PNTFS_VCB Vcb
)
367 Fcb
= NtfsGrabFCBFromTable(Vcb
, L
"\\");
370 Fcb
= NtfsMakeRootFCB(Vcb
);
378 NtfsMakeFCBFromDirEntry(PNTFS_VCB Vcb
,
379 PNTFS_FCB DirectoryFCB
,
380 PUNICODE_STRING Name
,
382 PFILE_RECORD_HEADER Record
,
386 WCHAR pathName
[MAX_PATH
];
387 PFILENAME_ATTRIBUTE FileName
;
388 PSTANDARD_INFORMATION StdInfo
;
390 ULONGLONG Size
, AllocatedSize
;
392 DPRINT1("NtfsMakeFCBFromDirEntry(%p, %p, %wZ, %p, %p, %p)\n", Vcb
, DirectoryFCB
, Name
, Stream
, Record
, fileFCB
);
394 FileName
= GetBestFileNameFromRecord(Vcb
, Record
);
397 return STATUS_OBJECT_NAME_NOT_FOUND
; // Not sure that's the best here
400 if (DirectoryFCB
&& Name
)
402 if (Name
->Buffer
[0] != 0 && wcslen(DirectoryFCB
->PathName
) +
403 sizeof(WCHAR
) + Name
->Length
/ sizeof(WCHAR
) > MAX_PATH
)
405 return STATUS_OBJECT_NAME_INVALID
;
408 wcscpy(pathName
, DirectoryFCB
->PathName
);
409 if (!NtfsFCBIsRoot(DirectoryFCB
))
411 wcscat(pathName
, L
"\\");
413 wcscat(pathName
, Name
->Buffer
);
417 RtlCopyMemory(pathName
, FileName
->Name
, FileName
->NameLength
* sizeof (WCHAR
));
418 pathName
[FileName
->NameLength
] = UNICODE_NULL
;
421 Size
= NtfsGetFileSize(Vcb
, Record
, (Stream
? Stream
: L
""), (Stream
? wcslen(Stream
) : 0), &AllocatedSize
);
423 rcFCB
= NtfsCreateFCB(pathName
, Stream
, Vcb
);
426 return STATUS_INSUFFICIENT_RESOURCES
;
429 memcpy(&rcFCB
->Entry
, FileName
, FIELD_OFFSET(FILENAME_ATTRIBUTE
, NameLength
));
430 rcFCB
->Entry
.NameType
= FileName
->NameType
;
431 rcFCB
->RFCB
.FileSize
.QuadPart
= Size
;
432 rcFCB
->RFCB
.ValidDataLength
.QuadPart
= Size
;
433 rcFCB
->RFCB
.AllocationSize
.QuadPart
= AllocatedSize
;
435 StdInfo
= GetStandardInformationFromRecord(Vcb
, Record
);
438 rcFCB
->Entry
.FileAttributes
|= StdInfo
->FileAttribute
;
441 NtfsFCBInitializeCache(Vcb
, rcFCB
);
443 rcFCB
->MFTIndex
= MFTIndex
;
444 rcFCB
->LinkCount
= Record
->LinkCount
;
445 NtfsAddFCBToTable(Vcb
, rcFCB
);
448 return STATUS_SUCCESS
;
453 NtfsAttachFCBToFileObject(PNTFS_VCB Vcb
,
455 PFILE_OBJECT FileObject
)
459 newCCB
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(NTFS_CCB
), TAG_CCB
);
462 return STATUS_INSUFFICIENT_RESOURCES
;
465 RtlZeroMemory(newCCB
, sizeof(NTFS_CCB
));
467 newCCB
->Identifier
.Type
= NTFS_TYPE_CCB
;
468 newCCB
->Identifier
.Size
= sizeof(NTFS_TYPE_CCB
);
470 FileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
471 FileObject
->FsContext
= Fcb
;
472 FileObject
->FsContext2
= newCCB
;
473 newCCB
->PtrFileObject
= FileObject
;
476 if (!(Fcb
->Flags
& FCB_CACHE_INITIALIZED
))
480 CcInitializeCacheMap(FileObject
,
481 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
483 &(NtfsGlobalData
->CacheMgrCallbacks
),
486 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
488 FileObject
->FsContext2
= NULL
;
489 ExFreePoolWithTag(newCCB
, TAG_CCB
);
490 return _SEH2_GetExceptionCode();
494 Fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
497 //DPRINT("file open: fcb:%x file size: %d\n", Fcb, Fcb->Entry.DataLengthL);
499 return STATUS_SUCCESS
;
504 NtfsDirFindFile(PNTFS_VCB Vcb
,
505 PNTFS_FCB DirectoryFcb
,
507 BOOLEAN CaseSensitive
,
511 ULONGLONG CurrentDir
;
513 PFILE_RECORD_HEADER FileRecord
;
515 PWSTR Colon
, OldColon
;
516 PNTFS_ATTR_CONTEXT DataContext
;
519 DPRINT1("NtfsDirFindFile(%p, %p, %S, %s, %p)\n",
523 CaseSensitive
? "TRUE" : "FALSE",
527 RtlInitUnicodeString(&File
, FileToFind
);
528 CurrentDir
= DirectoryFcb
->MFTIndex
;
530 Colon
= wcsrchr(FileToFind
, L
':');
533 Length
= File
.Length
;
534 File
.Length
= (Colon
- FileToFind
) * sizeof(WCHAR
);
536 if (_wcsicmp(Colon
+ 1, L
"$DATA") == 0)
539 Colon
[0] = UNICODE_NULL
;
540 Colon
= wcsrchr(FileToFind
, L
':');
543 Length
= File
.Length
;
544 File
.Length
= (Colon
- FileToFind
) * sizeof(WCHAR
);
555 DPRINT1("Will now look for file '%wZ' with stream '%S'\n", &File
, Colon
);
558 Status
= NtfsLookupFileAt(Vcb
, &File
, CaseSensitive
, &FileRecord
, &MFTIndex
, CurrentDir
);
559 if (!NT_SUCCESS(Status
))
566 File
.Length
= Length
;
569 if ((FileRecord
->Flags
& FRH_DIRECTORY
) && Colon
!= 0)
571 ExFreeToNPagedLookasideList(&Vcb
->FileRecLookasideList
, FileRecord
);
572 return STATUS_INVALID_PARAMETER
;
576 Status
= FindAttribute(Vcb
, FileRecord
, AttributeData
, Colon
, wcslen(Colon
), &DataContext
, NULL
);
577 if (!NT_SUCCESS(Status
))
579 return STATUS_OBJECT_NAME_NOT_FOUND
;
581 ReleaseAttributeContext(DataContext
);
584 Status
= NtfsMakeFCBFromDirEntry(Vcb
, DirectoryFcb
, &File
, Colon
, FileRecord
, MFTIndex
, FoundFCB
);
585 ExFreeToNPagedLookasideList(&Vcb
->FileRecLookasideList
, FileRecord
);
592 NtfsGetFCBForFile(PNTFS_VCB Vcb
,
593 PNTFS_FCB
*pParentFCB
,
596 BOOLEAN CaseSensitive
)
599 WCHAR pathName
[MAX_PATH
];
600 WCHAR elementName
[MAX_PATH
];
601 PCWSTR currentElement
;
605 DPRINT("NtfsGetFCBForFile(%p, %p, %p, '%S', %s)\n",
610 CaseSensitive
? "TRUE" : "FALSE");
613 // FCB = NtfsOpenRootFCB(Vcb);
615 // *pParentFCB = NULL;
618 /* Trivial case, open of the root directory on volume */
619 if (pFileName
[0] == L
'\0' || wcscmp(pFileName
, L
"\\") == 0)
621 DPRINT("returning root FCB\n");
623 FCB
= NtfsOpenRootFCB(Vcb
);
627 return (FCB
!= NULL
) ? STATUS_SUCCESS
: STATUS_OBJECT_PATH_NOT_FOUND
;
631 currentElement
= pFileName
+ 1;
632 wcscpy (pathName
, L
"\\");
633 FCB
= NtfsOpenRootFCB (Vcb
);
638 /* Parse filename and check each path element for existence and access */
639 while (NtfsGetNextPathElement(currentElement
) != 0)
641 /* Skip blank directory levels */
642 if ((NtfsGetNextPathElement(currentElement
) - currentElement
) == 0)
648 DPRINT("Parsing, currentElement:%S\n", currentElement
);
649 DPRINT(" parentFCB:%p FCB:%p\n", parentFCB
, FCB
);
651 /* Descend to next directory level */
654 NtfsReleaseFCB(Vcb
, parentFCB
);
658 /* fail if element in FCB is not a directory */
659 if (!NtfsFCBIsDirectory(FCB
))
661 DPRINT("Element in requested path is not a directory\n");
663 NtfsReleaseFCB(Vcb
, FCB
);
668 return STATUS_OBJECT_PATH_NOT_FOUND
;
673 /* Extract next directory level into dirName */
674 NtfsWSubString(pathName
,
676 NtfsGetNextPathElement(currentElement
) - pFileName
);
677 DPRINT(" pathName:%S\n", pathName
);
679 FCB
= NtfsGrabFCBFromTable(Vcb
, pathName
);
682 NtfsWSubString(elementName
,
684 NtfsGetNextPathElement(currentElement
) - currentElement
);
685 DPRINT(" elementName:%S\n", elementName
);
687 Status
= NtfsDirFindFile(Vcb
, parentFCB
, elementName
, CaseSensitive
, &FCB
);
688 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
690 *pParentFCB
= parentFCB
;
692 currentElement
= NtfsGetNextPathElement(currentElement
);
693 if (*currentElement
== L
'\0' || NtfsGetNextPathElement(currentElement
+ 1) == 0)
695 return STATUS_OBJECT_NAME_NOT_FOUND
;
699 return STATUS_OBJECT_PATH_NOT_FOUND
;
702 else if (!NT_SUCCESS(Status
))
704 NtfsReleaseFCB(Vcb
, parentFCB
);
712 currentElement
= NtfsGetNextPathElement(currentElement
);
715 *pParentFCB
= parentFCB
;
719 return STATUS_SUCCESS
;
724 NtfsReadFCBAttribute(PNTFS_VCB Vcb
,
732 PFILE_RECORD_HEADER FileRecord
;
733 PNTFS_ATTR_CONTEXT AttrCtxt
;
734 ULONGLONG AttrLength
;
736 FileRecord
= ExAllocateFromNPagedLookasideList(&Vcb
->FileRecLookasideList
);
737 if (FileRecord
== NULL
)
739 return STATUS_INSUFFICIENT_RESOURCES
;
742 Status
= ReadFileRecord(Vcb
, pFCB
->MFTIndex
, FileRecord
);
743 if (!NT_SUCCESS(Status
))
745 ExFreeToNPagedLookasideList(&Vcb
->FileRecLookasideList
, FileRecord
);
749 Status
= FindAttribute(Vcb
, FileRecord
, Type
, Name
, NameLength
, &AttrCtxt
, NULL
);
750 if (!NT_SUCCESS(Status
))
752 ExFreeToNPagedLookasideList(&Vcb
->FileRecLookasideList
, FileRecord
);
756 AttrLength
= AttributeDataLength(AttrCtxt
->pRecord
);
757 *Data
= ExAllocatePoolWithTag(NonPagedPool
, AttrLength
, TAG_NTFS
);
760 ReleaseAttributeContext(AttrCtxt
);
761 ExFreeToNPagedLookasideList(&Vcb
->FileRecLookasideList
, FileRecord
);
762 return STATUS_INSUFFICIENT_RESOURCES
;
765 ReadAttribute(Vcb
, AttrCtxt
, 0, *Data
, AttrLength
);
767 ReleaseAttributeContext(AttrCtxt
);
768 ExFreeToNPagedLookasideList(&Vcb
->FileRecLookasideList
, FileRecord
);
770 return STATUS_SUCCESS
;