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