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