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