3 * Copyright (C) 2002 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 * PROGRAMMER: Eric Kohl
26 /* INCLUDES *****************************************************************/
33 /* MACROS *******************************************************************/
35 #define TAG_FCB 'BCFI'
37 /* FUNCTIONS ****************************************************************/
41 NtfsGetNextPathElement(PWCHAR FileName
)
43 if (*FileName
== L
'\0')
48 while (*FileName
!= L
'\0' && *FileName
!= L
'\\')
59 NtfsWSubString(PWCHAR pTarget
,
63 wcsncpy(pTarget
, pSource
, pLength
);
64 pTarget
[pLength
] = L
'\0';
69 NtfsCreateFCB(PCWSTR FileName
,
75 ASSERT(Vcb
->Identifier
.Type
== NTFS_TYPE_VCB
);
77 Fcb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(NTFS_FCB
), TAG_FCB
);
78 RtlZeroMemory(Fcb
, sizeof(NTFS_FCB
));
80 Fcb
->Identifier
.Type
= NTFS_TYPE_FCB
;
81 Fcb
->Identifier
.Size
= sizeof(NTFS_TYPE_FCB
);
87 wcscpy(Fcb
->PathName
, FileName
);
88 if (wcsrchr(Fcb
->PathName
, '\\') != 0)
90 Fcb
->ObjectName
= wcsrchr(Fcb
->PathName
, '\\');
94 Fcb
->ObjectName
= Fcb
->PathName
;
98 ExInitializeResourceLite(&Fcb
->MainResource
);
100 Fcb
->RFCB
.Resource
= &(Fcb
->MainResource
);
107 NtfsDestroyFCB(PNTFS_FCB Fcb
)
110 ASSERT(Fcb
->Identifier
.Type
== NTFS_TYPE_FCB
);
112 ExDeleteResourceLite(&Fcb
->MainResource
);
119 NtfsFCBIsDirectory(PNTFS_FCB Fcb
)
121 return ((Fcb
->Entry
.FileAttributes
& NTFS_FILE_TYPE_DIRECTORY
) == NTFS_FILE_TYPE_DIRECTORY
);
126 NtfsFCBIsRoot(PNTFS_FCB Fcb
)
128 return (wcscmp(Fcb
->PathName
, L
"\\") == 0);
133 NtfsGrabFCB(PNTFS_VCB Vcb
,
138 DPRINT("grabbing FCB at %p: %S, refCount:%d\n",
143 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
145 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
150 NtfsReleaseFCB(PNTFS_VCB Vcb
,
155 DPRINT("releasing FCB at %p: %S, refCount:%d\n",
160 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
162 if (Fcb
->RefCount
<= 0 && !NtfsFCBIsDirectory(Fcb
))
164 RemoveEntryList(&Fcb
->FcbListEntry
);
165 CcUninitializeCacheMap(Fcb
->FileObject
, NULL
, NULL
);
169 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
174 NtfsAddFCBToTable(PNTFS_VCB Vcb
,
179 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
181 InsertTailList(&Vcb
->FcbListHead
, &Fcb
->FcbListEntry
);
182 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
187 NtfsGrabFCBFromTable(PNTFS_VCB Vcb
,
192 PLIST_ENTRY current_entry
;
194 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
196 if (FileName
== NULL
|| *FileName
== 0)
198 DPRINT("Return FCB for stream file object\n");
199 Fcb
= Vcb
->StreamFileObject
->FsContext
;
201 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
205 current_entry
= Vcb
->FcbListHead
.Flink
;
206 while (current_entry
!= &Vcb
->FcbListHead
)
208 Fcb
= CONTAINING_RECORD(current_entry
, NTFS_FCB
, FcbListEntry
);
210 DPRINT("Comparing '%S' and '%S'\n", FileName
, Fcb
->PathName
);
211 if (_wcsicmp(FileName
, Fcb
->PathName
) == 0)
214 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
218 //FIXME: need to compare against short name in FCB here
220 current_entry
= current_entry
->Flink
;
223 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
230 NtfsFCBInitializeCache(PNTFS_VCB Vcb
,
233 PFILE_OBJECT FileObject
;
237 FileObject
= IoCreateStreamFileObject(NULL
, Vcb
->StorageDevice
);
239 newCCB
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(NTFS_CCB
), TAG_CCB
);
242 return STATUS_INSUFFICIENT_RESOURCES
;
245 RtlZeroMemory(newCCB
, sizeof(NTFS_CCB
));
247 newCCB
->Identifier
.Type
= NTFS_TYPE_CCB
;
248 newCCB
->Identifier
.Size
= sizeof(NTFS_TYPE_CCB
);
250 FileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
251 FileObject
->FsContext
= Fcb
;
252 FileObject
->FsContext2
= newCCB
;
253 newCCB
->PtrFileObject
= FileObject
;
254 Fcb
->FileObject
= FileObject
;
257 Status
= STATUS_SUCCESS
;
258 CcInitializeCacheMap(FileObject
,
259 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
261 &(NtfsGlobalData
->CacheMgrCallbacks
),
264 ObDereferenceObject(FileObject
);
265 Fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
272 NtfsMakeRootFCB(PNTFS_VCB Vcb
)
275 PFILE_RECORD_HEADER MftRecord
;
276 PFILENAME_ATTRIBUTE FileName
;
278 MftRecord
= ExAllocatePoolWithTag(NonPagedPool
,
279 Vcb
->NtfsInfo
.BytesPerFileRecord
,
281 if (MftRecord
== NULL
)
286 if (!NT_SUCCESS(ReadFileRecord(Vcb
, NTFS_FILE_ROOT
, MftRecord
)))
288 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
292 FileName
= GetFileNameFromRecord(MftRecord
);
295 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
299 Fcb
= NtfsCreateFCB(L
"\\", Vcb
);
302 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
306 memcpy(&Fcb
->Entry
, FileName
, FIELD_OFFSET(FILENAME_ATTRIBUTE
, NameLength
));
307 Fcb
->Entry
.NameType
= FileName
->NameType
;
308 Fcb
->Entry
.NameLength
= 0;
309 Fcb
->Entry
.Name
[0] = UNICODE_NULL
;
312 Fcb
->RFCB
.FileSize
.QuadPart
= FileName
->DataSize
;
313 Fcb
->RFCB
.ValidDataLength
.QuadPart
= FileName
->DataSize
;
314 Fcb
->RFCB
.AllocationSize
.QuadPart
= FileName
->AllocatedSize
;
315 Fcb
->MFTIndex
= NTFS_FILE_ROOT
;
317 NtfsFCBInitializeCache(Vcb
, Fcb
);
318 NtfsAddFCBToTable(Vcb
, Fcb
);
319 NtfsGrabFCB(Vcb
, Fcb
);
321 ExFreePoolWithTag(MftRecord
, TAG_NTFS
);
328 NtfsOpenRootFCB(PNTFS_VCB Vcb
)
332 Fcb
= NtfsGrabFCBFromTable(Vcb
, L
"\\");
335 Fcb
= NtfsMakeRootFCB(Vcb
);
344 NtfsGetDirEntryName(PDEVICE_EXTENSION DeviceExt
,
348 * FUNCTION: Retrieves the file name, be it in short or long file name format
351 if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 0)
355 else if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 1)
361 if (DeviceExt
->CdInfo
.JolietLevel
== 0)
365 for (i
= 0; i
< Record
->FileIdLength
&& Record
->FileId
[i
] != ';'; i
++)
366 Name
[i
] = (WCHAR
)Record
->FileId
[i
];
371 NtfsSwapString(Name
, Record
->FileId
, Record
->FileIdLength
);
375 DPRINT("Name '%S'\n", Name
);
381 NtfsMakeFCBFromDirEntry(PNTFS_VCB Vcb
,
382 PNTFS_FCB DirectoryFCB
,
383 PUNICODE_STRING Name
,
384 PFILE_RECORD_HEADER Record
,
388 WCHAR pathName
[MAX_PATH
];
389 PFILENAME_ATTRIBUTE FileName
;
392 DPRINT1("NtfsMakeFCBFromDirEntry(%p, %p, %wZ, %p, %p)\n", Vcb
, DirectoryFCB
, Name
, Record
, fileFCB
);
394 FileName
= GetFileNameFromRecord(Record
);
397 return STATUS_OBJECT_NAME_NOT_FOUND
; // Not sure that's the best here
400 if (Name
->Buffer
[0] != 0 && wcslen(DirectoryFCB
->PathName
) +
401 sizeof(WCHAR
) + Name
->Length
/ sizeof(WCHAR
) > MAX_PATH
)
403 return STATUS_OBJECT_NAME_INVALID
;
406 wcscpy(pathName
, DirectoryFCB
->PathName
);
407 if (!NtfsFCBIsRoot(DirectoryFCB
))
409 wcscat(pathName
, L
"\\");
411 wcscat(pathName
, Name
->Buffer
);
413 rcFCB
= NtfsCreateFCB(pathName
, Vcb
);
416 return STATUS_INSUFFICIENT_RESOURCES
;
419 memcpy(&rcFCB
->Entry
, FileName
, FIELD_OFFSET(FILENAME_ATTRIBUTE
, NameLength
));
420 rcFCB
->Entry
.NameType
= FileName
->NameType
;
421 rcFCB
->RFCB
.FileSize
.QuadPart
= FileName
->DataSize
;
422 rcFCB
->RFCB
.ValidDataLength
.QuadPart
= FileName
->DataSize
;
423 rcFCB
->RFCB
.AllocationSize
.QuadPart
= FileName
->AllocatedSize
;
425 NtfsFCBInitializeCache(Vcb
, rcFCB
);
427 rcFCB
->MFTIndex
= MFTIndex
;
428 NtfsAddFCBToTable(Vcb
, rcFCB
);
431 return STATUS_SUCCESS
;
436 NtfsAttachFCBToFileObject(PNTFS_VCB Vcb
,
438 PFILE_OBJECT FileObject
)
442 newCCB
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(NTFS_CCB
), TAG_CCB
);
445 return STATUS_INSUFFICIENT_RESOURCES
;
448 RtlZeroMemory(newCCB
, sizeof(NTFS_CCB
));
450 newCCB
->Identifier
.Type
= NTFS_TYPE_CCB
;
451 newCCB
->Identifier
.Size
= sizeof(NTFS_TYPE_CCB
);
453 FileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
454 FileObject
->FsContext
= Fcb
;
455 FileObject
->FsContext2
= newCCB
;
456 newCCB
->PtrFileObject
= FileObject
;
459 if (!(Fcb
->Flags
& FCB_CACHE_INITIALIZED
))
461 CcInitializeCacheMap(FileObject
,
462 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
467 Fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
470 //DPRINT("file open: fcb:%x file size: %d\n", Fcb, Fcb->Entry.DataLengthL);
472 return STATUS_SUCCESS
;
477 NtfsDirFindFile(PNTFS_VCB Vcb
,
478 PNTFS_FCB DirectoryFcb
,
483 ULONGLONG CurrentDir
;
485 PFILE_RECORD_HEADER FileRecord
;
486 PNTFS_ATTR_CONTEXT DataContext
;
489 DPRINT1("NtfsDirFindFile(%p, %p, %S, %p)\n", Vcb
, DirectoryFcb
, FileToFind
, FoundFCB
);
492 RtlInitUnicodeString(&File
, FileToFind
);
493 CurrentDir
= DirectoryFcb
->MFTIndex
;
495 Status
= NtfsLookupFileAt(Vcb
, &File
, &FileRecord
, &DataContext
, &MFTIndex
, CurrentDir
);
496 if (!NT_SUCCESS(Status
))
501 Status
= NtfsMakeFCBFromDirEntry(Vcb
, DirectoryFcb
, &File
, FileRecord
, MFTIndex
, FoundFCB
);
502 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
509 NtfsGetFCBForFile(PNTFS_VCB Vcb
,
510 PNTFS_FCB
*pParentFCB
,
512 const PWSTR pFileName
)
515 WCHAR pathName
[MAX_PATH
];
516 WCHAR elementName
[MAX_PATH
];
517 PWCHAR currentElement
;
521 DPRINT("NtfsGetFCBForFile(%p, %p, %p, '%S')\n",
528 // FCB = NtfsOpenRootFCB(Vcb);
530 // *pParentFCB = NULL;
533 /* Trivial case, open of the root directory on volume */
534 if (pFileName
[0] == L
'\0' || wcscmp(pFileName
, L
"\\") == 0)
536 DPRINT("returning root FCB\n");
538 FCB
= NtfsOpenRootFCB(Vcb
);
542 return (FCB
!= NULL
) ? STATUS_SUCCESS
: STATUS_OBJECT_PATH_NOT_FOUND
;
546 currentElement
= pFileName
+ 1;
547 wcscpy (pathName
, L
"\\");
548 FCB
= NtfsOpenRootFCB (Vcb
);
553 /* Parse filename and check each path element for existance and access */
554 while (NtfsGetNextPathElement(currentElement
) != 0)
556 /* Skip blank directory levels */
557 if ((NtfsGetNextPathElement(currentElement
) - currentElement
) == 0)
563 DPRINT("Parsing, currentElement:%S\n", currentElement
);
564 DPRINT(" parentFCB:%p FCB:%p\n", parentFCB
, FCB
);
566 /* Descend to next directory level */
569 NtfsReleaseFCB(Vcb
, parentFCB
);
573 /* fail if element in FCB is not a directory */
574 if (!NtfsFCBIsDirectory(FCB
))
576 DPRINT("Element in requested path is not a directory\n");
578 NtfsReleaseFCB(Vcb
, FCB
);
583 return STATUS_OBJECT_PATH_NOT_FOUND
;
588 /* Extract next directory level into dirName */
589 NtfsWSubString(pathName
,
591 NtfsGetNextPathElement(currentElement
) - pFileName
);
592 DPRINT(" pathName:%S\n", pathName
);
594 FCB
= NtfsGrabFCBFromTable(Vcb
, pathName
);
597 NtfsWSubString(elementName
,
599 NtfsGetNextPathElement(currentElement
) - currentElement
);
600 DPRINT(" elementName:%S\n", elementName
);
602 Status
= NtfsDirFindFile(Vcb
, parentFCB
, elementName
, &FCB
);
603 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
605 *pParentFCB
= parentFCB
;
607 currentElement
= NtfsGetNextPathElement(currentElement
);
608 if (*currentElement
== L
'\0' || NtfsGetNextPathElement(currentElement
+ 1) == 0)
610 return STATUS_OBJECT_NAME_NOT_FOUND
;
614 return STATUS_OBJECT_PATH_NOT_FOUND
;
617 else if (!NT_SUCCESS(Status
))
619 NtfsReleaseFCB(Vcb
, parentFCB
);
627 currentElement
= NtfsGetNextPathElement(currentElement
);
630 *pParentFCB
= parentFCB
;
634 return STATUS_SUCCESS
;