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