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