[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 #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 DPRINT("Partition Information:\n");
93 DPRINT("StartingOffset %I64x\n", PartitionInfo.StartingOffset.QuadPart / 512);
94 DPRINT("PartitionLength %I64x\n", PartitionInfo.PartitionLength.QuadPart / 512);
95 DPRINT("HiddenSectors %u\n", PartitionInfo.HiddenSectors);
96 DPRINT("PartitionNumber %u\n", PartitionInfo.PartitionNumber);
97 DPRINT("PartitionType %u\n", PartitionInfo.PartitionType);
98 DPRINT("BootIndicator %u\n", PartitionInfo.BootIndicator);
99 DPRINT("RecognizedPartition %u\n", PartitionInfo.RecognizedPartition);
100 DPRINT("RewritePartition %u\n", PartitionInfo.RewritePartition);
101 if (PartitionInfo.PartitionType)
102 {
103 if (PartitionInfo.PartitionType == PARTITION_FAT_12 ||
104 PartitionInfo.PartitionType == PARTITION_FAT_16 ||
105 PartitionInfo.PartitionType == PARTITION_HUGE ||
106 PartitionInfo.PartitionType == PARTITION_FAT32 ||
107 PartitionInfo.PartitionType == PARTITION_FAT32_XINT13 ||
108 PartitionInfo.PartitionType == PARTITION_XINT13)
109 {
110 PartitionInfoIsValid = TRUE;
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 PartitionInfoIsValid = TRUE;
121 *RecognizedFS = TRUE;
122 }
123 }
124 else
125 {
126 *RecognizedFS = TRUE;
127 }
128
129 if (*RecognizedFS)
130 {
131 Boot = ExAllocatePoolWithTag(NonPagedPool, DiskGeometry.BytesPerSector, TAG_VFAT);
132 if (Boot == NULL)
133 {
134 return STATUS_INSUFFICIENT_RESOURCES;
135 }
136
137 Offset.QuadPart = 0;
138
139 /* Try to recognize FAT12/FAT16/FAT32 partitions */
140 Status = VfatReadDisk(DeviceToMount, &Offset, DiskGeometry.BytesPerSector, (PUCHAR) Boot, FALSE);
141 if (NT_SUCCESS(Status))
142 {
143 if (Boot->Signatur1 != 0xaa55)
144 {
145 *RecognizedFS = FALSE;
146 }
147
148 if (*RecognizedFS &&
149 Boot->BytesPerSector != 512 &&
150 Boot->BytesPerSector != 1024 &&
151 Boot->BytesPerSector != 2048 &&
152 Boot->BytesPerSector != 4096)
153 {
154 DPRINT1("BytesPerSector %u\n", Boot->BytesPerSector);
155 *RecognizedFS = FALSE;
156 }
157
158 if (*RecognizedFS &&
159 Boot->FATCount != 1 &&
160 Boot->FATCount != 2)
161 {
162 DPRINT1("FATCount %u\n", Boot->FATCount);
163 *RecognizedFS = FALSE;
164 }
165
166 if (*RecognizedFS &&
167 Boot->Media != 0xf0 &&
168 Boot->Media != 0xf8 &&
169 Boot->Media != 0xf9 &&
170 Boot->Media != 0xfa &&
171 Boot->Media != 0xfb &&
172 Boot->Media != 0xfc &&
173 Boot->Media != 0xfd &&
174 Boot->Media != 0xfe &&
175 Boot->Media != 0xff)
176 {
177 DPRINT1("Media %02x\n", Boot->Media);
178 *RecognizedFS = FALSE;
179 }
180
181 if (*RecognizedFS &&
182 Boot->SectorsPerCluster != 1 &&
183 Boot->SectorsPerCluster != 2 &&
184 Boot->SectorsPerCluster != 4 &&
185 Boot->SectorsPerCluster != 8 &&
186 Boot->SectorsPerCluster != 16 &&
187 Boot->SectorsPerCluster != 32 &&
188 Boot->SectorsPerCluster != 64 &&
189 Boot->SectorsPerCluster != 128)
190 {
191 DPRINT1("SectorsPerCluster %02x\n", Boot->SectorsPerCluster);
192 *RecognizedFS = FALSE;
193 }
194
195 if (*RecognizedFS &&
196 Boot->BytesPerSector * Boot->SectorsPerCluster > 32 * 1024)
197 {
198 DPRINT1("ClusterSize %dx\n", Boot->BytesPerSector * Boot->SectorsPerCluster);
199 *RecognizedFS = FALSE;
200 }
201
202 if (*RecognizedFS)
203 {
204 FatInfo.VolumeID = Boot->VolumeID;
205 FatInfo.FATStart = Boot->ReservedSectors;
206 FatInfo.FATCount = Boot->FATCount;
207 FatInfo.FATSectors = Boot->FATSectors ? Boot->FATSectors : ((struct _BootSector32*) Boot)->FATSectors32;
208 FatInfo.BytesPerSector = Boot->BytesPerSector;
209 FatInfo.SectorsPerCluster = Boot->SectorsPerCluster;
210 FatInfo.BytesPerCluster = FatInfo.BytesPerSector * FatInfo.SectorsPerCluster;
211 FatInfo.rootDirectorySectors = ((Boot->RootEntries * 32) + Boot->BytesPerSector - 1) / Boot->BytesPerSector;
212 FatInfo.rootStart = FatInfo.FATStart + FatInfo.FATCount * FatInfo.FATSectors;
213 FatInfo.dataStart = FatInfo.rootStart + FatInfo.rootDirectorySectors;
214 FatInfo.Sectors = Sectors = Boot->Sectors ? Boot->Sectors : Boot->SectorsHuge;
215 Sectors -= Boot->ReservedSectors + FatInfo.FATCount * FatInfo.FATSectors + FatInfo.rootDirectorySectors;
216 FatInfo.NumberOfClusters = Sectors / Boot->SectorsPerCluster;
217 if (FatInfo.NumberOfClusters < 4085)
218 {
219 DPRINT("FAT12\n");
220 FatInfo.FatType = FAT12;
221 FatInfo.RootCluster = (FatInfo.rootStart - 1) / FatInfo.SectorsPerCluster;
222 }
223 else if (FatInfo.NumberOfClusters >= 65525)
224 {
225 DPRINT("FAT32\n");
226 FatInfo.FatType = FAT32;
227 FatInfo.RootCluster = ((struct _BootSector32*) Boot)->RootCluster;
228 FatInfo.rootStart = FatInfo.dataStart + ((FatInfo.RootCluster - 2) * FatInfo.SectorsPerCluster);
229 FatInfo.VolumeID = ((struct _BootSector32*) Boot)->VolumeID;
230 }
231 else
232 {
233 DPRINT("FAT16\n");
234 FatInfo.FatType = FAT16;
235 FatInfo.RootCluster = FatInfo.rootStart / FatInfo.SectorsPerCluster;
236 }
237
238 if (PartitionInfoIsValid &&
239 FatInfo.Sectors > PartitionInfo.PartitionLength.QuadPart / FatInfo.BytesPerSector)
240 {
241 *RecognizedFS = FALSE;
242 }
243
244 if (pFatInfo && *RecognizedFS)
245 {
246 *pFatInfo = FatInfo;
247 }
248 }
249 }
250
251 ExFreePool(Boot);
252 }
253
254 if (!*RecognizedFS && PartitionInfoIsValid)
255 {
256 BootFatX = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct _BootSectorFatX), TAG_VFAT);
257 if (BootFatX == NULL)
258 {
259 *RecognizedFS=FALSE;
260 return STATUS_INSUFFICIENT_RESOURCES;
261 }
262
263 Offset.QuadPart = 0;
264
265 /* Try to recognize FATX16/FATX32 partitions (Xbox) */
266 Status = VfatReadDisk(DeviceToMount, &Offset, sizeof(struct _BootSectorFatX), (PUCHAR) BootFatX, FALSE);
267 if (NT_SUCCESS(Status))
268 {
269 *RecognizedFS = TRUE;
270 if (BootFatX->SysType[0] != 'F' ||
271 BootFatX->SysType[1] != 'A' ||
272 BootFatX->SysType[2] != 'T' ||
273 BootFatX->SysType[3] != 'X')
274 {
275 DPRINT1("SysType %c%c%c%c\n", BootFatX->SysType[0], BootFatX->SysType[1], BootFatX->SysType[2], BootFatX->SysType[3]);
276 *RecognizedFS=FALSE;
277 }
278
279 if (*RecognizedFS &&
280 BootFatX->SectorsPerCluster != 1 &&
281 BootFatX->SectorsPerCluster != 2 &&
282 BootFatX->SectorsPerCluster != 4 &&
283 BootFatX->SectorsPerCluster != 8 &&
284 BootFatX->SectorsPerCluster != 16 &&
285 BootFatX->SectorsPerCluster != 32 &&
286 BootFatX->SectorsPerCluster != 64 &&
287 BootFatX->SectorsPerCluster != 128)
288 {
289 DPRINT1("SectorsPerCluster %lu\n", BootFatX->SectorsPerCluster);
290 *RecognizedFS=FALSE;
291 }
292
293 if (*RecognizedFS)
294 {
295 FatInfo.BytesPerSector = DiskGeometry.BytesPerSector;
296 FatInfo.SectorsPerCluster = BootFatX->SectorsPerCluster;
297 FatInfo.rootDirectorySectors = BootFatX->SectorsPerCluster;
298 FatInfo.BytesPerCluster = BootFatX->SectorsPerCluster * DiskGeometry.BytesPerSector;
299 FatInfo.Sectors = (ULONG)(PartitionInfo.PartitionLength.QuadPart / DiskGeometry.BytesPerSector);
300 if (FatInfo.Sectors / FatInfo.SectorsPerCluster < 65525)
301 {
302 DPRINT("FATX16\n");
303 FatInfo.FatType = FATX16;
304 }
305 else
306 {
307 DPRINT("FATX32\n");
308 FatInfo.FatType = FATX32;
309 }
310 FatInfo.VolumeID = BootFatX->VolumeID;
311 FatInfo.FATStart = sizeof(struct _BootSectorFatX) / DiskGeometry.BytesPerSector;
312 FatInfo.FATCount = BootFatX->FATCount;
313 FatInfo.FATSectors =
314 ROUND_UP(FatInfo.Sectors / FatInfo.SectorsPerCluster * (FatInfo.FatType == FATX16 ? 2 : 4), 4096) /
315 FatInfo.BytesPerSector;
316 FatInfo.rootStart = FatInfo.FATStart + FatInfo.FATCount * FatInfo.FATSectors;
317 FatInfo.RootCluster = (FatInfo.rootStart - 1) / FatInfo.SectorsPerCluster;
318 FatInfo.dataStart = FatInfo.rootStart + FatInfo.rootDirectorySectors;
319 FatInfo.NumberOfClusters = (FatInfo.Sectors - FatInfo.dataStart) / FatInfo.SectorsPerCluster;
320
321 if (pFatInfo && *RecognizedFS)
322 {
323 *pFatInfo = FatInfo;
324 }
325 }
326 }
327 ExFreePool(BootFatX);
328 }
329
330 DPRINT("VfatHasFileSystem done\n");
331 return Status;
332 }
333
334 /*
335 * FUNCTION: Mounts the device
336 */
337 static
338 NTSTATUS
339 VfatMountDevice(
340 PDEVICE_EXTENSION DeviceExt,
341 PDEVICE_OBJECT DeviceToMount)
342 {
343 NTSTATUS Status;
344 BOOLEAN RecognizedFS;
345
346 DPRINT("Mounting VFAT device...\n");
347
348 Status = VfatHasFileSystem(DeviceToMount, &RecognizedFS, &DeviceExt->FatInfo);
349 if (!NT_SUCCESS(Status))
350 {
351 return Status;
352 }
353 DPRINT("MountVfatdev %u, PAGE_SIZE = %d\n", DeviceExt->FatInfo.BytesPerCluster, PAGE_SIZE);
354
355 return STATUS_SUCCESS;
356 }
357
358
359 /*
360 * FUNCTION: Mount the filesystem
361 */
362 static
363 NTSTATUS
364 VfatMount(
365 PVFAT_IRP_CONTEXT IrpContext)
366 {
367 PDEVICE_OBJECT DeviceObject = NULL;
368 PDEVICE_EXTENSION DeviceExt = NULL;
369 BOOLEAN RecognizedFS;
370 NTSTATUS Status;
371 PVFATFCB Fcb = NULL;
372 PVFATFCB VolumeFcb = NULL;
373 PVFATCCB Ccb = NULL;
374 PDEVICE_OBJECT DeviceToMount;
375 PVPB Vpb;
376 UNICODE_STRING NameU = RTL_CONSTANT_STRING(L"\\$$Fat$$");
377 UNICODE_STRING VolumeNameU = RTL_CONSTANT_STRING(L"\\$$Volume$$");
378 ULONG HashTableSize;
379 ULONG eocMark;
380 FATINFO FatInfo;
381
382 DPRINT("VfatMount(IrpContext %p)\n", IrpContext);
383
384 ASSERT(IrpContext);
385
386 if (IrpContext->DeviceObject != VfatGlobalData->DeviceObject)
387 {
388 Status = STATUS_INVALID_DEVICE_REQUEST;
389 goto ByeBye;
390 }
391
392 DeviceToMount = IrpContext->Stack->Parameters.MountVolume.DeviceObject;
393 Vpb = IrpContext->Stack->Parameters.MountVolume.Vpb;
394
395 Status = VfatHasFileSystem(DeviceToMount, &RecognizedFS, &FatInfo);
396 if (!NT_SUCCESS(Status))
397 {
398 goto ByeBye;
399 }
400
401 if (RecognizedFS == FALSE)
402 {
403 DPRINT("VFAT: Unrecognized Volume\n");
404 Status = STATUS_UNRECOGNIZED_VOLUME;
405 goto ByeBye;
406 }
407
408 /* Use prime numbers for the table size */
409 if (FatInfo.FatType == FAT12)
410 {
411 HashTableSize = 4099; // 4096 = 4 * 1024
412 }
413 else if (FatInfo.FatType == FAT16 ||
414 FatInfo.FatType == FATX16)
415 {
416 HashTableSize = 16411; // 16384 = 16 * 1024
417 }
418 else
419 {
420 HashTableSize = 65537; // 65536 = 64 * 1024;
421 }
422 DPRINT("VFAT: Recognized volume\n");
423 Status = IoCreateDevice(VfatGlobalData->DriverObject,
424 ROUND_UP(sizeof (DEVICE_EXTENSION), sizeof(ULONG)) + sizeof(HASHENTRY*) * HashTableSize,
425 NULL,
426 FILE_DEVICE_DISK_FILE_SYSTEM,
427 DeviceToMount->Characteristics,
428 FALSE,
429 &DeviceObject);
430 if (!NT_SUCCESS(Status))
431 {
432 goto ByeBye;
433 }
434
435 DeviceExt = 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 DeviceExt->VolumeDevice = DeviceObject;
440
441 /* use same vpb as device disk */
442 DeviceObject->Vpb = Vpb;
443 DeviceToMount->Vpb = Vpb;
444
445 Status = VfatMountDevice(DeviceExt, DeviceToMount);
446 if (!NT_SUCCESS(Status))
447 {
448 /* FIXME: delete device object */
449 goto ByeBye;
450 }
451
452 DPRINT("BytesPerSector: %u\n", DeviceExt->FatInfo.BytesPerSector);
453 DPRINT("SectorsPerCluster: %u\n", DeviceExt->FatInfo.SectorsPerCluster);
454 DPRINT("FATCount: %u\n", DeviceExt->FatInfo.FATCount);
455 DPRINT("FATSectors: %u\n", DeviceExt->FatInfo.FATSectors);
456 DPRINT("RootStart: %u\n", DeviceExt->FatInfo.rootStart);
457 DPRINT("DataStart: %u\n", DeviceExt->FatInfo.dataStart);
458 if (DeviceExt->FatInfo.FatType == FAT32)
459 {
460 DPRINT("RootCluster: %u\n", DeviceExt->FatInfo.RootCluster);
461 }
462
463 switch (DeviceExt->FatInfo.FatType)
464 {
465 case FAT12:
466 DeviceExt->GetNextCluster = FAT12GetNextCluster;
467 DeviceExt->FindAndMarkAvailableCluster = FAT12FindAndMarkAvailableCluster;
468 DeviceExt->WriteCluster = FAT12WriteCluster;
469 DeviceExt->CleanShutBitMask = 0;
470 break;
471
472 case FAT16:
473 case FATX16:
474 DeviceExt->GetNextCluster = FAT16GetNextCluster;
475 DeviceExt->FindAndMarkAvailableCluster = FAT16FindAndMarkAvailableCluster;
476 DeviceExt->WriteCluster = FAT16WriteCluster;
477 DeviceExt->CleanShutBitMask = 0x8000;
478 break;
479
480 case FAT32:
481 case FATX32:
482 DeviceExt->GetNextCluster = FAT32GetNextCluster;
483 DeviceExt->FindAndMarkAvailableCluster = FAT32FindAndMarkAvailableCluster;
484 DeviceExt->WriteCluster = FAT32WriteCluster;
485 DeviceExt->CleanShutBitMask = 0x80000000;
486 break;
487 }
488
489 if (DeviceExt->FatInfo.FatType == FATX16 ||
490 DeviceExt->FatInfo.FatType == FATX32)
491 {
492 DeviceExt->Flags |= VCB_IS_FATX;
493 DeviceExt->GetNextDirEntry = FATXGetNextDirEntry;
494 DeviceExt->BaseDateYear = 2000;
495 }
496 else
497 {
498 DeviceExt->GetNextDirEntry = FATGetNextDirEntry;
499 DeviceExt->BaseDateYear = 1980;
500 }
501
502 DeviceExt->StorageDevice = DeviceToMount;
503 DeviceExt->StorageDevice->Vpb->DeviceObject = DeviceObject;
504 DeviceExt->StorageDevice->Vpb->RealDevice = DeviceExt->StorageDevice;
505 DeviceExt->StorageDevice->Vpb->Flags |= VPB_MOUNTED;
506 DeviceObject->StackSize = DeviceExt->StorageDevice->StackSize + 1;
507 DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
508
509 DPRINT("FsDeviceObject %p\n", DeviceObject);
510
511 /* Initialize this resource early ... it's used in VfatCleanup */
512 ExInitializeResourceLite(&DeviceExt->DirResource);
513
514 DeviceExt->IoVPB = DeviceObject->Vpb;
515 DeviceExt->SpareVPB = ExAllocatePoolWithTag(NonPagedPool, sizeof(VPB), TAG_VFAT);
516 if (DeviceExt->SpareVPB == NULL)
517 {
518 Status = STATUS_INSUFFICIENT_RESOURCES;
519 goto ByeBye;
520 }
521
522 DeviceExt->FATFileObject = IoCreateStreamFileObject(NULL, DeviceExt->StorageDevice);
523 Fcb = vfatNewFCB(DeviceExt, &NameU);
524 if (Fcb == NULL)
525 {
526 Status = STATUS_INSUFFICIENT_RESOURCES;
527 goto ByeBye;
528 }
529
530 Ccb = ExAllocateFromNPagedLookasideList(&VfatGlobalData->CcbLookasideList);
531 if (Ccb == NULL)
532 {
533 Status = STATUS_INSUFFICIENT_RESOURCES;
534 goto ByeBye;
535 }
536
537 RtlZeroMemory(Ccb, sizeof (VFATCCB));
538 DeviceExt->FATFileObject->FsContext = Fcb;
539 DeviceExt->FATFileObject->FsContext2 = Ccb;
540 DeviceExt->FATFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
541 DeviceExt->FATFileObject->PrivateCacheMap = NULL;
542 DeviceExt->FATFileObject->Vpb = DeviceObject->Vpb;
543 Fcb->FileObject = DeviceExt->FATFileObject;
544
545 Fcb->Flags |= FCB_IS_FAT;
546
547 Fcb->RFCB.FileSize.QuadPart = DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector;
548 Fcb->RFCB.ValidDataLength = Fcb->RFCB.FileSize;
549 Fcb->RFCB.AllocationSize = Fcb->RFCB.FileSize;
550
551 _SEH2_TRY
552 {
553 CcInitializeCacheMap(DeviceExt->FATFileObject,
554 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
555 TRUE,
556 &VfatGlobalData->CacheMgrCallbacks,
557 Fcb);
558 }
559 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
560 {
561 Status = _SEH2_GetExceptionCode();
562 goto ByeBye;
563 }
564 _SEH2_END;
565
566 DeviceExt->LastAvailableCluster = 2;
567 ExInitializeResourceLite(&DeviceExt->FatResource);
568
569 InitializeListHead(&DeviceExt->FcbListHead);
570
571 VolumeFcb = vfatNewFCB(DeviceExt, &VolumeNameU);
572 if (VolumeFcb == NULL)
573 {
574 Status = STATUS_INSUFFICIENT_RESOURCES;
575 goto ByeBye;
576 }
577
578 VolumeFcb->Flags = FCB_IS_VOLUME;
579 VolumeFcb->RFCB.FileSize.QuadPart = DeviceExt->FatInfo.Sectors * DeviceExt->FatInfo.BytesPerSector;
580 VolumeFcb->RFCB.ValidDataLength = VolumeFcb->RFCB.FileSize;
581 VolumeFcb->RFCB.AllocationSize = VolumeFcb->RFCB.FileSize;
582 DeviceExt->VolumeFcb = VolumeFcb;
583
584 ExAcquireResourceExclusiveLite(&VfatGlobalData->VolumeListLock, TRUE);
585 InsertHeadList(&VfatGlobalData->VolumeListHead, &DeviceExt->VolumeListEntry);
586 ExReleaseResourceLite(&VfatGlobalData->VolumeListLock);
587
588 /* read serial number */
589 DeviceObject->Vpb->SerialNumber = DeviceExt->FatInfo.VolumeID;
590
591 /* read volume label */
592 ReadVolumeLabel(DeviceExt, DeviceObject->Vpb);
593
594 /* read clean shutdown bit status */
595 Status = GetNextCluster(DeviceExt, 1, &eocMark);
596 if (NT_SUCCESS(Status))
597 {
598 if (eocMark & DeviceExt->CleanShutBitMask)
599 {
600 /* unset clean shutdown bit */
601 eocMark &= ~DeviceExt->CleanShutBitMask;
602 WriteCluster(DeviceExt, 1, eocMark);
603 VolumeFcb->Flags |= VCB_CLEAR_DIRTY;
604 }
605 }
606
607 VolumeFcb->Flags |= VCB_IS_DIRTY;
608
609 FsRtlNotifyVolumeEvent(DeviceExt->FATFileObject, FSRTL_VOLUME_MOUNT);
610 FsRtlNotifyInitializeSync(&DeviceExt->NotifySync);
611 InitializeListHead(&DeviceExt->NotifyList);
612
613 DPRINT("Mount success\n");
614
615 Status = STATUS_SUCCESS;
616
617 ByeBye:
618 if (!NT_SUCCESS(Status))
619 {
620 /* Cleanup */
621 if (DeviceExt && DeviceExt->FATFileObject)
622 ObDereferenceObject (DeviceExt->FATFileObject);
623 if (DeviceExt && DeviceExt->SpareVPB)
624 ExFreePoolWithTag(DeviceExt->SpareVPB, TAG_VFAT);
625 if (Fcb)
626 vfatDestroyFCB(Fcb);
627 if (Ccb)
628 vfatDestroyCCB(Ccb);
629 if (DeviceObject)
630 IoDeleteDevice(DeviceObject);
631 }
632
633 return Status;
634 }
635
636
637 /*
638 * FUNCTION: Verify the filesystem
639 */
640 static
641 NTSTATUS
642 VfatVerify(
643 PVFAT_IRP_CONTEXT IrpContext)
644 {
645 PDEVICE_OBJECT DeviceToVerify;
646 NTSTATUS Status = STATUS_SUCCESS;
647 FATINFO FatInfo;
648 BOOLEAN RecognizedFS;
649 PDEVICE_EXTENSION DeviceExt = IrpContext->DeviceExt;
650
651 DPRINT("VfatVerify(IrpContext %p)\n", IrpContext);
652
653 DeviceToVerify = IrpContext->Stack->Parameters.VerifyVolume.DeviceObject;
654 Status = VfatBlockDeviceIoControl(DeviceToVerify,
655 IOCTL_DISK_CHECK_VERIFY,
656 NULL,
657 0,
658 NULL,
659 0,
660 TRUE);
661 DeviceToVerify->Flags &= ~DO_VERIFY_VOLUME;
662 if (!NT_SUCCESS(Status) && Status != STATUS_VERIFY_REQUIRED)
663 {
664 DPRINT("VfatBlockDeviceIoControl() failed (Status %lx)\n", Status);
665 Status = STATUS_WRONG_VOLUME;
666 }
667 else
668 {
669 Status = VfatHasFileSystem(DeviceToVerify, &RecognizedFS, &FatInfo);
670 if (!NT_SUCCESS(Status) || RecognizedFS == FALSE)
671 {
672 Status = STATUS_WRONG_VOLUME;
673 }
674 else if (sizeof(FATINFO) == RtlCompareMemory(&FatInfo, &DeviceExt->FatInfo, sizeof(FATINFO)))
675 {
676 /*
677 * FIXME:
678 * Preformated floppy disks have very often a serial number of 0000:0000.
679 * We should calculate a crc sum over the sectors from the root directory as secondary volume number.
680 * Each write to the root directory must update this crc sum.
681 */
682 }
683 else
684 {
685 Status = STATUS_WRONG_VOLUME;
686 }
687 }
688
689 return Status;
690 }
691
692
693 static
694 NTSTATUS
695 VfatGetVolumeBitmap(
696 PVFAT_IRP_CONTEXT IrpContext)
697 {
698 DPRINT("VfatGetVolumeBitmap (IrpContext %p)\n", IrpContext);
699 return STATUS_INVALID_DEVICE_REQUEST;
700 }
701
702
703 static
704 NTSTATUS
705 VfatGetRetrievalPointers(
706 PVFAT_IRP_CONTEXT IrpContext)
707 {
708 PIO_STACK_LOCATION Stack;
709 LARGE_INTEGER Vcn;
710 PRETRIEVAL_POINTERS_BUFFER RetrievalPointers;
711 PFILE_OBJECT FileObject;
712 ULONG MaxExtentCount;
713 PVFATFCB Fcb;
714 PDEVICE_EXTENSION DeviceExt;
715 ULONG FirstCluster;
716 ULONG CurrentCluster;
717 ULONG LastCluster;
718 NTSTATUS Status;
719
720 DPRINT("VfatGetRetrievalPointers(IrpContext %p)\n", IrpContext);
721
722 DeviceExt = IrpContext->DeviceExt;
723 FileObject = IrpContext->FileObject;
724 Stack = IrpContext->Stack;
725 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(STARTING_VCN_INPUT_BUFFER) ||
726 Stack->Parameters.DeviceIoControl.Type3InputBuffer == NULL)
727 {
728 return STATUS_INVALID_PARAMETER;
729 }
730
731 if (IrpContext->Irp->UserBuffer == NULL ||
732 Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(RETRIEVAL_POINTERS_BUFFER))
733 {
734 return STATUS_BUFFER_TOO_SMALL;
735 }
736
737 Fcb = FileObject->FsContext;
738
739 ExAcquireResourceSharedLite(&Fcb->MainResource, TRUE);
740
741 Vcn = ((PSTARTING_VCN_INPUT_BUFFER)Stack->Parameters.DeviceIoControl.Type3InputBuffer)->StartingVcn;
742 RetrievalPointers = IrpContext->Irp->UserBuffer;
743
744 MaxExtentCount = ((Stack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(RetrievalPointers->ExtentCount) - sizeof(RetrievalPointers->StartingVcn)) / sizeof(RetrievalPointers->Extents[0]));
745
746 if (Vcn.QuadPart >= Fcb->RFCB.AllocationSize.QuadPart / DeviceExt->FatInfo.BytesPerCluster)
747 {
748 Status = STATUS_INVALID_PARAMETER;
749 goto ByeBye;
750 }
751
752 CurrentCluster = FirstCluster = vfatDirEntryGetFirstCluster(DeviceExt, &Fcb->entry);
753 Status = OffsetToCluster(DeviceExt, FirstCluster,
754 Vcn.u.LowPart * DeviceExt->FatInfo.BytesPerCluster,
755 &CurrentCluster, FALSE);
756 if (!NT_SUCCESS(Status))
757 {
758 goto ByeBye;
759 }
760
761 RetrievalPointers->StartingVcn = Vcn;
762 RetrievalPointers->ExtentCount = 0;
763 RetrievalPointers->Extents[0].Lcn.u.HighPart = 0;
764 RetrievalPointers->Extents[0].Lcn.u.LowPart = CurrentCluster - 2;
765 LastCluster = 0;
766 while (CurrentCluster != 0xffffffff && RetrievalPointers->ExtentCount < MaxExtentCount)
767 {
768 LastCluster = CurrentCluster;
769 Status = NextCluster(DeviceExt, CurrentCluster, &CurrentCluster, FALSE);
770 Vcn.QuadPart++;
771 if (!NT_SUCCESS(Status))
772 {
773 goto ByeBye;
774 }
775
776 if (LastCluster + 1 != CurrentCluster)
777 {
778 RetrievalPointers->Extents[RetrievalPointers->ExtentCount].NextVcn = Vcn;
779 RetrievalPointers->ExtentCount++;
780 if (RetrievalPointers->ExtentCount < MaxExtentCount)
781 {
782 RetrievalPointers->Extents[RetrievalPointers->ExtentCount].Lcn.u.HighPart = 0;
783 RetrievalPointers->Extents[RetrievalPointers->ExtentCount].Lcn.u.LowPart = CurrentCluster - 2;
784 }
785 }
786 }
787
788 IrpContext->Irp->IoStatus.Information = sizeof(RETRIEVAL_POINTERS_BUFFER) + (sizeof(RetrievalPointers->Extents[0]) * (RetrievalPointers->ExtentCount - 1));
789 Status = STATUS_SUCCESS;
790
791 ByeBye:
792 ExReleaseResourceLite(&Fcb->MainResource);
793
794 return Status;
795 }
796
797 static
798 NTSTATUS
799 VfatMoveFile(
800 PVFAT_IRP_CONTEXT IrpContext)
801 {
802 DPRINT("VfatMoveFile(IrpContext %p)\n", IrpContext);
803 return STATUS_INVALID_DEVICE_REQUEST;
804 }
805
806 static
807 NTSTATUS
808 VfatIsVolumeDirty(
809 PVFAT_IRP_CONTEXT IrpContext)
810 {
811 PULONG Flags;
812
813 DPRINT("VfatIsVolumeDirty(IrpContext %p)\n", IrpContext);
814
815 if (IrpContext->Stack->Parameters.FileSystemControl.OutputBufferLength != sizeof(ULONG))
816 return STATUS_INVALID_BUFFER_SIZE;
817 else if (!IrpContext->Irp->AssociatedIrp.SystemBuffer)
818 return STATUS_INVALID_USER_BUFFER;
819
820 Flags = (PULONG)IrpContext->Irp->AssociatedIrp.SystemBuffer;
821 *Flags = 0;
822
823 if ((IrpContext->DeviceExt->VolumeFcb->Flags & VCB_IS_DIRTY) &&
824 !(IrpContext->DeviceExt->VolumeFcb->Flags & VCB_CLEAR_DIRTY))
825 {
826 *Flags |= VOLUME_IS_DIRTY;
827 }
828
829 return STATUS_SUCCESS;
830 }
831
832 static
833 NTSTATUS
834 VfatMarkVolumeDirty(
835 PVFAT_IRP_CONTEXT IrpContext)
836 {
837 ULONG eocMark;
838 PDEVICE_EXTENSION DeviceExt;
839 NTSTATUS Status = STATUS_SUCCESS;
840
841 DPRINT("VfatMarkVolumeDirty(IrpContext %p)\n", IrpContext);
842 DeviceExt = IrpContext->DeviceExt;
843
844 if (!(DeviceExt->VolumeFcb->Flags & VCB_IS_DIRTY))
845 {
846 Status = GetNextCluster(DeviceExt, 1, &eocMark);
847 if (NT_SUCCESS(Status))
848 {
849 /* unset clean shutdown bit */
850 eocMark &= ~DeviceExt->CleanShutBitMask;
851 Status = WriteCluster(DeviceExt, 1, eocMark);
852 }
853 }
854
855 DeviceExt->VolumeFcb->Flags &= ~VCB_CLEAR_DIRTY;
856
857 return Status;
858 }
859
860 static
861 NTSTATUS
862 VfatLockOrUnlockVolume(
863 PVFAT_IRP_CONTEXT IrpContext,
864 BOOLEAN Lock)
865 {
866 PFILE_OBJECT FileObject;
867 PDEVICE_EXTENSION DeviceExt;
868 PVFATFCB Fcb;
869
870 DPRINT("VfatLockOrUnlockVolume(%p, %d)\n", IrpContext, Lock);
871
872 DeviceExt = IrpContext->DeviceExt;
873 FileObject = IrpContext->FileObject;
874 Fcb = FileObject->FsContext;
875
876 /* Only allow locking with the volume open */
877 if (!(Fcb->Flags & FCB_IS_VOLUME))
878 {
879 return STATUS_ACCESS_DENIED;
880 }
881
882 /* Bail out if it's already in the demanded state */
883 if (((DeviceExt->Flags & VCB_VOLUME_LOCKED) && Lock) ||
884 (!(DeviceExt->Flags & VCB_VOLUME_LOCKED) && !Lock))
885 {
886 return STATUS_ACCESS_DENIED;
887 }
888
889 /* Deny locking if we're not alone */
890 if (Lock && DeviceExt->OpenHandleCount != 1)
891 {
892 return STATUS_ACCESS_DENIED;
893 }
894
895 /* Finally, proceed */
896 if (Lock)
897 {
898 DeviceExt->Flags |= VCB_VOLUME_LOCKED;
899 }
900 else
901 {
902 DeviceExt->Flags &= ~VCB_VOLUME_LOCKED;
903 }
904
905 return STATUS_SUCCESS;
906 }
907
908 static
909 NTSTATUS
910 VfatDismountVolume(
911 PVFAT_IRP_CONTEXT IrpContext)
912 {
913 PDEVICE_EXTENSION DeviceExt;
914 PLIST_ENTRY NextEntry;
915 PVFATFCB Fcb;
916 PFILE_OBJECT FileObject;
917 ULONG eocMark;
918 NTSTATUS Status;
919
920 DPRINT("VfatDismountVolume(%p)\n", IrpContext);
921
922 DeviceExt = IrpContext->DeviceExt;
923 FileObject = IrpContext->FileObject;
924
925 /* We HAVE to be locked. Windows also allows dismount with no lock
926 * but we're here mainly for 1st stage, so KISS
927 */
928 if (!(DeviceExt->Flags & VCB_VOLUME_LOCKED))
929 {
930 return STATUS_ACCESS_DENIED;
931 }
932
933 /* Race condition? */
934 if (DeviceExt->Flags & VCB_DISMOUNT_PENDING)
935 {
936 return STATUS_VOLUME_DISMOUNTED;
937 }
938
939 /* Notify we'll dismount. Pass that point there's no reason we fail */
940 FsRtlNotifyVolumeEvent(IrpContext->Stack->FileObject, FSRTL_VOLUME_DISMOUNT);
941
942 ExAcquireResourceExclusiveLite(&DeviceExt->FatResource, TRUE);
943
944 if (DeviceExt->VolumeFcb->Flags & VCB_CLEAR_DIRTY)
945 {
946 /* Set clean shutdown bit */
947 Status = GetNextCluster(DeviceExt, 1, &eocMark);
948 if (NT_SUCCESS(Status))
949 {
950 eocMark |= DeviceExt->CleanShutBitMask;
951 if (NT_SUCCESS(WriteCluster(DeviceExt, 1, eocMark)))
952 DeviceExt->VolumeFcb->Flags &= ~VCB_IS_DIRTY;
953 }
954 }
955
956 /* Flush volume & files */
957 VfatFlushVolume(DeviceExt, (PVFATFCB)FileObject->FsContext);
958
959 /* Rebrowse the FCB in order to free them now */
960 while (!IsListEmpty(&DeviceExt->FcbListHead))
961 {
962 NextEntry = RemoveHeadList(&DeviceExt->FcbListHead);
963 Fcb = CONTAINING_RECORD(NextEntry, VFATFCB, FcbListEntry);
964 vfatDestroyFCB(Fcb);
965 }
966
967 /* Mark we're being dismounted */
968 DeviceExt->Flags |= VCB_DISMOUNT_PENDING;
969 #ifndef ENABLE_SWAPOUT
970 IrpContext->DeviceObject->Vpb->Flags &= ~VPB_MOUNTED;
971 #endif
972
973 ExReleaseResourceLite(&DeviceExt->FatResource);
974
975 /* Release a few resources and quit, we're done */
976 ExDeleteResourceLite(&DeviceExt->DirResource);
977 ExDeleteResourceLite(&DeviceExt->FatResource);
978 ObDereferenceObject(DeviceExt->FATFileObject);
979
980 return STATUS_SUCCESS;
981 }
982
983 /*
984 * FUNCTION: File system control
985 */
986 NTSTATUS
987 VfatFileSystemControl(
988 PVFAT_IRP_CONTEXT IrpContext)
989 {
990 NTSTATUS Status;
991
992 DPRINT("VfatFileSystemControl(IrpContext %p)\n", IrpContext);
993
994 ASSERT(IrpContext);
995 ASSERT(IrpContext->Irp);
996 ASSERT(IrpContext->Stack);
997
998 IrpContext->Irp->IoStatus.Information = 0;
999
1000 switch (IrpContext->MinorFunction)
1001 {
1002 case IRP_MN_KERNEL_CALL:
1003 case IRP_MN_USER_FS_REQUEST:
1004 switch(IrpContext->Stack->Parameters.DeviceIoControl.IoControlCode)
1005 {
1006 case FSCTL_GET_VOLUME_BITMAP:
1007 Status = VfatGetVolumeBitmap(IrpContext);
1008 break;
1009
1010 case FSCTL_GET_RETRIEVAL_POINTERS:
1011 Status = VfatGetRetrievalPointers(IrpContext);
1012 break;
1013
1014 case FSCTL_MOVE_FILE:
1015 Status = VfatMoveFile(IrpContext);
1016 break;
1017
1018 case FSCTL_IS_VOLUME_DIRTY:
1019 Status = VfatIsVolumeDirty(IrpContext);
1020 break;
1021
1022 case FSCTL_MARK_VOLUME_DIRTY:
1023 Status = VfatMarkVolumeDirty(IrpContext);
1024 break;
1025
1026 case FSCTL_LOCK_VOLUME:
1027 Status = VfatLockOrUnlockVolume(IrpContext, TRUE);
1028 break;
1029
1030 case FSCTL_UNLOCK_VOLUME:
1031 Status = VfatLockOrUnlockVolume(IrpContext, FALSE);
1032 break;
1033
1034 case FSCTL_DISMOUNT_VOLUME:
1035 Status = VfatDismountVolume(IrpContext);
1036 break;
1037
1038 default:
1039 Status = STATUS_INVALID_DEVICE_REQUEST;
1040 }
1041 break;
1042
1043 case IRP_MN_MOUNT_VOLUME:
1044 Status = VfatMount(IrpContext);
1045 break;
1046
1047 case IRP_MN_VERIFY_VOLUME:
1048 DPRINT("VFATFS: IRP_MN_VERIFY_VOLUME\n");
1049 Status = VfatVerify(IrpContext);
1050 break;
1051
1052 default:
1053 DPRINT("VFAT FSC: MinorFunction %u\n", IrpContext->MinorFunction);
1054 Status = STATUS_INVALID_DEVICE_REQUEST;
1055 break;
1056 }
1057
1058 return Status;
1059 }