+ struct wim_inode *inode = dentry->d_inode;
+
+ dentry->extraction_skipped = 0;
+ dentry->was_hardlinked = 0;
+ dentry->skeleton_extracted = 0;
+ inode->i_visited = 0;
+ FREE(inode->i_extracted_file);
+ inode->i_extracted_file = NULL;
+ inode->i_dos_name_extracted = 0;
+ if ((void*)dentry->extraction_name != (void*)dentry->file_name)
+ FREE(dentry->extraction_name);
+ dentry->extraction_name = NULL;
+ return 0;
+}
+
+/* Tally features necessary to extract a dentry and the corresponding inode. */
+static int
+dentry_tally_features(struct wim_dentry *dentry, void *_features)
+{
+ struct wim_features *features = _features;
+ struct wim_inode *inode = dentry->d_inode;
+
+ if (inode->i_attributes & FILE_ATTRIBUTE_ARCHIVE)
+ features->archive_files++;
+ if (inode->i_attributes & FILE_ATTRIBUTE_HIDDEN)
+ features->hidden_files++;
+ if (inode->i_attributes & FILE_ATTRIBUTE_SYSTEM)
+ features->system_files++;
+ if (inode->i_attributes & FILE_ATTRIBUTE_COMPRESSED)
+ features->compressed_files++;
+ if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)
+ features->encrypted_files++;
+ if (inode->i_attributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
+ features->not_context_indexed_files++;
+ if (inode->i_attributes & FILE_ATTRIBUTE_SPARSE_FILE)
+ features->sparse_files++;
+ if (inode_has_named_stream(inode))
+ features->named_data_streams++;
+ if (inode->i_visited)
+ features->hard_links++;
+ if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+ features->reparse_points++;
+ if (inode_is_symlink(inode))
+ features->symlink_reparse_points++;
+ else
+ features->other_reparse_points++;
+ }
+ if (inode->i_security_id != -1)
+ features->security_descriptors++;
+ if (dentry->short_name_nbytes)
+ features->short_names++;
+ if (inode_has_unix_data(inode))
+ features->unix_data++;
+ inode->i_visited = 1;
+ return 0;
+}
+
+static int
+dentry_clear_inode_visited(struct wim_dentry *dentry, void *_ignore)
+{
+ dentry->d_inode->i_visited = 0;
+ return 0;
+}
+
+/* Tally the features necessary to extract a dentry tree. */
+static void
+dentry_tree_get_features(struct wim_dentry *root, struct wim_features *features)
+{
+ memset(features, 0, sizeof(struct wim_features));
+ for_dentry_in_tree(root, dentry_tally_features, features);
+ for_dentry_in_tree(root, dentry_clear_inode_visited, NULL);
+}
+
+static int
+do_feature_check(const struct wim_features *required_features,
+ const struct wim_features *supported_features,
+ int extract_flags,
+ const struct apply_operations *ops,
+ const tchar *wim_source_path)
+{
+ const tchar *loc;
+ const tchar *mode = "this extraction mode";
+
+ if (wim_source_path[0] == '\0')
+ loc = "the WIM image";
+ else
+ loc = wim_source_path;
+
+ /* We're an archive program, so theoretically we can do what we want
+ * with FILE_ATTRIBUTE_ARCHIVE (which is a dumb flag anyway). Don't
+ * bother the user about it. */
+#if 0
+ if (required_features->archive_files && !supported_features->archive_files)
+ {
+ WARNING(
+ "%lu files in %"TS" are marked as archived, but this attribute\n"
+" is not supported in %"TS".",
+ required_features->archive_files, loc, mode);
+ }
+#endif
+
+ if (required_features->hidden_files && !supported_features->hidden_files)
+ {
+ WARNING(
+ "%lu files in %"TS" are marked as hidden, but this\n"
+" attribute is not supported in %"TS".",
+ required_features->hidden_files, loc, mode);
+ }
+
+ if (required_features->system_files && !supported_features->system_files)
+ {
+ WARNING(
+ "%lu files in %"TS" are marked as system files,\n"
+" but this attribute is not supported in %"TS".",
+ required_features->system_files, loc, mode);
+ }
+
+ if (required_features->compressed_files && !supported_features->compressed_files)
+ {
+ WARNING(
+ "%lu files in %"TS" are marked as being transparently\n"
+" compressed, but transparent compression is not supported in\n"
+" %"TS". These files will be extracted as uncompressed.",
+ required_features->compressed_files, loc, mode);
+ }
+
+ if (required_features->encrypted_files && !supported_features->encrypted_files)
+ {
+ WARNING(
+ "%lu files in %"TS" are marked as being encrypted,\n"
+" but encryption is not supported in %"TS". These files\n"
+" will be extracted as raw encrypted data instead.",
+ required_features->encrypted_files, loc, mode);
+ }
+
+ if (required_features->not_context_indexed_files &&
+ !supported_features->not_context_indexed_files)
+ {
+ WARNING(
+ "%lu files in %"TS" are marked as not content indexed,\n"
+" but this attribute is not supported in %"TS".",
+ required_features->not_context_indexed_files, loc, mode);
+ }
+
+ if (required_features->sparse_files && !supported_features->sparse_files)
+ {
+ WARNING(
+ "%lu files in %"TS" are marked as sparse, but creating\n"
+" sparse files is not supported in %"TS". These files\n"
+" will be extracted as non-sparse.",
+ required_features->sparse_files, loc, mode);
+ }
+
+ if (required_features->named_data_streams &&
+ !supported_features->named_data_streams)
+ {
+ WARNING(
+ "%lu files in %"TS" contain one or more alternate (named)\n"
+" data streams, which are not supported in %"TS".\n"
+" Alternate data streams will NOT be extracted.",
+ required_features->named_data_streams, loc, mode);
+ }
+
+ if (unlikely(extract_flags & (WIMLIB_EXTRACT_FLAG_HARDLINK |
+ WIMLIB_EXTRACT_FLAG_SYMLINK)) &&
+ required_features->named_data_streams &&
+ supported_features->named_data_streams)
+ {
+ WARNING(
+ "%lu files in %"TS" contain one or more alternate (named)\n"
+" data streams, which are not supported in linked extraction mode.\n"
+" Alternate data streams will NOT be extracted.",
+ required_features->named_data_streams, loc);
+ }
+
+ if (required_features->hard_links && !supported_features->hard_links)
+ {
+ WARNING(
+ "%lu files in %"TS" are hard links, but hard links are\n"
+" not supported in %"TS". Hard links will be extracted as\n"
+" duplicate copies of the linked files.",
+ required_features->hard_links, loc, mode);
+ }
+
+ if (required_features->reparse_points && !supported_features->reparse_points)
+ {
+ if (supported_features->symlink_reparse_points) {
+ if (required_features->other_reparse_points) {
+ WARNING(
+ "%lu files in %"TS" are reparse points that are neither\n"
+" symbolic links nor junction points and are not supported in\n"
+" %"TS". These reparse points will not be extracted.",
+ required_features->other_reparse_points, loc,
+ mode);
+ }
+ } else {
+ WARNING(
+ "%lu files in %"TS" are reparse points, which are\n"
+" not supported in %"TS" and will not be extracted.",
+ required_features->reparse_points, loc, mode);
+ }
+ }
+
+ if (required_features->security_descriptors &&
+ !supported_features->security_descriptors)
+ {
+ WARNING(
+ "%lu files in %"TS" have Windows NT security descriptors,\n"
+" but extracting security descriptors is not supported in\n"
+" %"TS". No security descriptors will be extracted.",
+ required_features->security_descriptors, loc, mode);
+ }
+
+ if (required_features->short_names && !supported_features->short_names)
+ {
+ WARNING(
+ "%lu files in %"TS" have short (DOS) names, but\n"
+" extracting short names is not supported in %"TS".\n"
+" Short names will not be extracted.\n",
+ required_features->short_names, loc, mode);
+ }
+
+ if ((extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) &&
+ required_features->unix_data && !supported_features->unix_data)
+ {
+ ERROR("Extracting UNIX data is not supported in %"TS, mode);
+ return WIMLIB_ERR_UNSUPPORTED;
+ }
+ if ((extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES) &&
+ required_features->short_names && !supported_features->short_names)
+ {
+ ERROR("Extracting short names is not supported in %"TS"", mode);
+ return WIMLIB_ERR_UNSUPPORTED;
+ }
+ if ((extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_TIMESTAMPS) &&
+ !ops->set_timestamps)
+ {
+ ERROR("Extracting timestamps is not supported in %"TS"", mode);
+ return WIMLIB_ERR_UNSUPPORTED;
+ }
+ if (((extract_flags & (WIMLIB_EXTRACT_FLAG_STRICT_ACLS |
+ WIMLIB_EXTRACT_FLAG_UNIX_DATA))
+ == WIMLIB_EXTRACT_FLAG_STRICT_ACLS) &&
+ required_features->security_descriptors &&
+ !supported_features->security_descriptors)
+ {
+ ERROR("Extracting security descriptors is not supported in %"TS, mode);
+ return WIMLIB_ERR_UNSUPPORTED;
+ }
+
+ if ((extract_flags & WIMLIB_EXTRACT_FLAG_HARDLINK) &&
+ !supported_features->hard_links)
+ {
+ ERROR("Hard link extraction mode requested, but "
+ "%"TS" does not support hard links!", mode);
+ return WIMLIB_ERR_UNSUPPORTED;
+ }
+
+ if ((extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS) &&
+ required_features->symlink_reparse_points &&
+ !(supported_features->symlink_reparse_points ||
+ supported_features->reparse_points))
+ {
+ ERROR("Extracting symbolic links is not supported in %"TS, mode);
+ return WIMLIB_ERR_UNSUPPORTED;
+ }
+
+ if ((extract_flags & WIMLIB_EXTRACT_FLAG_SYMLINK) &&
+ !supported_features->symlink_reparse_points)
+ {
+ ERROR("Symbolic link extraction mode requested, but "
+ "%"TS" does not support symbolic "
+ "links!", mode);
+ return WIMLIB_ERR_UNSUPPORTED;
+ }
+ return 0;
+}
+
+/*
+ * extract_tree - Extract a file or directory tree from the currently selected
+ * WIM image.
+ *
+ * @wim: WIMStruct for the WIM file, with the desired image selected
+ * (as wim->current_image).
+ *
+ * @wim_source_path:
+ * "Canonical" (i.e. no leading or trailing slashes, path
+ * separators WIM_PATH_SEPARATOR) path inside the WIM image to
+ * extract. An empty string means the full image.
+ *
+ * @target:
+ * Filesystem path to extract the file or directory tree to.
+ * (Or, with WIMLIB_EXTRACT_FLAG_NTFS: the name of a NTFS volume.)
+ *
+ * @extract_flags:
+ * WIMLIB_EXTRACT_FLAG_*. Also, the private flag
+ * WIMLIB_EXTRACT_FLAG_MULTI_IMAGE will be set if this is being
+ * called through wimlib_extract_image() with WIMLIB_ALL_IMAGES as
+ * the image.
+ *
+ * @progress_func:
+ * If non-NULL, progress function for the extraction. The messages
+ * that may be sent in this function are:
+ *
+ * WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN or
+ * WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN;
+ * WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN;
+ * WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END;
+ * WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY;
+ * WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS;
+ * WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS;
+ * WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END or
+ * WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END.
+ *
+ * Returns 0 on success; a positive WIMLIB_ERR_* code on failure.
+ */
+static int
+extract_tree(WIMStruct *wim, const tchar *wim_source_path, const tchar *target,
+ int extract_flags, wimlib_progress_func_t progress_func)
+{
+ struct wim_dentry *root;
+ struct wim_features required_features;
+ struct apply_ctx ctx;