1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS VFAT filesystem library
4 * FILE: lib\fslib\vfatlib\vfatlib.c
5 * PURPOSE: Main API
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * Aleksey Bragin (aleksey@reactos.org)
8 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
9 */
10 /* fsck.fat.c - User interface
11
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>
15
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.
20
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.
25
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/>.
28
29 The complete text of the GNU General Public License
30 can be found in /usr/share/common-licenses/GPL-3 file.
31 */
32
33 /* INCLUDES *******************************************************************/
34
35 #include "vfatlib.h"
36
37 #define NDEBUG
38 #include <debug.h>
39
40
41 /* GLOBALS & FUNCTIONS ********************************************************/
42
43 PFMIFSCALLBACK ChkdskCallback = NULL;
44 ULONG FsCheckFlags;
45 PVOID FsCheckMemQueue;
46 ULONG FsCheckTotalFiles;
47
48 BOOLEAN
49 NTAPI
VfatFormat(IN PUNICODE_STRING DriveRoot,IN PFMIFSCALLBACK Callback,IN BOOLEAN QuickFormat,IN BOOLEAN BackwardCompatible,IN MEDIA_TYPE MediaType,IN PUNICODE_STRING Label,IN ULONG ClusterSize)50 VfatFormat(
51 IN PUNICODE_STRING DriveRoot,
52 IN PFMIFSCALLBACK Callback,
53 IN BOOLEAN QuickFormat,
54 IN BOOLEAN BackwardCompatible,
55 IN MEDIA_TYPE MediaType,
56 IN PUNICODE_STRING Label,
57 IN ULONG ClusterSize)
58 {
59 OBJECT_ATTRIBUTES ObjectAttributes;
60 DISK_GEOMETRY DiskGeometry;
61 IO_STATUS_BLOCK Iosb;
62 HANDLE FileHandle;
63 PARTITION_INFORMATION PartitionInfo;
64 FORMAT_CONTEXT Context;
65 NTSTATUS Status, LockStatus;
66
67 DPRINT("VfatFormat(DriveRoot '%wZ')\n", DriveRoot);
68
69 // FIXME:
70 UNREFERENCED_PARAMETER(BackwardCompatible);
71 UNREFERENCED_PARAMETER(MediaType);
72
73 Context.TotalSectorCount = 0;
74 Context.CurrentSectorCount = 0;
75 Context.Callback = Callback;
76 Context.Success = FALSE;
77 Context.Percent = 0;
78
79 InitializeObjectAttributes(&ObjectAttributes,
80 DriveRoot,
81 0,
82 NULL,
83 NULL);
84
85 Status = NtOpenFile(&FileHandle,
86 FILE_GENERIC_READ | FILE_GENERIC_WRITE | SYNCHRONIZE,
87 &ObjectAttributes,
88 &Iosb,
89 FILE_SHARE_READ,
90 FILE_SYNCHRONOUS_IO_ALERT);
91 if (!NT_SUCCESS(Status))
92 {
93 DPRINT1("NtOpenFile() failed with status 0x%08x\n", Status);
94 return FALSE;
95 }
96
97 Status = NtDeviceIoControlFile(FileHandle,
98 NULL,
99 NULL,
100 NULL,
101 &Iosb,
102 IOCTL_DISK_GET_DRIVE_GEOMETRY,
103 NULL,
104 0,
105 &DiskGeometry,
106 sizeof(DISK_GEOMETRY));
107 if (!NT_SUCCESS(Status))
108 {
109 DPRINT("IOCTL_DISK_GET_DRIVE_GEOMETRY failed with status 0x%08x\n", Status);
110 NtClose(FileHandle);
111 return FALSE;
112 }
113
114 if (DiskGeometry.MediaType == FixedMedia)
115 {
116 DPRINT("Cylinders %I64d\n", DiskGeometry.Cylinders.QuadPart);
117 DPRINT("TracksPerCylinder %ld\n", DiskGeometry.TracksPerCylinder);
118 DPRINT("SectorsPerTrack %ld\n", DiskGeometry.SectorsPerTrack);
119 DPRINT("BytesPerSector %ld\n", DiskGeometry.BytesPerSector);
120 DPRINT("DiskSize %I64d\n",
121 DiskGeometry.Cylinders.QuadPart *
122 (ULONGLONG)DiskGeometry.TracksPerCylinder *
123 (ULONGLONG)DiskGeometry.SectorsPerTrack *
124 (ULONGLONG)DiskGeometry.BytesPerSector);
125
126 Status = NtDeviceIoControlFile(FileHandle,
127 NULL,
128 NULL,
129 NULL,
130 &Iosb,
131 IOCTL_DISK_GET_PARTITION_INFO,
132 NULL,
133 0,
134 &PartitionInfo,
135 sizeof(PARTITION_INFORMATION));
136 if (!NT_SUCCESS(Status))
137 {
138 DPRINT("IOCTL_DISK_GET_PARTITION_INFO failed with status 0x%08x\n", Status);
139 NtClose(FileHandle);
140 return FALSE;
141 }
142 }
143 else
144 {
145 PartitionInfo.PartitionType = 0;
146 PartitionInfo.StartingOffset.QuadPart = 0ULL;
147 PartitionInfo.PartitionLength.QuadPart =
148 DiskGeometry.Cylinders.QuadPart *
149 (ULONGLONG)DiskGeometry.TracksPerCylinder *
150 (ULONGLONG)DiskGeometry.SectorsPerTrack *
151 (ULONGLONG)DiskGeometry.BytesPerSector;
152 PartitionInfo.HiddenSectors = 0;
153 PartitionInfo.PartitionNumber = 0;
154 PartitionInfo.BootIndicator = FALSE;
155 PartitionInfo.RewritePartition = FALSE;
156 PartitionInfo.RecognizedPartition = FALSE;
157 }
158
159 /* If it already has a FAT FS, we'll use that type.
160 * If it doesn't, we will determine the FAT type based on size and offset */
161 if (PartitionInfo.PartitionType != PARTITION_FAT_12 &&
162 PartitionInfo.PartitionType != PARTITION_FAT_16 &&
163 PartitionInfo.PartitionType != PARTITION_HUGE &&
164 PartitionInfo.PartitionType != PARTITION_XINT13 &&
165 PartitionInfo.PartitionType != PARTITION_FAT32 &&
166 PartitionInfo.PartitionType != PARTITION_FAT32_XINT13)
167 {
168 /* Determine the correct type based upon size and offset (copied from usetup) */
169 if (PartitionInfo.PartitionLength.QuadPart < (4200LL * 1024LL))
170 {
171 /* FAT12 CHS partition (disk is smaller than 4.1MB) */
172 PartitionInfo.PartitionType = PARTITION_FAT_12;
173 }
174 else if (PartitionInfo.StartingOffset.QuadPart < (1024LL * 255LL * 63LL * 512LL))
175 {
176 /* Partition starts below the 8.4GB boundary ==> CHS partition */
177
178 if (PartitionInfo.PartitionLength.QuadPart < (32LL * 1024LL * 1024LL))
179 {
180 /* FAT16 CHS partition (partition size < 32MB) */
181 PartitionInfo.PartitionType = PARTITION_FAT_16;
182 }
183 else if (PartitionInfo.PartitionLength.QuadPart < (512LL * 1024LL * 1024LL))
184 {
185 /* FAT16 CHS partition (partition size < 512MB) */
186 PartitionInfo.PartitionType = PARTITION_HUGE;
187 }
188 else
189 {
190 /* FAT32 CHS partition (partition size >= 512MB) */
191 PartitionInfo.PartitionType = PARTITION_FAT32;
192 }
193 }
194 else
195 {
196 /* Partition starts above the 8.4GB boundary ==> LBA partition */
197
198 if (PartitionInfo.PartitionLength.QuadPart < (512LL * 1024LL * 1024LL))
199 {
200 /* FAT16 LBA partition (partition size < 512MB) */
201 PartitionInfo.PartitionType = PARTITION_XINT13;
202 }
203 else
204 {
205 /* FAT32 LBA partition (partition size >= 512MB) */
206 PartitionInfo.PartitionType = PARTITION_FAT32_XINT13;
207 }
208 }
209 }
210
211 DPRINT("PartitionType 0x%x\n", PartitionInfo.PartitionType);
212 DPRINT("StartingOffset %I64d\n", PartitionInfo.StartingOffset.QuadPart);
213 DPRINT("PartitionLength %I64d\n", PartitionInfo.PartitionLength.QuadPart);
214 DPRINT("HiddenSectors %lu\n", PartitionInfo.HiddenSectors);
215 DPRINT("PartitionNumber %d\n", PartitionInfo.PartitionNumber);
216 DPRINT("BootIndicator 0x%x\n", PartitionInfo.BootIndicator);
217 DPRINT("RewritePartition %d\n", PartitionInfo.RewritePartition);
218 DPRINT("RecognizedPartition %d\n", PartitionInfo.RecognizedPartition);
219
220 if (Callback != NULL)
221 {
222 Context.Percent = 0;
223 Callback(PROGRESS, 0, (PVOID)&Context.Percent);
224 }
225
226 LockStatus = NtFsControlFile(FileHandle,
227 NULL,
228 NULL,
229 NULL,
230 &Iosb,
231 FSCTL_LOCK_VOLUME,
232 NULL,
233 0,
234 NULL,
235 0);
236 if (!NT_SUCCESS(LockStatus))
237 {
238 DPRINT1("WARNING: Failed to lock volume for formatting! Format may fail! (Status: 0x%x)\n", LockStatus);
239 }
240
241 if (PartitionInfo.PartitionType == PARTITION_FAT_12)
242 {
243 /* FAT12 */
244 Status = Fat12Format(FileHandle,
245 &PartitionInfo,
246 &DiskGeometry,
247 Label,
248 QuickFormat,
249 ClusterSize,
250 &Context);
251 }
252 else if (PartitionInfo.PartitionType == PARTITION_FAT_16 ||
253 PartitionInfo.PartitionType == PARTITION_HUGE ||
254 PartitionInfo.PartitionType == PARTITION_XINT13)
255 {
256 /* FAT16 */
257 Status = Fat16Format(FileHandle,
258 &PartitionInfo,
259 &DiskGeometry,
260 Label,
261 QuickFormat,
262 ClusterSize,
263 &Context);
264 }
265 else if (PartitionInfo.PartitionType == PARTITION_FAT32 ||
266 PartitionInfo.PartitionType == PARTITION_FAT32_XINT13)
267 {
268 /* FAT32 */
269 Status = Fat32Format(FileHandle,
270 &PartitionInfo,
271 &DiskGeometry,
272 Label,
273 QuickFormat,
274 ClusterSize,
275 &Context);
276 }
277 else
278 {
279 Status = STATUS_INVALID_PARAMETER;
280 }
281
282 /* Attempt to dismount formatted volume */
283 LockStatus = NtFsControlFile(FileHandle,
284 NULL,
285 NULL,
286 NULL,
287 &Iosb,
288 FSCTL_DISMOUNT_VOLUME,
289 NULL,
290 0,
291 NULL,
292 0);
293 if (!NT_SUCCESS(LockStatus))
294 {
295 DPRINT1("Failed to umount volume (Status: 0x%x)\n", LockStatus);
296 }
297
298 LockStatus = NtFsControlFile(FileHandle,
299 NULL,
300 NULL,
301 NULL,
302 &Iosb,
303 FSCTL_UNLOCK_VOLUME,
304 NULL,
305 0,
306 NULL,
307 0);
308 if (!NT_SUCCESS(LockStatus))
309 {
310 DPRINT1("Failed to unlock volume (Status: 0x%x)\n", LockStatus);
311 }
312
313 NtClose(FileHandle);
314
315 DPRINT("VfatFormat() done. Status 0x%08x\n", Status);
316 return NT_SUCCESS(Status);
317 }
318
319
320 VOID
UpdateProgress(PFORMAT_CONTEXT Context,ULONG Increment)321 UpdateProgress(PFORMAT_CONTEXT Context,
322 ULONG Increment)
323 {
324 ULONG NewPercent;
325
326 Context->CurrentSectorCount += (ULONGLONG)Increment;
327
328 NewPercent = (Context->CurrentSectorCount * 100ULL) / Context->TotalSectorCount;
329
330 if (NewPercent > Context->Percent)
331 {
332 Context->Percent = NewPercent;
333 if (Context->Callback != NULL)
334 {
335 Context->Callback(PROGRESS, 0, &Context->Percent);
336 }
337 }
338 }
339
340
341 VOID
VfatPrintV(PCHAR Format,va_list args)342 VfatPrintV(PCHAR Format, va_list args)
343 {
344 TEXTOUTPUT TextOut;
345 CHAR TextBuf[512];
346
347 _vsnprintf(TextBuf, sizeof(TextBuf), Format, args);
348
349 /* Prepare parameters */
350 TextOut.Lines = 1;
351 TextOut.Output = TextBuf;
352
353 DPRINT1("VfatPrint -- %s", TextBuf);
354
355 /* Do the callback */
356 if (ChkdskCallback)
357 ChkdskCallback(OUTPUT, 0, &TextOut);
358 }
359
360
361 VOID
VfatPrint(PCHAR Format,...)362 VfatPrint(PCHAR Format, ...)
363 {
364 va_list args;
365
366 va_start(args, Format);
367 VfatPrintV(Format, args);
368 va_end(args);
369 }
370
371
372 BOOLEAN
373 NTAPI
VfatChkdsk(IN PUNICODE_STRING DriveRoot,IN PFMIFSCALLBACK Callback,IN BOOLEAN FixErrors,IN BOOLEAN Verbose,IN BOOLEAN CheckOnlyIfDirty,IN BOOLEAN ScanDrive,IN PVOID pUnknown1,IN PVOID pUnknown2,IN PVOID pUnknown3,IN PVOID pUnknown4,IN PULONG ExitStatus)374 VfatChkdsk(
375 IN PUNICODE_STRING DriveRoot,
376 IN PFMIFSCALLBACK Callback,
377 IN BOOLEAN FixErrors,
378 IN BOOLEAN Verbose,
379 IN BOOLEAN CheckOnlyIfDirty,
380 IN BOOLEAN ScanDrive,
381 IN PVOID pUnknown1,
382 IN PVOID pUnknown2,
383 IN PVOID pUnknown3,
384 IN PVOID pUnknown4,
385 IN PULONG ExitStatus)
386 {
387 BOOLEAN verify;
388 BOOLEAN salvage_files;
389 ULONG free_clusters;
390 DOS_FS fs;
391 NTSTATUS Status;
392
393 UNREFERENCED_PARAMETER(pUnknown1);
394 UNREFERENCED_PARAMETER(pUnknown2);
395 UNREFERENCED_PARAMETER(pUnknown3);
396 UNREFERENCED_PARAMETER(pUnknown4);
397
398 RtlZeroMemory(&fs, sizeof(fs));
399
400 /* Store callback pointer */
401 ChkdskCallback = Callback;
402 FsCheckMemQueue = NULL;
403
404 /* Set parameters */
405 FsCheckFlags = 0;
406 if (Verbose)
407 FsCheckFlags |= FSCHECK_VERBOSE;
408 if (FixErrors)
409 FsCheckFlags |= FSCHECK_READ_WRITE;
410
411 FsCheckTotalFiles = 0;
412
413 verify = TRUE;
414 salvage_files = TRUE;
415
416 /* Open filesystem and lock it */
417 Status = fs_open(DriveRoot, FsCheckFlags & FSCHECK_READ_WRITE);
418 if (Status == STATUS_ACCESS_DENIED)
419 {
420 /* We failed to lock, ask the caller whether we should continue */
421 if (Callback(VOLUMEINUSE, 0, NULL))
422 {
423 Status = STATUS_SUCCESS;
424 }
425 }
426 if (!NT_SUCCESS(Status))
427 {
428 fs_close(FALSE);
429 *ExitStatus = (ULONG)STATUS_DISK_CORRUPT_ERROR;
430 return FALSE;
431 }
432
433 if (CheckOnlyIfDirty && !fs_isdirty())
434 {
435 /* Unlock volume if required */
436 if (FsCheckFlags & FSCHECK_READ_WRITE)
437 fs_lock(FALSE);
438
439 /* No need to check FS */
440 Status = (fs_close(FALSE) == 0 ? STATUS_SUCCESS : STATUS_DISK_CORRUPT_ERROR);
441 *ExitStatus = (ULONG)Status;
442 return (Status == STATUS_SUCCESS);
443 }
444 else if (CheckOnlyIfDirty && fs_isdirty())
445 {
446 if (!(FsCheckFlags & FSCHECK_READ_WRITE) && !(FsCheckFlags & FSCHECK_INTERACTIVE))
447 {
448 fs_close(FALSE);
449 *ExitStatus = (ULONG)STATUS_DISK_CORRUPT_ERROR;
450 return FALSE;
451 }
452 }
453
454 read_boot(&fs);
455
456 if (verify)
457 VfatPrint("Starting check/repair pass.\n");
458
459 while (read_fat(&fs), scan_root(&fs))
460 qfree(&FsCheckMemQueue);
461
462 if (ScanDrive)
463 fix_bad(&fs);
464
465 if (salvage_files)
466 reclaim_file(&fs);
467 else
468 reclaim_free(&fs);
469
470 free_clusters = update_free(&fs);
471 file_unused();
472 qfree(&FsCheckMemQueue);
473 if (verify)
474 {
475 FsCheckTotalFiles = 0;
476 VfatPrint("Starting verification pass.\n");
477 read_fat(&fs);
478 scan_root(&fs);
479 reclaim_free(&fs);
480 qfree(&FsCheckMemQueue);
481 }
482
483 if (fs_changed())
484 {
485 if (FsCheckFlags & FSCHECK_READ_WRITE)
486 {
487 if (FsCheckFlags & FSCHECK_INTERACTIVE)
488 {
489 FixErrors = get_key("yn", "Perform changes ? (y/n)") == 'y';
490 if (FixErrors)
491 FsCheckFlags |= FSCHECK_READ_WRITE;
492 else
493 FsCheckFlags &= ~FSCHECK_READ_WRITE;
494 }
495 else
496 VfatPrint("Performing changes.\n");
497 }
498 else
499 {
500 VfatPrint("Leaving filesystem unchanged.\n");
501 }
502 }
503
504 VfatPrint("%wZ: %u files, %lu/%lu clusters\n", DriveRoot,
505 FsCheckTotalFiles, fs.data_clusters - free_clusters, fs.data_clusters);
506
507 if (FsCheckFlags & FSCHECK_READ_WRITE)
508 {
509 /* Dismount the volume */
510 fs_dismount();
511
512 /* Unlock the volume */
513 fs_lock(FALSE);
514 }
515
516 // https://technet.microsoft.com/en-us/library/cc730714.aspx
517 // https://support.microsoft.com/en-us/kb/265533
518
519 /* Close the volume */
520 Status = (fs_close(FsCheckFlags & FSCHECK_READ_WRITE) == 0 ? STATUS_SUCCESS : STATUS_DISK_CORRUPT_ERROR);
521 *ExitStatus = (ULONG)Status;
522 return (Status == STATUS_SUCCESS);
523 }
524
525 /* EOF */
526