From da295f258b60e1593de305385c0669eac4b76644 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 19 Aug 2013 19:17:36 -0500 Subject: [PATCH] Generalized support for referencing resources in external WIMs In preparation for supporting "delta" WIMs, this commit generalizes the library interface for handling split WIMs. Instead of cluttering up various functions with additional split WIM parameters, there is now a function wimlib_reference_resource_files() available that can load additional resources (e.g. from a split WIM part) into a WIMStruct that has metadata available (e.g. the first part of a split WIM, or a standalone WIM). wimlib_reference_resource_files() can handle file globbing by itself, so it actually makes it easier for library users to process split WIMs. Lower-level APIs wimlib_reference_resources() and wimlib_unreference_resources() are also provided, but not yet used directly by `wimlib-imagex'. Some instances of WIMLIB_ERR_SPLIT_UNSUPPORTED error returns were changed to the new error code WIMLIB_ERR_METADATA_NOT_FOUND. This commit also re-writes wimlib_export_image() to correctly roll back changes on multi-image exports and better handle resources in the source WIM being missing, as well as add a flag to make all destination image(s) unnamed. --- Makefile.am | 3 +- NEWS | 63 ++--- doc/imagex-apply.1.in | 22 +- doc/imagex-export.1.in | 24 +- doc/imagex-extract.1.in | 6 +- doc/imagex-mount.1.in | 39 ++- include/wimlib.h | 512 +++++++++++++++++++++------------- include/wimlib/glob.h | 35 +++ include/wimlib/lookup_table.h | 3 + include/wimlib/swm.h | 17 -- include/wimlib/wim.h | 16 ++ include/wimlib_tchar.h | 4 +- programs/imagex-win32.c | 108 ------- programs/imagex-win32.h | 34 --- programs/imagex.c | 322 ++++++++++----------- src/dentry.c | 6 +- src/export_image.c | 364 ++++++++++++------------ src/extract.c | 43 +-- src/integrity.c | 3 - src/join.c | 117 +++++++- src/lookup_table.c | 246 +++++++++++++++- src/mount_image.c | 38 +-- src/split.c | 4 +- src/swm.c | 194 ------------- src/util.c | 4 + src/wim.c | 30 +- src/win32_apply.c | 2 +- src/win32_capture.c | 2 +- src/win32_replacements.c | 106 +++++++ src/write.c | 10 +- src/xml.c | 20 +- 31 files changed, 1321 insertions(+), 1076 deletions(-) create mode 100644 include/wimlib/glob.h delete mode 100644 include/wimlib/swm.h delete mode 100644 src/swm.c diff --git a/Makefile.am b/Makefile.am index 6d24bdf1..d2e80a2f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -45,7 +45,6 @@ libwim_la_SOURCES = \ src/security.c \ src/sha1.c \ src/split.c \ - src/swm.c \ src/reparse.c \ src/timestamp.c \ src/update_image.c \ @@ -68,6 +67,7 @@ libwim_la_SOURCES = \ include/wimlib/endianness.h \ include/wimlib/error.h \ include/wimlib/file_io.h \ + include/wimlib/glob.h \ include/wimlib/header.h \ include/wimlib/integrity.h \ include/wimlib/list.h \ @@ -80,7 +80,6 @@ libwim_la_SOURCES = \ include/wimlib/resource.h \ include/wimlib/security.h \ include/wimlib/sha1.h \ - include/wimlib/swm.h \ include/wimlib/timestamp.h \ include/wimlib/types.h \ include/wimlib/util.h \ diff --git a/NEWS b/NEWS index c31cea5c..783e0256 100644 --- a/NEWS +++ b/NEWS @@ -5,11 +5,18 @@ Version 1.5.0: to standard output and applying images from standard input, but they are not compatible with Microsoft's software and are not created by default. See the documentation for --pipable flag of `wimlib-imagex capture' for - more information. Two new functions have been added to the library to - fully support this: wimlib_write_to_fd() and - wimlib_extract_image_from_pipe(). + more information. - wimlib now preserve WIM integrity tables by default, even if + To better support incremental backups, added support for declaring an + image as a modified form of a prior image. See the documentation for + the '--delta-from' option of `wimlib-imagex append'. + + The library support for managing split WIMs has been changed to support + other arrangements, such as delta WIMs, and be easier to use. This + change is visible in `wimlib-imagex', which also can now accept the + '--ref' option multiple times. + + wimlib now preserves WIM integrity tables by default, even if WIMLIB_WRITE_FLAG_CHECK_INTEGRITY is not specified. This changes the behavior of `wimlib-imagex' whenever the WIM being operated on contains an integrity table and the '--check' option is not specified. @@ -19,52 +26,30 @@ Version 1.5.0: ratio by default, which is usually what is desired, at a cost of some speed. - For convenience, `wimlib-imagex' now supports being invoked as - wimCOMMAND, where COMMAND is the command as in `wimlib-imagex COMMAND'; - for example, it can be invoked as `wimapply' as an alternative to - `wimlib-imagex apply'. The appropriate hard links are created in UNIX - installations of `wimlib-imagex', while for the Windows distribution of - `wimlib-imagex', batch files that emulate this behavior are generated. - - `wimlib-imagex' no longer recognizes the 'mount', 'mountrw', and - 'unmount' commands on Windows, since they didn't work on Windows anyway. + `wimlib-imagex' now supports being invoked as wimCOMMAND, where COMMAND + is the command as in `wimlib-imagex COMMAND'; for example, it can be + invoked as `wimapply' as an alternative to `wimlib-imagex apply'. The + appropriate hard links are created in UNIX installations of + `wimlib-imagex', while for the Windows distribution of `wimlib-imagex', + batch files that emulate this behavior are generated. Security descriptors are now extracted correctly on Windows. - `mkwinpeimg' now supports grabbing files from the WAIK supplement rather - than the WAIK itself. - - The test suite no longer fails when run in a locale where the decimal - separator is not a period. - - From the library, WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY has been removed - and WIMLIB_EXTRACT_FLAG_VERBOSE re-reserved for future use. Also, - WIMLIB_PROGRESS_MSG_JOIN_STREAMS has been removed, but - WIMLIB_PROGRESS_MSG_WRITE_STREAMS will be received instead and now - provides WIM part numbers. + Fixed archiving DOS names in NTFS-3g capture mode. The extraction code has been rewritten and it will now be easier to support new features on all supported backends (currently Win32, UNIX, and NTFS-3g). For example, hard-linked extraction mode (--hardlink) is now supported on all backends, not just UNIX. - The LZX compression and decompression code now compiles correctly on the - ARM architecture (where 'char' is unsigned). - - wimlib_split() progress messages now report the total number of parts - being written. - - Fixed storing DOS names in NTFS-3g capture mode. - - A few changes were made to the error codes returned by library routines. + `mkwinpeimg' now supports grabbing files from the WAIK supplement rather + than the WAIK itself. - To make wimlib easier to use on Windows, wimlib_global_init() now - automatically attempts to acquire additional privileges on Windows, so - library clients need not do this (although they can provide a flag to - get the old behavior and manage privileges themselves). + wimlib_global_init() now, by default, attempts to acquire additional + privileges on Windows, so library clients need not do this. - This update bumps the shared library version number up to 9, since it - doesn't quite maintain binary compatibility with previous releases. + This update bumps the shared library version number up to 9, since it is + not binary compatibible with previous releases. Version 1.4.2: Fixed bug in `wimlib-imagex export' that made it impossible to export an diff --git a/doc/imagex-apply.1.in b/doc/imagex-apply.1.in index 1166a6a6..0c536df2 100644 --- a/doc/imagex-apply.1.in +++ b/doc/imagex-apply.1.in @@ -235,16 +235,11 @@ Files with full paths over 260 characters (MAX_PATH) are extracted by using the most Windows software and may not be able to be deleted easily. .SH SPLIT WIMS You may use \fB@IMAGEX_PROGNAME@ apply\fR to apply images from a split WIM. The -\fIWIMFILE\fR argument is used to specify the first part of the split WIM, and -the \fB--refs\fR="\fIGLOB\fR" option is used to provide a shell-style file glob -that specifies the additional parts of the split WIM. \fIGLOB\fR is expected to -be a single string on the command line, so \fIGLOB\fR must be quoted so that it -is protected against shell expansion. \fIGLOB\fR must expand to all parts of -the split WIM, except optionally the first part which may either omitted or -included in the glob (but the first part MUST be specified as \fIWIMFILE\fR as -well). -.PP -Here's an example. The names for the split WIMs usually go something like: +\fIWIMFILE\fR argument must specify the first part of the split WIM, while the +additional parts of the split WIM must be specified in one or more +\fB--ref\fR="\fIGLOB\fR" options. Since globbing is built into the \fB--ref\fR +option, typically only one \fB--ref\fR option is necessary. For example, the +names for the split WIM parts usually go something like: .RS .PP .nf @@ -294,8 +289,11 @@ When reading \fIWIMFILE\fR, verify its integrity if the integrity table is present. .TP \fB--ref\fR="\fIGLOB\fR" -File glob of additional split WIM parts that are part of the split WIM being -applied. See \fBSPLIT_WIMS\fR. +File glob of additional WIMs or split WIM parts to reference resources from. +See \fBSPLIT_WIMS\fR. This option can be specified multiple times. Note: +\fIGLOB\fR is listed in quotes because it is interpreted by +\fB@IMAGEX_PROGNAME@\fR and may need to be quoted to protect against shell +expansion. .TP \fB--rpfix\fR, \fB--norpfix\fR Set whether to fix targets of absolute symbolic links (reparse points in Windows diff --git a/doc/imagex-export.1.in b/doc/imagex-export.1.in index 34b63eab..f1d7e3e8 100644 --- a/doc/imagex-export.1.in +++ b/doc/imagex-export.1.in @@ -88,8 +88,11 @@ little bit of space that would otherwise be left as a hole in the WIM. Also see \fB@IMAGEX_PROGNAME@ optimize\fR. .TP \fB--ref\fR="\fIGLOB\fR" -File glob of additional split WIM parts that are part of the split WIM being -exported. See \fBSPLIT_WIMS\fR. +File glob of additional WIMs or split WIM parts to reference resources from. +See \fBSPLIT_WIMS\fR. This option can be specified multiple times. Note: +\fIGLOB\fR is listed in quotes because it is interpreted by +\fB@IMAGEX_PROGNAME@\fR and may need to be quoted to protect against shell +expansion. .TP \fB--pipable\fR Build, or rebuild, \fIDEST_WIMFILE\fR as a "pipable WIM" so that it can be @@ -103,17 +106,12 @@ Build, or rebuld, \fIDEST_WIMFILE\fR as a normal, non-pipable WIM. This is the default behavior, unless \fIDEST_WIMFILE\fR already existed and was already pipable, or if \fIDEST_WIMFILE\fR was "-" (standard output). .SH SPLIT WIMS -You may use \fB@IMAGEX_PROGNAME@ export\fR to export images from a split WIM. The -\fISRC_WIMFILE\fR argument is used to specify the first part of the split WIM, and -the \fB--refs\fR="\fIGLOB\fR" option is used to provide a shell-style file glob -that specifies the additional parts of the split WIM. \fIGLOB\fR is expected to -be a single string on the command line, so \fIGLOB\fR must be quoted so that it -is protected against shell expansion. \fIGLOB\fR must expand to all parts of -the split WIM, except optionally the first part which may either omitted or -included in the glob (but the first part MUST be specified as \fISRC_WIMFILE\fR as -well). -.PP -Here's an example. The names for the split WIMs usually go something like: +You may use \fB@IMAGEX_PROGNAME@ export\fR to export images from a split WIM. +The \fISRC_WIMFILE\fR argument must specify the first part of the split WIM, +while the additional parts of the split WIM must be specified in one or more +\fB--ref\fR="\fIGLOB\fR" options. Since globbing is built into the \fB--ref\fR +option, typically only one \fB--ref\fR option is necessary. For example, the +names for the split WIM parts usually go something like: .PP .RS .nf diff --git a/doc/imagex-extract.1.in b/doc/imagex-extract.1.in index 40e29763..55155a83 100644 --- a/doc/imagex-extract.1.in +++ b/doc/imagex-extract.1.in @@ -51,8 +51,10 @@ When reading \fIWIMFILE\fR, verify its integrity if an integrity table is present. .TP \fB--ref\fR="\fIGLOB\fR" -File glob of additional split WIM parts that are part of the split WIM. See -\fBSPLIT_WIMS\fR. +File glob of additional WIMs or split WIM parts to reference resources from. +See \fBSPLIT_WIMS\fR. Note: since \fIGLOB\fR is listed in quotes because it is +interpreted by \fB@IMAGEX_PROGNAME@\fR and may need to be quoted to protect +against shell expansion. .TP \fB--verbose\fR This option no longer does anything but is reserved for future use. diff --git a/doc/imagex-mount.1.in b/doc/imagex-mount.1.in index 33102ff9..f2a50e63 100644 --- a/doc/imagex-mount.1.in +++ b/doc/imagex-mount.1.in @@ -25,21 +25,16 @@ The WIM image can be unmounted using the \fB@IMAGEX_PROGNAME@ unmount\fR command made to a WIM mounted read-write will be discarded unless the \fB--commit\fR flag is provided to \fB@IMAGEX_PROGNAME@ unmount\fR. .SH SPLIT WIMS -You may use \fB@IMAGEX_PROGNAME@ mount\fR to mount an image from a split WIM read-only. -However, you may not mount an image from a split WIM read-write. +You may use \fB@IMAGEX_PROGNAME@ mount\fR to mount an image from a split WIM +read-only. However, you may not mount an image from a split WIM read-write. .PP -The \fIWIMFILE\fR argument is used to specify the first part of the split WIM, and -the \fB--refs\fR="\fIGLOB\fR" option is used to provide a shell-style file glob -that specifies the additional parts of the split WIM. \fIGLOB\fR is expected to -be a single string on the command line, so \fIGLOB\fR must be quoted so that it -is protected against shell expansion. \fIGLOB\fR must expand to all parts of -the split WIM, except optionally the first part which may either omitted or -included in the glob (but the first part MUST be specified as \fIWIMFILE\fR as -well). +The \fIWIMFILE\fR argument must specify the first part of the split WIM, while +the additional parts of the split WIM must be specified in one or more +\fB--ref\fR="\fIGLOB\fR" options. Since globbing is built into the \fB--ref\fR +option, typically only one \fB--ref\fR option is necessary. For example, the +names for the split WIM parts usually go something like: .PP -Here's an example. The names for the split WIMs usually go something like: .RS -.PP .nf mywim.swm mywim2.swm @@ -48,7 +43,7 @@ mywim4.swm mywim5.swm .RE .PP -To mount the first image of this split WIM to the directory "dir", we would do: +To mount the first image of this split WIM to the directory "dir", run: .PP .RS @IMAGEX_PROGNAME@ mount mywim.swm 1 dir --ref="mywim*.swm" @@ -58,7 +53,7 @@ To mount the first image of this split WIM to the directory "dir", we would do: If wimlib was configured using the \fB--without-fuse\fR flag, then the \fB@IMAGEX_PROGNAME@ mount\fR, \fB@IMAGEX_PROGNAME@ mountrw\fR, and \fB@IMAGEX_PROGNAME@ unmount\fR commands will not work. Also, these commands -are not available in the Windows builds of wimlib. +are not available in the Windows builds of \fB@IMAGEX_PROGNAME@\fR. .PP You can mount multiple images from a WIM file read-only at the same time, but you can only mount one image at a time from a WIM read-write. @@ -93,7 +88,8 @@ If "xattr" (default), named data streams will be accessible through extended file attributes, unless this support was disabled when compiling wimlib. The named data streams may be accessed through extended attributes named "user.*", where the * is the name of the named data stream. See \fBsetfattr\fR (1) and -\fBgetfattr\fR (1). +\fBgetfattr\fR (1). Note that this is not an ideal interface, since named data +streams may be larger than the maximum allowed extended attribute size. .IP "" If "windows", the named data streams will be accessible by specifying the filename, then a colon, then the name of the named data stream; for example, @@ -109,9 +105,11 @@ Turn on debugging information printed by the FUSE library, and do not fork into the background. .TP \fB--ref\fR="\fIGLOB\fR" -File glob of additional split WIM parts that are part of the split WIM being -mounted. This option is valid for \fB@IMAGEX_PROGNAME@ mount\fR but not \fB@IMAGEX_PROGNAME@ -mountrw\fR. See \fBSPLIT_WIMS\fR. +File glob of additional WIMs or split WIM parts to reference resources from. +See \fBSPLIT_WIMS\fR. This option can be specified multiple times. Note: +\fIGLOB\fR is listed in quotes because it is interpreted by +\fB@IMAGEX_PROGNAME@\fR and may need to be quoted to protect against shell +expansion. .TP \fB--staging-dir\fR=\fIDIR\fR Store temporary staging files in a subdirectory of the directory \fIDIR\fR. @@ -168,9 +166,6 @@ the temporary staging directory is deleted. .PP \fB@IMAGEX_PROGNAME@ unmount\fR runs in a separate process from the process that previously ran \fB@IMAGEX_PROGNAME@ mount\fR, and these two processes communicate using POSIX message -queues. See \fIsrc/mount_image.c\fR in the sources for details. Note: As of -wimlib v1.2.1, \fB@IMAGEX_PROGNAME@ unmount\fR correctly fails with an error within a -reasonable amount of time (1 second) if the filesystem daemon is abnormally -terminated. +queues. See \fIsrc/mount_image.c\fR in the sources for details. .SH SEE ALSO .BR @IMAGEX_PROGNAME@ (1) diff --git a/include/wimlib.h b/include/wimlib.h index 40d062aa..197f9b65 100644 --- a/include/wimlib.h +++ b/include/wimlib.h @@ -900,6 +900,14 @@ typedef int (*wimlib_iterate_dir_tree_callback_t)(const struct wimlib_dir_entry typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resource_entry *resource, void *user_ctx); +/** + * @name Directory tree iteration flags + * + * The following flags can be passed to wimlib_iterate_dir_tree(). + * + * @{ + */ + /** For wimlib_iterate_dir_tree(): Iterate recursively on children rather than * just on the specified path. */ #define WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE 0x00000001 @@ -910,9 +918,15 @@ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resour -/***************************** - * WIMLIB_ADD_FLAG_* - *****************************/ +/** + * @name Add flags + * + * The following flags can be passed to wimlib_add_image(), + * wimlib_add_image_multisource(), and add commands passed to + * wimlib_update_image(). + * + * @{ + */ /** Directly capture a NTFS volume rather than a generic directory. This flag * cannot be combined with ::WIMLIB_ADD_FLAG_DEREFERENCE or @@ -1011,9 +1025,14 @@ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resour WIMLIB_ADD_FLAG_NO_UNSUPPORTED_EXCLUDE #define WIMLIB_ADD_IMAGE_FLAG_WINCONFIG WIMLIB_ADD_FLAG_WINCONFIG -/****************************** - * WIMLIB_DELETE_FLAG_* - ******************************/ +/** + * @name Delete flags + * + * The following flags can be specified in delete commands passed to + * wimlib_update_image(). + * + * @{ + */ /** Do not issue an error if the path to delete does not exist. */ #define WIMLIB_DELETE_FLAG_FORCE 0x00000001 @@ -1022,16 +1041,38 @@ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resour * issued if the path to delete is a directory. */ #define WIMLIB_DELETE_FLAG_RECURSIVE 0x00000002 -/****************************** - * WIMLIB_EXPORT_FLAG_* - ******************************/ +/** + * @name Export flags + * + * The following flags can be passed to wimlib_export_image(). + * + * @{ + */ -/** See documentation for wimlib_export_image(). */ +/** + * If a single image is being exported, mark it bootable in the destination WIM. + * Alternatively, if ::WIMLIB_ALL_IMAGES is specified as the image to export, + * the image in the source WIM (if any) that is marked as bootable is also + * marked as bootable in the destination WIM. + */ #define WIMLIB_EXPORT_FLAG_BOOT 0x00000001 -/****************************** - * WIMLIB_EXTRACT_FLAG_* - ******************************/ +/** Give the exported image(s) no names. Avoids problems with image name + * collisions. + */ +#define WIMLIB_EXPORT_FLAG_NO_NAMES 0x00000002 + +/** Give the exported image(s) no descriptions. */ +#define WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS 0x00000004 + +/** + * @name Extract flags + * + * The following flags can be passed to wimlib_extract_image(), + * wimlib_extract_files(), and wimlib_extract_image_from_pipe(). + * + * @{ + */ /** Extract the image directly to a NTFS volume rather than a generic directory. * This mode is only available if wimlib was compiled with libntfs-3g support; @@ -1117,9 +1158,13 @@ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resour * behavior is currently less than satisfactory. Do not use (yet). */ #define WIMLIB_EXTRACT_FLAG_RESUME 0x00010000 -/****************************** - * WIMLIB_MOUNT_FLAG_* - ******************************/ +/** + * @name Mount flags + * + * The following flags can be passed to wimlib_mount_image(). + * + * @{ + */ /** Mount the WIM image read-write rather than the default of read-only. */ #define WIMLIB_MOUNT_FLAG_READWRITE 0x00000001 @@ -1146,9 +1191,13 @@ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resour * allow_other option to FUSE mount) */ #define WIMLIB_MOUNT_FLAG_ALLOW_OTHER 0x00000040 -/****************************** - * WIMLIB_OPEN_FLAG_* - ******************************/ +/** + * @name Open flags + * + * The following flags can be passed to wimlib_open_wim() and several other + * functions such as wimlib_join(). + * @{ + */ /** Verify the WIM contents against the WIM's integrity table, if present. This * causes the raw data of the WIM file, divided into 10 MB chunks, to be @@ -1171,9 +1220,12 @@ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resour * an error sooner rather than later. */ #define WIMLIB_OPEN_FLAG_WRITE_ACCESS 0x00000004 -/****************************** - * WIMLIB_UNMOUNT_FLAG_* - ******************************/ +/** + * @name Unmount flags + * + * The following flags can be passed to wimlib_unmount_image(). + * @{ + */ /** See ::WIMLIB_WRITE_FLAG_CHECK_INTEGRITY. */ #define WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY 0x00000001 @@ -1191,17 +1243,24 @@ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resour /** Do a "lazy" unmount (detach filesystem immediately, even if busy). */ #define WIMLIB_UNMOUNT_FLAG_LAZY 0x00000010 -/****************************** - * WIMLIB_UPDATE_FLAG_* - ******************************/ +/** + * @name Update flags + * + * The following flags can be passed to wimlib_update_image(). + * @{ + */ /** Send ::WIMLIB_PROGRESS_MSG_UPDATE_BEGIN_COMMAND and * ::WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND messages. */ #define WIMLIB_UPDATE_FLAG_SEND_PROGRESS 0x00000001 -/****************************** - * WIMLIB_WRITE_FLAG_* - ******************************/ +/** + * @name Write flags + * + * The following flags can be passed to wimlib_write(), wimlib_overwrite(), + * wimlib_write_to_fd(), and several other functions such as wimlib_join(). + * @{ + */ /** Include an integrity table in the WIM. * @@ -1260,9 +1319,11 @@ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resour * set the readonly flag on the on-disk WIM file. */ #define WIMLIB_WRITE_FLAG_IGNORE_READONLY_FLAG 0x00000100 -/****************************** - * WIMLIB_INIT_FLAG_* - ******************************/ +/** + * @name Init flags + * + * The following flags can be passed to wimlib_global_init(). + */ /** Assume that strings are represented in UTF-8, even if this is not the * locale's character encoding. This flag is ignored on Windows, where wimlib @@ -1292,6 +1353,29 @@ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resour */ #define WIMLIB_INIT_FLAG_STRICT_APPLY_PRIVILEGES 0x00000008 +/** + * @name Resource reference flags + * + * The following flags can be passed to wimlib_reference_resource_files() and + * wimlib_reference_resources(). + */ + +/** wimlib_reference_resource_files() only: Enable shell-style filename + * globbing. */ +#define WIMLIB_REF_FLAG_GLOB_ENABLE 0x00000001 + +/** wimlib_reference_resource_files() only: Issue an error + * (::WIMLIB_ERR_GLOB_HAD_NO_MATCHES) if a glob did not match any files. The + * default behavior without this flag is to issue no error at that point, but + * then attempt to open the glob as a literal path, which of course will fail + * anyway if no file exists at that path. No effect if + * ::WIMLIB_REF_FLAG_GLOB_ENABLE is not also specified. */ +#define WIMLIB_REF_FLAG_GLOB_ERR_ON_NOMATCH 0x00000002 + +/** + * @} + */ + /** Specification of an update to perform on a WIM image. */ struct wimlib_update_command { @@ -1381,6 +1465,7 @@ enum wimlib_error_code { WIMLIB_ERR_FORK, WIMLIB_ERR_FUSE, WIMLIB_ERR_FUSERMOUNT, + WIMLIB_ERR_GLOB_HAD_NO_MATCHES, WIMLIB_ERR_ICONV_NOT_AVAILABLE, WIMLIB_ERR_IMAGE_COUNT, WIMLIB_ERR_IMAGE_NAME_COLLISION, @@ -1401,13 +1486,13 @@ enum wimlib_error_code { WIMLIB_ERR_INVALID_PIPABLE_WIM, WIMLIB_ERR_INVALID_REPARSE_DATA, WIMLIB_ERR_INVALID_RESOURCE_HASH, - WIMLIB_ERR_INVALID_SECURITY_DATA, WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE, WIMLIB_ERR_INVALID_UTF16_STRING, WIMLIB_ERR_INVALID_UTF8_STRING, WIMLIB_ERR_IS_DIRECTORY, WIMLIB_ERR_LIBXML_UTF16_HANDLER_NOT_AVAILABLE, WIMLIB_ERR_LINK, + WIMLIB_ERR_METADATA_NOT_FOUND, WIMLIB_ERR_MKDIR, WIMLIB_ERR_MQUEUE, WIMLIB_ERR_NOMEM, @@ -1628,10 +1713,11 @@ wimlib_create_new_wim(int ctype, WIMStruct **wim_ret); * flag. * * This function can additionally return ::WIMLIB_ERR_DECOMPRESSION, - * ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_NOMEM, - * ::WIMLIB_ERR_READ, or ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE, all of which - * indicate failure (for different reasons) to read the metadata resource for an - * image that needed to be deleted. + * ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_METADATA_NOT_FOUND, + * ::WIMLIB_ERR_NOMEM, ::WIMLIB_ERR_READ, or + * ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE, all of which indicate failure (for + * different reasons) to read the metadata resource for an image that needed to + * be deleted. */ extern int wimlib_delete_image(WIMStruct *wim, int image); @@ -1658,76 +1744,63 @@ wimlib_delete_image(WIMStruct *wim, int image); * Pointer to the ::WIMStruct for a stand-alone WIM or part 1 of a split * WIM that contains the image(s) being exported. * @param src_image - * The image to export from @p src_wim. Can be the number of an image, or - * ::WIMLIB_ALL_IMAGES to export all images. + * The image to export from @p src_wim, as either a 1-based image index to + * export a single image, or ::WIMLIB_ALL_IMAGES to export all images. * @param dest_wim - * Pointer to the ::WIMStruct for a WIM file that will receive the images - * being exported. + * Pointer to the ::WIMStruct for a WIM that will receive the images being + * exported. * @param dest_name - * The name to give the exported image in the new WIM file. If left @c - * NULL, the name from @p src_wim is used. This parameter must be left @c - * NULL if @p src_image is ::WIMLIB_ALL_IMAGES and @p src_wim contains more - * than one image; in that case, the names are all taken from the @p - * src_wim. (This is allowed even if one or more images being exported has - * no name.) + * For single-image exports, the name to give the exported image in @p + * dest_wim. If left @c NULL, the name from @p src_wim is used. For + * ::WIMLIB_ALL_IMAGES exports, this parameter must be left @c NULL; in + * that case, the names are all taken from @p src_wim. This parameter is + * overridden by ::WIMLIB_EXPORT_FLAG_NO_NAMES. * @param dest_description - * The description to give the exported image in the new WIM file. If left - * @c NULL, the description from the @p src_wim is used. This parameter must - * be left @c NULL if @p src_image is ::WIMLIB_ALL_IMAGES and @p src_wim contains - * more than one image; in that case, the descriptions are all taken from - * @p src_wim. (This is allowed even if one or more images being exported - * has no description.) + * For single-image exports, the description to give the exported image in + * the new WIM file. If left @c NULL, the description from @p src_wim is + * used. For ::WIMLIB_ALL_IMAGES exports, this parameter must be left @c + * NULL; in that case, the description are all taken from @p src_wim. This + * parameter is overridden by ::WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS. * @param export_flags - * ::WIMLIB_EXPORT_FLAG_BOOT if the image being exported is to be made - * bootable, or 0 if which image is marked as bootable in the destination - * WIM is to be left unchanged. If @p src_image is ::WIMLIB_ALL_IMAGES and - * there are multiple images in @p src_wim, specifying - * ::WIMLIB_EXPORT_FLAG_BOOT is valid only if one of the exported images is - * currently marked as bootable in @p src_wim; if that is the case, then - * that image is marked as bootable in the destination WIM. - * @param additional_swms - * Array of pointers to the ::WIMStruct for each additional part in the - * split WIM. Ignored if @p num_additional_swms is 0. The pointers do not - * need to be in any particular order, but they must include all parts of - * the split WIM other than the first part, which must be provided in the - * @p wim parameter. - * @param num_additional_swms - * Number of additional WIM parts provided in the @p additional_swms array. - * This number should be one less than the total number of parts in the - * split WIM. Set to 0 if the WIM is a standalone WIM. + * Bitwise OR of flags prefixed with WIMLIB_EXPORT_FLAG. * @param progress_func - * If non-NULL, a function that will be called periodically with the - * progress of the current operation. + * Currently ignored, but reserved for a function that will be called with + * information about the operation. Use NULL if no additional information + * is desired. * * @return 0 on success; nonzero on error. * @retval ::WIMLIB_ERR_IMAGE_NAME_COLLISION * One or more of the names being given to an exported image was already in * use in the destination WIM. * @retval ::WIMLIB_ERR_INVALID_IMAGE - * @p src_image does not exist in @p src_wim. + * @p src_image does not exist in @p src_wim and was not + * ::WIMLIB_ALL_IMAGES. * @retval ::WIMLIB_ERR_INVALID_PARAM - * ::WIMLIB_EXPORT_FLAG_BOOT was specified in @p flags, @p src_image was - * ::WIMLIB_ALL_IMAGES, @p src_wim contains multiple images, and no images in - * @p src_wim are marked as bootable; or @p dest_name and/or @p - * dest_description were non-NULL, @p src_image was - * ::WIMLIB_ALL_IMAGES, and @p src_wim contains multiple images. + * @p src_wim and/or @p dest_wim were @c NULL; or @p src_image was + * ::WIMLIB_ALL_IMAGES but @p dest_name and/or @p dest_description were not + * @c NULL. + * @retval ::WIMLIB_ERR_METADATA_NOT_FOUND + * Either @p src_wim or @p dest_wim did not contain metadata resources; for + * example, one of them was a non-first part of a split WIM. * @retval ::WIMLIB_ERR_NOMEM * Failed to allocate needed memory. - * @retval ::WIMLIB_ERR_SPLIT_INVALID - * The source WIM is a split WIM, but the parts specified do not form a - * complete split WIM because they do not include all the parts of the - * split WIM, there are duplicate parts, or not all the parts are in fact - * from the same split WIM. + * @retval ::WIMLIB_ERR_RESOURCE_NOT_FOUND + * A resource that needed to be exported could not be found in either the + * source or destination WIMs. This error can occur if, for example, @p + * src_wim is part of a split WIM but resources from the other split WIM + * parts were not referenced with wimlib_reference_resources() or + * wimlib_reference_resource_files(). * @retval ::WIMLIB_ERR_WIM_IS_READONLY * @p dest_wim is considered read-only because of any of the reasons * mentioned in the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS * flag. * * This function can additionally return ::WIMLIB_ERR_DECOMPRESSION, - * ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_NOMEM, - * ::WIMLIB_ERR_READ, or ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE, all of which - * indicate failure (for different reasons) to read the metadata resource for an - * image in @p src_wim that needed to be exported. + * ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_METADATA_NOT_FOUND, + * ::WIMLIB_ERR_NOMEM, ::WIMLIB_ERR_READ, or + * ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE, all of which indicate failure (for + * different reasons) to read the metadata resource for an image in @p src_wim + * that needed to be exported. */ extern int wimlib_export_image(WIMStruct *src_wim, int src_image, @@ -1735,8 +1808,6 @@ wimlib_export_image(WIMStruct *src_wim, int src_image, const wimlib_tchar *dest_name, const wimlib_tchar *dest_description, int export_flags, - WIMStruct **additional_swms, - unsigned num_additional_swms, wimlib_progress_func_t progress_func); /** @@ -1766,18 +1837,6 @@ wimlib_export_image(WIMStruct *src_wim, int src_image, * been specified in the ::wimlib_extract_command.extract_flags member in * each extraction command, in combination with any flags already present. * - * @param additional_swms - * Array of pointers to the ::WIMStruct for each additional part in the - * split WIM. Ignored if @p num_additional_swms is 0. The pointers do not - * need to be in any particular order, but they must include all parts of - * the split WIM other than the first part, which must be provided in the - * @p wim parameter. - * - * @param num_additional_swms - * Number of additional WIM parts provided in the @p additional_swms array. - * This number should be one less than the total number of parts in the - * split WIM. Set to 0 if the WIM is a standalone WIM. - * * @param progress_func * If non-NULL, a function that will be called periodically with the * progress of the current operation. @@ -1810,8 +1869,6 @@ wimlib_extract_files(WIMStruct *wim, const struct wimlib_extract_command *cmds, size_t num_cmds, int default_extract_flags, - WIMStruct **additional_swms, - unsigned num_additional_swms, wimlib_progress_func_t progress_func); /** @@ -1844,25 +1901,13 @@ wimlib_extract_files(WIMStruct *wim, * path to the unmounted NTFS volume to extract the image to. * @param extract_flags * Bitwise OR of the flags prefixed with WIMLIB_EXTRACT_FLAG. - * @param additional_swms - * Array of pointers to the ::WIMStruct for each additional part in the - * split WIM. Ignored if @p num_additional_swms is 0. The pointers do not - * need to be in any particular order, but they must include all parts of - * the split WIM other than the first part, which must be provided in the - * @p wim parameter. - * @param num_additional_swms - * Number of additional WIM parts provided in the @p additional_swms array. - * This number should be one less than the total number of parts in the - * split WIM. Set to 0 if the WIM is a standalone WIM. - * * @param progress_func * If non-NULL, a function that will be called periodically with the * progress of the current operation. * * @return 0 on success; nonzero on error. * @retval ::WIMLIB_ERR_DECOMPRESSION - * Failed to decompress a resource to be extracted, or failed to decompress - * a needed metadata resource. + * Failed to decompress a resource to be extracted. * @retval ::WIMLIB_ERR_INVALID_PARAM * Both ::WIMLIB_EXTRACT_FLAG_HARDLINK and ::WIMLIB_EXTRACT_FLAG_SYMLINK * were specified in @p extract_flags; or both @@ -1872,8 +1917,6 @@ wimlib_extract_files(WIMStruct *wim, * ::WIMLIB_EXTRACT_FLAG_RESUME was specified in @p extract_flags; or if * ::WIMLIB_EXTRACT_FLAG_NTFS was specified in @p extract_flags and * @p image was ::WIMLIB_ALL_IMAGES. - * @retval ::WIMLIB_ERR_INVALID_METADATA_RESOURCE - * The metadata resource for an image being extracted was invalid. * @retval ::WIMLIB_ERR_INVALID_RESOURCE_HASH * The SHA1 message digest of an extracted stream did not match the SHA1 * message digest given in the WIM file. @@ -1913,11 +1956,6 @@ wimlib_extract_files(WIMStruct *wim, * @retval ::WIMLIB_ERR_SET_TIMESTAMPS * Failed to set timestamps on a file (only if * ::WIMLIB_EXTRACT_FLAG_STRICT_TIMESTAMPS was specified in @p extract_flags). - * @retval ::WIMLIB_ERR_SPLIT_INVALID - * The WIM is a split WIM, but the parts specified do not form a complete - * split WIM because they do not include all the parts of the original WIM, - * there are duplicate parts, or not all the parts have the same GUID and - * compression type. * @retval ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE * Unexpected end-of-file occurred when reading data from the WIM file * associated with @p wim. @@ -1940,13 +1978,18 @@ wimlib_extract_files(WIMStruct *wim, * ::WIMLIB_EXTRACT_FLAG_NTFS was not specified in @p extract_flags. * @retval ::WIMLIB_ERR_WRITE * Failed to write data to a file being extracted. + * + * This function can additionally return ::WIMLIB_ERR_DECOMPRESSION, + * ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_METADATA_NOT_FOUND, + * ::WIMLIB_ERR_NOMEM, ::WIMLIB_ERR_READ, or + * ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE, all of which indicate failure (for + * different reasons) to read the metadata resource for an image that needed to + * be extracted. */ extern int wimlib_extract_image(WIMStruct *wim, int image, const wimlib_tchar *target, int extract_flags, - WIMStruct **additional_swms, - unsigned num_additional_swms, wimlib_progress_func_t progress_func); /** @@ -2197,10 +2240,11 @@ wimlib_image_name_in_use(const WIMStruct *wim, const wimlib_tchar *name); * Failed to allocate memory needed to create a ::wimlib_dir_entry. * * This function can additionally return ::WIMLIB_ERR_DECOMPRESSION, - * ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_NOMEM, - * ::WIMLIB_ERR_READ, or ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE, all of which - * indicate failure (for different reasons) to read the metadata resource for an - * image over which iteration needed to be done. + * ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_METADATA_NOT_FOUND, + * ::WIMLIB_ERR_NOMEM, ::WIMLIB_ERR_READ, or + * ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE, all of which indicate failure (for + * different reasons) to read the metadata resource for an image over which + * iteration needed to be done. */ extern int wimlib_iterate_dir_tree(WIMStruct *wim, int image, const wimlib_tchar *path, @@ -2366,16 +2410,6 @@ wimlib_lzx_decompress(const void *compressed_data, unsigned compressed_len, * The path to an existing empty directory to mount the image on. * @param mount_flags * Bitwise OR of the flags prefixed with WIMLIB_MOUNT_FLAG. - * @param additional_swms - * Array of pointers to the ::WIMStruct for each additional part in the - * split WIM. Ignored if @p num_additional_swms is 0. The pointers do not - * need to be in any particular order, but they must include all parts of - * the split WIM other than the first part, which must be provided in the - * @p wim parameter. - * @param num_additional_swms - * Number of additional WIM parts provided in the @p additional_swms array. - * This number should be one less than the total number of parts in the - * split WIM. Set to 0 if the WIM is a standalone WIM. * @param staging_dir * If non-NULL, the name of a directory in which the staging directory will * be created. Ignored if ::WIMLIB_MOUNT_FLAG_READWRITE is not specified @@ -2389,14 +2423,10 @@ wimlib_lzx_decompress(const void *compressed_data, unsigned compressed_len, * the on-disk WIM file could not be acquired because another thread or * process has mounted an image from the WIM read-write or is currently * modifying the WIM in-place. - * @retval ::WIMLIB_ERR_DECOMPRESSION - * Could not decompress the metadata resource for @p image in @p wim. * @retval ::WIMLIB_ERR_FUSE * A non-zero status was returned by @c fuse_main(). * @retval ::WIMLIB_ERR_INVALID_IMAGE * @p image does not specify an existing, single image in @p wim. - * @retval ::WIMLIB_ERR_INVALID_METADATA_RESOURCE - * The metadata resource for @p image is invalid. * @retval ::WIMLIB_ERR_INVALID_PARAM * @p image is shared among multiple ::WIMStruct's as a result of a call to * wimlib_export_image(), or @p image has been added with @@ -2404,40 +2434,32 @@ wimlib_lzx_decompress(const void *compressed_data, unsigned compressed_len, * @retval ::WIMLIB_ERR_MKDIR * ::WIMLIB_MOUNT_FLAG_READWRITE was specified in @p mount_flags, but the * staging directory could not be created. - * @retval ::WIMLIB_ERR_NOMEM - * Failed to allocate needed memory. * @retval ::WIMLIB_ERR_NOTDIR * Could not determine the current working directory. - * @retval ::WIMLIB_ERR_READ - * Failed to read data from the WIM file associated with @p wim. * @retval ::WIMLIB_ERR_RESOURCE_NOT_FOUND * One of the dentries in the image referenced a stream not present in the * WIM's lookup table (or in any of the lookup tables of the split WIM * parts). - * @retval ::WIMLIB_ERR_SPLIT_INVALID - * The WIM is a split WIM, but the parts specified do not form a complete - * split WIM because they do not include all the parts of the original WIM, - * there are duplicate parts, or not all the parts have the same GUID and - * compression type. * @retval ::WIMLIB_ERR_WIM_IS_READONLY * ::WIMLIB_MOUNT_FLAG_READWRITE was specified in @p mount_flags, but @p * wim is considered read-only because of any of the reasons mentioned in * the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS flag. - * @retval ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE - * Unexpected end-of-file occurred while reading data from the WIM file - * associated with @p wim. * @retval ::WIMLIB_ERR_UNSUPPORTED * Mounting is not supported, either because the platform is Windows, or * because the platform is UNIX-like and wimlib was compiled with @c * --without-fuse. + * + * This function can additionally return ::WIMLIB_ERR_DECOMPRESSION, + * ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_METADATA_NOT_FOUND, + * ::WIMLIB_ERR_NOMEM, ::WIMLIB_ERR_READ, or + * ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE, all of which indicate failure (for + * different reasons) to read the metadata resource for the image to mount. */ extern int wimlib_mount_image(WIMStruct *wim, int image, const wimlib_tchar *dir, int mount_flags, - WIMStruct **additional_swms, - unsigned num_additional_swms, const wimlib_tchar *staging_dir); /** @@ -2625,6 +2647,93 @@ wimlib_print_header(const WIMStruct *wim) _wimlib_deprecated; extern int wimlib_print_metadata(WIMStruct *wim, int image) _wimlib_deprecated; +/** + * Reference resources from other WIM files or split WIM parts. This function + * can be used on WIMs that are not standalone, such as split or "delta" WIMs, + * to load needed resources (that is, "streams" keyed by SHA1 message digest) + * from other files, before calling a function such as wimlib_extract_image() + * that requires the resources to be present. + * + * @param wim + * The ::WIMStruct for a WIM that contains metadata resources, but is not + * necessarily "standalone". In the case of split WIMs, this should be the + * first part, since only the first part contains the metadata resources. + * In the case of delta WIMs, this should be the delta WIM rather than the + * WIM on which it is based. + * @param resource_wimfiles_or_globs + * Array of paths to WIM files and/or split WIM parts to reference. + * Alternatively, when ::WIMLIB_REF_FLAG_GLOB_ENABLE is specified in @p + * ref_flags, these are treated as globs rather than literal paths. That + * is, using this function you can specify zero or more globs, each of + * which expands to one or more literal paths. + * @param count + * Number of entries in @p resource_wimfiles_or_globs. + * @param ref_flags + * Bitwise OR of ::WIMLIB_REF_FLAG_GLOB_ENABLE and/or + * ::WIMLIB_REF_FLAG_GLOB_ERR_ON_NOMATCH. + * @param open_flags + * Additional open flags, such as ::WIMLIB_OPEN_FLAG_CHECK_INTEGRITY, to + * pass to internal calls to wimlib_open_wim() on the reference files. + * Note: ::WIMLIB_OPEN_FLAG_SPLIT_OK will be supplied regardless of this + * parameter. + * @param progress_func + * Passed to internal calls to wimlib_open_wim() on the reference files. + * + * @return 0 on success; nonzero on error. + * + * @retval ::WIMLIB_ERR_GLOB_HAD_NO_MATCHES + * One of the specified globs did not match any paths (only with both + * ::WIMLIB_REF_FLAG_GLOB_ENABLE and ::WIMLIB_REF_FLAG_GLOB_ERR_ON_NOMATCH + * specified in @p ref_flags). + * @retval ::WIMLIB_ERR_NOMEM + * Failed to allocate memory. + * @retval ::WIMLIB_ERR_READ + * I/O or permissions error while processing a file glob. + * + * This function can additionally return most values that can be returned by + * wimlib_open_wim(). + */ +extern int +wimlib_reference_resource_files(WIMStruct *wim, + const wimlib_tchar * const *resource_wimfiles_or_globs, + unsigned count, + int ref_flags, + int open_flags, + wimlib_progress_func_t progress_func); + +/** + * Similar to wimlib_reference_resource_files(), but operates at a lower level + * where the caller must open the ::WIMStruct for each referenced file itself. + * + * @param wim + * The ::WIMStruct for a WIM that contains metadata resources, but is not + * necessarily "standalone". In the case of split WIMs, this should be the + * first part, since only the first part contains the metadata resources. + * @param resource_wims + * Array of pointers to the ::WIMStruct's for additional resource WIMs or + * split WIM parts to reference. + * @param num_resource_wims + * Number of entries in @p resource_wims. + * @param ref_flags + * Currently ignored (set to 0). + * + * @return 0 on success; nonzero on error. On success, the ::WIMStruct's + * specified in @p resource_wims should be considered "don't touch" until either + * wimlib_free() is called on @p wim, or wimlib_unreference_resources() is + * called to unreference them. + * + * @retval ::WIMLIB_ERR_INVALID_PARAM + * @p wim was @c NULL, or @p num_resource_wims was nonzero but @p + * resource_wims was @c NULL, or @p wim did not contain metadata resources, + * or an entry in @p resource_wims was @p NULL, or an entry in @p + * resource_wims was already referenced by a call to this function without + * a corresponding call to wimlib_free() on the metadata WIM, or + * wimlib_unreference_resources(). + */ +extern int +wimlib_reference_resources(WIMStruct *wim, WIMStruct **resource_wims, + unsigned num_resource_wims, int ref_flags); + /** * Declares that a newly added image is mostly the same as a prior image, but * captured at a later point in time, possibly with some modifications in the @@ -2651,18 +2760,18 @@ wimlib_print_metadata(WIMStruct *wim, int image) _wimlib_deprecated; * wimlib_add_image()), but before writing the updated WIM file (e.g. with * wimlib_overwrite()). * - * @p wim + * @param wim * Pointer to the ::WIMStruct for a WIM. - * @p new_image + * @param new_image * 1-based index in the WIM of the newly added image. This image can have * been added with wimlib_add_image() or wimlib_add_image_multisource(), or * wimlib_add_empty_image() followed by wimlib_update_image(). - * @p template_image + * @param template_image * 1-based index in the WIM of a template image that reflects a prior state * of the directory tree being captured. - * @p flags + * @param flags * Reserved; must be 0. - * @p progress_func + * @param progress_func * Currently ignored, but reserved for a function that will be called with * information about the operation. Use NULL if no additional information * is desired. @@ -2672,6 +2781,10 @@ wimlib_print_metadata(WIMStruct *wim, int image) _wimlib_deprecated; * @retval ::WIMLIB_ERR_INVALID_IMAGE * @p new_image and/or @p template_image were not a valid image indices in * the WIM. + * @retval ::WIMLIB_ERR_METADATA_NOT_FOUND + * The specified ::WIMStruct did not actually contain the metadata resource + * for the new or template image; for example, it was a non-first part of a + * split WIM. * @retval ::WIMLIB_ERR_NOMEM * Failed to allocate needed memory. * @retval ::WIMLIB_ERR_INVALID_PARAM @@ -2679,10 +2792,10 @@ wimlib_print_metadata(WIMStruct *wim, int image) _wimlib_deprecated; * an image that had not been modified since opening the WIM. * * This function can additionally return ::WIMLIB_ERR_DECOMPRESSION, - * ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_NOMEM, - * ::WIMLIB_ERR_READ, or ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE, all of which - * indicate failure (for different reasons) to read the metadata resource for - * the template image. + * ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_METADATA_NOT_FOUND, + * ::WIMLIB_ERR_NOMEM, ::WIMLIB_ERR_READ, or + * ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE, all of which indicate failure (for + * different reasons) to read the metadata resource for the template image. */ extern int wimlib_reference_template_image(WIMStruct *wim, int new_image, @@ -2827,8 +2940,8 @@ wimlib_set_image_flags(WIMStruct *wim, int image, const wimlib_tchar *flags); * @p wim is considered read-only because of any of the reasons mentioned * in the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS flag. */ -extern int wimlib_set_image_name(WIMStruct *wim, int image, - const wimlib_tchar *name); +extern int +wimlib_set_image_name(WIMStruct *wim, int image, const wimlib_tchar *name); /** * Set the functions that wimlib uses to allocate and free memory. @@ -2918,8 +3031,6 @@ wimlib_set_print_errors(bool show_messages); * codes that can be returned by wimlib_write() as well as the following error * codes: * - * @retval ::WIMLIB_ERR_SPLIT_UNSUPPORTED - * @p wim was not part 1 of a stand-alone WIM. * @retval ::WIMLIB_ERR_INVALID_PARAM * @p swm_name was not a nonempty string, or @p part_size was 0. * @@ -3002,6 +3113,30 @@ wimlib_unmount_image(const wimlib_tchar *dir, int unmount_flags, wimlib_progress_func_t progress_func); +/** + * Unreferences resources previously referenced with + * wimlib_reference_resources(). + * + * Calling this is not necessary (or even possible) if the higher-level function + * wimlib_reference_resource_files() is used. + * + * @param wim + * See corresponding parameter to wimlib_reference_resources(). + * @param resource_wims + * See corresponding parameter to wimlib_reference_resources(). + * @param num_resource_wims + * See corresponding parameter to wimlib_reference_resources(). + * + * @return 0 on success; nonzero on error. + * + * @retval ::WIMLIB_ERR_INVALID_PARAM + * Not all entries in @p resource_wims specify valid ::WIMStruct's that are + * referenced by @p wim. + */ +extern int +wimlib_unreference_resources(WIMStruct *wim, WIMStruct **resource_wims, + unsigned num_resource_wims); + /** * Update a WIM image by adding, deleting, and/or renaming files or directories. * @@ -3025,8 +3160,6 @@ wimlib_unmount_image(const wimlib_tchar *dir, * update commands may have been executed. No individual update command will * have been partially executed. Possible error codes include: * - * @retval ::WIMLIB_ERR_DECOMPRESSION - * Failed to decompress the metadata resource from @p image in @p wim. * @retval ::WIMLIB_ERR_INVALID_CAPTURE_CONFIG * The capture configuration structure specified for an add command was * invalid. @@ -3047,10 +3180,6 @@ wimlib_unmount_image(const wimlib_tchar *dir, * @retval ::WIMLIB_ERR_INVALID_REPARSE_DATA * (Windows only): While executing an add command, tried to capture a * reparse point with invalid data. - * @retval ::WIMLIB_ERR_INVALID_RESOURCE_SIZE - * The metadata resource for @p image in @p wim is invalid. - * @retval ::WIMLIB_ERR_INVALID_SECURITY_DATA - * The security data for image @p image in @p wim is invalid. * @retval ::WIMLIB_ERR_IS_DIRECTORY * A delete command without ::WIMLIB_DELETE_FLAG_RECURSIVE specified was * for a WIM path that corresponded to a directory; or, a rename command @@ -3077,20 +3206,14 @@ wimlib_unmount_image(const wimlib_tchar *dir, * WIM path that did not exist; or, a rename command attempted to rename a * file that does not exist. * @retval ::WIMLIB_ERR_READ - * Failed to read the metadata resource for @p image in @p wim; or, while - * executing an add command, failed to read data from a file or directory - * to be captured. + * While executing an add command, failed to read data from a file or + * directory to be captured. * @retval ::WIMLIB_ERR_READLINK * While executing an add command, failed to read the target of a symbolic * link or junction point. * @retval ::WIMLIB_ERR_REPARSE_POINT_FIXUP_FAILED * (Windows only) Failed to perform a reparse point fixup because of * problems with the data of a reparse point. - * @retval ::WIMLIB_ERR_UNSUPPORTED_FILE - * While executing an add command, attempted to capture a file that was not - * a supported file type (e.g. a device file). Only if - * ::WIMLIB_ADD_FLAG_NO_UNSUPPORTED_EXCLUDE specified in @p the add_flags - * for an update command. * @retval ::WIMLIB_ERR_STAT * While executing an add command, failed to get attributes for a file or * directory. @@ -3100,10 +3223,22 @@ wimlib_unmount_image(const wimlib_tchar *dir, * or, the platform is Windows and either the ::WIMLIB_ADD_FLAG_UNIX_DATA * or the ::WIMLIB_ADD_FLAG_DEREFERENCE flags were specified in the @p * add_flags for an update command. + * @retval ::WIMLIB_ERR_UNSUPPORTED_FILE + * While executing an add command, attempted to capture a file that was not + * a supported file type (e.g. a device file). Only if + * ::WIMLIB_ADD_FLAG_NO_UNSUPPORTED_EXCLUDE specified in @p the add_flags + * for an update command. * @retval ::WIMLIB_ERR_WIM_IS_READONLY * The WIM file is considered read-only because of any of the reasons * mentioned in the documentation for the ::WIMLIB_OPEN_FLAG_WRITE_ACCESS * flag. + * + * This function can additionally return ::WIMLIB_ERR_DECOMPRESSION, + * ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_METADATA_NOT_FOUND, + * ::WIMLIB_ERR_NOMEM, ::WIMLIB_ERR_READ, or + * ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE, all of which indicate failure (for + * different reasons) to read the metadata resource for an image that needed to + * be updated. */ extern int wimlib_update_image(WIMStruct *wim, @@ -3147,8 +3282,6 @@ wimlib_update_image(WIMStruct *wim, * * @return 0 on success; nonzero on error. * - * @retval ::WIMLIB_ERR_DECOMPRESSION - * Failed to decompress a metadata or file resource in @p wim. * @retval ::WIMLIB_ERR_INVALID_IMAGE * @p image does not specify a single existing image in @p wim, and is not * ::WIMLIB_ALL_IMAGES. @@ -3158,8 +3291,6 @@ wimlib_update_image(WIMStruct *wim, * message digest check. * @retval ::WIMLIB_ERR_INVALID_PARAM * @p path was @c NULL. - * @retval ::WIMLIB_ERR_INVALID_METADATA_RESOURCE - * The metadata resource for @p image in @p wim is invalid. * @retval ::WIMLIB_ERR_NOMEM * Failed to allocate needed memory. * @retval ::WIMLIB_ERR_OPEN @@ -3173,6 +3304,13 @@ wimlib_update_image(WIMStruct *wim, * files. * @retval ::WIMLIB_ERR_WRITE * An error occurred when trying to write data to the new WIM file. + * + * This function can additionally return ::WIMLIB_ERR_DECOMPRESSION, + * ::WIMLIB_ERR_INVALID_METADATA_RESOURCE, ::WIMLIB_ERR_METADATA_NOT_FOUND, + * ::WIMLIB_ERR_NOMEM, ::WIMLIB_ERR_READ, or + * ::WIMLIB_ERR_UNEXPECTED_END_OF_FILE, all of which indicate failure (for + * different reasons) to read the metadata resource for an image that needed to + * be written. */ extern int wimlib_write(WIMStruct *wim, diff --git a/include/wimlib/glob.h b/include/wimlib/glob.h new file mode 100644 index 00000000..22ebfa9e --- /dev/null +++ b/include/wimlib/glob.h @@ -0,0 +1,35 @@ +#ifndef _WIMLIB_GLOB_H +#define _WIMLIB_GLOB_H + +#ifndef __WIN32__ +# include +#else + +#include +#include + +typedef struct { + size_t gl_pathc; + wchar_t **gl_pathv; + size_t gl_offs; +} glob_t; + +/* WARNING: this is a reduced functionality replacement */ +extern int +win32_wglob(const wchar_t *pattern, int flags, + int (*errfunc)(const wchar_t *epath, int eerrno), + glob_t *pglob); + +extern void globfree(glob_t *pglob); + +#define GLOB_ERR 0x1 /* Return on read errors. */ +#define GLOB_NOSORT 0x2 /* Don't sort the names. */ + +/* Error returns from `glob'. */ +#define GLOB_NOSPACE 1 /* Ran out of memory. */ +#define GLOB_ABORTED 2 /* Read error. */ +#define GLOB_NOMATCH 3 /* No matches found. */ + +#endif /* __WIN32__ */ + +#endif /* _WIMLIB_GLOB_H */ diff --git a/include/wimlib/lookup_table.h b/include/wimlib/lookup_table.h index 481794da..746d7449 100644 --- a/include/wimlib/lookup_table.h +++ b/include/wimlib/lookup_table.h @@ -416,6 +416,9 @@ extern int inode_resolve_ltes(struct wim_inode *inode, struct wim_lookup_table *table, bool force); +extern int +resource_not_found_error(struct wim_inode *inode, const u8 *hash); + extern void inode_unresolve_ltes(struct wim_inode *inode); diff --git a/include/wimlib/swm.h b/include/wimlib/swm.h deleted file mode 100644 index 08a51fc6..00000000 --- a/include/wimlib/swm.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef _WIMLIB_SWM_H -#define _WIMLIB_SWM_H - -#include "wimlib/types.h" - -extern int -verify_swm_set(WIMStruct *wim, - WIMStruct **additional_swms, unsigned num_additional_swms); - -extern void -merge_lookup_tables(WIMStruct *wim, - WIMStruct **additional_swms, unsigned num_additional_swms); - -extern void -unmerge_lookup_table(WIMStruct *wim); - -#endif diff --git a/include/wimlib/wim.h b/include/wimlib/wim.h index dfd31fa3..fc36c519 100644 --- a/include/wimlib/wim.h +++ b/include/wimlib/wim.h @@ -4,6 +4,7 @@ #include "wimlib/header.h" #include "wimlib/types.h" #include "wimlib/file_io.h" +#include "wimlib/list.h" struct wim_info; struct wim_lookup_table; @@ -41,6 +42,12 @@ struct WIMStruct { /* Temporary field */ void *private; + WIMStruct *master_wim; + + struct list_head resource_wims; + + struct list_head resource_wim_node; + /* The currently selected image, indexed starting at 1. If not 0, * subtract 1 from this to get the index of the current image in the * image_metadata array. */ @@ -58,6 +65,10 @@ struct WIMStruct { u8 wim_locked : 1; + u8 being_unmerged : 1; + + u8 is_owned_by_master : 1; + /* One of WIMLIB_COMPRESSION_TYPE_*, cached from the header flags. */ u8 compression_type : 2; }; @@ -72,6 +83,11 @@ static inline bool wim_has_integrity_table(const WIMStruct *wim) return (wim->hdr.integrity.offset != 0); } +static inline bool wim_has_metadata(const WIMStruct *wim) +{ + return (wim->image_metadata != NULL || wim->hdr.image_count == 0); +} + extern void wim_recalculate_refcnts(WIMStruct *wim); diff --git a/include/wimlib_tchar.h b/include/wimlib_tchar.h index fefaabc4..4d33efef 100644 --- a/include/wimlib_tchar.h +++ b/include/wimlib_tchar.h @@ -24,7 +24,7 @@ typedef wchar_t tchar; # define tmempcpy wmempcpy # define tstrcpy wcscpy # define tprintf wprintf -# define tsprintf swprintf +# define tsprintf _swprintf # define tfprintf fwprintf # define tvfprintf vfwprintf # define istalpha iswalpha @@ -61,6 +61,7 @@ typedef wchar_t tchar; # define tstrerror_r win32_strerror_r_replacement # define trename win32_rename_replacement # define ttruncate win32_truncate_replacement +# define tglob win32_wglob #else /* __WIN32__ */ /* For non-Windows builds, the "tchar" type will be one byte and will specify a * string in the locale-dependent multibyte encoding. However, only UTF-8 is @@ -118,6 +119,7 @@ typedef char tchar; # define trename rename # define ttruncate truncate # define taccess access +# define tglob glob #endif /* !__WIN32__ */ #endif /* _WIMLIB_TCHAR_H */ diff --git a/programs/imagex-win32.c b/programs/imagex-win32.c index a0cbc33d..083c1453 100644 --- a/programs/imagex-win32.c +++ b/programs/imagex-win32.c @@ -5,119 +5,11 @@ #endif #include "imagex-win32.h" -#include -#include #include #include #include -#include #include -/* Replacement for glob() in Windows native builds that operates on wide - * characters. */ -int -win32_wglob(const wchar_t *pattern, int flags, - int (*errfunc)(const wchar_t *epath, int eerrno), - glob_t *pglob) -{ - WIN32_FIND_DATAW dat; - DWORD err; - HANDLE hFind; - int ret; - size_t nspaces; - - const wchar_t *backslash, *end_slash; - size_t prefix_len; - - backslash = wcsrchr(pattern, L'\\'); - end_slash = wcsrchr(pattern, L'/'); - - if (backslash > end_slash) - end_slash = backslash; - - if (end_slash) - prefix_len = end_slash - pattern + 1; - else - prefix_len = 0; - - /* This function does not support all functionality of the POSIX glob(), - * so make sure the parameters are consistent with supported - * functionality. */ - assert(errfunc == NULL); - assert((flags & GLOB_ERR) == GLOB_ERR); - assert((flags & ~(GLOB_NOSORT | GLOB_ERR)) == 0); - - hFind = FindFirstFileW(pattern, &dat); - if (hFind == INVALID_HANDLE_VALUE) { - err = GetLastError(); - if (err == ERROR_FILE_NOT_FOUND) { - errno = 0; - return GLOB_NOMATCH; - } else { - /* The other possible error codes for FindFirstFile() - * are undocumented. */ - errno = EIO; - return GLOB_ABORTED; - } - } - pglob->gl_pathc = 0; - pglob->gl_pathv = NULL; - nspaces = 0; - do { - wchar_t *path; - if (pglob->gl_pathc == nspaces) { - size_t new_nspaces; - wchar_t **pathv; - - new_nspaces = nspaces * 2 + 1; - pathv = realloc(pglob->gl_pathv, - new_nspaces * sizeof(pglob->gl_pathv[0])); - if (!pathv) - goto oom; - pglob->gl_pathv = pathv; - nspaces = new_nspaces; - } - size_t filename_len = wcslen(dat.cFileName); - size_t len_needed = prefix_len + filename_len; - - path = malloc((len_needed + 1) * sizeof(wchar_t)); - if (!path) - goto oom; - - wmemcpy(path, pattern, prefix_len); - wmemcpy(path + prefix_len, dat.cFileName, filename_len + 1); - pglob->gl_pathv[pglob->gl_pathc++] = path; - } while (FindNextFileW(hFind, &dat)); - err = GetLastError(); - CloseHandle(hFind); - if (err == ERROR_NO_MORE_FILES) { - errno = 0; - return 0; - } else { - /* Other possible error codes for FindNextFile() are - * undocumented */ - errno = EIO; - ret = GLOB_ABORTED; - goto fail_globfree; - } -oom: - CloseHandle(hFind); - errno = ENOMEM; - ret = GLOB_NOSPACE; -fail_globfree: - globfree(pglob); - return ret; -} - -void -globfree(glob_t *pglob) -{ - size_t i; - for (i = 0; i < pglob->gl_pathc; i++) - free(pglob->gl_pathv[i]); - free(pglob->gl_pathv); -} - /* Convert a string from the "current Windows codepage" to UTF-16LE. */ wchar_t * win32_mbs_to_wcs(const char *mbs, size_t mbs_nbytes, size_t *num_wchars_ret) diff --git a/programs/imagex-win32.h b/programs/imagex-win32.h index 0d5f5ad2..bf1d2a7f 100644 --- a/programs/imagex-win32.h +++ b/programs/imagex-win32.h @@ -5,40 +5,6 @@ #include #include -typedef struct { - size_t gl_pathc; - wchar_t **gl_pathv; - size_t gl_offs; -} glob_t; - -/* WARNING: this is a reduced functionality replacement */ -extern int -win32_wglob(const wchar_t *pattern, int flags, - int (*errfunc)(const wchar_t *epath, int eerrno), - glob_t *pglob); - -extern void globfree(glob_t *pglob); - -#define GLOB_ERR 0x1 /* Return on read errors. */ -#define GLOB_NOSORT 0x2 /* Don't sort the names. */ - -/* Error returns from `glob'. */ -#define GLOB_NOSPACE 1 /* Ran out of memory. */ -#define GLOB_ABORTED 2 /* Read error. */ -#define GLOB_NOMATCH 3 /* No matches found. */ - -extern void -win32_acquire_capture_privileges(void); - -extern void -win32_release_capture_privileges(void); - -extern void -win32_acquire_restore_privileges(void); - -extern void -win32_release_restore_privileges(void); - extern wchar_t * win32_mbs_to_wcs(const char *mbs, size_t mbs_nbytes, size_t *num_wchars_ret); diff --git a/programs/imagex.c b/programs/imagex.c index f7a3620b..e3800ca3 100644 --- a/programs/imagex.c +++ b/programs/imagex.c @@ -49,7 +49,6 @@ #ifdef __WIN32__ # include "imagex-win32.h" # define tbasename win32_wbasename -# define tglob win32_wglob # define OS_PREFERRED_PATH_SEPARATOR L'\\' # define OS_PREFERRED_PATH_SEPARATOR_STRING L"\\" #else /* __WIN32__ */ @@ -57,7 +56,6 @@ # include # include # define tbasename basename -# define tglob glob # define OS_PREFERRED_PATH_SEPARATOR '/' # define OS_PREFERRED_PATH_SEPARATOR_STRING "/" static inline void set_fd_to_binary_mode(int fd) @@ -80,7 +78,6 @@ static inline void set_fd_to_binary_mode(int fd) #define for_opt(c, opts) while ((c = getopt_long_only(argc, (tchar**)argv, T(""), \ opts, NULL)) != -1) - enum { CMD_NONE = -1, CMD_APPEND = 0, @@ -110,6 +107,9 @@ static void usage_all(FILE *fp); static void recommend_man_page(int cmd, FILE *fp); static const tchar *get_cmd_string(int cmd, bool nospace); +static int imagex_progress_func(enum wimlib_progress_msg msg, + const union wimlib_progress_info *info); + static bool imagex_be_quiet = false; static FILE *imagex_info_file; @@ -411,6 +411,75 @@ get_compression_type(const tchar *optarg) } } +struct refglob_set { + const tchar **globs; + unsigned num_globs; + unsigned num_alloc_globs; +}; + +#define REFGLOB_SET_INITIALIZER \ + { .globs = NULL, .num_globs = 0, .num_alloc_globs = 0, } + +#define REFGLOB_SET(_refglobs) \ + struct refglob_set _refglobs = REFGLOB_SET_INITIALIZER + +static int +refglob_set_append(struct refglob_set *set, const tchar *glob) +{ + unsigned num_alloc_globs = set->num_alloc_globs; + + if (set->num_globs == num_alloc_globs) { + const tchar **new_globs; + + num_alloc_globs += 4; + new_globs = realloc(set->globs, sizeof(set->globs[0]) * num_alloc_globs); + if (!new_globs) { + imagex_error(T("Out of memory!")); + return -1; + } + set->globs = new_globs; + set->num_alloc_globs = num_alloc_globs; + } + set->globs[set->num_globs++] = glob; + return 0; +} + +static int +wim_reference_globs(WIMStruct *wim, struct refglob_set *set, int open_flags) +{ + return wimlib_reference_resource_files(wim, set->globs, set->num_globs, + WIMLIB_REF_FLAG_GLOB_ENABLE, + open_flags, + imagex_progress_func); +} + +static void +refglob_set_destroy(struct refglob_set *set) +{ + free(set->globs); +} + +static void +do_resource_not_found_warning(const tchar *wimfile, + const struct wimlib_wim_info *info, + const struct refglob_set *refglobs) +{ + if (info->total_parts > 1) { + if (refglobs->num_globs == 0) { + imagex_error(T("\"%"TS"\" is part of a split WIM. " + "Use --ref to specify the other parts."), + wimfile); + } else { + imagex_error(T("Perhaps the '--ref' argument did not " + "specify all other parts of the split " + "WIM?")); + } + } else { + imagex_error(T("If this is a delta WIM, use the --ref argument " + "to specify the WIM on which it is based.")); + } +} + /* Returns the size of a file given its name, or -1 if the file does not exist * or its size cannot be determined. */ static off_t @@ -1154,70 +1223,6 @@ imagex_progress_func(enum wimlib_progress_msg msg, return 0; } -/* Open all the split WIM parts that correspond to a file glob. - * - * @first_part specifies the first part of the split WIM and it may be either - * included or omitted from the glob. */ -static int -open_swms_from_glob(const tchar *swm_glob, - const tchar *first_part, - int open_flags, - WIMStruct ***additional_swms_ret, - unsigned *num_additional_swms_ret) -{ - unsigned num_additional_swms = 0; - WIMStruct **additional_swms = NULL; - glob_t globbuf; - int ret; - - /* Warning: glob() is replaced in Windows native builds */ - ret = tglob(swm_glob, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf); - if (ret) { - if (ret == GLOB_NOMATCH) { - imagex_error(T("Found no files for glob \"%"TS"\""), - swm_glob); - } else { - imagex_error_with_errno(T("Failed to process glob \"%"TS"\""), - swm_glob); - } - ret = -1; - goto out; - } - num_additional_swms = globbuf.gl_pathc; - additional_swms = calloc(num_additional_swms, sizeof(additional_swms[0])); - if (!additional_swms) { - imagex_error(T("Out of memory")); - ret = -1; - goto out_globfree; - } - unsigned offset = 0; - for (unsigned i = 0; i < num_additional_swms; i++) { - if (tstrcmp(globbuf.gl_pathv[i], first_part) == 0) { - offset++; - continue; - } - ret = wimlib_open_wim(globbuf.gl_pathv[i], - open_flags | WIMLIB_OPEN_FLAG_SPLIT_OK, - &additional_swms[i - offset], - imagex_progress_func); - if (ret) - goto out_close_swms; - } - *additional_swms_ret = additional_swms; - *num_additional_swms_ret = num_additional_swms - offset; - ret = 0; - goto out_globfree; -out_close_swms: - for (unsigned i = 0; i < num_additional_swms; i++) - wimlib_free(additional_swms[i]); - free(additional_swms); -out_globfree: - globfree(&globbuf); -out: - return ret; -} - - static unsigned parse_num_threads(const tchar *optarg) { @@ -1448,15 +1453,14 @@ imagex_apply(int argc, tchar **argv, int cmd) int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK; int image; WIMStruct *wim; + struct wimlib_wim_info info; int ret; const tchar *wimfile; const tchar *target; const tchar *image_num_or_name; int extract_flags = WIMLIB_EXTRACT_FLAG_SEQUENTIAL; - const tchar *swm_glob = NULL; - WIMStruct **additional_swms; - unsigned num_additional_swms; + REFGLOB_SET(refglobs); for_opt(c, apply_options) { switch (c) { @@ -1473,7 +1477,9 @@ imagex_apply(int argc, tchar **argv, int cmd) extract_flags |= WIMLIB_EXTRACT_FLAG_VERBOSE; break; case IMAGEX_REF_OPTION: - swm_glob = optarg; + ret = refglob_set_append(&refglobs, optarg); + if (ret) + goto out_free_refglobs; break; case IMAGEX_UNIX_DATA_OPTION: extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA; @@ -1518,13 +1524,13 @@ imagex_apply(int argc, tchar **argv, int cmd) target = argv[2]; } wim = NULL; - num_additional_swms = 0; - additional_swms = NULL; } else { ret = wimlib_open_wim(wimfile, open_flags, &wim, imagex_progress_func); if (ret) - goto out; + goto out_free_refglobs; + + wimlib_get_wim_info(wim, &info); if (argc >= 3) { /* Image explicitly specified. */ @@ -1537,9 +1543,7 @@ imagex_apply(int argc, tchar **argv, int cmd) } else { /* No image specified; default to image 1, but only if the WIM * contains exactly one image. */ - struct wimlib_wim_info info; - wimlib_get_wim_info(wim, &info); if (info.image_count != 1) { imagex_error(T("\"%"TS"\" contains %d images; " "Please select one (or all)."), @@ -1550,17 +1554,17 @@ imagex_apply(int argc, tchar **argv, int cmd) image = 1; target = argv[1]; } + } - if (swm_glob) { - ret = open_swms_from_glob(swm_glob, wimfile, open_flags, - &additional_swms, - &num_additional_swms); - if (ret) - goto out_wimlib_free; - } else { - additional_swms = NULL; - num_additional_swms = 0; + if (refglobs.num_globs) { + if (wim == NULL) { + imagex_error(T("Can't specify --ref when applying from stdin!")); + ret = -1; + goto out_wimlib_free; } + ret = wim_reference_globs(wim, &refglobs, open_flags); + if (ret) + goto out_wimlib_free; } #ifndef __WIN32__ @@ -1574,7 +1578,7 @@ imagex_apply(int argc, tchar **argv, int cmd) imagex_error_with_errno(T("Failed to stat \"%"TS"\""), target); ret = -1; - goto out_free_swms; + goto out_wimlib_free; } } else { if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) @@ -1585,7 +1589,6 @@ imagex_apply(int argc, tchar **argv, int cmd) if (wim) { ret = wimlib_extract_image(wim, image, target, extract_flags, - additional_swms, num_additional_swms, imagex_progress_func); } else { set_fd_to_binary_mode(STDIN_FILENO); @@ -1594,21 +1597,28 @@ imagex_apply(int argc, tchar **argv, int cmd) target, extract_flags, imagex_progress_func); } - if (ret == 0) + if (ret == 0) { imagex_printf(T("Done applying WIM image.\n")); -out_free_swms: - for (unsigned i = 0; i < num_additional_swms; i++) - wimlib_free(additional_swms[i]); - free(additional_swms); + } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) { + if (wim) { + do_resource_not_found_warning(wimfile, &info, &refglobs); + } else { + imagex_error(T( "If you are applying an image " + "from a split pipable WIM,\n" + " make sure you have " + "concatenated together all parts.")); + } + } out_wimlib_free: wimlib_free(wim); -out: +out_free_refglobs: + refglob_set_destroy(&refglobs); return ret; out_usage: usage(CMD_APPLY, stderr); ret = -1; - goto out; + goto out_free_refglobs; } /* Create a WIM image from a directory tree, NTFS volume, or multiple files or @@ -2144,14 +2154,13 @@ imagex_export(int argc, tchar **argv, int cmd) const tchar *dest_name; const tchar *dest_desc; WIMStruct *src_wim; + struct wimlib_wim_info src_info; WIMStruct *dest_wim; int ret; int image; struct stat stbuf; bool wim_is_new; - const tchar *swm_glob = NULL; - WIMStruct **additional_swms; - unsigned num_additional_swms; + REFGLOB_SET(refglobs); unsigned num_threads = 0; for_opt(c, export_options) { @@ -2172,7 +2181,9 @@ imagex_export(int argc, tchar **argv, int cmd) goto out_err; break; case IMAGEX_REF_OPTION: - swm_glob = optarg; + ret = refglob_set_append(&refglobs, optarg); + if (ret) + goto out_free_refglobs; break; case IMAGEX_THREADS_OPTION: num_threads = parse_num_threads(optarg); @@ -2205,7 +2216,9 @@ imagex_export(int argc, tchar **argv, int cmd) open_flags | WIMLIB_OPEN_FLAG_SPLIT_OK, &src_wim, imagex_progress_func); if (ret) - goto out; + goto out_free_refglobs; + + wimlib_get_wim_info(src_wim, &src_info); /* Determine if the destination is an existing file or not. If so, we * try to append the exported image(s) to it; otherwise, we create a new @@ -2278,9 +2291,6 @@ imagex_export(int argc, tchar **argv, int cmd) /* The user did not specify a compression type; default * to that of the source WIM. */ - struct wimlib_wim_info src_info; - - wimlib_get_wim_info(src_wim, &src_info); compression_type = src_info.compression_type; } ret = wimlib_create_new_wim(compression_type, &dest_wim); @@ -2293,22 +2303,30 @@ imagex_export(int argc, tchar **argv, int cmd) if (ret) goto out_free_dest_wim; - if (swm_glob) { - ret = open_swms_from_glob(swm_glob, src_wimfile, open_flags, - &additional_swms, - &num_additional_swms); + if (refglobs.num_globs) { + ret = wim_reference_globs(src_wim, &refglobs, open_flags); if (ret) goto out_free_dest_wim; - } else { - additional_swms = NULL; - num_additional_swms = 0; + } + + if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) && + image == WIMLIB_ALL_IMAGES && src_info.boot_index == 0) + { + imagex_error(T("--boot specified for all-images export, but source WIM " + "has no bootable image.")); + ret = -1; + goto out_free_dest_wim; } ret = wimlib_export_image(src_wim, image, dest_wim, dest_name, - dest_desc, export_flags, additional_swms, - num_additional_swms, imagex_progress_func); - if (ret) - goto out_free_swms; + dest_desc, export_flags, imagex_progress_func); + if (ret) { + if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) { + do_resource_not_found_warning(src_wimfile, + &src_info, &refglobs); + } + goto out_free_dest_wim; + } if (!wim_is_new) ret = wimlib_overwrite(dest_wim, write_flags, num_threads, @@ -2321,24 +2339,19 @@ imagex_export(int argc, tchar **argv, int cmd) ret = wimlib_write_to_fd(dest_wim, dest_wim_fd, WIMLIB_ALL_IMAGES, write_flags, num_threads, imagex_progress_func); - if (ret) - imagex_error(T("Export failed.")); -out_free_swms: - for (unsigned i = 0; i < num_additional_swms; i++) - wimlib_free(additional_swms[i]); - free(additional_swms); out_free_dest_wim: wimlib_free(dest_wim); out_free_src_wim: wimlib_free(src_wim); -out: +out_free_refglobs: + refglob_set_destroy(&refglobs); return ret; out_usage: usage(CMD_EXPORT, stderr); out_err: ret = -1; - goto out; + goto out_free_refglobs; } static bool @@ -2416,9 +2429,7 @@ imagex_extract(int argc, tchar **argv, int cmd) tchar *dest_dir = T("."); int extract_flags = WIMLIB_EXTRACT_FLAG_SEQUENTIAL | WIMLIB_EXTRACT_FLAG_NORPFIX; - const tchar *swm_glob = NULL; - WIMStruct **additional_swms; - unsigned num_additional_swms; + REFGLOB_SET(refglobs); struct wimlib_extract_command *cmds; size_t num_cmds; @@ -2432,7 +2443,9 @@ imagex_extract(int argc, tchar **argv, int cmd) extract_flags |= WIMLIB_EXTRACT_FLAG_VERBOSE; break; case IMAGEX_REF_OPTION: - swm_glob = optarg; + ret = refglob_set_append(&refglobs, optarg); + if (ret) + goto out_free_refglobs; break; case IMAGEX_UNIX_DATA_OPTION: extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA; @@ -2487,19 +2500,13 @@ imagex_extract(int argc, tchar **argv, int cmd) if (ret) goto out_wimlib_free; - if (swm_glob) { - ret = open_swms_from_glob(swm_glob, wimfile, open_flags, - &additional_swms, - &num_additional_swms); + if (refglobs.num_globs) { + ret = wim_reference_globs(wim, &refglobs, open_flags); if (ret) goto out_wimlib_free; - } else { - additional_swms = NULL; - num_additional_swms = 0; } ret = wimlib_extract_files(wim, image, cmds, num_cmds, 0, - additional_swms, num_additional_swms, imagex_progress_func); if (ret == 0) { if (!imagex_be_quiet) @@ -2509,22 +2516,25 @@ imagex_extract(int argc, tchar **argv, int cmd) "files and directories\n" " are in the WIM image.\n"), get_cmd_string(CMD_INFO, false)); + } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) { + struct wimlib_wim_info info; + + wimlib_get_wim_info(wim, &info); + do_resource_not_found_warning(wimfile, &info, &refglobs); } - for (unsigned i = 0; i < num_additional_swms; i++) - wimlib_free(additional_swms[i]); - free(additional_swms); out_wimlib_free: wimlib_free(wim); out_free_cmds: free_extract_commands(cmds, num_cmds, dest_dir); -out: +out_free_refglobs: + refglob_set_destroy(&refglobs); return ret; out_usage: usage(CMD_EXTRACT, stderr); out_err: ret = -1; - goto out; + goto out_free_refglobs; } static void print_byte_field(const uint8_t field[], size_t len) @@ -2928,15 +2938,15 @@ imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd) int c; int mount_flags = 0; int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK; - const tchar *swm_glob = NULL; const tchar *staging_dir = NULL; const tchar *wimfile; const tchar *dir; WIMStruct *wim; + struct wimlib_wim_info info; int image; int ret; - WIMStruct **additional_swms; - unsigned num_additional_swms; + + REFGLOB_SET(refglobs); if (cmd == CMD_MOUNTRW) { mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE; @@ -2968,7 +2978,9 @@ imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd) } break; case IMAGEX_REF_OPTION: - swm_glob = optarg; + ret = refglob_set_append(&refglobs, optarg); + if (ret) + goto out_free_refglobs; break; case IMAGEX_STAGING_DIR_OPTION: staging_dir = optarg; @@ -2989,7 +3001,9 @@ imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd) ret = wimlib_open_wim(wimfile, open_flags, &wim, imagex_progress_func); if (ret) - goto out; + goto out_free_refglobs; + + wimlib_get_wim_info(wim, &info); if (argc >= 3) { /* Image explicitly specified. */ @@ -3001,9 +3015,7 @@ imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd) } else { /* No image specified; default to image 1, but only if the WIM * contains exactly one image. */ - struct wimlib_wim_info info; - wimlib_get_wim_info(wim, &info); if (info.image_count != 1) { imagex_error(T("\"%"TS"\" contains %d images; Please " "select one."), wimfile, info.image_count); @@ -3014,36 +3026,28 @@ imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd) dir = argv[1]; } - if (swm_glob) { - ret = open_swms_from_glob(swm_glob, wimfile, open_flags, - &additional_swms, - &num_additional_swms); + if (refglobs.num_globs) { + ret = wim_reference_globs(wim, &refglobs, open_flags); if (ret) goto out_free_wim; - } else { - additional_swms = NULL; - num_additional_swms = 0; } - ret = wimlib_mount_image(wim, image, dir, mount_flags, additional_swms, - num_additional_swms, staging_dir); + ret = wimlib_mount_image(wim, image, dir, mount_flags, staging_dir); if (ret) { imagex_error(T("Failed to mount image %d from \"%"TS"\" " "on \"%"TS"\""), image, wimfile, dir); } - for (unsigned i = 0; i < num_additional_swms; i++) - wimlib_free(additional_swms[i]); - free(additional_swms); out_free_wim: wimlib_free(wim); -out: +out_free_refglobs: + refglob_set_destroy(&refglobs); return ret; out_usage: usage(cmd, stderr); ret = -1; - goto out; + goto out_free_refglobs; } #endif /* WIM_MOUNTING_SUPPORTED */ diff --git a/src/dentry.c b/src/dentry.c index ca9fe0b1..a73828ba 100644 --- a/src/dentry.c +++ b/src/dentry.c @@ -2596,9 +2596,6 @@ wimlib_reference_template_image(WIMStruct *wim, int new_image, int template_imag int ret; struct wim_image_metadata *new_imd; - if (wim->hdr.part_number != 1) - return WIMLIB_ERR_SPLIT_UNSUPPORTED; - if (new_image < 1 || new_image > wim->hdr.image_count) return WIMLIB_ERR_INVALID_IMAGE; @@ -2608,6 +2605,9 @@ wimlib_reference_template_image(WIMStruct *wim, int new_image, int template_imag if (new_image == template_image) return WIMLIB_ERR_INVALID_PARAM; + if (!wim_has_metadata(wim)) + return WIMLIB_ERR_METADATA_NOT_FOUND; + new_imd = wim->image_metadata[new_image - 1]; if (!new_imd->modified) return WIMLIB_ERR_INVALID_PARAM; diff --git a/src/export_image.c b/src/export_image.c index 17c4f7dd..81c66594 100644 --- a/src/export_image.c +++ b/src/export_image.c @@ -30,69 +30,68 @@ #include "wimlib/error.h" #include "wimlib/lookup_table.h" #include "wimlib/metadata.h" -#include "wimlib/swm.h" #include "wimlib/xml.h" +#include static int -inode_allocate_needed_ltes(struct wim_inode *inode, - struct wim_lookup_table *src_lookup_table, - struct wim_lookup_table *dest_lookup_table, - struct list_head *lte_list_head) +inode_export_streams(struct wim_inode *inode, + const struct wim_lookup_table *src_lookup_table, + struct wim_lookup_table *dest_lookup_table) { - struct wim_lookup_table_entry *src_lte, *dest_lte; unsigned i; + const u8 *hash; + struct wim_lookup_table_entry *src_lte, *dest_lte; inode_unresolve_ltes(inode); for (i = 0; i <= inode->i_num_ads; i++) { - src_lte = inode_stream_lte_unresolved(inode, i, - src_lookup_table); - if (src_lte && src_lte->out_refcnt == 0) { - src_lte->out_refcnt = 1; - dest_lte = inode_stream_lte_unresolved(inode, i, - dest_lookup_table); - if (!dest_lte) { - dest_lte = clone_lookup_table_entry(src_lte); - if (!dest_lte) - return WIMLIB_ERR_NOMEM; - list_add_tail(&dest_lte->export_stream_list, - lte_list_head); - } + + /* Retrieve SHA1 message digest of stream to export. */ + hash = inode_stream_hash(inode, i); + if (is_zero_hash(hash)) /* Empty stream? */ + continue; + + /* Search for the stream (via SHA1 message digest) in the + * destination WIM. */ + dest_lte = __lookup_resource(dest_lookup_table, hash); + if (!dest_lte) { + /* Stream not yet present in destination WIM. Search + * for it in the source WIM, then export it into the + * destination WIM. */ + src_lte = __lookup_resource(src_lookup_table, hash); + if (!src_lte) + return resource_not_found_error(inode, hash); + + dest_lte = clone_lookup_table_entry(src_lte); + if (!dest_lte) + return WIMLIB_ERR_NOMEM; + dest_lte->refcnt = 0; + dest_lte->out_refcnt = 0; + lookup_table_insert(dest_lookup_table, dest_lte); } + + /* Stream is present in destination WIM (either pre-existing, + * already exported, or just exported above). Increment its + * reference count appropriately. Note: we use 'refcnt' for + * the raw reference count, but 'out_refcnt' for references + * arising just from the export operation; this is used to roll + * back a failed export if needed. */ + dest_lte->refcnt += inode->i_nlink; + dest_lte->out_refcnt += inode->i_nlink; } return 0; } -static void -inode_move_ltes_to_table(struct wim_inode *inode, - struct wim_lookup_table *src_lookup_table, - struct wim_lookup_table *dest_lookup_table, - struct list_head *lte_list_head) +static int +lte_unexport(struct wim_lookup_table_entry *lte, void *_lookup_table) { - struct wim_lookup_table_entry *src_lte, *dest_lte; - unsigned i; + struct wim_lookup_table *lookup_table = _lookup_table; - for (i = 0; i <= inode->i_num_ads; i++) { - src_lte = inode_stream_lte_unresolved(inode, i, src_lookup_table); - if (src_lte) { - dest_lte = inode_stream_lte_unresolved(inode, i, - dest_lookup_table); - if (!dest_lte) { - struct list_head *next; - - wimlib_assert(!list_empty(lte_list_head)); - next = lte_list_head->next; - list_del(next); - dest_lte = container_of(next, - struct wim_lookup_table_entry, - export_stream_list); - dest_lte->part_number = 1; - dest_lte->refcnt = 0; - wimlib_assert(hashes_equal(dest_lte->hash, src_lte->hash)); - lookup_table_insert(dest_lookup_table, dest_lte); - } - dest_lte->refcnt += inode->i_nlink; - } + lte->refcnt -= lte->out_refcnt; + if (lte->refcnt == 0) { + lookup_table_unlink(lookup_table, lte); + free_lookup_table_entry(lte); } + return 0; } /* API function documented in wimlib.h */ @@ -103,91 +102,47 @@ wimlib_export_image(WIMStruct *src_wim, const tchar *dest_name, const tchar *dest_description, int export_flags, - WIMStruct **additional_swms, - unsigned num_additional_swms, wimlib_progress_func_t progress_func) { int ret; - struct wim_image_metadata *src_imd; - struct list_head lte_list_head; - struct wim_inode *inode; + int start_image; + int end_image; + int image; + u32 orig_dest_boot_idx; + u32 orig_dest_image_count; + + /* Check for sane parameters. */ + if (src_wim == NULL || dest_wim == NULL) + return WIMLIB_ERR_INVALID_PARAM; + + if (!wim_has_metadata(src_wim) || !wim_has_metadata(dest_wim)) + return WIMLIB_ERR_METADATA_NOT_FOUND; + /* Destination WIM must be writable. */ ret = can_modify_wim(dest_wim); if (ret) return ret; if (src_image == WIMLIB_ALL_IMAGES) { - if (src_wim->hdr.image_count > 1) { - - /* multi-image export. */ - - if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) && - (src_wim->hdr.boot_idx == 0)) - { - /* Specifying the boot flag on a multi-image - * source WIM makes the boot index default to - * the bootable image in the source WIM. It is - * an error if there is no such bootable image. - * */ - ERROR("Cannot specify `boot' flag when " - "exporting multiple images from a WIM " - "with no bootable images"); - return WIMLIB_ERR_INVALID_PARAM; - } - if (dest_name || dest_description) { - ERROR("Image name or image description was " - "specified, but we are exporting " - "multiple images"); - return WIMLIB_ERR_INVALID_PARAM; - } - for (int i = 1; i <= src_wim->hdr.image_count; i++) { - int new_flags = export_flags; - - if (i != src_wim->hdr.boot_idx) - new_flags &= ~WIMLIB_EXPORT_FLAG_BOOT; - - ret = wimlib_export_image(src_wim, i, dest_wim, - NULL, NULL, - new_flags, - additional_swms, - num_additional_swms, - progress_func); - if (ret) - return ret; - } - return 0; - } else if (src_wim->hdr.image_count == 1) { - src_image = 1; - } else { - return 0; + /* Multi-image export. */ + if ((!(export_flags & WIMLIB_EXPORT_FLAG_NO_NAMES) && + dest_name) || + (!(export_flags & WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS) && + dest_description)) + { + ERROR("Image name or image description was " + "specified, but we are exporting " + "multiple images"); + return WIMLIB_ERR_INVALID_PARAM; } + start_image = 1; + end_image = src_wim->hdr.image_count; + } else { + start_image = src_image; + end_image = src_image; } - if (!dest_name) { - dest_name = wimlib_get_image_name(src_wim, src_image); - DEBUG("Using name `%"TS"' for source image %d", - dest_name, src_image); - } - - if (!dest_description) { - dest_description = wimlib_get_image_description(src_wim, - src_image); - DEBUG("Using description `%"TS"' for source image %d", - dest_description, src_image); - } - - DEBUG("Exporting image %d from `%"TS"'", src_image, src_wim->filename); - - if (wimlib_image_name_in_use(dest_wim, dest_name)) { - ERROR("There is already an image named `%"TS"' in the " - "destination WIM", dest_name); - return WIMLIB_ERR_IMAGE_NAME_COLLISION; - } - - ret = verify_swm_set(src_wim, additional_swms, num_additional_swms); - if (ret) - return ret; - + /* Stream checksums must be known before proceeding. */ ret = wim_checksum_unhashed_streams(src_wim); if (ret) return ret; @@ -195,77 +150,124 @@ wimlib_export_image(WIMStruct *src_wim, if (ret) return ret; - if (num_additional_swms) - merge_lookup_tables(src_wim, additional_swms, num_additional_swms); + /* Zero 'out_refcnt' in all lookup table entries in the destination WIM; + * this tracks the number of references found from the source WIM + * image(s). */ + for_lookup_table_entry(dest_wim->lookup_table, lte_zero_out_refcnt, + NULL); - ret = select_wim_image(src_wim, src_image); - if (ret) { - ERROR("Could not select image %d from the WIM `%"TS"' " - "to export it", src_image, src_wim->filename); - goto out; - } + /* Save the original count of images in the destination WIM and the boot + * index (used if rollback necessary). */ + orig_dest_image_count = dest_wim->hdr.image_count; + orig_dest_boot_idx = dest_wim->hdr.boot_idx; + + /* Export each requested image. */ + for (image = start_image; image <= end_image; image++) { + const tchar *next_dest_name, *next_dest_description; + struct wim_image_metadata *src_imd; + struct wim_inode *inode; + + DEBUG("Exporting image %d from \"%"TS"\"", + image, src_wim->filename); - /* Pre-allocate the new lookup table entries that will be needed. This - * way, it's not possible to run out of memory part-way through - * modifying the lookup table of the destination WIM. */ - for_lookup_table_entry(src_wim->lookup_table, lte_zero_out_refcnt, NULL); - src_imd = wim_get_current_image_metadata(src_wim); - INIT_LIST_HEAD(<e_list_head); - image_for_each_inode(inode, src_imd) { - ret = inode_allocate_needed_ltes(inode, - src_wim->lookup_table, - dest_wim->lookup_table, - <e_list_head); + /* Determine destination image name and description. */ + + if (export_flags & WIMLIB_EXPORT_FLAG_NO_NAMES) { + next_dest_name = NULL; + } else if (dest_name) { + next_dest_name = dest_name; + } else { + next_dest_name = wimlib_get_image_name(src_wim, + image); + } + + DEBUG("Using name \"%"TS"\"", next_dest_name); + + if (export_flags & WIMLIB_EXPORT_FLAG_NO_DESCRIPTIONS) { + next_dest_description = NULL; + } if (dest_description) { + next_dest_description = dest_description; + } else { + next_dest_description = wimlib_get_image_description( + src_wim, image); + } + + DEBUG("Using description \"%"TS"\"", next_dest_description); + + /* Check for name conflict. */ + if (wimlib_image_name_in_use(dest_wim, next_dest_name)) { + ERROR("There is already an image named \"%"TS"\" " + "in the destination WIM", next_dest_name); + ret = WIMLIB_ERR_IMAGE_NAME_COLLISION; + goto out_rollback; + } + + /* Load metadata for source image into memory. */ + ret = select_wim_image(src_wim, image); if (ret) - goto out_free_ltes; - } + goto out_rollback; - ret = xml_export_image(src_wim->wim_info, src_image, - &dest_wim->wim_info, dest_name, - dest_description); - if (ret) - goto out_free_ltes; + src_imd = wim_get_current_image_metadata(src_wim); - ret = append_image_metadata(dest_wim, src_imd); - if (ret) - goto out_xml_delete_image; - - /* The `struct image_metadata' is now referenced by both the @src_wim - * and the @dest_wim. */ - src_imd->refcnt++; - src_imd->modified = 1; - - /* All memory allocations have been taken care of, so it's no longer - * possible for this function to fail. Go ahead and update the lookup - * table of the destination WIM and the boot index, if needed. */ - image_for_each_inode(inode, src_imd) { - inode_move_ltes_to_table(inode, - src_wim->lookup_table, - dest_wim->lookup_table, - <e_list_head); - } + /* Iterate through inodes in the source image and export their + * streams into the destination WIM. */ + image_for_each_inode(inode, src_imd) { + ret = inode_export_streams(inode, + src_wim->lookup_table, + dest_wim->lookup_table); + if (ret) + goto out_rollback; + } + + /* Export XML information into the destination WIM. */ + ret = xml_export_image(src_wim->wim_info, image, + &dest_wim->wim_info, next_dest_name, + next_dest_description); + if (ret) + goto out_rollback; + + /* Reference the source image metadata from the destination WIM. + */ + ret = append_image_metadata(dest_wim, src_imd); + if (ret) + goto out_rollback; + src_imd->refcnt++; + + /* Lock the metadata into memory. XXX: need better solution for + * this. */ + src_imd->modified = 1; - if (export_flags & WIMLIB_EXPORT_FLAG_BOOT) - dest_wim->hdr.boot_idx = dest_wim->hdr.image_count; + /* Set boot index in destination WIM. */ + if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) && + (src_image != WIMLIB_ALL_IMAGES || + image == src_wim->hdr.boot_idx)) + { + DEBUG("Marking destination image %u as bootable.", + dest_wim->hdr.image_count); + dest_wim->hdr.boot_idx = dest_wim->hdr.image_count; + } + + } + /* Set the reparse point fixup flag on the destination WIM if the flag + * is set on the source WIM. */ if (src_wim->hdr.flags & WIM_HDR_FLAG_RP_FIX) - { - /* Set the reparse point fixup flag on the destination WIM if - * the flag is set on the source WIM. */ dest_wim->hdr.flags |= WIM_HDR_FLAG_RP_FIX; + DEBUG("Export operation successful."); + return 0; + +out_rollback: + while ((image = wim_info_get_num_images(dest_wim->wim_info)) + > orig_dest_image_count) + { + xml_delete_image(&dest_wim->wim_info, image); } - DEBUG("Successfully exported image."); - ret = 0; - goto out; -out_xml_delete_image: - xml_delete_image(&dest_wim->wim_info, dest_wim->hdr.image_count + 1); -out_free_ltes: + while (dest_wim->hdr.image_count > orig_dest_image_count) { - struct wim_lookup_table_entry *lte, *tmp; - list_for_each_entry_safe(lte, tmp, <e_list_head, export_stream_list) - free_lookup_table_entry(lte); + put_image_metadata(dest_wim->image_metadata[ + --dest_wim->hdr.image_count], NULL); } -out: - if (num_additional_swms) - unmerge_lookup_table(src_wim); + for_lookup_table_entry(dest_wim->lookup_table, lte_unexport, + dest_wim->lookup_table); + dest_wim->hdr.boot_idx = orig_dest_boot_idx; return ret; } diff --git a/src/extract.c b/src/extract.c index c04f11b6..c9f6e21f 100644 --- a/src/extract.c +++ b/src/extract.c @@ -53,7 +53,6 @@ #include "wimlib/reparse.h" #include "wimlib/resource.h" #include "wimlib/security.h" -#include "wimlib/swm.h" #ifdef __WIN32__ # include "wimlib/win32.h" /* for realpath() equivalent */ #endif @@ -2561,8 +2560,6 @@ wimlib_extract_files(WIMStruct *wim, const struct wimlib_extract_command *cmds, size_t num_cmds, int default_extract_flags, - WIMStruct **additional_swms, - unsigned num_additional_swms, wimlib_progress_func_t progress_func) { int ret; @@ -2571,21 +2568,12 @@ wimlib_extract_files(WIMStruct *wim, default_extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC; - ret = verify_swm_set(wim, additional_swms, num_additional_swms); - if (ret) - goto out; - if (num_cmds == 0) - goto out; - - if (num_additional_swms) - merge_lookup_tables(wim, additional_swms, num_additional_swms); + return 0; cmds_copy = CALLOC(num_cmds, sizeof(cmds[0])); - if (!cmds_copy) { - ret = WIMLIB_ERR_NOMEM; - goto out_restore_lookup_table; - } + if (!cmds_copy) + return WIMLIB_ERR_NOMEM; for (size_t i = 0; i < num_cmds; i++) { cmds_copy[i].extract_flags = (default_extract_flags | @@ -2622,10 +2610,6 @@ out_free_cmds_copy: FREE(cmds_copy[i].fs_dest_path); } FREE(cmds_copy); -out_restore_lookup_table: - if (num_additional_swms) - unmerge_lookup_table(wim); -out: return ret; } @@ -2746,24 +2730,10 @@ do_wimlib_extract_image(WIMStruct *wim, int image, const tchar *target, int extract_flags, - WIMStruct **additional_swms, - unsigned num_additional_swms, wimlib_progress_func_t progress_func) { int ret; - if (extract_flags & WIMLIB_EXTRACT_FLAG_FROM_PIPE) { - wimlib_assert(wim->hdr.part_number == 1); - wimlib_assert(num_additional_swms == 0); - } else { - ret = verify_swm_set(wim, additional_swms, num_additional_swms); - if (ret) - return ret; - - if (num_additional_swms) - merge_lookup_tables(wim, additional_swms, num_additional_swms); - } - if (image == WIMLIB_ALL_IMAGES) { ret = extract_all_images(wim, target, extract_flags, progress_func); @@ -2779,8 +2749,6 @@ do_wimlib_extract_image(WIMStruct *wim, lte_free_extracted_file, NULL); } - if (num_additional_swms) - unmerge_lookup_table(wim); return ret; } @@ -2934,7 +2902,7 @@ wimlib_extract_image_from_pipe(int pipe_fd, const tchar *image_num_or_name, /* Extract the image. */ extract_flags |= WIMLIB_EXTRACT_FLAG_FROM_PIPE; ret = do_wimlib_extract_image(pwm, image, target, - extract_flags, NULL, 0, progress_func); + extract_flags, progress_func); /* Clean up and return. */ out_wimlib_free: wimlib_free(pwm); @@ -2947,12 +2915,9 @@ wimlib_extract_image(WIMStruct *wim, int image, const tchar *target, int extract_flags, - WIMStruct **additional_swms, - unsigned num_additional_swms, wimlib_progress_func_t progress_func) { extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC; return do_wimlib_extract_image(wim, image, target, extract_flags, - additional_swms, num_additional_swms, progress_func); } diff --git a/src/integrity.c b/src/integrity.c index 7502b6d2..99a9a9fd 100644 --- a/src/integrity.c +++ b/src/integrity.c @@ -328,13 +328,10 @@ write_integrity_table(WIMStruct *wim, struct integrity_table *old_table; struct integrity_table *new_table; int ret; - off_t cur_offset; u32 new_table_size; wimlib_assert(old_lookup_table_end <= new_lookup_table_end); - cur_offset = wim->out_fd.offset; - if (wim->hdr.integrity.offset == 0 || old_lookup_table_end == 0) { old_table = NULL; } else { diff --git a/src/join.c b/src/join.c index 2157697d..1045fc71 100644 --- a/src/join.c +++ b/src/join.c @@ -28,11 +28,119 @@ #endif #include "wimlib.h" +#include "wimlib/error.h" #include "wimlib/types.h" -#include "wimlib/swm.h" #include "wimlib/util.h" #include "wimlib/wim.h" +/* + * verify_swm_set: - Sanity checks to make sure a set of WIMs correctly + * correspond to a spanned set. + * + * @wim: + * Part 1 of the set. + * + * @additional_swms: + * All parts of the set other than part 1. + * + * @num_additional_swms: + * Number of WIMStructs in @additional_swms. Or, the total number of parts + * in the set minus 1. + * + * @return: + * 0 on success; WIMLIB_ERR_SPLIT_INVALID if the set is not valid. + */ +static int +verify_swm_set(WIMStruct *wim, WIMStruct **additional_swms, + unsigned num_additional_swms) +{ + unsigned total_parts = wim->hdr.total_parts; + int ctype; + const u8 *guid; + + if (total_parts != num_additional_swms + 1) { + ERROR("`%"TS"' says there are %u parts in the spanned set, " + "but %"TS"%u part%"TS" provided", + wim->filename, total_parts, + (num_additional_swms + 1 < total_parts) ? T("only ") : T(""), + num_additional_swms + 1, + (num_additional_swms) ? T("s were") : T(" was")); + return WIMLIB_ERR_SPLIT_INVALID; + } + if (wim->hdr.part_number != 1) { + ERROR("WIM `%"TS"' is not the first part of the split WIM.", + wim->filename); + return WIMLIB_ERR_SPLIT_INVALID; + } + for (unsigned i = 0; i < num_additional_swms; i++) { + if (additional_swms[i]->hdr.total_parts != total_parts) { + ERROR("WIM `%"TS"' says there are %u parts in the " + "spanned set, but %u parts were provided", + additional_swms[i]->filename, + additional_swms[i]->hdr.total_parts, + total_parts); + return WIMLIB_ERR_SPLIT_INVALID; + } + } + + /* keep track of ctype and guid just to make sure they are the same for + * all the WIMs. */ + ctype = wim->compression_type; + guid = wim->hdr.guid; + + { + /* parts_to_swms is not allocated at function scope because it + * should only be allocated after num_additional_swms was + * checked to be the same as wim->hdr.total_parts. Otherwise, it + * could be unexpectedly high and cause a stack overflow. */ + WIMStruct *parts_to_swms[num_additional_swms]; + ZERO_ARRAY(parts_to_swms); + for (unsigned i = 0; i < num_additional_swms; i++) { + + WIMStruct *swm = additional_swms[i]; + + if (swm->compression_type != ctype) { + ERROR("The split WIMs do not all have the same " + "compression type"); + return WIMLIB_ERR_SPLIT_INVALID; + } + if (memcmp(guid, swm->hdr.guid, WIM_GID_LEN) != 0) { + ERROR("The split WIMs do not all have the same " + "GUID"); + return WIMLIB_ERR_SPLIT_INVALID; + } + if (swm->hdr.part_number == 1) { + ERROR("WIMs `%"TS"' and `%"TS"' both are marked " + "as the first WIM in the spanned set", + wim->filename, swm->filename); + return WIMLIB_ERR_SPLIT_INVALID; + } + if (swm->hdr.part_number == 0 || + swm->hdr.part_number > total_parts) + { + ERROR("WIM `%"TS"' says it is part %u in the " + "spanned set, but the part number must " + "be in the range [1, %u]", + swm->filename, swm->hdr.part_number, total_parts); + return WIMLIB_ERR_SPLIT_INVALID; + } + if (parts_to_swms[swm->hdr.part_number - 2]) + { + ERROR("`%"TS"' and `%"TS"' are both marked as " + "part %u of %u in the spanned set", + parts_to_swms[swm->hdr.part_number - 2]->filename, + swm->filename, + swm->hdr.part_number, + total_parts); + return WIMLIB_ERR_SPLIT_INVALID; + } else { + parts_to_swms[swm->hdr.part_number - 2] = swm; + } + } + } + return 0; +} + /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_join(const tchar * const *swm_names, @@ -82,10 +190,15 @@ wimlib_join(const tchar * const *swm_names, if (ret) goto out_free_swms; - merge_lookup_tables(swm0, additional_swms, num_additional_swms); + ret = wimlib_reference_resources(swm0, additional_swms, + num_additional_swms, 0); + if (ret) + goto out_free_swms; ret = wimlib_write(swm0, output_path, WIMLIB_ALL_IMAGES, wim_write_flags, 1, progress_func); + wimlib_unreference_resources(swm0, additional_swms, + num_additional_swms); out_free_swms: for (i = 0; i < num_additional_swms; i++) wimlib_free(additional_swms[i]); diff --git a/src/lookup_table.c b/src/lookup_table.c index fee0f247..a6202ef2 100644 --- a/src/lookup_table.c +++ b/src/lookup_table.c @@ -31,6 +31,7 @@ #include "wimlib/endianness.h" #include "wimlib/error.h" #include "wimlib/file_io.h" +#include "wimlib/glob.h" #include "wimlib/lookup_table.h" #include "wimlib/metadata.h" #include "wimlib/paths.h" @@ -1010,6 +1011,18 @@ out: } #endif +int +resource_not_found_error(struct wim_inode *inode, const u8 *hash) +{ + if (wimlib_print_errors) { + ERROR("\"%"TS"\": resource not found", inode_first_full_path(inode)); + tfprintf(stderr, T(" SHA-1 message digest of missing resource:\n ")); + print_hash(hash, stderr); + tputc(T('\n'), stderr); + } + return WIMLIB_ERR_RESOURCE_NOT_FOUND; +} + /* * Resolve an inode's lookup table entries. * @@ -1081,14 +1094,9 @@ inode_resolve_ltes(struct wim_inode *inode, struct wim_lookup_table *table, inode->i_resolved = 1; } return 0; + resource_not_found: - if (wimlib_print_errors) { - ERROR("\"%"TS"\": resource not found", inode_first_full_path(inode)); - tfprintf(stderr, T(" SHA-1 message digest of missing resource:\n ")); - print_hash(hash, stderr); - tputc(T('\n'), stderr); - } - return WIMLIB_ERR_RESOURCE_NOT_FOUND; + return resource_not_found_error(inode, hash); } void @@ -1272,3 +1280,227 @@ hash_unhashed_stream(struct wim_lookup_table_entry *lte, *lte_ret = lte; return 0; } + +static int +move_lte_to_table(struct wim_lookup_table_entry *lte, void *_combined_table) +{ + struct wim_lookup_table *combined_table = _combined_table; + + hlist_del(<e->hash_list); + lookup_table_insert(combined_table, lte); + return 0; +} + +static void +lookup_table_join(struct wim_lookup_table *combined_table, + struct wim_lookup_table *part_table) +{ + for_lookup_table_entry(part_table, move_lte_to_table, combined_table); + part_table->num_entries = 0; +} + +static void +merge_lookup_tables(WIMStruct *wim, WIMStruct **resource_wims, + unsigned num_resource_wims) +{ + for (unsigned i = 0; i < num_resource_wims; i++) { + lookup_table_join(wim->lookup_table, resource_wims[i]->lookup_table); + list_add(&resource_wims[i]->resource_wim_node, &wim->resource_wims); + resource_wims[i]->master_wim = wim; + } +} + +static int +move_lte_to_orig_table(struct wim_lookup_table_entry *lte, void *_wim) +{ + WIMStruct *wim = _wim; + + if (lte->resource_location == RESOURCE_IN_WIM && + lte->wim->being_unmerged) + { + move_lte_to_table(lte, lte->wim->lookup_table); + wim->lookup_table->num_entries--; + } + return 0; +} + +static int +check_reference_params(WIMStruct *wim, + WIMStruct **resource_wims, unsigned num_resource_wims, + WIMStruct *expected_master) +{ + if (wim == NULL) + return WIMLIB_ERR_INVALID_PARAM; + + if (wim->hdr.part_number != 1) + return WIMLIB_ERR_INVALID_PARAM; + + if (num_resource_wims != 0 && resource_wims == NULL) + return WIMLIB_ERR_INVALID_PARAM; + + for (unsigned i = 0; i < num_resource_wims; i++) { + if (resource_wims[i] == NULL) + return WIMLIB_ERR_INVALID_PARAM; + if (resource_wims[i]->master_wim != expected_master) + return WIMLIB_ERR_INVALID_PARAM; + } + return 0; +} + +/* API function documented in wimlib.h */ +WIMLIBAPI int +wimlib_reference_resources(WIMStruct *wim, + WIMStruct **resource_wims, unsigned num_resource_wims, + int ref_flags) +{ + int ret; + + ret = check_reference_params(wim, resource_wims, + num_resource_wims, NULL); + if (ret) + return ret; + + merge_lookup_tables(wim, resource_wims, num_resource_wims); + return 0; +} + +static int +reference_resource_paths(WIMStruct *wim, + const tchar * const *resource_wimfiles, + unsigned num_resource_wimfiles, + int ref_flags, + int open_flags, + wimlib_progress_func_t progress_func) +{ + WIMStruct **resource_wims; + unsigned i; + int ret; + + open_flags |= WIMLIB_OPEN_FLAG_SPLIT_OK; + + resource_wims = CALLOC(num_resource_wimfiles, sizeof(resource_wims[0])); + if (!resource_wims) + return WIMLIB_ERR_NOMEM; + + for (i = 0; i < num_resource_wimfiles; i++) { + ret = wimlib_open_wim(resource_wimfiles[i], open_flags, + &resource_wims[i], progress_func); + if (ret) + goto out_free_resource_wims; + } + + ret = wimlib_reference_resources(wim, resource_wims, + num_resource_wimfiles, ref_flags); + if (ret) + goto out_free_resource_wims; + + for (i = 0; i < num_resource_wimfiles; i++) + resource_wims[i]->is_owned_by_master = 1; + + ret = 0; + goto out_free_array; + +out_free_resource_wims: + for (i = 0; i < num_resource_wimfiles; i++) + wimlib_free(resource_wims[i]); +out_free_array: + FREE(resource_wims); + return ret; +} + +static int +reference_resource_glob(WIMStruct *wim, const tchar *refglob, + int ref_flags, int open_flags, + wimlib_progress_func_t progress_func) +{ + glob_t globbuf; + int ret; + + /* Note: glob() is replaced in Windows native builds. */ + ret = tglob(refglob, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf); + if (ret) { + if (ret == GLOB_NOMATCH) { + if (ref_flags & WIMLIB_REF_FLAG_GLOB_ERR_ON_NOMATCH) { + ERROR("Found no files for glob \"%"TS"\"", refglob); + return WIMLIB_ERR_GLOB_HAD_NO_MATCHES; + } else { + return reference_resource_paths(wim, + &refglob, + 1, + ref_flags, + open_flags, + progress_func); + } + } else { + ERROR_WITH_ERRNO("Failed to process glob \"%"TS"\"", refglob); + if (ret == GLOB_NOSPACE) + return WIMLIB_ERR_NOMEM; + else + return WIMLIB_ERR_READ; + } + } + + ret = reference_resource_paths(wim, + (const tchar * const *)globbuf.gl_pathv, + globbuf.gl_pathc, + ref_flags, + open_flags, + progress_func); + globfree(&globbuf); + return ret; +} + +/* API function documented in wimlib.h */ +WIMLIBAPI int +wimlib_reference_resource_files(WIMStruct *wim, + const tchar * const * resource_wimfiles_or_globs, + unsigned count, + int ref_flags, + int open_flags, + wimlib_progress_func_t progress_func) +{ + unsigned i; + int ret; + + if (ref_flags & WIMLIB_REF_FLAG_GLOB_ENABLE) { + for (i = 0; i < count; i++) { + ret = reference_resource_glob(wim, + resource_wimfiles_or_globs[i], + ref_flags, + open_flags, + progress_func); + if (ret) + return ret; + } + return 0; + } else { + return reference_resource_paths(wim, resource_wimfiles_or_globs, + count, ref_flags, + open_flags, progress_func); + } +} + +/* API function documented in wimlib.h */ +WIMLIBAPI int +wimlib_unreference_resources(WIMStruct *wim, + WIMStruct **resource_wims, unsigned num_resource_wims) +{ + int ret; + unsigned i; + + ret = check_reference_params(wim, resource_wims, num_resource_wims, wim); + if (ret) + return ret; + + for (i = 0; i < num_resource_wims; i++) + resource_wims[i]->being_unmerged = 1; + + for_lookup_table_entry(wim->lookup_table, move_lte_to_orig_table, wim); + + for (i = 0; i < num_resource_wims; i++) { + resource_wims[i]->being_unmerged = 0; + list_del(&resource_wims[i]->resource_wim_node); + resource_wims[i]->master_wim = NULL; + } + return 0; +} diff --git a/src/mount_image.c b/src/mount_image.c index 12b193e3..85c0dee4 100644 --- a/src/mount_image.c +++ b/src/mount_image.c @@ -46,7 +46,6 @@ #include "wimlib/paths.h" #include "wimlib/reparse.h" #include "wimlib/resource.h" -#include "wimlib/swm.h" #include "wimlib/timestamp.h" #include "wimlib/version.h" #include "wimlib/write.h" @@ -2390,9 +2389,7 @@ static struct fuse_operations wimfs_operations = { /* API function documented in wimlib.h */ WIMLIBAPI int wimlib_mount_image(WIMStruct *wim, int image, const char *dir, - int mount_flags, WIMStruct **additional_swms, - unsigned num_additional_swms, - const char *staging_dir) + int mount_flags, const char *staging_dir) { int argc; char *argv[16]; @@ -2405,27 +2402,18 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir, DEBUG("Mount: wim = %p, image = %d, dir = %s, flags = %d, ", wim, image, dir, mount_flags); - if (!wim || !dir) { - ret = WIMLIB_ERR_INVALID_PARAM; - goto out; - } - - ret = verify_swm_set(wim, additional_swms, num_additional_swms); - if (ret) - goto out; + if (!wim || !dir) + return WIMLIB_ERR_INVALID_PARAM; if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) { ret = can_delete_from_wim(wim); if (ret) - goto out; + return ret; } - if (num_additional_swms) - merge_lookup_tables(wim, additional_swms, num_additional_swms); - ret = select_wim_image(wim, image); if (ret) - goto out_restore_lookup_table; + return ret; DEBUG("Selected image %d", image); @@ -2434,21 +2422,19 @@ wimlib_mount_image(WIMStruct *wim, int image, const char *dir, if (imd->refcnt != 1) { ERROR("Cannot mount image that was just exported with " "wimlib_export_image()"); - ret = WIMLIB_ERR_INVALID_PARAM; - goto out_restore_lookup_table; + return WIMLIB_ERR_INVALID_PARAM; } if (imd->modified) { ERROR("Cannot mount image that was added " "with wimlib_add_image()"); - ret = WIMLIB_ERR_INVALID_PARAM; - goto out_restore_lookup_table; + return WIMLIB_ERR_INVALID_PARAM; } if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) { ret = lock_wim(wim, wim->in_fd.fd); if (ret) - goto out_restore_lookup_table; + return ret; } /* Use default stream interface if one was not specified */ @@ -2572,10 +2558,6 @@ out_unlock: wim->wim_locked = 0; out_free_message_queue_names: free_message_queue_names(&ctx); -out_restore_lookup_table: - if (num_additional_swms) - unmerge_lookup_table(wim); -out: return ret; } @@ -2652,9 +2634,7 @@ wimlib_unmount_image(const tchar *dir, int unmount_flags, WIMLIBAPI int wimlib_mount_image(WIMStruct *wim, int image, const tchar *dir, - int mount_flags, WIMStruct **additional_swms, - unsigned num_additional_swms, - const tchar *staging_dir) + int mount_flags, const tchar *staging_dir) { return mount_unsupported_error(); } diff --git a/src/split.c b/src/split.c index afb4365c..2d855a6f 100644 --- a/src/split.c +++ b/src/split.c @@ -203,8 +203,8 @@ wimlib_split(WIMStruct *wim, const tchar *swm_name, if (swm_name == NULL || swm_name[0] == T('\0') || part_size == 0) return WIMLIB_ERR_INVALID_PARAM; - if (wim->hdr.total_parts != 1) - return WIMLIB_ERR_SPLIT_UNSUPPORTED; + if (!wim_has_metadata(wim)) + return WIMLIB_ERR_INVALID_PARAM; memset(&swm_info, 0, sizeof(swm_info)); swm_info.max_part_size = part_size; diff --git a/src/swm.c b/src/swm.c deleted file mode 100644 index 5ea5d4fa..00000000 --- a/src/swm.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * swm.c - * - * Functions to help handle split WIMs. - */ - -/* - * Copyright (C) 2012, 2013 Eric Biggers - * - * This file is part of wimlib, a library for working with WIM files. - * - * wimlib 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. - * - * wimlib 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 wimlib; if not, see http://www.gnu.org/licenses/. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "wimlib/error.h" -#include "wimlib/lookup_table.h" -#include "wimlib/swm.h" -#include "wimlib/wim.h" - -static int -move_lte_to_table(struct wim_lookup_table_entry *lte, void *combined_table) -{ - hlist_del(<e->hash_list); - lookup_table_insert((struct wim_lookup_table*)combined_table, lte); - return 0; -} - -static void -lookup_table_join(struct wim_lookup_table *combined_table, - struct wim_lookup_table *part_table) -{ - for_lookup_table_entry(part_table, move_lte_to_table, combined_table); - part_table->num_entries = 0; -} - -/* - * merge_lookup_tables() - Merge lookup tables from the parts of a split WIM. - * - * @wim specifies the first part, while @additional_swms and @num_additional_swms - * specify an array of pointers to the WIMStruct's for additional split WIM parts. - * - * The reason we join the lookup tables is so we only have to search one lookup - * table to find the location of a resource in the entire WIM. - */ -void -merge_lookup_tables(WIMStruct *wim, - WIMStruct **additional_swms, - unsigned num_additional_swms) -{ - for (unsigned i = 0; i < num_additional_swms; i++) - lookup_table_join(wim->lookup_table, additional_swms[i]->lookup_table); -} - -static int -move_lte_to_orig_table(struct wim_lookup_table_entry *lte, void *_wim) -{ - WIMStruct *wim = _wim; - if (lte->wim != wim) { - move_lte_to_table(lte, lte->wim->lookup_table); - wim->lookup_table->num_entries--; - } - return 0; -} - -/* Undo merge_lookup_tables(), given the first WIM part that contains the merged - * lookup table. */ -void -unmerge_lookup_table(WIMStruct *wim) -{ - for_lookup_table_entry(wim->lookup_table, move_lte_to_orig_table, wim); -} - -/* - * verify_swm_set: - Sanity checks to make sure a set of WIMs correctly - * correspond to a spanned set. - * - * @wim: - * Part 1 of the set. - * - * @additional_swms: - * All parts of the set other than part 1. - * - * @num_additional_swms: - * Number of WIMStructs in @additional_swms. Or, the total number of parts - * in the set minus 1. - * - * @return: - * 0 on success; WIMLIB_ERR_SPLIT_INVALID if the set is not valid. - */ -int -verify_swm_set(WIMStruct *wim, WIMStruct **additional_swms, - unsigned num_additional_swms) -{ - unsigned total_parts = wim->hdr.total_parts; - int ctype; - const u8 *guid; - - if (total_parts != num_additional_swms + 1) { - ERROR("`%"TS"' says there are %u parts in the spanned set, " - "but %"TS"%u part%"TS" provided", - wim->filename, total_parts, - (num_additional_swms + 1 < total_parts) ? T("only ") : T(""), - num_additional_swms + 1, - (num_additional_swms) ? T("s were") : T(" was")); - return WIMLIB_ERR_SPLIT_INVALID; - } - if (wim->hdr.part_number != 1) { - ERROR("WIM `%"TS"' is not the first part of the split WIM.", - wim->filename); - return WIMLIB_ERR_SPLIT_INVALID; - } - for (unsigned i = 0; i < num_additional_swms; i++) { - if (additional_swms[i]->hdr.total_parts != total_parts) { - ERROR("WIM `%"TS"' says there are %u parts in the " - "spanned set, but %u parts were provided", - additional_swms[i]->filename, - additional_swms[i]->hdr.total_parts, - total_parts); - return WIMLIB_ERR_SPLIT_INVALID; - } - } - - /* keep track of ctype and guid just to make sure they are the same for - * all the WIMs. */ - ctype = wim->compression_type; - guid = wim->hdr.guid; - - { - /* parts_to_swms is not allocated at function scope because it - * should only be allocated after num_additional_swms was - * checked to be the same as wim->hdr.total_parts. Otherwise, it - * could be unexpectedly high and cause a stack overflow. */ - WIMStruct *parts_to_swms[num_additional_swms]; - ZERO_ARRAY(parts_to_swms); - for (unsigned i = 0; i < num_additional_swms; i++) { - - WIMStruct *swm = additional_swms[i]; - - if (swm->compression_type != ctype) { - ERROR("The split WIMs do not all have the same " - "compression type"); - return WIMLIB_ERR_SPLIT_INVALID; - } - if (memcmp(guid, swm->hdr.guid, WIM_GID_LEN) != 0) { - ERROR("The split WIMs do not all have the same " - "GUID"); - return WIMLIB_ERR_SPLIT_INVALID; - } - if (swm->hdr.part_number == 1) { - ERROR("WIMs `%"TS"' and `%"TS"' both are marked " - "as the first WIM in the spanned set", - wim->filename, swm->filename); - return WIMLIB_ERR_SPLIT_INVALID; - } - if (swm->hdr.part_number == 0 || - swm->hdr.part_number > total_parts) - { - ERROR("WIM `%"TS"' says it is part %u in the " - "spanned set, but the part number must " - "be in the range [1, %u]", - swm->filename, swm->hdr.part_number, total_parts); - return WIMLIB_ERR_SPLIT_INVALID; - } - if (parts_to_swms[swm->hdr.part_number - 2]) - { - ERROR("`%"TS"' and `%"TS"' are both marked as " - "part %u of %u in the spanned set", - parts_to_swms[swm->hdr.part_number - 2]->filename, - swm->filename, - swm->hdr.part_number, - total_parts); - return WIMLIB_ERR_SPLIT_INVALID; - } else { - parts_to_swms[swm->hdr.part_number - 2] = swm; - } - } - } - return 0; -} diff --git a/src/util.c b/src/util.c index 6f17b5bf..8416bb46 100644 --- a/src/util.c +++ b/src/util.c @@ -286,6 +286,8 @@ static const tchar *error_strings[] = { [WIMLIB_ERR_FUSERMOUNT] = T("Could not execute the `fusermount' program, or it exited " "with a failure status"), + [WIMLIB_ERR_GLOB_HAD_NO_MATCHES] + = T("The provided file glob did not match any files"), [WIMLIB_ERR_ICONV_NOT_AVAILABLE] = T("The iconv() function does not seem to work. " "Maybe check to make sure the directory /usr/lib/gconv exists"), @@ -345,6 +347,8 @@ static const tchar *error_strings[] = { [WIMLIB_ERR_LINK] = T("Failed to create a hard or symbolic link when extracting " "a file from the WIM"), + [WIMLIB_ERR_METADATA_NOT_FOUND] + = T("A required metadata resource could not be located"), [WIMLIB_ERR_MKDIR] = T("Failed to create a directory"), [WIMLIB_ERR_MQUEUE] diff --git a/src/wim.c b/src/wim.c index 5ebf94ca..f91c98bc 100644 --- a/src/wim.c +++ b/src/wim.c @@ -71,6 +71,7 @@ new_wim_struct(void) wim->in_fd.fd = -1; wim->out_fd.fd = -1; } + INIT_LIST_HEAD(&wim->resource_wims); return wim; } @@ -167,9 +168,11 @@ select_wim_image(WIMStruct *wim, int image) return WIMLIB_ERR_INVALID_IMAGE; } - if (wim->hdr.part_number != 1) { - ERROR("Cannot select an image from a non-first part of a split WIM"); - return WIMLIB_ERR_SPLIT_UNSUPPORTED; + if (!wim_has_metadata(wim)) { + ERROR("\"%"TS"\" does not contain metadata resources!", wim->filename); + if (wim->hdr.part_number != 1) + ERROR("Specify the first part of the split WIM instead."); + return WIMLIB_ERR_METADATA_NOT_FOUND; } /* If a valid image is currently selected, it can be freed if it is not @@ -278,12 +281,6 @@ wimlib_print_available_images(const WIMStruct *wim, int image) WIMLIBAPI int wimlib_print_metadata(WIMStruct *wim, int image) { - if (wim->hdr.part_number != 1) { - ERROR("Cannot show the metadata from part %hu of a %hu-part split WIM!", - wim->hdr.part_number, wim->hdr.total_parts); - ERROR("Select the first part of the split WIM to see the metadata."); - return WIMLIB_ERR_SPLIT_UNSUPPORTED; - } return for_image(wim, image, image_print_metadata); } @@ -759,11 +756,26 @@ wimlib_free(WIMStruct *wim) if (!wim) return; + + while (!list_empty(&wim->resource_wims)) { + WIMStruct *resource_wim; + + resource_wim = list_entry(wim->resource_wims.next, + WIMStruct, resource_wim_node); + if (resource_wim->is_owned_by_master) { + list_del(&resource_wim->resource_wim_node); + wimlib_free(resource_wim); + } else { + wimlib_unreference_resources(wim, &resource_wim, 1); + } + } + if (filedes_valid(&wim->in_fd)) filedes_close(&wim->in_fd); if (filedes_valid(&wim->out_fd)) filedes_close(&wim->out_fd); + free_lookup_table(wim->lookup_table); FREE(wim->filename); diff --git a/src/win32_apply.c b/src/win32_apply.c index 48dd66d3..b1253956 100644 --- a/src/win32_apply.c +++ b/src/win32_apply.c @@ -191,7 +191,7 @@ win32_extract_stream(const wchar_t *path, const wchar_t *stream_name, stream_path = alloca(sizeof(wchar_t) * (wcslen(path) + 1 + wcslen(stream_name) + 1)); - swprintf(stream_path, L"%ls:%ls", path, stream_name); + tsprintf(stream_path, L"%ls:%ls", path, stream_name); } h = CreateFile(stream_path, FILE_WRITE_DATA, 0, NULL, diff --git a/src/win32_capture.c b/src/win32_capture.c index 3466ad47..14a6c134 100644 --- a/src/win32_capture.c +++ b/src/win32_capture.c @@ -945,7 +945,7 @@ win32_capture_stream(const wchar_t *path, spath_buf_nbytes = (spath_nchars + 1) * sizeof(wchar_t); spath = MALLOC(spath_buf_nbytes); - swprintf(spath, L"%ls%ls%ls%ls", + tsprintf(spath, L"%ls%ls%ls%ls", relpath_prefix, path, colonchar, stream_name); /* Make a new wim_lookup_table_entry */ diff --git a/src/win32_replacements.c b/src/win32_replacements.c index 17861ef7..4e9df0ae 100644 --- a/src/win32_replacements.c +++ b/src/win32_replacements.c @@ -28,12 +28,14 @@ # include "config.h" #endif +#include #include #include /* for PathMatchSpecW() */ #include "wimlib/win32_common.h" #include "wimlib/assert.h" #include "wimlib/file_io.h" +#include "wimlib/glob.h" #include "wimlib/error.h" #include "wimlib/util.h" @@ -287,5 +289,109 @@ out: return ret; } +/* Replacement for glob() in Windows native builds that operates on wide + * characters. */ +int +win32_wglob(const wchar_t *pattern, int flags, + int (*errfunc)(const wchar_t *epath, int eerrno), + glob_t *pglob) +{ + WIN32_FIND_DATAW dat; + DWORD err; + HANDLE hFind; + int ret; + size_t nspaces; + + const wchar_t *backslash, *end_slash; + size_t prefix_len; + + backslash = wcsrchr(pattern, L'\\'); + end_slash = wcsrchr(pattern, L'/'); + + if (backslash > end_slash) + end_slash = backslash; + + if (end_slash) + prefix_len = end_slash - pattern + 1; + else + prefix_len = 0; + + /* This function does not support all functionality of the POSIX glob(), + * so make sure the parameters are consistent with supported + * functionality. */ + wimlib_assert(errfunc == NULL); + wimlib_assert((flags & GLOB_ERR) == GLOB_ERR); + wimlib_assert((flags & ~(GLOB_NOSORT | GLOB_ERR)) == 0); + + hFind = FindFirstFileW(pattern, &dat); + if (hFind == INVALID_HANDLE_VALUE) { + err = GetLastError(); + if (err == ERROR_FILE_NOT_FOUND) { + errno = 0; + return GLOB_NOMATCH; + } else { + /* The other possible error codes for FindFirstFile() + * are undocumented. */ + errno = EIO; + return GLOB_ABORTED; + } + } + pglob->gl_pathc = 0; + pglob->gl_pathv = NULL; + nspaces = 0; + do { + wchar_t *path; + if (pglob->gl_pathc == nspaces) { + size_t new_nspaces; + wchar_t **pathv; + + new_nspaces = nspaces * 2 + 1; + pathv = REALLOC(pglob->gl_pathv, + new_nspaces * sizeof(pglob->gl_pathv[0])); + if (!pathv) + goto oom; + pglob->gl_pathv = pathv; + nspaces = new_nspaces; + } + size_t filename_len = wcslen(dat.cFileName); + size_t len_needed = prefix_len + filename_len; + + path = MALLOC((len_needed + 1) * sizeof(wchar_t)); + if (!path) + goto oom; + + wmemcpy(path, pattern, prefix_len); + wmemcpy(path + prefix_len, dat.cFileName, filename_len + 1); + pglob->gl_pathv[pglob->gl_pathc++] = path; + } while (FindNextFileW(hFind, &dat)); + err = GetLastError(); + CloseHandle(hFind); + if (err == ERROR_NO_MORE_FILES) { + errno = 0; + return 0; + } else { + /* Other possible error codes for FindNextFile() are + * undocumented */ + errno = EIO; + ret = GLOB_ABORTED; + goto fail_globfree; + } +oom: + CloseHandle(hFind); + errno = ENOMEM; + ret = GLOB_NOSPACE; +fail_globfree: + globfree(pglob); + return ret; +} + +void +globfree(glob_t *pglob) +{ + size_t i; + for (i = 0; i < pglob->gl_pathc; i++) + free(pglob->gl_pathv[i]); + free(pglob->gl_pathv); +} #endif /* __WIN32__ */ diff --git a/src/write.c b/src/write.c index 939fe459..b1654e9a 100644 --- a/src/write.c +++ b/src/write.c @@ -2416,10 +2416,12 @@ write_wim_part(WIMStruct *wim, (image < 1 || image > wim->hdr.image_count)) return WIMLIB_ERR_INVALID_IMAGE; - /* @wim must specify a standalone WIM, or at least the first part of a - * split WIM. */ - if (wim->hdr.part_number != 1) - return WIMLIB_ERR_SPLIT_UNSUPPORTED; + /* If we need to write metadata resources, make sure the ::WIMStruct has + * the needed information attached (e.g. is not a resource-only WIM, + * such as a non-first part of a split WIM). */ + if (!wim_has_metadata(wim) && + !(write_flags & WIMLIB_WRITE_FLAG_NO_METADATA)) + return WIMLIB_ERR_METADATA_NOT_FOUND; /* Check for contradictory flags. */ if ((write_flags & (WIMLIB_WRITE_FLAG_CHECK_INTEGRITY | diff --git a/src/xml.c b/src/xml.c index cd98a795..8d2b9106 100644 --- a/src/xml.c +++ b/src/xml.c @@ -132,27 +132,37 @@ windows_info_xml_string_specs[] = { u64 wim_info_get_total_bytes(const struct wim_info *info) { - if (!info) + if (info) + return info->total_bytes; + else return 0; - return info->total_bytes; } u64 wim_info_get_image_hard_link_bytes(const struct wim_info *info, int image) { - return info->images[image - 1].hard_link_bytes; + if (info) + return info->images[image - 1].hard_link_bytes; + else + return 0; } u64 wim_info_get_image_total_bytes(const struct wim_info *info, int image) { - return info->images[image - 1].total_bytes; + if (info) + return info->images[image - 1].total_bytes; + else + return 0; } unsigned wim_info_get_num_images(const struct wim_info *info) { - return info->num_images; + if (info) + return info->num_images; + else + return 0; } /* Returns a statically allocated string that is a string representation of the -- 2.43.0