Fixed the 'REACTOS' volume label bug.
[reactos.git] / reactos / drivers / fs / vfat / fcb.c
1 /* $Id: fcb.c,v 1.8 2001/07/28 07:05:56 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 && !vfatIsDirEntryVolume(&currentDirEntry))
471 {
472 if (currentLongName [0] != L'\0' && wstrcmpjoki (currentLongName, pFileToFind))
473 {
474 DPRINT ("Match found, %S\n", currentLongName);
475 status = vfatMakeFCBFromDirEntry (pDeviceExt,
476 pDirectoryFCB,
477 currentLongName,
478 &currentDirEntry,
479 pFoundFCB);
480 return status;
481 }
482 else
483 {
484 vfatGetDirEntryName (&currentDirEntry, currentEntryName);
485 DPRINT (" entryName:%S\n", currentEntryName);
486
487 if (wstrcmpjoki (currentEntryName, pFileToFind))
488 {
489 DPRINT ("Match found, %S\n", currentEntryName);
490 status = vfatMakeFCBFromDirEntry (pDeviceExt,
491 pDirectoryFCB,
492 currentLongName,
493 &currentDirEntry,
494 pFoundFCB);
495 return status;
496 }
497 }
498 }
499 }
500
501 return STATUS_OBJECT_NAME_NOT_FOUND;
502 }
503
504 NTSTATUS
505 vfatGetFCBForFile (PDEVICE_EXTENSION pVCB,
506 PVFATFCB *pParentFCB,
507 PVFATFCB *pFCB,
508 const PWSTR pFileName)
509 {
510 NTSTATUS status;
511 WCHAR pathName [MAX_PATH];
512 WCHAR elementName [MAX_PATH];
513 PWCHAR currentElement;
514 PVFATFCB FCB;
515 PVFATFCB parentFCB;
516
517 DPRINT ("vfatGetFCBForFile (%x,%x,%x,%S)\n",
518 pVCB,
519 pParentFCB,
520 pFCB,
521 pFileName);
522
523 // Trivial case, open of the root directory on volume
524 if (pFileName [0] == L'\0' || wcscmp (pFileName, L"\\") == 0)
525 {
526 DPRINT ("returning root FCB\n");
527
528 FCB = vfatOpenRootFCB (pVCB);
529 *pFCB = FCB;
530 *pParentFCB = NULL;
531
532 return (FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND;
533 }
534 else
535 {
536 currentElement = pFileName + 1;
537 wcscpy (pathName, L"\\");
538 FCB = vfatOpenRootFCB (pVCB);
539 }
540 parentFCB = NULL;
541
542 // Parse filename and check each path element for existance and access
543 while (vfatGetNextPathElement (currentElement) != 0)
544 {
545 // Skip blank directory levels
546 if ((vfatGetNextPathElement (currentElement) - currentElement) == 0)
547 {
548 currentElement++;
549 continue;
550 }
551
552 DPRINT ("Parsing, currentElement:%S\n", currentElement);
553 DPRINT (" parentFCB:%x FCB:%x\n", parentFCB, FCB);
554
555 // descend to next directory level
556 if (parentFCB)
557 {
558 vfatReleaseFCB (pVCB, parentFCB);
559 parentFCB = 0;
560 }
561 // fail if element in FCB is not a directory
562 if (!vfatFCBIsDirectory (pVCB, FCB))
563 {
564 DPRINT ("Element in requested path is not a directory\n");
565
566 vfatReleaseFCB (pVCB, FCB);
567 FCB = 0;
568 *pParentFCB = NULL;
569 *pFCB = NULL;
570
571 return STATUS_OBJECT_PATH_NOT_FOUND;
572 }
573 parentFCB = FCB;
574
575 // Extract next directory level into dirName
576 vfatWSubString (pathName,
577 pFileName,
578 vfatGetNextPathElement (currentElement) - pFileName);
579 DPRINT (" pathName:%S\n", pathName);
580
581 FCB = vfatGrabFCBFromTable (pVCB, pathName);
582 if (FCB == NULL)
583 {
584 vfatWSubString (elementName,
585 currentElement,
586 vfatGetNextPathElement (currentElement) - currentElement);
587 DPRINT (" elementName:%S\n", elementName);
588
589 status = vfatDirFindFile (pVCB, parentFCB, elementName, &FCB);
590 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
591 {
592 *pParentFCB = parentFCB;
593 *pFCB = NULL;
594 currentElement = vfatGetNextPathElement(currentElement);
595 if (*currentElement == L'\0' || vfatGetNextPathElement(currentElement + 1) == 0)
596 {
597 return STATUS_OBJECT_NAME_NOT_FOUND;
598 }
599 else
600 {
601 return STATUS_OBJECT_PATH_NOT_FOUND;
602 }
603 }
604 else if (!NT_SUCCESS (status))
605 {
606 vfatReleaseFCB (pVCB, parentFCB);
607 *pParentFCB = NULL;
608 *pFCB = NULL;
609
610 return status;
611 }
612 }
613 currentElement = vfatGetNextPathElement (currentElement);
614 }
615
616 *pParentFCB = parentFCB;
617 *pFCB = FCB;
618
619 return STATUS_SUCCESS;
620 }
621
622
623
624