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