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