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.
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>
40 /* MACROS *******************************************************************/
42 #define TAG_FCB TAG('I', 'F', 'C', 'B')
46 /* FUNCTIONS ****************************************************************/
49 NtfsGetNextPathElement(PWCHAR FileName)
51 if (*FileName == L'\0')
56 while (*FileName != L'\0' && *FileName != L'\\')
66 NtfsWSubString(PWCHAR pTarget, const PWCHAR pSource, size_t pLength)
68 wcsncpy (pTarget, pSource, pLength);
69 pTarget [pLength] = L'\0';
74 NtfsCreateFCB(PWSTR FileName)
78 Fcb = ExAllocatePoolWithTag(NonPagedPool, sizeof(FCB), TAG_FCB);
79 RtlZeroMemory(Fcb, sizeof(FCB));
83 wcscpy(Fcb->PathName, FileName);
84 if (wcsrchr(Fcb->PathName, '\\') != 0)
86 Fcb->ObjectName = wcsrchr(Fcb->PathName, '\\');
90 Fcb->ObjectName = Fcb->PathName;
94 ExInitializeResourceLite(&Fcb->MainResource);
101 NtfsDestroyFCB(PFCB Fcb)
103 ExDeleteResourceLite(&Fcb->MainResource);
110 NtfsFCBIsDirectory(PFCB Fcb)
112 // return(Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY);
113 // return(Fcb->Entry.FileFlags & 0x02);
119 NtfsFCBIsRoot(PFCB Fcb)
121 return(wcscmp(Fcb->PathName, L"\\") == 0);
126 NtfsGrabFCB(PDEVICE_EXTENSION Vcb,
131 DPRINT("grabbing FCB at %x: %S, refCount:%d\n",
136 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
138 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
143 NtfsReleaseFCB(PDEVICE_EXTENSION Vcb,
148 DPRINT("releasing FCB at %x: %S, refCount:%d\n",
153 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
155 if (Fcb->RefCount <= 0 && !NtfsFCBIsDirectory(Fcb))
157 RemoveEntryList(&Fcb->FcbListEntry);
158 CcRosReleaseFileCache(Fcb->FileObject);
161 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
166 NtfsAddFCBToTable(PDEVICE_EXTENSION Vcb,
171 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
173 InsertTailList(&Vcb->FcbListHead, &Fcb->FcbListEntry);
174 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
179 NtfsGrabFCBFromTable(PDEVICE_EXTENSION Vcb,
184 PLIST_ENTRY current_entry;
186 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
188 if (FileName == NULL || *FileName == 0)
190 DPRINT("Return FCB for stream file object\n");
191 Fcb = Vcb->StreamFileObject->FsContext;
193 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
197 current_entry = Vcb->FcbListHead.Flink;
198 while (current_entry != &Vcb->FcbListHead)
200 Fcb = CONTAINING_RECORD(current_entry, FCB, FcbListEntry);
202 DPRINT("Comparing '%S' and '%S'\n", FileName, Fcb->PathName);
203 if (_wcsicmp(FileName, Fcb->PathName) == 0)
206 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
210 //FIXME: need to compare against short name in FCB here
212 current_entry = current_entry->Flink;
214 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
221 NtfsFCBInitializeCache(PVCB Vcb,
224 PFILE_OBJECT FileObject;
228 FileObject = IoCreateStreamFileObject(NULL, Vcb->StorageDevice);
230 newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(CCB), TAG_CCB);
233 return(STATUS_INSUFFICIENT_RESOURCES);
235 RtlZeroMemory(newCCB,
238 FileObject->Flags = FileObject->Flags | FO_FCB_IS_VALID |
239 FO_DIRECT_CACHE_PAGING_READ;
240 FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
241 FileObject->FsContext = Fcb;
242 FileObject->FsContext2 = newCCB;
243 newCCB->PtrFileObject = FileObject;
244 Fcb->FileObject = FileObject;
247 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 = PAGE_SIZE;//Vcb->CdInfo.RootSize;
277 Fcb->RFCB.ValidDataLength.QuadPart = PAGE_SIZE;//Vcb->CdInfo.RootSize;
278 Fcb->RFCB.AllocationSize.QuadPart = PAGE_SIZE;//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 NtfsSwapString(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 (!NtfsFCBIsRoot(DirectoryFCB))
360 wcscat(pathName, L"\\");
365 wcscat(pathName, Name);
369 WCHAR entryName[MAX_PATH];
371 NtfsGetDirEntryName(Vcb, Record, entryName);
372 wcscat(pathName, entryName);
375 rcFCB = NtfsCreateFCB(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 NtfsFCBInitializeCache(Vcb, rcFCB);
386 NtfsAddFCBToTable(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->SectionObjectPointer = &Fcb->SectionObjectPointers;
412 FileObject->FsContext = Fcb;
413 FileObject->FsContext2 = newCCB;
414 newCCB->PtrFileObject = FileObject;
417 if (!(Fcb->Flags & FCB_CACHE_INITIALIZED))
419 Status = CcRosInitializeFileCache(FileObject,
421 if (!NT_SUCCESS(Status))
423 DbgPrint("CcRosInitializeFileCache failed\n");
426 Fcb->Flags |= FCB_CACHE_INITIALIZED;
429 //DPRINT("file open: fcb:%x file size: %d\n", Fcb, Fcb->Entry.DataLengthL);
431 return(STATUS_SUCCESS);
436 NtfsDirFindFile(PDEVICE_EXTENSION DeviceExt,
452 LARGE_INTEGER StreamOffset;
456 assert(DirectoryFcb);
459 DPRINT("NtfsDirFindFile(VCB:%08x, dirFCB:%08x, File:%S)\n",
463 DPRINT("Dir Path:%S\n", DirectoryFcb->PathName);
465 /* default to '.' if no filename specified */
466 if (wcslen(FileToFind) == 0)
470 FileToFind = TempName;
473 DirSize = DirectoryFcb->Entry.DataLengthL;
474 StreamOffset.QuadPart = (LONGLONG)DirectoryFcb->Entry.ExtentLocationL * (LONGLONG)BLOCKSIZE;
476 if(!CcMapData(DeviceExt->StreamFileObject, &StreamOffset,
477 BLOCKSIZE, TRUE, &Context, &Block))
479 DPRINT("CcMapData() failed\n");
480 return(STATUS_UNSUCCESSFUL);
485 Record = (PDIR_RECORD)Block;
488 if (Record->RecordLength == 0)
490 DPRINT("RecordLength == 0 Stopped!\n");
494 DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n",
495 Record->RecordLength, Record->ExtAttrRecordLength, Record->FileIdLength);
497 NtfsGetDirEntryName(DeviceExt, Record, Name);
498 DPRINT("Name '%S'\n", Name);
500 if (wstrcmpjoki(Name, FileToFind))
502 DPRINT("Match found, %S\n", Name);
503 Status = NtfsMakeFCBFromDirEntry(DeviceExt,
509 CcUnpinData(Context);
514 Offset += Record->RecordLength;
515 BlockOffset += Record->RecordLength;
516 Record = (PDIR_RECORD)(Block + BlockOffset);
517 if (BlockOffset >= BLOCKSIZE || Record->RecordLength == 0)
519 DPRINT("Map next sector\n");
520 CcUnpinData(Context);
521 StreamOffset.QuadPart += BLOCKSIZE;
522 Offset = ROUND_UP(Offset, BLOCKSIZE);
525 if (!CcMapData(DeviceExt->StreamFileObject,
530 DPRINT("CcMapData() failed\n");
531 return(STATUS_UNSUCCESSFUL);
533 Record = (PDIR_RECORD)(Block + BlockOffset);
536 if (Offset >= DirSize)
540 CcUnpinData(Context);
542 return(STATUS_OBJECT_NAME_NOT_FOUND);
547 NtfsGetFCBForFile(PDEVICE_EXTENSION Vcb,
550 const PWSTR pFileName)
553 WCHAR pathName [MAX_PATH];
554 WCHAR elementName [MAX_PATH];
555 PWCHAR currentElement;
559 DPRINT("NtfsGetFCBForFile(%x, %x, %x, '%S')\n",
566 // FCB = NtfsOpenRootFCB(Vcb);
568 // *pParentFCB = NULL;
571 /* Trivial case, open of the root directory on volume */
572 if (pFileName [0] == L'\0' || wcscmp(pFileName, L"\\") == 0)
574 DPRINT("returning root FCB\n");
576 FCB = NtfsOpenRootFCB(Vcb);
580 return((FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND);
584 currentElement = pFileName + 1;
585 wcscpy (pathName, L"\\");
586 FCB = NtfsOpenRootFCB (Vcb);
590 /* Parse filename and check each path element for existance and access */
591 while (NtfsGetNextPathElement(currentElement) != 0)
593 /* Skip blank directory levels */
594 if ((NtfsGetNextPathElement(currentElement) - currentElement) == 0)
600 DPRINT("Parsing, currentElement:%S\n", currentElement);
601 DPRINT(" parentFCB:%x FCB:%x\n", parentFCB, FCB);
603 /* Descend to next directory level */
606 NtfsReleaseFCB(Vcb, parentFCB);
610 /* fail if element in FCB is not a directory */
611 if (!NtfsFCBIsDirectory(FCB))
613 DPRINT("Element in requested path is not a directory\n");
615 NtfsReleaseFCB(Vcb, FCB);
620 return(STATUS_OBJECT_PATH_NOT_FOUND);
624 /* Extract next directory level into dirName */
625 NtfsWSubString(pathName,
627 NtfsGetNextPathElement(currentElement) - pFileName);
628 DPRINT(" pathName:%S\n", pathName);
630 FCB = NtfsGrabFCBFromTable(Vcb, pathName);
633 NtfsWSubString(elementName,
635 NtfsGetNextPathElement(currentElement) - currentElement);
636 DPRINT(" elementName:%S\n", elementName);
638 Status = NtfsDirFindFile(Vcb, parentFCB, elementName, &FCB);
639 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
641 *pParentFCB = parentFCB;
643 currentElement = NtfsGetNextPathElement(currentElement);
644 if (*currentElement == L'\0' || NtfsGetNextPathElement(currentElement + 1) == 0)
646 return(STATUS_OBJECT_NAME_NOT_FOUND);
650 return(STATUS_OBJECT_PATH_NOT_FOUND);
653 else if (!NT_SUCCESS(Status))
655 NtfsReleaseFCB(Vcb, parentFCB);
662 currentElement = NtfsGetNextPathElement(currentElement);
665 *pParentFCB = parentFCB;
669 return(STATUS_SUCCESS);