+ return match_pattern_list(path, path_nchars, pats);
+}
+
+/* Returns %true if the specified absolute path to a file in the WIM image can
+ * be subject to external backing when extracted. Otherwise returns %false. */
+static bool
+can_externally_back_path(const wchar_t *path, size_t path_nchars,
+ const struct win32_apply_ctx *ctx)
+{
+ if (in_prepopulate_list(path, path_nchars, ctx))
+ return false;
+
+ /* Since we attempt to modify the SYSTEM registry after it's extracted
+ * (see end_wimboot_extraction()), it can't be extracted as externally
+ * backed. This extends to associated files such as SYSTEM.LOG that
+ * also must be writable in order to write to the registry. Normally,
+ * SYSTEM is in [PrepopulateList], and the SYSTEM.* files match patterns
+ * in [ExclusionList] and therefore are not captured in the WIM at all.
+ * However, a WIM that wasn't specifically captured in "WIMBoot mode"
+ * may contain SYSTEM.* files. So to make things "just work", hard-code
+ * the pattern. */
+ if (match_path(path, path_nchars, L"\\Windows\\System32\\config\\SYSTEM*",
+ OS_PREFERRED_PATH_SEPARATOR, false))
+ return false;
+
+ return true;
+}
+
+static bool
+is_resource_valid_for_external_backing(const struct wim_resource_descriptor *rdesc)
+{
+ /* Must be the original WIM file format. This check excludes pipable
+ * resources and solid resources. It also excludes other resources
+ * contained in such files even if they would be otherwise compatible.
+ */
+ if (rdesc->wim->hdr.magic != WIM_MAGIC ||
+ rdesc->wim->hdr.wim_version != WIM_VERSION_DEFAULT)
+ return false;
+
+ /*
+ * Whitelist of compression types and chunk sizes supported by
+ * Microsoft's WOF driver.
+ *
+ * Notes:
+ * - Uncompressed WIMs result in BSOD. However, this only applies to
+ * the WIM file itself, not to uncompressed resources in a WIM file
+ * that is otherwise compressed.
+ * - XPRESS 64K sometimes appears to work, but sometimes it causes
+ * reads to fail with STATUS_UNSUCCESSFUL.
+ */
+ switch (rdesc->compression_type) {
+ case WIMLIB_COMPRESSION_TYPE_NONE:
+ if (rdesc->wim->compression_type == WIMLIB_COMPRESSION_TYPE_NONE)
+ return false;
+ break;
+ case WIMLIB_COMPRESSION_TYPE_XPRESS:
+ switch (rdesc->chunk_size) {
+ case 4096:
+ case 8192:
+ case 16384:
+ case 32768:
+ break;
+ default:
+ return false;
+ }
+ break;
+ case WIMLIB_COMPRESSION_TYPE_LZX:
+ switch (rdesc->chunk_size) {
+ case 32768:
+ break;
+ default:
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+
+ /* Microsoft's WoF driver errors out if it tries to satisfy a read with
+ * ending offset >= 4 GiB from an externally backed file. */
+ if (rdesc->uncompressed_size > 4200000000)
+ return false;
+
+ return true;
+}
+
+#define WIM_BACKING_NOT_ENABLED -1
+#define WIM_BACKING_NOT_POSSIBLE -2
+#define WIM_BACKING_EXCLUDED -3
+
+static int
+will_externally_back_inode(struct wim_inode *inode, struct win32_apply_ctx *ctx,
+ const struct wim_dentry **excluded_dentry_ret)
+{
+ struct list_head *next;
+ struct wim_dentry *dentry;
+ struct blob_descriptor *blob;
+ int ret;
+
+ if (inode->i_can_externally_back)
+ return 0;
+
+ /* This may do redundant checks because the cached value
+ * i_can_externally_back is 2-state (as opposed to 3-state:
+ * unknown/no/yes). But most files can be externally backed, so this
+ * way is fine. */
+
+ if (inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY |
+ FILE_ATTRIBUTE_REPARSE_POINT |
+ FILE_ATTRIBUTE_ENCRYPTED))
+ return WIM_BACKING_NOT_POSSIBLE;
+
+ blob = inode_get_blob_for_unnamed_data_stream_resolved(inode);
+
+ if (!blob || blob->blob_location != BLOB_IN_WIM ||
+ !is_resource_valid_for_external_backing(blob->rdesc, ctx))
+ return WIM_BACKING_NOT_POSSIBLE;
+
+ /*
+ * We need to check the patterns in [PrepopulateList] against every name
+ * of the inode, in case any of them match.
+ */
+ next = inode->i_extraction_aliases.next;
+ do {
+ dentry = list_entry(next, struct wim_dentry,
+ d_extraction_alias_node);
+
+ ret = calculate_dentry_full_path(dentry);
+ if (ret)
+ return ret;
+
+ if (!can_externally_back_path(dentry->_full_path,
+ wcslen(dentry->_full_path), ctx))
+ {
+ if (excluded_dentry_ret)
+ *excluded_dentry_ret = dentry;
+ return WIM_BACKING_EXCLUDED;
+ }
+ next = next->next;
+ } while (next != &inode->i_extraction_aliases);
+
+ inode->i_can_externally_back = 1;
+ return 0;