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 /* GLOBALS *****************************************************************/
37 /* MACROS *******************************************************************/
39 #define TAG_FCB 'BCFI'
43 /* FUNCTIONS ****************************************************************/
47 NtfsGetNextPathElement(PWCHAR FileName
)
49 if (*FileName
== L
'\0')
54 while (*FileName
!= L
'\0' && *FileName
!= L
'\\')
65 NtfsWSubString(PWCHAR pTarget
,
69 wcsncpy(pTarget
, pSource
, pLength
);
70 pTarget
[pLength
] = L
'\0';
75 NtfsCreateFCB(PCWSTR FileName
,
81 ASSERT(Vcb
->Identifier
.Type
== NTFS_TYPE_VCB
);
83 Fcb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(NTFS_FCB
), TAG_FCB
);
84 RtlZeroMemory(Fcb
, sizeof(NTFS_FCB
));
86 Fcb
->Identifier
.Type
= NTFS_TYPE_FCB
;
87 Fcb
->Identifier
.Size
= sizeof(NTFS_TYPE_FCB
);
93 wcscpy(Fcb
->PathName
, FileName
);
94 if (wcsrchr(Fcb
->PathName
, '\\') != 0)
96 Fcb
->ObjectName
= wcsrchr(Fcb
->PathName
, '\\');
100 Fcb
->ObjectName
= Fcb
->PathName
;
104 ExInitializeResourceLite(&Fcb
->MainResource
);
106 Fcb
->RFCB
.Resource
= &(Fcb
->MainResource
);
113 NtfsDestroyFCB(PNTFS_FCB Fcb
)
116 ASSERT(Fcb
->Identifier
.Type
== NTFS_TYPE_FCB
);
118 ExDeleteResourceLite(&Fcb
->MainResource
);
125 NtfsFCBIsDirectory(PNTFS_FCB Fcb
)
127 UNREFERENCED_PARAMETER(Fcb
);
128 // return(Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY);
129 // return(Fcb->Entry.FileFlags & 0x02);
135 NtfsFCBIsRoot(PNTFS_FCB Fcb
)
137 return (wcscmp(Fcb
->PathName
, L
"\\") == 0);
142 NtfsGrabFCB(PNTFS_VCB Vcb
,
147 DPRINT("grabbing FCB at %p: %S, refCount:%d\n",
152 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
154 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
159 NtfsReleaseFCB(PNTFS_VCB Vcb
,
164 DPRINT("releasing FCB at %p: %S, refCount:%d\n",
169 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
171 if (Fcb
->RefCount
<= 0 && !NtfsFCBIsDirectory(Fcb
))
173 RemoveEntryList(&Fcb
->FcbListEntry
);
174 CcUninitializeCacheMap(Fcb
->FileObject
, NULL
, NULL
);
178 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
183 NtfsAddFCBToTable(PNTFS_VCB Vcb
,
188 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
190 InsertTailList(&Vcb
->FcbListHead
, &Fcb
->FcbListEntry
);
191 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
196 NtfsGrabFCBFromTable(PNTFS_VCB Vcb
,
201 PLIST_ENTRY current_entry
;
203 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
205 if (FileName
== NULL
|| *FileName
== 0)
207 DPRINT("Return FCB for stream file object\n");
208 Fcb
= Vcb
->StreamFileObject
->FsContext
;
210 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
214 current_entry
= Vcb
->FcbListHead
.Flink
;
215 while (current_entry
!= &Vcb
->FcbListHead
)
217 Fcb
= CONTAINING_RECORD(current_entry
, NTFS_FCB
, FcbListEntry
);
219 DPRINT("Comparing '%S' and '%S'\n", FileName
, Fcb
->PathName
);
220 if (_wcsicmp(FileName
, Fcb
->PathName
) == 0)
223 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
227 //FIXME: need to compare against short name in FCB here
229 current_entry
= current_entry
->Flink
;
232 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
239 NtfsFCBInitializeCache(PNTFS_VCB Vcb
,
242 PFILE_OBJECT FileObject
;
246 FileObject
= IoCreateStreamFileObject(NULL
, Vcb
->StorageDevice
);
248 newCCB
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(NTFS_CCB
), TAG_CCB
);
251 return STATUS_INSUFFICIENT_RESOURCES
;
254 RtlZeroMemory(newCCB
, sizeof(NTFS_CCB
));
256 newCCB
->Identifier
.Type
= NTFS_TYPE_CCB
;
257 newCCB
->Identifier
.Size
= sizeof(NTFS_TYPE_CCB
);
259 FileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
260 FileObject
->FsContext
= Fcb
;
261 FileObject
->FsContext2
= newCCB
;
262 newCCB
->PtrFileObject
= FileObject
;
263 Fcb
->FileObject
= FileObject
;
266 Status
= STATUS_SUCCESS
;
267 CcInitializeCacheMap(FileObject
,
268 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
270 &(NtfsGlobalData
->CacheMgrCallbacks
),
273 ObDereferenceObject(FileObject
);
274 Fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
281 NtfsMakeRootFCB(PNTFS_VCB Vcb
)
285 Fcb
= NtfsCreateFCB(L
"\\", Vcb
);
287 // memset(Fcb->entry.Filename, ' ', 11);
289 // Fcb->Entry.DataLengthL = Vcb->CdInfo.RootSize;
290 // Fcb->Entry.ExtentLocationL = Vcb->CdInfo.RootStart;
291 // Fcb->Entry.FileFlags = 0x02; // FILE_ATTRIBUTE_DIRECTORY;
294 Fcb
->RFCB
.FileSize
.QuadPart
= PAGE_SIZE
;//Vcb->CdInfo.RootSize;
295 Fcb
->RFCB
.ValidDataLength
.QuadPart
= PAGE_SIZE
;//Vcb->CdInfo.RootSize;
296 Fcb
->RFCB
.AllocationSize
.QuadPart
= PAGE_SIZE
;//Vcb->CdInfo.RootSize;
298 NtfsFCBInitializeCache(Vcb
, Fcb
);
299 NtfsAddFCBToTable(Vcb
, Fcb
);
300 NtfsGrabFCB(Vcb
, Fcb
);
307 NtfsOpenRootFCB(PNTFS_VCB Vcb
)
311 Fcb
= NtfsGrabFCBFromTable(Vcb
, L
"\\");
314 Fcb
= NtfsMakeRootFCB(Vcb
);
323 NtfsGetDirEntryName(PDEVICE_EXTENSION DeviceExt
,
327 * FUNCTION: Retrieves the file name, be it in short or long file name format
330 if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 0)
334 else if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 1)
340 if (DeviceExt
->CdInfo
.JolietLevel
== 0)
344 for (i
= 0; i
< Record
->FileIdLength
&& Record
->FileId
[i
] != ';'; i
++)
345 Name
[i
] = (WCHAR
)Record
->FileId
[i
];
350 NtfsSwapString(Name
, Record
->FileId
, Record
->FileIdLength
);
354 DPRINT("Name '%S'\n", Name
);
359 NtfsMakeFCBFromDirEntry(PVCB Vcb
,
365 WCHAR pathName
[MAX_PATH
];
369 if (Name
[0] != 0 && wcslen (DirectoryFCB
->PathName
) +
370 sizeof(WCHAR
) + wcslen (Name
) > MAX_PATH
)
372 return(STATUS_OBJECT_NAME_INVALID
);
375 wcscpy(pathName
, DirectoryFCB
->PathName
);
376 if (!NtfsFCBIsRoot(DirectoryFCB
))
378 wcscat(pathName
, L
"\\");
383 wcscat(pathName
, Name
);
387 WCHAR entryName
[MAX_PATH
];
389 NtfsGetDirEntryName(Vcb
, Record
, entryName
);
390 wcscat(pathName
, entryName
);
393 rcFCB
= NtfsCreateFCB(pathName
, Vcb
);
394 memcpy(&rcFCB
->Entry
, Record
, sizeof(DIR_RECORD
));
396 Size
= rcFCB
->Entry
.DataLengthL
;
398 rcFCB
->RFCB
.FileSize
.QuadPart
= Size
;
399 rcFCB
->RFCB
.ValidDataLength
.QuadPart
= Size
;
400 rcFCB
->RFCB
.AllocationSize
.QuadPart
= ROUND_UP(Size
, BLOCKSIZE
);
401 // DPRINT1("%S %d %d\n", longName, Size, (ULONG)rcFCB->RFCB.AllocationSize.QuadPart);
402 NtfsFCBInitializeCache(Vcb
, rcFCB
);
404 NtfsAddFCBToTable(Vcb
, rcFCB
);
407 return(STATUS_SUCCESS
);
413 NtfsAttachFCBToFileObject(PNTFS_VCB Vcb
,
415 PFILE_OBJECT FileObject
)
419 newCCB
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(NTFS_CCB
), TAG_CCB
);
422 return STATUS_INSUFFICIENT_RESOURCES
;
425 RtlZeroMemory(newCCB
, sizeof(NTFS_CCB
));
427 newCCB
->Identifier
.Type
= NTFS_TYPE_CCB
;
428 newCCB
->Identifier
.Size
= sizeof(NTFS_TYPE_CCB
);
430 FileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
431 FileObject
->FsContext
= Fcb
;
432 FileObject
->FsContext2
= newCCB
;
433 newCCB
->PtrFileObject
= FileObject
;
436 if (!(Fcb
->Flags
& FCB_CACHE_INITIALIZED
))
438 CcInitializeCacheMap(FileObject
,
439 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
444 Fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
447 //DPRINT("file open: fcb:%x file size: %d\n", Fcb, Fcb->Entry.DataLengthL);
449 return STATUS_SUCCESS
;
454 NtfsDirFindFile(PNTFS_VCB Vcb
,
455 PNTFS_FCB DirectoryFcb
,
470 LARGE_INTEGER StreamOffset
;
474 ASSERT(DirectoryFcb
);
477 DPRINT("NtfsDirFindFile(VCB:%08x, dirFCB:%08x, File:%S)\n",
481 DPRINT("Dir Path:%S\n", DirectoryFcb
->PathName
);
483 /* default to '.' if no filename specified */
484 if (wcslen(FileToFind
) == 0)
488 FileToFind
= TempName
;
491 DirSize
= DirectoryFcb
->Entry
.DataLengthL
;
492 StreamOffset
.QuadPart
= (LONGLONG
)DirectoryFcb
->Entry
.ExtentLocationL
* (LONGLONG
)BLOCKSIZE
;
494 if(!CcMapData(DeviceExt
->StreamFileObject
, &StreamOffset
,
495 BLOCKSIZE
, TRUE
, &Context
, &Block
))
497 DPRINT("CcMapData() failed\n");
498 return(STATUS_UNSUCCESSFUL
);
503 Record
= (PDIR_RECORD
)Block
;
506 if (Record
->RecordLength
== 0)
508 DPRINT("RecordLength == 0 Stopped!\n");
512 DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n",
513 Record
->RecordLength
, Record
->ExtAttrRecordLength
, Record
->FileIdLength
);
515 NtfsGetDirEntryName(DeviceExt
, Record
, Name
);
516 DPRINT("Name '%S'\n", Name
);
518 if (wstrcmpjoki(Name
, FileToFind
))
520 DPRINT("Match found, %S\n", Name
);
521 Status
= NtfsMakeFCBFromDirEntry(DeviceExt
,
527 CcUnpinData(Context
);
532 Offset
+= Record
->RecordLength
;
533 BlockOffset
+= Record
->RecordLength
;
534 Record
= (PDIR_RECORD
)(Block
+ BlockOffset
);
535 if (BlockOffset
>= BLOCKSIZE
|| Record
->RecordLength
== 0)
537 DPRINT("Map next sector\n");
538 CcUnpinData(Context
);
539 StreamOffset
.QuadPart
+= BLOCKSIZE
;
540 Offset
= ROUND_UP(Offset
, BLOCKSIZE
);
543 if (!CcMapData(DeviceExt
->StreamFileObject
,
548 DPRINT("CcMapData() failed\n");
549 return(STATUS_UNSUCCESSFUL
);
551 Record
= (PDIR_RECORD
)(Block
+ BlockOffset
);
554 if (Offset
>= DirSize
)
558 CcUnpinData(Context
);
560 UNREFERENCED_PARAMETER(Vcb
);
561 UNREFERENCED_PARAMETER(DirectoryFcb
);
562 UNREFERENCED_PARAMETER(FileToFind
);
563 UNREFERENCED_PARAMETER(FoundFCB
);
565 return STATUS_OBJECT_NAME_NOT_FOUND
;
570 NtfsGetFCBForFile(PNTFS_VCB Vcb
,
571 PNTFS_FCB
*pParentFCB
,
573 const PWSTR pFileName
)
576 WCHAR pathName
[MAX_PATH
];
577 WCHAR elementName
[MAX_PATH
];
578 PWCHAR currentElement
;
582 DPRINT("NtfsGetFCBForFile(%p, %p, %p, '%S')\n",
589 // FCB = NtfsOpenRootFCB(Vcb);
591 // *pParentFCB = NULL;
594 /* Trivial case, open of the root directory on volume */
595 if (pFileName
[0] == L
'\0' || wcscmp(pFileName
, L
"\\") == 0)
597 DPRINT("returning root FCB\n");
599 FCB
= NtfsOpenRootFCB(Vcb
);
603 return (FCB
!= NULL
) ? STATUS_SUCCESS
: STATUS_OBJECT_PATH_NOT_FOUND
;
607 currentElement
= pFileName
+ 1;
608 wcscpy (pathName
, L
"\\");
609 FCB
= NtfsOpenRootFCB (Vcb
);
614 /* Parse filename and check each path element for existance and access */
615 while (NtfsGetNextPathElement(currentElement
) != 0)
617 /* Skip blank directory levels */
618 if ((NtfsGetNextPathElement(currentElement
) - currentElement
) == 0)
624 DPRINT("Parsing, currentElement:%S\n", currentElement
);
625 DPRINT(" parentFCB:%p FCB:%p\n", parentFCB
, FCB
);
627 /* Descend to next directory level */
630 NtfsReleaseFCB(Vcb
, parentFCB
);
634 /* fail if element in FCB is not a directory */
635 if (!NtfsFCBIsDirectory(FCB
))
637 DPRINT("Element in requested path is not a directory\n");
639 NtfsReleaseFCB(Vcb
, FCB
);
644 return STATUS_OBJECT_PATH_NOT_FOUND
;
649 /* Extract next directory level into dirName */
650 NtfsWSubString(pathName
,
652 NtfsGetNextPathElement(currentElement
) - pFileName
);
653 DPRINT(" pathName:%S\n", pathName
);
655 FCB
= NtfsGrabFCBFromTable(Vcb
, pathName
);
658 NtfsWSubString(elementName
,
660 NtfsGetNextPathElement(currentElement
) - currentElement
);
661 DPRINT(" elementName:%S\n", elementName
);
663 Status
= NtfsDirFindFile(Vcb
, parentFCB
, elementName
, &FCB
);
664 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
666 *pParentFCB
= parentFCB
;
668 currentElement
= NtfsGetNextPathElement(currentElement
);
669 if (*currentElement
== L
'\0' || NtfsGetNextPathElement(currentElement
+ 1) == 0)
671 return STATUS_OBJECT_NAME_NOT_FOUND
;
675 return STATUS_OBJECT_PATH_NOT_FOUND
;
678 else if (!NT_SUCCESS(Status
))
680 NtfsReleaseFCB(Vcb
, parentFCB
);
688 currentElement
= NtfsGetNextPathElement(currentElement
);
691 *pParentFCB
= parentFCB
;
695 return STATUS_SUCCESS
;