xref: /reactos/drivers/filesystems/vfatfs/fsctl.c (revision 542e9f2b)
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