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