"Fossies" - the Fresh Open Source Software Archive 
Member "rufus-3.13/src/format.c" (20 Nov 2020, 85463 Bytes) of package /linux/misc/rufus-3.13.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "format.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
3.12_vs_3.13.
1 /*
2 * Rufus: The Reliable USB Formatting Utility
3 * Formatting function calls
4 * Copyright © 2011-2020 Pete Batard <pete@akeo.ie>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19 #ifdef _CRTDBG_MAP_ALLOC
20 #include <stdlib.h>
21 #include <crtdbg.h>
22 #endif
23
24 #include <windows.h>
25 #include <windowsx.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <process.h>
30 #include <stddef.h>
31 #include <ctype.h>
32 #include <locale.h>
33 #include <assert.h>
34 #include <vds.h>
35
36 #include "rufus.h"
37 #include "missing.h"
38 #include "resource.h"
39 #include "settings.h"
40 #include "msapi_utf8.h"
41 #include "localization.h"
42
43 #include "br.h"
44 #include "fat16.h"
45 #include "fat32.h"
46 #include "ntfs.h"
47 #include "partition_info.h"
48 #include "file.h"
49 #include "drive.h"
50 #include "format.h"
51 #include "badblocks.h"
52 #include "bled/bled.h"
53 #include "../res/grub/grub_version.h"
54
55 /*
56 * Globals
57 */
58 const char* FileSystemLabel[FS_MAX] = { "FAT", "FAT32", "NTFS", "UDF", "exFAT", "ReFS", "ext2", "ext3", "ext4" };
59 DWORD FormatStatus = 0, LastWriteError = 0;
60 badblocks_report report = { 0 };
61 static float format_percent = 0.0f;
62 static int task_number = 0;
63 static unsigned int sec_buf_pos = 0;
64 extern const int nb_steps[FS_MAX];
65 extern uint32_t dur_mins, dur_secs;
66 extern uint32_t wim_nb_files, wim_proc_files, wim_extra_files;
67 static int actual_fs_type, wintogo_index = -1, wininst_index = 0;
68 extern BOOL force_large_fat32, enable_ntfs_compression, lock_drive, zero_drive, fast_zeroing, enable_file_indexing, write_as_image;
69 extern BOOL use_vds, write_as_esp;
70 uint8_t *grub2_buf = NULL, *sec_buf = NULL;
71 long grub2_len;
72
73 /*
74 * Convert the fmifs outputs messages (that use an OEM code page) to UTF-8
75 */
76 static void OutputUTF8Message(const char* src)
77 {
78 int len;
79 wchar_t* wdst = NULL;
80
81 if (src == NULL)
82 goto out;
83 len = (int)safe_strlen(src);
84 while ((len > 0) && ((src[len-1] == 0x0A) || (src[len-1] == 0x0D) || (src[len-1] == ' ')))
85 len--;
86 if (len == 0)
87 goto out;
88
89 len = MultiByteToWideChar(CP_OEMCP, 0, src, len, NULL, 0);
90 if (len == 0)
91 goto out;
92 wdst = (wchar_t*)calloc(len+1, sizeof(wchar_t));
93 if ((wdst == NULL) || (MultiByteToWideChar(CP_OEMCP, 0, src, len, wdst, len+1) == 0))
94 goto out;
95 uprintf("%S", wdst);
96
97 out:
98 safe_free(wdst);
99 }
100
101 /*
102 * FormatEx callback. Return FALSE to halt operations
103 */
104 static BOOLEAN __stdcall FormatExCallback(FILE_SYSTEM_CALLBACK_COMMAND Command, DWORD Action, PVOID pData)
105 {
106 char percent_str[8];
107 if (IS_ERROR(FormatStatus))
108 return FALSE;
109
110 assert((actual_fs_type >= 0) && (actual_fs_type < FS_MAX));
111 if ((actual_fs_type < 0) || (actual_fs_type >= FS_MAX))
112 return FALSE;
113
114 switch(Command) {
115 case FCC_PROGRESS:
116 static_sprintf(percent_str, "%lu%%", *((DWORD*)pData));
117 PrintInfo(0, MSG_217, percent_str);
118 UpdateProgress(OP_FORMAT, 1.0f * (*((DWORD*)pData)));
119 break;
120 case FCC_STRUCTURE_PROGRESS: // No progress on quick format
121 if (task_number < nb_steps[actual_fs_type] - 1) {
122 if (task_number == 0)
123 uprintf("Creating file system...");
124 PrintInfo(0, MSG_218, ++task_number, nb_steps[actual_fs_type]);
125 format_percent += 100.0f / (1.0f * nb_steps[actual_fs_type]);
126 UpdateProgress(OP_CREATE_FS, format_percent);
127 }
128 break;
129 case FCC_DONE:
130 PrintInfo(0, MSG_218, nb_steps[actual_fs_type], nb_steps[actual_fs_type]);
131 UpdateProgress(OP_CREATE_FS, 100.0f);
132 if(*(BOOLEAN*)pData == FALSE) {
133 uprintf("Error while formatting");
134 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_GEN_FAILURE;
135 }
136 break;
137 case FCC_DONE_WITH_STRUCTURE:
138 break;
139 case FCC_INCOMPATIBLE_FILE_SYSTEM:
140 uprintf("Incompatible File System");
141 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_INCOMPATIBLE_FS);
142 break;
143 case FCC_ACCESS_DENIED:
144 uprintf("Access denied");
145 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_ACCESS_DENIED;
146 break;
147 case FCC_MEDIA_WRITE_PROTECTED:
148 uprintf("Media is write protected");
149 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_PROTECT;
150 break;
151 case FCC_VOLUME_IN_USE:
152 uprintf("Volume is in use");
153 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_DEVICE_IN_USE;
154 break;
155 case FCC_DEVICE_NOT_READY:
156 uprintf("The device is not ready");
157 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_READY;
158 break;
159 case FCC_CANT_QUICK_FORMAT:
160 uprintf("Cannot quick format this volume");
161 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_QUICK_FORMAT);
162 break;
163 case FCC_BAD_LABEL:
164 uprintf("Bad label");
165 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_LABEL_TOO_LONG;
166 break;
167 case FCC_OUTPUT:
168 OutputUTF8Message(((PTEXTOUTPUT)pData)->Output);
169 break;
170 case FCC_CLUSTER_SIZE_TOO_BIG:
171 case FCC_CLUSTER_SIZE_TOO_SMALL:
172 uprintf("Unsupported cluster size");
173 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_INVALID_CLUSTER_SIZE);
174 break;
175 case FCC_VOLUME_TOO_BIG:
176 case FCC_VOLUME_TOO_SMALL:
177 uprintf("Volume is too %s", (Command == FCC_VOLUME_TOO_BIG)?"big":"small");
178 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_INVALID_VOLUME_SIZE);
179 break;
180 case FCC_NO_MEDIA_IN_DRIVE:
181 uprintf("No media in drive");
182 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NO_MEDIA_IN_DRIVE;
183 break;
184 case FCC_ALIGNMENT_VIOLATION:
185 uprintf("Partition start offset is not aligned to the cluster size");
186 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OFFSET_ALIGNMENT_VIOLATION;
187 break;
188 default:
189 uprintf("FormatExCallback: Received unhandled command 0x%02X - aborting", Command);
190 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_SUPPORTED;
191 break;
192 }
193 return (!IS_ERROR(FormatStatus));
194 }
195
196 /*
197 * Chkdsk callback. Return FALSE to halt operations
198 */
199 static BOOLEAN __stdcall ChkdskCallback(FILE_SYSTEM_CALLBACK_COMMAND Command, DWORD Action, PVOID pData)
200 {
201 DWORD* percent;
202 if (IS_ERROR(FormatStatus))
203 return FALSE;
204
205 switch (Command) {
206 case FCC_PROGRESS:
207 case FCC_CHECKDISK_PROGRESS:
208 percent = (DWORD*)pData;
209 PrintInfo(0, MSG_219, *percent);
210 break;
211 case FCC_DONE:
212 if (*(BOOLEAN*)pData == FALSE) {
213 uprintf("Error while checking disk");
214 return FALSE;
215 }
216 break;
217 case FCC_UNKNOWN1A:
218 case FCC_DONE_WITH_STRUCTURE:
219 // Silence these specific calls
220 break;
221 case FCC_INCOMPATIBLE_FILE_SYSTEM:
222 uprintf("Incompatible File System");
223 return FALSE;
224 case FCC_ACCESS_DENIED:
225 uprintf("Access denied");
226 return FALSE;
227 case FCC_MEDIA_WRITE_PROTECTED:
228 uprintf("Media is write protected");
229 return FALSE;
230 case FCC_VOLUME_IN_USE:
231 uprintf("Volume is in use");
232 return FALSE;
233 case FCC_OUTPUT:
234 OutputUTF8Message(((PTEXTOUTPUT)pData)->Output);
235 break;
236 case FCC_NO_MEDIA_IN_DRIVE:
237 uprintf("No media in drive");
238 return FALSE;
239 case FCC_READ_ONLY_MODE:
240 uprintf("Media has been switched to read-only - Leaving checkdisk");
241 break;
242 default:
243 uprintf("ChkdskExCallback: received unhandled command %X", Command);
244 // Assume the command isn't an error
245 break;
246 }
247 return TRUE;
248 }
249
250 /*
251 * Converts an UTF-8 label to a valid FAT/NTFS one
252 * TODO: Use IVdsService::QueryFileSystemTypes -> VDS_FILE_SYSTEM_TYPE_PROP
253 * to get the list of unauthorised and max length for each FS.
254 */
255 static void ToValidLabel(char* Label, BOOL bFAT)
256 {
257 size_t i, j, k;
258 BOOL found;
259 const WCHAR unauthorized[] = L"*?,;:/\\|+=<>[]\"";
260 const WCHAR to_underscore[] = L"\t.";
261 char label[16] = { 0 };
262 WCHAR *wLabel = utf8_to_wchar(Label);
263
264 if (wLabel == NULL)
265 return;
266
267 for (i = 0, k = 0; i < wcslen(wLabel); i++) {
268 if (bFAT) { // NTFS does allows all the FAT unauthorized above
269 found = FALSE;
270 for (j = 0; j < wcslen(unauthorized); j++) {
271 if (wLabel[i] == unauthorized[j]) {
272 found = TRUE;
273 break;
274 }
275 }
276 // A FAT label that contains extended chars will be rejected
277 if (wLabel[i] >= 0x80) {
278 wLabel[k++] = L'_';
279 found = TRUE;
280 }
281 if (found)
282 continue;
283 }
284 found = FALSE;
285 for (j = 0; j < wcslen(to_underscore); j++) {
286 if (wLabel[i] == to_underscore[j]) {
287 wLabel[k++] = '_';
288 found = TRUE;
289 break;
290 }
291 }
292 if (found)
293 continue;
294 wLabel[k++] = bFAT ? toupper(wLabel[i]) : wLabel[i];
295 }
296 wLabel[k] = 0;
297
298 if (bFAT) {
299 if (wcslen(wLabel) > 11)
300 wLabel[11] = 0;
301 for (i = 0, j = 0; wLabel[i] != 0 ; i++)
302 if (wLabel[i] == '_')
303 j++;
304 if (i < 2*j) {
305 // If the final label is mostly underscore, use an alternate label according to the
306 // size (eg: "256 MB", "7.9 GB"). Note that we can't use SelectedDrive.proposed_label
307 // here as it may contain localized character for GB or MB, so make sure that the
308 // effective label we use is an English one, and also make sure we convert the dot.
309 static_sprintf(label, "%s", SizeToHumanReadable(SelectedDrive.DiskSize, TRUE, FALSE));
310 for (i = 0; label[i] != 0; i++)
311 wLabel[i] = (label[i] == '.') ? '_' : label[i];
312 wLabel[i] = 0;
313 uprintf("FAT label is mostly underscores. Using '%S' label instead.", wLabel);
314 }
315 } else if (wcslen(wLabel) > 32) {
316 wLabel[32] = 0;
317 }
318
319 // Needed for disk by label isolinux.cfg workaround
320 wchar_to_utf8_no_alloc(wLabel, img_report.usb_label, sizeof(img_report.usb_label));
321 safe_strcpy(Label, strlen(Label) + 1, img_report.usb_label);
322 free(wLabel);
323 }
324
325 /*
326 * Call on VDS to format a partition
327 */
328 static BOOL FormatNativeVds(DWORD DriveIndex, uint64_t PartitionOffset, DWORD ClusterSize, LPCSTR FSName, LPCSTR Label, DWORD Flags)
329 {
330 BOOL r = FALSE, bFoundVolume = FALSE;
331 HRESULT hr;
332 ULONG ulFetched;
333 IVdsServiceLoader *pLoader;
334 IVdsService *pService;
335 IEnumVdsObject *pEnum;
336 IUnknown *pUnk;
337 char* VolumeName = NULL;
338 WCHAR *wVolumeName = NULL, *wLabel = utf8_to_wchar(Label), *wFSName = utf8_to_wchar(FSName);
339
340 if ((strcmp(FSName, FileSystemLabel[FS_EXFAT]) == 0) && !((dur_mins == 0) && (dur_secs == 0))) {
341 PrintInfoDebug(0, MSG_220, FSName, dur_mins, dur_secs);
342 } else {
343 PrintInfoDebug(0, MSG_222, FSName);
344 }
345 UpdateProgressWithInfoInit(NULL, TRUE);
346 VolumeName = GetLogicalName(DriveIndex, PartitionOffset, TRUE, TRUE);
347 wVolumeName = utf8_to_wchar(VolumeName);
348 if (wVolumeName == NULL) {
349 uprintf("Could not read volume name (%s)", VolumeName);
350 FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_GEN_FAILURE;
351 goto out;
352 }
353
354 // Initialize COM
355 IGNORE_RETVAL(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
356 IGNORE_RETVAL(CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_CONNECT,
357 RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL));
358
359 // Create a VDS Loader Instance
360 hr = CoCreateInstance(&CLSID_VdsLoader, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER,
361 &IID_IVdsServiceLoader, (void **)&pLoader);
362 if (hr != S_OK) {
363 VDS_SET_ERROR(hr);
364 uprintf("Could not create VDS Loader Instance: %s", WindowsErrorString());
365 goto out;
366 }
367
368 // Load the VDS Service
369 hr = IVdsServiceLoader_LoadService(pLoader, L"", &pService);
370 IVdsServiceLoader_Release(pLoader);
371 if (hr != S_OK) {
372 VDS_SET_ERROR(hr);
373 uprintf("Could not load VDS Service: %s", WindowsErrorString());
374 goto out;
375 }
376
377 // Wait for the Service to become ready if needed
378 hr = IVdsService_WaitForServiceReady(pService);
379 if (hr != S_OK) {
380 VDS_SET_ERROR(hr);
381 uprintf("VDS Service is not ready: %s", WindowsErrorString());
382 goto out;
383 }
384
385 // Query the VDS Service Providers
386 hr = IVdsService_QueryProviders(pService, VDS_QUERY_SOFTWARE_PROVIDERS, &pEnum);
387 if (hr != S_OK) {
388 VDS_SET_ERROR(hr);
389 uprintf("Could not query VDS Service Providers: %s", WindowsErrorString());
390 goto out;
391 }
392
393 while (IEnumVdsObject_Next(pEnum, 1, &pUnk, &ulFetched) == S_OK) {
394 IVdsProvider *pProvider;
395 IVdsSwProvider *pSwProvider;
396 IEnumVdsObject *pEnumPack;
397 IUnknown *pPackUnk;
398 CHECK_FOR_USER_CANCEL;
399
400 // Get VDS Provider
401 hr = IUnknown_QueryInterface(pUnk, &IID_IVdsProvider, (void **)&pProvider);
402 IUnknown_Release(pUnk);
403 if (hr != S_OK) {
404 VDS_SET_ERROR(hr);
405 uprintf("Could not get VDS Provider: %s", WindowsErrorString());
406 goto out;
407 }
408
409 // Get VDS Software Provider
410 hr = IVdsSwProvider_QueryInterface(pProvider, &IID_IVdsSwProvider, (void **)&pSwProvider);
411 IVdsProvider_Release(pProvider);
412 if (hr != S_OK) {
413 VDS_SET_ERROR(hr);
414 uprintf("Could not get VDS Software Provider: %s", WindowsErrorString());
415 goto out;
416 }
417
418 // Get VDS Software Provider Packs
419 hr = IVdsSwProvider_QueryPacks(pSwProvider, &pEnumPack);
420 IVdsSwProvider_Release(pSwProvider);
421 if (hr != S_OK) {
422 VDS_SET_ERROR(hr);
423 uprintf("Could not get VDS Software Provider Packs: %s", WindowsErrorString());
424 goto out;
425 }
426
427 // Enumerate Provider Packs
428 while (IEnumVdsObject_Next(pEnumPack, 1, &pPackUnk, &ulFetched) == S_OK) {
429 IVdsPack *pPack;
430 IEnumVdsObject *pEnumVolume;
431 IUnknown *pVolumeUnk;
432 CHECK_FOR_USER_CANCEL;
433
434 hr = IUnknown_QueryInterface(pPackUnk, &IID_IVdsPack, (void **)&pPack);
435 IUnknown_Release(pPackUnk);
436 if (hr != S_OK) {
437 VDS_SET_ERROR(hr);
438 uprintf("Could not query VDS Software Provider Pack: %s", WindowsErrorString());
439 goto out;
440 }
441
442 // Use the pack interface to access the volumes
443 hr = IVdsPack_QueryVolumes(pPack, &pEnumVolume);
444 if (hr != S_OK) {
445 VDS_SET_ERROR(hr);
446 uprintf("Could not query VDS volumes: %s", WindowsErrorString());
447 goto out;
448 }
449
450 // List volumes
451 while (IEnumVdsObject_Next(pEnumVolume, 1, &pVolumeUnk, &ulFetched) == S_OK) {
452 BOOL match;
453 HRESULT hr2 = E_FAIL;
454 VDS_VOLUME_PROP VolumeProps;
455 LPWSTR *wszPathArray;
456 ULONG ulPercentCompleted, ulNumberOfPaths;
457 USHORT usFsVersion = 0;
458 IVdsVolume *pVolume;
459 IVdsAsync* pAsync;
460 IVdsVolumeMF3 *pVolumeMF3;
461 CHECK_FOR_USER_CANCEL;
462
463 // Get the volume interface.
464 hr = IUnknown_QueryInterface(pVolumeUnk, &IID_IVdsVolume, (void **)&pVolume);
465 if (hr != S_OK) {
466 VDS_SET_ERROR(hr);
467 uprintf("Could not query VDS Volume Interface: %s", WindowsErrorString());
468 goto out;
469 }
470
471 hr = IVdsVolume_GetProperties(pVolume, &VolumeProps);
472 if ((hr != S_OK) && (hr != VDS_S_PROPERTIES_INCOMPLETE)) {
473 VDS_SET_ERROR(hr);
474 IVdsVolume_Release(pVolume);
475 uprintf("Could not query VDS Volume Properties: %s", WindowsErrorString());
476 continue;
477 }
478 CoTaskMemFree(VolumeProps.pwszName);
479
480 // Instantiate the IVdsVolumeMF3 interface for our volume.
481 hr = IVdsVolume_QueryInterface(pVolume, &IID_IVdsVolumeMF3, (void **)&pVolumeMF3);
482 IVdsVolume_Release(pVolume);
483 if (hr != S_OK) {
484 VDS_SET_ERROR(hr);
485 uprintf("Could not access VDS VolumeMF3 interface: %s", WindowsErrorString());
486 continue;
487 }
488
489 // Query the volume GUID
490 hr = IVdsVolumeMF3_QueryVolumeGuidPathnames(pVolumeMF3, &wszPathArray, &ulNumberOfPaths);
491 if (hr != S_OK) {
492 VDS_SET_ERROR(hr);
493 uprintf("Could not query VDS VolumeGuidPathnames: %s", WindowsErrorString());
494 continue;
495 }
496
497 if (ulNumberOfPaths > 1)
498 uprintf("Notice: Volume %S has more than one GUID...", wszPathArray[0]);
499
500 match = (wcscmp(wVolumeName, wszPathArray[0]) == 0);
501 CoTaskMemFree(wszPathArray);
502 if (!match)
503 continue;
504
505 bFoundVolume = TRUE;
506 if (strcmp(Label, FileSystemLabel[FS_UDF]) == 0)
507 usFsVersion = ReadSetting32(SETTING_USE_UDF_VERSION);
508 if (ClusterSize < 0x200) {
509 ClusterSize = 0;
510 uprintf("Using default cluster size");
511 } else {
512 uprintf("Using cluster size: %d bytes", ClusterSize);
513 }
514 format_percent = 0.0f;
515 uprintf("%s format was selected", (Flags & FP_QUICK) ? "Quick" : "Slow");
516 if (Flags & FP_COMPRESSION)
517 uprintf("NTFS compression is enabled");
518
519 hr = IVdsVolumeMF3_FormatEx2(pVolumeMF3, wFSName, usFsVersion, ClusterSize, wLabel, Flags, &pAsync);
520 while (SUCCEEDED(hr)) {
521 if (IS_ERROR(FormatStatus)) {
522 IVdsAsync_Cancel(pAsync);
523 break;
524 }
525 hr = IVdsAsync_QueryStatus(pAsync, &hr2, &ulPercentCompleted);
526 if (SUCCEEDED(hr)) {
527 if (Flags & FP_QUICK) {
528 // Progress report on quick format is useless, so we'll just pretend we have 2 tasks
529 PrintInfo(0, MSG_218, (ulPercentCompleted < 100) ? 1 : 2, 2);
530 UpdateProgress(OP_CREATE_FS, (float)ulPercentCompleted);
531 } else {
532 UpdateProgressWithInfo(OP_FORMAT, MSG_217, ulPercentCompleted, 100);
533 }
534 hr = hr2;
535 if (hr == S_OK)
536 break;
537 if (hr == VDS_E_OPERATION_PENDING)
538 hr = S_OK;
539 }
540 Sleep(500);
541 }
542 if (!SUCCEEDED(hr)) {
543 VDS_SET_ERROR(hr);
544 uprintf("Could not format drive: %s", WindowsErrorString());
545 goto out;
546 }
547
548 IVdsAsync_Release(pAsync);
549 IVdsVolumeMF3_Release(pVolumeMF3);
550
551 if (!IS_ERROR(FormatStatus)) {
552 uprintf("Format completed.");
553 r = TRUE;
554 }
555 goto out;
556 }
557 }
558 }
559
560 out:
561 if ((!bFoundVolume) && (FormatStatus == 0))
562 FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_PATH_NOT_FOUND;
563 safe_free(VolumeName);
564 safe_free(wVolumeName);
565 safe_free(wLabel);
566 safe_free(wFSName);
567 return r;
568 }
569
570 /*
571 * Call on fmifs.dll's FormatEx() to format the drive
572 */
573 static BOOL FormatNative(DWORD DriveIndex, uint64_t PartitionOffset, DWORD ClusterSize, LPCSTR FSName, LPCSTR Label, DWORD Flags)
574 {
575 BOOL r = FALSE;
576 PF_DECL(FormatEx);
577 PF_DECL(EnableVolumeCompression);
578 char *locale, *VolumeName = NULL;
579 WCHAR* wVolumeName = NULL, *wLabel = utf8_to_wchar(Label), *wFSName = utf8_to_wchar(FSName);
580 size_t i;
581
582 if ((strcmp(FSName, FileSystemLabel[FS_EXFAT]) == 0) && !((dur_mins == 0) && (dur_secs == 0))) {
583 PrintInfoDebug(0, MSG_220, FSName, dur_mins, dur_secs);
584 } else {
585 PrintInfoDebug(0, MSG_222, FSName);
586 }
587 VolumeName = GetLogicalName(DriveIndex, PartitionOffset, TRUE, TRUE);
588 wVolumeName = utf8_to_wchar(VolumeName);
589 if (wVolumeName == NULL) {
590 uprintf("Could not read volume name (%s)", VolumeName);
591 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_GEN_FAILURE;
592 goto out;
593 }
594 // Hey, nice consistency here, Microsoft! - FormatEx() fails if wVolumeName has
595 // a trailing backslash, but EnableCompression() fails without...
596 wVolumeName[wcslen(wVolumeName)-1] = 0; // Remove trailing backslash
597
598 // LoadLibrary("fmifs.dll") appears to changes the locale, which can lead to
599 // problems with tolower(). Make sure we restore the locale. For more details,
600 // see https://sourceforge.net/p/mingw/mailman/message/29269040/
601 locale = setlocale(LC_ALL, NULL);
602 PF_INIT_OR_OUT(FormatEx, fmifs);
603 PF_INIT(EnableVolumeCompression, fmifs);
604 setlocale(LC_ALL, locale);
605
606 if (ClusterSize < 0x200) {
607 // 0 is FormatEx's value for default, which we need to use for UDF
608 ClusterSize = 0;
609 uprintf("Using default cluster size");
610 } else {
611 uprintf("Using cluster size: %d bytes", ClusterSize);
612 }
613 format_percent = 0.0f;
614 task_number = 0;
615
616 uprintf("%s format was selected", (Flags & FP_QUICK) ? "Quick" : "Slow");
617 for (i = 0; i < WRITE_RETRIES; i++) {
618 pfFormatEx(wVolumeName, SelectedDrive.MediaType, wFSName, wLabel,
619 (Flags & FP_QUICK), ClusterSize, FormatExCallback);
620 if (!IS_ERROR(FormatStatus) || (HRESULT_CODE(FormatStatus) == ERROR_CANCELLED))
621 break;
622 uprintf("%s - Retrying...", WindowsErrorString());
623 Sleep(WRITE_TIMEOUT);
624 }
625 if (IS_ERROR(FormatStatus))
626 goto out;
627
628 if (Flags & FP_COMPRESSION) {
629 wVolumeName[wcslen(wVolumeName)] = '\\'; // Add trailing backslash back again
630 if (pfEnableVolumeCompression(wVolumeName, FPF_COMPRESSED)) {
631 uprintf("Enabled NTFS compression");
632 } else {
633 uprintf("Could not enable NTFS compression: %s", WindowsErrorString());
634 }
635 }
636
637 if (!IS_ERROR(FormatStatus)) {
638 uprintf("Format completed.");
639 r = TRUE;
640 }
641
642 out:
643 if (!r && !IS_ERROR(FormatStatus))
644 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|SCODE_CODE(GetLastError());
645 safe_free(VolumeName);
646 safe_free(wVolumeName);
647 safe_free(wLabel);
648 safe_free(wFSName);
649 return r;
650 }
651
652 static BOOL FormatPartition(DWORD DriveIndex, uint64_t PartitionOffset, DWORD UnitAllocationSize, USHORT FSType, LPCSTR Label, DWORD Flags)
653 {
654 if ((DriveIndex < 0x80) || (DriveIndex > 0x100) || (FSType >= FS_MAX) ||
655 ((UnitAllocationSize != 0) && (!IS_POWER_OF_2(UnitAllocationSize)))) {
656 ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_INVALID_PARAMETER;
657 return FALSE;
658 }
659 actual_fs_type = FSType;
660 if ((FSType == FS_FAT32) && ((SelectedDrive.DiskSize > LARGE_FAT32_SIZE) || (force_large_fat32) || (Flags & FP_LARGE_FAT32)))
661 return FormatLargeFAT32(DriveIndex, PartitionOffset, UnitAllocationSize, FileSystemLabel[FSType], Label, Flags);
662 else if (FSType >= FS_EXT2)
663 return FormatExtFs(DriveIndex, PartitionOffset, UnitAllocationSize, FileSystemLabel[FSType], Label, Flags);
664 else if (use_vds)
665 return FormatNativeVds(DriveIndex, PartitionOffset, UnitAllocationSize, FileSystemLabel[FSType], Label, Flags);
666 else
667 return FormatNative(DriveIndex, PartitionOffset, UnitAllocationSize, FileSystemLabel[FSType], Label, Flags);
668 }
669
670 /*
671 * Call on fmifs.dll's Chkdsk() to fixup the filesystem
672 */
673 static BOOL CheckDisk(char DriveLetter)
674 {
675 BOOL r = FALSE;
676 PF_DECL(Chkdsk);
677 WCHAR wDriveRoot[] = L"?:\\";
678 WCHAR wFSType[32];
679 size_t i;
680
681 wDriveRoot[0] = (WCHAR)DriveLetter;
682 PrintInfoDebug(0, MSG_223);
683
684 PF_INIT_OR_OUT(Chkdsk, Fmifs);
685
686 GetWindowTextW(hFileSystem, wFSType, ARRAYSIZE(wFSType));
687 // We may have a " (Default)" trail
688 for (i=0; i<wcslen(wFSType); i++) {
689 if (wFSType[i] == ' ') {
690 wFSType[i] = 0;
691 break;
692 }
693 }
694
695 pfChkdsk(wDriveRoot, wFSType, FALSE, FALSE, FALSE, FALSE, NULL, NULL, ChkdskCallback);
696 if (!IS_ERROR(FormatStatus)) {
697 uprintf("NTFS Fixup completed.\n");
698 r = TRUE;
699 }
700
701 out:
702 return r;
703 }
704
705 static BOOL ClearMBRGPT(HANDLE hPhysicalDrive, LONGLONG DiskSize, DWORD SectorSize, BOOL add1MB)
706 {
707 BOOL r = FALSE;
708 uint64_t i, last_sector = DiskSize/SectorSize, num_sectors_to_clear;
709 unsigned char* pBuf = (unsigned char*) calloc(SectorSize, 1);
710
711 PrintInfoDebug(0, MSG_224);
712 if (pBuf == NULL) {
713 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_ENOUGH_MEMORY;
714 goto out;
715 }
716 // http://en.wikipedia.org/wiki/GUID_Partition_Table tells us we should clear 34 sectors at the
717 // beginning and 33 at the end. We bump these values to MAX_SECTORS_TO_CLEAR each end to help
718 // with reluctant access to large drive.
719
720 // We try to clear at least 1MB + the PBR when Large FAT32 is selected (add1MB), but
721 // don't do it otherwise, as it seems unnecessary and may take time for slow drives.
722 // Also, for various reasons (one of which being that Windows seems to have issues
723 // with GPT drives that contain a lot of small partitions) we try not not to clear
724 // sectors further than the lowest partition already residing on the disk.
725 num_sectors_to_clear = min(SelectedDrive.FirstDataSector, (DWORD)((add1MB ? 2048 : 0) + MAX_SECTORS_TO_CLEAR));
726 // Special case for big floppy disks (FirstDataSector = 0)
727 if (num_sectors_to_clear < 4)
728 num_sectors_to_clear = (DWORD)((add1MB ? 2048 : 0) + MAX_SECTORS_TO_CLEAR);
729
730 uprintf("Erasing %d sectors", num_sectors_to_clear);
731 for (i = 0; i < num_sectors_to_clear; i++) {
732 CHECK_FOR_USER_CANCEL;
733 if (write_sectors(hPhysicalDrive, SectorSize, i, 1, pBuf) != SectorSize)
734 goto out;
735 }
736 for (i = last_sector - MAX_SECTORS_TO_CLEAR; i < last_sector; i++) {
737 CHECK_FOR_USER_CANCEL;
738 // Windows seems to be an ass about keeping a lock on a backup GPT,
739 // so we try to be lenient about not being able to clear it.
740 if (write_sectors(hPhysicalDrive, SectorSize, i, 1, pBuf) != SectorSize)
741 break;
742 }
743 r = TRUE;
744
745 out:
746 safe_free(pBuf);
747 return r;
748 }
749
750 /*
751 * Process the Master Boot Record
752 */
753 static BOOL WriteMBR(HANDLE hPhysicalDrive)
754 {
755 BOOL r = FALSE;
756 DWORD size;
757 unsigned char* buffer = NULL;
758 FAKE_FD fake_fd = { 0 };
759 FILE* fp = (FILE*)&fake_fd;
760 const char* using_msg = "Using %s MBR";
761
762 if (SelectedDrive.SectorSize < 512)
763 goto out;
764
765 if (partition_type == PARTITION_STYLE_GPT) {
766 // Add a notice with a protective MBR
767 fake_fd._handle = (char*)hPhysicalDrive;
768 set_bytes_per_sector(SelectedDrive.SectorSize);
769 uprintf(using_msg, "Rufus protective");
770 r = write_rufus_msg_mbr(fp);
771 goto notify;
772 }
773
774 // FormatEx rewrites the MBR and removes the LBA attribute of FAT16
775 // and FAT32 partitions - we need to correct this in the MBR
776 buffer = (unsigned char*)_mm_malloc(SelectedDrive.SectorSize, 16);
777 if (buffer == NULL) {
778 uprintf("Could not allocate memory for MBR");
779 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_ENOUGH_MEMORY;
780 goto out;
781 }
782
783 if (!read_sectors(hPhysicalDrive, SelectedDrive.SectorSize, 0, 1, buffer)) {
784 uprintf("Could not read MBR\n");
785 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_READ_FAULT;
786 goto out;
787 }
788
789 switch (ComboBox_GetCurItemData(hFileSystem)) {
790 case FS_FAT16:
791 if (buffer[0x1c2] == 0x0e) {
792 uprintf("Partition is already FAT16 LBA...\n");
793 } else if ((buffer[0x1c2] != 0x04) && (buffer[0x1c2] != 0x06)) {
794 uprintf("Warning: converting a non FAT16 partition to FAT16 LBA: FS type=0x%02x\n", buffer[0x1c2]);
795 }
796 buffer[0x1c2] = 0x0e;
797 break;
798 case FS_FAT32:
799 if (buffer[0x1c2] == 0x0c) {
800 uprintf("Partition is already FAT32 LBA...\n");
801 } else if (buffer[0x1c2] != 0x0b) {
802 uprintf("Warning: converting a non FAT32 partition to FAT32 LBA: FS type=0x%02x\n", buffer[0x1c2]);
803 }
804 buffer[0x1c2] = 0x0c;
805 break;
806 }
807 if ((boot_type != BT_NON_BOOTABLE) && (target_type == TT_BIOS)) {
808 // Set first partition bootable - masquerade as per the DiskID selected
809 buffer[0x1be] = IsChecked(IDC_RUFUS_MBR) ?
810 (BYTE)ComboBox_GetCurItemData(hDiskID):0x80;
811 uprintf("Set bootable USB partition as 0x%02X\n", buffer[0x1be]);
812 }
813
814 if (!write_sectors(hPhysicalDrive, SelectedDrive.SectorSize, 0, 1, buffer)) {
815 uprintf("Could not write MBR\n");
816 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
817 goto out;
818 }
819
820 fake_fd._handle = (char*)hPhysicalDrive;
821 set_bytes_per_sector(SelectedDrive.SectorSize);
822
823 // What follows is really a case statement with complex conditions listed
824 // by order of preference
825 if ((boot_type == BT_IMAGE) && HAS_WINDOWS(img_report) && (allow_dual_uefi_bios) && (target_type == TT_BIOS))
826 goto windows_mbr;
827
828 // Non bootable or forced UEFI (zeroed MBR)
829 if ((boot_type == BT_NON_BOOTABLE) || (target_type == TT_UEFI)) {
830 uprintf(using_msg, "Zeroed");
831 r = write_zero_mbr(fp);
832 goto notify;
833 }
834
835 // Syslinux
836 if ( (boot_type == BT_SYSLINUX_V4) || (boot_type == BT_SYSLINUX_V6) ||
837 ((boot_type == BT_IMAGE) && HAS_SYSLINUX(img_report)) ) {
838 uprintf(using_msg, "Syslinux");
839 r = write_syslinux_mbr(fp);
840 goto notify;
841 }
842
843 // Grub 2.0
844 if ( ((boot_type == BT_IMAGE) && (img_report.has_grub2)) || (boot_type == BT_GRUB2) ) {
845 uprintf(using_msg, "Grub 2.0");
846 r = write_grub2_mbr(fp);
847 goto notify;
848 }
849
850 // Grub4DOS
851 if ( ((boot_type == BT_IMAGE) && (img_report.has_grub4dos)) || (boot_type == BT_GRUB4DOS) ) {
852 uprintf(using_msg, "Grub4DOS");
853 r = write_grub4dos_mbr(fp);
854 goto notify;
855 }
856
857 // ReactOS
858 if (boot_type == BT_REACTOS) {
859 uprintf(using_msg, "ReactOS");
860 r = write_reactos_mbr(fp);
861 goto notify;
862 }
863
864 // KolibriOS
865 if ( (boot_type == BT_IMAGE) && HAS_KOLIBRIOS(img_report) && (IS_FAT(fs_type))) {
866 uprintf(using_msg, "KolibriOS");
867 r = write_kolibrios_mbr(fp);
868 goto notify;
869 }
870
871 // If everything else failed, fall back to a conventional Windows/Rufus MBR
872 windows_mbr:
873 if ((HAS_WINPE(img_report) && !img_report.uses_minint) || (IsChecked(IDC_RUFUS_MBR))) {
874 uprintf(using_msg, APPLICATION_NAME);
875 r = write_rufus_mbr(fp);
876 } else {
877 uprintf(using_msg, "Windows 7");
878 r = write_win7_mbr(fp);
879 }
880
881 notify:
882 // Tell the system we've updated the disk properties
883 if (!DeviceIoControl(hPhysicalDrive, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &size, NULL))
884 uprintf("Failed to notify system about disk properties update: %s\n", WindowsErrorString());
885
886 out:
887 safe_mm_free(buffer);
888 return r;
889 }
890
891 /*
892 * Write Secondary Boot Record (usually right after the MBR)
893 */
894 static BOOL WriteSBR(HANDLE hPhysicalDrive)
895 {
896 // TODO: Do we need anything special for 4K sectors?
897 DWORD size, max_size, br_size = 0x200;
898 int r, sub_type = boot_type;
899 unsigned char* buf = NULL;
900 FAKE_FD fake_fd = { 0 };
901 FILE* fp = (FILE*)&fake_fd;
902
903 fake_fd._handle = (char*)hPhysicalDrive;
904 set_bytes_per_sector(SelectedDrive.SectorSize);
905 // Syslinux has precedence over Grub
906 if ((boot_type == BT_IMAGE) && (!HAS_SYSLINUX(img_report))) {
907 if (img_report.has_grub4dos)
908 sub_type = BT_GRUB4DOS;
909 if (img_report.has_grub2)
910 sub_type = BT_GRUB2;
911 }
912
913 // Use BT_MAX for the protective message
914 if ((boot_type != BT_NON_BOOTABLE) && (partition_type == PARTITION_STYLE_GPT))
915 sub_type = BT_MAX;
916
917 switch (sub_type) {
918 case BT_GRUB4DOS:
919 uprintf("Writing Grub4Dos SBR");
920 buf = GetResource(hMainInstance, MAKEINTRESOURCEA(IDR_GR_GRUB_GRLDR_MBR), _RT_RCDATA, "grldr.mbr", &size, FALSE);
921 if ((buf == NULL) || (size <= br_size)) {
922 uprintf("grldr.mbr is either not present or too small");
923 return FALSE;
924 }
925 buf = &buf[br_size];
926 size -= br_size;
927 break;
928 case BT_GRUB2:
929 if (grub2_buf != NULL) {
930 uprintf("Writing Grub 2.0 SBR (from download) %s",
931 IsBufferInDB(grub2_buf, grub2_len)?"✓":"✗");
932 buf = grub2_buf;
933 size = (DWORD)grub2_len;
934 } else {
935 uprintf("Writing Grub 2.0 SBR (from embedded)");
936 buf = GetResource(hMainInstance, MAKEINTRESOURCEA(IDR_GR_GRUB2_CORE_IMG), _RT_RCDATA, "core.img", &size, FALSE);
937 if (buf == NULL) {
938 uprintf("Could not access core.img");
939 return FALSE;
940 }
941 }
942 break;
943 case BT_MAX:
944 uprintf("Writing protective message SBR");
945 size = 4 * KB;
946 br_size = 17 * KB; // 34 sectors are reserved for protective MBR + primary GPT
947 buf = GetResource(hMainInstance, MAKEINTRESOURCEA(IDR_SBR_MSG), _RT_RCDATA, "msg.txt", &size, TRUE);
948 if (buf == NULL) {
949 uprintf("Could not access message");
950 return FALSE;
951 }
952 break;
953 default:
954 // No need to write secondary block
955 return TRUE;
956 }
957
958 // Ensure that we have sufficient space for the SBR
959 max_size = IsChecked(IDC_OLD_BIOS_FIXES) ?
960 (DWORD)(SelectedDrive.SectorsPerTrack * SelectedDrive.SectorSize) : 1 * MB;
961 if (br_size + size > max_size) {
962 uprintf(" SBR size is too large - You may need to uncheck 'Add fixes for old BIOSes'.");
963 if (sub_type == BT_MAX)
964 safe_free(buf);
965 return FALSE;
966 }
967
968 r = write_data(fp, br_size, buf, (uint64_t)size);
969 safe_free(grub2_buf);
970 if (sub_type == BT_MAX)
971 safe_free(buf);
972 return (r != 0);
973 }
974
975 /*
976 * Process the Partition Boot Record
977 */
978 static __inline const char* bt_to_name(void) {
979 switch (boot_type) {
980 case BT_FREEDOS: return "FreeDOS";
981 case BT_REACTOS: return "ReactOS";
982 default:
983 return ((boot_type == BT_IMAGE) && HAS_KOLIBRIOS(img_report)) ? "KolibriOS" : "Standard";
984 }
985 }
986
987 BOOL WritePBR(HANDLE hLogicalVolume)
988 {
989 int i;
990 FAKE_FD fake_fd = { 0 };
991 FILE* fp = (FILE*)&fake_fd;
992 const char* using_msg = "Using %s %s partition boot record";
993
994 fake_fd._handle = (char*)hLogicalVolume;
995 set_bytes_per_sector(SelectedDrive.SectorSize);
996
997 switch (actual_fs_type) {
998 case FS_FAT16:
999 uprintf(using_msg, bt_to_name(), "FAT16");
1000 if (!is_fat_16_fs(fp)) {
1001 uprintf("New volume does not have a FAT16 boot sector - aborting");
1002 break;
1003 }
1004 uprintf("Confirmed new volume has a FAT16 boot sector");
1005 if (boot_type == BT_FREEDOS) {
1006 if (!write_fat_16_fd_br(fp, 0)) break;
1007 } else if (boot_type == BT_REACTOS) {
1008 if (!write_fat_16_ros_br(fp, 0)) break;
1009 } else if ((boot_type == BT_IMAGE) && HAS_KOLIBRIOS(img_report)) {
1010 uprintf("FAT16 is not supported for KolibriOS\n"); break;
1011 } else {
1012 if (!write_fat_16_br(fp, 0)) break;
1013 }
1014 // Disk Drive ID needs to be corrected on XP
1015 if (!write_partition_physical_disk_drive_id_fat16(fp))
1016 break;
1017 return TRUE;
1018 case FS_FAT32:
1019 uprintf(using_msg, bt_to_name(), "FAT32");
1020 for (i = 0; i < 2; i++) {
1021 if (!is_fat_32_fs(fp)) {
1022 uprintf("New volume does not have a %s FAT32 boot sector - aborting\n", i?"secondary":"primary");
1023 break;
1024 }
1025 uprintf("Confirmed new volume has a %s FAT32 boot sector\n", i?"secondary":"primary");
1026 uprintf("Setting %s FAT32 boot sector for boot...\n", i?"secondary":"primary");
1027 if (boot_type == BT_FREEDOS) {
1028 if (!write_fat_32_fd_br(fp, 0)) break;
1029 } else if (boot_type == BT_REACTOS) {
1030 if (!write_fat_32_ros_br(fp, 0)) break;
1031 } else if ((boot_type == BT_IMAGE) && HAS_KOLIBRIOS(img_report)) {
1032 if (!write_fat_32_kos_br(fp, 0)) break;
1033 } else if ((boot_type == BT_IMAGE) && HAS_BOOTMGR(img_report)) {
1034 if (!write_fat_32_pe_br(fp, 0)) break;
1035 } else if ((boot_type == BT_IMAGE) && HAS_WINPE(img_report)) {
1036 if (!write_fat_32_nt_br(fp, 0)) break;
1037 } else {
1038 if (!write_fat_32_br(fp, 0)) break;
1039 }
1040 // Disk Drive ID needs to be corrected on XP
1041 if (!write_partition_physical_disk_drive_id_fat32(fp))
1042 break;
1043 fake_fd._offset += 6 * SelectedDrive.SectorSize;
1044 }
1045 return TRUE;
1046 case FS_NTFS:
1047 uprintf(using_msg, bt_to_name(), "NTFS");
1048 if (!is_ntfs_fs(fp)) {
1049 uprintf("New volume does not have an NTFS boot sector - aborting\n");
1050 break;
1051 }
1052 uprintf("Confirmed new volume has an NTFS boot sector\n");
1053 if (!write_ntfs_br(fp)) break;
1054 // Note: NTFS requires a full remount after writing the PBR. We dismount when we lock
1055 // and also go through a forced remount, so that shouldn't be an issue.
1056 // But with NTFS, if you don't remount, you don't boot!
1057 return TRUE;
1058 default:
1059 uprintf("Unsupported FS for FS BR processing - aborting\n");
1060 break;
1061 }
1062 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
1063 return FALSE;
1064 }
1065
1066 /*
1067 * Setup WinPE for bootable USB
1068 */
1069 static BOOL SetupWinPE(char drive_letter)
1070 {
1071 char src[64], dst[32];
1072 const char* basedir[3] = { "i386", "amd64", "minint" };
1073 const char* patch_str_org[2] = { "\\minint\\txtsetup.sif", "\\minint\\system32\\" };
1074 const char* patch_str_rep[2][2] = { { "\\i386\\txtsetup.sif", "\\i386\\system32\\" } ,
1075 { "\\amd64\\txtsetup.sif", "\\amd64\\system32\\" } };
1076 const char *win_nt_bt_org = "$win_nt$.~bt";
1077 const char *rdisk_zero = "rdisk(0)";
1078 const LARGE_INTEGER liZero = { {0, 0} };
1079 char setupsrcdev[64];
1080 HANDLE handle = INVALID_HANDLE_VALUE;
1081 DWORD i, j, size, rw_size, index = 0;
1082 BOOL r = FALSE;
1083 char* buffer = NULL;
1084
1085 if ((img_report.winpe & WINPE_AMD64) == WINPE_AMD64)
1086 index = 1;
1087 else if ((img_report.winpe & WINPE_MININT) == WINPE_MININT)
1088 index = 2;
1089 // Allow other values than harddisk 1, as per user choice for disk ID
1090 static_sprintf(setupsrcdev, "SetupSourceDevice = \"\\device\\harddisk%d\\partition1\"",
1091 ComboBox_GetCurSel(hDiskID));
1092 // Copy of ntdetect.com in root
1093 static_sprintf(src, "%c:\\%s\\ntdetect.com", drive_letter, basedir[2*(index/2)]);
1094 static_sprintf(dst, "%c:\\ntdetect.com", drive_letter);
1095 CopyFileA(src, dst, TRUE);
1096 if (!img_report.uses_minint) {
1097 // Create a copy of txtsetup.sif, as we want to keep the i386/amd64 files unmodified
1098 static_sprintf(src, "%c:\\%s\\txtsetup.sif", drive_letter, basedir[index]);
1099 static_sprintf(dst, "%c:\\txtsetup.sif", drive_letter);
1100 if (!CopyFileA(src, dst, TRUE)) {
1101 uprintf("Did not copy %s as %s: %s\n", src, dst, WindowsErrorString());
1102 }
1103 if (insert_section_data(dst, "[SetupData]", setupsrcdev, FALSE) == NULL) {
1104 uprintf("Failed to add SetupSourceDevice in %s\n", dst);
1105 goto out;
1106 }
1107 uprintf("Successfully added '%s' to %s\n", setupsrcdev, dst);
1108 }
1109
1110 static_sprintf(src, "%c:\\%s\\setupldr.bin", drive_letter, basedir[2*(index/2)]);
1111 static_sprintf(dst, "%c:\\BOOTMGR", drive_letter);
1112 if (!CopyFileA(src, dst, TRUE)) {
1113 uprintf("Did not copy %s as %s: %s\n", src, dst, WindowsErrorString());
1114 }
1115
1116 // \minint with /minint option doesn't require further processing => return true
1117 // \minint and no \i386 without /minint is unclear => return error
1118 if (img_report.winpe&WINPE_MININT) {
1119 if (img_report.uses_minint) {
1120 uprintf("Detected \\minint directory with /minint option: nothing to patch\n");
1121 r = TRUE;
1122 } else if (!(img_report.winpe&(WINPE_I386|WINPE_AMD64))) {
1123 uprintf("Detected \\minint directory only but no /minint option: not sure what to do\n");
1124 }
1125 goto out;
1126 }
1127
1128 // At this stage we only handle \i386
1129 handle = CreateFileA(dst, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ,
1130 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1131 if (handle == INVALID_HANDLE_VALUE) {
1132 uprintf("Could not open %s for patching: %s\n", dst, WindowsErrorString());
1133 goto out;
1134 }
1135 size = GetFileSize(handle, NULL);
1136 if (size == INVALID_FILE_SIZE) {
1137 uprintf("Could not get size for file %s: %s\n", dst, WindowsErrorString());
1138 goto out;
1139 }
1140 buffer = (char*)malloc(size);
1141 if (buffer == NULL)
1142 goto out;
1143 if ((!ReadFile(handle, buffer, size, &rw_size, NULL)) || (size != rw_size)) {
1144 uprintf("Could not read file %s: %s\n", dst, WindowsErrorString());
1145 goto out;
1146 }
1147 if (!SetFilePointerEx(handle, liZero, NULL, FILE_BEGIN)) {
1148 uprintf("Could not rewind file %s: %s\n", dst, WindowsErrorString());
1149 goto out;
1150 }
1151
1152 // Patch setupldr.bin
1153 uprintf("Patching file %s\n", dst);
1154 // Remove CRC check for 32 bit part of setupldr.bin from Win2k3
1155 if ((size > 0x2061) && (buffer[0x2060] == 0x74) && (buffer[0x2061] == 0x03)) {
1156 buffer[0x2060] = 0xeb;
1157 buffer[0x2061] = 0x1a;
1158 uprintf(" 0x00002060: 0x74 0x03 -> 0xEB 0x1A (disable Win2k3 CRC check)\n");
1159 }
1160 for (i=1; i<size-32; i++) {
1161 for (j=0; j<ARRAYSIZE(patch_str_org); j++) {
1162 if (safe_strnicmp(&buffer[i], patch_str_org[j], strlen(patch_str_org[j])-1) == 0) {
1163 uprintf(" 0x%08X: '%s' -> '%s'\n", i, &buffer[i], patch_str_rep[index][j]);
1164 strcpy(&buffer[i], patch_str_rep[index][j]);
1165 i += (DWORD)max(strlen(patch_str_org[j]), strlen(patch_str_rep[index][j])); // in case org is a substring of rep
1166 }
1167 }
1168 }
1169
1170 if (!img_report.uses_minint) {
1171 // Additional setupldr.bin/bootmgr patching
1172 for (i=0; i<size-32; i++) {
1173 // rdisk(0) -> rdisk(#) disk masquerading
1174 // NB: only the first one seems to be needed
1175 if (safe_strnicmp(&buffer[i], rdisk_zero, strlen(rdisk_zero)-1) == 0) {
1176 buffer[i+6] = 0x30 + ComboBox_GetCurSel(hDiskID);
1177 uprintf(" 0x%08X: '%s' -> 'rdisk(%c)'\n", i, rdisk_zero, buffer[i+6]);
1178 }
1179 // $WIN_NT$_~BT -> i386/amd64
1180 if (safe_strnicmp(&buffer[i], win_nt_bt_org, strlen(win_nt_bt_org)-1) == 0) {
1181 uprintf(" 0x%08X: '%s' -> '%s%s'\n", i, &buffer[i], basedir[index], &buffer[i+strlen(win_nt_bt_org)]);
1182 strcpy(&buffer[i], basedir[index]);
1183 // This ensures that we keep the terminator backslash
1184 buffer[i+strlen(basedir[index])] = buffer[i+strlen(win_nt_bt_org)];
1185 buffer[i+strlen(basedir[index])+1] = 0;
1186 }
1187 }
1188 }
1189
1190 if (!WriteFileWithRetry(handle, buffer, size, &rw_size, WRITE_RETRIES)) {
1191 uprintf("Could not write patched file: %s\n", WindowsErrorString());
1192 goto out;
1193 }
1194 r = TRUE;
1195
1196 out:
1197 safe_closehandle(handle);
1198 safe_free(buffer);
1199 return r;
1200 }
1201
1202 // Checks which versions of Windows are available in an install image
1203 // to set our extraction index. Asks the user to select one if needed.
1204 // Returns -2 on user cancel, -1 on other error, >=0 on success.
1205 int SetWinToGoIndex(void)
1206 {
1207 char *mounted_iso, *build, mounted_image_path[128];
1208 char xml_file[MAX_PATH] = "";
1209 char *install_names[MAX_WININST];
1210 StrArray version_name, version_index;
1211 int i, build_nr = 0;
1212 BOOL bNonStandard = FALSE;
1213
1214 // Sanity checks
1215 wintogo_index = -1;
1216 wininst_index = 0;
1217 if ((nWindowsVersion < WINDOWS_8) || ((WimExtractCheck(FALSE) & 4) == 0) ||
1218 (ComboBox_GetCurItemData(hFileSystem) != FS_NTFS)) {
1219 return -1;
1220 }
1221
1222 // If we have multiple windows install images, ask the user the one to use
1223 if (img_report.wininst_index > 1) {
1224 for (i = 0; i < img_report.wininst_index; i++)
1225 install_names[i] = &img_report.wininst_path[i][2];
1226 wininst_index = SelectionDialog(lmprintf(MSG_130), lmprintf(MSG_131), install_names, img_report.wininst_index);
1227 if (wininst_index < 0)
1228 return -2;
1229 wininst_index--;
1230 if ((wininst_index < 0) || (wininst_index >= MAX_WININST))
1231 wininst_index = 0;
1232 }
1233
1234 // If we're not using a straigth install.wim, we need to mount the ISO to access it
1235 if (!img_report.is_windows_img) {
1236 mounted_iso = MountISO(image_path);
1237 if (mounted_iso == NULL) {
1238 uprintf("Could not mount ISO for Windows To Go selection");
1239 return FALSE;
1240 }
1241 static_sprintf(mounted_image_path, "%s%s", mounted_iso, &img_report.wininst_path[wininst_index][2]);
1242 }
1243
1244 // Now take a look at the XML file in install.wim to list our versions
1245 if ((GetTempFileNameU(temp_dir, APPLICATION_NAME, 0, xml_file) == 0) || (xml_file[0] == 0)) {
1246 // Last ditch effort to get a tmp file - just extract it to the current directory
1247 static_strcpy(xml_file, ".\\RufVXml.tmp");
1248 }
1249 // GetTempFileName() may leave a file behind
1250 DeleteFileU(xml_file);
1251
1252 // Must use the Windows WIM API as 7z messes up the XML
1253 if (!WimExtractFile_API(img_report.is_windows_img ? image_path : mounted_image_path,
1254 0, "[1].xml", xml_file, FALSE)) {
1255 uprintf("Could not acquire WIM index");
1256 goto out;
1257 }
1258
1259 StrArrayCreate(&version_name, 16);
1260 StrArrayCreate(&version_index, 16);
1261 for (i = 0; StrArrayAdd(&version_index, get_token_data_file_indexed("IMAGE INDEX", xml_file, i + 1), FALSE) >= 0; i++) {
1262 // Some people are apparently creating *unofficial* Windows ISOs that don't have DISPLAYNAME elements.
1263 // If we are parsing such an ISO, try to fall back to using DESCRIPTION. Of course, since we don't use
1264 // a formal XML parser, if an ISO mixes entries with both DISPLAYNAME and DESCRIPTION and others with
1265 // only DESCRIPTION, the version names we report will be wrong.
1266 // But hey, there's only so far I'm willing to go to help people who, not content to have demonstrated
1267 // their utter ignorance on development matters, are also trying to lecture experienced developers
1268 // about specific "noob mistakes"... that don't exist in the code they are trying to criticize.
1269 if (StrArrayAdd(&version_name, get_token_data_file_indexed("DISPLAYNAME", xml_file, i + 1), FALSE) < 0) {
1270 bNonStandard = TRUE;
1271 if (StrArrayAdd(&version_name, get_token_data_file_indexed("DESCRIPTION", xml_file, i + 1), FALSE) < 0) {
1272 uprintf("Warning: Could not find a description for image index %d", i + 1);
1273 StrArrayAdd(&version_name, "Unknown Windows Version", TRUE);
1274 }
1275 }
1276 }
1277 if (bNonStandard)
1278 uprintf("Warning: Nonstandard Windows image (missing <DISPLAYNAME> entries)");
1279
1280 if (i > 1)
1281 i = SelectionDialog(lmprintf(MSG_291), lmprintf(MSG_292), version_name.String, i);
1282 if (i < 0) {
1283 wintogo_index = -2; // Cancelled by the user
1284 } else if (i == 0) {
1285 wintogo_index = 1;
1286 } else {
1287 wintogo_index = atoi(version_index.String[i - 1]);
1288 }
1289 if (i > 0) {
1290 // Get the build version
1291 build = get_token_data_file_indexed("BUILD", xml_file, i);
1292 if (build != NULL)
1293 build_nr = atoi(build);
1294 free(build);
1295 uprintf("Will use '%s' (Build: %d, Index %s) for Windows To Go",
1296 version_name.String[i - 1], build_nr, version_index.String[i - 1]);
1297 // Need Windows 10 Creator Update or later for boot on REMOVABLE to work
1298 if ((build_nr < 15000) && (SelectedDrive.MediaType != FixedMedia)) {
1299 if (MessageBoxExU(hMainDialog, lmprintf(MSG_098), lmprintf(MSG_190),
1300 MB_YESNO | MB_ICONWARNING | MB_IS_RTL, selected_langid) != IDYES)
1301 wintogo_index = -2;
1302 }
1303 // Display a notice about WppRecorder.sys for 1809 ISOs
1304 if (build_nr == 17763) {
1305 notification_info more_info;
1306 more_info.id = MORE_INFO_URL;
1307 more_info.url = WPPRECORDER_MORE_INFO_URL;
1308 Notification(MSG_INFO, NULL, &more_info, lmprintf(MSG_128, "Windows To Go"), lmprintf(MSG_133));
1309 }
1310 }
1311 StrArrayDestroy(&version_name);
1312 StrArrayDestroy(&version_index);
1313
1314 out:
1315 DeleteFileU(xml_file);
1316 if (!img_report.is_windows_img)
1317 UnMountISO();
1318 return wintogo_index;
1319 }
1320
1321 // http://technet.microsoft.com/en-ie/library/jj721578.aspx
1322 // As opposed to the technet guide above, we don't set internal drives offline,
1323 // due to people wondering why they can't see them by default and we also use
1324 // bcdedit rather than 'unattend.xml' to disable the recovery environment.
1325 static BOOL SetupWinToGo(DWORD DriveIndex, const char* drive_name, BOOL use_esp)
1326 {
1327 char *mounted_iso, *ms_efi = NULL, mounted_image_path[128], cmd[MAX_PATH];
1328 ULONG cluster_size;
1329
1330 uprintf("Windows To Go mode selected");
1331 // Additional sanity checks
1332 if ((use_esp) && (SelectedDrive.MediaType != FixedMedia) && (nWindowsBuildNumber < 15000)) {
1333 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_SUPPORTED;
1334 return FALSE;
1335 }
1336
1337 if (!img_report.is_windows_img) {
1338 mounted_iso = MountISO(image_path);
1339 if (mounted_iso == NULL) {
1340 uprintf("Could not mount ISO for Windows To Go installation");
1341 FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_ISO_EXTRACT);
1342 return FALSE;
1343 }
1344 static_sprintf(mounted_image_path, "%s%s", mounted_iso, &img_report.wininst_path[wininst_index][2]);
1345 uprintf("Mounted ISO as '%s'", mounted_iso);
1346 }
1347
1348 // Now we use the WIM API to apply that image
1349 if (!WimApplyImage(img_report.is_windows_img ? image_path : mounted_image_path, wintogo_index, drive_name)) {
1350 uprintf("Failed to apply Windows To Go image");
1351 if (!IS_ERROR(FormatStatus))
1352 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_ISO_EXTRACT);
1353 if (!img_report.is_windows_img)
1354 UnMountISO();
1355 return FALSE;
1356 }
1357 if (!img_report.is_windows_img)
1358 UnMountISO();
1359
1360 if (use_esp) {
1361 uprintf("Setting up EFI System Partition");
1362 // According to Ubuntu (https://bugs.launchpad.net/ubuntu/+source/partman-efi/+bug/811485) you want to use FAT32.
1363 // However, you have to be careful that the cluster size needs to be greater or equal to the sector size, which
1364 // in turn has an impact on the minimum EFI partition size we can create (see ms_efi_size_MB in drive.c)
1365 if (SelectedDrive.SectorSize <= 1024)
1366 cluster_size = 1024;
1367 else if (SelectedDrive.SectorSize <= 4096)
1368 cluster_size = 4096;
1369 else // Go for broke
1370 cluster_size = (ULONG)SelectedDrive.SectorSize;
1371 // Boy do you *NOT* want to specify a label here, and spend HOURS figuring out why your EFI partition cannot boot...
1372 // Also, we use the Large FAT32 facility Microsoft APIs are *UTTERLY USELESS* for achieving what we want:
1373 // VDS cannot list ESP volumes (talk about allegedly improving on the old disk and volume APIs, only to
1374 // completely neuter it) and IVdsDiskPartitionMF::FormatPartitionEx(), which is what you are supposed to
1375 // use for ESPs, explicitly states: "This method cannot be used to format removable media."
1376 if (!FormatPartition(DriveIndex, partition_offset[PI_ESP], cluster_size, FS_FAT32, "",
1377 FP_QUICK | FP_FORCE | FP_LARGE_FAT32 | FP_NO_BOOT)) {
1378 uprintf("Could not format EFI System Partition");
1379 return FALSE;
1380 }
1381 Sleep(200);
1382 // Need to have the ESP mounted to invoke bcdboot
1383 ms_efi = AltMountVolume(DriveIndex, partition_offset[PI_ESP], FALSE);
1384 if (ms_efi == NULL) {
1385 FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_CANT_ASSIGN_LETTER);
1386 return FALSE;
1387 }
1388 }
1389
1390 // We invoke the 'bcdboot' command from the host, as the one from the drive produces problems (#558)
1391 // and of course, we couldn't invoke an ARM64 'bcdboot' binary on an x86 host anyway...
1392 // Also, since Rufus should (usually) be running as a 32 bit app, on 64 bit systems, we need to use
1393 // 'C:\Windows\Sysnative' and not 'C:\Windows\System32' to invoke bcdboot, as 'C:\Windows\System32'
1394 // will get converted to 'C:\Windows\SysWOW64' behind the scenes, and there is no bcdboot.exe there.
1395 uprintf("Enabling boot using command:");
1396 static_sprintf(cmd, "%s\\bcdboot.exe %s\\Windows /v /f %s /s %s", sysnative_dir, drive_name,
1397 HAS_BOOTMGR_BIOS(img_report) ? (HAS_BOOTMGR_EFI(img_report) ? "ALL" : "BIOS") : "UEFI",
1398 (use_esp)?ms_efi:drive_name);
1399 uprintf(cmd);
1400 if (RunCommand(cmd, sysnative_dir, usb_debug) != 0) {
1401 // Try to continue... but report a failure
1402 uprintf("Failed to enable boot");
1403 FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_ISO_EXTRACT);
1404 }
1405
1406 UpdateProgressWithInfo(OP_FILE_COPY, MSG_267, wim_proc_files + 2 * wim_extra_files, wim_nb_files);
1407
1408 uprintf("Disable the use of the Windows Recovery Environment using command:");
1409 static_sprintf(cmd, "%s\\bcdedit.exe /store %s\\EFI\\Microsoft\\Boot\\BCD /set {default} recoveryenabled no",
1410 sysnative_dir, (use_esp) ? ms_efi : drive_name);
1411 uprintf(cmd);
1412 RunCommand(cmd, sysnative_dir, usb_debug);
1413
1414 UpdateProgressWithInfo(OP_FILE_COPY, MSG_267, wim_nb_files, wim_nb_files);
1415
1416 if (use_esp) {
1417 Sleep(200);
1418 AltUnmountVolume(ms_efi, FALSE);
1419 }
1420
1421 return TRUE;
1422 }
1423
1424 static void update_progress(const uint64_t processed_bytes)
1425 {
1426 // NB: We don't really care about resetting this value to UINT64_MAX for a new pass.
1427 static uint64_t last_value = UINT64_MAX;
1428 uint64_t cur_value;
1429
1430 UpdateProgressWithInfo(OP_FORMAT, MSG_261, processed_bytes, img_report.image_size);
1431 cur_value = (processed_bytes * min(80, img_report.image_size)) / img_report.image_size;
1432 if (cur_value != last_value) {
1433 last_value = cur_value;
1434 uprintfs("+");
1435 }
1436 }
1437
1438 // Some compressed images use streams that aren't multiple of the sector
1439 // size and cause write failures => Use a write override that alleviates
1440 // the problem. See GitHub issue #1422 for details.
1441 static int sector_write(int fd, const void* _buf, unsigned int count)
1442 {
1443 const uint8_t* buf = (const uint8_t*)_buf;
1444 unsigned int sec_size = (unsigned int)SelectedDrive.SectorSize;
1445 int written, fill_size = 0;
1446
1447 if (sec_size == 0)
1448 sec_size = 512;
1449
1450 // If we are on a sector boundary and count is multiple of the
1451 // sector size, just issue a regular write
1452 if ((sec_buf_pos == 0) && (count % sec_size == 0))
1453 return _write(fd, buf, count);
1454
1455 // If we have an existing partial sector, fill and write it
1456 if (sec_buf_pos > 0) {
1457 fill_size = min(sec_size - sec_buf_pos, count);
1458 memcpy(&sec_buf[sec_buf_pos], buf, fill_size);
1459 sec_buf_pos += fill_size;
1460 // If we don't have a full sector just buffer it for next call
1461 if (sec_buf_pos < sec_size)
1462 return (int)count;
1463 sec_buf_pos = 0;
1464 written = _write(fd, sec_buf, sec_size);
1465 if (written != sec_size)
1466 return written;
1467 }
1468
1469 // Now write as many full sectors as we can
1470 uint32_t sec_num = (count - fill_size) / sec_size;
1471 written = _write(fd, &buf[fill_size], sec_num * sec_size);
1472 if (written < 0)
1473 return written;
1474 else if (written != sec_num * sec_size)
1475 return fill_size + written;
1476 sec_buf_pos = count - fill_size - written;
1477 assert(sec_buf_pos < sec_size);
1478
1479 // Keep leftover bytes, if any, in the sector buffer
1480 if (sec_buf_pos != 0)
1481 memcpy(sec_buf, &buf[fill_size + written], sec_buf_pos);
1482 return (int)count;
1483 }
1484
1485 /* Write an image file or zero a drive */
1486 static BOOL WriteDrive(HANDLE hPhysicalDrive, HANDLE hSourceImage)
1487 {
1488 BOOL s, ret = FALSE;
1489 LARGE_INTEGER li;
1490 DWORD i, rSize, wSize, xSize, BufSize;
1491 uint64_t wb, target_size = hSourceImage?img_report.image_size:SelectedDrive.DiskSize;
1492 uint64_t cur_value, last_value = UINT64_MAX;
1493 int64_t bled_ret;
1494 uint8_t* buffer = NULL;
1495 uint32_t zero_data, *cmp_buffer = NULL;
1496 int throttle_fast_zeroing = 0;
1497
1498 if (SelectedDrive.SectorSize < 512) {
1499 uprintf("Unexpected sector size (%d) - Aborting", SelectedDrive.SectorSize);
1500 return FALSE;
1501 }
1502
1503 // We poked the MBR and other stuff, so we need to rewind
1504 li.QuadPart = 0;
1505 if (!SetFilePointerEx(hPhysicalDrive, li, NULL, FILE_BEGIN))
1506 uprintf("Warning: Unable to rewind image position - wrong data might be copied!");
1507 UpdateProgressWithInfoInit(NULL, FALSE);
1508
1509 if (img_report.compression_type != BLED_COMPRESSION_NONE) {
1510 uprintf("Writing compressed image:");
1511 sec_buf = (uint8_t*)_mm_malloc(SelectedDrive.SectorSize, SelectedDrive.SectorSize);
1512 if (sec_buf == NULL) {
1513 FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_NOT_ENOUGH_MEMORY;
1514 uprintf("Could not allocate disk write buffer");
1515 goto out;
1516 }
1517 assert((uintptr_t)sec_buf % SelectedDrive.SectorSize == 0);
1518 sec_buf_pos = 0;
1519 bled_init(_uprintf, NULL, sector_write, update_progress, NULL, &FormatStatus);
1520 bled_ret = bled_uncompress_with_handles(hSourceImage, hPhysicalDrive, img_report.compression_type);
1521 bled_exit();
1522 if ((bled_ret >= 0) && (sec_buf_pos != 0)) {
1523 // A disk image that doesn't end up on disk boundary should be a rare
1524 // enough case, so we dont bother checking the write operation and
1525 // just issue a notice about it in the log.
1526 uprintf("Notice: Compressed image data didn't end on block boundary.");
1527 // Gonna assert that WriteFile() and _write() share the same file offset
1528 WriteFile(hPhysicalDrive, sec_buf, SelectedDrive.SectorSize, &wSize, NULL);
1529 }
1530 safe_mm_free(sec_buf);
1531 if ((bled_ret < 0) && (SCODE_CODE(FormatStatus) != ERROR_CANCELLED)) {
1532 // Unfortunately, different compression backends return different negative error codes
1533 uprintf("Could not write compressed image: %lld", bled_ret);
1534 FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_WRITE_FAULT;
1535 goto out;
1536 }
1537 } else {
1538 uprintf(hSourceImage?"Writing Image:":fast_zeroing?"Fast-zeroing drive:":"Zeroing drive:");
1539 // Our buffer size must be a multiple of the sector size and *ALIGNED* to the sector size
1540 BufSize = ((DD_BUFFER_SIZE + SelectedDrive.SectorSize - 1) / SelectedDrive.SectorSize) * SelectedDrive.SectorSize;
1541 buffer = (uint8_t*)_mm_malloc(BufSize, SelectedDrive.SectorSize);
1542 if (buffer == NULL) {
1543 FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_NOT_ENOUGH_MEMORY;
1544 uprintf("Could not allocate disk write buffer");
1545 goto out;
1546 }
1547 assert((uintptr_t)buffer % SelectedDrive.SectorSize == 0);
1548
1549 // Clear buffer
1550 memset(buffer, fast_zeroing ? 0xff : 0x00, BufSize);
1551
1552 if (fast_zeroing) {
1553 cmp_buffer = (uint32_t*)_mm_malloc(BufSize, SelectedDrive.SectorSize);
1554 if (cmp_buffer == NULL) {
1555 FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_NOT_ENOUGH_MEMORY;
1556 uprintf("Could not allocate disk comparison buffer");
1557 goto out;
1558 }
1559 assert((uintptr_t)cmp_buffer % SelectedDrive.SectorSize == 0);
1560 }
1561
1562 // Don't bother trying for something clever, using double buffering overlapped and whatnot:
1563 // With Windows' default optimizations, sync read + sync write for sequential operations
1564 // will be as fast, if not faster, than whatever async scheme you can come up with.
1565 rSize = BufSize;
1566 for (wb = 0, wSize = 0; wb < target_size; wb += wSize) {
1567 UpdateProgressWithInfo(OP_FORMAT, hSourceImage ? MSG_261 : fast_zeroing ? MSG_306 : MSG_286, wb, target_size);
1568 cur_value = (wb * min(80, target_size)) / target_size;
1569 if (cur_value != last_value) {
1570 last_value = cur_value;
1571 uprintfs("+");
1572 }
1573 if (hSourceImage != NULL) {
1574 s = ReadFile(hSourceImage, buffer, BufSize, &rSize, NULL);
1575 if (!s) {
1576 FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_READ_FAULT;
1577 uprintf("Read error: %s", WindowsErrorString());
1578 goto out;
1579 }
1580 if (rSize == 0)
1581 break;
1582 }
1583 // Don't overflow our projected size (mostly for VHDs)
1584 if (wb + rSize > target_size) {
1585 rSize = (DWORD)(target_size - wb);
1586 }
1587
1588 // WriteFile fails unless the size is a multiple of sector size
1589 if (rSize % SelectedDrive.SectorSize != 0)
1590 rSize = ((rSize + SelectedDrive.SectorSize - 1) / SelectedDrive.SectorSize) * SelectedDrive.SectorSize;
1591
1592 // Fast-zeroing: Depending on your hardware, reading from flash may be much faster than writing, so
1593 // we might speed things up by skipping empty blocks, or skipping the write if the data is the same.
1594 // Notes: A block is declared empty when all bits are either 0 (zeros) or 1 (flash block erased).
1595 // Also, a back-off strategy is used to limit reading.
1596 if (throttle_fast_zeroing) {
1597 throttle_fast_zeroing--;
1598 } else if (fast_zeroing) {
1599 assert(hSourceImage == NULL); // Only enabled for zeroing
1600 CHECK_FOR_USER_CANCEL;
1601
1602 // Read block and compare against the block that needs to be written
1603 s = ReadFile(hPhysicalDrive, cmp_buffer, rSize, &xSize, NULL);
1604 if ((!s) || (xSize != rSize) ) {
1605 uprintf("Read error: Could not read data for fast zeroing comparison - %s", WindowsErrorString());
1606 goto out;
1607 }
1608
1609 // Check for an empty block by comparing with the first element
1610 zero_data = cmp_buffer[0];
1611 // Check all bits are the same
1612 if ((zero_data == 0) || (zero_data == 0xffffffff)) {
1613 // Compare the rest of the block against the first element
1614 for (i = 1; (i < rSize / sizeof(uint32_t)) && (cmp_buffer[i] == zero_data); i++);
1615 if (i >= rSize / sizeof(uint32_t)) {
1616 // Block is empty, skip write
1617 wSize = rSize;
1618 continue;
1619 }
1620 }
1621
1622 // Move the file pointer position back for writing
1623 li.QuadPart = wb;
1624 if (!SetFilePointerEx(hPhysicalDrive, li, NULL, FILE_BEGIN)) {
1625 uprintf("Error: Could not reset position - %s", WindowsErrorString());
1626 goto out;
1627 }
1628 // Throttle read operations
1629 throttle_fast_zeroing = 15;
1630 }
1631
1632 for (i = 1; i <= WRITE_RETRIES; i++) {
1633 CHECK_FOR_USER_CANCEL;
1634 s = WriteFile(hPhysicalDrive, buffer, rSize, &wSize, NULL);
1635 if ((s) && (wSize == rSize))
1636 break;
1637 if (s)
1638 uprintf("Write error: Wrote %d bytes, expected %d bytes", wSize, rSize);
1639 else
1640 uprintf("Write error at sector %lld: %s", wb / SelectedDrive.SectorSize, WindowsErrorString());
1641 if (i < WRITE_RETRIES) {
1642 li.QuadPart = wb;
1643 uprintf("Retrying in %d seconds...", WRITE_TIMEOUT / 1000);
1644 Sleep(WRITE_TIMEOUT);
1645 if (!SetFilePointerEx(hPhysicalDrive, li, NULL, FILE_BEGIN)) {
1646 uprintf("Write error: Could not reset position - %s", WindowsErrorString());
1647 goto out;
1648 }
1649 } else {
1650 FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_WRITE_FAULT;
1651 goto out;
1652 }
1653 Sleep(200);
1654 }
1655 if (i > WRITE_RETRIES)
1656 goto out;
1657 }
1658 }
1659 RefreshDriveLayout(hPhysicalDrive);
1660 ret = TRUE;
1661 out:
1662 safe_mm_free(buffer);
1663 safe_mm_free(cmp_buffer);
1664 return ret;
1665 }
1666
1667 /*
1668 * Standalone thread for the formatting operation
1669 * According to http://msdn.microsoft.com/en-us/library/windows/desktop/aa364562.aspx
1670 * To change a volume file system
1671 * Open a volume.
1672 * Lock the volume.
1673 * Format the volume.
1674 * Dismount the volume.
1675 * Unlock the volume.
1676 * Close the volume handle.
1677 */
1678 DWORD WINAPI FormatThread(void* param)
1679 {
1680 int r;
1681 BOOL ret, use_large_fat32, windows_to_go, actual_lock_drive = lock_drive;
1682 BOOL need_logical = FALSE;
1683 DWORD cr, DriveIndex = (DWORD)(uintptr_t)param, ClusterSize, Flags;
1684 HANDLE hPhysicalDrive = INVALID_HANDLE_VALUE;
1685 HANDLE hLogicalVolume = INVALID_HANDLE_VALUE;
1686 HANDLE hSourceImage = INVALID_HANDLE_VALUE;
1687 SYSTEMTIME lt;
1688 FILE* log_fd;
1689 uint8_t *buffer = NULL, extra_partitions = 0;
1690 char *bb_msg, *volume_name = NULL;
1691 char drive_name[] = "?:\\";
1692 char drive_letters[27], fs_name[32], label[64];
1693 char logfile[MAX_PATH], *userdir;
1694 char efi_dst[] = "?:\\efi\\boot\\bootx64.efi";
1695 char kolibri_dst[] = "?:\\MTLD_F32";
1696 char grub4dos_dst[] = "?:\\grldr";
1697
1698 use_large_fat32 = (fs_type == FS_FAT32) && ((SelectedDrive.DiskSize > LARGE_FAT32_SIZE) || (force_large_fat32));
1699 windows_to_go = (image_options & IMOP_WINTOGO) && (boot_type == BT_IMAGE) && HAS_WINTOGO(img_report) &&
1700 ComboBox_GetCurItemData(hImageOption);
1701 large_drive = (SelectedDrive.DiskSize > (1*TB));
1702 if (large_drive)
1703 uprintf("Notice: Large drive detected (may produce short writes)");
1704 // Find out if we need to add any extra partitions
1705 if ((windows_to_go) && (target_type == TT_UEFI) && (partition_type == PARTITION_STYLE_GPT))
1706 // According to Microsoft, every GPT disk (we RUN Windows from) must have an MSR due to not having hidden sectors
1707 // http://msdn.microsoft.com/en-us/library/windows/hardware/dn640535.aspx#gpt_faq_what_disk_require_msr
1708 extra_partitions = XP_ESP | XP_MSR;
1709 else if ( ((fs_type == FS_NTFS) || (fs_type == FS_EXFAT)) &&
1710 ((boot_type == BT_UEFI_NTFS) || ((boot_type == BT_IMAGE) && IS_EFI_BOOTABLE(img_report) &&
1711 ((target_type == TT_UEFI) || (windows_to_go) || (allow_dual_uefi_bios)))) )
1712 extra_partitions = XP_UEFI_NTFS;
1713 else if ((boot_type == BT_IMAGE) && !write_as_image && HAS_PERSISTENCE(img_report) && persistence_size)
1714 extra_partitions = XP_CASPER;
1715 else if (IsChecked(IDC_OLD_BIOS_FIXES))
1716 extra_partitions = XP_COMPAT;
1717 // On pre 1703 platforms (and even on later ones), anything with ext2/ext3 doesn't sit
1718 // too well with Windows. Same with ESPs. Relaxing our locking rules seems to help...
1719 if ((extra_partitions & (XP_ESP | XP_CASPER)) || (fs_type >= FS_EXT2))
1720 actual_lock_drive = FALSE;
1721
1722 PrintInfoDebug(0, MSG_225);
1723 hPhysicalDrive = GetPhysicalHandle(DriveIndex, actual_lock_drive, FALSE, !actual_lock_drive);
1724 if (hPhysicalDrive == INVALID_HANDLE_VALUE) {
1725 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED;
1726 goto out;
1727 }
1728
1729 // At this stage we have both a handle and a lock to the physical drive
1730 if (!GetDriveLetters(DriveIndex, drive_letters)) {
1731 uprintf("Failed to get a drive letter");
1732 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_ASSIGN_LETTER);
1733 goto out;
1734 }
1735
1736 // Unassign all drives letters
1737 drive_name[0] = RemoveDriveLetters(DriveIndex, TRUE, FALSE);
1738 if (drive_name[0] == 0) {
1739 uprintf("Unable to find a drive letter to use");
1740 FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_CANT_ASSIGN_LETTER);
1741 goto out;
1742 }
1743 uprintf("Will use '%C:' as volume mountpoint", drive_name[0]);
1744
1745 // It kind of blows, but we have to relinquish access to the physical drive
1746 // for VDS to be able to delete the partitions that reside on it...
1747 safe_unlockclose(hPhysicalDrive);
1748 PrintInfo(0, MSG_239, lmprintf(MSG_307));
1749 if (!DeletePartition(DriveIndex, 0, FALSE)) {
1750 SetLastError(FormatStatus);
1751 FormatStatus = 0;
1752 // If we couldn't delete partitions, Windows give us trouble unless we
1753 // request access to the logical drive. Don't ask me why!
1754 need_logical = TRUE;
1755 }
1756
1757 // An extra refresh of the (now empty) partition data here appears to be helpful
1758 GetDrivePartitionData(SelectedDrive.DeviceNumber, fs_name, sizeof(fs_name), TRUE);
1759
1760 // Now get RW access to the physical drive
1761 hPhysicalDrive = GetPhysicalHandle(DriveIndex, actual_lock_drive, TRUE, !actual_lock_drive);
1762 if (hPhysicalDrive == INVALID_HANDLE_VALUE) {
1763 FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_OPEN_FAILED;
1764 goto out;
1765 }
1766 RefreshDriveLayout(hPhysicalDrive);
1767
1768 // If we write an image that contains an ESP, Windows forcibly reassigns/removes the target
1769 // drive, which causes a write error. To work around this, we must lock the logical drive.
1770 // Also need to lock logical drive if we couldn't delete partitions, to keep Windows happy...
1771 if (((boot_type == BT_IMAGE) && write_as_image) || (need_logical)) {
1772 uprintf("Requesting logical volume handle...");
1773 hLogicalVolume = GetLogicalHandle(DriveIndex, 0, TRUE, FALSE, !actual_lock_drive);
1774 if (hLogicalVolume == INVALID_HANDLE_VALUE) {
1775 uprintf("Could not access logical volume");
1776 FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_OPEN_FAILED;
1777 goto out;
1778 // If the call succeeds (and we don't get a NULL logical handle as returned for
1779 // unpartitioned drives), try to unmount the volume.
1780 } else if ((hLogicalVolume == NULL) && (!UnmountVolume(hLogicalVolume))) {
1781 uprintf("Trying to continue regardless...");
1782 }
1783 }
1784 CHECK_FOR_USER_CANCEL;
1785
1786 if (!zero_drive && !write_as_image) {
1787 PrintInfoDebug(0, MSG_226);
1788 AnalyzeMBR(hPhysicalDrive, "Drive", FALSE);
1789 UpdateProgress(OP_ANALYZE_MBR, -1.0f);
1790 }
1791
1792 if (zero_drive) {
1793 WriteDrive(hPhysicalDrive, NULL);
1794 goto out;
1795 }
1796
1797 // Zap partition records. This helps prevent access errors.
1798 // Note, Microsoft's way of cleaning partitions (IOCTL_DISK_CREATE_DISK, which is what we apply
1799 // in InitializeDisk) is *NOT ENOUGH* to reset a disk and can render it inoperable for partitioning
1800 // or formatting under Windows. See https://github.com/pbatard/rufus/issues/759 for details.
1801 if ((boot_type != BT_IMAGE) || (img_report.is_iso && !write_as_image)) {
1802 if ((!ClearMBRGPT(hPhysicalDrive, SelectedDrive.DiskSize, SelectedDrive.SectorSize, use_large_fat32)) ||
1803 (!InitializeDisk(hPhysicalDrive))) {
1804 uprintf("Could not reset partitions");
1805 FormatStatus = (LastWriteError != 0) ? LastWriteError : (ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_PARTITION_FAILURE);
1806 goto out;
1807 }
1808 }
1809
1810 if (IsChecked(IDC_BAD_BLOCKS)) {
1811 do {
1812 int sel = ComboBox_GetCurSel(hNBPasses);
1813 // create a log file for bad blocks report. Since %USERPROFILE% may
1814 // have localized characters, we use the UTF-8 API.
1815 userdir = getenvU("USERPROFILE");
1816 static_strcpy(logfile, userdir);
1817 safe_free(userdir);
1818 GetLocalTime(<);
1819 safe_sprintf(&logfile[strlen(logfile)], sizeof(logfile)-strlen(logfile)-1,
1820 "\\rufus_%04d%02d%02d_%02d%02d%02d.log",
1821 lt.wYear, lt.wMonth, lt.wDay, lt.wHour, lt.wMinute, lt.wSecond);
1822 log_fd = fopenU(logfile, "w+");
1823 if (log_fd == NULL) {
1824 uprintf("Could not create log file for bad blocks check");
1825 } else {
1826 fprintf(log_fd, APPLICATION_NAME " bad blocks check started on: %04d.%02d.%02d %02d:%02d:%02d",
1827 lt.wYear, lt.wMonth, lt.wDay, lt.wHour, lt.wMinute, lt.wSecond);
1828 fflush(log_fd);
1829 }
1830
1831 if (!BadBlocks(hPhysicalDrive, SelectedDrive.DiskSize, (sel >= 2) ? 4 : sel +1, sel, &report, log_fd)) {
1832 uprintf("Bad blocks: Check failed.");
1833 if (!IS_ERROR(FormatStatus))
1834 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_BADBLOCKS_FAILURE);
1835 ClearMBRGPT(hPhysicalDrive, SelectedDrive.DiskSize, SelectedDrive.SectorSize, FALSE);
1836 fclose(log_fd);
1837 DeleteFileU(logfile);
1838 goto out;
1839 }
1840 uprintf("Bad Blocks: Check completed, %d bad block%s found. (%d/%d/%d errors)",
1841 report.bb_count, (report.bb_count==1)?"":"s",
1842 report.num_read_errors, report.num_write_errors, report.num_corruption_errors);
1843 r = IDOK;
1844 if (report.bb_count) {
1845 bb_msg = lmprintf(MSG_011, report.bb_count, report.num_read_errors, report.num_write_errors,
1846 report.num_corruption_errors);
1847 fprintf(log_fd, "%s", bb_msg);
1848 GetLocalTime(<);
1849 fprintf(log_fd, APPLICATION_NAME " bad blocks check ended on: %04d.%02d.%02d %02d:%02d:%02d",
1850 lt.wYear, lt.wMonth, lt.wDay, lt.wHour, lt.wMinute, lt.wSecond);
1851 fclose(log_fd);
1852 r = MessageBoxExU(hMainDialog, lmprintf(MSG_012, bb_msg, logfile),
1853 lmprintf(MSG_010), MB_ABORTRETRYIGNORE|MB_ICONWARNING|MB_IS_RTL, selected_langid);
1854 } else {
1855 // We didn't get any errors => delete the log file
1856 fclose(log_fd);
1857 DeleteFileU(logfile);
1858 }
1859 } while (r == IDRETRY);
1860 if (r == IDABORT) {
1861 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_CANCELLED;
1862 goto out;
1863 }
1864
1865 // Especially after destructive badblocks test, you must zero the MBR/GPT completely
1866 // before repartitioning. Else, all kind of bad things happen.
1867 if (!ClearMBRGPT(hPhysicalDrive, SelectedDrive.DiskSize, SelectedDrive.SectorSize, use_large_fat32)) {
1868 uprintf("unable to zero MBR/GPT");
1869 if (!IS_ERROR(FormatStatus))
1870 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
1871 goto out;
1872 }
1873 }
1874
1875 // Write an image file
1876 if ((boot_type == BT_IMAGE) && write_as_image) {
1877 hSourceImage = CreateFileU(image_path, GENERIC_READ, FILE_SHARE_READ, NULL,
1878 OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1879 if (hSourceImage == INVALID_HANDLE_VALUE) {
1880 uprintf("Could not open image '%s': %s", image_path, WindowsErrorString());
1881 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED;
1882 goto out;
1883 }
1884
1885 WriteDrive(hPhysicalDrive, hSourceImage);
1886
1887 // If the image contains a partition we might be able to access, try to re-mount it
1888 safe_unlockclose(hPhysicalDrive);
1889 safe_unlockclose(hLogicalVolume);
1890 Sleep(200);
1891 WaitForLogical(DriveIndex, 0);
1892 if (GetDrivePartitionData(SelectedDrive.DeviceNumber, fs_name, sizeof(fs_name), TRUE)) {
1893 volume_name = GetLogicalName(DriveIndex, 0, TRUE, TRUE);
1894 if ((volume_name != NULL) && (MountVolume(drive_name, volume_name)))
1895 uprintf("Remounted %s as %C:", volume_name, drive_name[0]);
1896 }
1897 goto out;
1898 }
1899
1900 UpdateProgress(OP_ZERO_MBR, -1.0f);
1901 CHECK_FOR_USER_CANCEL;
1902
1903 if (!CreatePartition(hPhysicalDrive, partition_type, fs_type, (partition_type == PARTITION_STYLE_MBR)
1904 && (target_type == TT_UEFI), extra_partitions)) {
1905 FormatStatus = (LastWriteError != 0) ? LastWriteError : (ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_PARTITION_FAILURE);
1906 goto out;
1907 }
1908 UpdateProgress(OP_PARTITION, -1.0f);
1909
1910 // Close the (unmounted) volume before formatting
1911 if ((hLogicalVolume != NULL) && (hLogicalVolume != INVALID_HANDLE_VALUE)) {
1912 PrintInfoDebug(0, MSG_227);
1913 if (!CloseHandle(hLogicalVolume)) {
1914 uprintf("Could not close volume: %s", WindowsErrorString());
1915 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_ACCESS_DENIED;
1916 goto out;
1917 }
1918 }
1919 hLogicalVolume = INVALID_HANDLE_VALUE;
1920
1921 // VDS wants us to unlock the phys
1922 if (use_vds) {
1923 safe_unlockclose(hPhysicalDrive);
1924 uprintf("Refreshing drive layout...");
1925 // Note: This may leave the device disabled on re-plug or reboot
1926 // so only do this for the experimental VDS path for now...
1927 cr = CycleDevice(ComboBox_GetCurSel(hDeviceList));
1928 if (cr == ERROR_DEVICE_REINITIALIZATION_NEEDED) {
1929 uprintf("Zombie device detected, trying again...");
1930 Sleep(1000);
1931 cr = CycleDevice(ComboBox_GetCurSel(hDeviceList));
1932 }
1933 if (cr == 0)
1934 uprintf("Successfully cycled device");
1935 else
1936 uprintf("Cycling device failed!");
1937 RefreshLayout(DriveIndex);
1938 }
1939
1940 // Wait for the logical drive we just created to appear
1941 uprintf("Waiting for logical drive to reappear...");
1942 Sleep(200);
1943 if (!WaitForLogical(DriveIndex, partition_offset[PI_MAIN])) {
1944 uprintf("Logical drive was not found - aborting");
1945 if (!IS_ERROR(FormatStatus))
1946 FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_TIMEOUT;
1947 goto out;
1948 }
1949 CHECK_FOR_USER_CANCEL;
1950
1951 // Format Casper partition if required. Do it before we format anything with
1952 // a file system that Windows will recognize, to avoid concurrent access.
1953 if (extra_partitions & XP_CASPER) {
1954 uint32_t ext_version = ReadSetting32(SETTING_USE_EXT_VERSION);
1955 if ((ext_version < 2) || (ext_version > 4))
1956 ext_version = 3;
1957 uprintf("Using %s-like method to enable persistence", img_report.uses_casper ? "Ubuntu" : "Debian");
1958 if (!FormatPartition(DriveIndex, partition_offset[PI_CASPER], 0, FS_EXT2 + (ext_version - 2),
1959 img_report.uses_casper ? "casper-rw" : "persistence",
1960 (img_report.uses_casper ? 0 : FP_CREATE_PERSISTENCE_CONF) |
1961 (IsChecked(IDC_QUICK_FORMAT) ? FP_QUICK : 0))) {
1962 if (!IS_ERROR(FormatStatus))
1963 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
1964 goto out;
1965 }
1966 }
1967
1968 GetWindowTextU(hLabel, label, sizeof(label));
1969 if (fs_type < FS_EXT2)
1970 ToValidLabel(label, (fs_type == FS_FAT16) || (fs_type == FS_FAT32) || (fs_type == FS_EXFAT));
1971 ClusterSize = (DWORD)ComboBox_GetCurItemData(hClusterSize);
1972 if ((ClusterSize < 0x200) || (write_as_esp))
1973 ClusterSize = 0; // 0 = default cluster size
1974
1975 Flags = FP_FORCE;
1976 if (IsChecked(IDC_QUICK_FORMAT))
1977 Flags |= FP_QUICK;
1978 if ((fs_type == FS_NTFS) && (enable_ntfs_compression))
1979 Flags |= FP_COMPRESSION;
1980
1981 ret = FormatPartition(DriveIndex, partition_offset[PI_MAIN], ClusterSize, fs_type, label, Flags);
1982 if (!ret) {
1983 // Error will be set by FormatPartition() in FormatStatus
1984 uprintf("Format error: %s", StrError(FormatStatus, TRUE));
1985 goto out;
1986 }
1987
1988 if (use_vds) {
1989 // Get RW access back to the physical drive...
1990 hPhysicalDrive = GetPhysicalHandle(DriveIndex, actual_lock_drive, TRUE, !actual_lock_drive);
1991 if (hPhysicalDrive == INVALID_HANDLE_VALUE) {
1992 FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_OPEN_FAILED;
1993 goto out;
1994 }
1995 }
1996
1997 // Thanks to Microsoft, we must fix the MBR AFTER the drive has been formatted
1998 if ((partition_type == PARTITION_STYLE_MBR) || ((boot_type != BT_NON_BOOTABLE) && (partition_type == PARTITION_STYLE_GPT))) {
1999 PrintInfoDebug(0, MSG_228); // "Writing master boot record..."
2000 if ((!WriteMBR(hPhysicalDrive)) || (!WriteSBR(hPhysicalDrive))) {
2001 if (!IS_ERROR(FormatStatus))
2002 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
2003 goto out;
2004 }
2005 UpdateProgress(OP_FIX_MBR, -1.0f);
2006 }
2007 Sleep(200);
2008 WaitForLogical(DriveIndex, 0);
2009 // Try to continue
2010 CHECK_FOR_USER_CANCEL;
2011
2012 volume_name = GetLogicalName(DriveIndex, partition_offset[PI_MAIN], TRUE, TRUE);
2013 if (volume_name == NULL) {
2014 uprintf("Could not get volume name");
2015 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NO_VOLUME_ID;
2016 goto out;
2017 }
2018 uprintf("Found volume %s", volume_name);
2019
2020 // Windows is really finicky with regards to reassigning drive letters even after
2021 // we forcibly removed them, so add yet another explicit call to RemoveDriveLetters()
2022 RemoveDriveLetters(DriveIndex, FALSE, TRUE);
2023 if (!MountVolume(drive_name, volume_name)) {
2024 uprintf("Could not remount %s as %C: %s\n", volume_name, drive_name[0], WindowsErrorString());
2025 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_MOUNT_VOLUME);
2026 goto out;
2027 }
2028 CHECK_FOR_USER_CANCEL;
2029
2030 // Disable file indexing, unless it was force-enabled by the user
2031 if ((!enable_file_indexing) && ((fs_type == FS_NTFS) || (fs_type == FS_UDF) || (fs_type == FS_REFS))) {
2032 uprintf("Disabling file indexing...");
2033 if (!SetFileAttributesA(volume_name, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED))
2034 uprintf("Could not disable file indexing: %s", WindowsErrorString());
2035 }
2036
2037 // Refresh the drive label - This is needed as Windows may have altered it from
2038 // the name we proposed, and we require an exact label, to patch config files.
2039 if ((fs_type < FS_EXT2) && !GetVolumeInformationU(drive_name, img_report.usb_label,
2040 ARRAYSIZE(img_report.usb_label), NULL, NULL, NULL, NULL, 0)) {
2041 uprintf("Warning: Failed to refresh label: %s", WindowsErrorString());
2042 } else if ((fs_type >= FS_EXT2) && (fs_type <= FS_EXT4)) {
2043 const char* ext_label = GetExtFsLabel(DriveIndex, 0);
2044 if (ext_label != NULL)
2045 static_strcpy(img_report.usb_label, label);
2046 }
2047
2048 if (boot_type != BT_NON_BOOTABLE) {
2049 if (boot_type == BT_UEFI_NTFS) {
2050 // All good
2051 } else if (target_type == TT_UEFI) {
2052 // For once, no need to do anything - just check our sanity
2053 assert((boot_type == BT_IMAGE) && IS_EFI_BOOTABLE(img_report) && (fs_type <= FS_NTFS));
2054 if ( (boot_type != BT_IMAGE) || !IS_EFI_BOOTABLE(img_report) || (fs_type > FS_NTFS) ) {
2055 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_INSTALL_FAILURE;
2056 goto out;
2057 }
2058 } else if ( (boot_type == BT_SYSLINUX_V4) || (boot_type == BT_SYSLINUX_V6) ||
2059 ((boot_type == BT_IMAGE) && (HAS_SYSLINUX(img_report) || HAS_REACTOS(img_report)) &&
2060 (!HAS_WINDOWS(img_report) || !allow_dual_uefi_bios)) ) {
2061 if (!InstallSyslinux(DriveIndex, drive_name[0], fs_type)) {
2062 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_INSTALL_FAILURE;
2063 goto out;
2064 }
2065 } else {
2066 // We still have a lock, which we need to modify the volume boot record
2067 // => no need to reacquire the lock...
2068 hLogicalVolume = GetLogicalHandle(DriveIndex, partition_offset[PI_MAIN], FALSE, TRUE, FALSE);
2069 if ((hLogicalVolume == INVALID_HANDLE_VALUE) || (hLogicalVolume == NULL)) {
2070 uprintf("Could not re-mount volume for partition boot record access");
2071 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED;
2072 goto out;
2073 }
2074 // NB: if you unmount the logical volume here, XP will report error:
2075 // [0x00000456] The media in the drive may have changed
2076 PrintInfoDebug(0, MSG_229);
2077 if (!WritePBR(hLogicalVolume)) {
2078 if (!IS_ERROR(FormatStatus))
2079 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
2080 goto out;
2081 }
2082 // We must close and unlock the volume to write files to it
2083 safe_unlockclose(hLogicalVolume);
2084 }
2085 } else {
2086 if (IsChecked(IDC_EXTENDED_LABEL))
2087 SetAutorun(drive_name);
2088 }
2089 CHECK_FOR_USER_CANCEL;
2090
2091 // We issue a complete remount of the filesystem on account of:
2092 // - Ensuring the file explorer properly detects that the volume was updated
2093 // - Ensuring that an NTFS system will be reparsed so that it becomes bootable
2094 if (!RemountVolume(drive_name, FALSE))
2095 goto out;
2096 CHECK_FOR_USER_CANCEL;
2097
2098 if (boot_type != BT_NON_BOOTABLE) {
2099 if ((boot_type == BT_MSDOS) || (boot_type == BT_FREEDOS)) {
2100 UpdateProgress(OP_FILE_COPY, -1.0f);
2101 PrintInfoDebug(0, MSG_230);
2102 if (!ExtractDOS(drive_name)) {
2103 if (!IS_ERROR(FormatStatus))
2104 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_CANNOT_COPY;
2105 goto out;
2106 }
2107 } else if (boot_type == BT_GRUB4DOS) {
2108 grub4dos_dst[0] = drive_name[0];
2109 IGNORE_RETVAL(_chdirU(app_dir));
2110 uprintf("Installing: %s (Grub4DOS loader) %s", grub4dos_dst,
2111 IsFileInDB(FILES_DIR "\\grub4dos-" GRUB4DOS_VERSION "\\grldr")?"✓":"✗");
2112 if (!CopyFileU(FILES_DIR "\\grub4dos-" GRUB4DOS_VERSION "\\grldr", grub4dos_dst, FALSE))
2113 uprintf("Failed to copy file: %s", WindowsErrorString());
2114 } else if ((boot_type == BT_IMAGE) && (image_path != NULL) && (img_report.is_iso || img_report.is_windows_img)) {
2115 UpdateProgress(OP_FILE_COPY, 0.0f);
2116 drive_name[2] = 0; // Ensure our drive is something like 'D:'
2117 if (windows_to_go) {
2118 PrintInfoDebug(0, MSG_268);
2119 if (!SetupWinToGo(DriveIndex, drive_name, (extra_partitions & XP_ESP))) {
2120 if (!IS_ERROR(FormatStatus))
2121 FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_ISO_EXTRACT);
2122 goto out;
2123 }
2124 } else {
2125 assert(!img_report.is_windows_img);
2126 if (!ExtractISO(image_path, drive_name, FALSE)) {
2127 if (!IS_ERROR(FormatStatus))
2128 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_ISO_EXTRACT);
2129 goto out;
2130 }
2131 if (HAS_KOLIBRIOS(img_report)) {
2132 kolibri_dst[0] = drive_name[0];
2133 uprintf("Installing: %s (KolibriOS loader)", kolibri_dst);
2134 if (ExtractISOFile(image_path, "HD_Load/USB_Boot/MTLD_F32", kolibri_dst,
2135 FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM) == 0) {
2136 uprintf("Warning: loader installation failed - KolibriOS will not boot!");
2137 }
2138 }
2139 // EFI mode selected, with no 'boot###.efi' but Windows 7 x64's 'bootmgr.efi' (bit #0)
2140 if (((target_type == TT_UEFI) || allow_dual_uefi_bios) && HAS_WIN7_EFI(img_report)) {
2141 PrintInfo(0, MSG_232, lmprintf(MSG_307));
2142 uprintf("Win7 EFI boot setup");
2143 img_report.wininst_path[0][0] = drive_name[0];
2144 efi_dst[0] = drive_name[0];
2145 efi_dst[sizeof(efi_dst) - sizeof("\\bootx64.efi")] = 0;
2146 if (!CreateDirectoryA(efi_dst, 0)) {
2147 uprintf("Could not create directory '%s': %s", efi_dst, WindowsErrorString());
2148 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_PATCH);
2149 } else {
2150 efi_dst[sizeof(efi_dst) - sizeof("\\bootx64.efi")] = '\\';
2151 if (!WimExtractFile(img_report.wininst_path[0], 1, "Windows\\Boot\\EFI\\bootmgfw.efi", efi_dst, FALSE)) {
2152 uprintf("Failed to setup Win7 EFI boot");
2153 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_PATCH);
2154 }
2155 }
2156 }
2157 if ( (target_type == TT_BIOS) && HAS_WINPE(img_report) ) {
2158 // Apply WinPe fixup
2159 if (!SetupWinPE(drive_name[0]))
2160 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_PATCH);
2161 }
2162 }
2163 }
2164 UpdateProgress(OP_FINALIZE, -1.0f);
2165 PrintInfoDebug(0, MSG_233);
2166 if (IsChecked(IDC_EXTENDED_LABEL))
2167 SetAutorun(drive_name);
2168 // Issue another complete remount before we exit, to ensure we're clean
2169 RemountVolume(drive_name, TRUE);
2170 // NTFS fixup (WinPE/AIK images don't seem to boot without an extra checkdisk)
2171 if ((boot_type == BT_IMAGE) && (img_report.is_iso) && (fs_type == FS_NTFS)) {
2172 // Try to ensure that all messages from Checkdisk will be in English
2173 if (PRIMARYLANGID(GetThreadUILanguage()) != LANG_ENGLISH) {
2174 SetThreadUILanguage(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US));
2175 if (PRIMARYLANGID(GetThreadUILanguage()) != LANG_ENGLISH)
2176 uprintf("Note: CheckDisk messages may be localized");
2177 }
2178 CheckDisk(drive_name[0]);
2179 UpdateProgress(OP_FINALIZE, -1.0f);
2180 }
2181 }
2182
2183 out:
2184 safe_free(volume_name);
2185 safe_free(buffer);
2186 safe_closehandle(hSourceImage);
2187 safe_unlockclose(hLogicalVolume);
2188 safe_unlockclose(hPhysicalDrive); // This can take a while
2189 if (IS_ERROR(FormatStatus)) {
2190 volume_name = GetLogicalName(DriveIndex, partition_offset[PI_MAIN], TRUE, TRUE);
2191 if (volume_name != NULL) {
2192 if (MountVolume(drive_name, volume_name))
2193 uprintf("Re-mounted volume as %C: after error", drive_name[0]);
2194 free(volume_name);
2195 }
2196 }
2197 PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)TRUE, 0);
2198 ExitThread(0);
2199 }
2200
2201 DWORD WINAPI SaveImageThread(void* param)
2202 {
2203 BOOL s;
2204 DWORD rSize, wSize;
2205 IMG_SAVE *img_save = (IMG_SAVE*)param;
2206 HANDLE hPhysicalDrive = INVALID_HANDLE_VALUE;
2207 HANDLE hDestImage = INVALID_HANDLE_VALUE;
2208 LARGE_INTEGER li;
2209 uint8_t *buffer = NULL;
2210 uint64_t wb;
2211 int i;
2212
2213 PrintInfoDebug(0, MSG_225);
2214 switch (img_save->Type) {
2215 case IMG_SAVE_TYPE_VHD:
2216 hPhysicalDrive = GetPhysicalHandle(img_save->DeviceNum, TRUE, FALSE, FALSE);
2217 break;
2218 case IMG_SAVE_TYPE_ISO:
2219 hPhysicalDrive = CreateFileA(img_save->DevicePath, GENERIC_READ, FILE_SHARE_READ,
2220 NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
2221 break;
2222 default:
2223 uprintf("Invalid image type");
2224 }
2225 if (hPhysicalDrive == INVALID_HANDLE_VALUE) {
2226 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED;
2227 goto out;
2228 }
2229
2230 // Write an image file
2231 // We may have poked the MBR and other stuff, so need to rewind
2232 li.QuadPart = 0;
2233 if (!SetFilePointerEx(hPhysicalDrive, li, NULL, FILE_BEGIN))
2234 uprintf("Warning: Unable to rewind device position - wrong data might be copied!");
2235 hDestImage = CreateFileU(img_save->ImagePath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
2236 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2237 if (hDestImage == INVALID_HANDLE_VALUE) {
2238 uprintf("Could not open image '%s': %s", img_save->ImagePath, WindowsErrorString());
2239 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED;
2240 goto out;
2241 }
2242
2243 buffer = (uint8_t*)_mm_malloc(img_save->BufSize, 16);
2244 if (buffer == NULL) {
2245 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_ENOUGH_MEMORY;
2246 uprintf("could not allocate buffer");
2247 goto out;
2248 }
2249
2250 uprintf("Will use a buffer size of %s", SizeToHumanReadable(img_save->BufSize, FALSE, FALSE));
2251 uprintf("Saving to image '%s'...", img_save->ImagePath);
2252
2253 // Don't bother trying for something clever, using double buffering overlapped and whatnot:
2254 // With Windows' default optimizations, sync read + sync write for sequential operations
2255 // will be as fast, if not faster, than whatever async scheme you can come up with.
2256 UpdateProgressWithInfoInit(NULL, FALSE);
2257 for (wb = 0; ; wb += wSize) {
2258 if (img_save->Type == IMG_SAVE_TYPE_ISO) {
2259 // Optical drives do not appear to increment the sectors to read automatically
2260 li.QuadPart = wb;
2261 if (!SetFilePointerEx(hPhysicalDrive, li, NULL, FILE_BEGIN))
2262 uprintf("Warning: Unable to set device position - wrong data might be copied!");
2263 }
2264 s = ReadFile(hPhysicalDrive, buffer,
2265 (DWORD)MIN(img_save->BufSize, img_save->DeviceSize - wb), &rSize, NULL);
2266 if (!s) {
2267 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_READ_FAULT;
2268 uprintf("Read error: %s", WindowsErrorString());
2269 goto out;
2270 }
2271 if (rSize == 0)
2272 break;
2273 UpdateProgressWithInfo(OP_FORMAT, MSG_261, wb, img_save->DeviceSize);
2274 for (i = 1; i <= WRITE_RETRIES; i++) {
2275 CHECK_FOR_USER_CANCEL;
2276 s = WriteFile(hDestImage, buffer, rSize, &wSize, NULL);
2277 if ((s) && (wSize == rSize))
2278 break;
2279 if (s)
2280 uprintf("Write error: Wrote %d bytes, expected %d bytes", wSize, rSize);
2281 else
2282 uprintf("Write error: %s", WindowsErrorString());
2283 if (i < WRITE_RETRIES) {
2284 li.QuadPart = wb;
2285 uprintf("Retrying in %d seconds...", WRITE_TIMEOUT / 1000);
2286 Sleep(WRITE_TIMEOUT);
2287 if (!SetFilePointerEx(hDestImage, li, NULL, FILE_BEGIN)) {
2288 uprintf("Write error: Could not reset position - %s", WindowsErrorString());
2289 goto out;
2290 }
2291 } else {
2292 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
2293 goto out;
2294 }
2295 Sleep(200);
2296 }
2297 if (i > WRITE_RETRIES)
2298 goto out;
2299 }
2300 if (wb != img_save->DeviceSize) {
2301 uprintf("Error: wrote %s, expected %s", SizeToHumanReadable(wb, FALSE, FALSE),
2302 SizeToHumanReadable(img_save->DeviceSize, FALSE, FALSE));
2303 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
2304 goto out;
2305 }
2306 if (img_save->Type == IMG_SAVE_TYPE_VHD) {
2307 uprintf("Appending VHD footer...");
2308 if (!AppendVHDFooter(img_save->ImagePath)) {
2309 FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
2310 goto out;
2311 }
2312 }
2313 uprintf("Operation complete (Wrote %s).", SizeToHumanReadable(wb, FALSE, FALSE));
2314
2315 out:
2316 safe_free(img_save->ImagePath);
2317 safe_mm_free(buffer);
2318 safe_closehandle(hDestImage);
2319 safe_unlockclose(hPhysicalDrive);
2320 PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)TRUE, 0);
2321 ExitThread(0);
2322 }