Sync with trunk revision 64099.
[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 /* FUNCTIONS ****************************************************************/
34
35 #define CACHEPAGESIZE(pDeviceExt) ((pDeviceExt)->FatInfo.BytesPerCluster > PAGE_SIZE ? \
36 (pDeviceExt)->FatInfo.BytesPerCluster : PAGE_SIZE)
37
38 static
39 NTSTATUS
40 VfatHasFileSystem(
41 PDEVICE_OBJECT DeviceToMount,
42 PBOOLEAN RecognizedFS,
43 PFATINFO pFatInfo)
44 {
45 NTSTATUS Status;
46 PARTITION_INFORMATION PartitionInfo;
47 DISK_GEOMETRY DiskGeometry;
48 FATINFO FatInfo;
49 ULONG Size;
50 ULONG Sectors;
51 LARGE_INTEGER Offset;
52 struct _BootSector* Boot;
53 struct _BootSectorFatX* BootFatX;
54 BOOLEAN PartitionInfoIsValid = FALSE;
55
56 DPRINT("VfatHasFileSystem\n");
57
58 *RecognizedFS = FALSE;
59
60 Size = sizeof(DISK_GEOMETRY);
61 Status = VfatBlockDeviceIoControl(DeviceToMount,
62 IOCTL_DISK_GET_DRIVE_GEOMETRY,
63 NULL,
64 0,
65 &DiskGeometry,
66 &Size,
67 FALSE);
68 if (!NT_SUCCESS(Status))
69 {
70 DPRINT("VfatBlockDeviceIoControl faild (%x)\n", Status);
71 return Status;
72 }
73
74 FatInfo.FixedMedia = DiskGeometry.MediaType == FixedMedia ? TRUE : FALSE;
75 if (DiskGeometry.MediaType == FixedMedia || DiskGeometry.MediaType == RemovableMedia)
76 {
77 // We have found a hard disk
78 Size = sizeof(PARTITION_INFORMATION);
79 Status = VfatBlockDeviceIoControl(DeviceToMount,
80 IOCTL_DISK_GET_PARTITION_INFO,
81 NULL,
82 0,
83 &PartitionInfo,
84 &Size,
85 FALSE);
86 if (!NT_SUCCESS(Status))
87 {
88 DPRINT("VfatBlockDeviceIoControl faild (%x)\n", Status);
89 return Status;
90 }
91
92 PartitionInfoIsValid = TRUE;
93 DPRINT("Partition Information:\n");
94 DPRINT("StartingOffset %I64x\n", PartitionInfo.StartingOffset.QuadPart / 512);
95 DPRINT("PartitionLength %I64x\n", PartitionInfo.PartitionLength.QuadPart / 512);
96 DPRINT("HiddenSectors %u\n", PartitionInfo.HiddenSectors);
97 DPRINT("PartitionNumber %u\n", PartitionInfo.PartitionNumber);
98 DPRINT("PartitionType %u\n", PartitionInfo.PartitionType);
99 DPRINT("BootIndicator %u\n", PartitionInfo.BootIndicator);
100 DPRINT("RecognizedPartition %u\n", PartitionInfo.RecognizedPartition);
101 DPRINT("RewritePartition %u\n", PartitionInfo.RewritePartition);
102 if (PartitionInfo.PartitionType)
103 {
104 if (PartitionInfo.PartitionType == PARTITION_FAT_12 ||
105 PartitionInfo.PartitionType == PARTITION_FAT_16 ||
106 PartitionInfo.PartitionType == PARTITION_HUGE ||
107 PartitionInfo.PartitionType == PARTITION_FAT32 ||
108 PartitionInfo.PartitionType == PARTITION_FAT32_XINT13 ||
109 PartitionInfo.PartitionType == PARTITION_XINT13)
110 {
111 *RecognizedFS = TRUE;
112 }
113 }
114 else if (DiskGeometry.MediaType == RemovableMedia &&
115 PartitionInfo.PartitionNumber > 0 &&
116 PartitionInfo.StartingOffset.QuadPart == 0 &&
117 PartitionInfo.PartitionLength.QuadPart > 0)
118 {
119 /* This is possible a removable media formated as super floppy */
120 *RecognizedFS = TRUE;
121 }
122 }
123 else if (DiskGeometry.MediaType == Unknown)
124 {
125 /*
126 * Floppy disk driver can return Unknown as media type if it
127 * doesn't know yet what floppy in the drive really is. This is
128 * perfectly correct to do under Windows.
129 */
130 *RecognizedFS = TRUE;
131 DiskGeometry.BytesPerSector = 512;
132 }
133 else
134 {
135 *RecognizedFS = TRUE;
136 }
137
138 if (*RecognizedFS)
139 {
140 Boot = ExAllocatePoolWithTag(NonPagedPool, DiskGeometry.BytesPerSector, TAG_VFAT);
141 if (Boot == NULL)
142 {
143 return STATUS_INSUFFICIENT_RESOURCES;
144 }
145
146 Offset.QuadPart = 0;
147
148 /* Try to recognize FAT12/FAT16/FAT32 partitions */
149 Status = VfatReadDisk(DeviceToMount, &Offset, DiskGeometry.BytesPerSector, (PUCHAR) Boot, FALSE);
150 if (NT_SUCCESS(Status))
151 {
152 if (Boot->Signatur1 != 0xaa55)
153 {
154 *RecognizedFS = FALSE;
155 }
156
157 if (*RecognizedFS &&
158 Boot->BytesPerSector != 512 &&
159 Boot->BytesPerSector != 1024 &&
160 Boot->BytesPerSector != 2048 &&
161 Boot->BytesPerSector != 4096)
162 {
163 DPRINT1("BytesPerSector %u\n", Boot->BytesPerSector);
164 *RecognizedFS = FALSE;
165 }
166
167 if (*RecognizedFS &&
168 Boot->FATCount != 1 &&
169 Boot->FATCount != 2)
170 {
171 DPRINT1("FATCount %u\n", Boot->FATCount);
172 *RecognizedFS = FALSE;
173 }
174
175 if (*RecognizedFS &&
176 Boot->Media != 0xf0 &&
177 Boot->Media != 0xf8 &&
178 Boot->Media != 0xf9 &&
179 Boot->Media != 0xfa &&
180 Boot->Media != 0xfb &&
181 Boot->Media != 0xfc &&
182 Boot->Media != 0xfd &&
183 Boot->Media != 0xfe &&
184 Boot->Media != 0xff)
185 {
186 DPRINT1("Media %02x\n", Boot->Media);
187 *RecognizedFS = FALSE;
188 }
189
190 if (*RecognizedFS &&
191 Boot->SectorsPerCluster != 1 &&
192 Boot->SectorsPerCluster != 2 &&
193 Boot->SectorsPerCluster != 4 &&
194 Boot->SectorsPerCluster != 8 &&
195 Boot->SectorsPerCluster != 16 &&
196 Boot->SectorsPerCluster != 32 &&
197 Boot->SectorsPerCluster != 64 &&
198 Boot->SectorsPerCluster != 128)
199 {
200 DPRINT1("SectorsPerCluster %02x\n", Boot->SectorsPerCluster);
201 *RecognizedFS = FALSE;
202 }
203
204 if (*RecognizedFS &&
205 Boot->BytesPerSector * Boot->SectorsPerCluster > 32 * 1024)
206 {
207 DPRINT1("ClusterSize %dx\n", Boot->BytesPerSector * Boot->SectorsPerCluster);
208 *RecognizedFS = FALSE;
209 }
210
211 if (*RecognizedFS)
212 {
213 FatInfo.VolumeID = Boot->VolumeID;
214 FatInfo.FATStart = Boot->ReservedSectors;
215 FatInfo.FATCount = Boot->FATCount;
216 FatInfo.FATSectors = Boot->FATSectors ? Boot->FATSectors : ((struct _BootSector32*) Boot)->FATSectors32;
217 FatInfo.BytesPerSector = Boot->BytesPerSector;
218 FatInfo.SectorsPerCluster = Boot->SectorsPerCluster;
219 FatInfo.BytesPerCluster = FatInfo.BytesPerSector * FatInfo.SectorsPerCluster;
220 FatInfo.rootDirectorySectors = ((Boot->RootEntries * 32) + Boot->BytesPerSector - 1) / Boot->BytesPerSector;
221 FatInfo.rootStart = FatInfo.FATStart + FatInfo.FATCount * FatInfo.FATSectors;
222 FatInfo.dataStart = FatInfo.rootStart + FatInfo.rootDirectorySectors;
223 FatInfo.Sectors = Sectors = Boot->Sectors ? Boot->Sectors : Boot->SectorsHuge;
224 Sectors -= Boot->ReservedSectors + FatInfo.FATCount * FatInfo.FATSectors + FatInfo.rootDirectorySectors;
225 FatInfo.NumberOfClusters = Sectors / Boot->SectorsPerCluster;
226 if (FatInfo.NumberOfClusters < 4085)
227 {
228 DPRINT("FAT12\n");
229 FatInfo.FatType = FAT12;
230 FatInfo.RootCluster = (FatInfo.rootStart - 1) / FatInfo.SectorsPerCluster;
231 }
232 else if (FatInfo.NumberOfClusters >= 65525)
233 {
234 DPRINT("FAT32\n");
235 FatInfo.FatType = FAT32;
236 FatInfo.RootCluster = ((struct _BootSector32*) Boot)->RootCluster;
237 FatInfo.rootStart = FatInfo.dataStart + ((FatInfo.RootCluster - 2) * FatInfo.SectorsPerCluster);
238 FatInfo.VolumeID = ((struct _BootSector32*) Boot)->VolumeID;
239 }
240 else
241 {
242 DPRINT("FAT16\n");
243 FatInfo.FatType = FAT16;
244 FatInfo.RootCluster = FatInfo.rootStart / FatInfo.SectorsPerCluster;
245 }
246
247 if (PartitionInfoIsValid &&
248 FatInfo.Sectors > PartitionInfo.PartitionLength.QuadPart / FatInfo.BytesPerSector)
249 {
250 *RecognizedFS = FALSE;
251 }
252
253 if (pFatInfo && *RecognizedFS)
254 {
255 *pFatInfo = FatInfo;
256 }
257 }
258 }
259
260 ExFreePool(Boot);
261 }
262
263 if (!*RecognizedFS && PartitionInfoIsValid)
264 {
265 BootFatX = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct _BootSectorFatX), TAG_VFAT);
266 if (BootFatX == NULL)
267 {
268 *RecognizedFS=FALSE;
269 return STATUS_INSUFFICIENT_RESOURCES;
270 }
271
272 Offset.QuadPart = 0;
273
274 /* Try to recognize FATX16/FATX32 partitions (Xbox) */
275 Status = VfatReadDisk(DeviceToMount, &Offset, sizeof(struct _BootSectorFatX), (PUCHAR) BootFatX, FALSE);
276 if (NT_SUCCESS(Status))
277 {
278 *RecognizedFS = TRUE;
279 if (BootFatX->SysType[0] != 'F' ||
280 BootFatX->SysType[1] != 'A' ||
281 BootFatX->SysType[2] != 'T' ||
282 BootFatX->SysType[3] != 'X')
283 {
284 DPRINT1("SysType %c%c%c%c\n", BootFatX->SysType[0], BootFatX->SysType[1], BootFatX->SysType[2], BootFatX->SysType[3]);
285 *RecognizedFS=FALSE;
286 }
287
288 if (*RecognizedFS &&
289 BootFatX->SectorsPerCluster != 1 &&
290 BootFatX->SectorsPerCluster != 2 &&
291 BootFatX->SectorsPerCluster != 4 &&
292 BootFatX->SectorsPerCluster != 8 &&
293 BootFatX->SectorsPerCluster != 16 &&
294 BootFatX->SectorsPerCluster != 32 &&
295 BootFatX->SectorsPerCluster != 64 &&
296 BootFatX->SectorsPerCluster != 128)
297 {
298 DPRINT1("SectorsPerCluster %lu\n", BootFatX->SectorsPerCluster);
299 *RecognizedFS=FALSE;
300 }
301
302 if (*RecognizedFS)
303 {
304 FatInfo.BytesPerSector = DiskGeometry.BytesPerSector;
305 FatInfo.SectorsPerCluster = BootFatX->SectorsPerCluster;
306 FatInfo.rootDirectorySectors = BootFatX->SectorsPerCluster;
307 FatInfo.BytesPerCluster = BootFatX->SectorsPerCluster * DiskGeometry.BytesPerSector;
308 FatInfo.Sectors = (ULONG)(PartitionInfo.PartitionLength.QuadPart / DiskGeometry.BytesPerSector);
309 if (FatInfo.Sectors / FatInfo.SectorsPerCluster < 65525)
310 {
311 DPRINT("FATX16\n");
312 FatInfo.FatType = FATX16;
313 }
314 else
315 {
316 DPRINT("FATX32\n");
317 FatInfo.FatType = FATX32;
318 }
319 FatInfo.VolumeID = BootFatX->VolumeID;
320 FatInfo.FATStart = sizeof(struct _BootSectorFatX) / DiskGeometry.BytesPerSector;
321 FatInfo.FATCount = BootFatX->FATCount;
322 FatInfo.FATSectors =
323 ROUND_UP(FatInfo.Sectors / FatInfo.SectorsPerCluster * (FatInfo.FatType == FATX16 ? 2 : 4), 4096) /
324 FatInfo.BytesPerSector;
325 FatInfo.rootStart = FatInfo.FATStart + FatInfo.FATCount * FatInfo.FATSectors;
326 FatInfo.RootCluster = (FatInfo.rootStart - 1) / FatInfo.SectorsPerCluster;
327 FatInfo.dataStart = FatInfo.rootStart + FatInfo.rootDirectorySectors;
328 FatInfo.NumberOfClusters = (FatInfo.Sectors - FatInfo.dataStart) / FatInfo.SectorsPerCluster;
329
330 if (pFatInfo && *RecognizedFS)
331 {
332 *pFatInfo = FatInfo;
333 }
334 }
335 }
336 ExFreePool(BootFatX);
337 }
338
339 DPRINT("VfatHasFileSystem done\n");
340 return Status;
341 }
342
343 /*
344 * FUNCTION: Mounts the device
345 */
346 static
347 NTSTATUS
348 VfatMountDevice(
349 PDEVICE_EXTENSION DeviceExt,
350 PDEVICE_OBJECT DeviceToMount)
351 {
352 NTSTATUS Status;
353 BOOLEAN RecognizedFS;
354
355 DPRINT("Mounting VFAT device...\n");
356
357 Status = VfatHasFileSystem(DeviceToMount, &RecognizedFS, &DeviceExt->FatInfo);
358 if (!NT_SUCCESS(Status))
359 {
360 return Status;
361 }
362 DPRINT("MountVfatdev %u, PAGE_SIZE = %d\n", DeviceExt->FatInfo.BytesPerCluster, PAGE_SIZE);
363
364 return STATUS_SUCCESS;
365 }
366
367
368 /*
369 * FUNCTION: Mount the filesystem
370 */
371 static
372 NTSTATUS
373 VfatMount(
374 PVFAT_IRP_CONTEXT IrpContext)
375 {
376 PDEVICE_OBJECT DeviceObject = NULL;
377 PDEVICE_EXTENSION DeviceExt = NULL;
378 BOOLEAN RecognizedFS;
379 NTSTATUS Status;
380 PVFATFCB Fcb = NULL;
381 PVFATFCB VolumeFcb = NULL;
382 PVFATCCB Ccb = NULL;
383 PDEVICE_OBJECT DeviceToMount;
384 PVPB Vpb;
385 UNICODE_STRING NameU = RTL_CONSTANT_STRING(L"\\$$Fat$$");
386 UNICODE_STRING VolumeNameU = RTL_CONSTANT_STRING(L"\\$$Volume$$");
387 ULONG HashTableSize;
388 ULONG eocMark;
389 FATINFO FatInfo;
390
391 DPRINT("VfatMount(IrpContext %p)\n", IrpContext);
392
393 ASSERT(IrpContext);
394
395 if (IrpContext->DeviceObject != VfatGlobalData->DeviceObject)
396 {
397 Status = STATUS_INVALID_DEVICE_REQUEST;
398 goto ByeBye;
399 }
400
401 DeviceToMount = IrpContext->Stack->Parameters.MountVolume.DeviceObject;
402 Vpb = IrpContext->Stack->Parameters.MountVolume.Vpb;
403
404 Status = VfatHasFileSystem(DeviceToMount, &RecognizedFS, &FatInfo);
405 if (!NT_SUCCESS(Status))
406 {
407 goto ByeBye;
408 }
409
410 if (RecognizedFS == FALSE)
411 {
412 DPRINT("VFAT: Unrecognized Volume\n");
413 Status = STATUS_UNRECOGNIZED_VOLUME;
414 goto ByeBye;
415 }
416
417 /* Use prime numbers for the table size */
418 if (FatInfo.FatType == FAT12)
419 {
420 HashTableSize = 4099; // 4096 = 4 * 1024
421 }
422 else if (FatInfo.FatType == FAT16 ||
423 FatInfo.FatType == FATX16)
424 {
425 HashTableSize = 16411; // 16384 = 16 * 1024
426 }
427 else
428 {
429 HashTableSize = 65537; // 65536 = 64 * 1024;
430 }
431 HashTableSize = FCB_HASH_TABLE_SIZE;
432 DPRINT("VFAT: Recognized volume\n");
433 Status = IoCreateDevice(VfatGlobalData->DriverObject,
434 ROUND_UP(sizeof (DEVICE_EXTENSION), sizeof(ULONG)) + sizeof(HASHENTRY*) * HashTableSize,
435 NULL,
436 FILE_DEVICE_DISK_FILE_SYSTEM,
437 DeviceToMount->Characteristics,
438 FALSE,
439 &DeviceObject);
440 if (!NT_SUCCESS(Status))
441 {
442 goto ByeBye;
443 }
444
445 DeviceExt = DeviceObject->DeviceExtension;
446 RtlZeroMemory(DeviceExt, ROUND_UP(sizeof(DEVICE_EXTENSION), sizeof(ULONG)) + sizeof(HASHENTRY*) * HashTableSize);
447 DeviceExt->FcbHashTable = (HASHENTRY**)((ULONG_PTR)DeviceExt + ROUND_UP(sizeof(DEVICE_EXTENSION), sizeof(ULONG)));
448 DeviceExt->HashTableSize = HashTableSize;
449
450 /* use same vpb as device disk */
451 DeviceObject->Vpb = Vpb;
452 DeviceToMount->Vpb = Vpb;
453
454 Status = VfatMountDevice(DeviceExt, DeviceToMount);
455 if (!NT_SUCCESS(Status))
456 {
457 /* FIXME: delete device object */
458 goto ByeBye;
459 }
460
461 DPRINT("BytesPerSector: %u\n", DeviceExt->FatInfo.BytesPerSector);
462 DPRINT("SectorsPerCluster: %u\n", DeviceExt->FatInfo.SectorsPerCluster);
463 DPRINT("FATCount: %u\n", DeviceExt->FatInfo.FATCount);
464 DPRINT("FATSectors: %u\n", DeviceExt->FatInfo.FATSectors);
465 DPRINT("RootStart: %u\n", DeviceExt->FatInfo.rootStart);
466 DPRINT("DataStart: %u\n", DeviceExt->FatInfo.dataStart);
467 if (DeviceExt->FatInfo.FatType == FAT32)
468 {
469 DPRINT("RootCluster: %u\n", DeviceExt->FatInfo.RootCluster);
470 }
471
472 switch (DeviceExt->FatInfo.FatType)
473 {
474 case FAT12:
475 DeviceExt->GetNextCluster = FAT12GetNextCluster;
476 DeviceExt->FindAndMarkAvailableCluster = FAT12FindAndMarkAvailableCluster;
477 DeviceExt->WriteCluster = FAT12WriteCluster;
478 DeviceExt->CleanShutBitMask = 0;
479 break;
480
481 case FAT16:
482 case FATX16:
483 DeviceExt->GetNextCluster = FAT16GetNextCluster;
484 DeviceExt->FindAndMarkAvailableCluster = FAT16FindAndMarkAvailableCluster;
485 DeviceExt->WriteCluster = FAT16WriteCluster;
486 DeviceExt->CleanShutBitMask = 0x8000;
487 break;
488
489 case FAT32:
490 case FATX32:
491 DeviceExt->GetNextCluster = FAT32GetNextCluster;
492 DeviceExt->FindAndMarkAvailableCluster = FAT32FindAndMarkAvailableCluster;
493 DeviceExt->WriteCluster = FAT32WriteCluster;
494 DeviceExt->CleanShutBitMask = 0x80000000;
495 break;
496 }
497
498 if (DeviceExt->FatInfo.FatType == FATX16 ||
499 DeviceExt->FatInfo.FatType == FATX32)
500 {
501 DeviceExt->Flags |= VCB_IS_FATX;
502 DeviceExt->GetNextDirEntry = FATXGetNextDirEntry;
503 DeviceExt->BaseDateYear = 2000;
504 }
505 else
506 {
507 DeviceExt->GetNextDirEntry = FATGetNextDirEntry;
508 DeviceExt->BaseDateYear = 1980;
509 }
510
511 DeviceExt->StorageDevice = DeviceToMount;
512 DeviceExt->StorageDevice->Vpb->DeviceObject = DeviceObject;
513 DeviceExt->StorageDevice->Vpb->RealDevice = DeviceExt->StorageDevice;
514 DeviceExt->StorageDevice->Vpb->Flags |= VPB_MOUNTED;
515 DeviceObject->StackSize = DeviceExt->StorageDevice->StackSize + 1;
516 DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
517
518 DPRINT("FsDeviceObject %p\n", DeviceObject);
519
520 /* Initialize this resource early ... it's used in VfatCleanup */
521 ExInitializeResourceLite(&DeviceExt->DirResource);
522
523 DeviceExt->FATFileObject = IoCreateStreamFileObject(NULL, DeviceExt->StorageDevice);
524 Fcb = vfatNewFCB(DeviceExt, &NameU);
525 if (Fcb == NULL)
526 {
527 Status = STATUS_INSUFFICIENT_RESOURCES;
528 goto ByeBye;
529 }
530
531 Ccb = ExAllocateFromNPagedLookasideList(&VfatGlobalData->CcbLookasideList);
532 if (Ccb == NULL)
533 {
534 Status = STATUS_INSUFFICIENT_RESOURCES;
535 goto ByeBye;
536 }
537
538 RtlZeroMemory(Ccb, sizeof (VFATCCB));
539 DeviceExt->FATFileObject->FsContext = Fcb;
540 DeviceExt->FATFileObject->FsContext2 = Ccb;
541 DeviceExt->FATFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
542 DeviceExt->FATFileObject->PrivateCacheMap = NULL;
543 DeviceExt->FATFileObject->Vpb = DeviceObject->Vpb;
544 Fcb->FileObject = DeviceExt->FATFileObject;
545
546 Fcb->Flags |= FCB_IS_FAT;
547
548 Fcb->RFCB.FileSize.QuadPart = DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector;
549 Fcb->RFCB.ValidDataLength = Fcb->RFCB.FileSize;
550 Fcb->RFCB.AllocationSize = Fcb->RFCB.FileSize;
551
552 _SEH2_TRY
553 {
554 CcInitializeCacheMap(DeviceExt->FATFileObject,
555 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
556 TRUE,
557 &VfatGlobalData->CacheMgrCallbacks,
558 Fcb);
559 }
560 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
561 {
562 Status = _SEH2_GetExceptionCode();
563 goto ByeBye;
564 }
565 _SEH2_END;
566
567 DeviceExt->LastAvailableCluster = 2;
568 ExInitializeResourceLite(&DeviceExt->FatResource);
569
570 InitializeListHead(&DeviceExt->FcbListHead);
571
572 VolumeFcb = vfatNewFCB(DeviceExt, &VolumeNameU);
573 if (VolumeFcb == NULL)
574 {
575 Status = STATUS_INSUFFICIENT_RESOURCES;
576 goto ByeBye;
577 }
578
579 VolumeFcb->Flags = FCB_IS_VOLUME;
580 VolumeFcb->RFCB.FileSize.QuadPart = DeviceExt->FatInfo.Sectors * DeviceExt->FatInfo.BytesPerSector;
581 VolumeFcb->RFCB.ValidDataLength = VolumeFcb->RFCB.FileSize;
582 VolumeFcb->RFCB.AllocationSize = VolumeFcb->RFCB.FileSize;
583 DeviceExt->VolumeFcb = VolumeFcb;
584
585 ExAcquireResourceExclusiveLite(&VfatGlobalData->VolumeListLock, TRUE);
586 InsertHeadList(&VfatGlobalData->VolumeListHead, &DeviceExt->VolumeListEntry);
587 ExReleaseResourceLite(&VfatGlobalData->VolumeListLock);
588
589 /* read serial number */
590 DeviceObject->Vpb->SerialNumber = DeviceExt->FatInfo.VolumeID;
591
592 /* read volume label */
593 ReadVolumeLabel(DeviceExt, DeviceObject->Vpb);
594
595 /* read clean shutdown bit status */
596 Status = GetNextCluster(DeviceExt, 1, &eocMark);
597 if (NT_SUCCESS(Status))
598 {
599 if (eocMark & DeviceExt->CleanShutBitMask)
600 {
601 /* unset clean shutdown bit */
602 eocMark &= ~DeviceExt->CleanShutBitMask;
603 WriteCluster(DeviceExt, 1, eocMark);
604 VolumeFcb->Flags |= VCB_CLEAR_DIRTY;
605 }
606 }
607
608 VolumeFcb->Flags |= VCB_IS_DIRTY;
609
610 FsRtlNotifyVolumeEvent(DeviceExt->FATFileObject, FSRTL_VOLUME_MOUNT);
611 FsRtlNotifyInitializeSync(&DeviceExt->NotifySync);
612 InitializeListHead(&DeviceExt->NotifyList);
613
614 Status = STATUS_SUCCESS;
615
616 ByeBye:
617 if (!NT_SUCCESS(Status))
618 {
619 /* Cleanup */
620 if (DeviceExt && DeviceExt->FATFileObject)
621 ObDereferenceObject (DeviceExt->FATFileObject);
622 if (Fcb)
623 vfatDestroyFCB(Fcb);
624 if (Ccb)
625 vfatDestroyCCB(Ccb);
626 if (DeviceObject)
627 IoDeleteDevice(DeviceObject);
628 }
629
630 return Status;
631 }
632
633
634 /*
635 * FUNCTION: Verify the filesystem
636 */
637 static
638 NTSTATUS
639 VfatVerify(
640 PVFAT_IRP_CONTEXT IrpContext)
641 {
642 PDEVICE_OBJECT DeviceToVerify;
643 NTSTATUS Status = STATUS_SUCCESS;
644 FATINFO FatInfo;
645 BOOLEAN RecognizedFS;
646 PDEVICE_EXTENSION DeviceExt = IrpContext->DeviceExt;
647
648 DPRINT("VfatVerify(IrpContext %p)\n", IrpContext);
649
650 DeviceToVerify = IrpContext->Stack->Parameters.VerifyVolume.DeviceObject;
651 Status = VfatBlockDeviceIoControl(DeviceToVerify,
652 IOCTL_DISK_CHECK_VERIFY,
653 NULL,
654 0,
655 NULL,
656 0,
657 TRUE);
658 DeviceToVerify->Flags &= ~DO_VERIFY_VOLUME;
659 if (!NT_SUCCESS(Status) && Status != STATUS_VERIFY_REQUIRED)
660 {
661 DPRINT("VfatBlockDeviceIoControl() failed (Status %lx)\n", Status);
662 Status = STATUS_WRONG_VOLUME;
663 }
664 else
665 {
666 Status = VfatHasFileSystem(DeviceToVerify, &RecognizedFS, &FatInfo);
667 if (!NT_SUCCESS(Status) || RecognizedFS == FALSE)
668 {
669 Status = STATUS_WRONG_VOLUME;
670 }
671 else if (sizeof(FATINFO) == RtlCompareMemory(&FatInfo, &DeviceExt->FatInfo, sizeof(FATINFO)))
672 {
673 /*
674 * FIXME:
675 * Preformated floppy disks have very often a serial number of 0000:0000.
676 * We should calculate a crc sum over the sectors from the root directory as secondary volume number.
677 * Each write to the root directory must update this crc sum.
678 */
679 }
680 else
681 {
682 Status = STATUS_WRONG_VOLUME;
683 }
684 }
685
686 return Status;
687 }
688
689
690 static
691 NTSTATUS
692 VfatGetVolumeBitmap(
693 PVFAT_IRP_CONTEXT IrpContext)
694 {
695 DPRINT("VfatGetVolumeBitmap (IrpContext %p)\n", IrpContext);
696 return STATUS_INVALID_DEVICE_REQUEST;
697 }
698
699
700 static
701 NTSTATUS
702 VfatGetRetrievalPointers(
703 PVFAT_IRP_CONTEXT IrpContext)
704 {
705 PIO_STACK_LOCATION Stack;
706 LARGE_INTEGER Vcn;
707 PRETRIEVAL_POINTERS_BUFFER RetrievalPointers;
708 PFILE_OBJECT FileObject;
709 ULONG MaxExtentCount;
710 PVFATFCB Fcb;
711 PDEVICE_EXTENSION DeviceExt;
712 ULONG FirstCluster;
713 ULONG CurrentCluster;
714 ULONG LastCluster;
715 NTSTATUS Status;
716
717 DPRINT("VfatGetRetrievalPointers(IrpContext %p)\n", IrpContext);
718
719 DeviceExt = IrpContext->DeviceExt;
720 FileObject = IrpContext->FileObject;
721 Stack = IrpContext->Stack;
722 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(STARTING_VCN_INPUT_BUFFER) ||
723 Stack->Parameters.DeviceIoControl.Type3InputBuffer == NULL)
724 {
725 return STATUS_INVALID_PARAMETER;
726 }
727
728 if (IrpContext->Irp->UserBuffer == NULL ||
729 Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(RETRIEVAL_POINTERS_BUFFER))
730 {
731 return STATUS_BUFFER_TOO_SMALL;
732 }
733
734 Fcb = FileObject->FsContext;
735
736 ExAcquireResourceSharedLite(&Fcb->MainResource, TRUE);
737
738 Vcn = ((PSTARTING_VCN_INPUT_BUFFER)Stack->Parameters.DeviceIoControl.Type3InputBuffer)->StartingVcn;
739 RetrievalPointers = IrpContext->Irp->UserBuffer;
740
741 MaxExtentCount = ((Stack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(RetrievalPointers->ExtentCount) - sizeof(RetrievalPointers->StartingVcn)) / sizeof(RetrievalPointers->Extents[0]));
742
743 if (Vcn.QuadPart >= Fcb->RFCB.AllocationSize.QuadPart / DeviceExt->FatInfo.BytesPerCluster)
744 {
745 Status = STATUS_INVALID_PARAMETER;
746 goto ByeBye;
747 }
748
749 CurrentCluster = FirstCluster = vfatDirEntryGetFirstCluster(DeviceExt, &Fcb->entry);
750 Status = OffsetToCluster(DeviceExt, FirstCluster,
751 Vcn.u.LowPart * DeviceExt->FatInfo.BytesPerCluster,
752 &CurrentCluster, FALSE);
753 if (!NT_SUCCESS(Status))
754 {
755 goto ByeBye;
756 }
757
758 RetrievalPointers->StartingVcn = Vcn;
759 RetrievalPointers->ExtentCount = 0;
760 RetrievalPointers->Extents[0].Lcn.u.HighPart = 0;
761 RetrievalPointers->Extents[0].Lcn.u.LowPart = CurrentCluster - 2;
762 LastCluster = 0;
763 while (CurrentCluster != 0xffffffff && RetrievalPointers->ExtentCount < MaxExtentCount)
764 {
765 LastCluster = CurrentCluster;
766 Status = NextCluster(DeviceExt, CurrentCluster, &CurrentCluster, FALSE);
767 Vcn.QuadPart++;
768 if (!NT_SUCCESS(Status))
769 {
770 goto ByeBye;
771 }
772
773 if (LastCluster + 1 != CurrentCluster)
774 {
775 RetrievalPointers->Extents[RetrievalPointers->ExtentCount].NextVcn = Vcn;
776 RetrievalPointers->ExtentCount++;
777 if (RetrievalPointers->ExtentCount < MaxExtentCount)
778 {
779 RetrievalPointers->Extents[RetrievalPointers->ExtentCount].Lcn.u.HighPart = 0;
780 RetrievalPointers->Extents[RetrievalPointers->ExtentCount].Lcn.u.LowPart = CurrentCluster - 2;
781 }
782 }
783 }
784
785 IrpContext->Irp->IoStatus.Information = sizeof(RETRIEVAL_POINTERS_BUFFER) + (sizeof(RetrievalPointers->Extents[0]) * (RetrievalPointers->ExtentCount - 1));
786 Status = STATUS_SUCCESS;
787
788 ByeBye:
789 ExReleaseResourceLite(&Fcb->MainResource);
790
791 return Status;
792 }
793
794 static
795 NTSTATUS
796 VfatMoveFile(
797 PVFAT_IRP_CONTEXT IrpContext)
798 {
799 DPRINT("VfatMoveFile(IrpContext %p)\n", IrpContext);
800 return STATUS_INVALID_DEVICE_REQUEST;
801 }
802
803 static
804 NTSTATUS
805 VfatIsVolumeDirty(
806 PVFAT_IRP_CONTEXT IrpContext)
807 {
808 PULONG Flags;
809
810 DPRINT("VfatIsVolumeDirty(IrpContext %p)\n", IrpContext);
811
812 if (IrpContext->Stack->Parameters.FileSystemControl.OutputBufferLength != sizeof(ULONG))
813 return STATUS_INVALID_BUFFER_SIZE;
814 else if (!IrpContext->Irp->AssociatedIrp.SystemBuffer)
815 return STATUS_INVALID_USER_BUFFER;
816
817 Flags = (PULONG)IrpContext->Irp->AssociatedIrp.SystemBuffer;
818 *Flags = 0;
819
820 if ((IrpContext->DeviceExt->VolumeFcb->Flags & VCB_IS_DIRTY) &&
821 !(IrpContext->DeviceExt->VolumeFcb->Flags & VCB_CLEAR_DIRTY))
822 {
823 *Flags |= VOLUME_IS_DIRTY;
824 }
825
826 return STATUS_SUCCESS;
827 }
828
829 static
830 NTSTATUS
831 VfatMarkVolumeDirty(
832 PVFAT_IRP_CONTEXT IrpContext)
833 {
834 ULONG eocMark;
835 PDEVICE_EXTENSION DeviceExt;
836 NTSTATUS Status = STATUS_SUCCESS;
837
838 DPRINT("VfatMarkVolumeDirty(IrpContext %p)\n", IrpContext);
839 DeviceExt = IrpContext->DeviceExt;
840
841 if (!(DeviceExt->VolumeFcb->Flags & VCB_IS_DIRTY))
842 {
843 Status = GetNextCluster(DeviceExt, 1, &eocMark);
844 if (NT_SUCCESS(Status))
845 {
846 /* unset clean shutdown bit */
847 eocMark &= ~DeviceExt->CleanShutBitMask;
848 Status = WriteCluster(DeviceExt, 1, eocMark);
849 }
850 }
851
852 DeviceExt->VolumeFcb->Flags &= ~VCB_CLEAR_DIRTY;
853
854 return Status;
855 }
856
857 /*
858 * FUNCTION: File system control
859 */
860 NTSTATUS
861 VfatFileSystemControl(
862 PVFAT_IRP_CONTEXT IrpContext)
863 {
864 NTSTATUS Status;
865
866 DPRINT("VfatFileSystemControl(IrpContext %p)\n", IrpContext);
867
868 ASSERT(IrpContext);
869 ASSERT(IrpContext->Irp);
870 ASSERT(IrpContext->Stack);
871
872 IrpContext->Irp->IoStatus.Information = 0;
873
874 switch (IrpContext->MinorFunction)
875 {
876 case IRP_MN_KERNEL_CALL:
877 case IRP_MN_USER_FS_REQUEST:
878 switch(IrpContext->Stack->Parameters.DeviceIoControl.IoControlCode)
879 {
880 case FSCTL_GET_VOLUME_BITMAP:
881 Status = VfatGetVolumeBitmap(IrpContext);
882 break;
883
884 case FSCTL_GET_RETRIEVAL_POINTERS:
885 Status = VfatGetRetrievalPointers(IrpContext);
886 break;
887
888 case FSCTL_MOVE_FILE:
889 Status = VfatMoveFile(IrpContext);
890 break;
891
892 case FSCTL_IS_VOLUME_DIRTY:
893 Status = VfatIsVolumeDirty(IrpContext);
894 break;
895
896 case FSCTL_MARK_VOLUME_DIRTY:
897 Status = VfatMarkVolumeDirty(IrpContext);
898 break;
899
900 default:
901 Status = STATUS_INVALID_DEVICE_REQUEST;
902 }
903 break;
904
905 case IRP_MN_MOUNT_VOLUME:
906 Status = VfatMount(IrpContext);
907 break;
908
909 case IRP_MN_VERIFY_VOLUME:
910 DPRINT("VFATFS: IRP_MN_VERIFY_VOLUME\n");
911 Status = VfatVerify(IrpContext);
912 break;
913
914 default:
915 DPRINT("VFAT FSC: MinorFunction %u\n", IrpContext->MinorFunction);
916 Status = STATUS_INVALID_DEVICE_REQUEST;
917 break;
918 }
919
920 IrpContext->Irp->IoStatus.Status = Status;
921
922 IoCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT);
923 VfatFreeIrpContext(IrpContext);
924 return Status;
925 }