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