+ struct wim_lookup_table_entry *lte;
+
+ /* Start initializing the apply_ctx. */
+ memset(&ctx, 0, sizeof(struct apply_ctx));
+ ctx.wim = wim;
+ ctx.extract_flags = extract_flags;
+ ctx.target = target;
+ ctx.target_nchars = tstrlen(target);
+ ctx.progress_func = progress_func;
+ if (progress_func) {
+ ctx.progress.extract.wimfile_name = wim->filename;
+ ctx.progress.extract.image = wim->current_image;
+ ctx.progress.extract.extract_flags = (extract_flags &
+ WIMLIB_EXTRACT_MASK_PUBLIC);
+ ctx.progress.extract.image_name = wimlib_get_image_name(wim,
+ wim->current_image);
+ ctx.progress.extract.extract_root_wim_source_path = wim_source_path;
+ ctx.progress.extract.target = target;
+ }
+ INIT_LIST_HEAD(&ctx.stream_list);
+
+ /* Translate the path to extract into the corresponding
+ * `struct wim_dentry', which will be the root of the
+ * "dentry tree" to extract. */
+ root = get_dentry(wim, wim_source_path);
+ if (!root) {
+ ERROR("Path \"%"TS"\" does not exist in WIM image %d",
+ wim_source_path, wim->current_image);
+ ret = WIMLIB_ERR_PATH_DOES_NOT_EXIST;
+ goto out;
+ }
+
+ ctx.extract_root = root;
+
+ /* Select the appropriate apply_operations based on the
+ * platform and extract_flags. */
+#ifdef __WIN32__
+ ctx.ops = &win32_apply_ops;
+#else
+ ctx.ops = &unix_apply_ops;
+#endif
+
+#ifdef WITH_NTFS_3G
+ if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS)
+ ctx.ops = &ntfs_3g_apply_ops;
+#endif
+
+ /* Call the start_extract() callback. This gives the apply_operations
+ * implementation a chance to do any setup needed to access the volume.
+ * Furthermore, it's expected to set the supported features of this
+ * extraction mode (ctx.supported_features), which are determined at
+ * runtime as they may vary depending on the actual volume. These
+ * features are then compared with the actual features extracting this
+ * dentry tree requires. Some mismatches will merely produce warnings
+ * and the unsupported data will be ignored; others will produce errors.
+ */
+ ret = ctx.ops->start_extract(target, &ctx);
+ if (ret)
+ goto out;
+
+ dentry_tree_get_features(root, &required_features);
+ ret = do_feature_check(&required_features, &ctx.supported_features,
+ extract_flags, ctx.ops, wim_source_path);
+ if (ret)
+ goto out_finish_or_abort_extract;
+
+ /* Figure out whether the root dentry is being extracted to the root of
+ * a volume and therefore needs to be treated "specially", for example
+ * not being explicitly created and not having attributes set. */
+ if (ctx.ops->target_is_root && ctx.ops->root_directory_is_special)
+ ctx.root_dentry_is_special = ctx.ops->target_is_root(target);
+
+ /* Calculate the actual filename component of each extracted dentry. In
+ * the process, set the dentry->extraction_skipped flag on dentries that
+ * are being skipped for some reason (e.g. invalid filename). */
+ ret = for_dentry_in_tree(root, dentry_calculate_extraction_path, &ctx);
+ if (ret)
+ goto out_dentry_reset_needs_extraction;
+
+ /* Build the list of the streams that need to be extracted and
+ * initialize ctx.progress.extract with stream information. */
+ ret = for_dentry_in_tree(ctx.extract_root,
+ dentry_resolve_and_zero_lte_refcnt, &ctx);
+ if (ret)
+ goto out_dentry_reset_needs_extraction;
+
+ ret = for_dentry_in_tree(ctx.extract_root,
+ dentry_add_streams_to_extract, &ctx);
+ if (ret)
+ goto out_teardown_stream_list;
+
+ if (extract_flags & WIMLIB_EXTRACT_FLAG_FROM_PIPE) {
+ /* When extracting from a pipe, the number of bytes of data to
+ * extract can't be determined in the normal way (examining the
+ * lookup table), since at this point all we have is a set of
+ * SHA1 message digests of streams that need to be extracted.
+ * However, we can get a reasonably accurate estimate by taking
+ * <TOTALBYTES> from the corresponding <IMAGE> in the WIM XML
+ * data. This does assume that a full image is being extracted,
+ * but currently there is no API for doing otherwise. (Also,
+ * subtract <HARDLINKBYTES> from this if hard links are
+ * supported by the extraction mode.) */
+ ctx.progress.extract.total_bytes =
+ wim_info_get_image_total_bytes(wim->wim_info,
+ wim->current_image);
+ if (ctx.supported_features.hard_links) {
+ ctx.progress.extract.total_bytes -=
+ wim_info_get_image_hard_link_bytes(wim->wim_info,
+ wim->current_image);
+ }
+ }
+
+ /* Handle the special case of extracting a file to standard
+ * output. In that case, "root" should be a single file, not a
+ * directory tree. (If not, extract_dentry_to_stdout() will
+ * return an error.) */
+ if (extract_flags & WIMLIB_EXTRACT_FLAG_TO_STDOUT) {
+ ret = extract_dentry_to_stdout(root);
+ goto out_teardown_stream_list;
+ }
+
+ /* If a sequential extraction was specified, sort the streams to be
+ * extracted by their position in the WIM file so that the WIM file can
+ * be read sequentially. */
+ if ((extract_flags & (WIMLIB_EXTRACT_FLAG_SEQUENTIAL |
+ WIMLIB_EXTRACT_FLAG_FROM_PIPE))
+ == WIMLIB_EXTRACT_FLAG_SEQUENTIAL)
+ {
+ ret = sort_stream_list_by_sequential_order(
+ &ctx.stream_list,
+ offsetof(struct wim_lookup_table_entry,
+ extraction_list));
+ if (ret)
+ goto out_teardown_stream_list;
+ }
+
+ if (ctx.ops->realpath_works_on_nonexisting_files &&
+ ((extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX) ||
+ ctx.ops->requires_realtarget_in_paths))
+ {
+ ctx.realtarget = realpath(target, NULL);
+ if (!ctx.realtarget) {
+ ret = WIMLIB_ERR_NOMEM;
+ goto out_teardown_stream_list;
+ }
+ ctx.realtarget_nchars = tstrlen(ctx.realtarget);
+ }
+
+ if (progress_func) {
+ progress_func(*wim_source_path ? WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN :
+ WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN,
+ &ctx.progress);
+ }
+
+ if (!ctx.root_dentry_is_special)
+ {
+ tchar path[ctx.ops->path_max];
+ if (build_extraction_path(path, root, &ctx))
+ {
+ ret = extract_inode(path, &ctx, root->d_inode);
+ if (ret)
+ goto out_free_realtarget;
+ }
+ }
+
+ /* If we need to fix up the targets of absolute symbolic links
+ * (WIMLIB_EXTRACT_FLAG_RPFIX) or the extraction mode requires paths to
+ * be absolute, use realpath() (or its replacement on Windows) to get
+ * the absolute path to the extraction target. Note that this requires
+ * the target directory to exist, unless
+ * realpath_works_on_nonexisting_files is set in the apply_operations.
+ * */
+ if (!ctx.realtarget &&
+ (((extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX) &&
+ required_features.symlink_reparse_points) ||
+ ctx.ops->requires_realtarget_in_paths))
+ {
+ ctx.realtarget = realpath(target, NULL);
+ if (!ctx.realtarget) {
+ ret = WIMLIB_ERR_NOMEM;
+ goto out_free_realtarget;
+ }
+ ctx.realtarget_nchars = tstrlen(ctx.realtarget);
+ }
+
+ /* Finally, the important part: extract the tree of files. */
+ if (extract_flags & (WIMLIB_EXTRACT_FLAG_SEQUENTIAL |
+ WIMLIB_EXTRACT_FLAG_FROM_PIPE)) {
+ /* Sequential extraction requested, so two passes are needed
+ * (one for directory structure, one for streams.) */
+ if (progress_func)
+ progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN,
+ &ctx.progress);
+
+ if (!(extract_flags & WIMLIB_EXTRACT_FLAG_RESUME)) {
+ ret = for_dentry_in_tree(root, dentry_extract_skeleton, &ctx);
+ if (ret)
+ goto out_free_realtarget;
+ }
+ if (progress_func)
+ progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END,
+ &ctx.progress);
+ if (extract_flags & WIMLIB_EXTRACT_FLAG_FROM_PIPE)
+ ret = extract_streams_from_pipe(&ctx);
+ else
+ ret = extract_stream_list(&ctx);
+ if (ret)
+ goto out_free_realtarget;
+ } else {
+ /* Sequential extraction was not requested, so we can make do
+ * with one pass where we both create the files and extract
+ * streams. */
+ if (progress_func)
+ progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN,
+ &ctx.progress);
+ ret = for_dentry_in_tree(root, dentry_extract, &ctx);
+ if (ret)
+ goto out_free_realtarget;
+ if (progress_func)
+ progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END,
+ &ctx.progress);
+ }
+
+ /* If the total number of bytes to extract was miscalculated, just jump
+ * to the calculated number in order to avoid confusing the progress
+ * function. This should only occur when extracting from a pipe. */
+ if (ctx.progress.extract.completed_bytes != ctx.progress.extract.total_bytes)
+ {
+ DEBUG("Calculated %"PRIu64" bytes to extract, but actually "
+ "extracted %"PRIu64,
+ ctx.progress.extract.total_bytes,
+ ctx.progress.extract.completed_bytes);
+ }
+ if (progress_func &&
+ ctx.progress.extract.completed_bytes < ctx.progress.extract.total_bytes)
+ {
+ ctx.progress.extract.completed_bytes = ctx.progress.extract.total_bytes;
+ progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS, &ctx.progress);
+ }