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>
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(Fcb->FileObject);
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 = Vcb->StreamFileObject->FsContext;
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 = Fcb;
240 FileObject->FsContext2 = newCCB;
241 newCCB->PtrFileObject = FileObject;
242 Fcb->FileObject = FileObject;
245 Status = CcRosInitializeFileCache(FileObject,
247 if (!NT_SUCCESS(Status))
249 DbgPrint("CcRosInitializeFileCache failed\n");
253 ObDereferenceObject(FileObject);
254 Fcb->Flags |= FCB_CACHE_INITIALIZED;
261 NtfsMakeRootFCB(PDEVICE_EXTENSION Vcb)
265 Fcb = NtfsCreateFCB(L"\\");
267 // memset(Fcb->entry.Filename, ' ', 11);
269 // Fcb->Entry.DataLengthL = Vcb->CdInfo.RootSize;
270 // Fcb->Entry.ExtentLocationL = Vcb->CdInfo.RootStart;
271 // Fcb->Entry.FileFlags = 0x02; // FILE_ATTRIBUTE_DIRECTORY;
274 Fcb->RFCB.FileSize.QuadPart = PAGE_SIZE;//Vcb->CdInfo.RootSize;
275 Fcb->RFCB.ValidDataLength.QuadPart = PAGE_SIZE;//Vcb->CdInfo.RootSize;
276 Fcb->RFCB.AllocationSize.QuadPart = PAGE_SIZE;//Vcb->CdInfo.RootSize;
278 NtfsFCBInitializeCache(Vcb, Fcb);
279 NtfsAddFCBToTable(Vcb, Fcb);
280 NtfsGrabFCB(Vcb, Fcb);
287 NtfsOpenRootFCB(PDEVICE_EXTENSION Vcb)
291 Fcb = NtfsGrabFCBFromTable(Vcb, L"\\");
294 Fcb = NtfsMakeRootFCB(Vcb);
303 NtfsGetDirEntryName(PDEVICE_EXTENSION DeviceExt,
307 * FUNCTION: Retrieves the file name, be it in short or long file name format
310 if (Record->FileIdLength == 1 && Record->FileId[0] == 0)
314 else if (Record->FileIdLength == 1 && Record->FileId[0] == 1)
320 if (DeviceExt->CdInfo.JolietLevel == 0)
324 for (i = 0; i < Record->FileIdLength && Record->FileId[i] != ';'; i++)
325 Name[i] = (WCHAR)Record->FileId[i];
330 CdfsSwapString(Name, Record->FileId, Record->FileIdLength);
334 DPRINT("Name '%S'\n", Name);
339 NtfsMakeFCBFromDirEntry(PVCB Vcb,
345 WCHAR pathName[MAX_PATH];
349 if (Name [0] != 0 && wcslen (DirectoryFCB->PathName) +
350 sizeof(WCHAR) + wcslen (Name) > MAX_PATH)
352 return(STATUS_OBJECT_NAME_INVALID);
355 wcscpy(pathName, DirectoryFCB->PathName);
356 if (!CdfsFCBIsRoot(DirectoryFCB))
358 wcscat(pathName, L"\\");
363 wcscat(pathName, Name);
367 WCHAR entryName[MAX_PATH];
369 CdfsGetDirEntryName(Vcb, Record, entryName);
370 wcscat(pathName, entryName);
373 rcFCB = CdfsCreateFCB(pathName);
374 memcpy(&rcFCB->Entry, Record, sizeof(DIR_RECORD));
376 Size = rcFCB->Entry.DataLengthL;
378 rcFCB->RFCB.FileSize.QuadPart = Size;
379 rcFCB->RFCB.ValidDataLength.QuadPart = Size;
380 rcFCB->RFCB.AllocationSize.QuadPart = ROUND_UP(Size, BLOCKSIZE);
381 // DPRINT1("%S %d %d\n", longName, Size, (ULONG)rcFCB->RFCB.AllocationSize.QuadPart);
382 CdfsFCBInitializeCache(Vcb, rcFCB);
384 CdfsAddFCBToTable(Vcb, rcFCB);
387 return(STATUS_SUCCESS);
393 NtfsAttachFCBToFileObject(PDEVICE_EXTENSION Vcb,
395 PFILE_OBJECT FileObject)
400 newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(CCB), TAG_CCB);
403 return(STATUS_INSUFFICIENT_RESOURCES);
405 memset(newCCB, 0, sizeof(CCB));
407 FileObject->Flags = FileObject->Flags | FO_FCB_IS_VALID |
408 FO_DIRECT_CACHE_PAGING_READ;
409 FileObject->SectionObjectPointers = &Fcb->SectionObjectPointers;
410 FileObject->FsContext = Fcb;
411 FileObject->FsContext2 = newCCB;
412 newCCB->PtrFileObject = FileObject;
415 if (!(Fcb->Flags & FCB_CACHE_INITIALIZED))
417 Status = CcRosInitializeFileCache(FileObject,
419 if (!NT_SUCCESS(Status))
421 DbgPrint("CcRosInitializeFileCache failed\n");
424 Fcb->Flags |= FCB_CACHE_INITIALIZED;
427 DPRINT("file open: fcb:%x file size: %d\n", Fcb, Fcb->Entry.DataLengthL);
429 return(STATUS_SUCCESS);
435 NtfsDirFindFile(PDEVICE_EXTENSION DeviceExt,
450 LARGE_INTEGER StreamOffset;
454 assert(DirectoryFcb);
457 DPRINT("CdfsDirFindFile(VCB:%08x, dirFCB:%08x, File:%S)\n",
461 DPRINT("Dir Path:%S\n", DirectoryFcb->PathName);
463 /* default to '.' if no filename specified */
464 if (wcslen(FileToFind) == 0)
468 FileToFind = TempName;
471 DirSize = DirectoryFcb->Entry.DataLengthL;
472 StreamOffset.QuadPart = (LONGLONG)DirectoryFcb->Entry.ExtentLocationL * (LONGLONG)BLOCKSIZE;
474 if(!CcMapData(DeviceExt->StreamFileObject, &StreamOffset,
475 BLOCKSIZE, TRUE, &Context, &Block))
477 DPRINT("CcMapData() failed\n");
478 return(STATUS_UNSUCCESSFUL);
483 Record = (PDIR_RECORD)Block;
486 if (Record->RecordLength == 0)
488 DPRINT("RecordLength == 0 Stopped!\n");
492 DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n",
493 Record->RecordLength, Record->ExtAttrRecordLength, Record->FileIdLength);
495 CdfsGetDirEntryName(DeviceExt, Record, Name);
496 DPRINT("Name '%S'\n", Name);
498 if (wstrcmpjoki(Name, FileToFind))
500 DPRINT("Match found, %S\n", Name);
501 Status = CdfsMakeFCBFromDirEntry(DeviceExt,
507 CcUnpinData(Context);
512 Offset += Record->RecordLength;
513 BlockOffset += Record->RecordLength;
514 Record = (PDIR_RECORD)(Block + BlockOffset);
515 if (BlockOffset >= BLOCKSIZE || Record->RecordLength == 0)
517 DPRINT("Map next sector\n");
518 CcUnpinData(Context);
519 StreamOffset.QuadPart += BLOCKSIZE;
520 Offset = ROUND_UP(Offset, BLOCKSIZE);
523 if (!CcMapData(DeviceExt->StreamFileObject,
528 DPRINT("CcMapData() failed\n");
529 return(STATUS_UNSUCCESSFUL);
531 Record = (PDIR_RECORD)(Block + BlockOffset);
534 if (Offset >= DirSize)
538 CcUnpinData(Context);
540 return(STATUS_OBJECT_NAME_NOT_FOUND);
545 NtfsGetFCBForFile(PDEVICE_EXTENSION Vcb,
548 const PWSTR pFileName)
551 WCHAR pathName [MAX_PATH];
552 WCHAR elementName [MAX_PATH];
553 PWCHAR currentElement;
557 DPRINT("NtfsGetFCBForFile(%x, %x, %x, '%S')\n",
564 FCB = NtfsOpenRootFCB(Vcb);
569 /* Trivial case, open of the root directory on volume */
570 if (pFileName [0] == L'\0' || wcscmp(pFileName, L"\\") == 0)
572 DPRINT("returning root FCB\n");
574 FCB = NtfsOpenRootFCB(Vcb);
578 return((FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND);
582 currentElement = pFileName + 1;
583 wcscpy (pathName, L"\\");
584 FCB = CdfsOpenRootFCB (Vcb);
588 /* Parse filename and check each path element for existance and access */
589 while (CdfsGetNextPathElement(currentElement) != 0)
591 /* Skip blank directory levels */
592 if ((CdfsGetNextPathElement(currentElement) - currentElement) == 0)
598 DPRINT("Parsing, currentElement:%S\n", currentElement);
599 DPRINT(" parentFCB:%x FCB:%x\n", parentFCB, FCB);
601 /* Descend to next directory level */
604 CdfsReleaseFCB(Vcb, parentFCB);
608 /* fail if element in FCB is not a directory */
609 if (!CdfsFCBIsDirectory(FCB))
611 DPRINT("Element in requested path is not a directory\n");
613 CdfsReleaseFCB(Vcb, FCB);
618 return(STATUS_OBJECT_PATH_NOT_FOUND);
622 /* Extract next directory level into dirName */
623 CdfsWSubString(pathName,
625 CdfsGetNextPathElement(currentElement) - pFileName);
626 DPRINT(" pathName:%S\n", pathName);
628 FCB = CdfsGrabFCBFromTable(Vcb, pathName);
631 CdfsWSubString(elementName,
633 CdfsGetNextPathElement(currentElement) - currentElement);
634 DPRINT(" elementName:%S\n", elementName);
636 Status = CdfsDirFindFile(Vcb, parentFCB, elementName, &FCB);
637 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
639 *pParentFCB = parentFCB;
641 currentElement = CdfsGetNextPathElement(currentElement);
642 if (*currentElement == L'\0' || CdfsGetNextPathElement(currentElement + 1) == 0)
644 return(STATUS_OBJECT_NAME_NOT_FOUND);
648 return(STATUS_OBJECT_PATH_NOT_FOUND);
651 else if (!NT_SUCCESS(Status))
653 CdfsReleaseFCB(Vcb, parentFCB);
660 currentElement = CdfsGetNextPathElement(currentElement);
663 *pParentFCB = parentFCB;
667 return(STATUS_SUCCESS);