[iso] enable splitting of large WIMs when Alt-E is enabled

* Now that we have wimlib, we might as well add this has an option for people who really
  don't want to use NTFS, even if it falls way short of the performance we get with NTFS.
* Also use wimlib to read the install.wim version.
* Also fix a couple Coverity warnings in stdfn.c/stdio.c.
This commit is contained in:
Pete Batard 2025-05-06 01:31:10 +01:00
parent 2470c56c7e
commit ea01cd41c0
No known key found for this signature in database
GPG key ID: 38E0CF5E69EDD671
9 changed files with 228 additions and 160 deletions

254
src/iso.c
View file

@ -44,6 +44,7 @@
#include "rufus.h"
#include "ui.h"
#include "vhd.h"
#include "drive.h"
#include "libfat.h"
#include "missing.h"
@ -69,8 +70,6 @@ _Static_assert(256 * KB >= ISO_BLOCKSIZE, "Can't set PROGRESS_THRESHOLD");
CdIo_t* cdio_open (const char* psz_source, driver_id_t driver_id) {return NULL;}
void cdio_destroy (CdIo_t* p_cdio) {}
uint32_t GetInstallWimVersion(const char* iso);
typedef struct {
BOOLEAN is_cfg;
BOOLEAN is_conf;
@ -81,7 +80,10 @@ typedef struct {
} EXTRACT_PROPS;
RUFUS_IMG_REPORT img_report;
FILE* fd_md5sum = NULL;
int64_t iso_blocking_status = -1;
uint64_t total_blocks, extra_blocks, nb_blocks, last_nb_blocks;
extern uint64_t md5sum_totalbytes;
extern BOOL preserve_timestamps, enable_ntfs_compression, validate_md5sum;
extern HANDLE format_thread;
@ -124,9 +126,7 @@ const char* old_c32_name[NB_OLD_C32] = OLD_C32_NAMES;
static const int64_t old_c32_threshold[NB_OLD_C32] = OLD_C32_THRESHOLD;
static uint8_t joliet_level = 0;
static uint32_t md5sum_size = 0;
static uint64_t total_blocks, extra_blocks, nb_blocks, last_nb_blocks;
static BOOL scan_only = FALSE;
static FILE* fd_md5sum = NULL;
static StrArray config_path, isolinux_path;
static char symlinked_syslinux[MAX_PATH], *md5sum_data = NULL, *md5sum_pos = NULL;
@ -161,6 +161,83 @@ static void log_handler (cdio_log_level_t level, const char *message)
uprintf("libcdio: %s", message);
}
// Returns TRUE if a path appears in md5sum.txt
static BOOL is_in_md5sum(char* path)
{
BOOL found = FALSE;
char c[3], * p, * pos = md5sum_pos, * nul_pos;
// If we are creating the md5sum file from scratch, every file is in it.
if (fd_md5sum != NULL)
return TRUE;
// If we don't have an existing file at this stage, then no file is in it.
if (md5sum_size == 0 || md5sum_data == NULL)
return FALSE;
// We should have a "X:/xyz" path
assert(path[1] == ':' && path[2] == '/');
// Modify the path to have " ./xyz"
c[0] = path[0];
c[1] = path[1];
path[0] = ' ';
path[1] = '.';
// Search for the string in the remainder of the md5sum.txt
// NB: md5sum_data is always NUL terminated.
p = strstr(pos, path);
// Cater for the case where we matched a partial string and look for the full one
while (p != NULL && p[strlen(path)] != '\n' && p[strlen(path)] != '\r' && p[strlen(path)] != '\0') {
pos = p + strlen(path);
p = strstr(pos, path);
}
found = (p != NULL);
// If not found in remainder and we have a remainder, loop to search from beginning
if (!found && pos != md5sum_data) {
nul_pos = pos;
c[2] = *nul_pos;
*nul_pos = 0;
p = strstr(md5sum_data, path);
while (p != NULL && p[strlen(path)] != '\n' && p[strlen(path)] != '\r' && p[strlen(path)] != '\0') {
pos = p + strlen(path);
p = strstr(pos, path);
}
*nul_pos = c[2];
found = (p != NULL);
}
path[0] = c[0];
path[1] = c[1];
if (found)
md5sum_pos = p + strlen(path);
return found;
}
static void _print_extracted_file(char* psz_fullpath, uint64_t file_length, BOOL split)
{
size_t nul_pos;
if (psz_fullpath == NULL)
return;
// Replace slashes with backslashes and append the size to the path for UI display
to_windows_path(psz_fullpath);
nul_pos = strlen(psz_fullpath);
safe_sprintf(&psz_fullpath[nul_pos], 24, " (%s)", SizeToHumanReadable(file_length, TRUE, FALSE));
uprintf(split ? "Splitting: %s" : "Extracting: %s", psz_fullpath);
safe_sprintf(&psz_fullpath[nul_pos], 24, " (%s)", SizeToHumanReadable(file_length, FALSE, FALSE));
PrintStatus(0, MSG_000, psz_fullpath); // MSG_000 is "%s"
// Remove the appended size for extraction
psz_fullpath[nul_pos] = 0;
// ISO9660 cannot handle backslashes
to_unix_path(psz_fullpath);
// Update md5sum_totalbytes as needed
if (is_in_md5sum(psz_fullpath))
md5sum_totalbytes += file_length;
}
#define print_extracted_file(p, l) _print_extracted_file(p, l, FALSE)
#define print_split_file(p, l) _print_extracted_file(p, l, TRUE)
/*
* Scan and set ISO properties
* Returns true if the the current file does not need to be processed further
@ -169,7 +246,7 @@ static BOOL check_iso_props(const char* psz_dirname, int64_t file_length, const
const char* psz_fullpath, EXTRACT_PROPS *props)
{
size_t i, j, k, len;
char bootloader_name[32];
char bootloader_name[32], path[MAX_PATH];
// Check for an isolinux/syslinux config file anywhere
memset(props, 0, sizeof(EXTRACT_PROPS));
@ -216,6 +293,26 @@ static BOOL check_iso_props(const char* psz_dirname, int64_t file_length, const
uprintf("Skipping '%s' file from ISO image", psz_basename);
return TRUE;
}
// Split a >4GB install.wim if the target filesystem is FAT
if (file_length >= 4 * GB && psz_dirname != NULL && IS_FAT(fs_type) && img_report.has_4GB_file == 0x81) {
if (safe_stricmp(&psz_dirname[max(0, ((int)safe_strlen(psz_dirname)) -
((int)strlen(sources_str)))], sources_str) == 0) {
for (i = 0; i < ARRAYSIZE(wininst_name) - 1; i++) {
if (safe_stricmp(psz_basename, wininst_name[i]) == 0 && file_length >= 4 * GB) {
print_split_file((char*)psz_fullpath, file_length);
char* dst = safe_strdup(psz_fullpath);
dst[strlen(dst) - 3] = 's';
dst[strlen(dst) - 2] = 'w';
dst[strlen(dst) - 1] = 'm';
static_sprintf(path, "%s|%s/%s", image_path, psz_dirname, psz_basename);
WimSplitFile(path, dst);
free(dst);
return TRUE;
}
}
}
}
} else { // Scan-time checks
// Check for GRUB artifacts
for (i = 0; i < ARRAYSIZE(grub_dirname); i++) {
@ -327,6 +424,8 @@ static BOOL check_iso_props(const char* psz_dirname, int64_t file_length, const
static_sprintf(img_report.wininst_path[img_report.wininst_index],
"?:%s", psz_fullpath);
img_report.wininst_index++;
if (file_length >= 4 * GB)
img_report.has_4GB_file |= 0x80;
}
}
}
@ -357,7 +456,7 @@ static BOOL check_iso_props(const char* psz_dirname, int64_t file_length, const
img_report.has_old_c32[i] = TRUE;
}
if (file_length >= 4 * GB)
img_report.has_4GB_file = TRUE;
img_report.has_4GB_file++;
// Compute projected size needed (NB: ISO_BLOCKSIZE = UDF_BLOCKSIZE)
if (file_length != 0)
total_blocks += (file_length + (ISO_BLOCKSIZE - 1)) / ISO_BLOCKSIZE;
@ -498,81 +597,6 @@ static void fix_config(const char* psz_fullpath, const char* psz_path, const cha
free(src);
}
// Returns TRUE if a path appears in md5sum.txt
static BOOL is_in_md5sum(char* path)
{
BOOL found = FALSE;
char c[3], *p, *pos = md5sum_pos, *nul_pos;
// If we are creating the md5sum file from scratch, every file is in it.
if (fd_md5sum != NULL)
return TRUE;
// If we don't have an existing file at this stage, then no file is in it.
if (md5sum_size == 0 || md5sum_data == NULL)
return FALSE;
// We should have a "X:/xyz" path
assert(path[1] == ':' && path[2] == '/');
// Modify the path to have " ./xyz"
c[0] = path[0];
c[1] = path[1];
path[0] = ' ';
path[1] = '.';
// Search for the string in the remainder of the md5sum.txt
// NB: md5sum_data is always NUL terminated.
p = strstr(pos, path);
// Cater for the case where we matched a partial string and look for the full one
while (p != NULL && p[strlen(path)] != '\n' && p[strlen(path)] != '\r' && p[strlen(path)] != '\0') {
pos = p + strlen(path);
p = strstr(pos, path);
}
found = (p != NULL);
// If not found in remainder and we have a remainder, loop to search from beginning
if (!found && pos != md5sum_data) {
nul_pos = pos;
c[2] = *nul_pos;
*nul_pos = 0;
p = strstr(md5sum_data, path);
while (p != NULL && p[strlen(path)] != '\n' && p[strlen(path)] != '\r' && p[strlen(path)] != '\0') {
pos = p + strlen(path);
p = strstr(pos, path);
}
*nul_pos = c[2];
found = (p != NULL);
}
path[0] = c[0];
path[1] = c[1];
if (found)
md5sum_pos = p + strlen(path);
return found;
}
static void print_extracted_file(char* psz_fullpath, uint64_t file_length)
{
size_t nul_pos;
if (psz_fullpath == NULL)
return;
// Replace slashes with backslashes and append the size to the path for UI display
to_windows_path(psz_fullpath);
nul_pos = strlen(psz_fullpath);
safe_sprintf(&psz_fullpath[nul_pos], 24, " (%s)", SizeToHumanReadable(file_length, TRUE, FALSE));
uprintf("Extracting: %s", psz_fullpath);
safe_sprintf(&psz_fullpath[nul_pos], 24, " (%s)", SizeToHumanReadable(file_length, FALSE, FALSE));
PrintStatus(0, MSG_000, psz_fullpath); // MSG_000 is "%s"
// Remove the appended size for extraction
psz_fullpath[nul_pos] = 0;
// ISO9660 cannot handle backslashes
to_unix_path(psz_fullpath);
// Update md5sum_totalbytes as needed
if (is_in_md5sum(psz_fullpath))
md5sum_totalbytes += file_length;
}
// Convert from time_t to FILETIME
// Uses 3 static entries so that we can convert 3 concurrent values at the same time
static LPFILETIME __inline to_filetime(time_t t)
@ -1319,7 +1343,8 @@ out:
safe_free(tmp);
}
if (HAS_WININST(img_report)) {
img_report.wininst_version = GetInstallWimVersion(src_iso);
static_sprintf(path, "%s|%s", image_path, &img_report.wininst_path[0][2]);
img_report.wininst_version = GetWimVersion(path);
}
if (img_report.has_grub2) {
char grub_path[128];
@ -1644,71 +1669,6 @@ out:
return ret;
}
uint32_t GetInstallWimVersion(const char* iso)
{
char *wim_path = NULL, buf[UDF_BLOCKSIZE] = { 0 };
uint32_t* wim_header = (uint32_t*)buf, r = 0xffffffff;
iso9660_t* p_iso = NULL;
udf_t* p_udf = NULL;
udf_dirent_t *p_udf_root = NULL, *p_udf_file = NULL;
iso9660_stat_t *p_statbuf = NULL;
wim_path = safe_strdup(&img_report.wininst_path[0][2]);
if (wim_path == NULL)
goto out;
// UDF indiscriminately accepts slash or backslash delimiters,
// but ISO-9660 requires slash
to_unix_path(wim_path);
// First try to open as UDF - fallback to ISO if it failed
p_udf = udf_open(iso);
if (p_udf == NULL)
goto try_iso;
p_udf_root = udf_get_root(p_udf, true, 0);
if (p_udf_root == NULL) {
uprintf("Could not locate UDF root directory");
goto out;
}
p_udf_file = udf_fopen(p_udf_root, wim_path);
if (!p_udf_file) {
uprintf("Could not locate file %s in ISO image", wim_path);
goto out;
}
if (udf_read_block(p_udf_file, buf, 1) != UDF_BLOCKSIZE) {
uprintf("Error reading UDF file %s", wim_path);
goto out;
}
r = wim_header[3];
goto out;
try_iso:
p_iso = iso9660_open_ext(iso, ISO_EXTENSION_MASK);
if (p_iso == NULL) {
uprintf("Could not open image '%s'", iso);
goto out;
}
p_statbuf = iso9660_ifs_stat_translate(p_iso, wim_path);
if (p_statbuf == NULL) {
uprintf("Could not get ISO-9660 file information for file %s", wim_path);
goto out;
}
if (iso9660_iso_seek_read(p_iso, buf, p_statbuf->lsn, 1) != ISO_BLOCKSIZE) {
uprintf("Error reading ISO-9660 file %s at LSN %d", wim_path, p_statbuf->lsn);
goto out;
}
r = wim_header[3];
out:
iso9660_stat_free(p_statbuf);
udf_dirent_free(p_udf_root);
udf_dirent_free(p_udf_file);
iso9660_close(p_iso);
udf_close(p_udf);
safe_free(wim_path);
return bswap_uint32(r);
}
#define ISO_NB_BLOCKS 16
typedef struct {
iso9660_t* p_iso;

View file

@ -188,8 +188,8 @@ static void SetAllowedFileSystems(void)
break;
case BT_IMAGE:
allowed_filesystem[FS_NTFS] = TRUE;
// Don't allow anything besides NTFS if the image has a >4GB file or explicitly requires NTFS
if ((image_path != NULL) && (img_report.has_4GB_file || img_report.needs_ntfs))
// Don't allow anything besides NTFS if the image is not compatible
if ((image_path != NULL) && !IS_FAT32_COMPAT(img_report))
break;
if (!HAS_WINDOWS(img_report) || (target_type != TT_BIOS) || allow_dual_uefi_bios) {
if (!HAS_WINTOGO(img_report) || (ComboBox_GetCurItemData(hImageOption) != IMOP_WIN_TO_GO)) {
@ -1561,7 +1561,7 @@ static DWORD WINAPI BootCheckThread(LPVOID param)
MessageBoxExU(hMainDialog, lmprintf(MSG_189), lmprintf(MSG_099), MB_OK | MB_ICONERROR | MB_IS_RTL, selected_langid);
goto out;
}
if ((IS_FAT(fs_type)) && (img_report.has_4GB_file)) {
if (IS_FAT(fs_type) && !IS_FAT32_COMPAT(img_report)) {
// This ISO image contains a file larger than 4GB file (FAT32)
MessageBoxExU(hMainDialog, lmprintf(MSG_100), lmprintf(MSG_099), MB_OK | MB_ICONERROR | MB_IS_RTL, selected_langid);
goto out;
@ -3841,7 +3841,7 @@ extern int TestHashes(void);
if (no_focus)
continue;
// Alt +/- => Increase or decrease thread priority for format/file-copy/wim-apply operations
// Alt +/- => Increase or decrease thread priority for format/file-copy operations
if ((msg.message == WM_SYSKEYDOWN) && ((msg.wParam == VK_OEM_PLUS) || (msg.wParam == VK_OEM_MINUS) ||
(msg.wParam == VK_ADD) || (msg.wParam == VK_SUBTRACT))) {
int delta = ((msg.wParam == VK_OEM_PLUS) || (msg.wParam == VK_ADD)) ? +1 : -1;
@ -3912,6 +3912,7 @@ extern int TestHashes(void);
WriteSettingBool(SETTING_ENABLE_WIN_DUAL_EFI_BIOS, allow_dual_uefi_bios);
PrintStatusTimeout(lmprintf(MSG_266), allow_dual_uefi_bios);
SetPartitionSchemeAndTargetSystem(FALSE);
SetFileSystemAndClusterSize(NULL);
continue;
}
// Alt-F => Toggle detection of USB HDDs

View file

@ -369,6 +369,7 @@ enum EFI_BOOT_TYPE {
#define HAS_WIN7_EFI(r) ((r.has_efi == 1) && HAS_WININST(r))
#define IS_WINDOWS_1X(r) (r.has_bootmgr_efi && (r.win_version.major >= 10))
#define IS_WINDOWS_11(r) (r.has_bootmgr_efi && (r.win_version.major >= 11))
#define IS_FAT32_COMPAT(r) ((r.has_4GB_file == 0 || (r.has_4GB_file == 0x81 && allow_dual_uefi_bios)) && !r.needs_ntfs)
#define HAS_EFI_IMG(r) (r.efi_img_path[0] != 0)
#define IS_DD_BOOTABLE(r) (r.is_bootable_img > 0)
#define IS_DD_ONLY(r) ((r.is_bootable_img > 0) && (!r.is_iso || r.disable_iso))
@ -437,7 +438,7 @@ typedef struct {
uint8_t has_md5sum;
uint8_t wininst_index;
uint8_t has_symlinks;
BOOLEAN has_4GB_file;
uint8_t has_4GB_file;
BOOLEAN has_long_filename;
BOOLEAN has_deep_directories;
BOOLEAN has_bootmgr;

View file

@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_DIALOG DIALOGEX 12, 12, 232, 326
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_ACCEPTFILES
CAPTION "Rufus 4.8.2240"
CAPTION "Rufus 4.8.2241"
FONT 9, "Segoe UI Symbol", 400, 0, 0x0
BEGIN
LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP
@ -407,8 +407,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 4,8,2240,0
PRODUCTVERSION 4,8,2240,0
FILEVERSION 4,8,2241,0
PRODUCTVERSION 4,8,2241,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -426,13 +426,13 @@ BEGIN
VALUE "Comments", "https://rufus.ie"
VALUE "CompanyName", "Akeo Consulting"
VALUE "FileDescription", "Rufus"
VALUE "FileVersion", "4.8.2240"
VALUE "FileVersion", "4.8.2241"
VALUE "InternalName", "Rufus"
VALUE "LegalCopyright", "© 2011-2025 Pete Batard (GPL v3)"
VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html"
VALUE "OriginalFilename", "rufus-4.8.exe"
VALUE "ProductName", "Rufus"
VALUE "ProductVersion", "4.8.2240"
VALUE "ProductVersion", "4.8.2241"
END
END
BLOCK "VarFileInfo"

View file

@ -1,7 +1,7 @@
/*
* Rufus: The Reliable USB Formatting Utility
* Standard Windows function calls
* 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
@ -885,7 +885,17 @@ next_progress_line:
};
} else {
// TODO: Detect user cancellation here?
WaitForSingleObject(pi.hProcess, INFINITE);
switch (WaitForSingleObject(pi.hProcess, 1800000)) {
case WAIT_TIMEOUT:
uprintf("Command did not terminate within timeout duration");
break;
case WAIT_OBJECT_0:
uprintf("Command was terminated by user");
break;
default:
uprintf("Error while waiting for command to be terminated: %s", WindowsErrorString());
break;
}
}
if (!GetExitCodeProcess(pi.hProcess, &ret))

View file

@ -579,7 +579,17 @@ HANDLE CreateFileWithTimeout(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwS
if (hThread != NULL) {
if (WaitForSingleObject(hThread, dwTimeOut) == WAIT_TIMEOUT) {
CancelSynchronousIo(hThread);
WaitForSingleObject(hThread, 30000);
switch (WaitForSingleObject(hThread, 30000)) {
case WAIT_TIMEOUT:
uprintf("File was not created within timeout duration");
break;
case WAIT_OBJECT_0:
uprintf("File creation aborted by user");
break;
default:
uprintf("Error while waiting for file to ne created: %s", WindowsErrorString());
break;
}
params.dwError = WAIT_TIMEOUT;
}
CloseHandle(hThread);

View file

@ -49,10 +49,13 @@ extern char* save_image_type;
extern BOOL ignore_boot_marker, has_ffu_support;
extern RUFUS_DRIVE rufus_drive[MAX_DRIVES];
extern HANDLE format_thread;
extern FILE* fd_md5sum;
extern uint64_t total_blocks, extra_blocks, nb_blocks, last_nb_blocks;
static char physical_path[128] = "";
static int progress_op = OP_FILE_COPY, progress_msg = MSG_267;
static HANDLE mounted_handle = INVALID_HANDLE_VALUE;
static struct wimlib_progress_info_split last_split_progress;
typedef struct {
const char* ext;
@ -239,6 +242,29 @@ static enum wimlib_progress_status WimProgressFunc(enum wimlib_progress_msg msg_
if (info->extract.current_file_count >= info->extract.end_file_count)
uprintf("\n");
break;
case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
last_split_progress = info->split;
uprintf("● %S", info->split.part_name);
break;
case WIMLIB_PROGRESS_MSG_SPLIT_END_PART:
if (fd_md5sum != NULL) {
// Don't bother computing the hash at write time - just do it post creation
uint8_t sum[MD5_HASHSIZE];
char* filename = wchar_to_utf8(info->split.part_name);
if (filename != NULL) {
HashFile(HASH_MD5, filename, sum);
for (int j = 0; j < MD5_HASHSIZE; j++)
fprintf(fd_md5sum, "%02x", sum[j]);
fprintf(fd_md5sum, " ./%s\n", &filename[3]);
free(filename);
}
}
break;
case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
uint64_t completed_bytes = last_split_progress.completed_bytes + info->write_streams.completed_compressed_bytes;
nb_blocks = last_nb_blocks + completed_bytes / 2048;
UpdateProgressWithInfo(OP_FILE_COPY, MSG_231, nb_blocks, total_blocks + extra_blocks);
break;
default:
break;
}
@ -246,6 +272,27 @@ static enum wimlib_progress_status WimProgressFunc(enum wimlib_progress_msg msg_
return WIMLIB_PROGRESS_STATUS_CONTINUE;
}
// Return the WIM version of an image
uint32_t GetWimVersion(const char* image)
{
int r;
WIMStruct* wim;
struct wimlib_wim_info info;
if (image == NULL)
return 0;
r = wimlib_open_wimU(image, 0, &wim);
if (r == 0) {
r = wimlib_get_wim_info(wim, &info);
wimlib_free(wim);
if (r == 0)
return info.wim_version;
}
uprintf("WARNING: Could not get WIM version: Error %d", r);
return 0;
}
// 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)
{
@ -283,6 +330,29 @@ out:
return (r == 0);
}
// Split an install.wim for FAT32 limits
BOOL WimSplitFile(const char* src, const char* dst)
{
int r = 1;
WIMStruct* wim;
if ((src == NULL) || (dst == NULL))
goto out;
wimlib_global_init(0);
wimlib_set_print_errors(true);
r = wimlib_open_wimU(src, 0, &wim);
if (r == 0) {
wimlib_register_progress_function(wim, WimProgressFunc, NULL);
r = wimlib_splitU(wim, dst, 4094ULL * MB, WIMLIB_WRITE_FLAG_FSYNC);
wimlib_free(wim);
}
wimlib_global_cleanup();
out:
return (r == 0);
}
BOOL WimApplyImage(const char* image, int index, const char* dst)
{
int r = 1;

View file

@ -109,8 +109,10 @@ enum WIMMessage {
WIM_MSG_ABORT_IMAGE = -1
};
extern uint32_t GetWimVersion(const char* image);
extern BOOL WimExtractFile(const char* wim_image, int index, const char* src, const char* dst);
extern BOOL WimApplyImage(const char* image, int index, const char* dst);
extern BOOL WimSplitFile(const char* src, const char* dst);
extern int8_t IsBootableImage(const char* path);
extern char* VhdMountImageAndGetSize(const char* path, uint64_t* disksize);
#define VhdMountImage(path) VhdMountImageAndGetSize(path, NULL)

View file

@ -4434,6 +4434,20 @@ wimlib_split(WIMStruct *wim,
uint64_t part_size,
int write_flags);
#ifdef _RUFUS
static __inline int
wimlib_splitU(WIMStruct *wim,
const char *swm_name,
uint64_t part_size,
int write_flags)
{
wconvert(swm_name);
int r = wimlib_split(wim, wswm_name, part_size, write_flags);
wfree(swm_name);
return r;
}
#endif
/**
* @ingroup G_general
*