From 032d4413c8b2a2074eb2917d6be0f78af890c9ae Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Fri, 16 Jan 2015 01:51:24 +0000 Subject: [PATCH] [togo] Add Windows To Go support - part 2 * Closes #126 * Only supported on Windows 8 or later for now * Also fix MinGW and WDK compilation issues --- res/localization/rufus.loc | 14 +++- res/togo/san_policy.xml | 11 ++++ res/togo/unattend.xml | 11 ++++ src/.msvc/rufus.vcxproj | 1 - src/.msvc/rufus.vcxproj.filters | 3 - src/bled/libbb.h | 10 +-- src/drive.c | 2 +- src/format.c | 109 ++++++++++++++++++++++++++------ src/iso.c | 7 +- src/localization.c | 4 +- src/msapi_utf8.h | 2 +- src/resource.h | 4 +- src/rufus.c | 48 +++++++++----- src/rufus.h | 1 + src/rufus.rc | 20 +++--- src/stdfn.c | 62 ++++++++++++++++++ src/vhd.c | 12 ++-- 17 files changed, 251 insertions(+), 70 deletions(-) create mode 100644 res/togo/san_policy.xml create mode 100644 res/togo/unattend.xml diff --git a/res/localization/rufus.loc b/res/localization/rufus.loc index b046ca78..4d05a422 100644 --- a/res/localization/rufus.loc +++ b/res/localization/rufus.loc @@ -259,9 +259,16 @@ t MSG_093 "IMPORTANT: THIS DRIVE CONTAINS MULTIPLE PARTITIONS!!\n\n" "Should you wish to proceed, you are responsible for any data loss on these partitions." t MSG_094 "Multiple partitions detected" t MSG_095 "DD Image" -t MSG_096 "Only FAT/FAT32 is supported for this type of ISO. Please select FAT/FAT32 as the File system." -t MSG_097 "Only 'bootmgr' or 'WinPE' based ISO images can currently be used with NTFS." -t MSG_098 "FAT/FAT32 can only be used for isolinux based ISO images or when the Target Type is UEFI." +t MSG_096 "The file system currently selected can not be used with this type of ISO. " + "Please select a different file system or use a different ISO." +t MSG_097 "'Windows To Go' can only be applied if the file system is NTFS." +t MSG_098 "IMPORTANT: You are trying to install 'Windows To Go', but your USB drive doesn't " + "have the 'FIXED' attribute. Because of this Windows will most likely freeze during boot, " + "as Microsoft hasn't designed it to work with drives that are 'REMOVABLE'.\n\n" + "Do you still want to proceed?\n\n" + "Note: the 'FIXED/REMOVABLE' attribute is a hardware property that can only be changed " + "using custom tools from the drive manufacturer. However those tools are ALMOST NEVER " + "provided to the public..." t MSG_099 "Filesystem limitation" t MSG_100 "This ISO image contains a file larger than 4GB, which is more than the " "maximum size allowed for a FAT or FAT32 file system." @@ -364,6 +371,7 @@ t MSG_186 "Rufus does not install or run background services, therefore update c t MSG_187 "Invalid image for selected boot option" t MSG_188 "The current image doesn't match the boot option selected. Please use a different image or choose a different boot option." t MSG_189 "This ISO image is not compatible with the selected filesystem" +t MSG_190 "Incompatible drive detected" t MSG_191 "Write pass" t MSG_192 "Read pass" t MSG_193 "Downloaded %s" diff --git a/res/togo/san_policy.xml b/res/togo/san_policy.xml new file mode 100644 index 00000000..b85de4dc --- /dev/null +++ b/res/togo/san_policy.xml @@ -0,0 +1,11 @@ + + + + + 4 + + + 4 + + + \ No newline at end of file diff --git a/res/togo/unattend.xml b/res/togo/unattend.xml new file mode 100644 index 00000000..0f2847d2 --- /dev/null +++ b/res/togo/unattend.xml @@ -0,0 +1,11 @@ + + + + + true + + + true + + + \ No newline at end of file diff --git a/src/.msvc/rufus.vcxproj b/src/.msvc/rufus.vcxproj index 64360b8b..bffa495e 100644 --- a/src/.msvc/rufus.vcxproj +++ b/src/.msvc/rufus.vcxproj @@ -223,7 +223,6 @@ - diff --git a/src/.msvc/rufus.vcxproj.filters b/src/.msvc/rufus.vcxproj.filters index 7da8b596..ca115177 100644 --- a/src/.msvc/rufus.vcxproj.filters +++ b/src/.msvc/rufus.vcxproj.filters @@ -139,9 +139,6 @@ Resource Files - - Resource Files - diff --git a/src/bled/libbb.h b/src/bled/libbb.h index fe5e8add..9019e9ed 100644 --- a/src/bled/libbb.h +++ b/src/bled/libbb.h @@ -187,6 +187,11 @@ static inline ssize_t full_read(int fd, void *buf, size_t count) { return rb; } +static inline struct tm *localtime_r(const time_t *timep, struct tm *result) { + result = localtime(timep); + return result; +} + #define full_write _write #define safe_read full_read #define lstat stat @@ -197,11 +202,6 @@ static inline ssize_t full_read(int fd, void *buf, size_t count) { #define mkdir(x, y) _mkdirU(x) #if defined(_MSC_VER) -static inline struct tm *localtime_r(const time_t *timep, struct tm *result) { - result = localtime(timep); - return result; -} - #define _S_IFBLK 0x3000 #define S_IFMT _S_IFMT diff --git a/src/drive.c b/src/drive.c index 09501210..7e9b9aaf 100644 --- a/src/drive.c +++ b/src/drive.c @@ -751,7 +751,7 @@ BOOL GetDrivePartitionData(DWORD DriveIndex, char* FileSystemName, DWORD FileSys SelectedDrive.nPartitions++; tmp[0] = 0; wchar_to_utf8_no_alloc(DriveLayout->PartitionEntry[i].Gpt.Name, tmp, sizeof(tmp)); - suprintf("Partition %d:\r\n Type: %s\r\n Name: '%s'\n", DriveLayout->PartitionEntry[i].PartitionNumber, + suprintf("Partition %d:\r\n Type: %s\r\n Name: '%s'\n", i+1, GuidToString(&DriveLayout->PartitionEntry[i].Gpt.PartitionType), tmp); suprintf(" ID: %s\r\n Size: %s (%lld bytes)\r\n Start Sector: %lld, Attributes: 0x%016llX\n", GuidToString(&DriveLayout->PartitionEntry[i].Gpt.PartitionId), SizeToHumanReadable(DriveLayout->PartitionEntry[i].PartitionLength.QuadPart, TRUE, FALSE), diff --git a/src/format.c b/src/format.c index 5b675bc1..d4bc27fc 100644 --- a/src/format.c +++ b/src/format.c @@ -1225,6 +1225,79 @@ out: return r; } +// http://technet.microsoft.com/en-ie/library/jj721578.aspx +static BOOL SetupWinToGo(const char* drive_name) +{ + char san_policy_path[] = "?:\\san_policy.xml", unattend_path[] = "?:\\Windows\\System32\\sysprep\\unattend.xml"; + char *mounted_iso, image[128], cmd[128]; + unsigned char *buffer; + DWORD bufsize; + FILE* fd; + + uprintf("Windows To Go mode selected"); + + // First, we need to access the install.wim image, that resides on the ISO + mounted_iso = MountISO(image_path); + if (mounted_iso == NULL) { + uprintf("Could not mount ISO for Windows To Go installation"); + FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_ISO_EXTRACT); + return FALSE; + } + uprintf("Mounted ISO as '%s'", mounted_iso); + + // Now we use the WIM API to apply that image + static_sprintf(image, "%s\\sources\\install.wim", mounted_iso); + if (!WimApplyImage(image, 1, drive_name)) { + uprintf("Failed to apply Windows To Go image"); + if (!IS_ERROR(FormatStatus)) + FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_ISO_EXTRACT); + UnMountISO(); + return FALSE; + } + UnMountISO(); + + uprintf("Setting up boot for Windows To Go..."); + static_sprintf(cmd, "%C:\\Windows\\System32\\bcdboot.exe %C:\\Windows /f ALL /s %C:", + drive_name[0], drive_name[0], drive_name[0]); + if (RunCommand(cmd, NULL, TRUE) != 0) { + // Fatal, as the UFD is unlikely to boot then + uprintf("Command '%s' failed to run", cmd); + FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_ISO_EXTRACT); + return FALSE; + } + UpdateProgress(OP_DOS, 99.0f); + + // The following are non fatal if they fail + buffer = GetResource(hMainInstance, MAKEINTRESOURCEA(IDR_TOGO_SAN_POLICY_XML), + _RT_RCDATA, "san_policy.xml", &bufsize, FALSE); + san_policy_path[0] = drive_name[0]; + uprintf("Applying san_policy.xml..."); + fd = fopenU(san_policy_path, "wb"); + if ((fd == NULL) || (fwrite(buffer, 1, bufsize, fd) != bufsize)) { + uprintf("Could not write '%s'\n", san_policy_path); + if (fd) + fclose(fd); + } else { + fclose(fd); + static_sprintf(cmd, "dism /Image:%C:\\ /Apply-Unattend:%s", drive_name[0], san_policy_path); + if (RunCommand(cmd, NULL, TRUE) != 0) + uprintf("Command '%s' failed to run"); + } + + uprintf("Copying 'unattend.xml'"); + buffer = GetResource(hMainInstance, MAKEINTRESOURCEA(IDR_TOGO_UNATTEND_XML), + _RT_RCDATA, "unattend.xml", &bufsize, FALSE); + unattend_path[0] = drive_name[0]; + fd = fopenU(unattend_path, "wb"); + if ((fd == NULL) || (fwrite(buffer, 1, bufsize, fd) != bufsize)) { + uprintf("Could not write '%s'\n", unattend_path); + } + fclose(fd); + UpdateProgress(OP_DOS, 100.0f); + + return TRUE; +} + /* * Detect if a Windows Format prompt is active, by enumerating the * whole Windows tree and looking for the relevant popup @@ -1305,10 +1378,10 @@ DWORD WINAPI FormatThread(void* param) LARGE_INTEGER li; uint64_t wb; uint8_t *buffer = NULL, *aligned_buffer; - char *bb_msg, *guid_volume = NULL, *mounted_iso; + char *bb_msg, *guid_volume = NULL; char drive_name[] = "?:\\"; char drive_letters[27]; - char logfile[MAX_PATH], image[128], *userdir; + char logfile[MAX_PATH], *userdir; char wim_image[] = "?:\\sources\\install.wim"; char efi_dst[] = "?:\\efi\\boot\\bootx64.efi"; char kolibri_dst[] = "?:\\MTLD_F32"; @@ -1693,28 +1766,24 @@ DWORD WINAPI FormatThread(void* param) UpdateProgress(OP_DOS, 0.0f); PrintInfoDebug(0, MSG_231); drive_name[2] = 0; - // TODO: Check that we have apply-wim support if (HAS_TOGO(iso_report) && (Button_GetCheck(GetDlgItem(hMainDialog, IDC_WINDOWS_TO_GO)) == BST_CHECKED)) { - uprintf("Windows To Go mode selected"); - mounted_iso = MountISO(image_path); - if (mounted_iso == NULL) { - uprintf("Could not mount ISO for Windows To Go installation"); - FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_ISO_EXTRACT); - } else { - uprintf("Mounted ISO as '%s'", mounted_iso); - static_sprintf(image, "%s\\sources\\install.wim", mounted_iso); - if (!WimApplyImage(image, 1, drive_name)) { - uprintf("Failed to setup Windows To Go"); - if (!IS_ERROR(FormatStatus)) - FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_ISO_EXTRACT); - } - UnMountISO(); - } - if (IS_ERROR(FormatStatus)) + // Sanity checks + if (fs != FS_NTFS) { + FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_INCOMPATIBLE_FS); goto out; + } + if ((nWindowsVersion < WINDOWS_8) || ((WimExtractCheck() & 4) == 0)) { + FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_SUPPORTED; + goto out; + } + if (!SetupWinToGo(drive_name)) { + if (!IS_ERROR(FormatStatus)) + FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_ISO_EXTRACT; + goto out; + } } else if (!ExtractISO(image_path, drive_name, FALSE)) { if (!IS_ERROR(FormatStatus)) - FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_CANNOT_COPY; + FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_ISO_EXTRACT; goto out; } if (iso_report.has_kolibrios) { diff --git a/src/iso.c b/src/iso.c index 2d97fd03..9075a8b1 100644 --- a/src/iso.c +++ b/src/iso.c @@ -925,8 +925,8 @@ out: /* * The following is used for native ISO mounting in Windows 8 or later */ -const GUID VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT = - { 0xEC984AECL, 0xA0F9, 0x47e9, { 0x90, 0x1F, 0x71, 0x41, 0x5A, 0x66, 0x34, 0x5B } }; +#define VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT \ + { 0xEC984AECL, 0xA0F9, 0x47e9, { 0x90, 0x1F, 0x71, 0x41, 0x5A, 0x66, 0x34, 0x5B } } typedef enum _VIRTUAL_DISK_ACCESS_MASK { VIRTUAL_DISK_ACCESS_NONE = 0x00000000, @@ -1015,7 +1015,7 @@ static HANDLE mounted_handle = INVALID_HANDLE_VALUE; char* MountISO(const char* path) { VIRTUAL_STORAGE_TYPE vtype = { 1, VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT }; - ATTACH_VIRTUAL_DISK_PARAMETERS vparams = { ATTACH_VIRTUAL_DISK_VERSION_1, 0 }; + ATTACH_VIRTUAL_DISK_PARAMETERS vparams = {0}; DWORD r; wchar_t wtmp[128]; ULONG size = ARRAYSIZE(wtmp); @@ -1037,6 +1037,7 @@ char* MountISO(const char* path) goto out; } + vparams.Version = ATTACH_VIRTUAL_DISK_VERSION_1; r = pfAttachVirtualDisk(mounted_handle, NULL, ATTACH_VIRTUAL_DISK_FLAG_READ_ONLY | ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER, 0, &vparams, NULL); if (r != ERROR_SUCCESS) { diff --git a/src/localization.c b/src/localization.c index a4604a6d..f3586890 100644 --- a/src/localization.c +++ b/src/localization.c @@ -407,11 +407,11 @@ char* lmprintf(int msg_id, ...) #define MSG_INFO 1 #define MSG_LOW_PRI 0 #define MSG_HIGH_PRI 1 -char szMessage[2][2][MSG_LEN] = { 0 }; +char szMessage[2][2][MSG_LEN] = { {"", ""}, {"", ""} }; char* szStatusMessage = szMessage[MSG_STATUS][MSG_HIGH_PRI]; static BOOL bStatusTimerArmed = FALSE; -static __inline OutputMessage(BOOL info, char* msg) +static void __inline OutputMessage(BOOL info, char* msg) { if (info) SetWindowTextU(hInfo, msg); diff --git a/src/msapi_utf8.h b/src/msapi_utf8.h index 87609fe0..32c0eb57 100644 --- a/src/msapi_utf8.h +++ b/src/msapi_utf8.h @@ -506,9 +506,9 @@ static __inline int SHDeleteDirectoryExU(HWND hwnd, const char* pszPath, FILEOP_ // which is always expected to be larger than our UTF-16 one, and add 2 chars for good measure. size_t wpszPath_len = strlen(pszPath) + 2; wchar_t* wpszPath = (wchar_t*)calloc(wpszPath_len, sizeof(wchar_t)); + SHFILEOPSTRUCTW shfo = { hwnd, FO_DELETE, wpszPath, NULL, fFlags, FALSE, NULL, NULL }; utf8_to_wchar_no_alloc(pszPath, wpszPath, wpszPath_len); // FOF_SILENT | FOF_NOERRORUI | FOF_NOCONFIRMATION, - SHFILEOPSTRUCTW shfo = { hwnd, FO_DELETE, wpszPath, NULL, fFlags, FALSE, NULL, NULL }; ret = SHFileOperationW(&shfo); wfree(pszPath); return ret; diff --git a/src/resource.h b/src/resource.h index 1910b489..8b804f88 100644 --- a/src/resource.h +++ b/src/resource.h @@ -70,6 +70,8 @@ #define IDR_LC_RUFUS_LOC 500 #define IDR_XT_HOGGER 501 #define IDR_UEFI_TOGO 502 +#define IDR_TOGO_SAN_POLICY_XML 503 +#define IDR_TOGO_UNATTEND_XML 504 #define IDC_DEVICE 1001 #define IDC_FILESYSTEM 1002 #define IDC_START 1003 @@ -411,7 +413,7 @@ #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 -#define _APS_NEXT_RESOURCE_VALUE 502 +#define _APS_NEXT_RESOURCE_VALUE 505 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1071 #define _APS_NEXT_SYMED_VALUE 4000 diff --git a/src/rufus.c b/src/rufus.c index 2d10f341..96ad85d5 100644 --- a/src/rufus.c +++ b/src/rufus.c @@ -933,8 +933,11 @@ static void DisplayISOProps(void) uprintf(" Uses ReactOS: %s", YesNo(IS_REACTOS(iso_report))); uprintf(" Uses WinPE: %s%s", YesNo(IS_WINPE(iso_report.winpe)), (iso_report.uses_minint) ? " (with /minint)" : ""); - if ( ((!togo_mode) && (HAS_TOGO(iso_report))) || ((togo_mode) && (!HAS_TOGO(iso_report))) ) - ToggleToGo(); + // We don't support ToGo on Windows 7 or earlier, for lack of ISO mount capabilities + // TODO: add install.wim extraction workaround for Windows 7 + if (nWindowsVersion >= WINDOWS_8) + if ( ((!togo_mode) && (HAS_TOGO(iso_report))) || ((togo_mode) && (!HAS_TOGO(iso_report))) ) + ToggleToGo(); } // The scanning process can be blocking for message processing => use a thread @@ -1186,7 +1189,18 @@ static BOOL BootCheck(void) } fs = (int)ComboBox_GetItemData(hFileSystem, ComboBox_GetCurSel(hFileSystem)); bt = GETBIOSTYPE((int)ComboBox_GetItemData(hPartitionScheme, ComboBox_GetCurSel(hPartitionScheme))); - if (bt == BT_UEFI) { + if ((togo_mode) && (Button_GetCheck(GetDlgItem(hMainDialog, IDC_WINDOWS_TO_GO)) == BST_CHECKED)) { + if (fs != FS_NTFS) { + // Windows To Go only works for NTFS + MessageBoxU(hMainDialog, lmprintf(MSG_097), lmprintf(MSG_092), MB_OK|MB_ICONERROR|MB_IS_RTL); + return FALSE; + } else if (SelectedDrive.Geometry.MediaType != FixedMedia) { + // I never had any success with drives that have the REMOVABLE attribute set, no matter the + // method or tool I tried. If you manage to get this working, I'd like to hear from you! + if (MessageBoxU(hMainDialog, lmprintf(MSG_098), lmprintf(MSG_190), MB_YESNO|MB_ICONWARNING|MB_IS_RTL) != IDYES) + return FALSE; + } + } else if (bt == BT_UEFI) { if (!iso_report.has_efi) { // Unsupported ISO MessageBoxU(hMainDialog, lmprintf(MSG_091), lmprintf(MSG_090), MB_OK|MB_ICONERROR|MB_IS_RTL); @@ -1198,24 +1212,16 @@ static BOOL BootCheck(void) ShellExecuteA(hMainDialog, "open", SEVENZIP_URL, NULL, NULL, SW_SHOWNORMAL); return FALSE; } - } else if ((fs == FS_NTFS) && (!iso_report.has_bootmgr) && (!IS_WINPE(iso_report.winpe)) && (!IS_GRUB(iso_report))) { - if (HAS_SYSLINUX(iso_report)) { - // Only FAT/FAT32 is supported for this type of ISO - MessageBoxU(hMainDialog, lmprintf(MSG_096), lmprintf(MSG_092), MB_OK|MB_ICONERROR|MB_IS_RTL); - } else { - // Only 'bootmgr' or 'WinPE' based ISO images can currently be used with NTFS - MessageBoxU(hMainDialog, lmprintf(MSG_097), lmprintf(MSG_090), MB_OK|MB_ICONERROR|MB_IS_RTL); - } + } else if ( ((fs == FS_NTFS) && (!iso_report.has_bootmgr) && (!IS_WINPE(iso_report.winpe)) && (!IS_GRUB(iso_report))) + || (((fs == FS_FAT16)||(fs == FS_FAT32)) && (!HAS_SYSLINUX(iso_report)) && (!allow_dual_uefi_bios) && + (!IS_REACTOS(iso_report)) && (!iso_report.has_kolibrios) && (!IS_GRUB(iso_report))) ) { + // Incompatible FS and ISO + MessageBoxU(hMainDialog, lmprintf(MSG_096), lmprintf(MSG_092), MB_OK|MB_ICONERROR|MB_IS_RTL); return FALSE; } else if ((fs == FS_FAT16) && (iso_report.has_kolibrios)) { // KolibriOS doesn't support FAT16 MessageBoxU(hMainDialog, lmprintf(MSG_189), lmprintf(MSG_099), MB_OK|MB_ICONERROR|MB_IS_RTL); return FALSE; - } else if (((fs == FS_FAT16)||(fs == FS_FAT32)) && (!HAS_SYSLINUX(iso_report)) && (!allow_dual_uefi_bios) && - (!IS_REACTOS(iso_report)) && (!iso_report.has_kolibrios) && (!IS_GRUB(iso_report))) { - // FAT/FAT32 can only be used for isolinux based ISO images or when the Target Type is UEFI - MessageBoxU(hMainDialog, lmprintf(MSG_098), lmprintf(MSG_090), MB_OK|MB_ICONERROR|MB_IS_RTL); - return FALSE; } if (((fs == FS_FAT16)||(fs == FS_FAT32)) && (iso_report.has_4GB_file)) { // This ISO image contains a file larger than 4GB file (FAT32) @@ -2153,6 +2159,16 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_START_THREAD); } break; + case IDC_WINDOWS_TO_GO: + if (Button_GetCheck(GetDlgItem(hMainDialog, IDC_WINDOWS_TO_GO)) == BST_CHECKED) { + for (i=0; i