- * This function clobbers the tmp_list member of @dentry and its ancestors up
- * until the extraction root. */
-static bool
-build_extraction_path(tchar path[], struct wim_dentry *dentry,
- struct apply_ctx *ctx)
-{
- size_t path_nchars;
- LIST_HEAD(ancestor_list);
- tchar *p = path;
- const tchar *target_prefix;
- size_t target_prefix_nchars;
- struct wim_dentry *d;
-
- if (dentry->extraction_skipped)
- return false;
-
- path_nchars = ctx->ops->path_prefix_nchars;
-
- if (ctx->ops->requires_realtarget_in_paths) {
- target_prefix = ctx->realtarget;
- target_prefix_nchars = ctx->realtarget_nchars;
- } else if (ctx->ops->requires_target_in_paths) {
- target_prefix = ctx->target;
- target_prefix_nchars = ctx->target_nchars;
- } else {
- target_prefix = NULL;
- target_prefix_nchars = 0;
- }
- path_nchars += target_prefix_nchars;
-
- for (d = dentry; d != ctx->extract_root; d = d->parent) {
- path_nchars += d->extraction_name_nchars + 1;
- list_add(&d->tmp_list, &ancestor_list);
- }
-
- path_nchars++; /* null terminator */
-
- if (path_nchars > ctx->ops->path_max) {
- WARNING("\"%"TS"\": Path too long to extract",
- dentry_full_path(dentry));
- return false;
- }
-
- p = tmempcpy(p, ctx->ops->path_prefix, ctx->ops->path_prefix_nchars);
- p = tmempcpy(p, target_prefix, target_prefix_nchars);
- list_for_each_entry(d, &ancestor_list, tmp_list) {
- *p++ = ctx->ops->path_separator;
- p = tmempcpy(p, d->extraction_name, d->extraction_name_nchars);
- }
- *p++ = T('\0');
- wimlib_assert(p - path == path_nchars);
- return true;
-}
-
-static unsigned
-get_num_path_components(const tchar *path, tchar path_separator)
-{
- unsigned num_components = 0;
-#ifdef __WIN32__
- /* Ignore drive letter. */
- if (path[0] != L'\0' && path[1] == L':')
- path += 2;
-#endif
-
- while (*path) {
- while (*path == path_separator)
- path++;
- if (*path)
- num_components++;
- while (*path && *path != path_separator)
- path++;
- }
- return num_components;
-}
-
-static int
-extract_multiimage_symlink(const tchar *oldpath, const tchar *newpath,
- struct apply_ctx *ctx, struct wim_dentry *dentry)
-{
- size_t num_raw_path_components;
- const struct wim_dentry *d;
- size_t num_target_path_components;
- tchar *p;
- const tchar *p_old;
- int ret;
-
- num_raw_path_components = 0;
- for (d = dentry; d != ctx->extract_root; d = d->parent)
- num_raw_path_components++;
-
- if (ctx->ops->requires_realtarget_in_paths)
- num_target_path_components = get_num_path_components(ctx->realtarget,
- ctx->ops->path_separator);
- else if (ctx->ops->requires_target_in_paths)
- num_target_path_components = get_num_path_components(ctx->target,
- ctx->ops->path_separator);
- else
- num_target_path_components = 0;
-
- if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_MULTI_IMAGE) {
- wimlib_assert(num_target_path_components > 0);
- num_raw_path_components++;
- num_target_path_components--;
- }
-
- p_old = oldpath + ctx->ops->path_prefix_nchars;
-#ifdef __WIN32__
- if (p_old[0] != L'\0' && p_old[1] == ':')
- p_old += 2;
-#endif
- while (*p_old == ctx->ops->path_separator)
- p_old++;
- while (--num_target_path_components) {
- while (*p_old != ctx->ops->path_separator)
- p_old++;
- while (*p_old == ctx->ops->path_separator)
- p_old++;
- }
-
- tchar symlink_target[tstrlen(p_old) + 3 * num_raw_path_components + 1];
-
- p = &symlink_target[0];
- while (num_raw_path_components--) {
- *p++ = '.';
- *p++ = '.';
- *p++ = ctx->ops->path_separator;
- }
- tstrcpy(p, p_old);
- DEBUG("Creating symlink \"%"TS"\" => \"%"TS"\"",
- newpath, symlink_target);
- ret = ctx->ops->create_symlink(symlink_target, newpath, ctx);
- if (ret) {
- ERROR_WITH_ERRNO("Failed to create symlink "
- "\"%"TS"\" => \"%"TS"\"",
- newpath, symlink_target);
- }
- return ret;
-}
-
-/* Create the "skeleton" of an extracted file or directory. Don't yet extract
- * data streams, reparse data (including symbolic links), timestamps, and
- * security descriptors. Basically, everything that doesn't require reading
- * non-metadata resources from the WIM file and isn't delayed until the final
- * pass. */
-static int
-do_dentry_extract_skeleton(tchar path[], struct wim_dentry *dentry,
- struct apply_ctx *ctx)
-{
- struct wim_inode *inode = dentry->d_inode;
- int ret;
- const tchar *oldpath;
-
- if (unlikely(is_linked_extraction(ctx))) {
- struct wim_lookup_table_entry *unnamed_lte;
-
- unnamed_lte = inode_unnamed_lte_resolved(dentry->d_inode);
- if (unnamed_lte && unnamed_lte->extracted_file) {
- oldpath = unnamed_lte->extracted_file;
- if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_HARDLINK)
- goto hardlink;
- else
- goto symlink;
- }
- }
-
- /* Create hard link if this dentry corresponds to an already-extracted
- * inode. */
- if (inode->i_extracted_file) {
- oldpath = inode->i_extracted_file;
- goto hardlink;
- }
-
- /* Skip symlinks unless they can be extracted as reparse points rather
- * than created directly. */
- if (inode_is_symlink(inode) && !ctx->supported_features.reparse_points)
- return 0;
-
- /* Create this file or directory unless it's the extraction root, which
- * was already created if necessary. */
- if (dentry != ctx->extract_root) {
- ret = extract_inode(path, ctx, inode);
- if (ret)
- return ret;
- }
-
- /* Create empty named data streams. */
- if (can_extract_named_data_streams(ctx)) {
- for (u16 i = 0; i < inode->i_num_ads; i++) {
- file_spec_t file_spec;
- struct wim_ads_entry *entry = &inode->i_ads_entries[i];
-
- if (!ads_entry_is_named_stream(entry))
- continue;
- if (entry->lte)
- continue;
- if (ctx->ops->uses_cookies)
- file_spec.cookie = inode->extract_cookie;
- else
- file_spec.path = path;
- ret = ctx->ops->extract_named_stream(file_spec,
- entry->stream_name,
- entry->stream_name_nbytes / 2,
- entry->lte, ctx);
- if (ret) {
- ERROR_WITH_ERRNO("\"%"TS"\": failed to create "
- "empty named data stream",
- path);
- return ret;
- }
- }
- }
-
- /* Set file attributes (if supported). */
- ret = extract_file_attributes(path, ctx, dentry, 0);
- if (ret)
- return ret;
-
- /* Set or remove file short name (if supported). */
- ret = extract_short_name(path, ctx, dentry);
- if (ret)
- return ret;
-
- /* If inode has multiple links and hard links are supported in this
- * extraction mode and volume, save the path to the extracted file in
- * case it's needed to create a hard link. */
- if (unlikely(is_linked_extraction(ctx))) {
- struct wim_lookup_table_entry *unnamed_lte;
-
- unnamed_lte = inode_unnamed_lte_resolved(dentry->d_inode);
- if (unnamed_lte) {
- unnamed_lte->extracted_file = TSTRDUP(path);
- if (!unnamed_lte->extracted_file)
- return WIMLIB_ERR_NOMEM;
- }
- } else if (inode->i_nlink > 1 && ctx->supported_features.hard_links) {
- inode->i_extracted_file = TSTRDUP(path);
- if (!inode->i_extracted_file)
- return WIMLIB_ERR_NOMEM;
- }
- return 0;
-
-symlink:
- ret = extract_multiimage_symlink(oldpath, path, ctx, dentry);
- if (ret)
- return ret;
- dentry->was_hardlinked = 1;
- return 0;
-
-hardlink:
- ret = extract_hardlink(oldpath, path, ctx);
- if (ret)
- return ret;
- dentry->was_hardlinked = 1;
- return 0;
-}
-
-/* This is a wrapper around do_dentry_extract_skeleton() that handles building
- * the path, doing short name reordering. This is also idempotent; dentries
- * already processed have skeleton_extracted set and no action is taken. See
- * apply_operations.requires_short_name_reordering for more details about short
- * name reordering. */
-static int
-dentry_extract_skeleton(struct wim_dentry *dentry, void *_ctx)
-{
- struct apply_ctx *ctx = _ctx;
- tchar path[ctx->ops->path_max];
- struct wim_dentry *orig_dentry;
- struct wim_dentry *other_dentry;
- int ret;
-
- if (dentry->skeleton_extracted)
- return 0;
-
- orig_dentry = NULL;
- if (ctx->supported_features.short_names
- && ctx->ops->requires_short_name_reordering
- && !dentry_has_short_name(dentry)
- && !dentry->d_inode->i_dos_name_extracted)
- {
- inode_for_each_dentry(other_dentry, dentry->d_inode) {
- if (dentry_has_short_name(other_dentry)
- && !other_dentry->skeleton_extracted
- && other_dentry->in_extraction_tree)
- {
- DEBUG("Creating %"TS" before %"TS" "
- "to guarantee correct DOS name extraction",
- dentry_full_path(other_dentry),
- dentry_full_path(dentry));
- orig_dentry = dentry;
- dentry = other_dentry;
- break;
- }
- }
- }
-again:
- if (!build_extraction_path(path, dentry, ctx))
- return 0;
- ret = do_dentry_extract_skeleton(path, dentry, ctx);
- if (ret)
- return ret;
-
- dentry->skeleton_extracted = 1;
-
- if (orig_dentry) {
- dentry = orig_dentry;
- orig_dentry = NULL;
- goto again;
- }
- dentry->d_inode->i_dos_name_extracted = 1;
- return 0;
-}
-
-static int
-dentry_extract_dir_skeleton(struct wim_dentry *dentry, void *_ctx)
-{
- if (dentry->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
- return dentry_extract_skeleton(dentry, _ctx);
- return 0;
-}
-
-/* Create a file or directory, then immediately extract all streams. This
- * assumes that WIMLIB_EXTRACT_FLAG_SEQUENTIAL is not specified, since the WIM
- * may not be read sequentially by this function. */
-static int
-dentry_extract(struct wim_dentry *dentry, void *_ctx)
-{
- struct apply_ctx *ctx = _ctx;
- tchar path[ctx->ops->path_max];
- int ret;
-
- ret = dentry_extract_skeleton(dentry, ctx);
- if (ret)
- return ret;
-
- if (!build_extraction_path(path, dentry, ctx))
- return 0;
-
- return extract_streams(path, ctx, dentry, NULL, NULL);
-}
-
-/* Creates a temporary file opened for writing. The open file descriptor is
- * returned in @fd_ret and its name is returned in @name_ret (dynamically
- * allocated). */
-static int
-create_temporary_file(struct filedes *fd_ret, tchar **name_ret)
-{
- tchar *name;
- int raw_fd;
-
-retry:
- name = ttempnam(NULL, T("wimlib"));
- if (name == NULL) {
- ERROR_WITH_ERRNO("Failed to create temporary filename");
- return WIMLIB_ERR_NOMEM;
- }
-
- raw_fd = topen(name, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0600);
-
- if (raw_fd < 0) {
- int errno_save = errno;
- FREE(name);
- if (errno_save == EEXIST)
- goto retry;
- ERROR_WITH_ERRNO("Failed to open temporary file \"%"TS"\"", name);
- return WIMLIB_ERR_OPEN;
- }
-
- filedes_init(fd_ret, raw_fd);
- *name_ret = name;
- return 0;
-}
-
-/* Extract all instances of the stream @lte that are being extracted in this
- * call of extract_tree(), but actually read the stream data from @lte_override.