@ -1,7 +1,7 @@
/*
* Rufus : The Reliable USB Formatting Utility
* Virtual Disk Handling functions
* Copyright © 2013 - 2024 Pete Batard < pete @ akeo . ie >
* Copyright © 2013 - 2025 Pete Batard < pete @ akeo . ie >
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
@ -41,56 +41,19 @@
# include "msapi_utf8.h"
# include "drive.h"
# include "wimlib.h"
# include "registry.h"
# include "bled/bled.h"
/ / WIM API Prototypes
PF_TYPE_DECL ( WINAPI , HANDLE , WIMCreateFile , ( PWSTR , DWORD , DWORD , DWORD , DWORD , PDWORD ) ) ;
PF_TYPE_DECL ( WINAPI , BOOL , WIMSetTemporaryPath , ( HANDLE , PWSTR ) ) ;
PF_TYPE_DECL ( WINAPI , HANDLE , WIMLoadImage , ( HANDLE , DWORD ) ) ;
PF_TYPE_DECL ( WINAPI , BOOL , WIMMountImage , ( PCWSTR , PCWSTR , DWORD , PCWSTR ) ) ;
PF_TYPE_DECL ( WINAPI , BOOL , WIMUnmountImage , ( PCWSTR , PCWSTR , DWORD , BOOL ) ) ;
PF_TYPE_DECL ( WINAPI , BOOL , WIMApplyImage , ( HANDLE , PCWSTR , DWORD ) ) ;
PF_TYPE_DECL ( WINAPI , BOOL , WIMExtractImagePath , ( HANDLE , PWSTR , PWSTR , DWORD ) ) ;
PF_TYPE_DECL ( WINAPI , BOOL , WIMGetImageInformation , ( HANDLE , PVOID , PDWORD ) ) ;
PF_TYPE_DECL ( WINAPI , BOOL , WIMCloseHandle , ( HANDLE ) ) ;
PF_TYPE_DECL ( WINAPI , DWORD , WIMRegisterMessageCallback , ( HANDLE , FARPROC , PVOID ) ) ;
PF_TYPE_DECL ( WINAPI , DWORD , WIMUnregisterMessageCallback , ( HANDLE , FARPROC ) ) ;
typedef struct {
int index ;
BOOL commit ;
const char * image ;
const char * dst ;
} mount_params_t ;
uint32_t wim_nb_files , wim_proc_files , wim_extra_files ;
HANDLE wim_thread = NULL ;
extern int default_thread_priority ;
extern char * save_image_type ;
extern BOOL ignore_boot_marker , has_ffu_support ;
extern RUFUS_DRIVE rufus_drive [ MAX_DRIVES ] ;
extern HANDLE format_thread ;
static uint8_t wim_flags = 0 ;
static uint32_t progress_report_mask ;
static uint64_t progress_offset = 0 , progress_total = 100 ;
static wchar_t wmount_path [ MAX_PATH ] = { 0 } , wmount_track [ MAX_PATH ] = { 0 } ;
static char sevenzip_path [ MAX_PATH ] , physical_path [ 128 ] = " " ;
static char physical_path [ 128 ] = " " ;
static int progress_op = OP_FILE_COPY , progress_msg = MSG_267 ;
static BOOL count_files ;
static HANDLE mounted_handle = INVALID_HANDLE_VALUE ;
static BOOL Get7ZipPath ( void )
{
if ( ( GetRegistryKeyStr ( REGKEY_HKCU , " Software \\ 7-Zip \\ Path " , sevenzip_path , sizeof ( sevenzip_path ) ) )
| | ( GetRegistryKeyStr ( REGKEY_HKLM , " Software \\ 7-Zip \\ Path " , sevenzip_path , sizeof ( sevenzip_path ) ) ) ) {
static_strcat ( sevenzip_path , " \\ 7z.exe " ) ;
return ( _accessU ( sevenzip_path , 0 ) ! = - 1 ) ;
}
return FALSE ;
}
typedef struct {
const char * ext ;
uint8_t type ;
@ -232,681 +195,115 @@ out:
}
/ / WIM operations progress callback
DWORD WINAPI WimProgressCallback ( DWORD dwMsgId , WPARAM wParam , LPARAM lParam , PVOID pvIgnored )
static enum wimlib_progress_status WimProgressFunc ( enum wimlib_progress_msg msg_type ,
union wimlib_progress_info * info , void * progctx )
{
PBOOL pbCancel = NULL ;
PWIN32_FIND_DATA pFileData ;
const char * level = NULL ;
uint64_t size ;
static BOOL init [ 3 ] = { 0 } ;
if IS_ERROR ( ErrorStatus )
return WIMLIB_PROGRESS_STATUS_ABORT ;
switch ( dwMsgId ) {
case WIM_MSG_PROGRESS :
/ / The default WIM progress is useless for apply ( freezes at 95 % , which is usually when
/ / only half the files have been processed ) , so we only use it for mounting / unmounting .
if ( ! ( progress_report_mask & WIM_REPORT_PROGRESS ) )
break ;
UpdateProgressWithInfo ( progress_op , progress_msg , progress_offset + wParam , progress_total ) ;
switch ( msg_type ) {
case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN :
memset ( init , 0 , sizeof ( init ) ) ;
uprintf ( " Applying image %d ( \" %S \" ) from '%S' to '%S' " ,
info - > extract . image , info - > extract . image_name ,
info - > extract . wimfile_name , info - > extract . target ) ;
break ;
case WIM_MSG_PROCESS :
if ( ! ( progress_report_mask & WIM_REPORT_PROCESS ) )
break ;
/ / The amount of files processed is overwhelming ( 16 k + for a typical image ) ,
/ / and trying to display it * WILL * slow us down , so we don ' t .
#if 0
uprintf ( " %S " , ( PWSTR ) wParam ) ;
PrintStatus ( 0 , MSG_000 , str ) ; / / MSG_000 is " %s "
# endif
if ( count_files ) {
wim_nb_files + + ;
} else {
/ / At the end of an actual apply , the WIM API re - lists a bunch of directories it already processed ,
/ / so , even as we try to compensate , we might end up with more entries than counted - ignore those .
if ( wim_proc_files < wim_nb_files )
wim_proc_files + + ;
else
wim_extra_files + + ;
UpdateProgressWithInfo ( progress_op , progress_msg , wim_proc_files , wim_nb_files ) ;
case WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE :
if ( ! init [ 0 ] ) {
uprintf ( " Creating file structure... " ) ;
init [ 0 ] = TRUE ;
uprint_progress ( 0 , 0 ) ;
}
/ / Halt on error
if ( IS_ERROR ( ErrorStatus ) ) {
pbCancel = ( PBOOL ) lParam ;
* pbCancel = TRUE ;
break ;
UpdateProgressWithInfoUpTo ( 98 , progress_op , progress_msg , info - > extract . current_file_count , info - > extract . end_file_count * 6 ) ;
uprint_progress ( info - > extract . current_file_count , info - > extract . end_file_count ) ;
break ;
case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS :
if ( ! init [ 1 ] ) {
uprintf ( " \n Extracting file data... " ) ;
init [ 1 ] = TRUE ;
uprint_progress ( 0 , 0 ) ;
}
UpdateProgressWithInfoUpTo ( 98 , progress_op , progress_msg , info - > extract . total_bytes + ( 4 * info - > extract . completed_bytes ) , info - > extract . total_bytes * 6 ) ;
uprint_progress ( info - > extract . completed_bytes , info - > extract . total_bytes ) ;
break ;
case WIM_MSG_FILEINFO :
if ( ! ( progress_report_mask & WIM_REPORT_FILEINFO ) )
break ;
pFileData = ( PWIN32_FIND_DATA ) lParam ;
if ( pFileData - > dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
uprintf ( " Creating: %S " , ( PWSTR ) wParam ) ;
} else {
size = ( ( ( uint64_t ) pFileData - > nFileSizeHigh ) < < 32 ) + pFileData - > nFileSizeLow ;
uprintf ( " Extracting: %S (%s) " , ( PWSTR ) wParam , SizeToHumanReadable ( size , FALSE , FALSE ) ) ;
case WIMLIB_PROGRESS_MSG_EXTRACT_METADATA :
if ( ! init [ 2 ] ) {
uprintf ( " \n Applying metadata to files... " ) ;
init [ 2 ] = TRUE ;
uprint_progress ( 0 , 0 ) ;
}
UpdateProgressWithInfoUpTo ( 98 , progress_op , progress_msg , info - > extract . current_file_count + ( 5 * info - > extract . end_file_count ) , info - > extract . end_file_count * 6 ) ;
uprint_progress ( info - > extract . current_file_count , info - > extract . end_file_count ) ;
if ( info - > extract . current_file_count > = info - > extract . end_file_count )
uprintf ( " \n " ) ;
break ;
case WIM_MSG_RETRY :
level = " retry " ;
/ / fall through
case WIM_MSG_INFO :
if ( level = = NULL ) level = " info " ;
/ / fall through
case WIM_MSG_WARNING :
if ( level = = NULL ) level = " warning " ;
/ / fall through
case WIM_MSG_ERROR :
if ( level = = NULL ) level = " error " ;
SetLastError ( ( DWORD ) lParam ) ;
uprintf ( " WIM processing %s: %S [%s] \n " , level , ( PWSTR ) wParam , WindowsErrorString ( ) ) ;
default :
break ;
}
return IS_ERROR ( ErrorStatus ) ? WIM_MSG_ABORT_IMAGE : WIM_MSG_SUCCESS ;
}
/ / Find out if we have any way to extract / apply WIM files on this platform
/ / Returns a bitfield of the methods we can use ( 1 = Extract using wimgapi , 2 = Extract using 7 - Zip , 4 = Apply using wimgapi )
uint8_t WimExtractCheck ( BOOL bSilent )
{
PF_INIT ( WIMCreateFile , Wimgapi ) ;
PF_INIT ( WIMSetTemporaryPath , Wimgapi ) ;
PF_INIT ( WIMLoadImage , Wimgapi ) ;
PF_INIT ( WIMApplyImage , Wimgapi ) ;
PF_INIT ( WIMExtractImagePath , Wimgapi ) ;
PF_INIT ( WIMGetImageInformation , Wimgapi ) ;
PF_INIT ( WIMRegisterMessageCallback , Wimgapi ) ;
PF_INIT ( WIMUnregisterMessageCallback , Wimgapi ) ;
PF_INIT ( WIMCloseHandle , Wimgapi ) ;
if ( pfWIMCreateFile & & pfWIMSetTemporaryPath & & pfWIMLoadImage & & pfWIMExtractImagePath & & pfWIMCloseHandle )
wim_flags | = WIM_HAS_API_EXTRACT ;
if ( Get7ZipPath ( ) )
wim_flags | = WIM_HAS_7Z_EXTRACT ;
if ( ( wim_flags & WIM_HAS_API_EXTRACT ) & & pfWIMApplyImage & & pfWIMRegisterMessageCallback & & pfWIMUnregisterMessageCallback )
wim_flags | = WIM_HAS_API_APPLY ;
suprintf ( " WIM extraction method(s) supported: %s%s%s " , ( wim_flags & WIM_HAS_7Z_EXTRACT ) ? " 7-Zip " :
( ( wim_flags & WIM_HAS_API_EXTRACT ) ? " " : " NONE " ) ,
( WIM_HAS_EXTRACT ( wim_flags ) = = ( WIM_HAS_API_EXTRACT | WIM_HAS_7Z_EXTRACT ) ) ? " , " :
" " , ( wim_flags & WIM_HAS_API_EXTRACT ) ? " wimgapi.dll " : " " ) ;
suprintf ( " WIM apply method supported: %s " , ( wim_flags & WIM_HAS_API_APPLY ) ? " wimgapi.dll " : " NONE " ) ;
return wim_flags ;
}
/ /
/ / Looks like Microsoft ' s idea of " mount " for WIM images involves the creation
/ / of a as many virtual junctions as there exist directories on the image . . .
/ / So , yeah , this is both very slow and wasteful of space .
/ /
/ / NB : You can see mounted WIMs , along with their mountpoint , by checking :
/ / HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ WIMMount \ Mounted Images
/ / You can also mount / unmount images from an elevated prompt with something like :
/ / dism / mount - image [ / readonly ] / imagefile : F : \ sources \ boot . wim / index : 2 / mountdir : C : \ test \ offline
/ / dism / unmount - image / discard / mountdir : C : \ test \ offline
/ /
static DWORD WINAPI WimMountImageThread ( LPVOID param )
{
BOOL r = FALSE ;
wconvert ( temp_dir ) ;
mount_params_t * mp = ( mount_params_t * ) param ;
wchar_t * wimage = utf8_to_wchar ( mp - > image ) ;
PF_INIT_OR_OUT ( WIMRegisterMessageCallback , Wimgapi ) ;
PF_INIT_OR_OUT ( WIMMountImage , Wimgapi ) ;
PF_INIT_OR_OUT ( WIMUnmountImage , Wimgapi ) ;
PF_INIT_OR_OUT ( WIMUnregisterMessageCallback , Wimgapi ) ;
if ( wmount_path [ 0 ] ! = 0 ) {
uprintf ( " WimMountImage: An image is already mounted. Trying to unmount it... " ) ;
if ( pfWIMUnmountImage ( wmount_path , wimage , mp - > index , FALSE ) )
uprintf ( " WimMountImage: Successfully unmounted existing image.. " ) ;
else
goto out ;
}
if ( GetTempFileNameW ( wtemp_dir , L " Ruf " , 0 , wmount_path ) = = 0 ) {
uprintf ( " WimMountImage: Can not generate mount directory: %s " , WindowsErrorString ( ) ) ;
goto out ;
}
DeleteFileW ( wmount_path ) ;
if ( ! CreateDirectoryW ( wmount_path , 0 ) ) {
uprintf ( " WimMountImage: Can not create mount directory '%S': %s " , wmount_path , WindowsErrorString ( ) ) ;
goto out ;
}
if ( GetTempFileNameW ( wtemp_dir , L " Ruf " , 0 , wmount_track ) = = 0 ) {
uprintf ( " WimMountImage: Can not generate tracking directory: %s " , WindowsErrorString ( ) ) ;
goto out ;
}
DeleteFileW ( wmount_track ) ;
if ( ! CreateDirectoryW ( wmount_track , 0 ) ) {
uprintf ( " WimMountImage: Can not create tracking directory '%S': %s " , wmount_track , WindowsErrorString ( ) ) ;
goto out ;
}
progress_report_mask = WIM_REPORT_PROGRESS ;
progress_op = OP_PATCH ;
progress_msg = MSG_325 ;
progress_offset = 1 ;
progress_total = PATCH_PROGRESS_TOTAL ;
if ( pfWIMRegisterMessageCallback ( NULL , ( FARPROC ) WimProgressCallback , NULL ) = = INVALID_CALLBACK_VALUE ) {
uprintf ( " WimMountImage: Could not set progress callback: %s " , WindowsErrorString ( ) ) ;
goto out ;
}
r = pfWIMMountImage ( wmount_path , wimage , mp - > index , wmount_track ) ;
pfWIMUnregisterMessageCallback ( NULL , ( FARPROC ) WimProgressCallback ) ;
if ( ! r ) {
uprintf ( " Could not mount '%S [%d]' on '%S': %s " , wimage , mp - > index , wmount_path , WindowsErrorString ( ) ) ;
goto out ;
}
uprintf ( " Mounted '%S [%d]' on '%S' " , wimage , mp - > index , wmount_path ) ;
out :
if ( ! r ) {
if ( wmount_track [ 0 ] ! = 0 ) {
RemoveDirectoryW ( wmount_track ) ;
wmount_track [ 0 ] = 0 ;
}
if ( wmount_path [ 0 ] ! = 0 ) {
RemoveDirectoryW ( wmount_path ) ;
wmount_path [ 0 ] = 0 ;
}
}
wfree ( temp_dir ) ;
safe_free ( wimage ) ;
ExitThread ( ( DWORD ) r ) ;
}
/ / Returns the temporary mount path on success , NULL on error .
/ / Returned path must be freed by the caller .
char * WimMountImage ( const char * image , int index )
{
char * mount_path = NULL ;
DWORD dw = 0 ;
mount_params_t mp = { 0 } ;
mp . image = image ;
mp . index = index ;
/ / Try to unmount an existing stale image , if there is any
mount_path = WimGetExistingMountPoint ( image , index ) ;
if ( mount_path ! = NULL ) {
uprintf ( " WARNING: Found stale '%s [%d]' image mounted on '%s' - Attempting to unmount it... " ,
image , index , mount_path ) ;
utf8_to_wchar_no_alloc ( mount_path , wmount_path , ARRAYSIZE ( wmount_path ) ) ;
wmount_track [ 0 ] = 0 ;
WimUnmountImage ( image , index , FALSE ) ;
}
wim_thread = CreateThread ( NULL , 0 , WimMountImageThread , & mp , 0 , NULL ) ;
if ( wim_thread = = NULL ) {
uprintf ( " Unable to start mount-image thread " ) ;
return NULL ;
}
SetThreadPriority ( wim_thread , default_thread_priority ) ;
WaitForSingleObject ( wim_thread , INFINITE ) ;
if ( ! GetExitCodeThread ( wim_thread , & dw ) )
dw = 0 ;
wim_thread = NULL ;
return ( dw ) ? wchar_to_utf8 ( wmount_path ) : NULL ;
}
static DWORD WINAPI WimUnmountImageThread ( LPVOID param )
{
BOOL r = FALSE ;
mount_params_t * mp = ( mount_params_t * ) param ;
wchar_t * wimage = utf8_to_wchar ( mp - > image ) ;
PF_INIT_OR_OUT ( WIMRegisterMessageCallback , Wimgapi ) ;
PF_INIT_OR_OUT ( WIMUnmountImage , Wimgapi ) ;
PF_INIT_OR_OUT ( WIMUnregisterMessageCallback , Wimgapi ) ;
if ( wmount_path [ 0 ] = = 0 ) {
uprintf ( " WimUnmountImage: No image is mounted " ) ;
goto out ;
}
progress_report_mask = WIM_REPORT_PROGRESS ;
progress_op = OP_PATCH ;
progress_msg = MSG_325 ;
progress_offset = 105 ;
progress_total = PATCH_PROGRESS_TOTAL ;
if ( pfWIMRegisterMessageCallback ( NULL , ( FARPROC ) WimProgressCallback , NULL ) = = INVALID_CALLBACK_VALUE ) {
uprintf ( " WimUnmountImage: Could not set progress callback: %s " , WindowsErrorString ( ) ) ;
goto out ;
}
r = pfWIMUnmountImage ( wmount_path , wimage , mp - > index , mp - > commit ) ;
pfWIMUnregisterMessageCallback ( NULL , ( FARPROC ) WimProgressCallback ) ;
if ( ! r ) {
uprintf ( " Could not unmount '%S': %s " , wmount_path , WindowsErrorString ( ) ) ;
goto out ;
}
uprintf ( " Unmounted '%S [%d]' " , wmount_path , mp - > index ) ;
if ( wmount_track [ 0 ] ! = 0 & & ! RemoveDirectoryW ( wmount_track ) )
uprintf ( " Could not delete '%S' : %s " , wmount_track , WindowsErrorString ( ) ) ;
wmount_track [ 0 ] = 0 ;
if ( wmount_path [ 0 ] ! = 0 & & ! RemoveDirectoryW ( wmount_path ) )
uprintf ( " Could not delete '%S' : %s " , wmount_path , WindowsErrorString ( ) ) ;
wmount_path [ 0 ] = 0 ;
out :
safe_free ( wimage ) ;
ExitThread ( ( DWORD ) r ) ;
}
BOOL WimUnmountImage ( const char * image , int index , BOOL commit )
{
DWORD dw = 0 ;
mount_params_t mp = { 0 } ;
mp . image = image ;
mp . index = index ;
mp . commit = commit ;
wim_thread = CreateThread ( NULL , 0 , WimUnmountImageThread , & mp , 0 , NULL ) ;
if ( wim_thread = = NULL ) {
uprintf ( " Unable to start unmount-image thread " ) ;
return FALSE ;
}
SetThreadPriority ( wim_thread , default_thread_priority ) ;
WaitForSingleObject ( wim_thread , INFINITE ) ;
if ( ! GetExitCodeThread ( wim_thread , & dw ) )
dw = 0 ;
wim_thread = NULL ;
return dw ;
}
/ / Get the existing mount point ( if any ) for the image + index passed as parameters .
/ / Needed because Windows refuses to mount two images with the same path / index even
/ / if the previous has become stale or deleted . This situation may occur if the user
/ / force - closed Rufus when ' boot . wim ' was mounted , thus leaving them unable to mount
/ / ' boot . wim ' for subsequent sessions unless they invoke dism / Unmount - Wim manually .
/ / This basically parses HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ WIMMount \ Mounted Images
/ / to see if an instance exists with the image / index passed as parameter and returns
/ / the mount point of this image if found , or NULL otherwise .
char * WimGetExistingMountPoint ( const char * image , int index )
{
static char path [ MAX_PATH ] ;
char class [ MAX_PATH ] = " " , guid [ 40 ] , key_name [ MAX_PATH ] ;
HKEY hKey ;
DWORD dw = 0 , i , k , nb_subkeys = 0 , class_size ;
DWORD cbMaxSubKey , cchMaxClass , cValues , cchMaxValue ;
DWORD cbMaxValueData , cbSecurityDescriptor ;
FILETIME ftLastWriteTime ;
if ( RegOpenKeyExA ( HKEY_LOCAL_MACHINE , " SOFTWARE \\ Microsoft \\ WIMMount \\ Mounted Images " ,
0 , KEY_READ , & hKey ) ! = ERROR_SUCCESS )
return NULL ;
class_size = sizeof ( class ) ;
RegQueryInfoKeyA ( hKey , class , & class_size , NULL , & nb_subkeys ,
& cbMaxSubKey , & cchMaxClass , & cValues , & cchMaxValue ,
& cbMaxValueData , & cbSecurityDescriptor , & ftLastWriteTime ) ;
for ( k = 0 ; k < nb_subkeys ; k + + ) {
dw = sizeof ( guid ) ;
if ( RegEnumKeyExA ( hKey , k , guid , & dw , NULL , NULL , NULL ,
& ftLastWriteTime ) = = ERROR_SUCCESS ) {
static_sprintf ( key_name , " SOFTWARE \\ Microsoft \\ WIMMount \\ Mounted Images \\ %s \\ WIM Path " , guid ) ;
if ( GetRegistryKeyStr ( HKEY_LOCAL_MACHINE , key_name , path , sizeof ( path ) ) & &
( stricmp ( path , image ) ! = 0 ) )
continue ;
static_sprintf ( key_name , " SOFTWARE \\ Microsoft \\ WIMMount \\ Mounted Images \\ %s \\ Image Index " , guid ) ;
if ( GetRegistryKey32 ( HKEY_LOCAL_MACHINE , key_name , & i ) & & ( i ! = ( DWORD ) index ) )
continue ;
path [ 0 ] = 0 ;
static_sprintf ( key_name , " SOFTWARE \\ Microsoft \\ WIMMount \\ Mounted Images \\ %s \\ Mount Path " , guid ) ;
if ( GetRegistryKeyStr ( HKEY_LOCAL_MACHINE , key_name , path , sizeof ( path ) ) )
break ;
}
}
if ( k > = nb_subkeys )
path [ 0 ] = 0 ;
RegCloseKey ( hKey ) ;
return ( path [ 0 ] = = 0 ) ? NULL : path ;
}
/ / Extract a file from a WIM image using wimgapi . dll
/ / NB : if you want progress from a WIM callback , you must run the WIM API call in its own thread
/ / ( which we don ' t do here ) as it won ' t work otherwise . Thanks go to Erwan for figuring this out !
BOOL WimExtractFile_API ( const char * image , int index , const char * src , const char * dst , BOOL bSilent )
{
static char * index_name = " [1].xml " ;
BOOL r = FALSE ;
DWORD dw = 0 ;
HANDLE hWim = NULL ;
HANDLE hImage = NULL ;
HANDLE hFile = NULL ;
wchar_t wtemp [ MAX_PATH ] = { 0 } ;
wchar_t * wimage = utf8_to_wchar ( image ) ;
wchar_t * wsrc = utf8_to_wchar ( src ) ;
wchar_t * wdst = utf8_to_wchar ( dst ) ;
wchar_t * wim_info ;
PF_INIT_OR_OUT ( WIMCreateFile , Wimgapi ) ;
PF_INIT_OR_OUT ( WIMSetTemporaryPath , Wimgapi ) ;
PF_INIT_OR_OUT ( WIMLoadImage , Wimgapi ) ;
PF_INIT_OR_OUT ( WIMExtractImagePath , Wimgapi ) ;
PF_INIT_OR_OUT ( WIMCloseHandle , Wimgapi ) ;
suprintf ( " Opening: %s:[%d] (API) " , image , index ) ;
if ( GetTempPathW ( ARRAYSIZE ( wtemp ) , wtemp ) = = 0 ) {
uprintf ( " Could not fetch temp path: %s " , WindowsErrorString ( ) ) ;
goto out ;
}
/ / Thanks to dism + + for figuring out that you can use UNDOCUMENTED FLAG 0x20000000
/ / to open newer install . wim / install . esd images , without running into obnoxious error :
/ / [ 0x0000000B ] An attempt was made to load a program with an incorrect format .
/ / No thanks to Microsoft for NOT DOCUMENTING THEIR UTTER BULLSHIT with the WIM API !
hWim = pfWIMCreateFile ( wimage , WIM_GENERIC_READ , WIM_OPEN_EXISTING ,
( img_report . wininst_version > = SPECIAL_WIM_VERSION ) ? WIM_UNDOCUMENTED_BULLSHIT : 0 , 0 , NULL ) ;
if ( hWim = = NULL ) {
uprintf ( " Could not access image: %s " , WindowsErrorString ( ) ) ;
goto out ;
}
if ( ! pfWIMSetTemporaryPath ( hWim , wtemp ) ) {
uprintf ( " Could not set temp path: %s " , WindowsErrorString ( ) ) ;
goto out ;
}
suprintf ( " Extracting: %s (From %s) " , dst , src ) ;
if ( safe_strcmp ( src , index_name ) = = 0 ) {
if ( ! pfWIMGetImageInformation ( hWim , & wim_info , & dw ) | | ( dw = = 0 ) ) {
uprintf ( " Could not access WIM info: %s " , WindowsErrorString ( ) ) ;
goto out ;
}
hFile = CreateFileW ( wdst , GENERIC_READ | GENERIC_WRITE , FILE_SHARE_READ ,
NULL , CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL ) ;
if ( ( hFile = = INVALID_HANDLE_VALUE ) | | ( ! WriteFile ( hFile , wim_info , dw , & dw , NULL ) ) ) {
suprintf ( " Could not extract file: %s " , WindowsErrorString ( ) ) ;
goto out ;
}
} else {
hImage = pfWIMLoadImage ( hWim , ( DWORD ) index ) ;
if ( hImage = = NULL ) {
uprintf ( " Could not set index: %s " , WindowsErrorString ( ) ) ;
goto out ;
}
if ( ! pfWIMExtractImagePath ( hImage , wsrc , wdst , 0 ) ) {
suprintf ( " Could not extract file: %s " , WindowsErrorString ( ) ) ;
goto out ;
}
}
r = TRUE ;
out :
if ( ( hImage ! = NULL ) | | ( hWim ! = NULL ) ) {
suprintf ( " Closing: %s " , image ) ;
if ( hImage ! = NULL ) pfWIMCloseHandle ( hImage ) ;
if ( hWim ! = NULL ) pfWIMCloseHandle ( hWim ) ;
}
safe_closehandle ( hFile ) ;
safe_free ( wimage ) ;
safe_free ( wsrc ) ;
safe_free ( wdst ) ;
return r ;
return WIMLIB_PROGRESS_STATUS_CONTINUE ;
}
/ / Extract a file from a WIM image using 7 - Zip
BOOL WimExtractFile_7z ( const char * image , int index , const char * src , const char * dst , BOOL bSilen t )
/ / Extract a file from a WIM image . Returns the allocated path of the extracted file or NULL on error .
BOOL WimExtractFile ( const char * image , int index , const char * src , const char * dst )
{
int n ;
size_t i ;
char cmdline [ MAX_PATH ] ;
char tmpdst [ MAX_PATH ] ;
char index_prefix [ ] = " # \\ " ;
suprintf ( " Opening: %s:[%d] (7-Zip) " , image , index ) ;
int r = 1 ;
WIMStruct * wim ;
char tmp [ MAX_PATH ] = " " , * p ;
if ( ( image = = NULL ) | | ( src = = NULL ) | | ( dst = = NULL ) )
return FALSE ;
/ / If you shove more than 9 images in a WIM , don ' t come complaining
/ / that this breaks !
index_prefix [ 0 ] = ' 0 ' + index ;
suprintf ( " Extracting: %s (From %s) " , dst , src ) ;
/ / 7 z has a quirk where the image index MUST be specified if a
/ / WIM has multiple indexes , but it MUST be removed if there is
/ / only one image . Because of this ( and because 7 z will not
/ / return an error code if it can ' t extract the file ) , we need
/ / to issue 2 passes . See github issue # 680.
for ( n = 0 ; n < 2 ; n + + ) {
static_strcpy ( tmpdst , dst ) ;
for ( i = strlen ( tmpdst ) - 1 ; ( i > 0 ) & & ( tmpdst [ i ] ! = ' \\ ' ) & & ( tmpdst [ i ] ! = ' / ' ) ; i - - ) ;
tmpdst [ i ] = 0 ;
static_sprintf ( cmdline , " \" %s \" -y e \" %s \" %s%s " , sevenzip_path ,
image , ( n = = 0 ) ? index_prefix : " " , src ) ;
if ( RunCommand ( cmdline , tmpdst , FALSE ) ! = 0 ) {
uprintf ( " Could not launch 7z.exe: %s " , WindowsErrorString ( ) ) ;
return FALSE ;
}
for ( i = safe_strlen ( src ) ; ( i > 0 ) & & ( src [ i ] ! = ' \\ ' ) & & ( src [ i ] ! = ' / ' ) ; i - - ) ;
if ( i = = 0 )
static_strcat ( tmpdst , " \\ " ) ;
static_strcat ( tmpdst , & src [ i ] ) ;
if ( _access ( tmpdst , 0 ) = = 0 )
/ / File was extracted = > move on
break ;
}
if ( n > = 2 ) {
suprintf ( " 7z.exe did not extract %s " , tmpdst ) ;
return FALSE ;
}
/ / coverity [ toctou ]
if ( ! MoveFileExU ( tmpdst , dst , MOVEFILE_REPLACE_EXISTING ) ) {
uprintf ( " Could not rename %s to %s: %s " , tmpdst , dst , WindowsErrorString ( ) ) ;
return FALSE ;
}
return TRUE ;
}
/ / Extract a file from a WIM image
BOOL WimExtractFile ( const char * image , int index , const char * src , const char * dst , BOOL bSilent )
{
if ( ( wim_flags = = 0 ) & & ( ! WIM_HAS_EXTRACT ( WimExtractCheck ( TRUE ) ) ) )
return FALSE ;
if ( ( image = = NULL ) | | ( src = = NULL ) | | ( dst = = NULL ) )
return FALSE ;
/ / Prefer 7 - Zip as , unsurprisingly , it ' s faster than the Microsoft way ,
/ / but allow fallback if 7 - Zip doesn ' t succeed
return ( ( ( wim_flags & WIM_HAS_7Z_EXTRACT ) & & WimExtractFile_7z ( image , index , src , dst , bSilent ) )
| | ( ( wim_flags & WIM_HAS_API_EXTRACT ) & & WimExtractFile_API ( image , index , src , dst , bSilent ) ) ) ;
}
/ / / < summary >
/ / / Find if a specific index belongs to a WIM image .
/ / / < / summary >
/ / / < param name = " image " > The path to the WIM file . < / param >
/ / / < param name = " index " > The ( non - zero ) value of the index to check . < / param >
/ / / < returns > TRUE if the index was found in the image , FALSE otherwise . < / returns >
BOOL WimIsValidIndex ( const char * image , int index )
{
int i = 1 , cur_index ;
BOOL r = FALSE ;
DWORD dw = 0 ;
HANDLE hWim = NULL ;
HANDLE hFile = NULL ;
char xml_file [ MAX_PATH ] = { 0 } ;
char * str ;
wchar_t * wimage = utf8_to_wchar ( image ) ;
wchar_t * wim_info ;
PF_INIT_OR_OUT ( WIMCreateFile , Wimgapi ) ;
PF_INIT_OR_OUT ( WIMGetImageInformation , Wimgapi ) ;
PF_INIT_OR_OUT ( WIMCloseHandle , Wimgapi ) ;
/ / Zero indexes are invalid
if ( index = = 0 )
goto out ;
hWim = pfWIMCreateFile ( wimage , WIM_GENERIC_READ , WIM_OPEN_EXISTING ,
( img_report . wininst_version > = SPECIAL_WIM_VERSION ) ? WIM_UNDOCUMENTED_BULLSHIT : 0 , 0 , NULL ) ;
if ( hWim = = NULL ) {
uprintf ( " Could not access image: %s " , WindowsErrorString ( ) ) ;
goto out ;
}
if ( ! pfWIMGetImageInformation ( hWim , & wim_info , & dw ) | | ( dw = = 0 ) ) {
uprintf ( " Could not access WIM info: %s " , WindowsErrorString ( ) ) ;
goto out ;
}
if ( ( GetTempFileNameU ( temp_dir , APPLICATION_NAME , 0 , xml_file ) = = 0 ) | | ( xml_file [ 0 ] = = 0 ) )
static_strcpy ( xml_file , " . \\ RufVXml.tmp " ) ;
DeleteFileU ( xml_file ) ;
hFile = CreateFileU ( xml_file , GENERIC_READ | GENERIC_WRITE , FILE_SHARE_READ ,
NULL , CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL ) ;
if ( ( hFile = = INVALID_HANDLE_VALUE ) | | ( ! WriteFile ( hFile , wim_info , dw , & dw , NULL ) ) )
goto out ;
while ( ( str = get_token_data_file_indexed ( " IMAGE INDEX " , xml_file , i ) ) ! = NULL ) {
cur_index = atoi ( str ) ;
safe_free ( str ) ;
if ( cur_index = = index ) {
r = TRUE ;
break ;
assert ( strrchr ( src , ' \\ ' ) ! = NULL ) ;
assert ( strrchr ( dst , ' \\ ' ) ! = NULL ) ;
if ( strrchr ( src , ' \\ ' ) = = NULL | | strrchr ( dst , ' \\ ' ) = = NULL )
goto out ;
p = strrchr ( dst , ' \\ ' ) ;
* p = ' \0 ' ;
wimlib_global_init ( 0 ) ;
wimlib_set_print_errors ( true ) ;
r = wimlib_open_wimU ( image , 0 , & wim ) ;
if ( r = = 0 ) {
r = wimlib_extract_pathsU ( wim , index , dst , & src , 1 , WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE ) ;
wimlib_free ( wim ) ;
static_strcpy ( tmp , dst ) ;
static_strcat ( tmp , strrchr ( src , ' \\ ' ) ) ;
* p = ' \\ ' ;
if ( ! MoveFileExU ( tmp , dst , MOVEFILE_REPLACE_EXISTING ) ) {
uprintf ( " Could not rename %s to %s: %s " , tmp , dst , WindowsErrorString ( ) ) ;
r = 1 ;
}
i + + ;
}
out :
if ( hWim ! = NULL )
pfWIMCloseHandle ( hWim ) ;
safe_closehandle ( hFile ) ;
if ( xml_file [ 0 ] ! = 0 )
DeleteFileU ( xml_file ) ;
safe_free ( wimage ) ;
return r ;
}
/ / Apply a WIM image using wimgapi . dll
/ / https : / / docs . microsoft . com / en - us / previous - versions / msdn10 / dd851944 ( v = msdn .10 )
/ / To get progress , we must run this call within its own thread
static DWORD WINAPI WimApplyImageThread ( LPVOID param )
{
BOOL r = FALSE ;
HANDLE hWim = NULL ;
HANDLE hImage = NULL ;
wchar_t wtemp [ MAX_PATH ] = { 0 } ;
mount_params_t * mp = ( mount_params_t * ) param ;
wchar_t * wimage = utf8_to_wchar ( mp - > image ) ;
wchar_t * wdst = utf8_to_wchar ( mp - > dst ) ;
PF_INIT_OR_OUT ( WIMRegisterMessageCallback , Wimgapi ) ;
PF_INIT_OR_OUT ( WIMCreateFile , Wimgapi ) ;
PF_INIT_OR_OUT ( WIMSetTemporaryPath , Wimgapi ) ;
PF_INIT_OR_OUT ( WIMLoadImage , Wimgapi ) ;
PF_INIT_OR_OUT ( WIMApplyImage , Wimgapi ) ;
PF_INIT_OR_OUT ( WIMCloseHandle , Wimgapi ) ;
PF_INIT_OR_OUT ( WIMUnregisterMessageCallback , Wimgapi ) ;
uprintf ( " Opening: %s:[%d] " , mp - > image , mp - > index ) ;
progress_report_mask = WIM_REPORT_PROCESS | WIM_REPORT_FILEINFO ;
progress_op = OP_FILE_COPY ;
progress_msg = MSG_267 ;
progress_offset = 0 ;
progress_total = 100 ;
if ( pfWIMRegisterMessageCallback ( NULL , ( FARPROC ) WimProgressCallback , NULL ) = = INVALID_CALLBACK_VALUE ) {
uprintf ( " Could not set progress callback: %s " , WindowsErrorString ( ) ) ;
goto out ;
}
if ( GetTempPathW ( ARRAYSIZE ( wtemp ) , wtemp ) = = 0 ) {
uprintf ( " Could not fetch temp path: %s " , WindowsErrorString ( ) ) ;
goto out ;
}
hWim = pfWIMCreateFile ( wimage , WIM_GENERIC_READ , WIM_OPEN_EXISTING ,
( img_report . wininst_version > = SPECIAL_WIM_VERSION ) ? WIM_UNDOCUMENTED_BULLSHIT : 0 , 0 , NULL ) ;
if ( hWim = = NULL ) {
uprintf ( " Could not access image: %s " , WindowsErrorString ( ) ) ;
goto out ;
}
if ( ! pfWIMSetTemporaryPath ( hWim , wtemp ) ) {
uprintf ( " Could not set temp path: %s " , WindowsErrorString ( ) ) ;
goto out ;
}
hImage = pfWIMLoadImage ( hWim , ( DWORD ) mp - > index ) ;
if ( hImage = = NULL ) {
uprintf ( " Could not set index: %s " , WindowsErrorString ( ) ) ;
goto out ;
}
uprintf ( " Applying Windows image... " ) ;
UpdateProgressWithInfoInit ( NULL , TRUE ) ;
/ / Run a first pass using WIM_FLAG_NO_APPLY to count the files
wim_nb_files = 0 ;
wim_proc_files = 0 ;
wim_extra_files = 0 ;
count_files = TRUE ;
if ( ! pfWIMApplyImage ( hImage , wdst , WIM_FLAG_NO_APPLY ) ) {
uprintf ( " Could not count the files to apply: %s " , WindowsErrorString ( ) ) ;
goto out ;
}
/ / The latest Windows 10 ISOs have a ~ 17.5 % discrepancy between the number of
/ / files and directories actually applied vs . the ones counted when not applying .
/ / Therefore , we add a ' safe ' 20 % to our counted files to compensate for yet
/ / another dismal Microsoft progress reporting API . . .
wim_nb_files + = wim_nb_files / 5 ;
count_files = FALSE ;
/ / Actual apply
if ( ! pfWIMApplyImage ( hImage , wdst , WIM_FLAG_FILEINFO ) ) {
uprintf ( " Could not apply image: %s " , WindowsErrorString ( ) ) ;
goto out ;
}
/ / Ensure that we ' ll pick if need to readjust our 20 % above from user reports
if ( wim_extra_files > 0 )
uprintf ( " Notice: An extra %d files and directories were applied, from the %d expected " ,
wim_extra_files , wim_nb_files ) ;
/ / Re - use extra files as the final progress step
wim_extra_files = ( wim_nb_files - wim_proc_files ) / 3 ;
UpdateProgressWithInfo ( OP_FILE_COPY , MSG_267 , wim_proc_files + wim_extra_files , wim_nb_files ) ;
r = TRUE ;
wimlib_global_cleanup ( ) ;
out :
if ( ( hImage ! = NULL ) | | ( hWim ! = NULL ) ) {
uprintf ( " Closing: %s " , mp - > image ) ;
if ( hImage ! = NULL ) pfWIMCloseHandle ( hImage ) ;
if ( hWim ! = NULL ) pfWIMCloseHandle ( hWim ) ;
}
if ( pfWIMUnregisterMessageCallback ! = NULL )
pfWIMUnregisterMessageCallback ( NULL , ( FARPROC ) WimProgressCallback ) ;
safe_free ( wimage ) ;
safe_free ( wdst ) ;
ExitThread ( ( DWORD ) r ) ;
return ( r = = 0 ) ;
}
BOOL WimApplyImage ( const char * image , int index , const char * dst )
{
DWORD dw = 0 ;
mount_params_t mp = { 0 } ;
mp . image = image ;
mp . index = index ;
mp . dst = dst ;
wim_thread = CreateThread ( NULL , 0 , WimApplyImageThread , & mp , 0 , NULL ) ;
if ( wim_thread = = NULL ) {
uprintf ( " Unable to start apply-image thread " ) ;
return FALSE ;
int r = 1 ;
WIMStruct * wim ;
wimlib_global_init ( 0 ) ;
wimlib_set_print_errors ( true ) ;
uprintf ( " Opening: %s:[%d] " , image , index ) ;
r = wimlib_open_wimU ( image , 0 , & wim ) ;
if ( r = = 0 ) {
progress_op = OP_FILE_COPY ;
progress_msg = MSG_267 ;
wimlib_register_progress_function ( wim , WimProgressFunc , NULL ) ;
r = wimlib_extract_imageU ( wim , index , dst , 0 ) ;
wimlib_free ( wim ) ;
} else {
uprintf ( " Failed to open '%s': Wimlib error %d " , image , r ) ;
}
SetThreadPriority ( wim_thread , default_thread_priority ) ;
WaitForSingleObject ( wim_thread , INFINITE ) ;
if ( ! GetExitCodeThread ( wim_thread , & dw ) )
dw = 0 ;
wim_thread = NULL ;
return dw ;
wimlib_global_cleanup ( ) ;
return ( r = = 0 ) ;
}
/ / Mount an ISO or a VHD / VHDX image and provide its size