Generalized support for referencing resources in external WIMs
authorEric Biggers <ebiggers3@gmail.com>
Tue, 20 Aug 2013 00:17:36 +0000 (19:17 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Tue, 20 Aug 2013 00:28:38 +0000 (19:28 -0500)
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.

31 files changed:
Makefile.am
NEWS
doc/imagex-apply.1.in
doc/imagex-export.1.in
doc/imagex-extract.1.in
doc/imagex-mount.1.in
include/wimlib.h
include/wimlib/glob.h [new file with mode: 0644]
include/wimlib/lookup_table.h
include/wimlib/swm.h [deleted file]
include/wimlib/wim.h
include/wimlib_tchar.h
programs/imagex-win32.c
programs/imagex-win32.h
programs/imagex.c
src/dentry.c
src/export_image.c
src/extract.c
src/integrity.c
src/join.c
src/lookup_table.c
src/mount_image.c
src/split.c
src/swm.c [deleted file]
src/util.c
src/wim.c
src/win32_apply.c
src/win32_capture.c
src/win32_replacements.c
src/write.c
src/xml.c

index 6d24bdf..d2e80a2 100644 (file)
@@ -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 c31cea5..783e025 100644 (file)
--- 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
index 1166a6a..0c536df 100644 (file)
@@ -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
index 34b63ea..f1d7e3e 100644 (file)
@@ -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
index 40e2976..55155a8 100644 (file)
@@ -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.
index 33102ff..f2a50e6 100644 (file)
@@ -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)
index 40d062a..197f9b6 100644 (file)
@@ -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-<code>NULL</code>, @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);
 
 /**
@@ -2626,6 +2648,93 @@ 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
  * intervening time.  This is designed to be used in incremental backups of 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.
  *
@@ -3003,6 +3114,30 @@ wimlib_unmount_image(const wimlib_tchar *dir,
                     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.
  *
  * @param wim
@@ -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 (file)
index 0000000..22ebfa9
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef _WIMLIB_GLOB_H
+#define _WIMLIB_GLOB_H
+
+#ifndef __WIN32__
+#  include <glob.h>
+#else
+
+#include <wchar.h>
+#include <stddef.h>
+
+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 */
index 481794d..746d744 100644 (file)
@@ -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 (file)
index 08a51fc..0000000
+++ /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
index dfd31fa..fc36c51 100644 (file)
@@ -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);
 
index fefaabc..4d33efe 100644 (file)
@@ -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 */
index a0cbc33..083c145 100644 (file)
 #endif
 
 #include "imagex-win32.h"
-#include <assert.h>
-#include <errno.h>
 #include <fcntl.h>
 #include <io.h>
 #include <stdio.h>
-#include <string.h>
 #include <windows.h>
 
-/* 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)
index 0d5f5ad..bf1d2a7 100644 (file)
@@ -5,40 +5,6 @@
 #include <stdbool.h>
 #include <wchar.h>
 
-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);
 
index f7a3620..e3800ca 100644 (file)
@@ -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 <getopt.h>
 #  include <langinfo.h>
 #  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 */
 
index ca9fe0b..a73828b 100644 (file)
@@ -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;
index 17c4f7d..81c6659 100644 (file)
 #include "wimlib/error.h"
 #include "wimlib/lookup_table.h"
 #include "wimlib/metadata.h"
-#include "wimlib/swm.h"
 #include "wimlib/xml.h"
+#include <stdlib.h>
 
 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(&lte_list_head);
-       image_for_each_inode(inode, src_imd) {
-               ret = inode_allocate_needed_ltes(inode,
-                                                src_wim->lookup_table,
-                                                dest_wim->lookup_table,
-                                                &lte_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,
-                                        &lte_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, &lte_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;
 }
index c04f11b..c9f6e21 100644 (file)
@@ -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);
 }
index 7502b6d..99a9a9f 100644 (file)
@@ -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 {
index 2157697..1045fc7 100644 (file)
 #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]);
index fee0f24..a6202ef 100644 (file)
@@ -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(&lte->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;
+}
index 12b193e..85c0dee 100644 (file)
@@ -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();
 }
index afb4365..2d855a6 100644 (file)
@@ -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 (file)
index 5ea5d4f..0000000
--- 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(&lte->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;
-}
index 6f17b5b..8416bb4 100644 (file)
@@ -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]
index 5ebf94c..f91c98b 100644 (file)
--- 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);
index 48dd66d..b125395 100644 (file)
@@ -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,
index 3466ad4..14a6c13 100644 (file)
@@ -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 */
index 17861ef..4e9df0a 100644 (file)
 #  include "config.h"
 #endif
 
+#include <errno.h>
 #include <pthread.h>
 #include <shlwapi.h> /* 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__ */
index 939fe45..b1654e9 100644 (file)
@@ -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 |
index cd98a79..8d2b910 100644 (file)
--- 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