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., 675 Mass Ave, Cambridge, MA 02139, USA.
19 /* $Id: fcb.c,v 1.3 2002/09/08 10:22:11 chorns Exp $
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * FILE: services/fs/ntfs/fcb.c
24 * PURPOSE: NTFS filesystem driver
25 * PROGRAMMER: Eric Kohl
28 /* INCLUDES *****************************************************************/
30 #include <ddk/ntddk.h>
38 /* MACROS *******************************************************************/
40 #define TAG_FCB TAG('I', 'F', 'C', 'B')
44 /* FUNCTIONS ****************************************************************/
47 NtfsGetNextPathElement(PWCHAR FileName
)
49 if (*FileName
== L
'\0')
54 while (*FileName
!= L
'\0' && *FileName
!= L
'\\')
64 NtfsWSubString(PWCHAR pTarget
, const PWCHAR pSource
, size_t pLength
)
66 wcsncpy (pTarget
, pSource
, pLength
);
67 pTarget
[pLength
] = L
'\0';
72 NtfsCreateFCB(PWSTR FileName
)
76 Fcb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(FCB
), TAG_FCB
);
77 RtlZeroMemory(Fcb
, sizeof(FCB
));
81 wcscpy(Fcb
->PathName
, FileName
);
82 if (wcsrchr(Fcb
->PathName
, '\\') != 0)
84 Fcb
->ObjectName
= wcsrchr(Fcb
->PathName
, '\\');
88 Fcb
->ObjectName
= Fcb
->PathName
;
92 ExInitializeResourceLite(&Fcb
->MainResource
);
99 NtfsDestroyFCB(PFCB Fcb
)
101 ExDeleteResourceLite(&Fcb
->MainResource
);
108 NtfsFCBIsDirectory(PFCB Fcb
)
110 // return(Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY);
111 // return(Fcb->Entry.FileFlags & 0x02);
117 NtfsFCBIsRoot(PFCB Fcb
)
119 return(wcscmp(Fcb
->PathName
, L
"\\") == 0);
124 NtfsGrabFCB(PDEVICE_EXTENSION Vcb
,
129 DPRINT("grabbing FCB at %x: %S, refCount:%d\n",
134 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
136 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
141 NtfsReleaseFCB(PDEVICE_EXTENSION Vcb
,
146 DPRINT("releasing FCB at %x: %S, refCount:%d\n",
151 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
153 if (Fcb
->RefCount
<= 0 && !NtfsFCBIsDirectory(Fcb
))
155 RemoveEntryList(&Fcb
->FcbListEntry
);
156 CcRosReleaseFileCache(NULL
, Fcb
->RFCB
.Bcb
);
159 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
164 NtfsAddFCBToTable(PDEVICE_EXTENSION Vcb
,
169 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
171 InsertTailList(&Vcb
->FcbListHead
, &Fcb
->FcbListEntry
);
172 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
177 NtfsGrabFCBFromTable(PDEVICE_EXTENSION Vcb
,
182 PLIST_ENTRY current_entry
;
184 KeAcquireSpinLock(&Vcb
->FcbListLock
, &oldIrql
);
186 if (FileName
== NULL
|| *FileName
== 0)
188 DPRINT("Return FCB for stream file object\n");
189 Fcb
= ((PCCB
)Vcb
->StreamFileObject
->FsContext2
)->Fcb
;
191 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
195 current_entry
= Vcb
->FcbListHead
.Flink
;
196 while (current_entry
!= &Vcb
->FcbListHead
)
198 Fcb
= CONTAINING_RECORD(current_entry
, FCB
, FcbListEntry
);
200 DPRINT("Comparing '%S' and '%S'\n", FileName
, Fcb
->PathName
);
201 if (_wcsicmp(FileName
, Fcb
->PathName
) == 0)
204 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
208 //FIXME: need to compare against short name in FCB here
210 current_entry
= current_entry
->Flink
;
212 KeReleaseSpinLock(&Vcb
->FcbListLock
, oldIrql
);
219 NtfsFCBInitializeCache(PVCB Vcb
,
222 PFILE_OBJECT FileObject
;
226 FileObject
= IoCreateStreamFileObject(NULL
, Vcb
->StorageDevice
);
228 newCCB
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(CCB
), TAG_CCB
);
231 return(STATUS_INSUFFICIENT_RESOURCES
);
233 RtlZeroMemory(newCCB
,
236 FileObject
->Flags
= FileObject
->Flags
| FO_FCB_IS_VALID
|
237 FO_DIRECT_CACHE_PAGING_READ
;
238 FileObject
->SectionObjectPointers
= &Fcb
->SectionObjectPointers
;
239 FileObject
->FsContext
= (PVOID
) &Fcb
->RFCB
;
240 FileObject
->FsContext2
= newCCB
;
242 newCCB
->PtrFileObject
= FileObject
;
243 Fcb
->FileObject
= FileObject
;
246 Status
= CcRosInitializeFileCache(FileObject
,
249 if (!NT_SUCCESS(Status
))
251 DbgPrint("CcRosInitializeFileCache failed\n");
255 ObDereferenceObject(FileObject
);
256 Fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
263 NtfsMakeRootFCB(PDEVICE_EXTENSION Vcb
)
267 Fcb
= NtfsCreateFCB(L
"\\");
269 // memset(Fcb->entry.Filename, ' ', 11);
271 // Fcb->Entry.DataLengthL = Vcb->CdInfo.RootSize;
272 // Fcb->Entry.ExtentLocationL = Vcb->CdInfo.RootStart;
273 // Fcb->Entry.FileFlags = 0x02; // FILE_ATTRIBUTE_DIRECTORY;
276 Fcb
->RFCB
.FileSize
.QuadPart
= PAGESIZE
;//Vcb->CdInfo.RootSize;
277 Fcb
->RFCB
.ValidDataLength
.QuadPart
= PAGESIZE
;//Vcb->CdInfo.RootSize;
278 Fcb
->RFCB
.AllocationSize
.QuadPart
= PAGESIZE
;//Vcb->CdInfo.RootSize;
280 NtfsFCBInitializeCache(Vcb
, Fcb
);
281 NtfsAddFCBToTable(Vcb
, Fcb
);
282 NtfsGrabFCB(Vcb
, Fcb
);
289 NtfsOpenRootFCB(PDEVICE_EXTENSION Vcb
)
293 Fcb
= NtfsGrabFCBFromTable(Vcb
, L
"\\");
296 Fcb
= NtfsMakeRootFCB(Vcb
);
305 NtfsGetDirEntryName(PDEVICE_EXTENSION DeviceExt
,
309 * FUNCTION: Retrieves the file name, be it in short or long file name format
312 if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 0)
316 else if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 1)
322 if (DeviceExt
->CdInfo
.JolietLevel
== 0)
326 for (i
= 0; i
< Record
->FileIdLength
&& Record
->FileId
[i
] != ';'; i
++)
327 Name
[i
] = (WCHAR
)Record
->FileId
[i
];
332 CdfsSwapString(Name
, Record
->FileId
, Record
->FileIdLength
);
336 DPRINT("Name '%S'\n", Name
);
341 NtfsMakeFCBFromDirEntry(PVCB Vcb
,
347 WCHAR pathName
[MAX_PATH
];
351 if (Name
[0] != 0 && wcslen (DirectoryFCB
->PathName
) +
352 sizeof(WCHAR
) + wcslen (Name
) > MAX_PATH
)
354 return(STATUS_OBJECT_NAME_INVALID
);
357 wcscpy(pathName
, DirectoryFCB
->PathName
);
358 if (!CdfsFCBIsRoot(DirectoryFCB
))
360 wcscat(pathName
, L
"\\");
365 wcscat(pathName
, Name
);
369 WCHAR entryName
[MAX_PATH
];
371 CdfsGetDirEntryName(Vcb
, Record
, entryName
);
372 wcscat(pathName
, entryName
);
375 rcFCB
= CdfsCreateFCB(pathName
);
376 memcpy(&rcFCB
->Entry
, Record
, sizeof(DIR_RECORD
));
378 Size
= rcFCB
->Entry
.DataLengthL
;
380 rcFCB
->RFCB
.FileSize
.QuadPart
= Size
;
381 rcFCB
->RFCB
.ValidDataLength
.QuadPart
= Size
;
382 rcFCB
->RFCB
.AllocationSize
.QuadPart
= ROUND_UP(Size
, BLOCKSIZE
);
383 // DPRINT1("%S %d %d\n", longName, Size, (ULONG)rcFCB->RFCB.AllocationSize.QuadPart);
384 CdfsFCBInitializeCache(Vcb
, rcFCB
);
386 CdfsAddFCBToTable(Vcb
, rcFCB
);
389 return(STATUS_SUCCESS
);
395 NtfsAttachFCBToFileObject(PDEVICE_EXTENSION Vcb
,
397 PFILE_OBJECT FileObject
)
402 newCCB
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(CCB
), TAG_CCB
);
405 return(STATUS_INSUFFICIENT_RESOURCES
);
407 memset(newCCB
, 0, sizeof(CCB
));
409 FileObject
->Flags
= FileObject
->Flags
| FO_FCB_IS_VALID
|
410 FO_DIRECT_CACHE_PAGING_READ
;
411 FileObject
->SectionObjectPointers
= &Fcb
->SectionObjectPointers
;
412 FileObject
->FsContext
= (PVOID
)&Fcb
->RFCB
;
413 FileObject
->FsContext2
= newCCB
;
415 newCCB
->PtrFileObject
= FileObject
;
418 if (!(Fcb
->Flags
& FCB_CACHE_INITIALIZED
))
420 Status
= CcRosInitializeFileCache(FileObject
,
423 if (!NT_SUCCESS(Status
))
425 DbgPrint("CcRosInitializeFileCache failed\n");
428 Fcb
->Flags
|= FCB_CACHE_INITIALIZED
;
431 DPRINT("file open: fcb:%x file size: %d\n", Fcb
, Fcb
->Entry
.DataLengthL
);
433 return(STATUS_SUCCESS
);
439 NtfsDirFindFile(PDEVICE_EXTENSION DeviceExt
,
454 LARGE_INTEGER StreamOffset
;
458 assert(DirectoryFcb
);
461 DPRINT("CdfsDirFindFile(VCB:%08x, dirFCB:%08x, File:%S)\n",
465 DPRINT("Dir Path:%S\n", DirectoryFcb
->PathName
);
467 /* default to '.' if no filename specified */
468 if (wcslen(FileToFind
) == 0)
472 FileToFind
= TempName
;
475 DirSize
= DirectoryFcb
->Entry
.DataLengthL
;
476 StreamOffset
.QuadPart
= (LONGLONG
)DirectoryFcb
->Entry
.ExtentLocationL
* (LONGLONG
)BLOCKSIZE
;
478 if(!CcMapData(DeviceExt
->StreamFileObject
, &StreamOffset
,
479 BLOCKSIZE
, TRUE
, &Context
, &Block
))
481 DPRINT("CcMapData() failed\n");
482 return(STATUS_UNSUCCESSFUL
);
487 Record
= (PDIR_RECORD
)Block
;
490 if (Record
->RecordLength
== 0)
492 DPRINT("RecordLength == 0 Stopped!\n");
496 DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n",
497 Record
->RecordLength
, Record
->ExtAttrRecordLength
, Record
->FileIdLength
);
499 CdfsGetDirEntryName(DeviceExt
, Record
, Name
);
500 DPRINT("Name '%S'\n", Name
);
502 if (wstrcmpjoki(Name
, FileToFind
))
504 DPRINT("Match found, %S\n", Name
);
505 Status
= CdfsMakeFCBFromDirEntry(DeviceExt
,
511 CcUnpinData(Context
);
516 Offset
+= Record
->RecordLength
;
517 BlockOffset
+= Record
->RecordLength
;
518 Record
= (PDIR_RECORD
)(Block
+ BlockOffset
);
519 if (BlockOffset
>= BLOCKSIZE
|| Record
->RecordLength
== 0)
521 DPRINT("Map next sector\n");
522 CcUnpinData(Context
);
523 StreamOffset
.QuadPart
+= BLOCKSIZE
;
524 Offset
= ROUND_UP(Offset
, BLOCKSIZE
);
527 if (!CcMapData(DeviceExt
->StreamFileObject
,
532 DPRINT("CcMapData() failed\n");
533 return(STATUS_UNSUCCESSFUL
);
535 Record
= (PDIR_RECORD
)(Block
+ BlockOffset
);
538 if (Offset
>= DirSize
)
542 CcUnpinData(Context
);
544 return(STATUS_OBJECT_NAME_NOT_FOUND
);
549 NtfsGetFCBForFile(PDEVICE_EXTENSION Vcb
,
552 const PWSTR pFileName
)
555 WCHAR pathName
[MAX_PATH
];
556 WCHAR elementName
[MAX_PATH
];
557 PWCHAR currentElement
;
561 DPRINT("NtfsGetFCBForFile(%x, %x, %x, '%S')\n",
568 FCB
= NtfsOpenRootFCB(Vcb
);
573 /* Trivial case, open of the root directory on volume */
574 if (pFileName
[0] == L
'\0' || wcscmp(pFileName
, L
"\\") == 0)
576 DPRINT("returning root FCB\n");
578 FCB
= NtfsOpenRootFCB(Vcb
);
582 return((FCB
!= NULL
) ? STATUS_SUCCESS
: STATUS_OBJECT_PATH_NOT_FOUND
);
586 currentElement
= pFileName
+ 1;
587 wcscpy (pathName
, L
"\\");
588 FCB
= CdfsOpenRootFCB (Vcb
);
592 /* Parse filename and check each path element for existance and access */
593 while (CdfsGetNextPathElement(currentElement
) != 0)
595 /* Skip blank directory levels */
596 if ((CdfsGetNextPathElement(currentElement
) - currentElement
) == 0)
602 DPRINT("Parsing, currentElement:%S\n", currentElement
);
603 DPRINT(" parentFCB:%x FCB:%x\n", parentFCB
, FCB
);
605 /* Descend to next directory level */
608 CdfsReleaseFCB(Vcb
, parentFCB
);
612 /* fail if element in FCB is not a directory */
613 if (!CdfsFCBIsDirectory(FCB
))
615 DPRINT("Element in requested path is not a directory\n");
617 CdfsReleaseFCB(Vcb
, FCB
);
622 return(STATUS_OBJECT_PATH_NOT_FOUND
);
626 /* Extract next directory level into dirName */
627 CdfsWSubString(pathName
,
629 CdfsGetNextPathElement(currentElement
) - pFileName
);
630 DPRINT(" pathName:%S\n", pathName
);
632 FCB
= CdfsGrabFCBFromTable(Vcb
, pathName
);
635 CdfsWSubString(elementName
,
637 CdfsGetNextPathElement(currentElement
) - currentElement
);
638 DPRINT(" elementName:%S\n", elementName
);
640 Status
= CdfsDirFindFile(Vcb
, parentFCB
, elementName
, &FCB
);
641 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
643 *pParentFCB
= parentFCB
;
645 currentElement
= CdfsGetNextPathElement(currentElement
);
646 if (*currentElement
== L
'\0' || CdfsGetNextPathElement(currentElement
+ 1) == 0)
648 return(STATUS_OBJECT_NAME_NOT_FOUND
);
652 return(STATUS_OBJECT_PATH_NOT_FOUND
);
655 else if (!NT_SUCCESS(Status
))
657 CdfsReleaseFCB(Vcb
, parentFCB
);
664 currentElement
= CdfsGetNextPathElement(currentElement
);
667 *pParentFCB
= parentFCB
;
671 return(STATUS_SUCCESS
);