1 /*
2 * PROJECT: VFAT Filesystem
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Filesystem routines
5 * COPYRIGHT: Copyright 2002-2013 Eric Kohl <eric.kohl@reactos.org>
6 * Copyright 2008-2018 Pierre Schweitzer <pierre@reactos.org>
7 */
8
9 /* INCLUDES *****************************************************************/
10
11 #include "vfat.h"
12
13 #define NDEBUG
14 #include <debug.h>
15
16 extern VFAT_DISPATCH FatXDispatch;
17 extern VFAT_DISPATCH FatDispatch;
18
19 /* FUNCTIONS ****************************************************************/
20
21 #define CACHEPAGESIZE(pDeviceExt) ((pDeviceExt)->FatInfo.BytesPerCluster > PAGE_SIZE ? \
22 (pDeviceExt)->FatInfo.BytesPerCluster : PAGE_SIZE)
23
24 static
25 NTSTATUS
VfatHasFileSystem(PDEVICE_OBJECT DeviceToMount,PBOOLEAN RecognizedFS,PFATINFO pFatInfo,BOOLEAN Override)26 VfatHasFileSystem(
27 PDEVICE_OBJECT DeviceToMount,
28 PBOOLEAN RecognizedFS,
29 PFATINFO pFatInfo,
30 BOOLEAN Override)
31 {
32 NTSTATUS Status;
33 PARTITION_INFORMATION PartitionInfo;
34 DISK_GEOMETRY DiskGeometry;
35 FATINFO FatInfo;
36 ULONG Size;
37 ULONG Sectors;
38 LARGE_INTEGER Offset;
39 struct _BootSector* Boot;
40 struct _BootSectorFatX* BootFatX;
41 BOOLEAN PartitionInfoIsValid = FALSE;
42
43 DPRINT("VfatHasFileSystem\n");
44
45 *RecognizedFS = FALSE;
46
47 Size = sizeof(DISK_GEOMETRY);
48 Status = VfatBlockDeviceIoControl(DeviceToMount,
49 IOCTL_DISK_GET_DRIVE_GEOMETRY,
50 NULL,
51 0,
52 &DiskGeometry,
53 &Size,
54 Override);
55 if (!NT_SUCCESS(Status))
56 {
57 DPRINT("VfatBlockDeviceIoControl failed (%x)\n", Status);
58 return Status;
59 }
60
61 FatInfo.FixedMedia = DiskGeometry.MediaType == FixedMedia ? TRUE : FALSE;
62 if (DiskGeometry.MediaType == FixedMedia || DiskGeometry.MediaType == RemovableMedia)
63 {
64 // We have found a hard disk
65 Size = sizeof(PARTITION_INFORMATION);
66 Status = VfatBlockDeviceIoControl(DeviceToMount,
67 IOCTL_DISK_GET_PARTITION_INFO,
68 NULL,
69 0,
70 &PartitionInfo,
71 &Size,
72 Override);
73 if (!NT_SUCCESS(Status))
74 {
75 DPRINT("VfatBlockDeviceIoControl failed (%x)\n", Status);
76 return Status;
77 }
78
79 DPRINT("Partition Information:\n");
80 DPRINT("StartingOffset %I64x\n", PartitionInfo.StartingOffset.QuadPart / 512);
81 DPRINT("PartitionLength %I64x\n", PartitionInfo.PartitionLength.QuadPart / 512);
82 DPRINT("HiddenSectors %u\n", PartitionInfo.HiddenSectors);
83 DPRINT("PartitionNumber %u\n", PartitionInfo.PartitionNumber);
84 DPRINT("PartitionType %u\n", PartitionInfo.PartitionType);
85 DPRINT("BootIndicator %u\n", PartitionInfo.BootIndicator);
86 DPRINT("RecognizedPartition %u\n", PartitionInfo.RecognizedPartition);
87 DPRINT("RewritePartition %u\n", PartitionInfo.RewritePartition);
88 if (PartitionInfo.PartitionType)
89 {
90 if (PartitionInfo.PartitionType == PARTITION_FAT_12 ||
91 PartitionInfo.PartitionType == PARTITION_FAT_16 ||
92 PartitionInfo.PartitionType == PARTITION_HUGE ||
93 PartitionInfo.PartitionType == PARTITION_FAT32 ||
94 PartitionInfo.PartitionType == PARTITION_FAT32_XINT13 ||
95 PartitionInfo.PartitionType == PARTITION_XINT13)
96 {
97 PartitionInfoIsValid = TRUE;
98 *RecognizedFS = TRUE;
99 }
100 }
101 else if (DiskGeometry.MediaType == RemovableMedia &&
102 PartitionInfo.PartitionNumber > 0 &&
103 PartitionInfo.StartingOffset.QuadPart == 0 &&
104 PartitionInfo.PartitionLength.QuadPart > 0)
105 {
106 /* This is possible a removable media formated as super floppy */
107 PartitionInfoIsValid = TRUE;
108 *RecognizedFS = TRUE;
109 }
110 }
111 else
112 {
113 *RecognizedFS = TRUE;
114 }
115
116 if (*RecognizedFS)
117 {
118 Boot = ExAllocatePoolWithTag(NonPagedPool, DiskGeometry.BytesPerSector, TAG_BUFFER);
119 if (Boot == NULL)
120 {
121 return STATUS_INSUFFICIENT_RESOURCES;
122 }
123
124 Offset.QuadPart = 0;
125
126 /* Try to recognize FAT12/FAT16/FAT32 partitions */
127 Status = VfatReadDisk(DeviceToMount, &Offset, DiskGeometry.BytesPerSector, (PUCHAR) Boot, Override);
128 if (NT_SUCCESS(Status))
129 {
130 if (Boot->Signatur1 != 0xaa55)
131 {
132 *RecognizedFS = FALSE;
133 }
134
135 if (*RecognizedFS &&
136 Boot->BytesPerSector != 512 &&
137 Boot->BytesPerSector != 1024 &&
138 Boot->BytesPerSector != 2048 &&
139 Boot->BytesPerSector != 4096)
140 {
141 DPRINT1("BytesPerSector %u\n", Boot->BytesPerSector);
142 *RecognizedFS = FALSE;
143 }
144
145 if (*RecognizedFS &&
146 Boot->FATCount != 1 &&
147 Boot->FATCount != 2)
148 {
149 DPRINT1("FATCount %u\n", Boot->FATCount);
150 *RecognizedFS = FALSE;
151 }
152
153 if (*RecognizedFS &&
154 Boot->Media != 0xf0 &&
155 Boot->Media != 0xf8 &&
156 Boot->Media != 0xf9 &&
157 Boot->Media != 0xfa &&
158 Boot->Media != 0xfb &&
159 Boot->Media != 0xfc &&
160 Boot->Media != 0xfd &&
161 Boot->Media != 0xfe &&
162 Boot->Media != 0xff)
163 {
164 DPRINT1("Media %02x\n", Boot->Media);
165 *RecognizedFS = FALSE;
166 }
167
168 if (*RecognizedFS &&
169 Boot->SectorsPerCluster != 1 &&
170 Boot->SectorsPerCluster != 2 &&
171 Boot->SectorsPerCluster != 4 &&
172 Boot->SectorsPerCluster != 8 &&
173 Boot->SectorsPerCluster != 16 &&
174 Boot->SectorsPerCluster != 32 &&
175 Boot->SectorsPerCluster != 64 &&
176 Boot->SectorsPerCluster != 128)
177 {
178 DPRINT1("SectorsPerCluster %02x\n", Boot->SectorsPerCluster);
179 *RecognizedFS = FALSE;
180 }
181
182 if (*RecognizedFS &&
183 Boot->BytesPerSector * Boot->SectorsPerCluster > 64 * 1024)
184 {
185 DPRINT1("ClusterSize %d\n", Boot->BytesPerSector * Boot->SectorsPerCluster);
186 *RecognizedFS = FALSE;
187 }
188
189 if (*RecognizedFS)
190 {
191 FatInfo.VolumeID = Boot->VolumeID;
192 FatInfo.FATStart = Boot->ReservedSectors;
193 FatInfo.FATCount = Boot->FATCount;
194 FatInfo.FATSectors = Boot->FATSectors ? Boot->FATSectors : ((struct _BootSector32*) Boot)->FATSectors32;
195 FatInfo.BytesPerSector = Boot->BytesPerSector;
196 FatInfo.SectorsPerCluster = Boot->SectorsPerCluster;
197 FatInfo.BytesPerCluster = FatInfo.BytesPerSector * FatInfo.SectorsPerCluster;
198 FatInfo.rootDirectorySectors = ((Boot->RootEntries * 32) + Boot->BytesPerSector - 1) / Boot->BytesPerSector;
199 FatInfo.rootStart = FatInfo.FATStart + FatInfo.FATCount * FatInfo.FATSectors;
200 FatInfo.dataStart = FatInfo.rootStart + FatInfo.rootDirectorySectors;
201 FatInfo.Sectors = Sectors = Boot->Sectors ? Boot->Sectors : Boot->SectorsHuge;
202 Sectors -= Boot->ReservedSectors + FatInfo.FATCount * FatInfo.FATSectors + FatInfo.rootDirectorySectors;
203 FatInfo.NumberOfClusters = Sectors / Boot->SectorsPerCluster;
204 if (FatInfo.NumberOfClusters < 4085)
205 {
206 DPRINT("FAT12\n");
207 FatInfo.FatType = FAT12;
208 FatInfo.RootCluster = (FatInfo.rootStart - 1) / FatInfo.SectorsPerCluster;
209 RtlCopyMemory(&FatInfo.VolumeLabel, &Boot->VolumeLabel, sizeof(FatInfo.VolumeLabel));
210 }
211 else if (FatInfo.NumberOfClusters >= 65525)
212 {
213 DPRINT("FAT32\n");
214 FatInfo.FatType = FAT32;
215 FatInfo.RootCluster = ((struct _BootSector32*) Boot)->RootCluster;
216 FatInfo.rootStart = FatInfo.dataStart + ((FatInfo.RootCluster - 2) * FatInfo.SectorsPerCluster);
217 FatInfo.VolumeID = ((struct _BootSector32*) Boot)->VolumeID;
218 FatInfo.FSInfoSector = ((struct _BootSector32*) Boot)->FSInfoSector;
219 RtlCopyMemory(&FatInfo.VolumeLabel, &((struct _BootSector32*)Boot)->VolumeLabel, sizeof(FatInfo.VolumeLabel));
220 }
221 else
222 {
223 DPRINT("FAT16\n");
224 FatInfo.FatType = FAT16;
225 FatInfo.RootCluster = FatInfo.rootStart / FatInfo.SectorsPerCluster;
226 RtlCopyMemory(&FatInfo.VolumeLabel, &Boot->VolumeLabel, sizeof(FatInfo.VolumeLabel));
227 }
228
229 if (PartitionInfoIsValid &&
230 FatInfo.Sectors > PartitionInfo.PartitionLength.QuadPart / FatInfo.BytesPerSector)
231 {
232 *RecognizedFS = FALSE;
233 }
234
235 if (pFatInfo && *RecognizedFS)
236 {
237 *pFatInfo = FatInfo;
238 }
239 }
240 }
241
242 ExFreePoolWithTag(Boot, TAG_BUFFER);
243 }
244
245 if (!*RecognizedFS && PartitionInfoIsValid)
246 {
247 BootFatX = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct _BootSectorFatX), TAG_BUFFER);
248 if (BootFatX == NULL)
249 {
250 *RecognizedFS=FALSE;
251 return STATUS_INSUFFICIENT_RESOURCES;
252 }
253
254 Offset.QuadPart = 0;
255
256 /* Try to recognize FATX16/FATX32 partitions (Xbox) */
257 Status = VfatReadDisk(DeviceToMount, &Offset, sizeof(struct _BootSectorFatX), (PUCHAR) BootFatX, Override);
258 if (NT_SUCCESS(Status))
259 {
260 *RecognizedFS = TRUE;
261 if (BootFatX->SysType[0] != 'F' ||
262 BootFatX->SysType[1] != 'A' ||
263 BootFatX->SysType[2] != 'T' ||
264 BootFatX->SysType[3] != 'X')
265 {
266 DPRINT1("SysType %02X%02X%02X%02X (%c%c%c%c)\n",
267 BootFatX->SysType[0], BootFatX->SysType[1], BootFatX->SysType[2], BootFatX->SysType[3],
268 isprint(BootFatX->SysType[0]) ? BootFatX->SysType[0] : '.',
269 isprint(BootFatX->SysType[1]) ? BootFatX->SysType[1] : '.',
270 isprint(BootFatX->SysType[2]) ? BootFatX->SysType[2] : '.',
271 isprint(BootFatX->SysType[3]) ? BootFatX->SysType[3] : '.');
272
273 *RecognizedFS = FALSE;
274 }
275
276 if (*RecognizedFS &&
277 BootFatX->SectorsPerCluster != 1 &&
278 BootFatX->SectorsPerCluster != 2 &&
279 BootFatX->SectorsPerCluster != 4 &&
280 BootFatX->SectorsPerCluster != 8 &&
281 BootFatX->SectorsPerCluster != 16 &&
282 BootFatX->SectorsPerCluster != 32 &&
283 BootFatX->SectorsPerCluster != 64 &&
284 BootFatX->SectorsPerCluster != 128)
285 {
286 DPRINT1("SectorsPerCluster %lu\n", BootFatX->SectorsPerCluster);
287 *RecognizedFS=FALSE;
288 }
289
290 if (*RecognizedFS)
291 {
292 FatInfo.BytesPerSector = DiskGeometry.BytesPerSector;
293 FatInfo.SectorsPerCluster = BootFatX->SectorsPerCluster;
294 FatInfo.rootDirectorySectors = BootFatX->SectorsPerCluster;
295 FatInfo.BytesPerCluster = BootFatX->SectorsPerCluster * DiskGeometry.BytesPerSector;
296 FatInfo.Sectors = (ULONG)(PartitionInfo.PartitionLength.QuadPart / DiskGeometry.BytesPerSector);
297 if (FatInfo.Sectors / FatInfo.SectorsPerCluster < 65525)
298 {
299 DPRINT("FATX16\n");
300 FatInfo.FatType = FATX16;
301 }
302 else
303 {
304 DPRINT("FATX32\n");
305 FatInfo.FatType = FATX32;
306 }
307 FatInfo.VolumeID = BootFatX->VolumeID;
308 FatInfo.FATStart = sizeof(struct _BootSectorFatX) / DiskGeometry.BytesPerSector;
309 FatInfo.FATCount = BootFatX->FATCount;
310 FatInfo.FATSectors =
311 ROUND_UP(FatInfo.Sectors / FatInfo.SectorsPerCluster * (FatInfo.FatType == FATX16 ? 2 : 4), 4096) /
312 FatInfo.BytesPerSector;
313 FatInfo.rootStart = FatInfo.FATStart + FatInfo.FATCount * FatInfo.FATSectors;
314 FatInfo.RootCluster = (FatInfo.rootStart - 1) / FatInfo.SectorsPerCluster;
315 FatInfo.dataStart = FatInfo.rootStart + FatInfo.rootDirectorySectors;
316 FatInfo.NumberOfClusters = (FatInfo.Sectors - FatInfo.dataStart) / FatInfo.SectorsPerCluster;
317
318 if (pFatInfo && *RecognizedFS)
319 {
320 *pFatInfo = FatInfo;
321 }
322 }
323 }
324 ExFreePoolWithTag(BootFatX, TAG_BUFFER);
325 }
326
327 DPRINT("VfatHasFileSystem done\n");
328 return Status;
329 }
330
331 /*
332 * FUNCTION: Read the volume label
333 * WARNING: Read this comment carefully before using it (and using it wrong)
334 * Device parameter is expected to be the lower DO is start isn't 0
335 * otherwise, it is expected to be the VCB is start is 0
336 * Start parameter is expected to be, in bytes, the beginning of the root start.
337 * Set it to 0 if you wish to use the associated FCB with caching.
338 * In that specific case, Device parameter is expected to be the VCB!
339 * VolumeLabel parameter is expected to be a preallocated UNICODE_STRING (ie, with buffer)
340 * Its buffer has to be able to contain MAXIMUM_VOLUME_LABEL_LENGTH bytes
341 */
342 static
343 NTSTATUS
ReadVolumeLabel(PVOID Device,ULONG Start,BOOLEAN IsFatX,PUNICODE_STRING VolumeLabel)344 ReadVolumeLabel(
345 PVOID Device,
346 ULONG Start,
347 BOOLEAN IsFatX,
348 PUNICODE_STRING VolumeLabel)
349 {
350 PDEVICE_EXTENSION DeviceExt;
351 PDEVICE_OBJECT DeviceObject;
352 PVOID Context = NULL;
353 ULONG DirIndex = 0;
354 PDIR_ENTRY Entry;
355 PVFATFCB pFcb;
356 LARGE_INTEGER FileOffset;
357 ULONG SizeDirEntry;
358 ULONG EntriesPerPage;
359 OEM_STRING StringO;
360 BOOLEAN NoCache = (Start != 0);
361 PVOID Buffer;
362 NTSTATUS Status = STATUS_SUCCESS;
363
364 if (IsFatX)
365 {
366 SizeDirEntry = sizeof(FATX_DIR_ENTRY);
367 EntriesPerPage = FATX_ENTRIES_PER_PAGE;
368 }
369 else
370 {
371 SizeDirEntry = sizeof(FAT_DIR_ENTRY);
372 EntriesPerPage = FAT_ENTRIES_PER_PAGE;
373 }
374
375 FileOffset.QuadPart = Start;
376 if (!NoCache)
377 {
378 DeviceExt = Device;
379
380 /* FIXME: Check we really have a VCB
381 ASSERT();
382 */
383
384 ExAcquireResourceExclusiveLite(&DeviceExt->DirResource, TRUE);
385 pFcb = vfatOpenRootFCB(DeviceExt);
386 ExReleaseResourceLite(&DeviceExt->DirResource);
387
388 _SEH2_TRY
389 {
390 CcMapData(pFcb->FileObject, &FileOffset, SizeDirEntry, MAP_WAIT, &Context, (PVOID*)&Entry);
391 }
392 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
393 {
394 Status = _SEH2_GetExceptionCode();
395 }
396 _SEH2_END;
397 }
398 else
399 {
400 DeviceObject = Device;
401
402 ASSERT(DeviceObject->Type == 3);
403
404 Buffer = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, TAG_DIRENT);
405 if (Buffer != NULL)
406 {
407 Status = VfatReadDisk(DeviceObject, &FileOffset, PAGE_SIZE, (PUCHAR)Buffer, TRUE);
408 if (!NT_SUCCESS(Status))
409 {
410 ExFreePoolWithTag(Buffer, TAG_DIRENT);
411 }
412 else
413 {
414 Entry = Buffer;
415 }
416 }
417 else
418 {
419 Status = STATUS_INSUFFICIENT_RESOURCES;
420 }
421 }
422
423 if (NT_SUCCESS(Status))
424 {
425 while (TRUE)
426 {
427 if (ENTRY_VOLUME(IsFatX, Entry))
428 {
429 /* copy volume label */
430 if (IsFatX)
431 {
432 StringO.Buffer = (PCHAR)Entry->FatX.Filename;
433 StringO.MaximumLength = StringO.Length = Entry->FatX.FilenameLength;
434 RtlOemStringToUnicodeString(VolumeLabel, &StringO, FALSE);
435 }
436 else
437 {
438 vfat8Dot3ToString(&Entry->Fat, VolumeLabel);
439 }
440 break;
441 }
442 if (ENTRY_END(IsFatX, Entry))
443 {
444 break;
445 }
446 DirIndex++;
447 Entry = (PDIR_ENTRY)((ULONG_PTR)Entry + SizeDirEntry);
448 if ((DirIndex % EntriesPerPage) == 0)
449 {
450 FileOffset.u.LowPart += PAGE_SIZE;
451
452 if (!NoCache)
453 {
454 CcUnpinData(Context);
455
456 _SEH2_TRY
457 {
458 CcMapData(pFcb->FileObject, &FileOffset, SizeDirEntry, MAP_WAIT, &Context, (PVOID*)&Entry);
459 }
460 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
461 {
462 Status = _SEH2_GetExceptionCode();
463 }
464 _SEH2_END;
465 if (!NT_SUCCESS(Status))
466 {
467 Context = NULL;
468 break;
469 }
470 }
471 else
472 {
473 Status = VfatReadDisk(DeviceObject, &FileOffset, PAGE_SIZE, (PUCHAR)Buffer, TRUE);
474 if (!NT_SUCCESS(Status))
475 {
476 break;
477 }
478 Entry = Buffer;
479 }
480 }
481 }
482 if (Context)
483 {
484 CcUnpinData(Context);
485 }
486 else if (NoCache)
487 {
488 ExFreePoolWithTag(Buffer, TAG_DIRENT);
489 }
490 }
491
492 if (!NoCache)
493 {
494 ExAcquireResourceExclusiveLite(&DeviceExt->DirResource, TRUE);
495 vfatReleaseFCB(DeviceExt, pFcb);
496 ExReleaseResourceLite(&DeviceExt->DirResource);
497 }
498
499 return STATUS_SUCCESS;
500 }
501
502
503 /*
504 * FUNCTION: Mount the filesystem
505 */
506 static
507 NTSTATUS
VfatMount(PVFAT_IRP_CONTEXT IrpContext)508 VfatMount(
509 PVFAT_IRP_CONTEXT IrpContext)
510 {
511 PDEVICE_OBJECT DeviceObject = NULL;
512 PDEVICE_EXTENSION DeviceExt = NULL;
513 BOOLEAN RecognizedFS;
514 NTSTATUS Status;
515 PVFATFCB Fcb = NULL;
516 PVFATFCB VolumeFcb = NULL;
517 PDEVICE_OBJECT DeviceToMount;
518 PVPB Vpb;
519 UNICODE_STRING NameU = RTL_CONSTANT_STRING(L"\\$$Fat$$");
520 UNICODE_STRING VolumeNameU = RTL_CONSTANT_STRING(L"\\$$Volume$$");
521 UNICODE_STRING VolumeLabelU;
522 ULONG HashTableSize;
523 ULONG i;
524 FATINFO FatInfo;
525 BOOLEAN Dirty;
526
527 DPRINT("VfatMount(IrpContext %p)\n", IrpContext);
528
529 ASSERT(IrpContext);
530
531 if (IrpContext->DeviceObject != VfatGlobalData->DeviceObject)
532 {
533 Status = STATUS_INVALID_DEVICE_REQUEST;
534 goto ByeBye;
535 }
536
537 DeviceToMount = IrpContext->Stack->Parameters.MountVolume.DeviceObject;
538 Vpb = IrpContext->Stack->Parameters.MountVolume.Vpb;
539
540 Status = VfatHasFileSystem(DeviceToMount, &RecognizedFS, &FatInfo, FALSE);
541 if (!NT_SUCCESS(Status))
542 {
543 goto ByeBye;
544 }
545
546 if (RecognizedFS == FALSE)
547 {
548 DPRINT("VFAT: Unrecognized Volume\n");
549 Status = STATUS_UNRECOGNIZED_VOLUME;
550 goto ByeBye;
551 }
552
553 /* Use prime numbers for the table size */
554 if (FatInfo.FatType == FAT12)
555 {
556 HashTableSize = 4099; // 4096 = 4 * 1024
557 }
558 else if (FatInfo.FatType == FAT16 ||
559 FatInfo.FatType == FATX16)
560 {
561 HashTableSize = 16411; // 16384 = 16 * 1024
562 }
563 else
564 {
565 HashTableSize = 65537; // 65536 = 64 * 1024;
566 }
567 DPRINT("VFAT: Recognized volume\n");
568 Status = IoCreateDevice(VfatGlobalData->DriverObject,
569 ROUND_UP(sizeof (DEVICE_EXTENSION), sizeof(ULONG)) + sizeof(HASHENTRY*) * HashTableSize,
570 NULL,
571 FILE_DEVICE_DISK_FILE_SYSTEM,
572 DeviceToMount->Characteristics,
573 FALSE,
574 &DeviceObject);
575 if (!NT_SUCCESS(Status))
576 {
577 goto ByeBye;
578 }
579
580 DeviceExt = DeviceObject->DeviceExtension;
581 RtlZeroMemory(DeviceExt, ROUND_UP(sizeof(DEVICE_EXTENSION), sizeof(ULONG)) + sizeof(HASHENTRY*) * HashTableSize);
582 DeviceExt->FcbHashTable = (HASHENTRY**)((ULONG_PTR)DeviceExt + ROUND_UP(sizeof(DEVICE_EXTENSION), sizeof(ULONG)));
583 DeviceExt->HashTableSize = HashTableSize;
584 DeviceExt->VolumeDevice = DeviceObject;
585
586 KeInitializeSpinLock(&DeviceExt->OverflowQueueSpinLock);
587 InitializeListHead(&DeviceExt->OverflowQueue);
588 DeviceExt->OverflowQueueCount = 0;
589 DeviceExt->PostedRequestCount = 0;
590
591 /* use same vpb as device disk */
592 DeviceObject->Vpb = Vpb;
593 DeviceToMount->Vpb = Vpb;
594
595 RtlCopyMemory(&DeviceExt->FatInfo, &FatInfo, sizeof(FATINFO));
596
597 DPRINT("BytesPerSector: %u\n", DeviceExt->FatInfo.BytesPerSector);
598 DPRINT("SectorsPerCluster: %u\n", DeviceExt->FatInfo.SectorsPerCluster);
599 DPRINT("FATCount: %u\n", DeviceExt->FatInfo.FATCount);
600 DPRINT("FATSectors: %u\n", DeviceExt->FatInfo.FATSectors);
601 DPRINT("RootStart: %u\n", DeviceExt->FatInfo.rootStart);
602 DPRINT("DataStart: %u\n", DeviceExt->FatInfo.dataStart);
603 if (DeviceExt->FatInfo.FatType == FAT32)
604 {
605 DPRINT("RootCluster: %u\n", DeviceExt->FatInfo.RootCluster);
606 }
607
608 switch (DeviceExt->FatInfo.FatType)
609 {
610 case FAT12:
611 DeviceExt->GetNextCluster = FAT12GetNextCluster;
612 DeviceExt->FindAndMarkAvailableCluster = FAT12FindAndMarkAvailableCluster;
613 DeviceExt->WriteCluster = FAT12WriteCluster;
614 /* We don't define dirty bit functions here
615 * FAT12 doesn't have such bit and they won't get called
616 */
617 break;
618
619 case FAT16:
620 case FATX16:
621 DeviceExt->GetNextCluster = FAT16GetNextCluster;
622 DeviceExt->FindAndMarkAvailableCluster = FAT16FindAndMarkAvailableCluster;
623 DeviceExt->WriteCluster = FAT16WriteCluster;
624 DeviceExt->GetDirtyStatus = FAT16GetDirtyStatus;
625 DeviceExt->SetDirtyStatus = FAT16SetDirtyStatus;
626 break;
627
628 case FAT32:
629 case FATX32:
630 DeviceExt->GetNextCluster = FAT32GetNextCluster;
631 DeviceExt->FindAndMarkAvailableCluster = FAT32FindAndMarkAvailableCluster;
632 DeviceExt->WriteCluster = FAT32WriteCluster;
633 DeviceExt->GetDirtyStatus = FAT32GetDirtyStatus;
634 DeviceExt->SetDirtyStatus = FAT32SetDirtyStatus;
635 break;
636 }
637
638 if (DeviceExt->FatInfo.FatType == FATX16 ||
639 DeviceExt->FatInfo.FatType == FATX32)
640 {
641 DeviceExt->Flags |= VCB_IS_FATX;
642 DeviceExt->BaseDateYear = 2000;
643 RtlCopyMemory(&DeviceExt->Dispatch, &FatXDispatch, sizeof(VFAT_DISPATCH));
644 }
645 else
646 {
647 DeviceExt->BaseDateYear = 1980;
648 RtlCopyMemory(&DeviceExt->Dispatch, &FatDispatch, sizeof(VFAT_DISPATCH));
649 }
650
651 DeviceExt->StorageDevice = DeviceToMount;
652 DeviceExt->StorageDevice->Vpb->DeviceObject = DeviceObject;
653 DeviceExt->StorageDevice->Vpb->RealDevice = DeviceExt->StorageDevice;
654 DeviceExt->StorageDevice->Vpb->Flags |= VPB_MOUNTED;
655 DeviceObject->StackSize = DeviceExt->StorageDevice->StackSize + 1;
656 DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
657
658 DPRINT("FsDeviceObject %p\n", DeviceObject);
659
660 /* Initialize this resource early ... it's used in VfatCleanup */
661 ExInitializeResourceLite(&DeviceExt->DirResource);
662
663 DeviceExt->IoVPB = DeviceObject->Vpb;
664 DeviceExt->SpareVPB = ExAllocatePoolWithTag(NonPagedPool, sizeof(VPB), TAG_VPB);
665 if (DeviceExt->SpareVPB == NULL)
666 {
667 Status = STATUS_INSUFFICIENT_RESOURCES;
668 goto ByeBye;
669 }
670
671 DeviceExt->Statistics = ExAllocatePoolWithTag(NonPagedPool,
672 sizeof(STATISTICS) * VfatGlobalData->NumberProcessors,
673 TAG_STATS);
674 if (DeviceExt->Statistics == NULL)
675 {
676 Status = STATUS_INSUFFICIENT_RESOURCES;
677 goto ByeBye;
678 }
679
680 RtlZeroMemory(DeviceExt->Statistics, sizeof(STATISTICS) * VfatGlobalData->NumberProcessors);
681 for (i = 0; i < VfatGlobalData->NumberProcessors; ++i)
682 {
683 DeviceExt->Statistics[i].Base.FileSystemType = FILESYSTEM_STATISTICS_TYPE_FAT;
684 DeviceExt->Statistics[i].Base.Version = 1;
685 DeviceExt->Statistics[i].Base.SizeOfCompleteStructure = sizeof(STATISTICS);
686 }
687
688 DeviceExt->FATFileObject = IoCreateStreamFileObject(NULL, DeviceExt->StorageDevice);
689 Fcb = vfatNewFCB(DeviceExt, &NameU);
690 if (Fcb == NULL)
691 {
692 Status = STATUS_INSUFFICIENT_RESOURCES;
693 goto ByeBye;
694 }
695
696 Status = vfatAttachFCBToFileObject(DeviceExt, Fcb, DeviceExt->FATFileObject);
697 if (!NT_SUCCESS(Status))
698 goto ByeBye;
699
700 DeviceExt->FATFileObject->PrivateCacheMap = NULL;
701 Fcb->FileObject = DeviceExt->FATFileObject;
702
703 Fcb->Flags = FCB_IS_FAT;
704 Fcb->RFCB.FileSize.QuadPart = DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector;
705 Fcb->RFCB.ValidDataLength = Fcb->RFCB.FileSize;
706 Fcb->RFCB.AllocationSize = Fcb->RFCB.FileSize;
707
708 _SEH2_TRY
709 {
710 CcInitializeCacheMap(DeviceExt->FATFileObject,
711 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
712 TRUE,
713 &VfatGlobalData->CacheMgrCallbacks,
714 Fcb);
715 }
716 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
717 {
718 Status = _SEH2_GetExceptionCode();
719 goto ByeBye;
720 }
721 _SEH2_END;
722
723 DeviceExt->LastAvailableCluster = 2;
724 CountAvailableClusters(DeviceExt, NULL);
725 ExInitializeResourceLite(&DeviceExt->FatResource);
726
727 InitializeListHead(&DeviceExt->FcbListHead);
728
729 VolumeFcb = vfatNewFCB(DeviceExt, &VolumeNameU);
730 if (VolumeFcb == NULL)
731 {
732 Status = STATUS_INSUFFICIENT_RESOURCES;
733 goto ByeBye;
734 }
735
736 VolumeFcb->Flags = FCB_IS_VOLUME;
737 VolumeFcb->RFCB.FileSize.QuadPart = (LONGLONG) DeviceExt->FatInfo.Sectors * DeviceExt->FatInfo.BytesPerSector;
738 VolumeFcb->RFCB.ValidDataLength = VolumeFcb->RFCB.FileSize;
739 VolumeFcb->RFCB.AllocationSize = VolumeFcb->RFCB.FileSize;
740 DeviceExt->VolumeFcb = VolumeFcb;
741
742 ExAcquireResourceExclusiveLite(&VfatGlobalData->VolumeListLock, TRUE);
743 InsertHeadList(&VfatGlobalData->VolumeListHead, &DeviceExt->VolumeListEntry);
744 ExReleaseResourceLite(&VfatGlobalData->VolumeListLock);
745
746 /* read serial number */
747 DeviceObject->Vpb->SerialNumber = DeviceExt->FatInfo.VolumeID;
748
749 /* read volume label */
750 VolumeLabelU.Buffer = DeviceObject->Vpb->VolumeLabel;
751 VolumeLabelU.Length = 0;
752 VolumeLabelU.MaximumLength = sizeof(DeviceObject->Vpb->VolumeLabel);
753 ReadVolumeLabel(DeviceExt, 0, vfatVolumeIsFatX(DeviceExt), &VolumeLabelU);
754 Vpb->VolumeLabelLength = VolumeLabelU.Length;
755
756 /* read dirty bit status */
757 Status = GetDirtyStatus(DeviceExt, &Dirty);
758 if (NT_SUCCESS(Status))
759 {
760 /* The volume wasn't dirty, it was properly dismounted */
761 if (!Dirty)
762 {
763 /* Mark it dirty now! */
764 SetDirtyStatus(DeviceExt, TRUE);
765 VolumeFcb->Flags |= VCB_CLEAR_DIRTY;
766 }
767 else
768 {
769 DPRINT1("Mounting a dirty volume\n");
770 }
771 }
772
773 VolumeFcb->Flags |= VCB_IS_DIRTY;
774 if (BooleanFlagOn(Vpb->RealDevice->Flags, DO_SYSTEM_BOOT_PARTITION))
775 {
776 SetFlag(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE);
777 }
778
779 /* Initialize the notify list and synchronization object */
780 InitializeListHead(&DeviceExt->NotifyList);
781 FsRtlNotifyInitializeSync(&DeviceExt->NotifySync);
782
783 /* The VCB is OK for usage */
784 SetFlag(DeviceExt->Flags, VCB_GOOD);
785
786 /* Send the mount notification */
787 FsRtlNotifyVolumeEvent(DeviceExt->FATFileObject, FSRTL_VOLUME_MOUNT);
788
789 DPRINT("Mount success\n");
790
791 Status = STATUS_SUCCESS;
792
793 ByeBye:
794 if (!NT_SUCCESS(Status))
795 {
796 /* Cleanup */
797 if (DeviceExt && DeviceExt->FATFileObject)
798 {
799 LARGE_INTEGER Zero = {{0,0}};
800 PVFATCCB Ccb = (PVFATCCB)DeviceExt->FATFileObject->FsContext2;
801
802 CcUninitializeCacheMap(DeviceExt->FATFileObject,
803 &Zero,
804 NULL);
805 ObDereferenceObject(DeviceExt->FATFileObject);
806 if (Ccb)
807 vfatDestroyCCB(Ccb);
808 DeviceExt->FATFileObject = NULL;
809 }
810 if (Fcb)
811 vfatDestroyFCB(Fcb);
812 if (DeviceExt && DeviceExt->SpareVPB)
813 ExFreePoolWithTag(DeviceExt->SpareVPB, TAG_VPB);
814 if (DeviceExt && DeviceExt->Statistics)
815 ExFreePoolWithTag(DeviceExt->Statistics, TAG_STATS);
816 if (DeviceObject)
817 IoDeleteDevice(DeviceObject);
818 }
819
820 return Status;
821 }
822
823
824 /*
825 * FUNCTION: Verify the filesystem
826 */
827 static
828 NTSTATUS
VfatVerify(PVFAT_IRP_CONTEXT IrpContext)829 VfatVerify(
830 PVFAT_IRP_CONTEXT IrpContext)
831 {
832 PDEVICE_OBJECT DeviceToVerify;
833 NTSTATUS Status;
834 FATINFO FatInfo;
835 BOOLEAN RecognizedFS;
836 PDEVICE_EXTENSION DeviceExt;
837 BOOLEAN AllowRaw;
838 PVPB Vpb;
839 ULONG ChangeCount, BufSize = sizeof(ChangeCount);
840
841 DPRINT("VfatVerify(IrpContext %p)\n", IrpContext);
842
843 DeviceToVerify = IrpContext->Stack->Parameters.VerifyVolume.DeviceObject;
844 DeviceExt = DeviceToVerify->DeviceExtension;
845 Vpb = IrpContext->Stack->Parameters.VerifyVolume.Vpb;
846 AllowRaw = BooleanFlagOn(IrpContext->Stack->Flags, SL_ALLOW_RAW_MOUNT);
847
848 if (!BooleanFlagOn(Vpb->RealDevice->Flags, DO_VERIFY_VOLUME))
849 {
850 DPRINT("Already verified\n");
851 return STATUS_SUCCESS;
852 }
853
854 Status = VfatBlockDeviceIoControl(DeviceExt->StorageDevice,
855 IOCTL_DISK_CHECK_VERIFY,
856 NULL,
857 0,
858 &ChangeCount,
859 &BufSize,
860 TRUE);
861 if (!NT_SUCCESS(Status) && Status != STATUS_VERIFY_REQUIRED)
862 {
863 DPRINT("VfatBlockDeviceIoControl() failed (Status %lx)\n", Status);
864 Status = (AllowRaw ? STATUS_WRONG_VOLUME : Status);
865 }
866 else
867 {
868 Status = VfatHasFileSystem(DeviceExt->StorageDevice, &RecognizedFS, &FatInfo, TRUE);
869 if (!NT_SUCCESS(Status) || RecognizedFS == FALSE)
870 {
871 if (NT_SUCCESS(Status) || AllowRaw)
872 {
873 Status = STATUS_WRONG_VOLUME;
874 }
875 }
876 else if (sizeof(FATINFO) == RtlCompareMemory(&FatInfo, &DeviceExt->FatInfo, sizeof(FATINFO)))
877 {
878 WCHAR BufferU[MAXIMUM_VOLUME_LABEL_LENGTH / sizeof(WCHAR)];
879 UNICODE_STRING VolumeLabelU;
880 UNICODE_STRING VpbLabelU;
881
882 VolumeLabelU.Buffer = BufferU;
883 VolumeLabelU.Length = 0;
884 VolumeLabelU.MaximumLength = sizeof(BufferU);
885 Status = ReadVolumeLabel(DeviceExt->StorageDevice, FatInfo.rootStart * FatInfo.BytesPerSector, (FatInfo.FatType >= FATX16), &VolumeLabelU);
886 if (!NT_SUCCESS(Status))
887 {
888 if (AllowRaw)
889 {
890 Status = STATUS_WRONG_VOLUME;
891 }
892 }
893 else
894 {
895 VpbLabelU.Buffer = Vpb->VolumeLabel;
896 VpbLabelU.Length = Vpb->VolumeLabelLength;
897 VpbLabelU.MaximumLength = sizeof(Vpb->VolumeLabel);
898
899 if (RtlCompareUnicodeString(&VpbLabelU, &VolumeLabelU, FALSE) != 0)
900 {
901 Status = STATUS_WRONG_VOLUME;
902 }
903 else
904 {
905 DPRINT1("Same volume\n");
906 }
907 }
908 }
909 else
910 {
911 Status = STATUS_WRONG_VOLUME;
912 }
913 }
914
915 Vpb->RealDevice->Flags &= ~DO_VERIFY_VOLUME;
916
917 return Status;
918 }
919
920
921 static
922 NTSTATUS
VfatGetVolumeBitmap(PVFAT_IRP_CONTEXT IrpContext)923 VfatGetVolumeBitmap(
924 PVFAT_IRP_CONTEXT IrpContext)
925 {
926 DPRINT("VfatGetVolumeBitmap (IrpContext %p)\n", IrpContext);
927 return STATUS_INVALID_DEVICE_REQUEST;
928 }
929
930
931 static
932 NTSTATUS
VfatGetRetrievalPointers(PVFAT_IRP_CONTEXT IrpContext)933 VfatGetRetrievalPointers(
934 PVFAT_IRP_CONTEXT IrpContext)
935 {
936 PIO_STACK_LOCATION Stack;
937 LARGE_INTEGER Vcn;
938 PRETRIEVAL_POINTERS_BUFFER RetrievalPointers;
939 PFILE_OBJECT FileObject;
940 ULONG MaxExtentCount;
941 PVFATFCB Fcb;
942 PDEVICE_EXTENSION DeviceExt;
943 ULONG FirstCluster;
944 ULONG CurrentCluster;
945 ULONG LastCluster;
946 NTSTATUS Status;
947
948 DPRINT("VfatGetRetrievalPointers(IrpContext %p)\n", IrpContext);
949
950 DeviceExt = IrpContext->DeviceExt;
951 FileObject = IrpContext->FileObject;
952 Stack = IrpContext->Stack;
953 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(STARTING_VCN_INPUT_BUFFER) ||
954 Stack->Parameters.DeviceIoControl.Type3InputBuffer == NULL)
955 {
956 return STATUS_INVALID_PARAMETER;
957 }
958
959 if (IrpContext->Irp->UserBuffer == NULL ||
960 Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(RETRIEVAL_POINTERS_BUFFER))
961 {
962 return STATUS_BUFFER_TOO_SMALL;
963 }
964
965 Fcb = FileObject->FsContext;
966
967 ExAcquireResourceSharedLite(&Fcb->MainResource, TRUE);
968
969 Vcn = ((PSTARTING_VCN_INPUT_BUFFER)Stack->Parameters.DeviceIoControl.Type3InputBuffer)->StartingVcn;
970 RetrievalPointers = IrpContext->Irp->UserBuffer;
971
972 MaxExtentCount = ((Stack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(RetrievalPointers->ExtentCount) - sizeof(RetrievalPointers->StartingVcn)) / sizeof(RetrievalPointers->Extents[0]));
973
974 if (Vcn.QuadPart >= Fcb->RFCB.AllocationSize.QuadPart / DeviceExt->FatInfo.BytesPerCluster)
975 {
976 Status = STATUS_INVALID_PARAMETER;
977 goto ByeBye;
978 }
979
980 CurrentCluster = FirstCluster = vfatDirEntryGetFirstCluster(DeviceExt, &Fcb->entry);
981 Status = OffsetToCluster(DeviceExt, FirstCluster,
982 Vcn.u.LowPart * DeviceExt->FatInfo.BytesPerCluster,
983 &CurrentCluster, FALSE);
984 if (!NT_SUCCESS(Status))
985 {
986 goto ByeBye;
987 }
988
989 RetrievalPointers->StartingVcn = Vcn;
990 RetrievalPointers->ExtentCount = 0;
991 RetrievalPointers->Extents[0].Lcn.u.HighPart = 0;
992 RetrievalPointers->Extents[0].Lcn.u.LowPart = CurrentCluster - 2;
993 LastCluster = 0;
994 while (CurrentCluster != 0xffffffff && RetrievalPointers->ExtentCount < MaxExtentCount)
995 {
996 LastCluster = CurrentCluster;
997 Status = NextCluster(DeviceExt, CurrentCluster, &CurrentCluster, FALSE);
998 Vcn.QuadPart++;
999 if (!NT_SUCCESS(Status))
1000 {
1001 goto ByeBye;
1002 }
1003
1004 if (LastCluster + 1 != CurrentCluster)
1005 {
1006 RetrievalPointers->Extents[RetrievalPointers->ExtentCount].NextVcn = Vcn;
1007 RetrievalPointers->ExtentCount++;
1008 if (RetrievalPointers->ExtentCount < MaxExtentCount)
1009 {
1010 RetrievalPointers->Extents[RetrievalPointers->ExtentCount].Lcn.u.HighPart = 0;
1011 RetrievalPointers->Extents[RetrievalPointers->ExtentCount].Lcn.u.LowPart = CurrentCluster - 2;
1012 }
1013 }
1014 }
1015
1016 IrpContext->Irp->IoStatus.Information = sizeof(RETRIEVAL_POINTERS_BUFFER) + (sizeof(RetrievalPointers->Extents[0]) * (RetrievalPointers->ExtentCount - 1));
1017 Status = STATUS_SUCCESS;
1018
1019 ByeBye:
1020 ExReleaseResourceLite(&Fcb->MainResource);
1021
1022 return Status;
1023 }
1024
1025 static
1026 NTSTATUS
VfatMoveFile(PVFAT_IRP_CONTEXT IrpContext)1027 VfatMoveFile(
1028 PVFAT_IRP_CONTEXT IrpContext)
1029 {
1030 DPRINT("VfatMoveFile(IrpContext %p)\n", IrpContext);
1031 return STATUS_INVALID_DEVICE_REQUEST;
1032 }
1033
1034 static
1035 NTSTATUS
VfatIsVolumeDirty(PVFAT_IRP_CONTEXT IrpContext)1036 VfatIsVolumeDirty(
1037 PVFAT_IRP_CONTEXT IrpContext)
1038 {
1039 PULONG Flags;
1040
1041 DPRINT("VfatIsVolumeDirty(IrpContext %p)\n", IrpContext);
1042
1043 if (IrpContext->Stack->Parameters.FileSystemControl.OutputBufferLength != sizeof(ULONG))
1044 return STATUS_INVALID_BUFFER_SIZE;
1045 else if (!IrpContext->Irp->AssociatedIrp.SystemBuffer)
1046 return STATUS_INVALID_USER_BUFFER;
1047
1048 Flags = (PULONG)IrpContext->Irp->AssociatedIrp.SystemBuffer;
1049 *Flags = 0;
1050
1051 if (BooleanFlagOn(IrpContext->DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY) &&
1052 !BooleanFlagOn(IrpContext->DeviceExt->VolumeFcb->Flags, VCB_CLEAR_DIRTY))
1053 {
1054 *Flags |= VOLUME_IS_DIRTY;
1055 }
1056
1057 IrpContext->Irp->IoStatus.Information = sizeof(ULONG);
1058
1059 return STATUS_SUCCESS;
1060 }
1061
1062 static
1063 NTSTATUS
VfatMarkVolumeDirty(PVFAT_IRP_CONTEXT IrpContext)1064 VfatMarkVolumeDirty(
1065 PVFAT_IRP_CONTEXT IrpContext)
1066 {
1067 PDEVICE_EXTENSION DeviceExt;
1068 NTSTATUS Status = STATUS_SUCCESS;
1069
1070 DPRINT("VfatMarkVolumeDirty(IrpContext %p)\n", IrpContext);
1071 DeviceExt = IrpContext->DeviceExt;
1072
1073 if (!BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY))
1074 {
1075 Status = SetDirtyStatus(DeviceExt, TRUE);
1076 }
1077
1078 DeviceExt->VolumeFcb->Flags &= ~VCB_CLEAR_DIRTY;
1079
1080 return Status;
1081 }
1082
1083 static
1084 NTSTATUS
VfatLockOrUnlockVolume(PVFAT_IRP_CONTEXT IrpContext,BOOLEAN Lock)1085 VfatLockOrUnlockVolume(
1086 PVFAT_IRP_CONTEXT IrpContext,
1087 BOOLEAN Lock)
1088 {
1089 PFILE_OBJECT FileObject;
1090 PDEVICE_EXTENSION DeviceExt;
1091 PVFATFCB Fcb;
1092 PVPB Vpb;
1093
1094 DPRINT("VfatLockOrUnlockVolume(%p, %d)\n", IrpContext, Lock);
1095
1096 DeviceExt = IrpContext->DeviceExt;
1097 FileObject = IrpContext->FileObject;
1098 Fcb = FileObject->FsContext;
1099 Vpb = DeviceExt->FATFileObject->Vpb;
1100
1101 /* Only allow locking with the volume open */
1102 if (!BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME))
1103 {
1104 return STATUS_ACCESS_DENIED;
1105 }
1106
1107 /* Bail out if it's already in the demanded state */
1108 if ((BooleanFlagOn(DeviceExt->Flags, VCB_VOLUME_LOCKED) && Lock) ||
1109 (!BooleanFlagOn(DeviceExt->Flags, VCB_VOLUME_LOCKED) && !Lock))
1110 {
1111 return STATUS_ACCESS_DENIED;
1112 }
1113
1114 /* Bail out if it's already in the demanded state */
1115 if ((BooleanFlagOn(Vpb->Flags, VPB_LOCKED) && Lock) ||
1116 (!BooleanFlagOn(Vpb->Flags, VPB_LOCKED) && !Lock))
1117 {
1118 return STATUS_ACCESS_DENIED;
1119 }
1120
1121 if (Lock)
1122 {
1123 FsRtlNotifyVolumeEvent(IrpContext->Stack->FileObject, FSRTL_VOLUME_LOCK);
1124 }
1125
1126 /* Deny locking if we're not alone */
1127 if (Lock && DeviceExt->OpenHandleCount != 1)
1128 {
1129 PLIST_ENTRY ListEntry;
1130
1131 #if 1
1132 /* FIXME: Hack that allows locking the system volume on
1133 * boot so that autochk can run properly
1134 * That hack is, on purpose, really restrictive
1135 * it will only allow locking with two directories
1136 * open: current directory of smss and autochk.
1137 */
1138 BOOLEAN ForceLock = TRUE;
1139 ULONG HandleCount = 0;
1140
1141 /* Only allow boot volume */
1142 if (BooleanFlagOn(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE))
1143 {
1144 /* We'll browse all the FCB */
1145 ListEntry = DeviceExt->FcbListHead.Flink;
1146 while (ListEntry != &DeviceExt->FcbListHead)
1147 {
1148 Fcb = CONTAINING_RECORD(ListEntry, VFATFCB, FcbListEntry);
1149 ListEntry = ListEntry->Flink;
1150
1151 /* If no handle: that FCB is no problem for locking
1152 * so ignore it
1153 */
1154 if (Fcb->OpenHandleCount == 0)
1155 {
1156 continue;
1157 }
1158
1159 /* Not a dir? We're no longer at boot */
1160 if (!vfatFCBIsDirectory(Fcb))
1161 {
1162 ForceLock = FALSE;
1163 break;
1164 }
1165
1166 /* If we have cached initialized and several handles, we're
1167 not in the boot case
1168 */
1169 if (Fcb->FileObject != NULL && Fcb->OpenHandleCount > 1)
1170 {
1171 ForceLock = FALSE;
1172 break;
1173 }
1174
1175 /* Count the handles */
1176 HandleCount += Fcb->OpenHandleCount;
1177 /* More than two handles? Then, we're not booting anymore */
1178 if (HandleCount > 2)
1179 {
1180 ForceLock = FALSE;
1181 break;
1182 }
1183 }
1184 }
1185 else
1186 {
1187 ForceLock = FALSE;
1188 }
1189
1190 /* Here comes the hack, ignore the failure! */
1191 if (!ForceLock)
1192 {
1193 #endif
1194
1195 DPRINT1("Can't lock: %u opened\n", DeviceExt->OpenHandleCount);
1196
1197 ListEntry = DeviceExt->FcbListHead.Flink;
1198 while (ListEntry != &DeviceExt->FcbListHead)
1199 {
1200 Fcb = CONTAINING_RECORD(ListEntry, VFATFCB, FcbListEntry);
1201 ListEntry = ListEntry->Flink;
1202
1203 if (Fcb->OpenHandleCount > 0)
1204 {
1205 DPRINT1("Opened (%u - %u): %wZ\n", Fcb->OpenHandleCount, Fcb->RefCount, &Fcb->PathNameU);
1206 }
1207 }
1208
1209 FsRtlNotifyVolumeEvent(IrpContext->Stack->FileObject, FSRTL_VOLUME_LOCK_FAILED);
1210
1211 return STATUS_ACCESS_DENIED;
1212
1213 #if 1
1214 /* End of the hack: be verbose about its usage,
1215 * just in case we would mess up everything!
1216 */
1217 }
1218 else
1219 {
1220 DPRINT1("HACK: Using lock-hack!\n");
1221 }
1222 #endif
1223 }
1224
1225 /* Finally, proceed */
1226 if (Lock)
1227 {
1228 /* Flush volume & files */
1229 VfatFlushVolume(DeviceExt, DeviceExt->VolumeFcb);
1230
1231 /* The volume is now clean */
1232 if (BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_CLEAR_DIRTY) &&
1233 BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY))
1234 {
1235 /* Drop the dirty bit */
1236 if (NT_SUCCESS(SetDirtyStatus(DeviceExt, FALSE)))
1237 ClearFlag(DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY);
1238 }
1239
1240 DeviceExt->Flags |= VCB_VOLUME_LOCKED;
1241 Vpb->Flags |= VPB_LOCKED;
1242 }
1243 else
1244 {
1245 DeviceExt->Flags &= ~VCB_VOLUME_LOCKED;
1246 Vpb->Flags &= ~VPB_LOCKED;
1247
1248 FsRtlNotifyVolumeEvent(IrpContext->Stack->FileObject, FSRTL_VOLUME_UNLOCK);
1249 }
1250
1251 return STATUS_SUCCESS;
1252 }
1253
1254 static
1255 NTSTATUS
VfatDismountVolume(PVFAT_IRP_CONTEXT IrpContext)1256 VfatDismountVolume(
1257 PVFAT_IRP_CONTEXT IrpContext)
1258 {
1259 PDEVICE_EXTENSION DeviceExt;
1260 PLIST_ENTRY NextEntry;
1261 PVFATFCB Fcb;
1262 PFILE_OBJECT FileObject;
1263
1264 DPRINT("VfatDismountVolume(%p)\n", IrpContext);
1265
1266 DeviceExt = IrpContext->DeviceExt;
1267 FileObject = IrpContext->FileObject;
1268
1269 /* We HAVE to be locked. Windows also allows dismount with no lock
1270 * but we're here mainly for 1st stage, so KISS
1271 */
1272 if (!BooleanFlagOn(DeviceExt->Flags, VCB_VOLUME_LOCKED))
1273 {
1274 return STATUS_ACCESS_DENIED;
1275 }
1276
1277 /* Deny dismount of boot volume */
1278 if (BooleanFlagOn(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE))
1279 {
1280 return STATUS_ACCESS_DENIED;
1281 }
1282
1283 /* Race condition? */
1284 if (BooleanFlagOn(DeviceExt->Flags, VCB_DISMOUNT_PENDING))
1285 {
1286 return STATUS_VOLUME_DISMOUNTED;
1287 }
1288
1289 /* Notify we'll dismount. Pass that point there's no reason we fail */
1290 FsRtlNotifyVolumeEvent(IrpContext->Stack->FileObject, FSRTL_VOLUME_DISMOUNT);
1291
1292 ExAcquireResourceExclusiveLite(&DeviceExt->FatResource, TRUE);
1293
1294 /* Flush volume & files */
1295 VfatFlushVolume(DeviceExt, (PVFATFCB)FileObject->FsContext);
1296
1297 /* The volume is now clean */
1298 if (BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_CLEAR_DIRTY) &&
1299 BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY))
1300 {
1301 /* Drop the dirty bit */
1302 if (NT_SUCCESS(SetDirtyStatus(DeviceExt, FALSE)))
1303 DeviceExt->VolumeFcb->Flags &= ~VCB_IS_DIRTY;
1304 }
1305
1306 /* Rebrowse the FCB in order to free them now */
1307 while (!IsListEmpty(&DeviceExt->FcbListHead))
1308 {
1309 NextEntry = RemoveTailList(&DeviceExt->FcbListHead);
1310 Fcb = CONTAINING_RECORD(NextEntry, VFATFCB, FcbListEntry);
1311
1312 if (Fcb == DeviceExt->RootFcb)
1313 DeviceExt->RootFcb = NULL;
1314 else if (Fcb == DeviceExt->VolumeFcb)
1315 DeviceExt->VolumeFcb = NULL;
1316
1317 vfatDestroyFCB(Fcb);
1318 }
1319
1320 /* We are uninitializing, the VCB cannot be used anymore */
1321 ClearFlag(DeviceExt->Flags, VCB_GOOD);
1322
1323 /* Mark we're being dismounted */
1324 DeviceExt->Flags |= VCB_DISMOUNT_PENDING;
1325 #ifndef ENABLE_SWAPOUT
1326 IrpContext->DeviceObject->Vpb->Flags &= ~VPB_MOUNTED;
1327 #endif
1328
1329 ExReleaseResourceLite(&DeviceExt->FatResource);
1330
1331 return STATUS_SUCCESS;
1332 }
1333
1334 static
1335 NTSTATUS
VfatGetStatistics(PVFAT_IRP_CONTEXT IrpContext)1336 VfatGetStatistics(
1337 PVFAT_IRP_CONTEXT IrpContext)
1338 {
1339 PVOID Buffer;
1340 ULONG Length;
1341 NTSTATUS Status;
1342 PDEVICE_EXTENSION DeviceExt;
1343
1344 DeviceExt = IrpContext->DeviceExt;
1345 Length = IrpContext->Stack->Parameters.FileSystemControl.OutputBufferLength;
1346 Buffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
1347
1348 if (Length < sizeof(FILESYSTEM_STATISTICS))
1349 {
1350 return STATUS_BUFFER_TOO_SMALL;
1351 }
1352
1353 if (Buffer == NULL)
1354 {
1355 return STATUS_INVALID_USER_BUFFER;
1356 }
1357
1358 if (Length >= sizeof(STATISTICS) * VfatGlobalData->NumberProcessors)
1359 {
1360 Length = sizeof(STATISTICS) * VfatGlobalData->NumberProcessors;
1361 Status = STATUS_SUCCESS;
1362 }
1363 else
1364 {
1365 Status = STATUS_BUFFER_OVERFLOW;
1366 }
1367
1368 RtlCopyMemory(Buffer, DeviceExt->Statistics, Length);
1369 IrpContext->Irp->IoStatus.Information = Length;
1370
1371 return Status;
1372 }
1373
1374 /*
1375 * FUNCTION: File system control
1376 */
1377 NTSTATUS
VfatFileSystemControl(PVFAT_IRP_CONTEXT IrpContext)1378 VfatFileSystemControl(
1379 PVFAT_IRP_CONTEXT IrpContext)
1380 {
1381 NTSTATUS Status;
1382
1383 DPRINT("VfatFileSystemControl(IrpContext %p)\n", IrpContext);
1384
1385 ASSERT(IrpContext);
1386 ASSERT(IrpContext->Irp);
1387 ASSERT(IrpContext->Stack);
1388
1389 IrpContext->Irp->IoStatus.Information = 0;
1390
1391 switch (IrpContext->MinorFunction)
1392 {
1393 case IRP_MN_KERNEL_CALL:
1394 case IRP_MN_USER_FS_REQUEST:
1395 switch(IrpContext->Stack->Parameters.DeviceIoControl.IoControlCode)
1396 {
1397 case FSCTL_GET_VOLUME_BITMAP:
1398 Status = VfatGetVolumeBitmap(IrpContext);
1399 break;
1400
1401 case FSCTL_GET_RETRIEVAL_POINTERS:
1402 Status = VfatGetRetrievalPointers(IrpContext);
1403 break;
1404
1405 case FSCTL_MOVE_FILE:
1406 Status = VfatMoveFile(IrpContext);
1407 break;
1408
1409 case FSCTL_IS_VOLUME_DIRTY:
1410 Status = VfatIsVolumeDirty(IrpContext);
1411 break;
1412
1413 case FSCTL_MARK_VOLUME_DIRTY:
1414 Status = VfatMarkVolumeDirty(IrpContext);
1415 break;
1416
1417 case FSCTL_LOCK_VOLUME:
1418 Status = VfatLockOrUnlockVolume(IrpContext, TRUE);
1419 break;
1420
1421 case FSCTL_UNLOCK_VOLUME:
1422 Status = VfatLockOrUnlockVolume(IrpContext, FALSE);
1423 break;
1424
1425 case FSCTL_DISMOUNT_VOLUME:
1426 Status = VfatDismountVolume(IrpContext);
1427 break;
1428
1429 case FSCTL_FILESYSTEM_GET_STATISTICS:
1430 Status = VfatGetStatistics(IrpContext);
1431 break;
1432
1433 default:
1434 Status = STATUS_INVALID_DEVICE_REQUEST;
1435 }
1436 break;
1437
1438 case IRP_MN_MOUNT_VOLUME:
1439 Status = VfatMount(IrpContext);
1440 break;
1441
1442 case IRP_MN_VERIFY_VOLUME:
1443 DPRINT("VFATFS: IRP_MN_VERIFY_VOLUME\n");
1444 Status = VfatVerify(IrpContext);
1445 break;
1446
1447 default:
1448 DPRINT("VFAT FSC: MinorFunction %u\n", IrpContext->MinorFunction);
1449 Status = STATUS_INVALID_DEVICE_REQUEST;
1450 break;
1451 }
1452
1453 return Status;
1454 }
1455