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 NTSTATUS 49 NTAPI 50 VfatFormat(IN PUNICODE_STRING DriveRoot, 51 IN FMIFS_MEDIA_FLAG MediaFlag, 52 IN PUNICODE_STRING Label, 53 IN BOOLEAN QuickFormat, 54 IN ULONG ClusterSize, 55 IN PFMIFSCALLBACK Callback) 56 { 57 OBJECT_ATTRIBUTES ObjectAttributes; 58 DISK_GEOMETRY DiskGeometry; 59 IO_STATUS_BLOCK Iosb; 60 HANDLE FileHandle; 61 PARTITION_INFORMATION PartitionInfo; 62 FORMAT_CONTEXT Context; 63 NTSTATUS Status, LockStatus; 64 65 DPRINT("VfatFormat(DriveRoot '%wZ')\n", DriveRoot); 66 67 Context.TotalSectorCount = 0; 68 Context.CurrentSectorCount = 0; 69 Context.Callback = Callback; 70 Context.Success = FALSE; 71 Context.Percent = 0; 72 73 InitializeObjectAttributes(&ObjectAttributes, 74 DriveRoot, 75 0, 76 NULL, 77 NULL); 78 79 Status = NtOpenFile(&FileHandle, 80 FILE_GENERIC_READ | FILE_GENERIC_WRITE | SYNCHRONIZE, 81 &ObjectAttributes, 82 &Iosb, 83 FILE_SHARE_READ, 84 FILE_SYNCHRONOUS_IO_ALERT); 85 if (!NT_SUCCESS(Status)) 86 { 87 DPRINT1("NtOpenFile() failed with status 0x%.08x\n", Status); 88 return Status; 89 } 90 91 Status = NtDeviceIoControlFile(FileHandle, 92 NULL, 93 NULL, 94 NULL, 95 &Iosb, 96 IOCTL_DISK_GET_DRIVE_GEOMETRY, 97 NULL, 98 0, 99 &DiskGeometry, 100 sizeof(DISK_GEOMETRY)); 101 if (!NT_SUCCESS(Status)) 102 { 103 DPRINT("IOCTL_DISK_GET_DRIVE_GEOMETRY failed with status 0x%.08x\n", Status); 104 NtClose(FileHandle); 105 return Status; 106 } 107 108 if (DiskGeometry.MediaType == FixedMedia) 109 { 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); 119 120 Status = NtDeviceIoControlFile(FileHandle, 121 NULL, 122 NULL, 123 NULL, 124 &Iosb, 125 IOCTL_DISK_GET_PARTITION_INFO, 126 NULL, 127 0, 128 &PartitionInfo, 129 sizeof(PARTITION_INFORMATION)); 130 if (!NT_SUCCESS(Status)) 131 { 132 DPRINT("IOCTL_DISK_GET_PARTITION_INFO failed with status 0x%.08x\n", Status); 133 NtClose(FileHandle); 134 return Status; 135 } 136 } 137 else 138 { 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; 151 } 152 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) 161 { 162 /* Determine the correct type based upon size and offset (copied from usetup) */ 163 if (PartitionInfo.PartitionLength.QuadPart < (4200LL * 1024LL)) 164 { 165 /* FAT12 CHS partition (disk is smaller than 4.1MB) */ 166 PartitionInfo.PartitionType = PARTITION_FAT_12; 167 } 168 else if (PartitionInfo.StartingOffset.QuadPart < (1024LL * 255LL * 63LL * 512LL)) 169 { 170 /* Partition starts below the 8.4GB boundary ==> CHS partition */ 171 172 if (PartitionInfo.PartitionLength.QuadPart < (32LL * 1024LL * 1024LL)) 173 { 174 /* FAT16 CHS partition (partition size < 32MB) */ 175 PartitionInfo.PartitionType = PARTITION_FAT_16; 176 } 177 else if (PartitionInfo.PartitionLength.QuadPart < (512LL * 1024LL * 1024LL)) 178 { 179 /* FAT16 CHS partition (partition size < 512MB) */ 180 PartitionInfo.PartitionType = PARTITION_HUGE; 181 } 182 else 183 { 184 /* FAT32 CHS partition (partition size >= 512MB) */ 185 PartitionInfo.PartitionType = PARTITION_FAT32; 186 } 187 } 188 else 189 { 190 /* Partition starts above the 8.4GB boundary ==> LBA partition */ 191 192 if (PartitionInfo.PartitionLength.QuadPart < (512LL * 1024LL * 1024LL)) 193 { 194 /* FAT16 LBA partition (partition size < 512MB) */ 195 PartitionInfo.PartitionType = PARTITION_XINT13; 196 } 197 else 198 { 199 /* FAT32 LBA partition (partition size >= 512MB) */ 200 PartitionInfo.PartitionType = PARTITION_FAT32_XINT13; 201 } 202 } 203 } 204 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); 213 214 if (Callback != NULL) 215 { 216 Context.Percent = 0; 217 Callback (PROGRESS, 0, (PVOID)&Context.Percent); 218 } 219 220 LockStatus = NtFsControlFile(FileHandle, 221 NULL, 222 NULL, 223 NULL, 224 &Iosb, 225 FSCTL_LOCK_VOLUME, 226 NULL, 227 0, 228 NULL, 229 0); 230 if (!NT_SUCCESS(LockStatus)) 231 { 232 DPRINT1("WARNING: Failed to lock volume for formatting! Format may fail! (Status: 0x%x)\n", LockStatus); 233 } 234 235 if (PartitionInfo.PartitionType == PARTITION_FAT_12) 236 { 237 /* FAT12 */ 238 Status = Fat12Format(FileHandle, 239 &PartitionInfo, 240 &DiskGeometry, 241 Label, 242 QuickFormat, 243 ClusterSize, 244 &Context); 245 } 246 else if (PartitionInfo.PartitionType == PARTITION_FAT_16 || 247 PartitionInfo.PartitionType == PARTITION_HUGE || 248 PartitionInfo.PartitionType == PARTITION_XINT13) 249 { 250 /* FAT16 */ 251 Status = Fat16Format(FileHandle, 252 &PartitionInfo, 253 &DiskGeometry, 254 Label, 255 QuickFormat, 256 ClusterSize, 257 &Context); 258 } 259 else if (PartitionInfo.PartitionType == PARTITION_FAT32 || 260 PartitionInfo.PartitionType == PARTITION_FAT32_XINT13) 261 { 262 /* FAT32 */ 263 Status = Fat32Format(FileHandle, 264 &PartitionInfo, 265 &DiskGeometry, 266 Label, 267 QuickFormat, 268 ClusterSize, 269 &Context); 270 } 271 else 272 { 273 Status = STATUS_INVALID_PARAMETER; 274 } 275 276 /* Attempt to dismount formatted volume */ 277 LockStatus = NtFsControlFile(FileHandle, 278 NULL, 279 NULL, 280 NULL, 281 &Iosb, 282 FSCTL_DISMOUNT_VOLUME, 283 NULL, 284 0, 285 NULL, 286 0); 287 if (!NT_SUCCESS(LockStatus)) 288 { 289 DPRINT1("Failed to umount volume (Status: 0x%x)\n", LockStatus); 290 } 291 292 LockStatus = NtFsControlFile(FileHandle, 293 NULL, 294 NULL, 295 NULL, 296 &Iosb, 297 FSCTL_UNLOCK_VOLUME, 298 NULL, 299 0, 300 NULL, 301 0); 302 if (!NT_SUCCESS(LockStatus)) 303 { 304 DPRINT1("Failed to unlock volume (Status: 0x%x)\n", LockStatus); 305 } 306 307 NtClose(FileHandle); 308 309 if (Callback != NULL) 310 { 311 Context.Success = (BOOLEAN)(NT_SUCCESS(Status)); 312 Callback(DONE, 0, (PVOID)&Context.Success); 313 } 314 315 DPRINT("VfatFormat() done. Status 0x%.08x\n", Status); 316 317 return Status; 318 } 319 320 321 VOID 322 UpdateProgress(PFORMAT_CONTEXT Context, 323 ULONG Increment) 324 { 325 ULONG NewPercent; 326 327 Context->CurrentSectorCount += (ULONGLONG)Increment; 328 329 NewPercent = (Context->CurrentSectorCount * 100ULL) / Context->TotalSectorCount; 330 331 if (NewPercent > Context->Percent) 332 { 333 Context->Percent = NewPercent; 334 if (Context->Callback != NULL) 335 { 336 Context->Callback(PROGRESS, 0, &Context->Percent); 337 } 338 } 339 } 340 341 342 VOID 343 VfatPrintV(PCHAR Format, va_list args) 344 { 345 TEXTOUTPUT TextOut; 346 CHAR TextBuf[512]; 347 348 _vsnprintf(TextBuf, sizeof(TextBuf), Format, args); 349 350 /* Prepare parameters */ 351 TextOut.Lines = 1; 352 TextOut.Output = TextBuf; 353 354 DPRINT1("VfatPrint -- %s", TextBuf); 355 356 /* Do the callback */ 357 if (ChkdskCallback) 358 ChkdskCallback(OUTPUT, 0, &TextOut); 359 } 360 361 362 VOID 363 VfatPrint(PCHAR Format, ...) 364 { 365 va_list args; 366 367 va_start(args, Format); 368 VfatPrintV(Format, args); 369 va_end(args); 370 } 371 372 373 NTSTATUS 374 NTAPI 375 VfatChkdsk(IN PUNICODE_STRING DriveRoot, 376 IN BOOLEAN FixErrors, 377 IN BOOLEAN Verbose, 378 IN BOOLEAN CheckOnlyIfDirty, 379 IN BOOLEAN ScanDrive, 380 IN PFMIFSCALLBACK Callback) 381 { 382 BOOLEAN verify; 383 BOOLEAN salvage_files; 384 ULONG free_clusters; 385 DOS_FS fs; 386 NTSTATUS Status; 387 388 RtlZeroMemory(&fs, sizeof(fs)); 389 390 /* Store callback pointer */ 391 ChkdskCallback = Callback; 392 FsCheckMemQueue = NULL; 393 394 /* Set parameters */ 395 FsCheckFlags = 0; 396 if (Verbose) 397 FsCheckFlags |= FSCHECK_VERBOSE; 398 if (FixErrors) 399 FsCheckFlags |= FSCHECK_READ_WRITE; 400 401 FsCheckTotalFiles = 0; 402 403 verify = TRUE; 404 salvage_files = TRUE; 405 406 /* Open filesystem and lock it */ 407 Status = fs_open(DriveRoot, FsCheckFlags & FSCHECK_READ_WRITE); 408 if (Status == STATUS_ACCESS_DENIED) 409 { 410 /* We failed to lock, ask the caller whether we should continue */ 411 if (Callback(VOLUMEINUSE, 0, NULL)) 412 { 413 Status = STATUS_SUCCESS; 414 } 415 } 416 if (!NT_SUCCESS(Status)) 417 { 418 fs_close(FALSE); 419 return STATUS_DISK_CORRUPT_ERROR; 420 } 421 422 if (CheckOnlyIfDirty && !fs_isdirty()) 423 { 424 /* Unlock volume if required */ 425 if (FsCheckFlags & FSCHECK_READ_WRITE) 426 fs_lock(FALSE); 427 428 /* No need to check FS */ 429 return (fs_close(FALSE) == 0 ? STATUS_SUCCESS : STATUS_DISK_CORRUPT_ERROR); 430 } 431 else if (CheckOnlyIfDirty && fs_isdirty()) 432 { 433 if (!(FsCheckFlags & FSCHECK_READ_WRITE) && !(FsCheckFlags & FSCHECK_INTERACTIVE)) 434 { 435 fs_close(FALSE); 436 return STATUS_DISK_CORRUPT_ERROR; 437 } 438 } 439 440 read_boot(&fs); 441 442 if (verify) 443 VfatPrint("Starting check/repair pass.\n"); 444 445 while (read_fat(&fs), scan_root(&fs)) 446 qfree(&FsCheckMemQueue); 447 448 if (ScanDrive) 449 fix_bad(&fs); 450 451 if (salvage_files) 452 reclaim_file(&fs); 453 else 454 reclaim_free(&fs); 455 456 free_clusters = update_free(&fs); 457 file_unused(); 458 qfree(&FsCheckMemQueue); 459 if (verify) 460 { 461 FsCheckTotalFiles = 0; 462 VfatPrint("Starting verification pass.\n"); 463 read_fat(&fs); 464 scan_root(&fs); 465 reclaim_free(&fs); 466 qfree(&FsCheckMemQueue); 467 } 468 469 if (fs_changed()) 470 { 471 if (FsCheckFlags & FSCHECK_READ_WRITE) 472 { 473 if (FsCheckFlags & FSCHECK_INTERACTIVE) 474 { 475 FixErrors = get_key("yn", "Perform changes ? (y/n)") == 'y'; 476 if (FixErrors) 477 FsCheckFlags |= FSCHECK_READ_WRITE; 478 else 479 FsCheckFlags &= ~FSCHECK_READ_WRITE; 480 } 481 else 482 VfatPrint("Performing changes.\n"); 483 } 484 else 485 { 486 VfatPrint("Leaving filesystem unchanged.\n"); 487 } 488 } 489 490 VfatPrint("%wZ: %u files, %lu/%lu clusters\n", DriveRoot, 491 FsCheckTotalFiles, fs.data_clusters - free_clusters, fs.data_clusters); 492 493 if (FsCheckFlags & FSCHECK_READ_WRITE) 494 { 495 /* Dismount the volume */ 496 fs_dismount(); 497 498 /* Unlock the volume */ 499 fs_lock(FALSE); 500 } 501 502 // https://technet.microsoft.com/en-us/library/cc730714.aspx 503 // https://support.microsoft.com/en-us/kb/265533 504 505 /* Close the volume */ 506 return (fs_close(FsCheckFlags & FSCHECK_READ_WRITE) == 0 ? STATUS_SUCCESS : STATUS_DISK_CORRUPT_ERROR); 507 } 508 509 /* EOF */ 510