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