Sync with trunk.
[reactos.git] / drivers / filesystems / ntfs / fcb.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2002 ReactOS Team
4 *
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.
9 *
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.
14 *
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.
18 *
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
24 */
25
26 /* INCLUDES *****************************************************************/
27
28 #include "ntfs.h"
29
30 #define NDEBUG
31 #include <debug.h>
32
33 /* GLOBALS *****************************************************************/
34
35
36
37 /* MACROS *******************************************************************/
38
39 #define TAG_FCB 'BCFI'
40
41
42
43 /* FUNCTIONS ****************************************************************/
44
45 static
46 PWCHAR
47 NtfsGetNextPathElement(PWCHAR FileName)
48 {
49 if (*FileName == L'\0')
50 {
51 return NULL;
52 }
53
54 while (*FileName != L'\0' && *FileName != L'\\')
55 {
56 FileName++;
57 }
58
59 return FileName;
60 }
61
62
63 static
64 VOID
65 NtfsWSubString(PWCHAR pTarget,
66 const PWCHAR pSource,
67 size_t pLength)
68 {
69 wcsncpy(pTarget, pSource, pLength);
70 pTarget[pLength] = L'\0';
71 }
72
73
74 PNTFS_FCB
75 NtfsCreateFCB(PCWSTR FileName,
76 PNTFS_VCB Vcb)
77 {
78 PNTFS_FCB Fcb;
79
80 ASSERT(Vcb);
81 ASSERT(Vcb->Identifier.Type == NTFS_TYPE_VCB);
82
83 Fcb = ExAllocatePoolWithTag(NonPagedPool, sizeof(NTFS_FCB), TAG_FCB);
84 RtlZeroMemory(Fcb, sizeof(NTFS_FCB));
85
86 Fcb->Identifier.Type = NTFS_TYPE_FCB;
87 Fcb->Identifier.Size = sizeof(NTFS_TYPE_FCB);
88
89 Fcb->Vcb = Vcb;
90
91 if (FileName)
92 {
93 wcscpy(Fcb->PathName, FileName);
94 if (wcsrchr(Fcb->PathName, '\\') != 0)
95 {
96 Fcb->ObjectName = wcsrchr(Fcb->PathName, '\\');
97 }
98 else
99 {
100 Fcb->ObjectName = Fcb->PathName;
101 }
102 }
103
104 ExInitializeResourceLite(&Fcb->MainResource);
105
106 Fcb->RFCB.Resource = &(Fcb->MainResource);
107
108 return Fcb;
109 }
110
111
112 VOID
113 NtfsDestroyFCB(PNTFS_FCB Fcb)
114 {
115 ASSERT(Fcb);
116 ASSERT(Fcb->Identifier.Type == NTFS_TYPE_FCB);
117
118 ExDeleteResourceLite(&Fcb->MainResource);
119
120 ExFreePool(Fcb);
121 }
122
123
124 BOOLEAN
125 NtfsFCBIsDirectory(PNTFS_FCB Fcb)
126 {
127 UNREFERENCED_PARAMETER(Fcb);
128 // return(Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY);
129 // return(Fcb->Entry.FileFlags & 0x02);
130 return TRUE;
131 }
132
133
134 BOOLEAN
135 NtfsFCBIsRoot(PNTFS_FCB Fcb)
136 {
137 return (wcscmp(Fcb->PathName, L"\\") == 0);
138 }
139
140
141 VOID
142 NtfsGrabFCB(PNTFS_VCB Vcb,
143 PNTFS_FCB Fcb)
144 {
145 KIRQL oldIrql;
146
147 DPRINT("grabbing FCB at %p: %S, refCount:%d\n",
148 Fcb,
149 Fcb->PathName,
150 Fcb->RefCount);
151
152 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
153 Fcb->RefCount++;
154 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
155 }
156
157
158 VOID
159 NtfsReleaseFCB(PNTFS_VCB Vcb,
160 PNTFS_FCB Fcb)
161 {
162 KIRQL oldIrql;
163
164 DPRINT("releasing FCB at %p: %S, refCount:%d\n",
165 Fcb,
166 Fcb->PathName,
167 Fcb->RefCount);
168
169 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
170 Fcb->RefCount--;
171 if (Fcb->RefCount <= 0 && !NtfsFCBIsDirectory(Fcb))
172 {
173 RemoveEntryList(&Fcb->FcbListEntry);
174 CcUninitializeCacheMap(Fcb->FileObject, NULL, NULL);
175 NtfsDestroyFCB(Fcb);
176 }
177
178 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
179 }
180
181
182 VOID
183 NtfsAddFCBToTable(PNTFS_VCB Vcb,
184 PNTFS_FCB Fcb)
185 {
186 KIRQL oldIrql;
187
188 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
189 Fcb->Vcb = Vcb;
190 InsertTailList(&Vcb->FcbListHead, &Fcb->FcbListEntry);
191 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
192 }
193
194
195 PNTFS_FCB
196 NtfsGrabFCBFromTable(PNTFS_VCB Vcb,
197 PCWSTR FileName)
198 {
199 KIRQL oldIrql;
200 PNTFS_FCB Fcb;
201 PLIST_ENTRY current_entry;
202
203 KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
204
205 if (FileName == NULL || *FileName == 0)
206 {
207 DPRINT("Return FCB for stream file object\n");
208 Fcb = Vcb->StreamFileObject->FsContext;
209 Fcb->RefCount++;
210 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
211 return Fcb;
212 }
213
214 current_entry = Vcb->FcbListHead.Flink;
215 while (current_entry != &Vcb->FcbListHead)
216 {
217 Fcb = CONTAINING_RECORD(current_entry, NTFS_FCB, FcbListEntry);
218
219 DPRINT("Comparing '%S' and '%S'\n", FileName, Fcb->PathName);
220 if (_wcsicmp(FileName, Fcb->PathName) == 0)
221 {
222 Fcb->RefCount++;
223 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
224 return Fcb;
225 }
226
227 //FIXME: need to compare against short name in FCB here
228
229 current_entry = current_entry->Flink;
230 }
231
232 KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
233
234 return NULL;
235 }
236
237
238 NTSTATUS
239 NtfsFCBInitializeCache(PNTFS_VCB Vcb,
240 PNTFS_FCB Fcb)
241 {
242 PFILE_OBJECT FileObject;
243 NTSTATUS Status;
244 PNTFS_CCB newCCB;
245
246 FileObject = IoCreateStreamFileObject(NULL, Vcb->StorageDevice);
247
248 newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(NTFS_CCB), TAG_CCB);
249 if (newCCB == NULL)
250 {
251 return STATUS_INSUFFICIENT_RESOURCES;
252 }
253
254 RtlZeroMemory(newCCB, sizeof(NTFS_CCB));
255
256 newCCB->Identifier.Type = NTFS_TYPE_CCB;
257 newCCB->Identifier.Size = sizeof(NTFS_TYPE_CCB);
258
259 FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
260 FileObject->FsContext = Fcb;
261 FileObject->FsContext2 = newCCB;
262 newCCB->PtrFileObject = FileObject;
263 Fcb->FileObject = FileObject;
264 Fcb->Vcb = Vcb;
265
266 Status = STATUS_SUCCESS;
267 CcInitializeCacheMap(FileObject,
268 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
269 FALSE,
270 &(NtfsGlobalData->CacheMgrCallbacks),
271 Fcb);
272
273 ObDereferenceObject(FileObject);
274 Fcb->Flags |= FCB_CACHE_INITIALIZED;
275
276 return Status;
277 }
278
279
280 PNTFS_FCB
281 NtfsMakeRootFCB(PNTFS_VCB Vcb)
282 {
283 PNTFS_FCB Fcb;
284
285 Fcb = NtfsCreateFCB(L"\\", Vcb);
286
287 // memset(Fcb->entry.Filename, ' ', 11);
288
289 // Fcb->Entry.DataLengthL = Vcb->CdInfo.RootSize;
290 // Fcb->Entry.ExtentLocationL = Vcb->CdInfo.RootStart;
291 // Fcb->Entry.FileFlags = 0x02; // FILE_ATTRIBUTE_DIRECTORY;
292 Fcb->RefCount = 1;
293 Fcb->DirIndex = 0;
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;
297
298 NtfsFCBInitializeCache(Vcb, Fcb);
299 NtfsAddFCBToTable(Vcb, Fcb);
300 NtfsGrabFCB(Vcb, Fcb);
301
302 return Fcb;
303 }
304
305
306 PNTFS_FCB
307 NtfsOpenRootFCB(PNTFS_VCB Vcb)
308 {
309 PNTFS_FCB Fcb;
310
311 Fcb = NtfsGrabFCBFromTable(Vcb, L"\\");
312 if (Fcb == NULL)
313 {
314 Fcb = NtfsMakeRootFCB(Vcb);
315 }
316
317 return Fcb;
318 }
319
320
321 #if 0
322 static VOID
323 NtfsGetDirEntryName(PDEVICE_EXTENSION DeviceExt,
324 PDIR_RECORD Record,
325 PWSTR Name)
326 /*
327 * FUNCTION: Retrieves the file name, be it in short or long file name format
328 */
329 {
330 if (Record->FileIdLength == 1 && Record->FileId[0] == 0)
331 {
332 wcscpy(Name, L".");
333 }
334 else if (Record->FileIdLength == 1 && Record->FileId[0] == 1)
335 {
336 wcscpy(Name, L"..");
337 }
338 else
339 {
340 if (DeviceExt->CdInfo.JolietLevel == 0)
341 {
342 ULONG i;
343
344 for (i = 0; i < Record->FileIdLength && Record->FileId[i] != ';'; i++)
345 Name[i] = (WCHAR)Record->FileId[i];
346 Name[i] = 0;
347 }
348 else
349 {
350 NtfsSwapString(Name, Record->FileId, Record->FileIdLength);
351 }
352 }
353
354 DPRINT("Name '%S'\n", Name);
355 }
356
357
358 NTSTATUS
359 NtfsMakeFCBFromDirEntry(PVCB Vcb,
360 PFCB DirectoryFCB,
361 PWSTR Name,
362 PDIR_RECORD Record,
363 PFCB * fileFCB)
364 {
365 WCHAR pathName[MAX_PATH];
366 PFCB rcFCB;
367 ULONG Size;
368
369 if (Name [0] != 0 && wcslen (DirectoryFCB->PathName) +
370 sizeof(WCHAR) + wcslen (Name) > MAX_PATH)
371 {
372 return(STATUS_OBJECT_NAME_INVALID);
373 }
374
375 wcscpy(pathName, DirectoryFCB->PathName);
376 if (!NtfsFCBIsRoot(DirectoryFCB))
377 {
378 wcscat(pathName, L"\\");
379 }
380
381 if (Name[0] != 0)
382 {
383 wcscat(pathName, Name);
384 }
385 else
386 {
387 WCHAR entryName[MAX_PATH];
388
389 NtfsGetDirEntryName(Vcb, Record, entryName);
390 wcscat(pathName, entryName);
391 }
392
393 rcFCB = NtfsCreateFCB(pathName, Vcb);
394 memcpy(&rcFCB->Entry, Record, sizeof(DIR_RECORD));
395
396 Size = rcFCB->Entry.DataLengthL;
397
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);
403 rcFCB->RefCount++;
404 NtfsAddFCBToTable(Vcb, rcFCB);
405 *fileFCB = rcFCB;
406
407 return(STATUS_SUCCESS);
408 }
409 #endif
410
411
412 NTSTATUS
413 NtfsAttachFCBToFileObject(PNTFS_VCB Vcb,
414 PNTFS_FCB Fcb,
415 PFILE_OBJECT FileObject)
416 {
417 PNTFS_CCB newCCB;
418
419 newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(NTFS_CCB), TAG_CCB);
420 if (newCCB == NULL)
421 {
422 return STATUS_INSUFFICIENT_RESOURCES;
423 }
424
425 RtlZeroMemory(newCCB, sizeof(NTFS_CCB));
426
427 newCCB->Identifier.Type = NTFS_TYPE_CCB;
428 newCCB->Identifier.Size = sizeof(NTFS_TYPE_CCB);
429
430 FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
431 FileObject->FsContext = Fcb;
432 FileObject->FsContext2 = newCCB;
433 newCCB->PtrFileObject = FileObject;
434 Fcb->Vcb = Vcb;
435
436 if (!(Fcb->Flags & FCB_CACHE_INITIALIZED))
437 {
438 CcInitializeCacheMap(FileObject,
439 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
440 FALSE,
441 NULL,
442 NULL);
443
444 Fcb->Flags |= FCB_CACHE_INITIALIZED;
445 }
446
447 //DPRINT("file open: fcb:%x file size: %d\n", Fcb, Fcb->Entry.DataLengthL);
448
449 return STATUS_SUCCESS;
450 }
451
452
453 static NTSTATUS
454 NtfsDirFindFile(PNTFS_VCB Vcb,
455 PNTFS_FCB DirectoryFcb,
456 PWSTR FileToFind,
457 PNTFS_FCB *FoundFCB)
458 {
459 #if 0
460 WCHAR TempName[2];
461 WCHAR Name[256];
462 PVOID Block;
463 ULONG FirstSector;
464 ULONG DirSize;
465 PDIR_RECORD Record;
466 ULONG Offset;
467 ULONG BlockOffset;
468 NTSTATUS Status;
469
470 LARGE_INTEGER StreamOffset;
471 PVOID Context;
472
473 ASSERT(DeviceExt);
474 ASSERT(DirectoryFcb);
475 ASSERT(FileToFind);
476
477 DPRINT("NtfsDirFindFile(VCB:%08x, dirFCB:%08x, File:%S)\n",
478 DeviceExt,
479 DirectoryFcb,
480 FileToFind);
481 DPRINT("Dir Path:%S\n", DirectoryFcb->PathName);
482
483 /* default to '.' if no filename specified */
484 if (wcslen(FileToFind) == 0)
485 {
486 TempName[0] = L'.';
487 TempName[1] = 0;
488 FileToFind = TempName;
489 }
490
491 DirSize = DirectoryFcb->Entry.DataLengthL;
492 StreamOffset.QuadPart = (LONGLONG)DirectoryFcb->Entry.ExtentLocationL * (LONGLONG)BLOCKSIZE;
493
494 if(!CcMapData(DeviceExt->StreamFileObject, &StreamOffset,
495 BLOCKSIZE, TRUE, &Context, &Block))
496 {
497 DPRINT("CcMapData() failed\n");
498 return(STATUS_UNSUCCESSFUL);
499 }
500
501 Offset = 0;
502 BlockOffset = 0;
503 Record = (PDIR_RECORD)Block;
504 while(TRUE)
505 {
506 if (Record->RecordLength == 0)
507 {
508 DPRINT("RecordLength == 0 Stopped!\n");
509 break;
510 }
511
512 DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n",
513 Record->RecordLength, Record->ExtAttrRecordLength, Record->FileIdLength);
514
515 NtfsGetDirEntryName(DeviceExt, Record, Name);
516 DPRINT("Name '%S'\n", Name);
517
518 if (wstrcmpjoki(Name, FileToFind))
519 {
520 DPRINT("Match found, %S\n", Name);
521 Status = NtfsMakeFCBFromDirEntry(DeviceExt,
522 DirectoryFcb,
523 Name,
524 Record,
525 FoundFCB);
526
527 CcUnpinData(Context);
528
529 return(Status);
530 }
531
532 Offset += Record->RecordLength;
533 BlockOffset += Record->RecordLength;
534 Record = (PDIR_RECORD)(Block + BlockOffset);
535 if (BlockOffset >= BLOCKSIZE || Record->RecordLength == 0)
536 {
537 DPRINT("Map next sector\n");
538 CcUnpinData(Context);
539 StreamOffset.QuadPart += BLOCKSIZE;
540 Offset = ROUND_UP(Offset, BLOCKSIZE);
541 BlockOffset = 0;
542
543 if (!CcMapData(DeviceExt->StreamFileObject,
544 &StreamOffset,
545 BLOCKSIZE, TRUE,
546 &Context, &Block))
547 {
548 DPRINT("CcMapData() failed\n");
549 return(STATUS_UNSUCCESSFUL);
550 }
551 Record = (PDIR_RECORD)(Block + BlockOffset);
552 }
553
554 if (Offset >= DirSize)
555 break;
556 }
557
558 CcUnpinData(Context);
559 #else
560 UNREFERENCED_PARAMETER(Vcb);
561 UNREFERENCED_PARAMETER(DirectoryFcb);
562 UNREFERENCED_PARAMETER(FileToFind);
563 UNREFERENCED_PARAMETER(FoundFCB);
564 #endif
565 return STATUS_OBJECT_NAME_NOT_FOUND;
566 }
567
568
569 NTSTATUS
570 NtfsGetFCBForFile(PNTFS_VCB Vcb,
571 PNTFS_FCB *pParentFCB,
572 PNTFS_FCB *pFCB,
573 const PWSTR pFileName)
574 {
575 NTSTATUS Status;
576 WCHAR pathName [MAX_PATH];
577 WCHAR elementName [MAX_PATH];
578 PWCHAR currentElement;
579 PNTFS_FCB FCB;
580 PNTFS_FCB parentFCB;
581
582 DPRINT("NtfsGetFCBForFile(%p, %p, %p, '%S')\n",
583 Vcb,
584 pParentFCB,
585 pFCB,
586 pFileName);
587
588 /* Dummy code */
589 // FCB = NtfsOpenRootFCB(Vcb);
590 // *pFCB = FCB;
591 // *pParentFCB = NULL;
592
593 #if 1
594 /* Trivial case, open of the root directory on volume */
595 if (pFileName [0] == L'\0' || wcscmp(pFileName, L"\\") == 0)
596 {
597 DPRINT("returning root FCB\n");
598
599 FCB = NtfsOpenRootFCB(Vcb);
600 *pFCB = FCB;
601 *pParentFCB = NULL;
602
603 return (FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND;
604 }
605 else
606 {
607 currentElement = pFileName + 1;
608 wcscpy (pathName, L"\\");
609 FCB = NtfsOpenRootFCB (Vcb);
610 }
611
612 parentFCB = NULL;
613
614 /* Parse filename and check each path element for existance and access */
615 while (NtfsGetNextPathElement(currentElement) != 0)
616 {
617 /* Skip blank directory levels */
618 if ((NtfsGetNextPathElement(currentElement) - currentElement) == 0)
619 {
620 currentElement++;
621 continue;
622 }
623
624 DPRINT("Parsing, currentElement:%S\n", currentElement);
625 DPRINT(" parentFCB:%p FCB:%p\n", parentFCB, FCB);
626
627 /* Descend to next directory level */
628 if (parentFCB)
629 {
630 NtfsReleaseFCB(Vcb, parentFCB);
631 parentFCB = NULL;
632 }
633
634 /* fail if element in FCB is not a directory */
635 if (!NtfsFCBIsDirectory(FCB))
636 {
637 DPRINT("Element in requested path is not a directory\n");
638
639 NtfsReleaseFCB(Vcb, FCB);
640 FCB = 0;
641 *pParentFCB = NULL;
642 *pFCB = NULL;
643
644 return STATUS_OBJECT_PATH_NOT_FOUND;
645 }
646
647 parentFCB = FCB;
648
649 /* Extract next directory level into dirName */
650 NtfsWSubString(pathName,
651 pFileName,
652 NtfsGetNextPathElement(currentElement) - pFileName);
653 DPRINT(" pathName:%S\n", pathName);
654
655 FCB = NtfsGrabFCBFromTable(Vcb, pathName);
656 if (FCB == NULL)
657 {
658 NtfsWSubString(elementName,
659 currentElement,
660 NtfsGetNextPathElement(currentElement) - currentElement);
661 DPRINT(" elementName:%S\n", elementName);
662
663 Status = NtfsDirFindFile(Vcb, parentFCB, elementName, &FCB);
664 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
665 {
666 *pParentFCB = parentFCB;
667 *pFCB = NULL;
668 currentElement = NtfsGetNextPathElement(currentElement);
669 if (*currentElement == L'\0' || NtfsGetNextPathElement(currentElement + 1) == 0)
670 {
671 return STATUS_OBJECT_NAME_NOT_FOUND;
672 }
673 else
674 {
675 return STATUS_OBJECT_PATH_NOT_FOUND;
676 }
677 }
678 else if (!NT_SUCCESS(Status))
679 {
680 NtfsReleaseFCB(Vcb, parentFCB);
681 *pParentFCB = NULL;
682 *pFCB = NULL;
683
684 return Status;
685 }
686 }
687
688 currentElement = NtfsGetNextPathElement(currentElement);
689 }
690
691 *pParentFCB = parentFCB;
692 *pFCB = FCB;
693 #endif
694
695 return STATUS_SUCCESS;
696 }
697
698 /* EOF */