branch update for HEAD-2003050101
[reactos.git] / 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 <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(Fcb->FileObject);
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 = Vcb->StreamFileObject->FsContext;
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 = Fcb;
240   FileObject->FsContext2 = newCCB;
241   newCCB->PtrFileObject = FileObject;
242   Fcb->FileObject = FileObject;
243   Fcb->DevExt = Vcb;
244
245   Status = CcRosInitializeFileCache(FileObject,
246                                     CACHEPAGESIZE(Vcb));
247   if (!NT_SUCCESS(Status))
248     {
249       DbgPrint("CcRosInitializeFileCache failed\n");
250       KeBugCheck(0);
251     }
252
253   ObDereferenceObject(FileObject);
254   Fcb->Flags |= FCB_CACHE_INITIALIZED;
255
256   return(Status);
257 }
258
259
260 PFCB
261 NtfsMakeRootFCB(PDEVICE_EXTENSION Vcb)
262 {
263   PFCB Fcb;
264
265   Fcb = NtfsCreateFCB(L"\\");
266
267 //  memset(Fcb->entry.Filename, ' ', 11);
268
269 //  Fcb->Entry.DataLengthL = Vcb->CdInfo.RootSize;
270 //  Fcb->Entry.ExtentLocationL = Vcb->CdInfo.RootStart;
271 //  Fcb->Entry.FileFlags = 0x02; // FILE_ATTRIBUTE_DIRECTORY;
272   Fcb->RefCount = 1;
273   Fcb->DirIndex = 0;
274   Fcb->RFCB.FileSize.QuadPart = PAGE_SIZE;//Vcb->CdInfo.RootSize;
275   Fcb->RFCB.ValidDataLength.QuadPart = PAGE_SIZE;//Vcb->CdInfo.RootSize;
276   Fcb->RFCB.AllocationSize.QuadPart = PAGE_SIZE;//Vcb->CdInfo.RootSize;
277
278   NtfsFCBInitializeCache(Vcb, Fcb);
279   NtfsAddFCBToTable(Vcb, Fcb);
280   NtfsGrabFCB(Vcb, Fcb);
281
282   return(Fcb);
283 }
284
285
286 PFCB
287 NtfsOpenRootFCB(PDEVICE_EXTENSION Vcb)
288 {
289   PFCB Fcb;
290
291   Fcb = NtfsGrabFCBFromTable(Vcb, L"\\");
292   if (Fcb == NULL)
293     {
294       Fcb = NtfsMakeRootFCB(Vcb);
295     }
296
297   return(Fcb);
298 }
299
300
301 #if 0
302 static VOID
303 NtfsGetDirEntryName(PDEVICE_EXTENSION DeviceExt,
304                     PDIR_RECORD Record,
305                     PWSTR Name)
306 /*
307  * FUNCTION: Retrieves the file name, be it in short or long file name format
308  */
309 {
310   if (Record->FileIdLength == 1 && Record->FileId[0] == 0)
311     {
312       wcscpy(Name, L".");
313     }
314   else if (Record->FileIdLength == 1 && Record->FileId[0] == 1)
315     {
316       wcscpy(Name, L"..");
317     }
318   else
319     {
320       if (DeviceExt->CdInfo.JolietLevel == 0)
321         {
322           ULONG i;
323
324           for (i = 0; i < Record->FileIdLength && Record->FileId[i] != ';'; i++)
325             Name[i] = (WCHAR)Record->FileId[i];
326           Name[i] = 0;
327         }
328       else
329         {
330           CdfsSwapString(Name, Record->FileId, Record->FileIdLength);
331         }
332     }
333
334   DPRINT("Name '%S'\n", Name);
335 }
336
337
338 NTSTATUS
339 NtfsMakeFCBFromDirEntry(PVCB Vcb,
340                         PFCB DirectoryFCB,
341                         PWSTR Name,
342                         PDIR_RECORD Record,
343                         PFCB * fileFCB)
344 {
345   WCHAR pathName[MAX_PATH];
346   PFCB rcFCB;
347   ULONG Size;
348
349   if (Name [0] != 0 && wcslen (DirectoryFCB->PathName) +
350         sizeof(WCHAR) + wcslen (Name) > MAX_PATH)
351     {
352       return(STATUS_OBJECT_NAME_INVALID);
353     }
354
355   wcscpy(pathName, DirectoryFCB->PathName);
356   if (!CdfsFCBIsRoot(DirectoryFCB))
357     {
358       wcscat(pathName, L"\\");
359     }
360
361   if (Name[0] != 0)
362     {
363       wcscat(pathName, Name);
364     }
365   else
366     {
367       WCHAR entryName[MAX_PATH];
368
369       CdfsGetDirEntryName(Vcb, Record, entryName);
370       wcscat(pathName, entryName);
371     }
372
373   rcFCB = CdfsCreateFCB(pathName);
374   memcpy(&rcFCB->Entry, Record, sizeof(DIR_RECORD));
375
376   Size = rcFCB->Entry.DataLengthL;
377
378   rcFCB->RFCB.FileSize.QuadPart = Size;
379   rcFCB->RFCB.ValidDataLength.QuadPart = Size;
380   rcFCB->RFCB.AllocationSize.QuadPart = ROUND_UP(Size, BLOCKSIZE);
381 //  DPRINT1("%S %d %d\n", longName, Size, (ULONG)rcFCB->RFCB.AllocationSize.QuadPart);
382   CdfsFCBInitializeCache(Vcb, rcFCB);
383   rcFCB->RefCount++;
384   CdfsAddFCBToTable(Vcb, rcFCB);
385   *fileFCB = rcFCB;
386
387   return(STATUS_SUCCESS);
388 }
389 #endif
390
391
392 NTSTATUS
393 NtfsAttachFCBToFileObject(PDEVICE_EXTENSION Vcb,
394                           PFCB Fcb,
395                           PFILE_OBJECT FileObject)
396 {
397   NTSTATUS Status;
398   PCCB  newCCB;
399
400   newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(CCB), TAG_CCB);
401   if (newCCB == NULL)
402     {
403       return(STATUS_INSUFFICIENT_RESOURCES);
404     }
405   memset(newCCB, 0, sizeof(CCB));
406
407   FileObject->Flags = FileObject->Flags | FO_FCB_IS_VALID |
408       FO_DIRECT_CACHE_PAGING_READ;
409   FileObject->SectionObjectPointers = &Fcb->SectionObjectPointers;
410   FileObject->FsContext = Fcb;
411   FileObject->FsContext2 = newCCB;
412   newCCB->PtrFileObject = FileObject;
413   Fcb->DevExt = Vcb;
414
415   if (!(Fcb->Flags & FCB_CACHE_INITIALIZED))
416     {
417       Status = CcRosInitializeFileCache(FileObject,
418                                         CACHEPAGESIZE(Vcb));
419       if (!NT_SUCCESS(Status))
420         {
421           DbgPrint("CcRosInitializeFileCache failed\n");
422           KeBugCheck(0);
423         }
424       Fcb->Flags |= FCB_CACHE_INITIALIZED;
425     }
426
427   DPRINT("file open: fcb:%x file size: %d\n", Fcb, Fcb->Entry.DataLengthL);
428
429   return(STATUS_SUCCESS);
430 }
431
432
433 #if 0
434 NTSTATUS
435 NtfsDirFindFile(PDEVICE_EXTENSION DeviceExt,
436                 PFCB DirectoryFcb,
437                 PWSTR FileToFind,
438                 PFCB *FoundFCB)
439 {
440   WCHAR TempName[2];
441   WCHAR Name[256];
442   PVOID Block;
443   ULONG FirstSector;
444   ULONG DirSize;
445   PDIR_RECORD Record;
446   ULONG Offset;
447   ULONG BlockOffset;
448   NTSTATUS Status;
449
450   LARGE_INTEGER StreamOffset;
451   PVOID Context;
452
453   assert(DeviceExt);
454   assert(DirectoryFcb);
455   assert(FileToFind);
456
457   DPRINT("CdfsDirFindFile(VCB:%08x, dirFCB:%08x, File:%S)\n",
458          DeviceExt,
459          DirectoryFcb,
460          FileToFind);
461   DPRINT("Dir Path:%S\n", DirectoryFcb->PathName);
462
463   /*  default to '.' if no filename specified */
464   if (wcslen(FileToFind) == 0)
465     {
466       TempName[0] = L'.';
467       TempName[1] = 0;
468       FileToFind = TempName;
469     }
470
471   DirSize = DirectoryFcb->Entry.DataLengthL;
472   StreamOffset.QuadPart = (LONGLONG)DirectoryFcb->Entry.ExtentLocationL * (LONGLONG)BLOCKSIZE;
473
474   if(!CcMapData(DeviceExt->StreamFileObject, &StreamOffset,
475                 BLOCKSIZE, TRUE, &Context, &Block))
476   {
477     DPRINT("CcMapData() failed\n");
478     return(STATUS_UNSUCCESSFUL);
479   }
480
481   Offset = 0;
482   BlockOffset = 0;
483   Record = (PDIR_RECORD)Block;
484   while(TRUE)
485     {
486       if (Record->RecordLength == 0)
487         {
488           DPRINT("RecordLength == 0  Stopped!\n");
489           break;
490         }
491         
492       DPRINT("RecordLength %u  ExtAttrRecordLength %u  NameLength %u\n",
493              Record->RecordLength, Record->ExtAttrRecordLength, Record->FileIdLength);
494
495       CdfsGetDirEntryName(DeviceExt, Record, Name);
496       DPRINT("Name '%S'\n", Name);
497
498       if (wstrcmpjoki(Name, FileToFind))
499         {
500           DPRINT("Match found, %S\n", Name);
501           Status = CdfsMakeFCBFromDirEntry(DeviceExt,
502                                            DirectoryFcb,
503                                            Name,
504                                            Record,
505                                            FoundFCB);
506
507           CcUnpinData(Context);
508
509           return(Status);
510         }
511
512       Offset += Record->RecordLength;
513       BlockOffset += Record->RecordLength;
514       Record = (PDIR_RECORD)(Block + BlockOffset);
515       if (BlockOffset >= BLOCKSIZE || Record->RecordLength == 0)
516         {
517           DPRINT("Map next sector\n");
518           CcUnpinData(Context);
519           StreamOffset.QuadPart += BLOCKSIZE;
520           Offset = ROUND_UP(Offset, BLOCKSIZE);
521           BlockOffset = 0;
522
523           if (!CcMapData(DeviceExt->StreamFileObject,
524                          &StreamOffset,
525                          BLOCKSIZE, TRUE,
526                          &Context, &Block))
527             {
528               DPRINT("CcMapData() failed\n");
529               return(STATUS_UNSUCCESSFUL);
530             }
531           Record = (PDIR_RECORD)(Block + BlockOffset);
532         }
533
534       if (Offset >= DirSize)
535         break;
536     }
537
538   CcUnpinData(Context);
539
540   return(STATUS_OBJECT_NAME_NOT_FOUND);
541 }
542 #endif
543
544 NTSTATUS
545 NtfsGetFCBForFile(PDEVICE_EXTENSION Vcb,
546                   PFCB *pParentFCB,
547                   PFCB *pFCB,
548                   const PWSTR pFileName)
549 {
550   NTSTATUS Status;
551   WCHAR  pathName [MAX_PATH];
552   WCHAR  elementName [MAX_PATH];
553   PWCHAR  currentElement;
554   PFCB  FCB;
555   PFCB  parentFCB;
556
557   DPRINT("NtfsGetFCBForFile(%x, %x, %x, '%S')\n",
558          Vcb,
559          pParentFCB,
560          pFCB,
561          pFileName);
562
563   /* Dummy code */
564   FCB = NtfsOpenRootFCB(Vcb);
565   *pFCB = FCB;
566   *pParentFCB = NULL;
567
568 #if 0
569   /* Trivial case, open of the root directory on volume */
570   if (pFileName [0] == L'\0' || wcscmp(pFileName, L"\\") == 0)
571     {
572       DPRINT("returning root FCB\n");
573
574       FCB = NtfsOpenRootFCB(Vcb);
575       *pFCB = FCB;
576       *pParentFCB = NULL;
577
578       return((FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND);
579     }
580   else
581     {
582       currentElement = pFileName + 1;
583       wcscpy (pathName, L"\\");
584       FCB = CdfsOpenRootFCB (Vcb);
585     }
586   parentFCB = NULL;
587
588   /* Parse filename and check each path element for existance and access */
589   while (CdfsGetNextPathElement(currentElement) != 0)
590     {
591       /*  Skip blank directory levels */
592       if ((CdfsGetNextPathElement(currentElement) - currentElement) == 0)
593         {
594           currentElement++;
595           continue;
596         }
597
598       DPRINT("Parsing, currentElement:%S\n", currentElement);
599       DPRINT("  parentFCB:%x FCB:%x\n", parentFCB, FCB);
600
601       /* Descend to next directory level */
602       if (parentFCB)
603         {
604           CdfsReleaseFCB(Vcb, parentFCB);
605           parentFCB = NULL;
606         }
607
608       /* fail if element in FCB is not a directory */
609       if (!CdfsFCBIsDirectory(FCB))
610         {
611           DPRINT("Element in requested path is not a directory\n");
612
613           CdfsReleaseFCB(Vcb, FCB);
614           FCB = 0;
615           *pParentFCB = NULL;
616           *pFCB = NULL;
617
618           return(STATUS_OBJECT_PATH_NOT_FOUND);
619         }
620       parentFCB = FCB;
621
622       /* Extract next directory level into dirName */
623       CdfsWSubString(pathName,
624                      pFileName,
625                      CdfsGetNextPathElement(currentElement) - pFileName);
626       DPRINT("  pathName:%S\n", pathName);
627
628       FCB = CdfsGrabFCBFromTable(Vcb, pathName);
629       if (FCB == NULL)
630         {
631           CdfsWSubString(elementName,
632                          currentElement,
633                          CdfsGetNextPathElement(currentElement) - currentElement);
634           DPRINT("  elementName:%S\n", elementName);
635
636           Status = CdfsDirFindFile(Vcb, parentFCB, elementName, &FCB);
637           if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
638             {
639               *pParentFCB = parentFCB;
640               *pFCB = NULL;
641               currentElement = CdfsGetNextPathElement(currentElement);
642               if (*currentElement == L'\0' || CdfsGetNextPathElement(currentElement + 1) == 0)
643                 {
644                   return(STATUS_OBJECT_NAME_NOT_FOUND);
645                 }
646               else
647                 {
648                   return(STATUS_OBJECT_PATH_NOT_FOUND);
649                 }
650             }
651           else if (!NT_SUCCESS(Status))
652             {
653               CdfsReleaseFCB(Vcb, parentFCB);
654               *pParentFCB = NULL;
655               *pFCB = NULL;
656
657               return(Status);
658             }
659         }
660       currentElement = CdfsGetNextPathElement(currentElement);
661     }
662
663   *pParentFCB = parentFCB;
664   *pFCB = FCB;
665 #endif
666
667   return(STATUS_SUCCESS);
668 }
669
670 /* EOF */