:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / drivers / fs / vfat / fcb.c
1 /* $Id$
2  *
3  *
4  * FILE:             fcb.c
5  * PURPOSE:          Routines to manipulate FCBs.
6  * COPYRIGHT:        See COPYING in the top level directory
7  * PROJECT:          ReactOS kernel
8  * PROGRAMMER:       Jason Filby (jasonfilby@yahoo.com)
9  *                   Rex Jolliff (rex@lvcablemodem.com)
10  */
11
12 /*  -------------------------------------------------------  INCLUDES  */
13
14 #include <ddk/ntddk.h>
15 #include <wchar.h>
16 #include <limits.h>
17
18 #define NDEBUG
19 #include <debug.h>
20
21 #include "vfat.h"
22
23 /*  --------------------------------------------------------  DEFINES  */
24
25 #define TAG(A, B, C, D) (ULONG)(((A)<<0) + ((B)<<8) + ((C)<<16) + ((D)<<24))
26 #define TAG_FCB TAG('V', 'F', 'C', 'B')
27
28 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
29
30 /*  --------------------------------------------------------  PUBLICS  */
31
32 PVFATFCB
33 vfatNewFCB(PWCHAR pFileName)
34 {
35   PVFATFCB  rcFCB;
36
37   rcFCB = ExAllocatePoolWithTag (NonPagedPool, sizeof (VFATFCB), TAG_FCB);
38   memset (rcFCB, 0, sizeof (VFATFCB));
39   if (pFileName)
40   {
41     wcscpy (rcFCB->PathName, pFileName);
42     if (wcsrchr (rcFCB->PathName, '\\') != 0)
43     {
44       rcFCB->ObjectName = wcsrchr (rcFCB->PathName, '\\');
45     }
46     else
47     {
48       rcFCB->ObjectName = rcFCB->PathName;
49     }
50   }
51   ExInitializeResourceLite(&rcFCB->PagingIoResource);
52   ExInitializeResourceLite(&rcFCB->MainResource);
53   return  rcFCB;
54 }
55
56 VOID
57 vfatDestroyFCB(PVFATFCB  pFCB)
58 {
59   ExDeleteResourceLite(&pFCB->PagingIoResource);
60   ExDeleteResourceLite(&pFCB->MainResource);
61   if ((pFCB->Flags & FCB_IS_PAGE_FILE) && pFCB->FatChainSize)
62         ExFreePool(pFCB->FatChain);
63   ExFreePool (pFCB);
64 }
65
66 BOOL
67 vfatFCBIsDirectory(PDEVICE_EXTENSION pVCB, PVFATFCB FCB)
68 {
69   return  FCB->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY;
70 }
71
72 BOOL
73 vfatFCBIsRoot(PVFATFCB FCB)
74 {
75   return  wcscmp (FCB->PathName, L"\\") == 0;
76 }
77
78 VOID
79 vfatGrabFCB(PDEVICE_EXTENSION  pVCB, PVFATFCB  pFCB)
80 {
81   KIRQL  oldIrql;
82
83   DPRINT ("grabbing FCB at %x: %S, refCount:%d\n",
84           pFCB,
85           pFCB->PathName,
86           pFCB->RefCount);
87
88   KeAcquireSpinLock (&pVCB->FcbListLock, &oldIrql);
89   pFCB->RefCount++;
90   KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
91 }
92
93 VOID
94 vfatReleaseFCB(PDEVICE_EXTENSION  pVCB,  PVFATFCB  pFCB)
95 {
96   KIRQL  oldIrql;
97
98   DPRINT ("releasing FCB at %x: %S, refCount:%d\n",
99           pFCB,
100           pFCB->PathName,
101           pFCB->RefCount);
102
103   KeAcquireSpinLock (&pVCB->FcbListLock, &oldIrql);
104   pFCB->RefCount--;
105   if (pFCB->RefCount <= 0 && (!vfatFCBIsDirectory (pVCB, pFCB) || pFCB->Flags & FCB_DELETE_PENDING))
106   {
107     RemoveEntryList (&pFCB->FcbListEntry);    
108     KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
109     if (vfatFCBIsDirectory(pVCB, pFCB))
110     {
111       CcRosReleaseFileCache(pFCB->FileObject, pFCB->RFCB.Bcb);
112       ExFreePool(pFCB->FileObject->FsContext2);
113       pFCB->FileObject->FsContext2 = NULL;
114       ObDereferenceObject(pFCB->FileObject);
115     }
116     vfatDestroyFCB (pFCB);
117   }
118   else
119     KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
120 }
121
122 VOID
123 vfatAddFCBToTable(PDEVICE_EXTENSION  pVCB,  PVFATFCB  pFCB)
124 {
125   KIRQL  oldIrql;
126
127   KeAcquireSpinLock (&pVCB->FcbListLock, &oldIrql);
128   pFCB->pDevExt = pVCB;
129   InsertTailList (&pVCB->FcbListHead, &pFCB->FcbListEntry);
130   KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
131 }
132
133 PVFATFCB
134 vfatGrabFCBFromTable(PDEVICE_EXTENSION  pVCB, PWSTR  pFileName)
135 {
136   KIRQL  oldIrql;
137   PVFATFCB  rcFCB;
138   PLIST_ENTRY  current_entry;
139
140   KeAcquireSpinLock (&pVCB->FcbListLock, &oldIrql);
141   current_entry = pVCB->FcbListHead.Flink;
142   while (current_entry != &pVCB->FcbListHead)
143   {
144     rcFCB = CONTAINING_RECORD (current_entry, VFATFCB, FcbListEntry);
145
146     if (wstrcmpi (pFileName, rcFCB->PathName))
147     {
148       rcFCB->RefCount++;
149       KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
150       return  rcFCB;
151     }
152
153     //FIXME: need to compare against short name in FCB here
154
155     current_entry = current_entry->Flink;
156   }
157   KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
158
159   return  NULL;
160 }
161
162 NTSTATUS
163 vfatFCBInitializeCacheFromVolume (PVCB  vcb, PVFATFCB  fcb)
164 {
165   NTSTATUS  status;
166   PFILE_OBJECT  fileObject;
167   ULONG  fileCacheQuantum;
168   PVFATCCB  newCCB;
169
170   fileObject = IoCreateStreamFileObject (NULL, vcb->StorageDevice);
171
172   newCCB = ExAllocatePoolWithTag (NonPagedPool, sizeof (VFATCCB), TAG_CCB);
173   if (newCCB == NULL)
174   {
175     return  STATUS_INSUFFICIENT_RESOURCES;
176   }
177   memset (newCCB, 0, sizeof (VFATCCB));
178
179   fileObject->Flags = fileObject->Flags | FO_FCB_IS_VALID |
180       FO_DIRECT_CACHE_PAGING_READ;
181   fileObject->SectionObjectPointers = &fcb->SectionObjectPointers;
182   fileObject->FsContext = (PVOID) &fcb->RFCB;
183   fileObject->FsContext2 = newCCB;
184   newCCB->pFcb = fcb;
185   newCCB->PtrFileObject = fileObject;
186   fcb->FileObject = fileObject;
187   fcb->pDevExt = vcb;
188
189
190   fileCacheQuantum = (vcb->FatInfo.BytesPerCluster >= PAGE_SIZE) ?
191       vcb->FatInfo.BytesPerCluster : PAGE_SIZE;
192
193   status = CcRosInitializeFileCache (fileObject,
194                                      &fcb->RFCB.Bcb,
195                                      fileCacheQuantum);
196   if (!NT_SUCCESS (status))
197   {
198     DbgPrint ("CcRosInitializeFileCache failed\n");
199     KeBugCheck (0);
200   }
201
202   fcb->Flags |= FCB_CACHE_INITIALIZED;
203
204   return  status;
205 }
206
207 PVFATFCB
208 vfatMakeRootFCB(PDEVICE_EXTENSION  pVCB)
209 {
210   PVFATFCB  FCB;
211   ULONG FirstCluster, CurrentCluster, Size = 0;
212   NTSTATUS Status = STATUS_SUCCESS;
213
214   FCB = vfatNewFCB(L"\\");
215   memset(FCB->entry.Filename, ' ', 11);
216   FCB->entry.FileSize = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
217   FCB->entry.Attrib = FILE_ATTRIBUTE_DIRECTORY;
218   if (pVCB->FatInfo.FatType == FAT32)
219   {
220     CurrentCluster = FirstCluster = pVCB->FatInfo.RootCluster;
221     FCB->entry.FirstCluster = FirstCluster & 0xffff;
222     FCB->entry.FirstClusterHigh = FirstCluster >> 16;
223     CurrentCluster = FirstCluster;
224
225     while (CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
226     {
227       Size += pVCB->FatInfo.BytesPerCluster;
228       Status = NextCluster (pVCB, NULL, FirstCluster, &CurrentCluster, FALSE);
229     }
230   }
231   else
232   {
233     FCB->entry.FirstCluster = 1;
234     Size = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
235   }
236   FCB->RefCount = 1;
237   FCB->dirIndex = 0;
238   FCB->RFCB.FileSize.QuadPart = Size;
239   FCB->RFCB.ValidDataLength.QuadPart = Size;
240   FCB->RFCB.AllocationSize.QuadPart = Size;
241
242   vfatFCBInitializeCacheFromVolume(pVCB, FCB);
243   vfatAddFCBToTable(pVCB, FCB);
244   vfatGrabFCB(pVCB, FCB);
245
246   return(FCB);
247 }
248
249 PVFATFCB
250 vfatOpenRootFCB(PDEVICE_EXTENSION  pVCB)
251 {
252   PVFATFCB  FCB;
253
254   FCB = vfatGrabFCBFromTable (pVCB, L"\\");
255   if (FCB == NULL)
256   {
257     FCB = vfatMakeRootFCB (pVCB);
258   }
259
260   return  FCB;
261 }
262
263 NTSTATUS
264 vfatMakeFCBFromDirEntry(PVCB  vcb,
265                         PVFATFCB  directoryFCB,
266                         PWSTR  longName,
267                         PFAT_DIR_ENTRY  dirEntry,
268                         ULONG dirIndex,
269                         PVFATFCB* fileFCB)
270 {
271   PVFATFCB  rcFCB;
272   WCHAR  pathName [MAX_PATH];
273   ULONG Size;
274   if (longName [0] != 0 && wcslen (directoryFCB->PathName) +
275         sizeof(WCHAR) + wcslen (longName) > MAX_PATH)
276   {
277     return  STATUS_OBJECT_NAME_INVALID;
278   }
279   wcscpy (pathName, directoryFCB->PathName);
280   if (!vfatFCBIsRoot (directoryFCB))
281   {
282     wcscat (pathName, L"\\");
283   }
284   if (longName [0] != 0)
285   {
286     wcscat (pathName, longName);
287   }
288   else
289   {
290     WCHAR  entryName [MAX_PATH];
291
292     vfatGetDirEntryName (dirEntry, entryName);
293     wcscat (pathName, entryName);
294   }
295   rcFCB = vfatNewFCB (pathName);
296   memcpy (&rcFCB->entry, dirEntry, sizeof (FAT_DIR_ENTRY));
297
298   if (vfatFCBIsDirectory(vcb, rcFCB))
299   {
300     ULONG FirstCluster, CurrentCluster;
301     NTSTATUS Status;
302     Size = 0;
303     FirstCluster = vfatDirEntryGetFirstCluster (vcb, &rcFCB->entry);
304     if (FirstCluster == 1)
305     {
306       Size = vcb->FatInfo.rootDirectorySectors * vcb->FatInfo.BytesPerSector;
307     }
308     else
309     {
310       CurrentCluster = FirstCluster;
311       while (CurrentCluster != 0xffffffff)
312       {
313          Size += vcb->FatInfo.BytesPerCluster;
314          Status = NextCluster (vcb, NULL, FirstCluster, &CurrentCluster, FALSE);
315       }
316     }
317   }
318   else
319   {
320     Size = rcFCB->entry.FileSize;
321   }
322   rcFCB->dirIndex = dirIndex;
323   rcFCB->RFCB.FileSize.QuadPart = Size;
324   rcFCB->RFCB.ValidDataLength.QuadPart = Size;
325   rcFCB->RFCB.AllocationSize.QuadPart = ROUND_UP(Size, vcb->FatInfo.BytesPerCluster);
326   rcFCB->RefCount++;
327   if (vfatFCBIsDirectory(vcb, rcFCB))
328     {
329       vfatFCBInitializeCacheFromVolume(vcb, rcFCB);
330     }
331   vfatAddFCBToTable (vcb, rcFCB);
332   *fileFCB = rcFCB;
333
334   return  STATUS_SUCCESS;
335 }
336
337 NTSTATUS
338 vfatAttachFCBToFileObject (PDEVICE_EXTENSION  vcb,
339                            PVFATFCB  fcb,
340                            PFILE_OBJECT  fileObject)
341 {
342   NTSTATUS  status;
343   PVFATCCB  newCCB;
344
345   newCCB = ExAllocatePoolWithTag (NonPagedPool, sizeof (VFATCCB), TAG_CCB);
346   if (newCCB == NULL)
347   {
348     return  STATUS_INSUFFICIENT_RESOURCES;
349   }
350   memset (newCCB, 0, sizeof (VFATCCB));
351
352   fileObject->Flags = fileObject->Flags | FO_FCB_IS_VALID |
353       FO_DIRECT_CACHE_PAGING_READ;
354   fileObject->SectionObjectPointers = &fcb->SectionObjectPointers;
355   fileObject->FsContext = (PVOID) &fcb->RFCB;
356   fileObject->FsContext2 = newCCB;
357   newCCB->pFcb = fcb;
358   newCCB->PtrFileObject = fileObject;
359   fcb->pDevExt = vcb;
360   DPRINT ("file open: fcb:%x file size: %d\n", fcb, fcb->entry.FileSize);
361
362   return  STATUS_SUCCESS;
363 }
364
365 NTSTATUS
366 vfatDirFindFile (PDEVICE_EXTENSION  pDeviceExt,
367                  PVFATFCB  pDirectoryFCB,
368                  PWSTR  pFileToFind,
369                  PVFATFCB * pFoundFCB)
370 {
371   BOOL  finishedScanningDirectory;
372   ULONG  directoryIndex;
373   NTSTATUS  status;
374   WCHAR  defaultFileName [2];
375   WCHAR  currentLongName [256];
376   FAT_DIR_ENTRY  currentDirEntry;
377   WCHAR  currentEntryName [256];
378
379   assert (pDeviceExt);
380   assert (pDirectoryFCB);
381   assert (pFileToFind);
382
383   DPRINT ("vfatDirFindFile(VCB:%08x, dirFCB:%08x, File:%S)\n",
384           pDeviceExt,
385           pDirectoryFCB,
386           pFileToFind);
387   DPRINT ("Dir Path:%S\n", pDirectoryFCB->PathName);
388
389   //  default to '.' if no filename specified
390   if (wcslen (pFileToFind) == 0)
391   {
392     defaultFileName [0] = L'.';
393     defaultFileName [1] = 0;
394     pFileToFind = defaultFileName;
395   }
396
397   directoryIndex = 0;
398   finishedScanningDirectory = FALSE;
399   while (!finishedScanningDirectory)
400   {
401     status = vfatGetNextDirEntry (pDeviceExt,
402                                   pDirectoryFCB,
403                                   &directoryIndex,
404                                   currentLongName,
405                                   &currentDirEntry);
406     if (status == STATUS_NO_MORE_ENTRIES)
407     {
408       finishedScanningDirectory = TRUE;
409       continue;
410     }
411     else if (!NT_SUCCESS(status))
412     {
413       return  status;
414     }
415
416     DPRINT ("  Index:%d  longName:%S\n",
417             directoryIndex,
418             currentLongName);
419
420     if (!vfatIsDirEntryDeleted (&currentDirEntry)
421       && !vfatIsDirEntryVolume(&currentDirEntry))
422     {
423       if (currentLongName [0] != L'\0' && wstrcmpjoki (currentLongName, pFileToFind))
424       {
425         DPRINT ("Match found, %S\n", currentLongName);
426         status = vfatMakeFCBFromDirEntry (pDeviceExt,
427                                           pDirectoryFCB,
428                                           currentLongName,
429                                           &currentDirEntry,
430                                           directoryIndex - 1,
431                                           pFoundFCB);
432         return  status;
433       }
434       else
435       {
436         vfatGetDirEntryName (&currentDirEntry, currentEntryName);
437         DPRINT ("  entryName:%S\n", currentEntryName);
438
439         if (wstrcmpjoki (currentEntryName, pFileToFind))
440         {
441           DPRINT ("Match found, %S\n", currentEntryName);
442           status = vfatMakeFCBFromDirEntry (pDeviceExt,
443                                             pDirectoryFCB,
444                                             currentLongName,
445                                             &currentDirEntry,
446                                             directoryIndex - 1,
447                                             pFoundFCB);
448           return  status;
449         }
450       }
451     }
452   }
453
454   return  STATUS_OBJECT_NAME_NOT_FOUND;
455 }
456
457 NTSTATUS
458 vfatGetFCBForFile (PDEVICE_EXTENSION  pVCB,
459                    PVFATFCB  *pParentFCB,
460                    PVFATFCB  *pFCB,
461                    const PWSTR  pFileName)
462 {
463   NTSTATUS  status;
464   WCHAR  pathName [MAX_PATH];
465   WCHAR  elementName [MAX_PATH];
466   PWCHAR  currentElement;
467   PVFATFCB  FCB;
468   PVFATFCB  parentFCB;
469
470   DPRINT ("vfatGetFCBForFile (%x,%x,%x,%S)\n",
471           pVCB,
472           pParentFCB,
473           pFCB,
474           pFileName);
475
476   //  Trivial case, open of the root directory on volume
477   if (pFileName [0] == L'\0' || wcscmp (pFileName, L"\\") == 0)
478   {
479     DPRINT ("returning root FCB\n");
480
481     FCB = vfatOpenRootFCB (pVCB);
482     *pFCB = FCB;
483     *pParentFCB = NULL;
484
485     return  (FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND;
486   }
487   else
488   {
489     currentElement = pFileName + 1;
490     wcscpy (pathName, L"\\");
491     FCB = vfatOpenRootFCB (pVCB);
492   }
493   parentFCB = NULL;
494
495   //  Parse filename and check each path element for existance and access
496   while (vfatGetNextPathElement (currentElement) != 0)
497   {
498     //  Skip blank directory levels
499     if ((vfatGetNextPathElement (currentElement) - currentElement) == 0)
500     {
501       currentElement++;
502       continue;
503     }
504
505     DPRINT ("Parsing, currentElement:%S\n", currentElement);
506     DPRINT ("  parentFCB:%x FCB:%x\n", parentFCB, FCB);
507
508     //  descend to next directory level
509     if (parentFCB)
510     {
511       vfatReleaseFCB (pVCB, parentFCB);
512       parentFCB = 0;
513     }
514     //  fail if element in FCB is not a directory
515     if (!vfatFCBIsDirectory (pVCB, FCB))
516     {
517       DPRINT ("Element in requested path is not a directory\n");
518
519       vfatReleaseFCB (pVCB, FCB);
520       FCB = 0;
521       *pParentFCB = NULL;
522       *pFCB = NULL;
523
524       return  STATUS_OBJECT_PATH_NOT_FOUND;
525     }
526     parentFCB = FCB;
527
528     //  Extract next directory level into dirName
529     vfatWSubString (pathName,
530                     pFileName,
531                     vfatGetNextPathElement (currentElement) - pFileName);
532     DPRINT ("  pathName:%S\n", pathName);
533
534     FCB = vfatGrabFCBFromTable (pVCB, pathName);
535     if (FCB == NULL)
536     {
537       vfatWSubString (elementName,
538                       currentElement,
539                       vfatGetNextPathElement (currentElement) - currentElement);
540       DPRINT ("  elementName:%S\n", elementName);
541
542       status = vfatDirFindFile (pVCB, parentFCB, elementName, &FCB);
543       if (status == STATUS_OBJECT_NAME_NOT_FOUND)
544       {
545         *pParentFCB = parentFCB;
546         *pFCB = NULL;
547         currentElement = vfatGetNextPathElement(currentElement);
548         if (*currentElement == L'\0' || vfatGetNextPathElement(currentElement + 1) == 0)
549         {
550           return  STATUS_OBJECT_NAME_NOT_FOUND;
551         }
552         else
553         {
554           return  STATUS_OBJECT_PATH_NOT_FOUND;
555         }
556       }
557       else if (!NT_SUCCESS (status))
558       {
559         vfatReleaseFCB (pVCB, parentFCB);
560         *pParentFCB = NULL;
561         *pFCB = NULL;
562
563         return  status;
564       }
565     }
566     currentElement = vfatGetNextPathElement (currentElement);
567   }
568
569   *pParentFCB = parentFCB;
570   *pFCB = FCB;
571
572   return  STATUS_SUCCESS;
573 }
574
575
576
577