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