2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS VFAT filesystem library
4 * FILE: lib\fslib\vfatlib\vfatlib.c
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * Aleksey Bragin (aleksey@reactos.org)
8 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
10 /* fsck.fat.c - User interface
12 Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
13 Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
14 Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch>
16 This program is free software: you can redistribute it and/or modify
17 it under the terms of the GNU General Public License as published by
18 the Free Software Foundation, either version 3 of the License, or
19 (at your option) any later version.
21 This program is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program. If not, see <http://www.gnu.org/licenses/>.
29 The complete text of the GNU General Public License
30 can be found in /usr/share/common-licenses/GPL-3 file.
33 /* INCLUDES *******************************************************************/
41 /* GLOBALS & FUNCTIONS ********************************************************/
43 PFMIFSCALLBACK ChkdskCallback
= NULL
;
45 PVOID FsCheckMemQueue
;
46 ULONG FsCheckTotalFiles
;
50 VfatFormat(IN PUNICODE_STRING DriveRoot
,
51 IN FMIFS_MEDIA_FLAG MediaFlag
,
52 IN PUNICODE_STRING Label
,
53 IN BOOLEAN QuickFormat
,
55 IN PFMIFSCALLBACK Callback
)
57 OBJECT_ATTRIBUTES ObjectAttributes
;
58 DISK_GEOMETRY DiskGeometry
;
61 PARTITION_INFORMATION PartitionInfo
;
62 FORMAT_CONTEXT Context
;
63 NTSTATUS Status
, LockStatus
;
65 DPRINT("VfatFormat(DriveRoot '%wZ')\n", DriveRoot
);
67 Context
.TotalSectorCount
= 0;
68 Context
.CurrentSectorCount
= 0;
69 Context
.Callback
= Callback
;
70 Context
.Success
= FALSE
;
73 InitializeObjectAttributes(&ObjectAttributes
,
79 Status
= NtOpenFile(&FileHandle
,
80 FILE_GENERIC_READ
| FILE_GENERIC_WRITE
| SYNCHRONIZE
,
84 FILE_SYNCHRONOUS_IO_ALERT
);
85 if (!NT_SUCCESS(Status
))
87 DPRINT1("NtOpenFile() failed with status 0x%.08x\n", Status
);
91 Status
= NtDeviceIoControlFile(FileHandle
,
96 IOCTL_DISK_GET_DRIVE_GEOMETRY
,
100 sizeof(DISK_GEOMETRY
));
101 if (!NT_SUCCESS(Status
))
103 DPRINT("IOCTL_DISK_GET_DRIVE_GEOMETRY failed with status 0x%.08x\n", Status
);
108 if (DiskGeometry
.MediaType
== FixedMedia
)
110 DPRINT("Cylinders %I64d\n", DiskGeometry
.Cylinders
.QuadPart
);
111 DPRINT("TracksPerCylinder %ld\n", DiskGeometry
.TracksPerCylinder
);
112 DPRINT("SectorsPerTrack %ld\n", DiskGeometry
.SectorsPerTrack
);
113 DPRINT("BytesPerSector %ld\n", DiskGeometry
.BytesPerSector
);
114 DPRINT("DiskSize %I64d\n",
115 DiskGeometry
.Cylinders
.QuadPart
*
116 (ULONGLONG
)DiskGeometry
.TracksPerCylinder
*
117 (ULONGLONG
)DiskGeometry
.SectorsPerTrack
*
118 (ULONGLONG
)DiskGeometry
.BytesPerSector
);
120 Status
= NtDeviceIoControlFile(FileHandle
,
125 IOCTL_DISK_GET_PARTITION_INFO
,
129 sizeof(PARTITION_INFORMATION
));
130 if (!NT_SUCCESS(Status
))
132 DPRINT("IOCTL_DISK_GET_PARTITION_INFO failed with status 0x%.08x\n", Status
);
139 PartitionInfo
.PartitionType
= 0;
140 PartitionInfo
.StartingOffset
.QuadPart
= 0ULL;
141 PartitionInfo
.PartitionLength
.QuadPart
=
142 DiskGeometry
.Cylinders
.QuadPart
*
143 (ULONGLONG
)DiskGeometry
.TracksPerCylinder
*
144 (ULONGLONG
)DiskGeometry
.SectorsPerTrack
*
145 (ULONGLONG
)DiskGeometry
.BytesPerSector
;
146 PartitionInfo
.HiddenSectors
= 0;
147 PartitionInfo
.PartitionNumber
= 0;
148 PartitionInfo
.BootIndicator
= FALSE
;
149 PartitionInfo
.RewritePartition
= FALSE
;
150 PartitionInfo
.RecognizedPartition
= FALSE
;
153 /* If it already has a FAT FS, we'll use that type.
154 * If it doesn't, we will determine the FAT type based on size and offset */
155 if (PartitionInfo
.PartitionType
!= PARTITION_FAT_12
&&
156 PartitionInfo
.PartitionType
!= PARTITION_FAT_16
&&
157 PartitionInfo
.PartitionType
!= PARTITION_HUGE
&&
158 PartitionInfo
.PartitionType
!= PARTITION_XINT13
&&
159 PartitionInfo
.PartitionType
!= PARTITION_FAT32
&&
160 PartitionInfo
.PartitionType
!= PARTITION_FAT32_XINT13
)
162 /* Determine the correct type based upon size and offset (copied from usetup) */
163 if (PartitionInfo
.PartitionLength
.QuadPart
< (4200LL * 1024LL))
165 /* FAT12 CHS partition (disk is smaller than 4.1MB) */
166 PartitionInfo
.PartitionType
= PARTITION_FAT_12
;
168 else if (PartitionInfo
.StartingOffset
.QuadPart
< (1024LL * 255LL * 63LL * 512LL))
170 /* Partition starts below the 8.4GB boundary ==> CHS partition */
172 if (PartitionInfo
.PartitionLength
.QuadPart
< (32LL * 1024LL * 1024LL))
174 /* FAT16 CHS partition (partition size < 32MB) */
175 PartitionInfo
.PartitionType
= PARTITION_FAT_16
;
177 else if (PartitionInfo
.PartitionLength
.QuadPart
< (512LL * 1024LL * 1024LL))
179 /* FAT16 CHS partition (partition size < 512MB) */
180 PartitionInfo
.PartitionType
= PARTITION_HUGE
;
184 /* FAT32 CHS partition (partition size >= 512MB) */
185 PartitionInfo
.PartitionType
= PARTITION_FAT32
;
190 /* Partition starts above the 8.4GB boundary ==> LBA partition */
192 if (PartitionInfo
.PartitionLength
.QuadPart
< (512LL * 1024LL * 1024LL))
194 /* FAT16 LBA partition (partition size < 512MB) */
195 PartitionInfo
.PartitionType
= PARTITION_XINT13
;
199 /* FAT32 LBA partition (partition size >= 512MB) */
200 PartitionInfo
.PartitionType
= PARTITION_FAT32_XINT13
;
205 DPRINT("PartitionType 0x%x\n", PartitionInfo
.PartitionType
);
206 DPRINT("StartingOffset %I64d\n", PartitionInfo
.StartingOffset
.QuadPart
);
207 DPRINT("PartitionLength %I64d\n", PartitionInfo
.PartitionLength
.QuadPart
);
208 DPRINT("HiddenSectors %lu\n", PartitionInfo
.HiddenSectors
);
209 DPRINT("PartitionNumber %d\n", PartitionInfo
.PartitionNumber
);
210 DPRINT("BootIndicator 0x%x\n", PartitionInfo
.BootIndicator
);
211 DPRINT("RewritePartition %d\n", PartitionInfo
.RewritePartition
);
212 DPRINT("RecognizedPartition %d\n", PartitionInfo
.RecognizedPartition
);
214 if (Callback
!= NULL
)
217 Callback (PROGRESS
, 0, (PVOID
)&Context
.Percent
);
220 LockStatus
= NtFsControlFile(FileHandle
,
230 if (!NT_SUCCESS(LockStatus
))
232 DPRINT1("WARNING: Failed to lock volume for formatting! Format may fail! (Status: 0x%x)\n", LockStatus
);
235 if (PartitionInfo
.PartitionType
== PARTITION_FAT_12
)
238 Status
= Fat12Format(FileHandle
,
246 else if (PartitionInfo
.PartitionType
== PARTITION_FAT_16
||
247 PartitionInfo
.PartitionType
== PARTITION_HUGE
||
248 PartitionInfo
.PartitionType
== PARTITION_XINT13
)
251 Status
= Fat16Format(FileHandle
,
259 else if (PartitionInfo
.PartitionType
== PARTITION_FAT32
||
260 PartitionInfo
.PartitionType
== PARTITION_FAT32_XINT13
)
263 Status
= Fat32Format(FileHandle
,
273 Status
= STATUS_INVALID_PARAMETER
;
276 /* Attempt to dismount formatted volume */
277 LockStatus
= NtFsControlFile(FileHandle
,
282 FSCTL_DISMOUNT_VOLUME
,
287 if (!NT_SUCCESS(LockStatus
))
289 DPRINT1("Failed to umount volume (Status: 0x%x)\n", LockStatus
);
292 LockStatus
= NtFsControlFile(FileHandle
,
302 if (!NT_SUCCESS(LockStatus
))
304 DPRINT1("Failed to unlock volume (Status: 0x%x)\n", LockStatus
);
309 if (Callback
!= NULL
)
311 Context
.Success
= (BOOLEAN
)(NT_SUCCESS(Status
));
312 Callback(DONE
, 0, (PVOID
)&Context
.Success
);
315 DPRINT("VfatFormat() done. Status 0x%.08x\n", Status
);
322 UpdateProgress(PFORMAT_CONTEXT Context
,
327 Context
->CurrentSectorCount
+= (ULONGLONG
)Increment
;
329 NewPercent
= (Context
->CurrentSectorCount
* 100ULL) / Context
->TotalSectorCount
;
331 if (NewPercent
> Context
->Percent
)
333 Context
->Percent
= NewPercent
;
334 if (Context
->Callback
!= NULL
)
336 Context
->Callback(PROGRESS
, 0, &Context
->Percent
);
343 VfatPrintV(PCHAR Format
, va_list args
)
348 _vsnprintf(TextBuf
, sizeof(TextBuf
), Format
, args
);
350 /* Prepare parameters */
352 TextOut
.Output
= TextBuf
;
354 DPRINT1("VfatPrint -- %s", TextBuf
);
356 /* Do the callback */
358 ChkdskCallback(OUTPUT
, 0, &TextOut
);
363 VfatPrint(PCHAR Format
, ...)
367 va_start(args
, Format
);
368 VfatPrintV(Format
, args
);
375 VfatChkdsk(IN PUNICODE_STRING DriveRoot
,
376 IN BOOLEAN FixErrors
,
378 IN BOOLEAN CheckOnlyIfDirty
,
379 IN BOOLEAN ScanDrive
,
380 IN PFMIFSCALLBACK Callback
)
383 BOOLEAN salvage_files
;
387 RtlZeroMemory(&fs
, sizeof(fs
));
389 /* Store callback pointer */
390 ChkdskCallback
= Callback
;
391 FsCheckMemQueue
= NULL
;
396 FsCheckFlags
|= FSCHECK_VERBOSE
;
398 FsCheckFlags
|= FSCHECK_READ_WRITE
;
400 FsCheckTotalFiles
= 0;
403 salvage_files
= TRUE
;
405 /* Open filesystem and lock it */
406 fs_open(DriveRoot
, FsCheckFlags
& FSCHECK_READ_WRITE
);
408 if (CheckOnlyIfDirty
&& !fs_isdirty())
410 /* Unlock volume if required */
411 if (FsCheckFlags
& FSCHECK_READ_WRITE
)
414 /* No need to check FS */
415 return (fs_close(FALSE
) == 0 ? STATUS_SUCCESS
: STATUS_DISK_CORRUPT_ERROR
);
421 VfatPrint("Starting check/repair pass.\n");
423 while (read_fat(&fs
), scan_root(&fs
))
424 qfree(&FsCheckMemQueue
);
434 free_clusters
= update_free(&fs
);
436 qfree(&FsCheckMemQueue
);
439 FsCheckTotalFiles
= 0;
440 VfatPrint("Starting verification pass.\n");
444 qfree(&FsCheckMemQueue
);
449 if (FsCheckFlags
& FSCHECK_READ_WRITE
)
451 if (FsCheckFlags
& FSCHECK_INTERACTIVE
)
453 FixErrors
= get_key("yn", "Perform changes ? (y/n)") == 'y';
455 FsCheckFlags
|= FSCHECK_READ_WRITE
;
457 FsCheckFlags
&= ~FSCHECK_READ_WRITE
;
460 VfatPrint("Performing changes.\n");
464 VfatPrint("Leaving filesystem unchanged.\n");
468 VfatPrint("%wZ: %u files, %lu/%lu clusters\n", DriveRoot
,
469 FsCheckTotalFiles
, fs
.data_clusters
- free_clusters
, fs
.data_clusters
);
471 if (FsCheckFlags
& FSCHECK_READ_WRITE
)
473 /* Dismount the volume */
476 /* Unlock the volume */
480 // https://technet.microsoft.com/en-us/library/cc730714.aspx
481 // https://support.microsoft.com/en-us/kb/265533
483 /* Close the volume */
484 return (fs_close(FsCheckFlags
& FSCHECK_READ_WRITE
) == 0 ? STATUS_SUCCESS
: STATUS_DISK_CORRUPT_ERROR
);