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