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