Added some fixes for accessing the page file.
[reactos.git] / reactos / drivers / fs / vfat / fcb.c
1 /* $Id: fcb.c,v 1.13 2002/01/15 21:54:51 hbirr Exp $
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 CcRosReleaseFileCache (NULL, pFCB->RFCB.Bcb);
109 vfatDestroyFCB (pFCB);
110 }
111 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
112 }
113
114 VOID
115 vfatAddFCBToTable(PDEVICE_EXTENSION pVCB, PVFATFCB pFCB)
116 {
117 KIRQL oldIrql;
118
119 KeAcquireSpinLock (&pVCB->FcbListLock, &oldIrql);
120 pFCB->pDevExt = pVCB;
121 InsertTailList (&pVCB->FcbListHead, &pFCB->FcbListEntry);
122 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
123 }
124
125 PVFATFCB
126 vfatGrabFCBFromTable(PDEVICE_EXTENSION pVCB, PWSTR pFileName)
127 {
128 KIRQL oldIrql;
129 PVFATFCB rcFCB;
130 PLIST_ENTRY current_entry;
131
132 KeAcquireSpinLock (&pVCB->FcbListLock, &oldIrql);
133 current_entry = pVCB->FcbListHead.Flink;
134 while (current_entry != &pVCB->FcbListHead)
135 {
136 rcFCB = CONTAINING_RECORD (current_entry, VFATFCB, FcbListEntry);
137
138 if (wstrcmpi (pFileName, rcFCB->PathName))
139 {
140 rcFCB->RefCount++;
141 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
142 return rcFCB;
143 }
144
145 //FIXME: need to compare against short name in FCB here
146
147 current_entry = current_entry->Flink;
148 }
149 KeReleaseSpinLock (&pVCB->FcbListLock, oldIrql);
150
151 return NULL;
152 }
153
154 NTSTATUS
155 vfatFCBInitializeCache (PVCB vcb, PVFATFCB fcb)
156 {
157 NTSTATUS status;
158 PFILE_OBJECT fileObject;
159 ULONG bytesPerCluster;
160 ULONG fileCacheQuantum;
161 PVFATCCB newCCB;
162
163 fileObject = IoCreateStreamFileObject (NULL, vcb->StorageDevice);
164
165 newCCB = ExAllocatePoolWithTag (NonPagedPool, sizeof (VFATCCB), TAG_CCB);
166 if (newCCB == NULL)
167 {
168 return STATUS_INSUFFICIENT_RESOURCES;
169 }
170 memset (newCCB, 0, sizeof (VFATCCB));
171
172 fileObject->Flags = fileObject->Flags | FO_FCB_IS_VALID |
173 FO_DIRECT_CACHE_PAGING_READ;
174 fileObject->SectionObjectPointers = &fcb->SectionObjectPointers;
175 fileObject->FsContext = (PVOID) &fcb->RFCB;
176 fileObject->FsContext2 = newCCB;
177 newCCB->pFcb = fcb;
178 newCCB->PtrFileObject = fileObject;
179 fcb->FileObject = fileObject;
180 fcb->pDevExt = vcb;
181
182
183 bytesPerCluster = vcb->Boot->SectorsPerCluster * BLOCKSIZE;
184 fileCacheQuantum = (bytesPerCluster >= PAGESIZE) ?
185 bytesPerCluster : PAGESIZE;
186
187 status = CcRosInitializeFileCache (fileObject,
188 &fcb->RFCB.Bcb,
189 fileCacheQuantum);
190 if (!NT_SUCCESS (status))
191 {
192 DbgPrint ("CcRosInitializeFileCache failed\n");
193 KeBugCheck (0);
194 }
195
196 ObDereferenceObject (fileObject);
197 fcb->Flags |= FCB_CACHE_INITIALIZED;
198
199 return status;
200 }
201
202 PVFATFCB
203 vfatMakeRootFCB(PDEVICE_EXTENSION pVCB)
204 {
205 PVFATFCB FCB;
206 ULONG FirstCluster, CurrentCluster, Size = 0;
207 NTSTATUS Status = STATUS_SUCCESS;
208
209 FCB = vfatNewFCB(L"\\");
210 memset(FCB->entry.Filename, ' ', 11);
211 FCB->entry.FileSize = pVCB->rootDirectorySectors * BLOCKSIZE;
212 FCB->entry.Attrib = FILE_ATTRIBUTE_DIRECTORY;
213 if (pVCB->FatType == FAT32)
214 {
215 CurrentCluster = FirstCluster = ((struct _BootSector32*)(pVCB->Boot))->RootCluster;
216 FCB->entry.FirstCluster = FirstCluster & 0xffff;
217 FCB->entry.FirstClusterHigh = FirstCluster >> 16;
218 CurrentCluster = FirstCluster;
219
220 while (CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
221 {
222 Size += pVCB->BytesPerCluster;
223 Status = NextCluster (pVCB, NULL, FirstCluster, &CurrentCluster, FALSE);
224 }
225 }
226 else
227 {
228 FCB->entry.FirstCluster = 1;
229 Size = pVCB->rootDirectorySectors * BLOCKSIZE;
230 }
231 FCB->RefCount = 1;
232 FCB->dirIndex = 0;
233 FCB->RFCB.FileSize.QuadPart = Size;
234 FCB->RFCB.ValidDataLength.QuadPart = Size;
235 FCB->RFCB.AllocationSize.QuadPart = Size;
236
237 vfatFCBInitializeCache(pVCB, FCB);
238 vfatAddFCBToTable(pVCB, FCB);
239 vfatGrabFCB(pVCB, FCB);
240
241 return(FCB);
242 }
243
244 PVFATFCB
245 vfatOpenRootFCB(PDEVICE_EXTENSION pVCB)
246 {
247 PVFATFCB FCB;
248
249 FCB = vfatGrabFCBFromTable (pVCB, L"\\");
250 if (FCB == NULL)
251 {
252 FCB = vfatMakeRootFCB (pVCB);
253 }
254
255 return FCB;
256 }
257
258 NTSTATUS
259 vfatMakeFCBFromDirEntry(PVCB vcb,
260 PVFATFCB directoryFCB,
261 PWSTR longName,
262 PFAT_DIR_ENTRY dirEntry,
263 ULONG dirIndex,
264 PVFATFCB * fileFCB)
265 {
266 PVFATFCB rcFCB;
267 WCHAR pathName [MAX_PATH];
268 ULONG Size;
269 if (longName [0] != 0 && wcslen (directoryFCB->PathName) +
270 sizeof(WCHAR) + wcslen (longName) > MAX_PATH)
271 {
272 return STATUS_OBJECT_NAME_INVALID;
273 }
274 wcscpy (pathName, directoryFCB->PathName);
275 if (!vfatFCBIsRoot (directoryFCB))
276 {
277 wcscat (pathName, L"\\");
278 }
279 if (longName [0] != 0)
280 {
281 wcscat (pathName, longName);
282 }
283 else
284 {
285 WCHAR entryName [MAX_PATH];
286
287 vfatGetDirEntryName (dirEntry, entryName);
288 wcscat (pathName, entryName);
289 }
290 rcFCB = vfatNewFCB (pathName);
291 memcpy (&rcFCB->entry, dirEntry, sizeof (FAT_DIR_ENTRY));
292
293 if (vfatFCBIsDirectory(vcb, rcFCB))
294 {
295 ULONG FirstCluster, CurrentCluster;
296 NTSTATUS Status;
297 Size = 0;
298 FirstCluster = vfatDirEntryGetFirstCluster (vcb, &rcFCB->entry);
299 if (FirstCluster == 1)
300 {
301 Size = vcb->rootDirectorySectors * BLOCKSIZE;
302 }
303 else
304 {
305 CurrentCluster = FirstCluster;
306 while (CurrentCluster != 0xffffffff)
307 {
308 Size += vcb->BytesPerCluster;
309 Status = NextCluster (vcb, NULL, FirstCluster, &CurrentCluster, FALSE);
310 }
311 }
312 }
313 else
314 {
315 Size = rcFCB->entry.FileSize;
316 }
317 rcFCB->dirIndex = dirIndex;
318 rcFCB->RFCB.FileSize.QuadPart = Size;
319 rcFCB->RFCB.ValidDataLength.QuadPart = Size;
320 rcFCB->RFCB.AllocationSize.QuadPart = ROUND_UP(Size, vcb->BytesPerCluster);
321 // DPRINT1("%S %d %d\n", longName, Size, (ULONG)rcFCB->RFCB.AllocationSize.QuadPart);
322 vfatFCBInitializeCache (vcb, rcFCB);
323 rcFCB->RefCount++;
324 vfatAddFCBToTable (vcb, rcFCB);
325 *fileFCB = rcFCB;
326
327 return STATUS_SUCCESS;
328 }
329
330 NTSTATUS
331 vfatAttachFCBToFileObject (PDEVICE_EXTENSION vcb,
332 PVFATFCB fcb,
333 PFILE_OBJECT fileObject)
334 {
335 NTSTATUS status;
336 PVFATCCB newCCB;
337
338 newCCB = ExAllocatePoolWithTag (NonPagedPool, sizeof (VFATCCB), TAG_CCB);
339 if (newCCB == NULL)
340 {
341 return STATUS_INSUFFICIENT_RESOURCES;
342 }
343 memset (newCCB, 0, sizeof (VFATCCB));
344
345 fileObject->Flags = fileObject->Flags | FO_FCB_IS_VALID |
346 FO_DIRECT_CACHE_PAGING_READ;
347 fileObject->SectionObjectPointers = &fcb->SectionObjectPointers;
348 fileObject->FsContext = (PVOID) &fcb->RFCB;
349 fileObject->FsContext2 = newCCB;
350 newCCB->pFcb = fcb;
351 newCCB->PtrFileObject = fileObject;
352 fcb->pDevExt = vcb;
353
354 if (!(fcb->Flags & FCB_CACHE_INITIALIZED))
355 {
356 ULONG bytesPerCluster;
357 ULONG fileCacheQuantum;
358
359 bytesPerCluster = vcb->Boot->SectorsPerCluster * BLOCKSIZE;
360 fileCacheQuantum = (bytesPerCluster >= PAGESIZE) ? bytesPerCluster : PAGESIZE;
361 status = CcRosInitializeFileCache (fileObject,
362 &fcb->RFCB.Bcb,
363 fileCacheQuantum);
364 if (!NT_SUCCESS (status))
365 {
366 DbgPrint ("CcRosInitializeFileCache failed\n");
367 KeBugCheck (0);
368 }
369 fcb->Flags |= FCB_CACHE_INITIALIZED;
370 }
371
372 DPRINT ("file open: fcb:%x file size: %d\n", fcb, fcb->entry.FileSize);
373
374 return STATUS_SUCCESS;
375 }
376
377 NTSTATUS
378 vfatDirFindFile (PDEVICE_EXTENSION pDeviceExt,
379 PVFATFCB pDirectoryFCB,
380 PWSTR pFileToFind,
381 PVFATFCB * pFoundFCB)
382 {
383 BOOL finishedScanningDirectory;
384 ULONG directoryIndex;
385 NTSTATUS status;
386 WCHAR defaultFileName [2];
387 WCHAR currentLongName [256];
388 FAT_DIR_ENTRY currentDirEntry;
389 WCHAR currentEntryName [256];
390
391 assert (pDeviceExt);
392 assert (pDirectoryFCB);
393 assert (pFileToFind);
394
395 DPRINT ("vfatDirFindFile(VCB:%08x, dirFCB:%08x, File:%S)\n",
396 pDeviceExt,
397 pDirectoryFCB,
398 pFileToFind);
399 DPRINT ("Dir Path:%S\n", pDirectoryFCB->PathName);
400
401 // default to '.' if no filename specified
402 if (wcslen (pFileToFind) == 0)
403 {
404 defaultFileName [0] = L'.';
405 defaultFileName [1] = 0;
406 pFileToFind = defaultFileName;
407 }
408
409 directoryIndex = 0;
410 finishedScanningDirectory = FALSE;
411 while (!finishedScanningDirectory)
412 {
413 status = vfatGetNextDirEntry (pDeviceExt,
414 pDirectoryFCB,
415 &directoryIndex,
416 currentLongName,
417 &currentDirEntry);
418 if (status == STATUS_NO_MORE_ENTRIES)
419 {
420 finishedScanningDirectory = TRUE;
421 continue;
422 }
423 else if (!NT_SUCCESS(status))
424 {
425 return status;
426 }
427
428 DPRINT (" Index:%d longName:%S\n",
429 directoryIndex,
430 currentLongName);
431
432 if (!vfatIsDirEntryDeleted (&currentDirEntry)
433 && !vfatIsDirEntryVolume(&currentDirEntry))
434 {
435 if (currentLongName [0] != L'\0' && wstrcmpjoki (currentLongName, pFileToFind))
436 {
437 DPRINT ("Match found, %S\n", currentLongName);
438 status = vfatMakeFCBFromDirEntry (pDeviceExt,
439 pDirectoryFCB,
440 currentLongName,
441 &currentDirEntry,
442 directoryIndex - 1,
443 pFoundFCB);
444 return status;
445 }
446 else
447 {
448 vfatGetDirEntryName (&currentDirEntry, currentEntryName);
449 DPRINT (" entryName:%S\n", currentEntryName);
450
451 if (wstrcmpjoki (currentEntryName, pFileToFind))
452 {
453 DPRINT ("Match found, %S\n", currentEntryName);
454 status = vfatMakeFCBFromDirEntry (pDeviceExt,
455 pDirectoryFCB,
456 currentLongName,
457 &currentDirEntry,
458 directoryIndex - 1,
459 pFoundFCB);
460 return status;
461 }
462 }
463 }
464 }
465
466 return STATUS_OBJECT_NAME_NOT_FOUND;
467 }
468
469 NTSTATUS
470 vfatGetFCBForFile (PDEVICE_EXTENSION pVCB,
471 PVFATFCB *pParentFCB,
472 PVFATFCB *pFCB,
473 const PWSTR pFileName)
474 {
475 NTSTATUS status;
476 WCHAR pathName [MAX_PATH];
477 WCHAR elementName [MAX_PATH];
478 PWCHAR currentElement;
479 PVFATFCB FCB;
480 PVFATFCB parentFCB;
481
482 DPRINT ("vfatGetFCBForFile (%x,%x,%x,%S)\n",
483 pVCB,
484 pParentFCB,
485 pFCB,
486 pFileName);
487
488 // Trivial case, open of the root directory on volume
489 if (pFileName [0] == L'\0' || wcscmp (pFileName, L"\\") == 0)
490 {
491 DPRINT ("returning root FCB\n");
492
493 FCB = vfatOpenRootFCB (pVCB);
494 *pFCB = FCB;
495 *pParentFCB = NULL;
496
497 return (FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND;
498 }
499 else
500 {
501 currentElement = pFileName + 1;
502 wcscpy (pathName, L"\\");
503 FCB = vfatOpenRootFCB (pVCB);
504 }
505 parentFCB = NULL;
506
507 // Parse filename and check each path element for existance and access
508 while (vfatGetNextPathElement (currentElement) != 0)
509 {
510 // Skip blank directory levels
511 if ((vfatGetNextPathElement (currentElement) - currentElement) == 0)
512 {
513 currentElement++;
514 continue;
515 }
516
517 DPRINT ("Parsing, currentElement:%S\n", currentElement);
518 DPRINT (" parentFCB:%x FCB:%x\n", parentFCB, FCB);
519
520 // descend to next directory level
521 if (parentFCB)
522 {
523 vfatReleaseFCB (pVCB, parentFCB);
524 parentFCB = 0;
525 }
526 // fail if element in FCB is not a directory
527 if (!vfatFCBIsDirectory (pVCB, FCB))
528 {
529 DPRINT ("Element in requested path is not a directory\n");
530
531 vfatReleaseFCB (pVCB, FCB);
532 FCB = 0;
533 *pParentFCB = NULL;
534 *pFCB = NULL;
535
536 return STATUS_OBJECT_PATH_NOT_FOUND;
537 }
538 parentFCB = FCB;
539
540 // Extract next directory level into dirName
541 vfatWSubString (pathName,
542 pFileName,
543 vfatGetNextPathElement (currentElement) - pFileName);
544 DPRINT (" pathName:%S\n", pathName);
545
546 FCB = vfatGrabFCBFromTable (pVCB, pathName);
547 if (FCB == NULL)
548 {
549 vfatWSubString (elementName,
550 currentElement,
551 vfatGetNextPathElement (currentElement) - currentElement);
552 DPRINT (" elementName:%S\n", elementName);
553
554 status = vfatDirFindFile (pVCB, parentFCB, elementName, &FCB);
555 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
556 {
557 *pParentFCB = parentFCB;
558 *pFCB = NULL;
559 currentElement = vfatGetNextPathElement(currentElement);
560 if (*currentElement == L'\0' || vfatGetNextPathElement(currentElement + 1) == 0)
561 {
562 return STATUS_OBJECT_NAME_NOT_FOUND;
563 }
564 else
565 {
566 return STATUS_OBJECT_PATH_NOT_FOUND;
567 }
568 }
569 else if (!NT_SUCCESS (status))
570 {
571 vfatReleaseFCB (pVCB, parentFCB);
572 *pParentFCB = NULL;
573 *pFCB = NULL;
574
575 return status;
576 }
577 }
578 currentElement = vfatGetNextPathElement (currentElement);
579 }
580
581 *pParentFCB = parentFCB;
582 *pFCB = FCB;
583
584 return STATUS_SUCCESS;
585 }
586
587
588
589