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