update for HEAD-2003091401
[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 <ntos.h>
36
37 #include "ntfs.h"
38
39
40 /* MACROS *******************************************************************/
41
42 #define TAG_FCB TAG('I', 'F', 'C', 'B')
43
44
45
46 /* FUNCTIONS ****************************************************************/
47
48 static PWCHAR
49 NtfsGetNextPathElement(PWCHAR FileName)
50 {
51   if (*FileName == L'\0')
52     {
53       return(NULL);
54     }
55
56   while (*FileName != L'\0' && *FileName != L'\\')
57     {
58       FileName++;
59     }
60
61   return(FileName);
62 }
63
64
65 static VOID
66 NtfsWSubString(PWCHAR pTarget, const PWCHAR pSource, size_t pLength)
67 {
68   wcsncpy (pTarget, pSource, pLength);
69   pTarget [pLength] = L'\0';
70 }
71
72
73 PFCB
74 NtfsCreateFCB(PWSTR FileName)
75 {
76   PFCB Fcb;
77
78   Fcb = ExAllocatePoolWithTag(NonPagedPool, sizeof(FCB), TAG_FCB);
79   RtlZeroMemory(Fcb, sizeof(FCB));
80
81   if (FileName)
82     {
83       wcscpy(Fcb->PathName, FileName);
84       if (wcsrchr(Fcb->PathName, '\\') != 0)
85         {
86           Fcb->ObjectName = wcsrchr(Fcb->PathName, '\\');
87         }
88       else
89         {
90           Fcb->ObjectName = Fcb->PathName;
91         }
92     }
93
94   ExInitializeResourceLite(&Fcb->MainResource);
95
96   return(Fcb);
97 }
98
99
100 VOID
101 NtfsDestroyFCB(PFCB Fcb)
102 {
103   ExDeleteResourceLite(&Fcb->MainResource);
104
105   ExFreePool(Fcb);
106 }
107
108
109 BOOLEAN
110 NtfsFCBIsDirectory(PFCB Fcb)
111 {
112 //  return(Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY);
113 //  return(Fcb->Entry.FileFlags & 0x02);
114   return(TRUE);
115 }
116
117
118 BOOLEAN
119 NtfsFCBIsRoot(PFCB Fcb)
120 {
121   return(wcscmp(Fcb->PathName, L"\\") == 0);
122 }
123
124
125 VOID
126 NtfsGrabFCB(PDEVICE_EXTENSION Vcb,
127             PFCB Fcb)
128 {
129   KIRQL  oldIrql;
130
131   DPRINT("grabbing FCB at %x: %S, refCount:%d\n",
132          Fcb,
133          Fcb->PathName,
134          Fcb->RefCount);
135
136   KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
137   Fcb->RefCount++;
138   KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
139 }
140
141
142 VOID
143 NtfsReleaseFCB(PDEVICE_EXTENSION Vcb,
144                PFCB Fcb)
145 {
146   KIRQL  oldIrql;
147
148   DPRINT("releasing FCB at %x: %S, refCount:%d\n",
149          Fcb,
150          Fcb->PathName,
151          Fcb->RefCount);
152
153   KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
154   Fcb->RefCount--;
155   if (Fcb->RefCount <= 0 && !NtfsFCBIsDirectory(Fcb))
156     {
157       RemoveEntryList(&Fcb->FcbListEntry);
158       CcRosReleaseFileCache(Fcb->FileObject);
159       NtfsDestroyFCB(Fcb);
160     }
161   KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
162 }
163
164
165 VOID
166 NtfsAddFCBToTable(PDEVICE_EXTENSION Vcb,
167                   PFCB Fcb)
168 {
169   KIRQL  oldIrql;
170
171   KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
172   Fcb->DevExt = Vcb;
173   InsertTailList(&Vcb->FcbListHead, &Fcb->FcbListEntry);
174   KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
175 }
176
177
178 PFCB
179 NtfsGrabFCBFromTable(PDEVICE_EXTENSION Vcb,
180                      PWSTR FileName)
181 {
182   KIRQL  oldIrql;
183   PFCB Fcb;
184   PLIST_ENTRY  current_entry;
185
186   KeAcquireSpinLock(&Vcb->FcbListLock, &oldIrql);
187
188   if (FileName == NULL || *FileName == 0)
189     {
190       DPRINT("Return FCB for stream file object\n");
191       Fcb = Vcb->StreamFileObject->FsContext;
192       Fcb->RefCount++;
193       KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
194       return(Fcb);
195     }
196
197   current_entry = Vcb->FcbListHead.Flink;
198   while (current_entry != &Vcb->FcbListHead)
199     {
200       Fcb = CONTAINING_RECORD(current_entry, FCB, FcbListEntry);
201
202       DPRINT("Comparing '%S' and '%S'\n", FileName, Fcb->PathName);
203       if (_wcsicmp(FileName, Fcb->PathName) == 0)
204         {
205           Fcb->RefCount++;
206           KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
207           return(Fcb);
208         }
209
210       //FIXME: need to compare against short name in FCB here
211
212       current_entry = current_entry->Flink;
213     }
214   KeReleaseSpinLock(&Vcb->FcbListLock, oldIrql);
215
216   return(NULL);
217 }
218
219
220 NTSTATUS
221 NtfsFCBInitializeCache(PVCB Vcb,
222                        PFCB Fcb)
223 {
224   PFILE_OBJECT FileObject;
225   NTSTATUS Status;
226   PCCB  newCCB;
227
228   FileObject = IoCreateStreamFileObject(NULL, Vcb->StorageDevice);
229
230   newCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(CCB), TAG_CCB);
231   if (newCCB == NULL)
232     {
233       return(STATUS_INSUFFICIENT_RESOURCES);
234     }
235   RtlZeroMemory(newCCB,
236                 sizeof(CCB));
237
238   FileObject->Flags = FileObject->Flags | FO_FCB_IS_VALID |
239       FO_DIRECT_CACHE_PAGING_READ;
240   FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
241   FileObject->FsContext = Fcb;
242   FileObject->FsContext2 = newCCB;
243   newCCB->PtrFileObject = FileObject;
244   Fcb->FileObject = FileObject;
245   Fcb->DevExt = Vcb;
246
247   Status = CcRosInitializeFileCache(FileObject,
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 = PAGE_SIZE;//Vcb->CdInfo.RootSize;
277   Fcb->RFCB.ValidDataLength.QuadPart = PAGE_SIZE;//Vcb->CdInfo.RootSize;
278   Fcb->RFCB.AllocationSize.QuadPart = PAGE_SIZE;//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           NtfsSwapString(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 (!NtfsFCBIsRoot(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       NtfsGetDirEntryName(Vcb, Record, entryName);
372       wcscat(pathName, entryName);
373     }
374
375   rcFCB = NtfsCreateFCB(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   NtfsFCBInitializeCache(Vcb, rcFCB);
385   rcFCB->RefCount++;
386   NtfsAddFCBToTable(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->SectionObjectPointer = &Fcb->SectionObjectPointers;
412   FileObject->FsContext = Fcb;
413   FileObject->FsContext2 = newCCB;
414   newCCB->PtrFileObject = FileObject;
415   Fcb->DevExt = Vcb;
416
417   if (!(Fcb->Flags & FCB_CACHE_INITIALIZED))
418     {
419       Status = CcRosInitializeFileCache(FileObject,
420                                         CACHEPAGESIZE(Vcb));
421       if (!NT_SUCCESS(Status))
422         {
423           DbgPrint("CcRosInitializeFileCache failed\n");
424           KEBUGCHECK(0);
425         }
426       Fcb->Flags |= FCB_CACHE_INITIALIZED;
427     }
428
429   //DPRINT("file open: fcb:%x file size: %d\n", Fcb, Fcb->Entry.DataLengthL);
430
431   return(STATUS_SUCCESS);
432 }
433
434
435 NTSTATUS
436 NtfsDirFindFile(PDEVICE_EXTENSION DeviceExt,
437                 PFCB DirectoryFcb,
438                 PWSTR FileToFind,
439                 PFCB *FoundFCB)
440 {
441 #if 0
442   WCHAR TempName[2];
443   WCHAR Name[256];
444   PVOID Block;
445   ULONG FirstSector;
446   ULONG DirSize;
447   PDIR_RECORD Record;
448   ULONG Offset;
449   ULONG BlockOffset;
450   NTSTATUS Status;
451
452   LARGE_INTEGER StreamOffset;
453   PVOID Context;
454
455   assert(DeviceExt);
456   assert(DirectoryFcb);
457   assert(FileToFind);
458
459   DPRINT("NtfsDirFindFile(VCB:%08x, dirFCB:%08x, File:%S)\n",
460          DeviceExt,
461          DirectoryFcb,
462          FileToFind);
463   DPRINT("Dir Path:%S\n", DirectoryFcb->PathName);
464
465   /*  default to '.' if no filename specified */
466   if (wcslen(FileToFind) == 0)
467     {
468       TempName[0] = L'.';
469       TempName[1] = 0;
470       FileToFind = TempName;
471     }
472
473   DirSize = DirectoryFcb->Entry.DataLengthL;
474   StreamOffset.QuadPart = (LONGLONG)DirectoryFcb->Entry.ExtentLocationL * (LONGLONG)BLOCKSIZE;
475
476   if(!CcMapData(DeviceExt->StreamFileObject, &StreamOffset,
477                 BLOCKSIZE, TRUE, &Context, &Block))
478   {
479     DPRINT("CcMapData() failed\n");
480     return(STATUS_UNSUCCESSFUL);
481   }
482
483   Offset = 0;
484   BlockOffset = 0;
485   Record = (PDIR_RECORD)Block;
486   while(TRUE)
487     {
488       if (Record->RecordLength == 0)
489         {
490           DPRINT("RecordLength == 0  Stopped!\n");
491           break;
492         }
493         
494       DPRINT("RecordLength %u  ExtAttrRecordLength %u  NameLength %u\n",
495              Record->RecordLength, Record->ExtAttrRecordLength, Record->FileIdLength);
496
497       NtfsGetDirEntryName(DeviceExt, Record, Name);
498       DPRINT("Name '%S'\n", Name);
499
500       if (wstrcmpjoki(Name, FileToFind))
501         {
502           DPRINT("Match found, %S\n", Name);
503           Status = NtfsMakeFCBFromDirEntry(DeviceExt,
504                                            DirectoryFcb,
505                                            Name,
506                                            Record,
507                                            FoundFCB);
508
509           CcUnpinData(Context);
510
511           return(Status);
512         }
513
514       Offset += Record->RecordLength;
515       BlockOffset += Record->RecordLength;
516       Record = (PDIR_RECORD)(Block + BlockOffset);
517       if (BlockOffset >= BLOCKSIZE || Record->RecordLength == 0)
518         {
519           DPRINT("Map next sector\n");
520           CcUnpinData(Context);
521           StreamOffset.QuadPart += BLOCKSIZE;
522           Offset = ROUND_UP(Offset, BLOCKSIZE);
523           BlockOffset = 0;
524
525           if (!CcMapData(DeviceExt->StreamFileObject,
526                          &StreamOffset,
527                          BLOCKSIZE, TRUE,
528                          &Context, &Block))
529             {
530               DPRINT("CcMapData() failed\n");
531               return(STATUS_UNSUCCESSFUL);
532             }
533           Record = (PDIR_RECORD)(Block + BlockOffset);
534         }
535
536       if (Offset >= DirSize)
537         break;
538     }
539
540   CcUnpinData(Context);
541 #endif
542   return(STATUS_OBJECT_NAME_NOT_FOUND);
543 }
544
545
546 NTSTATUS
547 NtfsGetFCBForFile(PDEVICE_EXTENSION Vcb,
548                   PFCB *pParentFCB,
549                   PFCB *pFCB,
550                   const PWSTR pFileName)
551 {
552   NTSTATUS Status;
553   WCHAR  pathName [MAX_PATH];
554   WCHAR  elementName [MAX_PATH];
555   PWCHAR  currentElement;
556   PFCB  FCB;
557   PFCB  parentFCB;
558
559   DPRINT("NtfsGetFCBForFile(%x, %x, %x, '%S')\n",
560          Vcb,
561          pParentFCB,
562          pFCB,
563          pFileName);
564
565   /* Dummy code */
566 //  FCB = NtfsOpenRootFCB(Vcb);
567 //  *pFCB = FCB;
568 //  *pParentFCB = NULL;
569
570 #if 1
571   /* Trivial case, open of the root directory on volume */
572   if (pFileName [0] == L'\0' || wcscmp(pFileName, L"\\") == 0)
573     {
574       DPRINT("returning root FCB\n");
575
576       FCB = NtfsOpenRootFCB(Vcb);
577       *pFCB = FCB;
578       *pParentFCB = NULL;
579
580       return((FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND);
581     }
582   else
583     {
584       currentElement = pFileName + 1;
585       wcscpy (pathName, L"\\");
586       FCB = NtfsOpenRootFCB (Vcb);
587     }
588   parentFCB = NULL;
589
590   /* Parse filename and check each path element for existance and access */
591   while (NtfsGetNextPathElement(currentElement) != 0)
592     {
593       /*  Skip blank directory levels */
594       if ((NtfsGetNextPathElement(currentElement) - currentElement) == 0)
595         {
596           currentElement++;
597           continue;
598         }
599
600       DPRINT("Parsing, currentElement:%S\n", currentElement);
601       DPRINT("  parentFCB:%x FCB:%x\n", parentFCB, FCB);
602
603       /* Descend to next directory level */
604       if (parentFCB)
605         {
606           NtfsReleaseFCB(Vcb, parentFCB);
607           parentFCB = NULL;
608         }
609
610       /* fail if element in FCB is not a directory */
611       if (!NtfsFCBIsDirectory(FCB))
612         {
613           DPRINT("Element in requested path is not a directory\n");
614
615           NtfsReleaseFCB(Vcb, FCB);
616           FCB = 0;
617           *pParentFCB = NULL;
618           *pFCB = NULL;
619
620           return(STATUS_OBJECT_PATH_NOT_FOUND);
621         }
622       parentFCB = FCB;
623
624       /* Extract next directory level into dirName */
625       NtfsWSubString(pathName,
626                      pFileName,
627                      NtfsGetNextPathElement(currentElement) - pFileName);
628       DPRINT("  pathName:%S\n", pathName);
629
630       FCB = NtfsGrabFCBFromTable(Vcb, pathName);
631       if (FCB == NULL)
632         {
633           NtfsWSubString(elementName,
634                          currentElement,
635                          NtfsGetNextPathElement(currentElement) - currentElement);
636           DPRINT("  elementName:%S\n", elementName);
637
638           Status = NtfsDirFindFile(Vcb, parentFCB, elementName, &FCB);
639           if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
640             {
641               *pParentFCB = parentFCB;
642               *pFCB = NULL;
643               currentElement = NtfsGetNextPathElement(currentElement);
644               if (*currentElement == L'\0' || NtfsGetNextPathElement(currentElement + 1) == 0)
645                 {
646                   return(STATUS_OBJECT_NAME_NOT_FOUND);
647                 }
648               else
649                 {
650                   return(STATUS_OBJECT_PATH_NOT_FOUND);
651                 }
652             }
653           else if (!NT_SUCCESS(Status))
654             {
655               NtfsReleaseFCB(Vcb, parentFCB);
656               *pParentFCB = NULL;
657               *pFCB = NULL;
658
659               return(Status);
660             }
661         }
662       currentElement = NtfsGetNextPathElement(currentElement);
663     }
664
665   *pParentFCB = parentFCB;
666   *pFCB = FCB;
667 #endif
668
669   return(STATUS_SUCCESS);
670 }
671
672 /* EOF */