mirror of
https://github.com/pbatard/rufus.git
synced 2025-05-31 14:58:26 -04:00
[iso] bootable NTFS from ISO image [EXPERIMENTAL]
* bootmgr ISOs only * extraction and ISO support UI improvements * UTF8 support through CreateFileU * cancellation on ISO file extraction * switch to using CreateThread
This commit is contained in:
parent
22276ccb5a
commit
472db8b592
7 changed files with 232 additions and 117 deletions
164
src/iso.c
164
src/iso.c
|
@ -52,8 +52,9 @@
|
|||
#endif
|
||||
// How often should we update the progress bar (in 2K blocks) as updating
|
||||
// the progress bar for every block will bring extraction to a crawl
|
||||
#define PROGRESS_UPDATE 1024
|
||||
#define FOUR_GIGABYTES 4294967296LL
|
||||
#define PROGRESS_THRESHOLD 1024
|
||||
#define THREADED_CLOSE_THRESHOLD (20 * 1024 * 1024) // 20 MB
|
||||
#define FOUR_GIGABYTES 4294967296LL
|
||||
|
||||
// Needed for UDF ISO access
|
||||
CdIo_t* cdio_open (const char *psz_source, driver_id_t driver_id) {return NULL;}
|
||||
|
@ -64,12 +65,52 @@ static const char *psz_extract_dir;
|
|||
static uint64_t total_blocks, nb_blocks;
|
||||
static BOOL scan_only = FALSE;
|
||||
|
||||
// TODO: Unicode support, timestamp & permissions preservation
|
||||
// TODO: Timestamp & permissions preservation
|
||||
|
||||
// Convert a file size to human readable
|
||||
static __inline char* size_to_hr(int64_t size)
|
||||
{
|
||||
int suffix = 0;
|
||||
static char str_size[24];
|
||||
const char* sizes[] = { "bytes", "KB", "MB", "GB", "TB", "PB" };
|
||||
double hr_size = (double)size;
|
||||
while ((suffix < ARRAYSIZE(sizes)) && (hr_size >= 1024.0)) {
|
||||
hr_size /= 1024.0;
|
||||
suffix++;
|
||||
}
|
||||
safe_sprintf(str_size, sizeof(str_size), " (%0.1f %s)", hr_size, sizes[suffix]);
|
||||
return str_size;
|
||||
}
|
||||
|
||||
// Interruptible thread for handle closure on large files
|
||||
DWORD WINAPI ISOCloseHandleThread(LPVOID param)
|
||||
{
|
||||
CloseHandle((HANDLE)param);
|
||||
ExitThread(0);
|
||||
}
|
||||
|
||||
#define SAFE_CLOSEHANDLE_THREADED(handle) \
|
||||
if (!threaded_close) { \
|
||||
safe_closehandle(handle); \
|
||||
} else { \
|
||||
thid = CreateThread(NULL, 0, ISOCloseHandleThread, (LPVOID)handle, 0, NULL); \
|
||||
while (WaitForSingleObject(thid, 1000) == WAIT_TIMEOUT) { \
|
||||
if (!FormatStatus) continue; \
|
||||
safe_closehandle(thid); \
|
||||
break; \
|
||||
} \
|
||||
handle = NULL; \
|
||||
threaded_close = FALSE; \
|
||||
}
|
||||
|
||||
// Returns 0 on success, nonzero on error
|
||||
static int udf_extract_files(udf_t *p_udf, udf_dirent_t *p_udf_dirent, const char *psz_path)
|
||||
{
|
||||
FILE *fd = NULL;
|
||||
HANDLE thid, file_handle = NULL;
|
||||
DWORD buf_size, wr_size;
|
||||
BOOL threaded_close = FALSE;
|
||||
int i_length;
|
||||
size_t i, nul_pos;
|
||||
char* psz_fullpath;
|
||||
const char* psz_basename;
|
||||
udf_dirent_t *p_udf_dirent2;
|
||||
|
@ -82,19 +123,24 @@ static int udf_extract_files(udf_t *p_udf, udf_dirent_t *p_udf_dirent, const cha
|
|||
while ((p_udf_dirent = udf_readdir(p_udf_dirent)) != NULL) {
|
||||
if (FormatStatus) goto out;
|
||||
psz_basename = udf_get_filename(p_udf_dirent);
|
||||
i_length = (int)(3 + strlen(psz_path) + strlen(psz_basename) + strlen(psz_extract_dir));
|
||||
i_length = (int)(3 + strlen(psz_path) + strlen(psz_basename) + strlen(psz_extract_dir) + 24);
|
||||
psz_fullpath = (char*)calloc(sizeof(char), i_length);
|
||||
if (psz_fullpath == NULL) {
|
||||
uprintf("Error allocating file name\n");
|
||||
goto out;
|
||||
}
|
||||
i_length = _snprintf(psz_fullpath, i_length, "%s%s/%s", psz_extract_dir, psz_path, psz_basename);
|
||||
i_length = safe_sprintf(psz_fullpath, i_length, "%s%s/%s", psz_extract_dir, psz_path, psz_basename);
|
||||
if (i_length < 0) {
|
||||
goto out;
|
||||
}
|
||||
if (udf_is_dir(p_udf_dirent)) {
|
||||
if (!scan_only)
|
||||
if (!scan_only) {
|
||||
_mkdir(psz_fullpath);
|
||||
} else {
|
||||
// Check for an "isolinux\" dir in root (psz_path = "")
|
||||
if ((*psz_path == 0) && (safe_strcmp(psz_basename, "isolinux") == 0))
|
||||
iso_report.has_isolinux = TRUE;
|
||||
}
|
||||
p_udf_dirent2 = udf_opendir(p_udf_dirent);
|
||||
if (p_udf_dirent2 != NULL) {
|
||||
if (udf_extract_files(p_udf, p_udf_dirent2, &psz_fullpath[strlen(psz_extract_dir)]))
|
||||
|
@ -103,6 +149,9 @@ static int udf_extract_files(udf_t *p_udf, udf_dirent_t *p_udf_dirent, const cha
|
|||
} else {
|
||||
i_file_length = udf_get_file_length(p_udf_dirent);
|
||||
if (scan_only) {
|
||||
// Check for a "bootmgr" file in root (psz_path = "")
|
||||
if ((*psz_path == 0) && (safe_strcmp(psz_basename, "bootmgr") == 0))
|
||||
iso_report.has_bootmgr = TRUE;
|
||||
if (i_file_length >= FOUR_GIGABYTES)
|
||||
iso_report.has_4GB_file = TRUE;
|
||||
total_blocks += i_file_length/UDF_BLOCKSIZE;
|
||||
|
@ -111,13 +160,25 @@ static int udf_extract_files(udf_t *p_udf, udf_dirent_t *p_udf_dirent, const cha
|
|||
safe_free(psz_fullpath);
|
||||
continue;
|
||||
}
|
||||
// Replace slashes with backslashes and append the size to the path for UI display
|
||||
nul_pos = safe_strlen(psz_fullpath);
|
||||
for (i=0; i<nul_pos; i++) {
|
||||
if (psz_fullpath[i] == '/')
|
||||
psz_fullpath[i] = '\\';
|
||||
}
|
||||
safe_strcpy(&psz_fullpath[nul_pos], 24, size_to_hr(i_file_length));
|
||||
uprintf("Extracting: %s\n", psz_fullpath);
|
||||
SetWindowTextU(hISOFileName, psz_fullpath);
|
||||
fd = fopen(psz_fullpath, "wb");
|
||||
if (fd == NULL) {
|
||||
uprintf(" Unable to create file\n");
|
||||
// Remove the appended size for extraction
|
||||
psz_fullpath[nul_pos] = 0;
|
||||
file_handle = CreateFileU(psz_fullpath, GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (file_handle == INVALID_HANDLE_VALUE) {
|
||||
uprintf(" Unable to create file: %s\n", WindowsErrorString());
|
||||
goto out;
|
||||
}
|
||||
threaded_close = (i_file_length > THREADED_CLOSE_THRESHOLD);
|
||||
if (threaded_close) uprintf("will use threaded close\n");
|
||||
while (i_file_length > 0) {
|
||||
if (FormatStatus) goto out;
|
||||
memset(buf, 0, UDF_BLOCKSIZE);
|
||||
|
@ -126,17 +187,23 @@ static int udf_extract_files(udf_t *p_udf, udf_dirent_t *p_udf_dirent, const cha
|
|||
uprintf(" Error reading UDF file %s\n", &psz_fullpath[strlen(psz_extract_dir)]);
|
||||
goto out;
|
||||
}
|
||||
fwrite(buf, (size_t)MIN(i_file_length, i_read), 1, fd);
|
||||
if (ferror(fd)) {
|
||||
uprintf(" Error writing file\n");
|
||||
buf_size = (DWORD)MIN(i_file_length, i_read);
|
||||
if (!WriteFile(file_handle, buf, buf_size, &wr_size, NULL) || (buf_size != wr_size)) {
|
||||
uprintf(" Error writing file: %s\n", WindowsErrorString());
|
||||
goto out;
|
||||
}
|
||||
i_file_length -= i_read;
|
||||
if (nb_blocks++ % PROGRESS_UPDATE == 0)
|
||||
if (nb_blocks++ % PROGRESS_THRESHOLD == 0) {
|
||||
SendMessage(hISOProgressBar, PBM_SETPOS, (WPARAM)((MAX_PROGRESS*nb_blocks)/total_blocks), 0);
|
||||
}
|
||||
}
|
||||
fclose(fd);
|
||||
fd = NULL;
|
||||
// If you have a fast USB 3.0 device, the default Windows buffering does an
|
||||
// excellent job at compensating for our small blocks read/writes to max out the
|
||||
// device's bandwidth.
|
||||
// The drawback however is with cancellation. With a large file, CloseHandle()
|
||||
// may take forever to complete on a large file and is not an interruptible
|
||||
// operation. To compensate for this, we create a thread when needed.
|
||||
SAFE_CLOSEHANDLE_THREADED(file_handle);
|
||||
}
|
||||
safe_free(psz_fullpath);
|
||||
}
|
||||
|
@ -145,30 +212,32 @@ static int udf_extract_files(udf_t *p_udf, udf_dirent_t *p_udf_dirent, const cha
|
|||
out:
|
||||
if (p_udf_dirent != NULL)
|
||||
udf_dirent_free(p_udf_dirent);
|
||||
if (fd != NULL)
|
||||
fclose(fd);
|
||||
SAFE_CLOSEHANDLE_THREADED(file_handle);
|
||||
safe_free(psz_fullpath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Returns 0 on success, nonzero on error
|
||||
static int iso_extract_files(iso9660_t* p_iso, const char *psz_path)
|
||||
{
|
||||
FILE *fd = NULL;
|
||||
HANDLE thid, file_handle = NULL;
|
||||
DWORD buf_size, wr_size;
|
||||
BOOL threaded_close = FALSE;
|
||||
int i_length, r = 1;
|
||||
char psz_fullpath[4096], *psz_basename;
|
||||
char psz_fullpath[1024], *psz_basename;
|
||||
const char *psz_iso_name = &psz_fullpath[strlen(psz_extract_dir)];
|
||||
unsigned char buf[ISO_BLOCKSIZE];
|
||||
CdioListNode_t* p_entnode;
|
||||
iso9660_stat_t *p_statbuf;
|
||||
CdioList_t* p_entlist;
|
||||
size_t i;
|
||||
size_t i, nul_pos;
|
||||
lsn_t lsn;
|
||||
int64_t i_file_length;
|
||||
|
||||
if ((p_iso == NULL) || (psz_path == NULL))
|
||||
return 1;
|
||||
|
||||
i_length = _snprintf(psz_fullpath, sizeof(psz_fullpath), "%s%s/", psz_extract_dir, psz_path);
|
||||
i_length = safe_sprintf(psz_fullpath, sizeof(psz_fullpath), "%s%s/", psz_extract_dir, psz_path);
|
||||
if (i_length < 0)
|
||||
return 1;
|
||||
psz_basename = &psz_fullpath[i_length];
|
||||
|
@ -186,13 +255,21 @@ static int iso_extract_files(iso9660_t* p_iso, const char *psz_path)
|
|||
continue;
|
||||
iso9660_name_translate(p_statbuf->filename, psz_basename);
|
||||
if (p_statbuf->type == _STAT_DIR) {
|
||||
if (!scan_only)
|
||||
if (!scan_only) {
|
||||
_mkdir(psz_fullpath);
|
||||
} else {
|
||||
// Check for an "isolinux\" dir in root (psz_path = "")
|
||||
if ((*psz_path == 0) && (safe_strcmp(psz_basename, "isolinux") == 0))
|
||||
iso_report.has_isolinux = TRUE;
|
||||
}
|
||||
if (iso_extract_files(p_iso, psz_iso_name))
|
||||
goto out;
|
||||
} else {
|
||||
i_file_length = p_statbuf->size;
|
||||
if (scan_only) {
|
||||
// Check for a "bootmgr" file in root (psz_path = "")
|
||||
if ((*psz_path == 0) && (safe_strcmp(psz_basename, "bootmgr") == 0))
|
||||
iso_report.has_bootmgr = TRUE;
|
||||
if (i_file_length >= FOUR_GIGABYTES)
|
||||
iso_report.has_4GB_file = TRUE;
|
||||
total_blocks += i_file_length/ISO_BLOCKSIZE;
|
||||
|
@ -200,12 +277,23 @@ static int iso_extract_files(iso9660_t* p_iso, const char *psz_path)
|
|||
total_blocks++;
|
||||
continue;
|
||||
}
|
||||
// Replace slashes with backslashes and append the size to the path for UI display
|
||||
nul_pos = safe_strlen(psz_fullpath);
|
||||
for (i=0; i<nul_pos; i++) {
|
||||
if (psz_fullpath[i] == '/')
|
||||
psz_fullpath[i] = '\\';
|
||||
}
|
||||
safe_strcpy(&psz_fullpath[nul_pos], 24, size_to_hr(i_file_length));
|
||||
uprintf("Extracting: %s\n", psz_fullpath);
|
||||
fd = fopen(psz_fullpath, "wb");
|
||||
if (fd == NULL) {
|
||||
uprintf(" Unable to create file\n");
|
||||
SetWindowTextU(hISOFileName, psz_fullpath);
|
||||
psz_fullpath[nul_pos] = 0;
|
||||
file_handle = CreateFileU(psz_fullpath, GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (file_handle == INVALID_HANDLE_VALUE) {
|
||||
uprintf(" Unable to create file: %s\n", WindowsErrorString());
|
||||
goto out;
|
||||
}
|
||||
threaded_close = (i_file_length > THREADED_CLOSE_THRESHOLD);
|
||||
for (i = 0; i_file_length > 0; i++) {
|
||||
if (FormatStatus) goto out;
|
||||
memset(buf, 0, ISO_BLOCKSIZE);
|
||||
|
@ -215,24 +303,23 @@ static int iso_extract_files(iso9660_t* p_iso, const char *psz_path)
|
|||
psz_iso_name, (long unsigned int)lsn);
|
||||
goto out;
|
||||
}
|
||||
fwrite(buf, (size_t)MIN(i_file_length, ISO_BLOCKSIZE), 1, fd);
|
||||
if (ferror(fd)) {
|
||||
uprintf(" Error writing file\n");
|
||||
buf_size = (DWORD)MIN(i_file_length, ISO_BLOCKSIZE);
|
||||
if (!WriteFile(file_handle, buf, buf_size, &wr_size, NULL) || (buf_size != wr_size)) {
|
||||
uprintf(" Error writing file: %s\n", WindowsErrorString());
|
||||
goto out;
|
||||
}
|
||||
i_file_length -= ISO_BLOCKSIZE;
|
||||
if (nb_blocks++ % PROGRESS_UPDATE == 0)
|
||||
if (nb_blocks++ % PROGRESS_THRESHOLD == 0) {
|
||||
SendMessage(hISOProgressBar, PBM_SETPOS, (WPARAM)((MAX_PROGRESS*nb_blocks)/total_blocks), 0);
|
||||
}
|
||||
}
|
||||
fclose(fd);
|
||||
fd = NULL;
|
||||
SAFE_CLOSEHANDLE_THREADED(file_handle);
|
||||
}
|
||||
}
|
||||
r = 0;
|
||||
|
||||
out:
|
||||
if (fd != NULL)
|
||||
fclose(fd);
|
||||
SAFE_CLOSEHANDLE_THREADED(file_handle);
|
||||
_cdio_list_free(p_entlist, true);
|
||||
return r;
|
||||
}
|
||||
|
@ -254,10 +341,11 @@ BOOL ExtractISO(const char* src_iso, const char* dest_dir, bool scan)
|
|||
psz_extract_dir = dest_dir;
|
||||
progress_style = GetWindowLong(hISOProgressBar, GWL_STYLE);
|
||||
if (scan_only) {
|
||||
uprintf(scan_text);
|
||||
total_blocks = 0;
|
||||
iso_report.projected_size = 0;
|
||||
iso_report.has_4GB_file = FALSE;
|
||||
iso_report.has_bootmgr = FALSE;
|
||||
iso_report.has_isolinux = FALSE;
|
||||
// Change the Window title and static text
|
||||
SetWindowTextU(hISOProgressDlg, scan_text);
|
||||
SetWindowTextU(hISOFileName, scan_text);
|
||||
|
@ -282,6 +370,7 @@ BOOL ExtractISO(const char* src_iso, const char* dest_dir, bool scan)
|
|||
p_udf = udf_open(src_iso);
|
||||
if (p_udf == NULL)
|
||||
goto try_iso;
|
||||
uprintf("Disc image is an UDF image\n");
|
||||
|
||||
p_udf_root = udf_get_root(p_udf, true, 0);
|
||||
if (p_udf_root == NULL) {
|
||||
|
@ -297,6 +386,7 @@ try_iso:
|
|||
uprintf("Unable to open image '%s'.\n", src_iso);
|
||||
goto out;
|
||||
}
|
||||
uprintf("Disc image is an ISO9660 image\n");
|
||||
r = iso_extract_files(p_iso, "");
|
||||
|
||||
out:
|
||||
|
@ -310,6 +400,6 @@ out:
|
|||
if (p_udf != NULL)
|
||||
udf_close(p_udf);
|
||||
if ((r != 0) && (FormatStatus == 0))
|
||||
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(scan_only?ERROR_ISO_SCAN:ERROR_ISO_EXTRACT);
|
||||
return r;
|
||||
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR((scan_only?ERROR_ISO_SCAN:ERROR_ISO_EXTRACT));
|
||||
return (r == 0);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue