+/* Returns the SHA1 message digest of the unnamed data stream of a WIM inode, or
+ * 'zero_hash' if the unnamed data stream is missing has all zeroes in its SHA1
+ * message digest field. */
+const u8 *
+inode_unnamed_stream_hash(const struct wim_inode *inode)
+{
+ const u8 *hash;
+
+ for (unsigned i = 0; i <= inode->i_num_ads; i++) {
+ if (inode_stream_name_nbytes(inode, i) == 0) {
+ hash = inode_stream_hash(inode, i);
+ if (!is_zero_hash(hash))
+ return hash;
+ }
+ }
+ return zero_hash;
+}
+
+struct wim_lookup_table_entry **
+retrieve_lte_pointer(struct wim_lookup_table_entry *lte)
+{
+ wimlib_assert(lte->unhashed);
+ struct wim_inode *inode = lte->back_inode;
+ u32 stream_id = lte->back_stream_id;
+ if (stream_id == 0)
+ return &inode->i_lte;
+ else
+ for (u16 i = 0; i < inode->i_num_ads; i++)
+ if (inode->i_ads_entries[i].stream_id == stream_id)
+ return &inode->i_ads_entries[i].lte;
+ wimlib_assert(0);
+ return NULL;
+}
+
+/* Calculate the SHA1 message digest of a stream and move it from the list of
+ * unhashed streams to the stream lookup table, possibly joining it with an
+ * existing lookup table entry for an identical stream.
+ *
+ * @lte: An unhashed lookup table entry.
+ * @lookup_table: Lookup table for the WIM.
+ * @lte_ret: On success, write a pointer to the resulting lookup table
+ * entry to this location. This will be the same as @lte
+ * if it was inserted into the lookup table, or different if
+ * a duplicate stream was found.
+ *
+ * Returns 0 on success; nonzero if there is an error reading the stream.
+ */
+int
+hash_unhashed_stream(struct wim_lookup_table_entry *lte,
+ struct wim_lookup_table *lookup_table,
+ struct wim_lookup_table_entry **lte_ret)
+{
+ int ret;
+ struct wim_lookup_table_entry *duplicate_lte;
+ struct wim_lookup_table_entry **back_ptr;
+
+ wimlib_assert(lte->unhashed);
+
+ /* back_ptr must be saved because @back_inode and @back_stream_id are in
+ * union with the SHA1 message digest and will no longer be valid once
+ * the SHA1 has been calculated. */
+ back_ptr = retrieve_lte_pointer(lte);
+
+ ret = sha1_stream(lte);
+ if (ret)
+ return ret;
+
+ /* Look for a duplicate stream */
+ duplicate_lte = lookup_resource(lookup_table, lte->hash);
+ list_del(<e->unhashed_list);
+ if (duplicate_lte) {
+ /* We have a duplicate stream. Transfer the reference counts
+ * from this stream to the duplicate, update the reference to
+ * this stream (in an inode or ads_entry) to point to the
+ * duplicate, then free this stream. */
+ wimlib_assert(!(duplicate_lte->unhashed));
+ duplicate_lte->refcnt += lte->refcnt;
+ duplicate_lte->out_refcnt += lte->out_refcnt;
+ *back_ptr = duplicate_lte;
+ free_lookup_table_entry(lte);
+ lte = duplicate_lte;
+ } else {
+ /* No duplicate stream, so we need to insert
+ * this stream into the lookup table and treat
+ * it as a hashed stream. */
+ lookup_table_insert(lookup_table, lte);
+ lte->unhashed = 0;
+ }
+ if (lte_ret)
+ *lte_ret = lte;
+ return 0;
+}
+
+static int
+lte_clone_if_new(struct wim_lookup_table_entry *lte, void *_lookup_table)
+{
+ struct wim_lookup_table *lookup_table = _lookup_table;
+
+ if (lookup_resource(lookup_table, lte->hash))
+ return 0; /* Resource already present. */
+
+ lte = clone_lookup_table_entry(lte);
+ if (!lte)
+ return WIMLIB_ERR_NOMEM;
+ lte->out_refcnt = 1;
+ lookup_table_insert(lookup_table, lte);
+ return 0;
+}
+
+static int
+lte_delete_if_new(struct wim_lookup_table_entry *lte, void *_lookup_table)
+{
+ struct wim_lookup_table *lookup_table = _lookup_table;
+
+ if (lte->out_refcnt) {
+ lookup_table_unlink(lookup_table, lte);
+ free_lookup_table_entry(lte);
+ }
+ 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;
+ unsigned i;
+
+ if (wim == NULL)
+ return WIMLIB_ERR_INVALID_PARAM;
+
+ if (num_resource_wims != 0 && resource_wims == NULL)
+ return WIMLIB_ERR_INVALID_PARAM;
+
+ for (i = 0; i < num_resource_wims; i++)
+ if (resource_wims[i] == NULL)
+ return WIMLIB_ERR_INVALID_PARAM;
+
+ for_lookup_table_entry(wim->lookup_table, lte_zero_out_refcnt, NULL);
+
+ for (i = 0; i < num_resource_wims; i++) {
+ ret = for_lookup_table_entry(resource_wims[i]->lookup_table,
+ lte_clone_if_new,
+ wim->lookup_table);
+ if (ret)
+ goto out_rollback;
+ }
+ return 0;
+
+out_rollback:
+ for_lookup_table_entry(wim->lookup_table, lte_delete_if_new,
+ wim->lookup_table);
+ return ret;
+}
+
+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;
+
+ 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++) {
+ DEBUG("Referencing resources from path \"%"TS"\"",
+ 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++)
+ list_add_tail(&resource_wims[i]->subwim_node, &wim->subwims);
+
+ 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);
+ }
+}