From e9d588a6e05ff08265fa0b7e87ffa9da07ebba37 Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Thu, 12 Oct 2023 17:32:20 +0100 Subject: [PATCH] [iso] add symlink support for target file systems that support it * For now that means only NTFS. And we only do that for ISO-9660/Rock Ridge images. --- src/iso.c | 112 ++++++++++++++++++++++++++++++----------------- src/msapi_utf8.h | 10 +++++ src/rufus.rc | 10 ++--- 3 files changed, 87 insertions(+), 45 deletions(-) diff --git a/src/iso.c b/src/iso.c index ddc16807..ef07807d 100644 --- a/src/iso.c +++ b/src/iso.c @@ -122,7 +122,7 @@ static const char* stupid_antivirus = " NOTE: This is usually caused by a poorl const char* old_c32_name[NB_OLD_C32] = OLD_C32_NAMES; static const int64_t old_c32_threshold[NB_OLD_C32] = OLD_C32_THRESHOLD; static uint8_t joliet_level = 0; -static uint64_t total_blocks, nb_blocks; +static uint64_t total_blocks, extra_blocks, nb_blocks; static BOOL scan_only = FALSE; static StrArray config_path, isolinux_path, modified_path; static char symlinked_syslinux[MAX_PATH]; @@ -311,20 +311,20 @@ static BOOL check_iso_props(const char* psz_dirname, int64_t file_length, const } // Check for PE (XP) specific files in "/i386", "/amd64" or "/minint" - for (i=0; iis_old_c32[i]) img_report.has_old_c32[i] = TRUE; } @@ -706,7 +706,7 @@ static int iso_extract_files(iso9660_t* p_iso, const char *psz_path) HANDLE file_handle = NULL; DWORD buf_size, wr_size, err; EXTRACT_PROPS props; - BOOL is_symlink, is_identical, free_p_statbuf = FALSE; + BOOL is_symlink, is_identical, create_file, free_p_statbuf = FALSE; int length, r = 1; char psz_fullpath[MAX_PATH], *psz_basename = NULL, *psz_sanpath = NULL; char tmp[128], target_path[256]; @@ -798,7 +798,7 @@ static int iso_extract_files(iso9660_t* p_iso, const char *psz_path) static_sprintf(target_path, "%s/%s", psz_path, p_statbuf->rr.psz_symlink); iso9660_stat_t *p_statbuf2 = iso9660_ifs_stat_translate(p_iso, target_path); if (p_statbuf2 != NULL) { - total_blocks += (p_statbuf2->total_size + ISO_BLOCKSIZE - 1) / ISO_BLOCKSIZE; + extra_blocks += (p_statbuf2->total_size + ISO_BLOCKSIZE - 1) / ISO_BLOCKSIZE; iso9660_stat_free(p_statbuf2); } } @@ -821,8 +821,26 @@ static int iso_extract_files(iso9660_t* p_iso, const char *psz_path) psz_sanpath = sanitize_filename(psz_fullpath, &is_identical); if (!is_identical) uprintf(" File name sanitized to '%s'", psz_sanpath); + create_file = TRUE; if (is_symlink) { - if (file_length == 0) { + if (fs_type == FS_NTFS) { + // Replicate symlinks if NTFS is being used + static_sprintf(target_path, "%s/%s", psz_path, p_statbuf->rr.psz_symlink); + iso9660_stat_t* p_statbuf2 = iso9660_ifs_stat_translate(p_iso, target_path); + if (p_statbuf2 != NULL) { + to_windows_path(psz_fullpath); + to_windows_path(p_statbuf->rr.psz_symlink); + uprintf("Symlinking: %s%s âž” %s", psz_fullpath, + (p_statbuf2->type == _STAT_DIR) ? "\\" : "", p_statbuf->rr.psz_symlink); + if (!CreateSymbolicLinkU(psz_fullpath, p_statbuf->rr.psz_symlink, + (p_statbuf2->type == _STAT_DIR) ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0)) + uprintf(" Could not create symlink: %s", WindowsErrorString()); + to_unix_path(p_statbuf->rr.psz_symlink); + to_unix_path(psz_fullpath); + iso9660_stat_free(p_statbuf2); + create_file = FALSE; + } + } else if (file_length == 0) { if ((safe_stricmp(p_statbuf->filename, "syslinux") == 0) && // Special handling for ISOs that have a syslinux → isolinux symbolic link (e.g. Knoppix) (safe_stricmp(p_statbuf->rr.psz_symlink, "isolinux") == 0)) { @@ -846,45 +864,58 @@ static int iso_extract_files(iso9660_t* p_iso, const char *psz_path) goto out; } } else { - // TODO: Ideally, we'd want to create a text file that contains the target link - print_extracted_file(psz_fullpath, file_length); + print_extracted_file(psz_fullpath, safe_strlen(p_statbuf->rr.psz_symlink)); uprintf(" Ignoring Rock Ridge symbolic link to '%s'", p_statbuf->rr.psz_symlink); } + } else { + uuprintf("Unexpected symlink length: %d", file_length); + create_file = FALSE; } } - file_handle = CreatePreallocatedFile(psz_sanpath, GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, file_length); - if (file_handle == INVALID_HANDLE_VALUE) { - err = GetLastError(); - uprintf(" Unable to create file: %s", WindowsErrorString()); - if (((err == ERROR_ACCESS_DENIED) || (err == ERROR_INVALID_HANDLE)) && - (safe_strcmp(&psz_sanpath[3], autorun_name) == 0)) - uprintf(stupid_antivirus); - else - goto out; - } else for (i = 0; file_length > 0; i++) { - if (FormatStatus) goto out; - memset(buf, 0, ISO_BLOCKSIZE); - lsn = p_statbuf->lsn + (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; + if (create_file) { + file_handle = CreatePreallocatedFile(psz_sanpath, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, file_length); + if (file_handle == INVALID_HANDLE_VALUE) { + err = GetLastError(); + uprintf(" Unable to create file: %s", WindowsErrorString()); + if (((err == ERROR_ACCESS_DENIED) || (err == ERROR_INVALID_HANDLE)) && + (safe_strcmp(&psz_sanpath[3], autorun_name) == 0)) + uprintf(stupid_antivirus); + else + goto out; + } else if (is_symlink) { + // Create a text file that contains the target link + ISO_BLOCKING(r = WriteFileWithRetry(file_handle, p_statbuf->rr.psz_symlink, + (DWORD)safe_strlen(p_statbuf->rr.psz_symlink), &wr_size, WRITE_RETRIES)); + if (!r) { + uprintf(" Error writing file: %s", WindowsErrorString()); + goto out; + } + } else for (i = 0; file_length > 0; i++) { + if (FormatStatus) goto out; + memset(buf, 0, ISO_BLOCKSIZE); + lsn = p_statbuf->lsn + (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(file_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; + } + file_length -= ISO_BLOCKSIZE; + if (nb_blocks++ % PROGRESS_THRESHOLD == 0) + UpdateProgressWithInfo(OP_FILE_COPY, MSG_231, nb_blocks, total_blocks + + ((fs_type != FS_NTFS) ? extra_blocks : 0)); } - buf_size = (DWORD)MIN(file_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; + 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()); } - file_length -= ISO_BLOCKSIZE; - if (nb_blocks++ % PROGRESS_THRESHOLD == 0) - UpdateProgressWithInfo(OP_FILE_COPY, MSG_231, nb_blocks, total_blocks); - } - 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()); } if (free_p_statbuf) iso9660_stat_free(p_statbuf); @@ -1014,6 +1045,7 @@ BOOL ExtractISO(const char* src_iso, const char* dest_dir, BOOL scan) uprintf("ISO analysis:"); SendMessage(hMainDialog, UM_PROGRESS_INIT, PBS_MARQUEE, 0); total_blocks = 0; + extra_blocks = 0; has_ldlinux_c32 = FALSE; // String array of all isolinux/syslinux locations StrArrayCreate(&config_path, 8); diff --git a/src/msapi_utf8.h b/src/msapi_utf8.h index 3762ea4e..67f96a24 100644 --- a/src/msapi_utf8.h +++ b/src/msapi_utf8.h @@ -1233,6 +1233,16 @@ static __inline BOOL MoveFileExU(const char* lpExistingFileName, const char* lpN return ret; } +static __inline BOOL CreateSymbolicLinkU(const char* lpSymlinkFileName, const char* lpTargetFileName, DWORD dwFlags) +{ + wconvert(lpSymlinkFileName); + wconvert(lpTargetFileName); + BOOL ret = CreateSymbolicLinkW(wlpSymlinkFileName, wlpTargetFileName, dwFlags); + wfree(lpTargetFileName); + wfree(lpSymlinkFileName); + return ret; +} + // The following expects PropertyBuffer to contain a single Unicode string static __inline BOOL SetupDiGetDeviceRegistryPropertyU(HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, DWORD Property, PDWORD PropertyRegDataType, PBYTE PropertyBuffer, DWORD PropertyBufferSize, PDWORD RequiredSize) diff --git a/src/rufus.rc b/src/rufus.rc index 1e04eef1..62375b1f 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 232, 326 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_ACCEPTFILES -CAPTION "Rufus 4.3.2086" +CAPTION "Rufus 4.3.2087" FONT 9, "Segoe UI Symbol", 400, 0, 0x0 BEGIN LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP @@ -392,8 +392,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,3,2086,0 - PRODUCTVERSION 4,3,2086,0 + FILEVERSION 4,3,2087,0 + PRODUCTVERSION 4,3,2087,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -411,13 +411,13 @@ BEGIN VALUE "Comments", "https://rufus.ie" VALUE "CompanyName", "Akeo Consulting" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "4.3.2086" + VALUE "FileVersion", "4.3.2087" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "© 2011-2023 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" VALUE "OriginalFilename", "rufus-4.3.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "4.3.2086" + VALUE "ProductVersion", "4.3.2087" END END BLOCK "VarFileInfo"