diff --git a/.vs/wimlib.vcxproj b/.vs/wimlib.vcxproj index 5d1b92fe..d82bbb86 100644 --- a/.vs/wimlib.vcxproj +++ b/.vs/wimlib.vcxproj @@ -264,7 +264,7 @@ Level3 Disabled - ..\src;..\src\wimlib;..\src\msvc-missing;%(AdditionalIncludeDirectories) + ..\src;..\src\wimlib;..\src\msvc-missing;..\src\libcdio;%(AdditionalIncludeDirectories) MultiThreadedDebug ProgramDatabase 4018;4146;4267;4244;4334;4789;4996;6201;6239;6246;6255;6262;6297;6326;28252;28253 @@ -286,7 +286,7 @@ Level3 Disabled - ..\src;..\src\wimlib;..\src\msvc-missing;%(AdditionalIncludeDirectories) + ..\src;..\src\wimlib;..\src\msvc-missing;..\src\libcdio;%(AdditionalIncludeDirectories) MultiThreadedDebug ProgramDatabase /std:clatest @@ -305,7 +305,7 @@ Level3 Disabled - ..\src;..\src\wimlib;..\src\msvc-missing;%(AdditionalIncludeDirectories) + ..\src;..\src\wimlib;..\src\msvc-missing;..\src\libcdio;%(AdditionalIncludeDirectories) MultiThreadedDebug ProgramDatabase /std:clatest @@ -321,7 +321,7 @@ Level3 - ..\src;..\src\wimlib;..\src\msvc-missing;%(AdditionalIncludeDirectories) + ..\src;..\src\wimlib;..\src\msvc-missing;..\src\libcdio;%(AdditionalIncludeDirectories) MultiThreaded ProgramDatabase 4018;4146;4267;4244;4334;4789;4996;6201;6239;6246;6255;6262;6297;6326;28252;28253 @@ -341,7 +341,7 @@ MaxSpeed true - ..\src;..\src\wimlib;..\src\msvc-missing;%(AdditionalIncludeDirectories) + ..\src;..\src\wimlib;..\src\msvc-missing;..\src\libcdio;%(AdditionalIncludeDirectories) MultiThreaded ProgramDatabase /std:clatest @@ -358,7 +358,7 @@ MaxSpeed true - ..\src;..\src\wimlib;..\src\msvc-missing;%(AdditionalIncludeDirectories) + ..\src;..\src\wimlib;..\src\msvc-missing;..\src\libcdio;%(AdditionalIncludeDirectories) MultiThreaded ProgramDatabase /std:clatest @@ -372,7 +372,7 @@ Level3 Disabled - ..\src;..\src\wimlib;..\src\msvc-missing;%(AdditionalIncludeDirectories) + ..\src;..\src\wimlib;..\src\msvc-missing;..\src\libcdio;%(AdditionalIncludeDirectories) MultiThreadedDebug ProgramDatabase 4018;4146;4267;4244;4334;4789;4996;6201;6239;6246;6255;6262;6297;6326;28252;28253 @@ -391,7 +391,7 @@ Level3 - ..\src;..\src\wimlib;..\src\msvc-missing;%(AdditionalIncludeDirectories) + ..\src;..\src\wimlib;..\src\msvc-missing;..\src\libcdio;%(AdditionalIncludeDirectories) MultiThreaded ProgramDatabase 4018;4146;4267;4244;4334;4789;4996;6201;6239;6246;6255;6262;6297;6326;28252;28253 diff --git a/src/rufus.c b/src/rufus.c index 20fc7392..4bb0388d 100755 --- a/src/rufus.c +++ b/src/rufus.c @@ -3831,9 +3831,28 @@ extern int TestHashes(void); && (msg.wParam == 'T')) { int r; WIMStruct* wim; - r = wimlib_open_wim(L"C:\\tmp\\boot.wim", WIMLIB_OPEN_FLAG_CHECK_INTEGRITY, &wim); + r = wimlib_open_wim(L"D:\\Incoming\\Win11_24H2_EnglishInternational_x64.iso|sources/boot.wim", WIMLIB_OPEN_FLAG_CHECK_INTEGRITY, &wim); +// r = wimlib_open_wim(L"C:\\tmp\\test.iso|sources/boot.wim", WIMLIB_OPEN_FLAG_CHECK_INTEGRITY, &wim); +// r = wimlib_open_wim(L"C:\\tmp\\boot.wim", WIMLIB_OPEN_FLAG_CHECK_INTEGRITY, &wim); if (r == 0) { + int image; + wchar_t* xml; + size_t xml_size; wimlib_print_header(wim); + if (wimlib_get_xml_data(wim, (void**) & xml, &xml_size) == 0) { + xml[(xml_size / sizeof(wchar_t)) - 1] = L'\0'; + wuprintf(L"%s", xml); + free(xml); + } + image = wimlib_resolve_image(wim, L"1"); + uprintf("image = %d", image); + const wchar_t* fn = L"Windows/win.ini"; + r = wimlib_extract_paths(wim, image, L"C:\\tmp\\wim", &fn, 1, WIMLIB_EXTRACT_FLAG_NORPFIX | + WIMLIB_EXTRACT_FLAG_GLOB_PATHS | WIMLIB_EXTRACT_FLAG_STRICT_GLOB | WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE); + if (r == 0) + uprintf("Successfully extracted '%S'", fn); + else + uprintf("Failed to extract '%S': %d", fn, r); wimlib_free(wim); } else { uprintf("Failed to open WIM: %d", r); diff --git a/src/rufus.rc b/src/rufus.rc index 53993946..0acbacf0 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.8.2237" +CAPTION "Rufus 4.8.2238" FONT 9, "Segoe UI Symbol", 400, 0, 0x0 BEGIN LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP @@ -407,8 +407,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,8,2237,0 - PRODUCTVERSION 4,8,2237,0 + FILEVERSION 4,8,2238,0 + PRODUCTVERSION 4,8,2238,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -426,13 +426,13 @@ BEGIN VALUE "Comments", "https://rufus.ie" VALUE "CompanyName", "Akeo Consulting" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "4.8.2237" + VALUE "FileVersion", "4.8.2238" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "© 2011-2025 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" VALUE "OriginalFilename", "rufus-4.8.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "4.8.2237" + VALUE "ProductVersion", "4.8.2238" END END BLOCK "VarFileInfo" diff --git a/src/wimlib/Makefile.am b/src/wimlib/Makefile.am index e9271d81..6fc7fdb6 100644 --- a/src/wimlib/Makefile.am +++ b/src/wimlib/Makefile.am @@ -8,4 +8,4 @@ libwim_a_SOURCES = avl_tree.c blob_table.c compress.c compress_common.c compress sha1.c solid.c split.c tagged_items.c textfile.c threads.c timestamp.c update_image.c \ util.c wim.c wimboot.c win32_apply.c win32_capture.c win32_common.c win32_replacements.c \ win32_vss.c write.c xml.c xmlproc.c xml_windows.c xpress_compress.c xpress_decompress.c -libwim_a_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/.. -DHAVE_CONFIG_H -D_RUFUS -D__SSE2__ -D_POSIX -D_POSIX_THREAD_SAFE_FUNCTIONS -DUNICODE -D_UNICODE -D__MINGW_USE_VC2005_COMPAT -Wno-undef -Wno-strict-aliasing -Wno-shadow -Wno-incompatible-pointer-types -Wno-sequence-point +libwim_a_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/.. -I$(srcdir)/../libcdio -DHAVE_CONFIG_H -D_RUFUS -D__SSE2__ -D_POSIX -D_POSIX_THREAD_SAFE_FUNCTIONS -DUNICODE -D_UNICODE -D__MINGW_USE_VC2005_COMPAT -Wno-undef -Wno-strict-aliasing -Wno-shadow -Wno-incompatible-pointer-types -Wno-sequence-point diff --git a/src/wimlib/Makefile.in b/src/wimlib/Makefile.in index 919757b1..5ec31881 100644 --- a/src/wimlib/Makefile.in +++ b/src/wimlib/Makefile.in @@ -291,7 +291,7 @@ libwim_a_SOURCES = avl_tree.c blob_table.c compress.c compress_common.c compress util.c wim.c wimboot.c win32_apply.c win32_capture.c win32_common.c win32_replacements.c \ win32_vss.c write.c xml.c xmlproc.c xml_windows.c xpress_compress.c xpress_decompress.c -libwim_a_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/.. -DHAVE_CONFIG_H -D_RUFUS -D__SSE2__ -D_POSIX -D_POSIX_THREAD_SAFE_FUNCTIONS -DUNICODE -D_UNICODE -D__MINGW_USE_VC2005_COMPAT -Wno-undef -Wno-strict-aliasing -Wno-shadow -Wno-incompatible-pointer-types -Wno-sequence-point +libwim_a_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/.. -I$(srcdir)/../libcdio -DHAVE_CONFIG_H -D_RUFUS -D__SSE2__ -D_POSIX -D_POSIX_THREAD_SAFE_FUNCTIONS -DUNICODE -D_UNICODE -D__MINGW_USE_VC2005_COMPAT -Wno-undef -Wno-strict-aliasing -Wno-shadow -Wno-incompatible-pointer-types -Wno-sequence-point all: all-am .SUFFIXES: diff --git a/src/wimlib/file_io.c b/src/wimlib/file_io.c index 5de562ca..fa8872eb 100644 --- a/src/wimlib/file_io.c +++ b/src/wimlib/file_io.c @@ -4,6 +4,7 @@ /* * Copyright (C) 2013 Eric Biggers + * Copyright (C) 2025 Pete Batard * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free @@ -38,6 +39,130 @@ # define pwrite win32_pwrite #endif +#ifdef WITH_LIBCDIO +static int +udf_pread(struct filedes* fd, void* buf, size_t count, off_t offset) +{ + ssize_t ret; + size_t partial_size; + uint64_t file_length; + char _buf[UDF_BLOCKSIZE]; + + if (count == 0) + return 0; + + if (!udf_setpos(fd->p_udf_file, (offset / UDF_BLOCKSIZE) * UDF_BLOCKSIZE)) { + errno = ERANGE; + return WIMLIB_ERR_READ; + } + fd->offset = (offset / UDF_BLOCKSIZE) * UDF_BLOCKSIZE; + + file_length = udf_get_file_length(fd->p_udf_file); + if (offset + count > file_length) + count = file_length - offset; + + if (offset % UDF_BLOCKSIZE) { + partial_size = min(UDF_BLOCKSIZE - offset % UDF_BLOCKSIZE, count); + ret = udf_read_block(fd->p_udf_file, _buf, 1); + if (unlikely(ret <= 0)) { + errno = EINVAL; + return WIMLIB_ERR_READ; + } + memcpy(buf, &_buf[offset % UDF_BLOCKSIZE], partial_size); + buf = _PTR(buf + partial_size); + fd->offset += partial_size; + count -= partial_size; + } + + while (count >= UDF_BLOCKSIZE) { + ret = udf_read_block(fd->p_udf_file, buf, count / UDF_BLOCKSIZE); + if (unlikely(ret <= 0)) { + errno = EINVAL; + return WIMLIB_ERR_READ; + } + buf = _PTR(buf + ret); + fd->offset += ret; + count -= ret; + } + + partial_size = count % UDF_BLOCKSIZE; + if (partial_size) { + ret = udf_read_block(fd->p_udf_file, _buf, 1); + if (unlikely(ret <= 0)) { + errno = EINVAL; + return WIMLIB_ERR_READ; + } + memcpy(buf, _buf, partial_size); + buf = _PTR(buf + partial_size); + fd->offset += partial_size; + } + + return 0; +} + +static int +iso_pread(struct filedes* fd, void* buf, size_t count, off_t offset) +{ + ssize_t ret; + size_t partial_size; + char _buf[ISO_BLOCKSIZE]; + lsn_t lsn_offset = fd->p_iso_file->lsn + offset / ISO_BLOCKSIZE; + + if (count == 0) + return 0; + + if (offset >= fd->p_iso_file->total_size) { + errno = ERANGE; + return WIMLIB_ERR_READ; + } + + if (offset + count > fd->p_iso_file->total_size) + count = fd->p_iso_file->total_size - offset; + + if (offset % ISO_BLOCKSIZE) { + partial_size = min(ISO_BLOCKSIZE - offset % ISO_BLOCKSIZE, count); + ret = iso9660_iso_seek_read(fd->p_iso, _buf, lsn_offset, 1); + if (unlikely(ret <= 0)) { + errno = EINVAL; + return WIMLIB_ERR_READ; + } + lsn_offset += 1; + memcpy(buf, &_buf[offset % ISO_BLOCKSIZE], partial_size); + buf = _PTR(buf + partial_size); + fd->offset += partial_size; + count -= partial_size; + } + + while (count >= ISO_BLOCKSIZE) { + ret = iso9660_iso_seek_read(fd->p_iso, _buf, + lsn_offset % ISO_BLOCKSIZE, 1); + ret = iso9660_iso_seek_read(fd->p_iso, buf, lsn_offset, count / ISO_BLOCKSIZE); + if (unlikely(ret <= 0)) { + errno = EINVAL; + return WIMLIB_ERR_READ; + } + lsn_offset += ret / ISO_BLOCKSIZE; + buf = _PTR(buf + ret); + fd->offset += ret; + count -= ret; + } + + partial_size = count % ISO_BLOCKSIZE; + if (partial_size) { + ret = iso9660_iso_seek_read(fd->p_iso, _buf, lsn_offset, 1); + if (unlikely(ret <= 0)) { + errno = EINVAL; + return WIMLIB_ERR_READ; + } + memcpy(buf, _buf, partial_size); + buf = _PTR(buf + partial_size); + fd->offset += partial_size; + } + + return 0; +} +#endif + /* * Wrapper around read() that checks for errors and keeps retrying until all * requested bytes have been read or until end-of file has occurred. @@ -50,6 +175,13 @@ int full_read(struct filedes *fd, void *buf, size_t count) { +#ifdef WITH_LIBCDIO + if (fd->is_udf) + return udf_pread(fd, buf, count, 0); + else if (fd->is_iso) + return iso_pread(fd, buf, count, 0); +#endif + while (count) { ssize_t ret = read(fd->fd, buf, count); if (unlikely(ret <= 0)) { @@ -117,6 +249,13 @@ full_pread(struct filedes *fd, void *buf, size_t count, off_t offset) if (fd->is_pipe) goto is_pipe; +#ifdef WITH_LIBCDIO + if (fd->is_udf) + return udf_pread(fd, buf, count, offset); + else if (fd->is_iso) + return iso_pread(fd, buf, count, offset); +#endif + while (count) { ssize_t ret = pread(fd->fd, buf, count, offset); if (unlikely(ret <= 0)) { @@ -195,6 +334,13 @@ full_pwrite(struct filedes *fd, const void *buf, size_t count, off_t offset) off_t filedes_seek(struct filedes *fd, off_t offset) { +#ifdef WITH_LIBCDIO + /* No arbitrary seek for ISO files */ + if (fd->is_udf || fd->is_iso) { + errno = ENFILE; + return -1; + } +#endif if (fd->is_pipe) { errno = ESPIPE; return -1; @@ -209,5 +355,10 @@ off_t filedes_seek(struct filedes *fd, off_t offset) bool filedes_is_seekable(struct filedes *fd) { +#ifdef WITH_LIBCDIO + /* No arbitrary seek for ISO files */ + if (fd->is_udf || fd->is_iso) + return false; +#endif return !fd->is_pipe && lseek(fd->fd, 0, SEEK_CUR) != -1; } diff --git a/src/wimlib/wim.c b/src/wimlib/wim.c index afecba9a..f1a5fa5c 100644 --- a/src/wimlib/wim.c +++ b/src/wimlib/wim.c @@ -4,6 +4,7 @@ /* * Copyright 2012-2023 Eric Biggers + * Copyright 2025 Pete Batard * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free @@ -616,11 +617,77 @@ wimlib_register_progress_function(WIMStruct *wim, wim->progctx = progctx; } +#ifdef WITH_LIBCDIO +static int +open_iso_wim_file(const tchar* filename, struct filedes* fd_ret) +{ + int ret = 0; + size_t n; + char *iso_filename, *iso_path = NULL; + udf_dirent_t *p_udf_root; + + /* If the wim path contains a pipe separator, look it up inside an ISO */ + if (tstr_to_utf8(filename, (tstrlen(filename) + 1) * sizeof(tchar), &iso_path, &n)) + return WIMLIB_ERR_NOMEM; + iso_filename = strchr(iso_path, '|'); + if (iso_filename == NULL) { + ret = WIMLIB_ERR_NO_FILENAME; + goto out; + } + + *iso_filename++ = '\0'; + filedes_init(fd_ret, 0); + + /* Try to open as UDF image */ + fd_ret->p_udf = udf_open(iso_path); + if (!fd_ret->p_udf) + goto try_iso; + p_udf_root = udf_get_root(fd_ret->p_udf, true, 0); + if (!p_udf_root) { + ret = WIMLIB_ERR_OPEN; + goto out; + } + fd_ret->p_udf_file = udf_fopen(p_udf_root, iso_filename); + udf_dirent_free(p_udf_root); + if (!fd_ret->p_udf_file) { + ret = WIMLIB_ERR_OPEN; + goto out; + } + fd_ret->is_udf = 1; + goto out; + +try_iso: + /* Try to open as ISO9660 image */ + fd_ret->p_iso = iso9660_open_ext(iso_path, ISO_EXTENSION_ALL); + if (!fd_ret->p_iso) { + ret = WIMLIB_ERR_OPEN; + goto out; + } + fd_ret->p_iso_file = iso9660_ifs_stat_translate(fd_ret->p_iso, iso_filename); + if (!fd_ret->p_iso_file) { + ret = WIMLIB_ERR_OPEN; + goto out; + } + fd_ret->is_iso = 1; + +out: + FREE(iso_path); + /* Because we use an union, make sure fd is cleared on error */ + if (ret) + fd_ret->fd = 0; + return ret; +} +#endif + static int open_wim_file(const tchar *filename, struct filedes *fd_ret) { int raw_fd; +#ifdef WITH_LIBCDIO + if (open_iso_wim_file(filename, fd_ret) == 0) + return 0; +#endif raw_fd = topen(filename, O_RDONLY | O_BINARY); if (raw_fd < 0) { ERROR_WITH_ERRNO("Can't open \"%"TS"\" read-only", filename); @@ -653,6 +720,16 @@ begin_read(WIMStruct *wim, const void *wim_filename_or_fd, int open_flags) return ret; /* The file size is needed for enforcing some limits later. */ +#ifdef WITH_LIBCDIO + if ((wim->in_fd.is_udf | wim->in_fd.is_iso) && + (open_flags & WIMLIB_OPEN_FLAG_WRITE_ACCESS)) + return WIMLIB_ERR_WIM_IS_READONLY; + if (wim->in_fd.is_udf) + wim->file_size = udf_get_file_length(wim->in_fd.p_udf_file); + else if (wim->in_fd.is_iso) + wim->file_size = wim->in_fd.p_iso_file->total_size; + else +#endif if (fstat(wim->in_fd.fd, &stbuf) == 0) wim->file_size = stbuf.st_size; @@ -668,6 +745,12 @@ begin_read(WIMStruct *wim, const void *wim_filename_or_fd, int open_flags) * Warning: in Windows native builds, realpath() calls the * replacement function in win32_replacements.c. */ +#ifdef WITH_LIBCDIO + /* No overwriting for libcdio, so simply duplicate */ + if (tstrchr(wimfile, T('|'))) + wim->filename = tstrdup(wimfile); + else +#endif wim->filename = realpath(wimfile, NULL); if (!wim->filename) { ERROR_WITH_ERRNO("Failed to get full path to file " @@ -900,10 +983,22 @@ wim_decrement_refcnt(WIMStruct *wim) wimlib_assert(wim->refcnt > 0); if (--wim->refcnt != 0) return; - if (filedes_valid(&wim->in_fd)) - filedes_close(&wim->in_fd); - if (filedes_valid(&wim->out_fd)) - filedes_close(&wim->out_fd); +#ifdef WITH_LIBCDIO + if (wim->in_fd.is_udf) { + udf_dirent_free(wim->in_fd.p_udf_file); + udf_close(wim->in_fd.p_udf); + } else if (wim->in_fd.is_iso) { + iso9660_stat_free(wim->in_fd.p_iso_file); + iso9660_close(wim->in_fd.p_iso); + } else { +#endif + if (filedes_valid(&wim->in_fd)) + filedes_close(&wim->in_fd); + if (filedes_valid(&wim->out_fd)) + filedes_close(&wim->out_fd); +#ifdef WITH_LIBCDIO + } +#endif wimlib_free_decompressor(wim->decompressor); xml_free_info_struct(wim->xml_info); FREE(wim->filename); diff --git a/src/wimlib/wimlib/file_io.h b/src/wimlib/wimlib/file_io.h index cd4ee041..f7deef8c 100644 --- a/src/wimlib/wimlib/file_io.h +++ b/src/wimlib/wimlib/file_io.h @@ -6,12 +6,35 @@ #include #include +#ifdef WITH_LIBCDIO +# define DO_NOT_WANT_COMPATIBILITY +# undef PRAGMA_BEGIN_PACKED +# undef PRAGMA_END_PACKED +# include +# include +#endif + /* Wrapper around a file descriptor that keeps track of offset (including in - * pipes, which don't support lseek()) and a cached flag that tells whether the - * file descriptor is a pipe or not. */ + * pipes, which don't support lseek()), allows the handling of files that are + * contained within ISO images, and a cached flag that tells whether the file + * descriptor is a pipe or not. */ struct filedes { - int fd; + union { + int fd; +#ifdef WITH_LIBCDIO + iso9660_t* p_iso; + udf_t* p_udf; +#endif + }; unsigned int is_pipe : 1; +#ifdef WITH_LIBCDIO + unsigned int is_iso : 1; + unsigned int is_udf : 1; + union { + udf_dirent_t* p_udf_file; + iso9660_stat_t* p_iso_file; + }; +#endif off_t offset; }; @@ -39,9 +62,8 @@ filedes_is_seekable(struct filedes *fd); static inline void filedes_init(struct filedes *fd, int raw_fd) { + memset(fd, 0, sizeof(*fd)); fd->fd = raw_fd; - fd->offset = 0; - fd->is_pipe = 0; } static inline void filedes_invalidate(struct filedes *fd)