2012-01-31 01:40:22 +00:00
|
|
|
/*
|
|
|
|
* Rufus: The Reliable USB Formatting Utility
|
|
|
|
* ISO file extraction
|
[net] add Windows retail ISO downloads
* This is accomplished through Fido (https://github.com/pbatard/Fido), a *SIGNED*
PowerShell script, that is downloaded from GitHub and that resides in memory for
the duration of a session.
* The reason we use a downloaded PS script, rather than an embedded on, is because:
- Microsoft have regularly been changing the deal with regards to how retail ISOs
can be downloaded, and not for the better, so we can't simply embed a static
means of downloading ISOs and expect that to work forever.
- By using an external script, we can immediately respond to whatever new means of
*ANNOYING* their legitimate users Microsoft will come up with next, as well as
make sure that, the minute a new retail version of Windows becomes available, it
also becomes available for download in Rufus.
* Note that if you are concerned about downloading a remote PS script that is being
run at the same level as an elevated application, you should understand that:
- Only scripts downloaded from GitHub, from an account that is protected with 2FA,
are allowed to run (i.e. someone would first have to steal a *physical* 2FA key
to be in a position to upload a malicious script).
- On top of this, only scripts that are signed with a separate private key (RSA +
AES-256), that is itself also protected with a strong unique password which only
a single person knows (and must manually enter each time they want to make a new
version of the script available for download), are allowed to run.
The above means that there's about as much chance for someone to manage to upload
a malicious script on the GitHub servers, that Rufus would allow to run, as there
is for someone to upload a malicious version of Rufus itself.
Still, if you are paranoid and have concerns that, even as you can validate from
its source that Rufus does not attempt to execute any remote script unless a user
actively selected and clicked the DOWNLOAD button, you can also completely disable
the remote script download feature, if you just set the update check to disabled
(which, by the way, Rufus *EXPLICITLY* asks you to choose whether you want to
enable or not, the very first time you run the application).
* Also remove _unlinkU() which duplicates what DeleteFileU() already does.
2019-03-02 23:28:56 +00:00
|
|
|
* Copyright © 2011-2019 Pete Batard <pete@akeo.ie>
|
2012-01-31 01:40:22 +00:00
|
|
|
* Based on libcdio's iso & udf samples:
|
2015-01-01 23:39:28 +00:00
|
|
|
* Copyright © 2003-2014 Rocky Bernstein <rocky@gnu.org>
|
2012-01-31 01:40:22 +00:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Memory leaks detection - define _CRTDBG_MAP_ALLOC as preprocessor macro */
|
|
|
|
#ifdef _CRTDBG_MAP_ALLOC
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <crtdbg.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <windows.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <malloc.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <direct.h>
|
2012-03-27 22:31:58 +00:00
|
|
|
#include <ctype.h>
|
2017-11-13 14:29:48 +00:00
|
|
|
#include <virtdisk.h>
|
2012-01-31 01:40:22 +00:00
|
|
|
|
|
|
|
#include <cdio/cdio.h>
|
|
|
|
#include <cdio/logging.h>
|
|
|
|
#include <cdio/iso9660.h>
|
|
|
|
#include <cdio/udf.h>
|
|
|
|
|
|
|
|
#include "rufus.h"
|
2017-08-09 15:27:11 +00:00
|
|
|
#include "libfat.h"
|
2016-02-20 22:52:32 +00:00
|
|
|
#include "missing.h"
|
2012-02-01 14:26:36 +00:00
|
|
|
#include "resource.h"
|
2016-02-20 22:52:32 +00:00
|
|
|
#include "msapi_utf8.h"
|
2013-10-15 21:58:27 +00:00
|
|
|
#include "localization.h"
|
2012-01-31 01:40:22 +00:00
|
|
|
|
2012-02-01 14:26:36 +00:00
|
|
|
// 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
|
2012-03-29 19:27:53 +00:00
|
|
|
#define PROGRESS_THRESHOLD 128
|
2012-02-07 02:05:58 +00:00
|
|
|
#define FOUR_GIGABYTES 4294967296LL
|
2012-01-31 01:40:22 +00:00
|
|
|
|
2012-02-01 14:26:36 +00:00
|
|
|
// Needed for UDF ISO access
|
2012-03-16 18:30:44 +00:00
|
|
|
CdIo_t* cdio_open (const char* psz_source, driver_id_t driver_id) {return NULL;}
|
|
|
|
void cdio_destroy (CdIo_t* p_cdio) {}
|
2012-01-31 01:40:22 +00:00
|
|
|
|
2015-08-27 17:22:27 +00:00
|
|
|
uint32_t GetInstallWimVersion(const char* iso);
|
|
|
|
|
2014-12-16 23:24:06 +00:00
|
|
|
typedef struct {
|
2017-11-01 12:30:29 +00:00
|
|
|
BOOLEAN is_cfg;
|
2019-04-04 18:12:48 +00:00
|
|
|
BOOLEAN is_conf;
|
2015-05-24 22:06:36 +00:00
|
|
|
BOOLEAN is_syslinux_cfg;
|
|
|
|
BOOLEAN is_grub_cfg;
|
2019-08-06 17:50:07 +00:00
|
|
|
BOOLEAN is_menu_cfg;
|
2015-05-24 22:06:36 +00:00
|
|
|
BOOLEAN is_old_c32[NB_OLD_C32];
|
2014-12-16 23:24:06 +00:00
|
|
|
} EXTRACT_PROPS;
|
|
|
|
|
2015-09-02 22:20:00 +00:00
|
|
|
RUFUS_IMG_REPORT img_report;
|
2012-02-07 16:17:14 +00:00
|
|
|
int64_t iso_blocking_status = -1;
|
2015-09-16 23:20:22 +00:00
|
|
|
extern BOOL preserve_timestamps;
|
|
|
|
BOOL enable_iso = TRUE, enable_joliet = TRUE, enable_rockridge = TRUE, has_ldlinux_c32;
|
2012-02-07 16:17:14 +00:00
|
|
|
#define ISO_BLOCKING(x) do {x; iso_blocking_status++; } while(0)
|
2012-03-16 18:30:44 +00:00
|
|
|
static const char* psz_extract_dir;
|
2018-09-14 13:10:40 +00:00
|
|
|
static const char* bootmgr_name = "bootmgr";
|
2013-01-19 04:04:54 +00:00
|
|
|
static const char* bootmgr_efi_name = "bootmgr.efi";
|
2014-11-14 23:40:00 +00:00
|
|
|
static const char* grldr_name = "grldr";
|
2012-03-16 18:30:44 +00:00
|
|
|
static const char* ldlinux_name = "ldlinux.sys";
|
2014-01-21 17:08:41 +00:00
|
|
|
static const char* ldlinux_c32 = "ldlinux.c32";
|
2019-08-02 16:57:10 +00:00
|
|
|
static const char* casper_dirname = "/casper";
|
2013-01-18 01:39:24 +00:00
|
|
|
static const char* efi_dirname = "/efi/boot";
|
2016-07-04 12:16:04 +00:00
|
|
|
static const char* efi_bootname[] = { "bootia32.efi", "bootia64.efi", "bootx64.efi", "bootarm.efi", "bootaa64.efi", "bootebc.efi" };
|
2019-01-08 18:30:07 +00:00
|
|
|
static const char* sources_str = "/sources";
|
|
|
|
static const char* wininst_name[] = { "install.wim", "install.esd", "install.swm" };
|
2016-06-17 14:14:54 +00:00
|
|
|
// We only support GRUB/BIOS (x86) that uses a standard config dir (/boot/grub/i386-pc/)
|
|
|
|
// If the disc was mastered properly, GRUB/EFI will take care of itself
|
|
|
|
static const char* grub_dirname = "/boot/grub/i386-pc";
|
2014-12-16 23:24:06 +00:00
|
|
|
static const char* grub_cfg = "grub.cfg";
|
2019-08-06 17:50:07 +00:00
|
|
|
static const char* menu_cfg = "menu.cfg";
|
2019-08-15 20:14:30 +00:00
|
|
|
static const char* syslinux_cfg[] = { "isolinux.cfg", "syslinux.cfg", "txt.cfg", "extlinux.conf" };
|
2016-08-07 16:41:47 +00:00
|
|
|
static const char* isolinux_bin[] = { "isolinux.bin", "boot.bin" };
|
2018-06-22 19:59:03 +00:00
|
|
|
static const char* pe_dirname[] = { "/i386", "/amd64", "/minint" };
|
2012-03-16 18:30:44 +00:00
|
|
|
static const char* pe_file[] = { "ntdetect.com", "setupldr.bin", "txtsetup.sif" };
|
2014-01-02 23:21:30 +00:00
|
|
|
static const char* reactos_name = "setupldr.sys"; // TODO: freeldr.sys doesn't seem to work
|
2014-05-15 20:17:12 +00:00
|
|
|
static const char* kolibri_name = "kolibri.img";
|
2013-11-17 22:24:50 +00:00
|
|
|
static const char* autorun_name = "autorun.inf";
|
2019-07-03 22:07:24 +00:00
|
|
|
static const char* manjaro_marker = ".miso";
|
2019-08-15 20:14:30 +00:00
|
|
|
static const char* pop_os_name = "pop-os";
|
2014-02-09 23:38:16 +00:00
|
|
|
static const char* stupid_antivirus = " NOTE: This is usually caused by a poorly designed security solution. "
|
2018-10-16 20:51:40 +00:00
|
|
|
"See https://goo.gl/QTobxX.\r\n This file will be skipped for now, but you should really "
|
2014-02-09 23:38:16 +00:00
|
|
|
"look into using a *SMARTER* antivirus solution.";
|
2014-02-04 20:01:28 +00:00
|
|
|
const char* old_c32_name[NB_OLD_C32] = OLD_C32_NAMES;
|
2012-12-15 03:27:14 +00:00
|
|
|
static const int64_t old_c32_threshold[NB_OLD_C32] = OLD_C32_THRESHOLD;
|
2018-06-04 11:20:14 +00:00
|
|
|
static uint8_t joliet_level = 0;
|
2012-02-01 14:26:36 +00:00
|
|
|
static uint64_t total_blocks, nb_blocks;
|
|
|
|
static BOOL scan_only = FALSE;
|
2014-01-21 17:08:41 +00:00
|
|
|
static StrArray config_path, isolinux_path;
|
2012-01-31 01:40:22 +00:00
|
|
|
|
2013-10-26 23:05:54 +00:00
|
|
|
// Ensure filenames do not contain invalid FAT32 or NTFS characters
|
2014-11-20 23:00:59 +00:00
|
|
|
static __inline char* sanitize_filename(char* filename, BOOL* is_identical)
|
2013-10-26 23:05:54 +00:00
|
|
|
{
|
|
|
|
size_t i, j;
|
2014-11-20 23:00:59 +00:00
|
|
|
char* ret = NULL;
|
2017-04-02 13:51:37 +00:00
|
|
|
char unauthorized[] = { '*', '?', '<', '>', ':', '|' };
|
2013-10-26 23:05:54 +00:00
|
|
|
|
2014-11-20 23:00:59 +00:00
|
|
|
*is_identical = TRUE;
|
|
|
|
ret = safe_strdup(filename);
|
|
|
|
if (ret == NULL) {
|
2015-01-01 23:39:28 +00:00
|
|
|
uprintf("Could not allocate string for sanitized path");
|
2014-11-20 23:00:59 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-10-26 23:05:54 +00:00
|
|
|
// Must start after the drive part (D:\...) so that we don't eliminate the first column
|
2014-11-20 23:00:59 +00:00
|
|
|
for (i=2; i<safe_strlen(ret); i++) {
|
2013-10-26 23:05:54 +00:00
|
|
|
for (j=0; j<sizeof(unauthorized); j++) {
|
2014-11-20 23:00:59 +00:00
|
|
|
if (ret[i] == unauthorized[j]) {
|
|
|
|
ret[i] = '_';
|
|
|
|
*is_identical = FALSE;
|
2013-10-26 23:05:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-02-14 20:52:39 +00:00
|
|
|
static void log_handler (cdio_log_level_t level, const char *message)
|
2012-02-07 02:05:58 +00:00
|
|
|
{
|
2012-02-14 20:52:39 +00:00
|
|
|
switch(level) {
|
|
|
|
case CDIO_LOG_DEBUG:
|
2015-01-25 00:56:38 +00:00
|
|
|
// TODO: use a setting to enable libcdio debug?
|
2012-02-14 20:52:39 +00:00
|
|
|
return;
|
|
|
|
default:
|
|
|
|
uprintf("libcdio: %s\n", message);
|
|
|
|
}
|
2012-02-07 02:05:58 +00:00
|
|
|
}
|
|
|
|
|
2012-02-21 19:46:28 +00:00
|
|
|
/*
|
|
|
|
* Scan and set ISO properties
|
|
|
|
* Returns true if the the current file does not need to be processed further
|
|
|
|
*/
|
2018-06-04 11:20:14 +00:00
|
|
|
static BOOL check_iso_props(const char* psz_dirname, int64_t file_length, const char* psz_basename,
|
2014-12-16 23:24:06 +00:00
|
|
|
const char* psz_fullpath, EXTRACT_PROPS *props)
|
2012-02-21 19:46:28 +00:00
|
|
|
{
|
2017-11-01 12:30:29 +00:00
|
|
|
size_t i, j, len;
|
2012-02-21 19:46:28 +00:00
|
|
|
// Check for an isolinux/syslinux config file anywhere
|
2014-12-16 23:24:06 +00:00
|
|
|
memset(props, 0, sizeof(EXTRACT_PROPS));
|
2014-01-21 17:08:41 +00:00
|
|
|
for (i=0; i<ARRAYSIZE(syslinux_cfg); i++) {
|
2014-10-29 02:00:46 +00:00
|
|
|
if (safe_stricmp(psz_basename, syslinux_cfg[i]) == 0) {
|
2017-11-01 12:30:29 +00:00
|
|
|
props->is_cfg = TRUE; // Required for "extlinux.conf"
|
2014-12-16 23:24:06 +00:00
|
|
|
props->is_syslinux_cfg = TRUE;
|
2014-10-29 02:00:46 +00:00
|
|
|
if ((scan_only) && (i == 1) && (safe_stricmp(psz_dirname, efi_dirname) == 0))
|
2015-09-02 22:20:00 +00:00
|
|
|
img_report.has_efi_syslinux = TRUE;
|
2014-10-29 02:00:46 +00:00
|
|
|
}
|
2012-02-21 19:46:28 +00:00
|
|
|
}
|
|
|
|
|
2012-12-15 03:27:14 +00:00
|
|
|
// Check for an old incompatible c32 file anywhere
|
|
|
|
for (i=0; i<NB_OLD_C32; i++) {
|
2018-06-04 11:20:14 +00:00
|
|
|
if ((safe_stricmp(psz_basename, old_c32_name[i]) == 0) && (file_length <= old_c32_threshold[i]))
|
2014-12-16 23:24:06 +00:00
|
|
|
props->is_old_c32[i] = TRUE;
|
2012-03-11 01:55:25 +00:00
|
|
|
}
|
|
|
|
|
2019-08-06 17:50:07 +00:00
|
|
|
if (!scan_only) { // Write-time checks
|
|
|
|
// Check for config files that may need patching
|
2017-11-01 12:30:29 +00:00
|
|
|
len = safe_strlen(psz_basename);
|
2019-08-06 17:50:07 +00:00
|
|
|
if ((len >= 4) && safe_stricmp(&psz_basename[len - 4], ".cfg") == 0) {
|
2017-11-01 12:30:29 +00:00
|
|
|
props->is_cfg = TRUE;
|
2019-08-06 17:50:07 +00:00
|
|
|
if (safe_stricmp(psz_basename, grub_cfg) == 0) {
|
|
|
|
props->is_grub_cfg = TRUE;
|
|
|
|
} else if (safe_stricmp(psz_basename, menu_cfg) == 0) {
|
|
|
|
props->is_menu_cfg = TRUE;
|
|
|
|
}
|
|
|
|
}
|
2016-08-14 13:45:29 +00:00
|
|
|
|
2019-08-06 17:50:07 +00:00
|
|
|
// In case there's an ldlinux.sys on the ISO, prevent it from overwriting ours
|
|
|
|
if ((psz_dirname != NULL) && (psz_dirname[0] == 0) && (safe_strcmp(psz_basename, ldlinux_name) == 0)) {
|
|
|
|
uprintf("skipping % file from ISO image\n", ldlinux_name);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
} else { // Scan-time checks
|
|
|
|
// Check for GRUB artifacts
|
2016-06-17 14:14:54 +00:00
|
|
|
if (safe_stricmp(psz_dirname, grub_dirname) == 0)
|
2015-09-02 22:20:00 +00:00
|
|
|
img_report.has_grub2 = TRUE;
|
2014-12-16 23:24:06 +00:00
|
|
|
|
|
|
|
// Check for a syslinux v5.0+ file anywhere
|
|
|
|
if (safe_stricmp(psz_basename, ldlinux_c32) == 0) {
|
|
|
|
has_ldlinux_c32 = TRUE;
|
|
|
|
}
|
|
|
|
|
2019-08-15 20:14:30 +00:00
|
|
|
// Check for a '/casper#####' directory (non-empty)
|
|
|
|
if (safe_strnicmp(psz_dirname, casper_dirname, strlen(casper_dirname)) == 0) {
|
2019-08-02 16:57:10 +00:00
|
|
|
img_report.uses_casper = TRUE;
|
2019-08-15 20:14:30 +00:00
|
|
|
if (safe_strstr(psz_dirname, pop_os_name) != NULL)
|
|
|
|
img_report.disable_iso = TRUE;
|
2019-08-02 16:57:10 +00:00
|
|
|
}
|
|
|
|
|
2014-05-15 20:17:12 +00:00
|
|
|
// Check for various files in root (psz_dirname = "")
|
2016-08-20 08:12:53 +00:00
|
|
|
if ((psz_dirname != NULL) && (psz_dirname[0] == 0)) {
|
2018-09-14 13:10:40 +00:00
|
|
|
if (safe_stricmp(psz_basename, bootmgr_name) == 0) {
|
2015-09-02 22:20:00 +00:00
|
|
|
img_report.has_bootmgr = TRUE;
|
2013-05-19 22:02:16 +00:00
|
|
|
}
|
2018-09-14 13:10:40 +00:00
|
|
|
if (safe_stricmp(psz_basename, bootmgr_efi_name) == 0) {
|
|
|
|
img_report.has_bootmgr_efi = TRUE;
|
|
|
|
}
|
2014-11-14 23:40:00 +00:00
|
|
|
if (safe_stricmp(psz_basename, grldr_name) == 0) {
|
2015-09-02 22:20:00 +00:00
|
|
|
img_report.has_grub4dos = TRUE;
|
2013-01-19 04:04:54 +00:00
|
|
|
}
|
2014-05-15 20:17:12 +00:00
|
|
|
if (safe_stricmp(psz_basename, kolibri_name) == 0) {
|
2015-09-02 22:20:00 +00:00
|
|
|
img_report.has_kolibrios = TRUE;
|
2014-05-15 20:17:12 +00:00
|
|
|
}
|
2014-11-14 23:40:00 +00:00
|
|
|
if (safe_stricmp(psz_basename, bootmgr_efi_name) == 0) {
|
2015-09-02 22:20:00 +00:00
|
|
|
img_report.has_efi |= 1;
|
2014-11-14 23:40:00 +00:00
|
|
|
}
|
2019-07-03 22:07:24 +00:00
|
|
|
if (safe_stricmp(psz_basename, manjaro_marker) == 0) {
|
|
|
|
img_report.disable_iso = TRUE;
|
|
|
|
}
|
2013-01-19 04:04:54 +00:00
|
|
|
}
|
2012-03-16 18:30:44 +00:00
|
|
|
|
2014-01-02 23:21:30 +00:00
|
|
|
// Check for ReactOS' setupldr.sys anywhere
|
2015-09-02 22:20:00 +00:00
|
|
|
if ((img_report.reactos_path[0] == 0) && (safe_stricmp(psz_basename, reactos_name) == 0))
|
2017-08-10 18:43:04 +00:00
|
|
|
static_strcpy(img_report.reactos_path, psz_fullpath);
|
2014-01-02 23:21:30 +00:00
|
|
|
|
2018-03-29 21:29:44 +00:00
|
|
|
// Check for the first 'efi*.img' we can find (that hopefully contains EFI boot files)
|
|
|
|
if (!HAS_EFI_IMG(img_report) && (safe_strlen(psz_basename) >= 7) &&
|
|
|
|
(safe_strnicmp(psz_basename, "efi", 3) == 0) &&
|
|
|
|
(safe_stricmp(&psz_basename[strlen(psz_basename) - 4], ".img") == 0))
|
2017-08-10 18:43:04 +00:00
|
|
|
static_strcpy(img_report.efi_img_path, psz_fullpath);
|
2017-08-09 15:27:11 +00:00
|
|
|
|
2015-01-13 02:11:57 +00:00
|
|
|
// Check for the EFI boot entries
|
|
|
|
if (safe_stricmp(psz_dirname, efi_dirname) == 0) {
|
|
|
|
for (i=0; i<ARRAYSIZE(efi_bootname); i++)
|
|
|
|
if (safe_stricmp(psz_basename, efi_bootname[i]) == 0)
|
2015-09-02 22:20:00 +00:00
|
|
|
img_report.has_efi |= (2<<i); // start at 2 since "bootmgr.efi" is bit 0
|
2015-01-13 02:11:57 +00:00
|
|
|
}
|
|
|
|
|
2019-01-08 18:30:07 +00:00
|
|
|
// Check for "install.###" in "###/sources/"
|
2019-03-15 10:57:17 +00:00
|
|
|
if (psz_dirname != NULL) {
|
|
|
|
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); i++) {
|
|
|
|
if (safe_stricmp(psz_basename, wininst_name[i]) == 0) {
|
|
|
|
if (img_report.wininst_index < MAX_WININST) {
|
|
|
|
static_sprintf(img_report.wininst_path[img_report.wininst_index], "?:%s", psz_fullpath);
|
|
|
|
img_report.wininst_index++;
|
|
|
|
}
|
2019-01-08 18:30:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-02-09 19:34:41 +00:00
|
|
|
}
|
2013-01-18 01:39:24 +00:00
|
|
|
|
2018-06-22 19:59:03 +00:00
|
|
|
// Check for PE (XP) specific files in "/i386", "/amd64" or "/minint"
|
2012-03-16 18:30:44 +00:00
|
|
|
for (i=0; i<ARRAYSIZE(pe_dirname); i++)
|
|
|
|
if (safe_stricmp(psz_dirname, pe_dirname[i]) == 0)
|
|
|
|
for (j=0; j<ARRAYSIZE(pe_file); j++)
|
|
|
|
if (safe_stricmp(psz_basename, pe_file[j]) == 0)
|
2018-06-22 19:59:03 +00:00
|
|
|
img_report.winpe |= (1<<j)<<(ARRAYSIZE(pe_dirname)*i);
|
2012-03-16 18:30:44 +00:00
|
|
|
|
2014-12-16 23:24:06 +00:00
|
|
|
if (props->is_syslinux_cfg) {
|
2012-02-21 19:46:28 +00:00
|
|
|
// Maintain a list of all the isolinux/syslinux configs identified so far
|
2016-12-13 14:21:51 +00:00
|
|
|
StrArrayAdd(&config_path, psz_fullpath, TRUE);
|
2012-02-21 19:46:28 +00:00
|
|
|
}
|
2016-08-07 16:41:47 +00:00
|
|
|
for (i=0; i<ARRAYSIZE(isolinux_bin); i++) {
|
|
|
|
if (safe_stricmp(psz_basename, isolinux_bin[i]) == 0) {
|
|
|
|
// Maintain a list of all the isolinux.bin files found
|
2016-12-13 14:21:51 +00:00
|
|
|
StrArrayAdd(&isolinux_path, psz_fullpath, TRUE);
|
2016-08-07 16:41:47 +00:00
|
|
|
}
|
2014-01-21 17:08:41 +00:00
|
|
|
}
|
|
|
|
|
2012-12-15 03:27:14 +00:00
|
|
|
for (i=0; i<NB_OLD_C32; i++) {
|
2014-12-16 23:24:06 +00:00
|
|
|
if (props->is_old_c32[i])
|
2015-09-02 22:20:00 +00:00
|
|
|
img_report.has_old_c32[i] = TRUE;
|
2012-12-15 03:27:14 +00:00
|
|
|
}
|
2018-06-04 11:20:14 +00:00
|
|
|
if (file_length >= FOUR_GIGABYTES)
|
2015-09-02 22:20:00 +00:00
|
|
|
img_report.has_4GB_file = TRUE;
|
2012-02-21 19:46:28 +00:00
|
|
|
// Compute projected size needed
|
2018-06-04 11:20:14 +00:00
|
|
|
total_blocks += file_length / ISO_BLOCKSIZE;
|
2012-02-21 19:46:28 +00:00
|
|
|
// NB: ISO_BLOCKSIZE = UDF_BLOCKSIZE
|
2018-06-04 11:20:14 +00:00
|
|
|
if ((file_length != 0) && (file_length % ISO_BLOCKSIZE != 0))
|
2012-02-21 19:46:28 +00:00
|
|
|
total_blocks++;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2015-08-13 22:31:12 +00:00
|
|
|
// Apply various workarounds to Linux config files
|
2014-12-16 23:24:06 +00:00
|
|
|
static void fix_config(const char* psz_fullpath, const char* psz_path, const char* psz_basename, EXTRACT_PROPS* props)
|
2014-11-07 23:57:17 +00:00
|
|
|
{
|
|
|
|
size_t i, nul_pos;
|
2014-12-16 23:24:06 +00:00
|
|
|
char *iso_label = NULL, *usb_label = NULL, *src, *dst;
|
2014-11-07 23:57:17 +00:00
|
|
|
|
|
|
|
nul_pos = safe_strlen(psz_fullpath);
|
|
|
|
src = safe_strdup(psz_fullpath);
|
|
|
|
if (src == NULL)
|
|
|
|
return;
|
|
|
|
for (i=0; i<nul_pos; i++)
|
|
|
|
if (src[i] == '/') src[i] = '\\';
|
|
|
|
|
2019-08-02 16:57:10 +00:00
|
|
|
// Add persistence to the kernel options
|
|
|
|
if ((boot_type == BT_IMAGE) && HAS_PERSISTENCE(img_report) && persistence_size) {
|
2019-08-15 20:14:30 +00:00
|
|
|
if ((props->is_grub_cfg) || (props->is_menu_cfg) || (props->is_syslinux_cfg)) {
|
2019-08-02 16:57:10 +00:00
|
|
|
// Ubuntu & derivatives are assumed to use '/casper/vmlinuz'
|
|
|
|
// in their kernel options and use 'persistent' as keyword.
|
2019-08-06 17:50:07 +00:00
|
|
|
if (replace_in_token_data(src, props->is_grub_cfg ? "linux" : "append",
|
2019-08-15 20:14:30 +00:00
|
|
|
props->is_grub_cfg ? "/casper/vmlinuz" : "initrd=/casper/initrd",
|
|
|
|
// Of course, Mint has to use 'initrd=/casper/initrd.lz' instead of 'initrd=/casper/initrd'
|
|
|
|
props->is_grub_cfg ? "/casper/vmlinuz persistent" : "persistent initrd=/casper/initrd", TRUE) != NULL)
|
2019-08-02 16:57:10 +00:00
|
|
|
uprintf(" Added 'persistent' kernel option");
|
|
|
|
// Debian & derivatives are assumed to use 'boot=live' in
|
|
|
|
// their kernel options and use 'persistence' as keyword.
|
2019-08-06 17:50:07 +00:00
|
|
|
else if (replace_in_token_data(src, props->is_grub_cfg ? "linux" : "append",
|
|
|
|
"boot=live", "boot=live persistence", TRUE) != NULL)
|
2019-08-02 16:57:10 +00:00
|
|
|
uprintf(" Added 'persistence' kernel option");
|
|
|
|
// Other distros can go to hell. Seriously, just check all partitions for
|
|
|
|
// an ext volume with the right label and use persistence *THEN*. I mean,
|
|
|
|
// why on earth do you need a bloody *NONSTANDARD* kernel option and/or a
|
|
|
|
// "persistence.conf" file. This is SO INCREDIBLY RETARDED that it makes
|
|
|
|
// Windows look smart in comparison. Great job there, Linux people!
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-13 22:31:12 +00:00
|
|
|
// Workaround for config files requiring an ISO label for kernel append that may be
|
|
|
|
// different from our USB label. Oh, and these labels must have spaces converted to \x20.
|
2019-04-04 18:12:48 +00:00
|
|
|
if ((props->is_cfg) || (props->is_conf)) {
|
2015-09-02 22:20:00 +00:00
|
|
|
iso_label = replace_char(img_report.label, ' ', "\\x20");
|
|
|
|
usb_label = replace_char(img_report.usb_label, ' ', "\\x20");
|
2014-12-16 23:24:06 +00:00
|
|
|
if ((iso_label != NULL) && (usb_label != NULL)) {
|
2019-08-02 16:57:10 +00:00
|
|
|
if (props->is_grub_cfg) {
|
|
|
|
// Older versions of GRUB EFI used "linuxefi", newer just use "linux"
|
|
|
|
if ((replace_in_token_data(src, "linux", iso_label, usb_label, TRUE) != NULL) ||
|
|
|
|
(replace_in_token_data(src, "linuxefi", iso_label, usb_label, TRUE) != NULL))
|
|
|
|
uprintf(" Patched %s: '%s' ➔ '%s'\n", src, iso_label, usb_label);
|
|
|
|
}
|
|
|
|
else if (replace_in_token_data(src, (props->is_conf) ? "options" : "append",
|
2016-08-14 13:45:29 +00:00
|
|
|
iso_label, usb_label, TRUE) != NULL)
|
2017-07-16 21:42:19 +00:00
|
|
|
uprintf(" Patched %s: '%s' ➔ '%s'\n", src, iso_label, usb_label);
|
2014-12-16 23:24:06 +00:00
|
|
|
}
|
2015-08-13 22:31:12 +00:00
|
|
|
safe_free(iso_label);
|
|
|
|
safe_free(usb_label);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fix dual BIOS + EFI support for tails and other ISOs
|
|
|
|
if ( (props->is_syslinux_cfg) && (safe_stricmp(psz_path, efi_dirname) == 0) &&
|
|
|
|
(safe_stricmp(psz_basename, syslinux_cfg[0]) == 0) &&
|
2015-09-02 22:20:00 +00:00
|
|
|
(!img_report.has_efi_syslinux) && (dst = safe_strdup(src)) ) {
|
2015-08-13 22:31:12 +00:00
|
|
|
dst[nul_pos-12] = 's'; dst[nul_pos-11] = 'y'; dst[nul_pos-10] = 's';
|
|
|
|
CopyFileA(src, dst, TRUE);
|
|
|
|
uprintf("Duplicated %s to %s\n", src, dst);
|
|
|
|
free(dst);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Workaround for FreeNAS
|
|
|
|
if (props->is_grub_cfg) {
|
2014-12-16 23:24:06 +00:00
|
|
|
iso_label = malloc(MAX_PATH);
|
|
|
|
usb_label = malloc(MAX_PATH);
|
|
|
|
if ((iso_label != NULL) && (usb_label != NULL)) {
|
2015-09-02 22:20:00 +00:00
|
|
|
safe_sprintf(iso_label, MAX_PATH, "cd9660:/dev/iso9660/%s", img_report.label);
|
|
|
|
safe_sprintf(usb_label, MAX_PATH, "msdosfs:/dev/msdosfs/%s", img_report.usb_label);
|
2014-12-16 23:24:06 +00:00
|
|
|
if (replace_in_token_data(src, "set", iso_label, usb_label, TRUE) != NULL)
|
2017-07-16 21:42:19 +00:00
|
|
|
uprintf(" Patched %s: '%s' ➔ '%s'\n", src, iso_label, usb_label);
|
2014-12-16 23:24:06 +00:00
|
|
|
}
|
2015-08-13 22:31:12 +00:00
|
|
|
safe_free(iso_label);
|
|
|
|
safe_free(usb_label);
|
2014-11-07 23:57:17 +00:00
|
|
|
}
|
2015-08-13 22:31:12 +00:00
|
|
|
|
2014-11-09 19:55:17 +00:00
|
|
|
free(src);
|
2014-11-07 23:57:17 +00:00
|
|
|
}
|
|
|
|
|
2018-06-04 11:20:14 +00:00
|
|
|
static void print_extracted_file(char* psz_fullpath, int64_t file_length)
|
2014-11-20 23:00:59 +00:00
|
|
|
{
|
|
|
|
size_t i, nul_pos;
|
|
|
|
|
2015-01-23 02:26:41 +00:00
|
|
|
if (psz_fullpath == NULL)
|
|
|
|
return;
|
2014-11-20 23:00:59 +00:00
|
|
|
// Replace slashes with backslashes and append the size to the path for UI display
|
2015-01-23 02:26:41 +00:00
|
|
|
nul_pos = strlen(psz_fullpath);
|
2014-11-20 23:00:59 +00:00
|
|
|
for (i=0; i<nul_pos; i++)
|
|
|
|
if (psz_fullpath[i] == '/') psz_fullpath[i] = '\\';
|
2018-06-04 11:20:14 +00:00
|
|
|
safe_sprintf(&psz_fullpath[nul_pos], 24, " (%s)", SizeToHumanReadable(file_length, TRUE, FALSE));
|
2014-11-20 23:00:59 +00:00
|
|
|
uprintf("Extracting: %s\n", psz_fullpath);
|
2018-06-04 11:20:14 +00:00
|
|
|
safe_sprintf(&psz_fullpath[nul_pos], 24, " (%s)", SizeToHumanReadable(file_length, FALSE, FALSE));
|
2015-01-01 23:39:28 +00:00
|
|
|
PrintStatus(0, MSG_000, psz_fullpath); // MSG_000 is "%s"
|
2014-11-20 23:00:59 +00:00
|
|
|
// ISO9660 cannot handle backslashes
|
|
|
|
for (i=0; i<nul_pos; i++)
|
|
|
|
if (psz_fullpath[i] == '\\') psz_fullpath[i] = '/';
|
|
|
|
// Remove the appended size for extraction
|
|
|
|
psz_fullpath[nul_pos] = 0;
|
|
|
|
}
|
|
|
|
|
2015-06-25 18:48:37 +00:00
|
|
|
// 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)
|
|
|
|
{
|
|
|
|
static int i = 0;
|
|
|
|
static FILETIME ft[3], *r;
|
|
|
|
LONGLONG ll = Int32x32To64(t, 10000000) + 116444736000000000;
|
|
|
|
|
|
|
|
r = &ft[i];
|
|
|
|
r->dwLowDateTime = (DWORD)ll;
|
|
|
|
r->dwHighDateTime = (DWORD)(ll >> 32);
|
|
|
|
i = (i + 1) % ARRAYSIZE(ft);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Helper function to restore the timestamp on a directory
|
|
|
|
static void __inline set_directory_timestamp(char* path, LPFILETIME creation, LPFILETIME last_access, LPFILETIME modify)
|
|
|
|
{
|
|
|
|
HANDLE dir_handle = CreateFileU(path, GENERIC_READ | GENERIC_WRITE,
|
2017-02-16 14:13:30 +00:00
|
|
|
FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
2015-06-25 18:48:37 +00:00
|
|
|
if ((dir_handle == INVALID_HANDLE_VALUE) || (!SetFileTime(dir_handle, creation, last_access, modify)))
|
|
|
|
uprintf(" Could not set timestamp for directory '%s': %s", path, WindowsErrorString());
|
|
|
|
safe_closehandle(dir_handle);
|
|
|
|
}
|
|
|
|
|
2018-06-23 22:59:36 +00:00
|
|
|
// Preallocates the target size of a newly created file in order to prevent fragmentation from repeated writes
|
|
|
|
static void __inline preallocate_filesize(HANDLE hFile, int64_t file_length)
|
|
|
|
{
|
|
|
|
SetFileInformationByHandle(hFile, FileEndOfFileInfo, &file_length, sizeof(file_length));
|
|
|
|
|
|
|
|
// FileAllocationInfo does not require the size to be a multiple of the cluster size; the FS driver takes care of this.
|
|
|
|
SetFileInformationByHandle(hFile, FileAllocationInfo, &file_length, sizeof(file_length));
|
|
|
|
}
|
|
|
|
|
2012-02-07 02:05:58 +00:00
|
|
|
// Returns 0 on success, nonzero on error
|
2012-01-31 01:40:22 +00:00
|
|
|
static int udf_extract_files(udf_t *p_udf, udf_dirent_t *p_udf_dirent, const char *psz_path)
|
|
|
|
{
|
2012-02-07 16:17:14 +00:00
|
|
|
HANDLE file_handle = NULL;
|
2013-11-17 22:24:50 +00:00
|
|
|
DWORD buf_size, wr_size, err;
|
2014-12-16 23:24:06 +00:00
|
|
|
EXTRACT_PROPS props;
|
|
|
|
BOOL r, is_identical;
|
2018-06-04 11:20:14 +00:00
|
|
|
int length;
|
2014-11-20 23:00:59 +00:00
|
|
|
size_t i;
|
|
|
|
char tmp[128], *psz_fullpath = NULL, *psz_sanpath = NULL;
|
2012-01-31 01:40:22 +00:00
|
|
|
const char* psz_basename;
|
|
|
|
udf_dirent_t *p_udf_dirent2;
|
|
|
|
uint8_t buf[UDF_BLOCKSIZE];
|
2018-06-04 11:20:14 +00:00
|
|
|
int64_t read, file_length;
|
2012-01-31 01:40:22 +00:00
|
|
|
|
|
|
|
if ((p_udf_dirent == NULL) || (psz_path == NULL))
|
|
|
|
return 1;
|
|
|
|
|
2019-08-20 17:06:07 +00:00
|
|
|
if (psz_path[0] == 0)
|
|
|
|
UpdateProgressWithInfoInit(NULL, TRUE);
|
2012-02-01 14:26:36 +00:00
|
|
|
while ((p_udf_dirent = udf_readdir(p_udf_dirent)) != NULL) {
|
|
|
|
if (FormatStatus) goto out;
|
2012-01-31 01:40:22 +00:00
|
|
|
psz_basename = udf_get_filename(p_udf_dirent);
|
2013-10-30 20:29:55 +00:00
|
|
|
if (strlen(psz_basename) == 0)
|
|
|
|
continue;
|
2018-06-04 11:20:14 +00:00
|
|
|
length = (int)(3 + strlen(psz_path) + strlen(psz_basename) + strlen(psz_extract_dir) + 24);
|
|
|
|
psz_fullpath = (char*)calloc(sizeof(char), length);
|
2012-01-31 01:40:22 +00:00
|
|
|
if (psz_fullpath == NULL) {
|
2016-05-25 11:20:20 +00:00
|
|
|
uprintf("Error allocating file name");
|
2012-01-31 01:40:22 +00:00
|
|
|
goto out;
|
|
|
|
}
|
2018-06-04 11:20:14 +00:00
|
|
|
length = _snprintf(psz_fullpath, length, "%s%s/%s", psz_extract_dir, psz_path, psz_basename);
|
|
|
|
if (length < 0) {
|
2012-01-31 01:40:22 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (udf_is_dir(p_udf_dirent)) {
|
2014-11-20 23:00:59 +00:00
|
|
|
if (!scan_only) {
|
|
|
|
psz_sanpath = sanitize_filename(psz_fullpath, &is_identical);
|
|
|
|
IGNORE_RETVAL(_mkdirU(psz_sanpath));
|
2015-06-25 18:48:37 +00:00
|
|
|
if (preserve_timestamps) {
|
|
|
|
set_directory_timestamp(psz_sanpath, to_filetime(udf_get_attribute_time(p_udf_dirent)),
|
|
|
|
to_filetime(udf_get_access_time(p_udf_dirent)), to_filetime(udf_get_modification_time(p_udf_dirent)));
|
|
|
|
}
|
2014-11-20 23:00:59 +00:00
|
|
|
safe_free(psz_sanpath);
|
|
|
|
}
|
2012-01-31 01:40:22 +00:00
|
|
|
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)]))
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
} else {
|
2018-06-04 11:20:14 +00:00
|
|
|
file_length = udf_get_file_length(p_udf_dirent);
|
|
|
|
if (check_iso_props(psz_path, file_length, psz_basename, psz_fullpath, &props)) {
|
2012-02-01 14:26:36 +00:00
|
|
|
safe_free(psz_fullpath);
|
|
|
|
continue;
|
|
|
|
}
|
2018-06-04 11:20:14 +00:00
|
|
|
print_extracted_file(psz_fullpath, file_length);
|
2012-12-15 03:27:14 +00:00
|
|
|
for (i=0; i<NB_OLD_C32; i++) {
|
2014-12-16 23:24:06 +00:00
|
|
|
if (props.is_old_c32[i] && use_own_c32[i]) {
|
2014-02-04 20:01:28 +00:00
|
|
|
static_sprintf(tmp, "%s/syslinux-%s/%s", FILES_DIR, embedded_sl_version_str[0], old_c32_name[i]);
|
2016-05-25 11:20:20 +00:00
|
|
|
if (CopyFileU(tmp, psz_fullpath, FALSE)) {
|
|
|
|
uprintf(" Replaced with local version %s", IsFileInDB(tmp)?"✓":"✗");
|
2012-12-15 03:27:14 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-05-25 11:20:20 +00:00
|
|
|
uprintf(" Could not replace file: %s", WindowsErrorString());
|
2012-03-11 01:55:25 +00:00
|
|
|
}
|
|
|
|
}
|
2012-12-15 03:27:14 +00:00
|
|
|
if (i < NB_OLD_C32)
|
|
|
|
continue;
|
2014-11-20 23:00:59 +00:00
|
|
|
psz_sanpath = sanitize_filename(psz_fullpath, &is_identical);
|
|
|
|
if (!is_identical)
|
2016-05-25 11:20:20 +00:00
|
|
|
uprintf(" File name sanitized to '%s'", psz_sanpath);
|
2014-11-20 23:00:59 +00:00
|
|
|
file_handle = CreateFileU(psz_sanpath, GENERIC_READ | GENERIC_WRITE,
|
2017-02-16 14:13:30 +00:00
|
|
|
FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
2012-02-07 02:05:58 +00:00
|
|
|
if (file_handle == INVALID_HANDLE_VALUE) {
|
2013-11-17 22:24:50 +00:00
|
|
|
err = GetLastError();
|
2016-05-25 11:20:20 +00:00
|
|
|
uprintf(" Unable to create file: %s", WindowsErrorString());
|
2014-11-20 23:00:59 +00:00
|
|
|
if ((err == ERROR_ACCESS_DENIED) && (safe_strcmp(&psz_sanpath[3], autorun_name) == 0))
|
2014-02-09 23:38:16 +00:00
|
|
|
uprintf(stupid_antivirus);
|
|
|
|
else
|
|
|
|
goto out;
|
2018-06-23 22:59:36 +00:00
|
|
|
} else {
|
|
|
|
preallocate_filesize(file_handle, file_length);
|
|
|
|
while (file_length > 0) {
|
|
|
|
if (FormatStatus) goto out;
|
|
|
|
memset(buf, 0, UDF_BLOCKSIZE);
|
|
|
|
read = udf_read_block(p_udf_dirent, buf, 1);
|
|
|
|
if (read < 0) {
|
|
|
|
uprintf(" Error reading UDF file %s", &psz_fullpath[strlen(psz_extract_dir)]);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
buf_size = (DWORD)MIN(file_length, read);
|
|
|
|
ISO_BLOCKING(r = WriteFileWithRetry(file_handle, buf, buf_size, &wr_size, WRITE_RETRIES));
|
|
|
|
if (!r) {
|
|
|
|
uprintf(" Error writing file: %s", WindowsErrorString());
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
file_length -= read;
|
|
|
|
if (nb_blocks++ % PROGRESS_THRESHOLD == 0)
|
2019-08-20 17:06:07 +00:00
|
|
|
UpdateProgressWithInfo(OP_FILE_COPY, MSG_231, nb_blocks, total_blocks);
|
2012-01-31 01:40:22 +00:00
|
|
|
}
|
|
|
|
}
|
2015-06-25 18:48:37 +00:00
|
|
|
if ((preserve_timestamps) && (!SetFileTime(file_handle, to_filetime(udf_get_attribute_time(p_udf_dirent)),
|
|
|
|
to_filetime(udf_get_access_time(p_udf_dirent)), to_filetime(udf_get_modification_time(p_udf_dirent)))))
|
|
|
|
uprintf(" Could not set timestamp: %s", WindowsErrorString());
|
|
|
|
|
2012-02-07 02:05:58 +00:00
|
|
|
// 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()
|
2012-02-07 16:17:14 +00:00
|
|
|
// may take forever to complete and is not interruptible. We try to detect this.
|
|
|
|
ISO_BLOCKING(safe_closehandle(file_handle));
|
2017-11-01 12:30:29 +00:00
|
|
|
if (props.is_cfg)
|
2014-12-16 23:24:06 +00:00
|
|
|
fix_config(psz_sanpath, psz_path, psz_basename, &props);
|
2014-11-20 23:00:59 +00:00
|
|
|
safe_free(psz_sanpath);
|
2012-01-31 01:40:22 +00:00
|
|
|
}
|
2012-02-01 14:26:36 +00:00
|
|
|
safe_free(psz_fullpath);
|
2012-01-31 01:40:22 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
out:
|
2012-02-01 14:26:36 +00:00
|
|
|
if (p_udf_dirent != NULL)
|
|
|
|
udf_dirent_free(p_udf_dirent);
|
2012-02-07 16:17:14 +00:00
|
|
|
ISO_BLOCKING(safe_closehandle(file_handle));
|
2019-05-23 12:09:25 +00:00
|
|
|
safe_free(psz_sanpath);
|
2012-02-01 14:26:36 +00:00
|
|
|
safe_free(psz_fullpath);
|
2012-01-31 01:40:22 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-02-07 02:05:58 +00:00
|
|
|
// Returns 0 on success, nonzero on error
|
2012-01-31 01:40:22 +00:00
|
|
|
static int iso_extract_files(iso9660_t* p_iso, const char *psz_path)
|
|
|
|
{
|
2012-02-07 16:17:14 +00:00
|
|
|
HANDLE file_handle = NULL;
|
2013-11-17 22:24:50 +00:00
|
|
|
DWORD buf_size, wr_size, err;
|
2014-12-16 23:24:06 +00:00
|
|
|
EXTRACT_PROPS props;
|
2016-01-14 17:43:02 +00:00
|
|
|
BOOL is_symlink, is_identical;
|
2018-06-04 11:20:14 +00:00
|
|
|
int length, r = 1;
|
2019-05-23 12:09:25 +00:00
|
|
|
char tmp[128], psz_fullpath[MAX_PATH], *psz_basename = NULL, *psz_sanpath = NULL;
|
2012-01-31 01:40:22 +00:00
|
|
|
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;
|
2018-06-04 11:20:14 +00:00
|
|
|
CdioISO9660FileList_t* p_entlist;
|
2017-09-14 16:30:41 +00:00
|
|
|
size_t i, j;
|
2012-01-31 01:40:22 +00:00
|
|
|
lsn_t lsn;
|
2018-06-04 11:20:14 +00:00
|
|
|
int64_t file_length, extent_length;
|
2012-01-31 01:40:22 +00:00
|
|
|
|
|
|
|
if ((p_iso == NULL) || (psz_path == NULL))
|
|
|
|
return 1;
|
|
|
|
|
2018-06-04 11:20:14 +00:00
|
|
|
length = _snprintf(psz_fullpath, sizeof(psz_fullpath), "%s%s/", psz_extract_dir, psz_path);
|
|
|
|
if (length < 0)
|
2012-01-31 01:40:22 +00:00
|
|
|
return 1;
|
2018-06-04 11:20:14 +00:00
|
|
|
psz_basename = &psz_fullpath[length];
|
2012-01-31 01:40:22 +00:00
|
|
|
|
|
|
|
p_entlist = iso9660_ifs_readdir(p_iso, psz_path);
|
2012-02-16 20:44:16 +00:00
|
|
|
if (!p_entlist) {
|
2016-05-25 11:20:20 +00:00
|
|
|
uprintf("Could not access directory %s", psz_path);
|
2012-01-31 01:40:22 +00:00
|
|
|
return 1;
|
2012-02-16 20:44:16 +00:00
|
|
|
}
|
2012-01-31 01:40:22 +00:00
|
|
|
|
2019-08-20 17:06:07 +00:00
|
|
|
if (psz_path[0] == 0)
|
|
|
|
UpdateProgressWithInfoInit(NULL, TRUE);
|
2012-02-15 21:55:41 +00:00
|
|
|
_CDIO_LIST_FOREACH(p_entnode, p_entlist) {
|
2012-02-01 14:26:36 +00:00
|
|
|
if (FormatStatus) goto out;
|
2012-01-31 01:40:22 +00:00
|
|
|
p_statbuf = (iso9660_stat_t*) _cdio_list_node_data(p_entnode);
|
2013-01-13 00:41:21 +00:00
|
|
|
// Eliminate . and .. entries
|
2012-01-31 01:40:22 +00:00
|
|
|
if ( (strcmp(p_statbuf->filename, ".") == 0)
|
|
|
|
|| (strcmp(p_statbuf->filename, "..") == 0) )
|
|
|
|
continue;
|
2013-10-13 23:02:32 +00:00
|
|
|
// Rock Ridge requires an exception
|
2014-02-13 21:25:34 +00:00
|
|
|
is_symlink = FALSE;
|
2013-10-13 23:02:32 +00:00
|
|
|
if ((p_statbuf->rr.b3_rock == yep) && enable_rockridge) {
|
2018-06-04 11:20:14 +00:00
|
|
|
safe_strcpy(psz_basename, sizeof(psz_fullpath) - length - 1, p_statbuf->filename);
|
2013-10-13 23:02:32 +00:00
|
|
|
if (safe_strlen(p_statbuf->filename) > 64)
|
2015-09-02 22:20:00 +00:00
|
|
|
img_report.has_long_filename = TRUE;
|
2014-02-13 21:25:34 +00:00
|
|
|
// libcdio has a memleak for Rock Ridge symlinks. It doesn't look like there's an easy fix there as
|
|
|
|
// a generic list that's unaware of RR extensions is being used, so we prevent that memleak ourselves
|
|
|
|
is_symlink = (p_statbuf->rr.psz_symlink != NULL);
|
|
|
|
if (is_symlink)
|
2015-09-02 22:20:00 +00:00
|
|
|
img_report.has_symlinks = TRUE;
|
2014-02-13 21:25:34 +00:00
|
|
|
if (scan_only)
|
|
|
|
safe_free(p_statbuf->rr.psz_symlink);
|
2013-10-13 23:02:32 +00:00
|
|
|
} else {
|
2018-06-04 11:20:14 +00:00
|
|
|
iso9660_name_translate_ext(p_statbuf->filename, psz_basename, joliet_level);
|
2013-01-13 00:41:21 +00:00
|
|
|
}
|
2012-01-31 01:40:22 +00:00
|
|
|
if (p_statbuf->type == _STAT_DIR) {
|
2014-11-20 23:00:59 +00:00
|
|
|
if (!scan_only) {
|
|
|
|
psz_sanpath = sanitize_filename(psz_fullpath, &is_identical);
|
|
|
|
IGNORE_RETVAL(_mkdirU(psz_sanpath));
|
2015-06-25 18:48:37 +00:00
|
|
|
if (preserve_timestamps) {
|
|
|
|
LPFILETIME ft = to_filetime(mktime(&p_statbuf->tm));
|
|
|
|
set_directory_timestamp(psz_sanpath, ft, ft, ft);
|
|
|
|
}
|
2014-11-20 23:00:59 +00:00
|
|
|
safe_free(psz_sanpath);
|
|
|
|
}
|
2012-01-31 01:40:22 +00:00
|
|
|
if (iso_extract_files(p_iso, psz_iso_name))
|
|
|
|
goto out;
|
|
|
|
} else {
|
2018-06-04 11:20:14 +00:00
|
|
|
file_length = p_statbuf->size;
|
|
|
|
if (check_iso_props(psz_path, file_length, psz_basename, psz_fullpath, &props)) {
|
2012-02-01 14:26:36 +00:00
|
|
|
continue;
|
|
|
|
}
|
2018-06-04 11:20:14 +00:00
|
|
|
print_extracted_file(psz_fullpath, file_length);
|
2012-12-15 03:27:14 +00:00
|
|
|
for (i=0; i<NB_OLD_C32; i++) {
|
2014-12-16 23:24:06 +00:00
|
|
|
if (props.is_old_c32[i] && use_own_c32[i]) {
|
2014-02-04 20:01:28 +00:00
|
|
|
static_sprintf(tmp, "%s/syslinux-%s/%s", FILES_DIR, embedded_sl_version_str[0], old_c32_name[i]);
|
2016-05-25 11:20:20 +00:00
|
|
|
if (CopyFileU(tmp, psz_fullpath, FALSE)) {
|
|
|
|
uprintf(" Replaced with local version %s", IsFileInDB(tmp)?"✓":"✗");
|
2012-12-15 03:27:14 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-05-25 11:20:20 +00:00
|
|
|
uprintf(" Could not replace file: %s", WindowsErrorString());
|
2012-03-11 01:55:25 +00:00
|
|
|
}
|
|
|
|
}
|
2012-12-15 03:27:14 +00:00
|
|
|
if (i < NB_OLD_C32)
|
|
|
|
continue;
|
2014-11-20 23:00:59 +00:00
|
|
|
psz_sanpath = sanitize_filename(psz_fullpath, &is_identical);
|
|
|
|
if (!is_identical)
|
2016-05-25 11:20:20 +00:00
|
|
|
uprintf(" File name sanitized to '%s'", psz_sanpath);
|
2014-02-13 21:25:34 +00:00
|
|
|
if (is_symlink) {
|
2018-06-04 11:20:14 +00:00
|
|
|
if (file_length == 0)
|
2016-05-25 11:20:20 +00:00
|
|
|
uprintf(" Ignoring Rock Ridge symbolic link to '%s'", p_statbuf->rr.psz_symlink);
|
2014-02-13 21:25:34 +00:00
|
|
|
safe_free(p_statbuf->rr.psz_symlink);
|
|
|
|
}
|
2014-11-20 23:00:59 +00:00
|
|
|
file_handle = CreateFileU(psz_sanpath, GENERIC_READ | GENERIC_WRITE,
|
2017-02-16 14:13:30 +00:00
|
|
|
FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
2012-02-07 02:05:58 +00:00
|
|
|
if (file_handle == INVALID_HANDLE_VALUE) {
|
2013-11-17 22:24:50 +00:00
|
|
|
err = GetLastError();
|
2016-05-25 11:20:20 +00:00
|
|
|
uprintf(" Unable to create file: %s", WindowsErrorString());
|
2014-11-20 23:00:59 +00:00
|
|
|
if ((err == ERROR_ACCESS_DENIED) && (safe_strcmp(&psz_sanpath[3], autorun_name) == 0))
|
2014-02-09 23:38:16 +00:00
|
|
|
uprintf(stupid_antivirus);
|
|
|
|
else
|
|
|
|
goto out;
|
2018-06-23 22:59:36 +00:00
|
|
|
} else {
|
|
|
|
preallocate_filesize(file_handle, file_length);
|
|
|
|
for (j=0; j<p_statbuf->extents; j++) {
|
|
|
|
extent_length = p_statbuf->extsize[j];
|
|
|
|
for (i=0; extent_length>0; i++) {
|
|
|
|
if (FormatStatus) goto out;
|
|
|
|
memset(buf, 0, ISO_BLOCKSIZE);
|
|
|
|
lsn = p_statbuf->lsn[j] + (lsn_t)i;
|
|
|
|
if (iso9660_iso_seek_read(p_iso, buf, lsn, 1) != ISO_BLOCKSIZE) {
|
|
|
|
uprintf(" Error reading ISO9660 file %s at LSN %lu",
|
|
|
|
psz_iso_name, (long unsigned int)lsn);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
buf_size = (DWORD)MIN(extent_length, ISO_BLOCKSIZE);
|
|
|
|
ISO_BLOCKING(r = WriteFileWithRetry(file_handle, buf, buf_size, &wr_size, WRITE_RETRIES));
|
|
|
|
if (!r) {
|
|
|
|
uprintf(" Error writing file: %s", WindowsErrorString());
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
extent_length -= ISO_BLOCKSIZE;
|
|
|
|
if (nb_blocks++ % PROGRESS_THRESHOLD == 0)
|
2019-08-20 17:06:07 +00:00
|
|
|
UpdateProgressWithInfo(OP_FILE_COPY, MSG_231, nb_blocks, total_blocks);
|
2017-09-14 16:30:41 +00:00
|
|
|
}
|
2012-01-31 01:40:22 +00:00
|
|
|
}
|
|
|
|
}
|
2015-06-25 18:48:37 +00:00
|
|
|
if (preserve_timestamps) {
|
|
|
|
LPFILETIME ft = to_filetime(mktime(&p_statbuf->tm));
|
|
|
|
if (!SetFileTime(file_handle, ft, ft, ft))
|
|
|
|
uprintf(" Could not set timestamp: %s", WindowsErrorString());
|
|
|
|
}
|
2012-02-07 16:17:14 +00:00
|
|
|
ISO_BLOCKING(safe_closehandle(file_handle));
|
2017-11-01 12:30:29 +00:00
|
|
|
if (props.is_cfg)
|
2014-12-16 23:24:06 +00:00
|
|
|
fix_config(psz_sanpath, psz_path, psz_basename, &props);
|
2014-11-20 23:00:59 +00:00
|
|
|
safe_free(psz_sanpath);
|
2012-01-31 01:40:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
r = 0;
|
|
|
|
|
|
|
|
out:
|
2012-02-07 16:17:14 +00:00
|
|
|
ISO_BLOCKING(safe_closehandle(file_handle));
|
2018-06-04 11:20:14 +00:00
|
|
|
iso9660_filelist_free(p_entlist);
|
2019-05-23 12:09:25 +00:00
|
|
|
safe_free(psz_sanpath);
|
2012-01-31 01:40:22 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2014-12-30 19:46:13 +00:00
|
|
|
void GetGrubVersion(char* buf, size_t buf_size)
|
|
|
|
{
|
|
|
|
char *p, unauthorized[] = {'<', '>', ':', '|', '*', '?', '\\', '/'};
|
|
|
|
size_t i;
|
|
|
|
const char grub_version_str[] = "GRUB version %s";
|
|
|
|
|
|
|
|
for (i=0; i<buf_size; i++) {
|
|
|
|
if (memcmp(&buf[i], grub_version_str, sizeof(grub_version_str)) == 0) {
|
2017-08-10 18:43:04 +00:00
|
|
|
static_strcpy(img_report.grub2_version, &buf[i + sizeof(grub_version_str)]);
|
2014-12-30 19:46:13 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Sanitize the string
|
2015-09-02 22:20:00 +00:00
|
|
|
for (p = &img_report.grub2_version[0]; *p; p++) {
|
2014-12-30 19:46:13 +00:00
|
|
|
for (i=0; i<sizeof(unauthorized); i++) {
|
|
|
|
if (*p == unauthorized[i])
|
|
|
|
*p = '_';
|
|
|
|
}
|
|
|
|
}
|
2015-03-17 01:18:09 +00:00
|
|
|
// <Shakes fist angrily> "KASPERSKYYYYYY!!!..." (https://github.com/pbatard/rufus/issues/467)
|
|
|
|
// But seriously, these guys should know better than "security" through obscurity...
|
2015-09-02 22:20:00 +00:00
|
|
|
if (img_report.grub2_version[0] == '0')
|
|
|
|
img_report.grub2_version[0] = 0;
|
2014-12-30 19:46:13 +00:00
|
|
|
}
|
|
|
|
|
2012-11-04 01:33:54 +00:00
|
|
|
BOOL ExtractISO(const char* src_iso, const char* dest_dir, BOOL scan)
|
2012-01-31 01:40:22 +00:00
|
|
|
{
|
2016-06-19 21:33:00 +00:00
|
|
|
size_t i, j, size, sl_index = 0;
|
2014-01-21 17:08:41 +00:00
|
|
|
uint16_t sl_version;
|
2012-02-21 00:08:31 +00:00
|
|
|
FILE* fd;
|
2014-02-09 02:54:07 +00:00
|
|
|
int r = 1;
|
2012-01-31 01:40:22 +00:00
|
|
|
iso9660_t* p_iso = NULL;
|
2017-06-24 16:23:06 +00:00
|
|
|
iso9660_pvd_t pvd;
|
2015-10-22 22:20:50 +00:00
|
|
|
udf_t* p_udf = NULL;
|
2012-01-31 01:40:22 +00:00
|
|
|
udf_dirent_t* p_udf_root;
|
2014-08-05 23:57:32 +00:00
|
|
|
char *tmp, *buf, *ext;
|
2015-03-30 01:09:03 +00:00
|
|
|
char path[MAX_PATH], path2[16];
|
2018-06-22 19:59:03 +00:00
|
|
|
const char* basedir[] = { "i386", "amd64", "minint" };
|
2012-03-27 19:31:15 +00:00
|
|
|
const char* tmp_sif = ".\\txtsetup.sif~";
|
2013-10-13 23:02:32 +00:00
|
|
|
iso_extension_mask_t iso_extension_mask = ISO_EXTENSION_ALL;
|
2015-08-22 14:18:25 +00:00
|
|
|
char* spacing = " ";
|
2012-02-01 14:26:36 +00:00
|
|
|
|
2014-04-13 14:20:20 +00:00
|
|
|
if ((!enable_iso) || (src_iso == NULL) || (dest_dir == NULL))
|
2012-02-01 14:26:36 +00:00
|
|
|
return FALSE;
|
2012-01-31 01:40:22 +00:00
|
|
|
|
2012-02-01 14:26:36 +00:00
|
|
|
scan_only = scan;
|
2015-08-22 14:18:25 +00:00
|
|
|
if (!scan_only)
|
|
|
|
spacing = "";
|
2012-02-14 20:52:39 +00:00
|
|
|
cdio_log_set_handler(log_handler);
|
2012-02-01 14:26:36 +00:00
|
|
|
psz_extract_dir = dest_dir;
|
2015-01-01 23:39:28 +00:00
|
|
|
// Change progress style to marquee for scanning
|
2012-02-01 14:26:36 +00:00
|
|
|
if (scan_only) {
|
2015-08-22 14:18:25 +00:00
|
|
|
uprintf("ISO analysis:");
|
2015-01-01 23:39:28 +00:00
|
|
|
SendMessage(hMainDialog, UM_PROGRESS_INIT, PBS_MARQUEE, 0);
|
2012-02-01 14:26:36 +00:00
|
|
|
total_blocks = 0;
|
2014-01-21 17:08:41 +00:00
|
|
|
has_ldlinux_c32 = FALSE;
|
2012-02-21 00:08:31 +00:00
|
|
|
// String array of all isolinux/syslinux locations
|
|
|
|
StrArrayCreate(&config_path, 8);
|
2014-01-21 17:08:41 +00:00
|
|
|
StrArrayCreate(&isolinux_path, 8);
|
2015-01-01 23:39:28 +00:00
|
|
|
PrintInfo(0, MSG_202);
|
2012-02-01 14:26:36 +00:00
|
|
|
} else {
|
|
|
|
uprintf("Extracting files...\n");
|
2014-02-04 20:01:28 +00:00
|
|
|
IGNORE_RETVAL(_chdirU(app_dir));
|
2019-08-20 17:06:07 +00:00
|
|
|
// PrintInfo(0, MSG_231);
|
2012-02-01 14:26:36 +00:00
|
|
|
if (total_blocks == 0) {
|
|
|
|
uprintf("Error: ISO has not been properly scanned.\n");
|
|
|
|
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_ISO_SCAN);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
nb_blocks = 0;
|
2012-02-07 16:17:14 +00:00
|
|
|
iso_blocking_status = 0;
|
2012-02-01 14:26:36 +00:00
|
|
|
}
|
2012-01-31 01:40:22 +00:00
|
|
|
|
2015-12-28 19:56:10 +00:00
|
|
|
// First try to open as UDF - fallback to ISO if it failed
|
2012-01-31 01:40:22 +00:00
|
|
|
p_udf = udf_open(src_iso);
|
|
|
|
if (p_udf == NULL)
|
|
|
|
goto try_iso;
|
2015-08-22 14:18:25 +00:00
|
|
|
uprintf("%sImage is an UDF image", spacing);
|
2012-01-31 01:40:22 +00:00
|
|
|
|
|
|
|
p_udf_root = udf_get_root(p_udf, true, 0);
|
|
|
|
if (p_udf_root == NULL) {
|
2015-08-22 14:18:25 +00:00
|
|
|
uprintf("%sCould not locate UDF root directory", spacing);
|
2012-01-31 01:40:22 +00:00
|
|
|
goto out;
|
|
|
|
}
|
2012-02-14 20:52:39 +00:00
|
|
|
if (scan_only) {
|
2015-09-02 22:20:00 +00:00
|
|
|
if (udf_get_logical_volume_id(p_udf, img_report.label, sizeof(img_report.label)) <= 0)
|
|
|
|
img_report.label[0] = 0;
|
2017-06-24 16:23:06 +00:00
|
|
|
// Open the UDF as ISO so that we can perform size checks
|
|
|
|
p_iso = iso9660_open(src_iso);
|
2012-02-14 20:52:39 +00:00
|
|
|
}
|
2012-01-31 01:40:22 +00:00
|
|
|
r = udf_extract_files(p_udf, p_udf_root, "");
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
try_iso:
|
2013-10-13 23:02:32 +00:00
|
|
|
// Perform our first scan with Joliet disabled (if Rock Ridge is enabled), so that we can find if
|
2014-02-13 21:25:34 +00:00
|
|
|
// there exists a Rock Ridge file with a name > 64 chars or if there are symlinks. If that is the
|
|
|
|
// case then we also disable Joliet during the extract phase.
|
2015-09-02 22:20:00 +00:00
|
|
|
if ((!enable_joliet) || (enable_rockridge && (scan_only || img_report.has_long_filename || img_report.has_symlinks))) {
|
2013-10-13 23:02:32 +00:00
|
|
|
iso_extension_mask &= ~ISO_EXTENSION_JOLIET;
|
|
|
|
}
|
|
|
|
if (!enable_rockridge) {
|
|
|
|
iso_extension_mask &= ~ISO_EXTENSION_ROCK_RIDGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
p_iso = iso9660_open_ext(src_iso, iso_extension_mask);
|
2012-01-31 01:40:22 +00:00
|
|
|
if (p_iso == NULL) {
|
2015-08-22 14:18:25 +00:00
|
|
|
uprintf("%s'%s' doesn't look like an ISO image", spacing, src_iso);
|
2014-02-09 02:54:07 +00:00
|
|
|
r = 1;
|
2012-01-31 01:40:22 +00:00
|
|
|
goto out;
|
|
|
|
}
|
2015-08-22 14:18:25 +00:00
|
|
|
uprintf("%sImage is an ISO9660 image", spacing);
|
2018-06-04 11:20:14 +00:00
|
|
|
joliet_level = iso9660_ifs_get_joliet_level(p_iso);
|
2012-02-14 20:52:39 +00:00
|
|
|
if (scan_only) {
|
2012-03-27 19:31:15 +00:00
|
|
|
if (iso9660_ifs_get_volume_id(p_iso, &tmp)) {
|
2017-08-10 18:43:04 +00:00
|
|
|
static_strcpy(img_report.label, tmp);
|
2012-03-27 19:31:15 +00:00
|
|
|
safe_free(tmp);
|
2012-02-16 20:44:16 +00:00
|
|
|
} else
|
2015-09-02 22:20:00 +00:00
|
|
|
img_report.label[0] = 0;
|
2014-02-13 21:25:34 +00:00
|
|
|
} else {
|
|
|
|
if (iso_extension_mask & (ISO_EXTENSION_JOLIET|ISO_EXTENSION_ROCK_RIDGE))
|
2015-08-22 14:18:25 +00:00
|
|
|
uprintf("%sThis image will be extracted using %s extensions (if present)", spacing,
|
2014-02-13 21:25:34 +00:00
|
|
|
(iso_extension_mask & ISO_EXTENSION_JOLIET)?"Joliet":"Rock Ridge");
|
|
|
|
else
|
2015-08-22 14:18:25 +00:00
|
|
|
uprintf("%sThis image will not be extracted using any ISO extensions", spacing);
|
2012-02-14 20:52:39 +00:00
|
|
|
}
|
2012-01-31 01:40:22 +00:00
|
|
|
r = iso_extract_files(p_iso, "");
|
|
|
|
|
|
|
|
out:
|
2012-02-07 16:17:14 +00:00
|
|
|
iso_blocking_status = -1;
|
2012-02-01 14:26:36 +00:00
|
|
|
if (scan_only) {
|
2017-06-24 16:23:06 +00:00
|
|
|
struct __stat64 stat;
|
|
|
|
// Find if there is a mismatch between the ISO size, as reported by the PVD, and the actual file size
|
|
|
|
if ((iso9660_ifs_read_pvd(p_iso, &pvd)) && (_stat64U(src_iso, &stat) == 0))
|
|
|
|
img_report.mismatch_size = (int64_t)(iso9660_get_pvd_space_size(&pvd)) * ISO_BLOCKSIZE - stat.st_size;
|
2012-05-30 23:49:28 +00:00
|
|
|
// Remove trailing spaces from the label
|
2016-06-19 21:33:00 +00:00
|
|
|
for (j=safe_strlen(img_report.label)-1; ((j>0)&&(isspaceU(img_report.label[j]))); j--)
|
2015-09-02 22:20:00 +00:00
|
|
|
img_report.label[j] = 0;
|
2012-02-01 14:26:36 +00:00
|
|
|
// We use the fact that UDF_BLOCKSIZE and ISO_BLOCKSIZE are the same here
|
2015-09-02 22:20:00 +00:00
|
|
|
img_report.projected_size = total_blocks * ISO_BLOCKSIZE;
|
2012-02-21 00:08:31 +00:00
|
|
|
// We will link the existing isolinux.cfg from a syslinux.cfg we create
|
2014-01-21 17:08:41 +00:00
|
|
|
// If multiple config files exist, choose the one with the shortest path
|
|
|
|
// (so that a '/syslinux.cfg' is preferred over a '/isolinux/isolinux.cfg')
|
|
|
|
if (!IsStrArrayEmpty(config_path)) {
|
2015-09-02 22:20:00 +00:00
|
|
|
// Set the img_report.cfg_path string to maximum length, so that we don't have to
|
2015-03-30 01:09:03 +00:00
|
|
|
// do a special case for StrArray entry 0.
|
2015-09-02 22:20:00 +00:00
|
|
|
memset(img_report.cfg_path, '_', sizeof(img_report.cfg_path)-1);
|
|
|
|
img_report.cfg_path[sizeof(img_report.cfg_path)-1] = 0;
|
2015-03-30 01:09:03 +00:00
|
|
|
for (i=0; i<config_path.Index; i++) {
|
|
|
|
// OpenSuse based Live image have a /syslinux.cfg that doesn't work, so we enforce
|
|
|
|
// the use of the one in '/boot/[i386|x86_64]/loader/isolinux.cfg' if present.
|
|
|
|
// Note that, because the openSuse live script are not designed to handle anything but
|
|
|
|
// an ISO9660 filesystem for the live device, this still won't allow for proper boot.
|
|
|
|
// See https://github.com/openSUSE/kiwi/issues/354
|
|
|
|
if ( (_stricmp(config_path.String[i], "/boot/i386/loader/isolinux.cfg") == 0) ||
|
|
|
|
(_stricmp(config_path.String[i], "/boot/x86_64/loader/isolinux.cfg") == 0)) {
|
2017-08-10 18:43:04 +00:00
|
|
|
static_strcpy(img_report.cfg_path, config_path.String[i]);
|
2015-09-02 22:20:00 +00:00
|
|
|
img_report.needs_syslinux_overwrite = TRUE;
|
2015-03-30 01:09:03 +00:00
|
|
|
break;
|
|
|
|
}
|
2014-08-05 23:57:32 +00:00
|
|
|
// Tails uses an '/EFI/BOOT/isolinux.cfg' along with a '/isolinux/isolinux.cfg'
|
|
|
|
// which are the exact same length. However, only the /isolinux one will work,
|
|
|
|
// so for now, at equal length, always pick the latest.
|
|
|
|
// We may have to revisit this and prefer a path that contains '/isolinux' if
|
|
|
|
// this hack is not enough for other images.
|
2015-09-02 22:20:00 +00:00
|
|
|
if (safe_strlen(img_report.cfg_path) >= safe_strlen(config_path.String[i]))
|
2017-08-10 18:43:04 +00:00
|
|
|
static_strcpy(img_report.cfg_path, config_path.String[i]);
|
2012-02-21 00:08:31 +00:00
|
|
|
}
|
2015-09-02 22:20:00 +00:00
|
|
|
uprintf(" Will use '%s' for Syslinux", img_report.cfg_path);
|
2014-01-21 17:08:41 +00:00
|
|
|
// Extract all of the isolinux.bin files we found to identify their versions
|
|
|
|
for (i=0; i<isolinux_path.Index; i++) {
|
2017-07-18 11:57:22 +00:00
|
|
|
char isolinux_tmp[MAX_PATH];
|
|
|
|
static_sprintf(isolinux_tmp, "%s\\isolinux.tmp", temp_dir);
|
2016-08-07 16:41:47 +00:00
|
|
|
size = (size_t)ExtractISOFile(src_iso, isolinux_path.String[i], isolinux_tmp, FILE_ATTRIBUTE_NORMAL);
|
2014-01-21 17:08:41 +00:00
|
|
|
if (size == 0) {
|
2015-08-22 14:18:25 +00:00
|
|
|
uprintf(" Could not access %s", isolinux_path.String[i]);
|
2014-01-21 17:08:41 +00:00
|
|
|
} else {
|
|
|
|
buf = (char*)calloc(size, 1);
|
|
|
|
if (buf == NULL) break;
|
2016-08-07 16:41:47 +00:00
|
|
|
fd = fopen(isolinux_tmp, "rb");
|
2014-01-21 17:08:41 +00:00
|
|
|
if (fd == NULL) {
|
|
|
|
free(buf);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
fread(buf, 1, size, fd);
|
|
|
|
fclose(fd);
|
2014-08-05 23:57:32 +00:00
|
|
|
sl_version = GetSyslinuxVersion(buf, size, &ext);
|
2015-09-02 22:20:00 +00:00
|
|
|
if (img_report.sl_version == 0) {
|
2017-08-10 18:43:04 +00:00
|
|
|
static_strcpy(img_report.sl_version_ext, ext);
|
2015-09-02 22:20:00 +00:00
|
|
|
img_report.sl_version = sl_version;
|
2016-06-19 21:33:00 +00:00
|
|
|
sl_index = i;
|
2015-09-02 22:20:00 +00:00
|
|
|
} else if ((img_report.sl_version != sl_version) || (safe_strcmp(img_report.sl_version_ext, ext) != 0)) {
|
2016-08-07 16:41:47 +00:00
|
|
|
uprintf(" Found conflicting isolinux versions:\n '%s' (%d.%02d%s) vs '%s' (%d.%02d%s)",
|
2016-06-19 21:33:00 +00:00
|
|
|
isolinux_path.String[sl_index], SL_MAJOR(img_report.sl_version), SL_MINOR(img_report.sl_version),
|
2015-09-02 22:20:00 +00:00
|
|
|
img_report.sl_version_ext, isolinux_path.String[i], SL_MAJOR(sl_version), SL_MINOR(sl_version), ext);
|
2016-06-19 21:33:00 +00:00
|
|
|
// Workaround for Antergos and other ISOs, that have multiple Syslinux versions.
|
|
|
|
// Where possible, prefer to the one that resides in the same directory as the config file.
|
|
|
|
for (j=safe_strlen(img_report.cfg_path); (j>0) && (img_report.cfg_path[j]!='/'); j--);
|
|
|
|
if (safe_strnicmp(img_report.cfg_path, isolinux_path.String[i], j) == 0) {
|
2017-08-10 18:43:04 +00:00
|
|
|
static_strcpy(img_report.sl_version_ext, ext);
|
2016-06-19 21:33:00 +00:00
|
|
|
img_report.sl_version = sl_version;
|
|
|
|
sl_index = i;
|
|
|
|
}
|
2014-01-21 17:08:41 +00:00
|
|
|
}
|
|
|
|
free(buf);
|
|
|
|
}
|
[net] add Windows retail ISO downloads
* This is accomplished through Fido (https://github.com/pbatard/Fido), a *SIGNED*
PowerShell script, that is downloaded from GitHub and that resides in memory for
the duration of a session.
* The reason we use a downloaded PS script, rather than an embedded on, is because:
- Microsoft have regularly been changing the deal with regards to how retail ISOs
can be downloaded, and not for the better, so we can't simply embed a static
means of downloading ISOs and expect that to work forever.
- By using an external script, we can immediately respond to whatever new means of
*ANNOYING* their legitimate users Microsoft will come up with next, as well as
make sure that, the minute a new retail version of Windows becomes available, it
also becomes available for download in Rufus.
* Note that if you are concerned about downloading a remote PS script that is being
run at the same level as an elevated application, you should understand that:
- Only scripts downloaded from GitHub, from an account that is protected with 2FA,
are allowed to run (i.e. someone would first have to steal a *physical* 2FA key
to be in a position to upload a malicious script).
- On top of this, only scripts that are signed with a separate private key (RSA +
AES-256), that is itself also protected with a strong unique password which only
a single person knows (and must manually enter each time they want to make a new
version of the script available for download), are allowed to run.
The above means that there's about as much chance for someone to manage to upload
a malicious script on the GitHub servers, that Rufus would allow to run, as there
is for someone to upload a malicious version of Rufus itself.
Still, if you are paranoid and have concerns that, even as you can validate from
its source that Rufus does not attempt to execute any remote script unless a user
actively selected and clicked the DOWNLOAD button, you can also completely disable
the remote script download feature, if you just set the update check to disabled
(which, by the way, Rufus *EXPLICITLY* asks you to choose whether you want to
enable or not, the very first time you run the application).
* Also remove _unlinkU() which duplicates what DeleteFileU() already does.
2019-03-02 23:28:56 +00:00
|
|
|
DeleteFileU(isolinux_tmp);
|
2014-01-21 17:08:41 +00:00
|
|
|
}
|
2015-09-02 22:20:00 +00:00
|
|
|
if (img_report.sl_version != 0) {
|
|
|
|
static_sprintf(img_report.sl_version_str, "%d.%02d",
|
|
|
|
SL_MAJOR(img_report.sl_version), SL_MINOR(img_report.sl_version));
|
2015-08-22 14:18:25 +00:00
|
|
|
uprintf(" Detected Syslinux version: %s%s (from '%s')",
|
2016-06-19 21:33:00 +00:00
|
|
|
img_report.sl_version_str, img_report.sl_version_ext, isolinux_path.String[sl_index]);
|
2015-09-02 22:20:00 +00:00
|
|
|
if ( (has_ldlinux_c32 && (SL_MAJOR(img_report.sl_version) < 5))
|
|
|
|
|| (!has_ldlinux_c32 && (SL_MAJOR(img_report.sl_version) >= 5)) )
|
2015-08-22 14:18:25 +00:00
|
|
|
uprintf(" Warning: Conflict between Isolinux version and the presence of ldlinux.c32...");
|
2014-01-21 17:08:41 +00:00
|
|
|
} else {
|
|
|
|
// Couldn't find a version from isolinux.bin. Force set to the versions we embed
|
2015-09-02 22:20:00 +00:00
|
|
|
img_report.sl_version = embedded_sl_version[has_ldlinux_c32?1:0];
|
|
|
|
static_sprintf(img_report.sl_version_str, "%d.%02d",
|
|
|
|
SL_MAJOR(img_report.sl_version), SL_MINOR(img_report.sl_version));
|
2015-08-22 14:18:25 +00:00
|
|
|
uprintf(" Warning: Could not detect Isolinux version - Forcing to %s (embedded)",
|
2015-09-02 22:20:00 +00:00
|
|
|
img_report.sl_version_str);
|
2014-01-21 17:08:41 +00:00
|
|
|
}
|
2018-11-01 19:18:57 +00:00
|
|
|
}
|
2019-04-04 18:12:48 +00:00
|
|
|
if (!IS_EFI_BOOTABLE(img_report) && HAS_EFI_IMG(img_report) && HasEfiImgBootLoaders()) {
|
2018-11-01 19:18:57 +00:00
|
|
|
img_report.has_efi = 0x80;
|
2012-02-21 00:08:31 +00:00
|
|
|
}
|
2016-12-14 23:27:04 +00:00
|
|
|
if (HAS_WINPE(img_report)) {
|
2012-03-27 19:31:15 +00:00
|
|
|
// In case we have a WinPE 1.x based iso, we extract and parse txtsetup.sif
|
|
|
|
// during scan, to see if /minint was provided for OsLoadOptions, as it decides
|
|
|
|
// whether we should use 0x80 or 0x81 as the disk ID in the MBR
|
2017-08-10 18:43:04 +00:00
|
|
|
static_sprintf(path, "/%s/txtsetup.sif",
|
2018-06-22 19:59:03 +00:00
|
|
|
basedir[((img_report.winpe&WINPE_I386) == WINPE_I386)?0:((img_report.winpe&WINPE_AMD64) == WINPE_AMD64?1:2)]);
|
2014-05-15 20:17:12 +00:00
|
|
|
ExtractISOFile(src_iso, path, tmp_sif, FILE_ATTRIBUTE_NORMAL);
|
2012-11-12 01:53:34 +00:00
|
|
|
tmp = get_token_data_file("OsLoadOptions", tmp_sif);
|
2012-03-27 19:31:15 +00:00
|
|
|
if (tmp != NULL) {
|
|
|
|
for (i=0; i<strlen(tmp); i++)
|
|
|
|
tmp[i] = (char)tolower(tmp[i]);
|
2015-08-22 14:18:25 +00:00
|
|
|
uprintf(" Checking txtsetup.sif:\n OsLoadOptions = %s", tmp);
|
2015-09-02 22:20:00 +00:00
|
|
|
img_report.uses_minint = (strstr(tmp, "/minint") != NULL);
|
2012-03-27 19:31:15 +00:00
|
|
|
}
|
[net] add Windows retail ISO downloads
* This is accomplished through Fido (https://github.com/pbatard/Fido), a *SIGNED*
PowerShell script, that is downloaded from GitHub and that resides in memory for
the duration of a session.
* The reason we use a downloaded PS script, rather than an embedded on, is because:
- Microsoft have regularly been changing the deal with regards to how retail ISOs
can be downloaded, and not for the better, so we can't simply embed a static
means of downloading ISOs and expect that to work forever.
- By using an external script, we can immediately respond to whatever new means of
*ANNOYING* their legitimate users Microsoft will come up with next, as well as
make sure that, the minute a new retail version of Windows becomes available, it
also becomes available for download in Rufus.
* Note that if you are concerned about downloading a remote PS script that is being
run at the same level as an elevated application, you should understand that:
- Only scripts downloaded from GitHub, from an account that is protected with 2FA,
are allowed to run (i.e. someone would first have to steal a *physical* 2FA key
to be in a position to upload a malicious script).
- On top of this, only scripts that are signed with a separate private key (RSA +
AES-256), that is itself also protected with a strong unique password which only
a single person knows (and must manually enter each time they want to make a new
version of the script available for download), are allowed to run.
The above means that there's about as much chance for someone to manage to upload
a malicious script on the GitHub servers, that Rufus would allow to run, as there
is for someone to upload a malicious version of Rufus itself.
Still, if you are paranoid and have concerns that, even as you can validate from
its source that Rufus does not attempt to execute any remote script unless a user
actively selected and clicked the DOWNLOAD button, you can also completely disable
the remote script download feature, if you just set the update check to disabled
(which, by the way, Rufus *EXPLICITLY* asks you to choose whether you want to
enable or not, the very first time you run the application).
* Also remove _unlinkU() which duplicates what DeleteFileU() already does.
2019-03-02 23:28:56 +00:00
|
|
|
DeleteFileU(tmp_sif);
|
2012-03-27 19:31:15 +00:00
|
|
|
safe_free(tmp);
|
|
|
|
}
|
2019-01-08 18:30:07 +00:00
|
|
|
if (HAS_WININST(img_report)) {
|
|
|
|
img_report.wininst_version = GetInstallWimVersion(src_iso);
|
2015-08-27 17:22:27 +00:00
|
|
|
}
|
2015-09-02 22:20:00 +00:00
|
|
|
if (img_report.has_grub2) {
|
2014-12-30 19:46:13 +00:00
|
|
|
// In case we have a GRUB2 based iso, we extract boot/grub/i386-pc/normal.mod to parse its version
|
2015-09-02 22:20:00 +00:00
|
|
|
img_report.grub2_version[0] = 0;
|
2014-12-30 19:46:13 +00:00
|
|
|
if ((GetTempPathU(sizeof(path), path) != 0) && (GetTempFileNameU(path, APPLICATION_NAME, 0, path) != 0)) {
|
|
|
|
size = (size_t)ExtractISOFile(src_iso, "boot/grub/i386-pc/normal.mod", path, FILE_ATTRIBUTE_NORMAL);
|
|
|
|
buf = (char*)calloc(size, 1);
|
|
|
|
fd = fopen(path, "rb");
|
|
|
|
if ((size == 0) || (buf == NULL) || (fd == NULL)) {
|
2015-08-22 14:18:25 +00:00
|
|
|
uprintf(" Could not read Grub version from 'boot/grub/i386-pc/normal.mod'");
|
2014-12-30 19:46:13 +00:00
|
|
|
} else {
|
|
|
|
fread(buf, 1, size, fd);
|
|
|
|
fclose(fd);
|
|
|
|
GetGrubVersion(buf, size);
|
|
|
|
}
|
|
|
|
free(buf);
|
[net] add Windows retail ISO downloads
* This is accomplished through Fido (https://github.com/pbatard/Fido), a *SIGNED*
PowerShell script, that is downloaded from GitHub and that resides in memory for
the duration of a session.
* The reason we use a downloaded PS script, rather than an embedded on, is because:
- Microsoft have regularly been changing the deal with regards to how retail ISOs
can be downloaded, and not for the better, so we can't simply embed a static
means of downloading ISOs and expect that to work forever.
- By using an external script, we can immediately respond to whatever new means of
*ANNOYING* their legitimate users Microsoft will come up with next, as well as
make sure that, the minute a new retail version of Windows becomes available, it
also becomes available for download in Rufus.
* Note that if you are concerned about downloading a remote PS script that is being
run at the same level as an elevated application, you should understand that:
- Only scripts downloaded from GitHub, from an account that is protected with 2FA,
are allowed to run (i.e. someone would first have to steal a *physical* 2FA key
to be in a position to upload a malicious script).
- On top of this, only scripts that are signed with a separate private key (RSA +
AES-256), that is itself also protected with a strong unique password which only
a single person knows (and must manually enter each time they want to make a new
version of the script available for download), are allowed to run.
The above means that there's about as much chance for someone to manage to upload
a malicious script on the GitHub servers, that Rufus would allow to run, as there
is for someone to upload a malicious version of Rufus itself.
Still, if you are paranoid and have concerns that, even as you can validate from
its source that Rufus does not attempt to execute any remote script unless a user
actively selected and clicked the DOWNLOAD button, you can also completely disable
the remote script download feature, if you just set the update check to disabled
(which, by the way, Rufus *EXPLICITLY* asks you to choose whether you want to
enable or not, the very first time you run the application).
* Also remove _unlinkU() which duplicates what DeleteFileU() already does.
2019-03-02 23:28:56 +00:00
|
|
|
DeleteFileU(path);
|
2014-12-30 19:46:13 +00:00
|
|
|
}
|
2015-09-02 22:20:00 +00:00
|
|
|
if (img_report.grub2_version[0] != 0)
|
|
|
|
uprintf(" Detected Grub version: %s", img_report.grub2_version);
|
2015-03-17 01:18:09 +00:00
|
|
|
else {
|
2015-08-22 14:18:25 +00:00
|
|
|
uprintf(" Could not detect Grub version");
|
2015-09-02 22:20:00 +00:00
|
|
|
img_report.has_grub2 = FALSE;
|
2015-03-17 01:18:09 +00:00
|
|
|
}
|
2014-12-30 19:46:13 +00:00
|
|
|
}
|
2012-02-21 00:08:31 +00:00
|
|
|
StrArrayDestroy(&config_path);
|
2014-01-21 17:08:41 +00:00
|
|
|
StrArrayDestroy(&isolinux_path);
|
2015-01-01 23:39:28 +00:00
|
|
|
SendMessage(hMainDialog, UM_PROGRESS_EXIT, 0, 0);
|
2017-08-09 15:27:11 +00:00
|
|
|
} else {
|
2019-04-04 18:12:48 +00:00
|
|
|
// Solus and other ISOs only provide EFI boot files in a FAT efi.img
|
2017-08-09 15:27:11 +00:00
|
|
|
if (img_report.has_efi == 0x80)
|
2019-04-04 18:12:48 +00:00
|
|
|
DumpFatDir(dest_dir, 0);
|
2017-08-09 15:27:11 +00:00
|
|
|
if (HAS_SYSLINUX(img_report)) {
|
2017-08-10 18:43:04 +00:00
|
|
|
static_sprintf(path, "%s\\syslinux.cfg", dest_dir);
|
2017-08-09 15:27:11 +00:00
|
|
|
// Create a /syslinux.cfg (if none exists) that points to the existing isolinux cfg
|
|
|
|
fd = fopen(path, "r");
|
|
|
|
if (fd != NULL && img_report.needs_syslinux_overwrite) {
|
|
|
|
fclose(fd);
|
|
|
|
fd = NULL;
|
2017-08-10 18:43:04 +00:00
|
|
|
static_sprintf(path2, "%s\\syslinux.org", dest_dir);
|
2017-08-09 15:27:11 +00:00
|
|
|
uprintf("Renaming: %s ➔ %s", path, path2);
|
|
|
|
IGNORE_RETVAL(rename(path, path2));
|
|
|
|
}
|
2012-02-21 00:08:31 +00:00
|
|
|
if (fd == NULL) {
|
2017-08-09 15:27:11 +00:00
|
|
|
fd = fopen(path, "w"); // No "/syslinux.cfg" => create a new one
|
|
|
|
if (fd == NULL) {
|
|
|
|
uprintf("Unable to create %s - booting from USB will not work", path);
|
|
|
|
r = 1;
|
|
|
|
} else {
|
|
|
|
fprintf(fd, "DEFAULT loadconfig\n\nLABEL loadconfig\n CONFIG %s\n", img_report.cfg_path);
|
|
|
|
for (i = safe_strlen(img_report.cfg_path); (i > 0) && (img_report.cfg_path[i] != '/'); i--);
|
|
|
|
if (i > 0) {
|
|
|
|
img_report.cfg_path[i] = 0;
|
|
|
|
fprintf(fd, " APPEND %s/\n", img_report.cfg_path);
|
|
|
|
img_report.cfg_path[i] = '/';
|
|
|
|
}
|
|
|
|
uprintf("Created: %s", path);
|
2012-02-21 00:08:31 +00:00
|
|
|
}
|
|
|
|
}
|
2017-08-09 15:27:11 +00:00
|
|
|
if (fd != NULL)
|
|
|
|
fclose(fd);
|
2012-02-21 00:08:31 +00:00
|
|
|
}
|
2012-02-01 14:26:36 +00:00
|
|
|
}
|
2012-01-31 01:40:22 +00:00
|
|
|
if (p_iso != NULL)
|
|
|
|
iso9660_close(p_iso);
|
|
|
|
if (p_udf != NULL)
|
|
|
|
udf_close(p_udf);
|
2012-02-01 14:26:36 +00:00
|
|
|
if ((r != 0) && (FormatStatus == 0))
|
2012-02-07 02:05:58 +00:00
|
|
|
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR((scan_only?ERROR_ISO_SCAN:ERROR_ISO_EXTRACT));
|
|
|
|
return (r == 0);
|
2012-01-31 01:40:22 +00:00
|
|
|
}
|
2012-03-27 19:31:15 +00:00
|
|
|
|
2014-05-15 20:17:12 +00:00
|
|
|
int64_t ExtractISOFile(const char* iso, const char* iso_file, const char* dest_file, DWORD attributes)
|
2012-03-27 19:31:15 +00:00
|
|
|
{
|
2017-09-14 16:30:41 +00:00
|
|
|
size_t i, j;
|
2012-03-27 22:31:58 +00:00
|
|
|
ssize_t read_size;
|
2018-06-04 11:20:14 +00:00
|
|
|
int64_t file_length, extent_length, r = 0;
|
2012-03-27 19:31:15 +00:00
|
|
|
char buf[UDF_BLOCKSIZE];
|
|
|
|
DWORD buf_size, wr_size;
|
|
|
|
iso9660_t* p_iso = NULL;
|
2015-10-22 22:20:50 +00:00
|
|
|
udf_t* p_udf = NULL;
|
2012-03-27 19:31:15 +00:00
|
|
|
udf_dirent_t *p_udf_root = NULL, *p_udf_file = NULL;
|
|
|
|
iso9660_stat_t *p_statbuf = NULL;
|
|
|
|
lsn_t lsn;
|
|
|
|
HANDLE file_handle = INVALID_HANDLE_VALUE;
|
|
|
|
|
|
|
|
file_handle = CreateFileU(dest_file, GENERIC_READ | GENERIC_WRITE,
|
2017-02-16 14:13:30 +00:00
|
|
|
FILE_SHARE_READ, NULL, CREATE_ALWAYS, attributes, NULL);
|
2012-03-27 19:31:15 +00:00
|
|
|
if (file_handle == INVALID_HANDLE_VALUE) {
|
2017-08-09 15:27:11 +00:00
|
|
|
uprintf(" Could not create file %s: %s", dest_file, WindowsErrorString());
|
2012-03-27 19:31:15 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2015-12-28 19:56:10 +00:00
|
|
|
// First try to open as UDF - fallback to ISO if it failed
|
2012-03-27 19:31:15 +00:00
|
|
|
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) {
|
2017-08-09 15:27:11 +00:00
|
|
|
uprintf("Could not locate UDF root directory");
|
2012-03-27 19:31:15 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
p_udf_file = udf_fopen(p_udf_root, iso_file);
|
|
|
|
if (!p_udf_file) {
|
2017-08-09 15:27:11 +00:00
|
|
|
uprintf("Could not locate file %s in ISO image", iso_file);
|
2012-03-27 19:31:15 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
file_length = udf_get_file_length(p_udf_file);
|
|
|
|
while (file_length > 0) {
|
|
|
|
memset(buf, 0, UDF_BLOCKSIZE);
|
2012-03-27 22:31:58 +00:00
|
|
|
read_size = udf_read_block(p_udf_file, buf, 1);
|
|
|
|
if (read_size < 0) {
|
2017-08-09 15:27:11 +00:00
|
|
|
uprintf("Error reading UDF file %s", iso_file);
|
2012-03-27 19:31:15 +00:00
|
|
|
goto out;
|
|
|
|
}
|
2012-03-27 22:31:58 +00:00
|
|
|
buf_size = (DWORD)MIN(file_length, read_size);
|
2016-01-14 17:43:02 +00:00
|
|
|
if (!WriteFileWithRetry(file_handle, buf, buf_size, &wr_size, WRITE_RETRIES)) {
|
2017-08-09 15:27:11 +00:00
|
|
|
uprintf(" Error writing file %s: %s", dest_file, WindowsErrorString());
|
2012-03-27 19:31:15 +00:00
|
|
|
goto out;
|
|
|
|
}
|
2012-03-27 22:31:58 +00:00
|
|
|
file_length -= read_size;
|
2014-01-21 17:08:41 +00:00
|
|
|
r += read_size;
|
2012-03-27 19:31:15 +00:00
|
|
|
}
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
try_iso:
|
|
|
|
p_iso = iso9660_open(iso);
|
|
|
|
if (p_iso == NULL) {
|
2017-08-09 15:27:11 +00:00
|
|
|
uprintf("Unable to open image '%s'", iso);
|
2012-03-27 19:31:15 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
p_statbuf = iso9660_ifs_stat_translate(p_iso, iso_file);
|
|
|
|
if (p_statbuf == NULL) {
|
2017-08-09 15:27:11 +00:00
|
|
|
uprintf("Could not get ISO-9660 file information for file %s", iso_file);
|
2012-03-27 19:31:15 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2017-09-14 16:30:41 +00:00
|
|
|
for (j = 0; j < p_statbuf->extents; j++) {
|
2018-06-04 11:20:14 +00:00
|
|
|
extent_length = p_statbuf->extsize[j];
|
|
|
|
for (i = 0; extent_length > 0; i++) {
|
2017-09-14 16:30:41 +00:00
|
|
|
memset(buf, 0, ISO_BLOCKSIZE);
|
|
|
|
lsn = p_statbuf->lsn[j] + (lsn_t)i;
|
|
|
|
if (iso9660_iso_seek_read(p_iso, buf, lsn, 1) != ISO_BLOCKSIZE) {
|
|
|
|
uprintf(" Error reading ISO9660 file %s at LSN %lu", iso_file, (long unsigned int)lsn);
|
|
|
|
goto out;
|
|
|
|
}
|
2018-06-04 11:20:14 +00:00
|
|
|
buf_size = (DWORD)MIN(extent_length, ISO_BLOCKSIZE);
|
2017-09-14 16:30:41 +00:00
|
|
|
if (!WriteFileWithRetry(file_handle, buf, buf_size, &wr_size, WRITE_RETRIES)) {
|
|
|
|
uprintf(" Error writing file %s: %s", dest_file, WindowsErrorString());
|
|
|
|
goto out;
|
|
|
|
}
|
2018-06-04 11:20:14 +00:00
|
|
|
extent_length -= ISO_BLOCKSIZE;
|
2017-09-14 16:30:41 +00:00
|
|
|
r += ISO_BLOCKSIZE;
|
2012-03-27 19:31:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
safe_closehandle(file_handle);
|
2012-03-27 22:31:58 +00:00
|
|
|
if (p_statbuf != NULL)
|
|
|
|
safe_free(p_statbuf->rr.psz_symlink);
|
2012-03-27 19:31:15 +00:00
|
|
|
safe_free(p_statbuf);
|
|
|
|
if (p_udf_root != NULL)
|
|
|
|
udf_dirent_free(p_udf_root);
|
|
|
|
if (p_udf_file != NULL)
|
|
|
|
udf_dirent_free(p_udf_file);
|
|
|
|
if (p_iso != NULL)
|
|
|
|
iso9660_close(p_iso);
|
|
|
|
if (p_udf != NULL)
|
|
|
|
udf_close(p_udf);
|
2014-01-21 17:08:41 +00:00
|
|
|
return r;
|
2012-03-27 19:31:15 +00:00
|
|
|
}
|
2015-01-14 00:51:41 +00:00
|
|
|
|
2015-08-27 17:22:27 +00:00
|
|
|
uint32_t GetInstallWimVersion(const char* iso)
|
|
|
|
{
|
|
|
|
char *wim_path = NULL, *p, 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;
|
|
|
|
|
2019-01-08 18:30:07 +00:00
|
|
|
wim_path = safe_strdup(&img_report.wininst_path[0][2]);
|
2015-12-28 19:56:10 +00:00
|
|
|
if (wim_path == NULL)
|
|
|
|
goto out;
|
|
|
|
// UDF indiscriminately accepts slash or backslash delimiters,
|
|
|
|
// but ISO-9660 requires slash
|
|
|
|
for (p = wim_path; *p != 0; p++)
|
2015-08-27 17:22:27 +00:00
|
|
|
if (*p == '\\') *p = '/';
|
|
|
|
|
2015-12-28 19:56:10 +00:00
|
|
|
// First try to open as UDF - fallback to ISO if it failed
|
2015-08-27 17:22:27 +00:00
|
|
|
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) {
|
2017-08-09 15:27:11 +00:00
|
|
|
uprintf("Could not locate UDF root directory");
|
2015-08-27 17:22:27 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
p_udf_file = udf_fopen(p_udf_root, wim_path);
|
|
|
|
if (!p_udf_file) {
|
2017-08-09 15:27:11 +00:00
|
|
|
uprintf("Could not locate file %s in ISO image", wim_path);
|
2015-08-27 17:22:27 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (udf_read_block(p_udf_file, buf, 1) != UDF_BLOCKSIZE) {
|
2017-08-09 15:27:11 +00:00
|
|
|
uprintf("Error reading UDF file %s", wim_path);
|
2015-08-27 17:22:27 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
r = wim_header[3];
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
try_iso:
|
|
|
|
p_iso = iso9660_open(iso);
|
|
|
|
if (p_iso == NULL) {
|
2017-08-09 15:27:11 +00:00
|
|
|
uprintf("Could not open image '%s'", iso);
|
2015-08-27 17:22:27 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
p_statbuf = iso9660_ifs_stat_translate(p_iso, wim_path);
|
|
|
|
if (p_statbuf == NULL) {
|
2017-08-09 15:27:11 +00:00
|
|
|
uprintf("Could not get ISO-9660 file information for file %s", wim_path);
|
2015-08-27 17:22:27 +00:00
|
|
|
goto out;
|
|
|
|
}
|
2017-09-14 16:30:41 +00:00
|
|
|
if (iso9660_iso_seek_read(p_iso, buf, p_statbuf->lsn[0], 1) != ISO_BLOCKSIZE) {
|
|
|
|
uprintf("Error reading ISO-9660 file %s at LSN %d", wim_path, p_statbuf->lsn[0]);
|
2015-08-27 17:22:27 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
r = wim_header[3];
|
2015-10-22 22:20:50 +00:00
|
|
|
|
2015-08-27 17:22:27 +00:00
|
|
|
out:
|
|
|
|
if (p_statbuf != NULL)
|
|
|
|
safe_free(p_statbuf->rr.psz_symlink);
|
|
|
|
safe_free(p_statbuf);
|
|
|
|
if (p_udf_root != NULL)
|
|
|
|
udf_dirent_free(p_udf_root);
|
|
|
|
if (p_udf_file != NULL)
|
|
|
|
udf_dirent_free(p_udf_file);
|
|
|
|
if (p_iso != NULL)
|
|
|
|
iso9660_close(p_iso);
|
|
|
|
if (p_udf != NULL)
|
|
|
|
udf_close(p_udf);
|
|
|
|
safe_free(wim_path);
|
2016-02-20 22:52:32 +00:00
|
|
|
return bswap_uint32(r);
|
2015-08-27 17:22:27 +00:00
|
|
|
}
|
|
|
|
|
2017-08-09 15:27:11 +00:00
|
|
|
#define ISO_NB_BLOCKS 16
|
|
|
|
typedef struct {
|
|
|
|
iso9660_t* p_iso;
|
|
|
|
lsn_t lsn;
|
|
|
|
libfat_sector_t sec_start;
|
|
|
|
// Use a multi block buffer, to improve sector reads
|
|
|
|
uint8_t buf[ISO_BLOCKSIZE * ISO_NB_BLOCKS];
|
|
|
|
} iso9660_readfat_private;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read sectors from a FAT img file residing on an ISO-9660 filesystem.
|
|
|
|
* NB: This assumes that the img file sectors are contiguous on the ISO.
|
|
|
|
*/
|
|
|
|
int iso9660_readfat(intptr_t pp, void *buf, size_t secsize, libfat_sector_t sec)
|
|
|
|
{
|
|
|
|
iso9660_readfat_private* p_private = (iso9660_readfat_private*)pp;
|
|
|
|
|
|
|
|
if (sizeof(p_private->buf) % secsize != 0) {
|
|
|
|
uprintf("iso9660_readfat: Sector size %d is not a divisor of %d", secsize, sizeof(p_private->buf));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((sec < p_private->sec_start) || (sec >= p_private->sec_start + sizeof(p_private->buf) / secsize)) {
|
|
|
|
// Sector being queried is not in our multi block buffer -> Update it
|
|
|
|
p_private->sec_start = (((sec * secsize) / ISO_BLOCKSIZE) * ISO_BLOCKSIZE) / secsize;
|
|
|
|
if (iso9660_iso_seek_read(p_private->p_iso, p_private->buf,
|
|
|
|
p_private->lsn + (lsn_t)((p_private->sec_start * secsize) / ISO_BLOCKSIZE), ISO_NB_BLOCKS)
|
|
|
|
!= ISO_NB_BLOCKS * ISO_BLOCKSIZE) {
|
|
|
|
uprintf("Error reading ISO-9660 file %s at LSN %lu\n", img_report.efi_img_path,
|
|
|
|
(long unsigned int)(p_private->lsn + (p_private->sec_start * secsize) / ISO_BLOCKSIZE));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
memcpy(buf, &p_private->buf[(sec - p_private->sec_start)*secsize], secsize);
|
|
|
|
return (int)secsize;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2019-04-04 18:12:48 +00:00
|
|
|
* Returns TRUE if an EFI bootloader exists in the img.
|
2017-08-09 15:27:11 +00:00
|
|
|
*/
|
2019-04-04 18:12:48 +00:00
|
|
|
BOOL HasEfiImgBootLoaders(void)
|
2017-08-09 15:27:11 +00:00
|
|
|
{
|
|
|
|
BOOL ret = FALSE;
|
|
|
|
iso9660_t* p_iso = NULL;
|
|
|
|
iso9660_stat_t* p_statbuf = NULL;
|
|
|
|
iso9660_readfat_private* p_private = NULL;
|
|
|
|
int32_t dc, c;
|
2018-03-22 23:14:20 +00:00
|
|
|
struct libfat_filesystem *lf_fs = NULL;
|
2017-08-09 15:27:11 +00:00
|
|
|
struct libfat_direntry direntry;
|
|
|
|
char name[12] = { 0 };
|
|
|
|
int i, j, k;
|
|
|
|
|
|
|
|
if ((image_path == NULL) || !HAS_EFI_IMG(img_report))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
p_iso = iso9660_open(image_path);
|
|
|
|
if (p_iso == NULL) {
|
|
|
|
uprintf("Could not open image '%s' as an ISO-9660 file system", image_path);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
p_statbuf = iso9660_ifs_stat_translate(p_iso, img_report.efi_img_path);
|
|
|
|
if (p_statbuf == NULL) {
|
|
|
|
uprintf("Could not get ISO-9660 file information for file %s\n", img_report.efi_img_path);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
p_private = malloc(sizeof(iso9660_readfat_private));
|
|
|
|
if (p_private == NULL)
|
|
|
|
goto out;
|
|
|
|
p_private->p_iso = p_iso;
|
2017-09-14 16:30:41 +00:00
|
|
|
p_private->lsn = p_statbuf->lsn[0]; // Image should be small enough not to use multiextents
|
2017-08-09 15:27:11 +00:00
|
|
|
p_private->sec_start = 0;
|
|
|
|
// Populate our intial buffer
|
|
|
|
if (iso9660_iso_seek_read(p_private->p_iso, p_private->buf, p_private->lsn, ISO_NB_BLOCKS) != ISO_NB_BLOCKS * ISO_BLOCKSIZE) {
|
|
|
|
uprintf("Error reading ISO-9660 file %s at LSN %lu\n", img_report.efi_img_path, (long unsigned int)p_private->lsn);
|
|
|
|
goto out;
|
|
|
|
}
|
2018-03-22 23:14:20 +00:00
|
|
|
lf_fs = libfat_open(iso9660_readfat, (intptr_t)p_private);
|
|
|
|
if (lf_fs == NULL) {
|
2017-08-09 15:27:11 +00:00
|
|
|
uprintf("FAT access error");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Navigate to /EFI/BOOT
|
2018-03-22 23:14:20 +00:00
|
|
|
if (libfat_searchdir(lf_fs, 0, "EFI ", &direntry) < 0)
|
2017-08-09 15:27:11 +00:00
|
|
|
goto out;
|
|
|
|
dc = direntry.entry[26] + (direntry.entry[27] << 8);
|
2018-03-22 23:14:20 +00:00
|
|
|
if (libfat_searchdir(lf_fs, dc, "BOOT ", &direntry) < 0)
|
2017-08-09 15:27:11 +00:00
|
|
|
goto out;
|
|
|
|
dc = direntry.entry[26] + (direntry.entry[27] << 8);
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAYSIZE(efi_bootname); i++) {
|
|
|
|
// Sanity check in case the EFI forum comes up with a 'bootmips64.efi' or something...
|
|
|
|
if (strlen(efi_bootname[i]) > 12) {
|
|
|
|
uprintf("Internal error: FAT 8.3");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (j = 0, k = 0; efi_bootname[i][j] != 0; j++) {
|
|
|
|
if (efi_bootname[i][j] == '.') {
|
|
|
|
while (k < 8)
|
|
|
|
name[k++] = ' ';
|
|
|
|
} else
|
|
|
|
name[k++] = toupper(efi_bootname[i][j]);
|
|
|
|
}
|
2018-03-22 23:14:20 +00:00
|
|
|
c = libfat_searchdir(lf_fs, dc, name, &direntry);
|
2017-08-09 15:27:11 +00:00
|
|
|
if (c > 0) {
|
2019-04-04 18:12:48 +00:00
|
|
|
if (!ret)
|
|
|
|
uprintf(" Detected EFI bootloader(s) (from '%s'):", img_report.efi_img_path);
|
|
|
|
uprintf(" ● '%s'", efi_bootname[i]);
|
|
|
|
ret = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (lf_fs != NULL)
|
|
|
|
libfat_close(lf_fs);
|
|
|
|
if (p_statbuf != NULL)
|
|
|
|
safe_free(p_statbuf->rr.psz_symlink);
|
|
|
|
safe_free(p_statbuf);
|
|
|
|
safe_free(p_private);
|
|
|
|
if (p_iso != NULL)
|
|
|
|
iso9660_close(p_iso);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL DumpFatDir(const char* path, int32_t cluster)
|
|
|
|
{
|
|
|
|
// We don't have concurrent calls to this function, so a static lf_fs is fine
|
|
|
|
static struct libfat_filesystem *lf_fs = NULL;
|
|
|
|
void* buf;
|
|
|
|
char *target = NULL, *name = NULL;
|
|
|
|
BOOL ret = FALSE;
|
2019-04-27 15:04:47 +00:00
|
|
|
HANDLE handle = NULL;
|
2019-04-04 18:12:48 +00:00
|
|
|
DWORD size, written;
|
|
|
|
libfat_diritem_t diritem = { 0 };
|
|
|
|
libfat_dirpos_t dirpos = { cluster, -1, 0 };
|
|
|
|
libfat_sector_t s;
|
|
|
|
iso9660_t* p_iso = NULL;
|
|
|
|
iso9660_stat_t* p_statbuf = NULL;
|
|
|
|
iso9660_readfat_private* p_private = NULL;
|
|
|
|
|
|
|
|
if (path == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (cluster == 0) {
|
|
|
|
// Root dir => Perform init stuff
|
|
|
|
if (image_path == NULL)
|
|
|
|
return FALSE;
|
|
|
|
p_iso = iso9660_open(image_path);
|
|
|
|
if (p_iso == NULL) {
|
|
|
|
uprintf("Could not open image '%s' as an ISO-9660 file system", image_path);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
p_statbuf = iso9660_ifs_stat_translate(p_iso, img_report.efi_img_path);
|
|
|
|
if (p_statbuf == NULL) {
|
|
|
|
uprintf("Could not get ISO-9660 file information for file %s\n", img_report.efi_img_path);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
p_private = malloc(sizeof(iso9660_readfat_private));
|
|
|
|
if (p_private == NULL)
|
|
|
|
goto out;
|
|
|
|
p_private->p_iso = p_iso;
|
|
|
|
p_private->lsn = p_statbuf->lsn[0]; // Image should be small enough not to use multiextents
|
|
|
|
p_private->sec_start = 0;
|
|
|
|
// Populate our intial buffer
|
|
|
|
if (iso9660_iso_seek_read(p_private->p_iso, p_private->buf, p_private->lsn, ISO_NB_BLOCKS) != ISO_NB_BLOCKS * ISO_BLOCKSIZE) {
|
|
|
|
uprintf("Error reading ISO-9660 file %s at LSN %lu\n", img_report.efi_img_path, (long unsigned int)p_private->lsn);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
lf_fs = libfat_open(iso9660_readfat, (intptr_t)p_private);
|
|
|
|
if (lf_fs == NULL) {
|
|
|
|
uprintf("FAT access error");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
dirpos.cluster = libfat_dumpdir(lf_fs, &dirpos, &diritem);
|
|
|
|
if (dirpos.cluster >= 0) {
|
|
|
|
name = wchar_to_utf8(diritem.name);
|
|
|
|
target = malloc(strlen(path) + safe_strlen(name) + 2);
|
|
|
|
if ((name == NULL) || (target == NULL)) {
|
|
|
|
uprintf("Could not allocate buffer");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
strcpy(target, path);
|
|
|
|
strcat(target, "\\");
|
|
|
|
strcat(target, name);
|
|
|
|
if (diritem.attributes & 0x10) {
|
|
|
|
// Directory => Create directory
|
|
|
|
if (!CreateDirectoryU(target, 0) && (GetLastError() != ERROR_ALREADY_EXISTS)) {
|
|
|
|
uprintf("Could not create directory '%s': %s\n", target, WindowsErrorString());
|
2017-08-09 15:27:11 +00:00
|
|
|
continue;
|
|
|
|
}
|
2019-04-04 18:12:48 +00:00
|
|
|
if (!DumpFatDir(target, dirpos.cluster))
|
|
|
|
goto out;
|
|
|
|
} else {
|
|
|
|
// Need to figure out if it's a .conf file (Damn you Solus!!)
|
|
|
|
EXTRACT_PROPS props = { 0 };
|
|
|
|
size_t len = strlen(name);
|
|
|
|
props.is_conf = ((len > 4) && (stricmp(&name[len - 5], ".conf") == 0));
|
|
|
|
uprintf("Extracting: %s (from '%s', %s)", target, img_report.efi_img_path,
|
|
|
|
SizeToHumanReadable(diritem.size, FALSE, FALSE));
|
|
|
|
handle = CreateFileU(target, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
|
|
|
|
NULL, CREATE_ALWAYS, diritem.attributes, NULL);
|
2017-08-09 15:27:11 +00:00
|
|
|
if (handle == INVALID_HANDLE_VALUE) {
|
2019-04-27 15:04:47 +00:00
|
|
|
uprintf("Could not create '%s': %s", target, WindowsErrorString());
|
2017-08-09 15:27:11 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
written = 0;
|
2019-04-04 18:12:48 +00:00
|
|
|
s = libfat_clustertosector(lf_fs, dirpos.cluster);
|
|
|
|
while ((s != 0) && (s < 0xFFFFFFFFULL) && (written < diritem.size)) {
|
2019-04-27 15:04:47 +00:00
|
|
|
if (FormatStatus)
|
|
|
|
goto out;
|
2018-03-22 23:14:20 +00:00
|
|
|
buf = libfat_get_sector(lf_fs, s);
|
2019-04-04 18:12:48 +00:00
|
|
|
size = MIN(LIBFAT_SECTOR_SIZE, diritem.size - written);
|
2017-08-09 15:27:11 +00:00
|
|
|
if (!WriteFileWithRetry(handle, buf, size, &size, WRITE_RETRIES) ||
|
2019-04-04 18:12:48 +00:00
|
|
|
(size != MIN(LIBFAT_SECTOR_SIZE, diritem.size - written))) {
|
2019-04-27 15:04:47 +00:00
|
|
|
uprintf("Could not write '%s': %s", target, WindowsErrorString());
|
|
|
|
break;
|
2017-08-09 15:27:11 +00:00
|
|
|
}
|
|
|
|
written += size;
|
2018-03-22 23:14:20 +00:00
|
|
|
s = libfat_nextsector(lf_fs, s);
|
2019-04-04 18:12:48 +00:00
|
|
|
// Trust me, you *REALLY* want to invoke libfat_flush() here
|
|
|
|
libfat_flush(lf_fs);
|
2017-08-09 15:27:11 +00:00
|
|
|
}
|
2019-04-27 15:04:47 +00:00
|
|
|
safe_closehandle(handle);
|
2019-04-04 18:12:48 +00:00
|
|
|
if (props.is_conf)
|
|
|
|
fix_config(target, NULL, NULL, &props);
|
2017-08-09 15:27:11 +00:00
|
|
|
}
|
2019-04-04 18:12:48 +00:00
|
|
|
safe_free(target);
|
|
|
|
safe_free(name);
|
2017-08-09 15:27:11 +00:00
|
|
|
}
|
2019-04-04 18:12:48 +00:00
|
|
|
} while (dirpos.cluster >= 0);
|
|
|
|
ret = TRUE;
|
2017-08-09 15:27:11 +00:00
|
|
|
|
|
|
|
out:
|
2019-04-04 18:12:48 +00:00
|
|
|
if (cluster == 0) {
|
|
|
|
if (lf_fs != NULL) {
|
|
|
|
libfat_close(lf_fs);
|
|
|
|
lf_fs = NULL;
|
|
|
|
}
|
|
|
|
if (p_statbuf != NULL)
|
|
|
|
safe_free(p_statbuf->rr.psz_symlink);
|
|
|
|
safe_free(p_statbuf);
|
|
|
|
safe_free(p_private);
|
|
|
|
if (p_iso != NULL)
|
|
|
|
iso9660_close(p_iso);
|
|
|
|
}
|
2019-04-27 15:04:47 +00:00
|
|
|
safe_closehandle(handle);
|
2019-04-05 21:02:36 +00:00
|
|
|
safe_free(name);
|
|
|
|
safe_free(target);
|
2017-08-09 15:27:11 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-01-14 00:51:41 +00:00
|
|
|
// VirtDisk API Prototypes - Only available for Windows 8 or later
|
|
|
|
PF_TYPE_DECL(WINAPI, DWORD, OpenVirtualDisk, (PVIRTUAL_STORAGE_TYPE, PCWSTR,
|
|
|
|
VIRTUAL_DISK_ACCESS_MASK, OPEN_VIRTUAL_DISK_FLAG, POPEN_VIRTUAL_DISK_PARAMETERS, PHANDLE));
|
|
|
|
PF_TYPE_DECL(WINAPI, DWORD, AttachVirtualDisk, (HANDLE, PSECURITY_DESCRIPTOR,
|
|
|
|
ATTACH_VIRTUAL_DISK_FLAG, ULONG, PATTACH_VIRTUAL_DISK_PARAMETERS, LPOVERLAPPED));
|
|
|
|
PF_TYPE_DECL(WINAPI, DWORD, DetachVirtualDisk, (HANDLE, DETACH_VIRTUAL_DISK_FLAG, ULONG));
|
|
|
|
PF_TYPE_DECL(WINAPI, DWORD, GetVirtualDiskPhysicalPath, (HANDLE, PULONG, PWSTR));
|
|
|
|
|
|
|
|
static char physical_path[128] = "";
|
|
|
|
static HANDLE mounted_handle = INVALID_HANDLE_VALUE;
|
|
|
|
|
|
|
|
char* MountISO(const char* path)
|
|
|
|
{
|
|
|
|
VIRTUAL_STORAGE_TYPE vtype = { 1, VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT };
|
2015-01-16 01:51:24 +00:00
|
|
|
ATTACH_VIRTUAL_DISK_PARAMETERS vparams = {0};
|
2015-01-14 00:51:41 +00:00
|
|
|
DWORD r;
|
|
|
|
wchar_t wtmp[128];
|
|
|
|
ULONG size = ARRAYSIZE(wtmp);
|
|
|
|
wconvert(path);
|
|
|
|
char* ret = NULL;
|
|
|
|
|
|
|
|
PF_INIT_OR_OUT(OpenVirtualDisk, VirtDisk);
|
|
|
|
PF_INIT_OR_OUT(AttachVirtualDisk, VirtDisk);
|
|
|
|
PF_INIT_OR_OUT(GetVirtualDiskPhysicalPath, VirtDisk);
|
|
|
|
|
|
|
|
if ((mounted_handle != NULL) && (mounted_handle != INVALID_HANDLE_VALUE))
|
|
|
|
UnMountISO();
|
|
|
|
|
|
|
|
r = pfOpenVirtualDisk(&vtype, wpath, VIRTUAL_DISK_ACCESS_READ | VIRTUAL_DISK_ACCESS_GET_INFO,
|
|
|
|
OPEN_VIRTUAL_DISK_FLAG_NONE, NULL, &mounted_handle);
|
|
|
|
if (r != ERROR_SUCCESS) {
|
|
|
|
SetLastError(r);
|
|
|
|
uprintf("Could not open ISO '%s': %s", path, WindowsErrorString());
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2015-01-16 01:51:24 +00:00
|
|
|
vparams.Version = ATTACH_VIRTUAL_DISK_VERSION_1;
|
2015-01-14 00:51:41 +00:00
|
|
|
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) {
|
|
|
|
SetLastError(r);
|
|
|
|
uprintf("Could not mount ISO '%s': %s", path, WindowsErrorString());
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = pfGetVirtualDiskPhysicalPath(mounted_handle, &size, wtmp);
|
|
|
|
if (r != ERROR_SUCCESS) {
|
|
|
|
SetLastError(r);
|
|
|
|
uprintf("Could not obtain physical path for mounted ISO '%s': %s", path, WindowsErrorString());
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
wchar_to_utf8_no_alloc(wtmp, physical_path, sizeof(physical_path));
|
|
|
|
ret = physical_path;
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (ret == NULL)
|
|
|
|
UnMountISO();
|
|
|
|
wfree(path);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void UnMountISO(void)
|
|
|
|
{
|
|
|
|
PF_INIT_OR_OUT(DetachVirtualDisk, VirtDisk);
|
|
|
|
|
|
|
|
if ((mounted_handle == NULL) || (mounted_handle == INVALID_HANDLE_VALUE))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
pfDetachVirtualDisk(mounted_handle, DETACH_VIRTUAL_DISK_FLAG_NONE, 0);
|
|
|
|
safe_closehandle(mounted_handle);
|
|
|
|
out:
|
|
|
|
physical_path[0] = 0;
|
|
|
|
}
|