-dentry_in_list(const struct wim_dentry *dentry)
-{
- return dentry->extraction_list.next != NULL;
-}
-
-static inline bool
-is_linked_extraction(const struct apply_ctx *ctx)
-{
- return 0 != (ctx->extract_flags & (WIMLIB_EXTRACT_FLAG_HARDLINK |
- WIMLIB_EXTRACT_FLAG_SYMLINK));
-}
-
-static inline bool
-can_extract_named_data_streams(const struct apply_ctx *ctx)
-{
- return ctx->supported_features.named_data_streams &&
- !is_linked_extraction(ctx);
-}
-/* Inform library user of progress of stream extraction following the successful
- * extraction of a copy of the stream specified by @lte. */
-static void
-update_extract_progress(struct apply_ctx *ctx,
- const struct wim_lookup_table_entry *lte)
-{
- wimlib_progress_func_t progress_func = ctx->progress_func;
- union wimlib_progress_info *progress = &ctx->progress;
-
- progress->extract.completed_bytes += lte->size;
- if (progress_func &&
- progress->extract.completed_bytes >= ctx->next_progress)
- {
- progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS, progress);
- if (progress->extract.completed_bytes >=
- progress->extract.total_bytes)
- {
- ctx->next_progress = ~0ULL;
- } else {
- ctx->next_progress += progress->extract.total_bytes / 128;
- if (ctx->next_progress > progress->extract.total_bytes)
- ctx->next_progress = progress->extract.total_bytes;
- }
- }
-}
-
-#ifndef __WIN32__
-/* Extract a symbolic link (not directly as reparse data), handling fixing up
- * the target of absolute symbolic links and updating the extract progress.
- *
- * @inode must specify the WIM inode for a symbolic link or junction reparse
- * point.
- *
- * @lte_override overrides the resource used as the reparse data for the
- * symbolic link. */
-static int
-extract_symlink(const tchar *path, struct apply_ctx *ctx,
- struct wim_inode *inode,
- struct wim_lookup_table_entry *lte_override)
-{
- ssize_t bufsize = ctx->ops->path_max;
- tchar target[bufsize];
- tchar *buf = target;
- tchar *fixed_target;
- ssize_t sret;
- int ret;
-
- /* If absolute symbolic link fixups requested, reserve space in the link
- * target buffer for the absolute path of the target directory. */
- if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX)
- {
- buf += ctx->realtarget_nchars;
- bufsize -= ctx->realtarget_nchars;
- }
-
- /* Translate the WIM inode's reparse data into the link target. */
- sret = wim_inode_readlink(inode, buf, bufsize - 1, lte_override);
- if (sret < 0) {
- errno = -sret;
- return WIMLIB_ERR_READLINK;
- }
- buf[sret] = '\0';
-
- if ((ctx->extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX) &&
- buf[0] == '/')
- {
- /* Fix absolute symbolic link target to point into the
- * actual extraction destination. */
- tmemcpy(target, ctx->realtarget, ctx->realtarget_nchars);
- fixed_target = target;
- } else {
- /* Keep same link target. */
- fixed_target = buf;
- }
-
- /* Call into the apply_operations to create the symbolic link. */
- DEBUG("Creating symlink \"%"TS"\" => \"%"TS"\"",
- path, fixed_target);
- ret = ctx->ops->create_symlink(fixed_target, path, ctx);
- if (ret) {
- ERROR_WITH_ERRNO("Failed to create symlink "
- "\"%"TS"\" => \"%"TS"\"", path, fixed_target);
- return ret;
- }
-
- /* Account for reparse data consumed. */
- update_extract_progress(ctx,
- (lte_override ? lte_override :
- inode_unnamed_lte_resolved(inode)));
- return 0;
-}
-#endif /* !__WIN32__ */
-
-/* Create a file, directory, or symbolic link. */
-static int
-extract_inode(const tchar *path, struct apply_ctx *ctx, struct wim_inode *inode)
-{
- int ret;
-
-#ifndef __WIN32__
- if (ctx->supported_features.symlink_reparse_points &&
- !ctx->supported_features.reparse_points &&
- inode_is_symlink(inode))
- {
- ret = extract_symlink(path, ctx, inode, NULL);
- } else
-#endif /* !__WIN32__ */
- if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
- ret = ctx->ops->create_directory(path, ctx, &inode->extract_cookie);
- if (ret) {
- ERROR_WITH_ERRNO("Failed to create the directory "
- "\"%"TS"\"", path);
- }
- } else if ((inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) &&
- ctx->ops->extract_encrypted_stream_creates_file &&
- ctx->supported_features.encrypted_files) {
- ret = ctx->ops->extract_encrypted_stream(
- path, inode_unnamed_lte_resolved(inode), ctx);
- if (ret) {
- ERROR_WITH_ERRNO("Failed to create and extract "
- "encrypted file \"%"TS"\"", path);
- }
- } else {
- ret = ctx->ops->create_file(path, ctx, &inode->extract_cookie);
- if (ret) {
- ERROR_WITH_ERRNO("Failed to create the file "
- "\"%"TS"\"", path);
- }
- }
- return ret;
-}
-
-static int
-extract_hardlink(const tchar *oldpath, const tchar *newpath,
- struct apply_ctx *ctx)
-{
- int ret;
-
- DEBUG("Creating hardlink \"%"TS"\" => \"%"TS"\"", newpath, oldpath);
- ret = ctx->ops->create_hardlink(oldpath, newpath, ctx);
- if (ret) {
- ERROR_WITH_ERRNO("Failed to create hardlink "
- "\"%"TS"\" => \"%"TS"\"",
- newpath, oldpath);
- }
- return ret;
-}
-
-#ifdef __WIN32__
-static int
-try_extract_rpfix(u8 *rpbuf,
- u16 *rpbuflen_p,
- const wchar_t *extract_root_realpath,
- unsigned extract_root_realpath_nchars)
-{
- struct reparse_data rpdata;
- wchar_t *target;
- size_t target_nchars;
- size_t stripped_nchars;
- wchar_t *stripped_target;
- wchar_t stripped_target_nchars;
- int ret;
-
- utf16lechar *new_target;
- utf16lechar *new_print_name;
- size_t new_target_nchars;
- size_t new_print_name_nchars;
- utf16lechar *p;
-
- ret = parse_reparse_data(rpbuf, *rpbuflen_p, &rpdata);
- if (ret)
- return ret;
-
- if (extract_root_realpath[0] == L'\0' ||
- extract_root_realpath[1] != L':' ||
- extract_root_realpath[2] != L'\\')
- return WIMLIB_ERR_REPARSE_POINT_FIXUP_FAILED;
-
- ret = parse_substitute_name(rpdata.substitute_name,
- rpdata.substitute_name_nbytes,
- rpdata.rptag);
- if (ret < 0)
- return 0;
- stripped_nchars = ret;
- target = rpdata.substitute_name;
- target_nchars = rpdata.substitute_name_nbytes / sizeof(utf16lechar);
- stripped_target = target + stripped_nchars;
- stripped_target_nchars = target_nchars - stripped_nchars;
-
- new_target = alloca((6 + extract_root_realpath_nchars +
- stripped_target_nchars) * sizeof(utf16lechar));
-
- p = new_target;
- if (stripped_nchars == 6) {
- /* Include \??\ prefix if it was present before */
- p = wmempcpy(p, L"\\??\\", 4);
- }
-
- /* Print name excludes the \??\ if present. */
- new_print_name = p;
- if (stripped_nchars != 0) {
- /* Get drive letter from real path to extract root, if a drive
- * letter was present before. */
- *p++ = extract_root_realpath[0];
- *p++ = extract_root_realpath[1];
- }
- /* Copy the rest of the extract root */
- p = wmempcpy(p, extract_root_realpath + 2, extract_root_realpath_nchars - 2);
-
- /* Append the stripped target */
- p = wmempcpy(p, stripped_target, stripped_target_nchars);
- new_target_nchars = p - new_target;
- new_print_name_nchars = p - new_print_name;
-
- if (new_target_nchars * sizeof(utf16lechar) >= REPARSE_POINT_MAX_SIZE ||
- new_print_name_nchars * sizeof(utf16lechar) >= REPARSE_POINT_MAX_SIZE)
- return WIMLIB_ERR_REPARSE_POINT_FIXUP_FAILED;
-
- rpdata.substitute_name = new_target;
- rpdata.substitute_name_nbytes = new_target_nchars * sizeof(utf16lechar);
- rpdata.print_name = new_print_name;
- rpdata.print_name_nbytes = new_print_name_nchars * sizeof(utf16lechar);
- return make_reparse_buffer(&rpdata, rpbuf, rpbuflen_p);
-}
-#endif /* __WIN32__ */
-
-/* Set reparse data on extracted file or directory that has
- * FILE_ATTRIBUTE_REPARSE_POINT set. */
-static int
-extract_reparse_data(const tchar *path, struct apply_ctx *ctx,
- struct wim_inode *inode,
- struct wim_lookup_table_entry *lte_override)
-{
- int ret;
- u8 rpbuf[REPARSE_POINT_MAX_SIZE];
- u16 rpbuflen;
-
- ret = wim_inode_get_reparse_data(inode, rpbuf, &rpbuflen, lte_override);
- if (ret)
- goto error;
-
-#ifdef __WIN32__
- /* Fix up target of absolute symbolic link or junction points so
- * that they point into the actual extraction target. */
- if ((ctx->extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX) &&
- (inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
- inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT) &&
- !inode->i_not_rpfixed)
- {
- ret = try_extract_rpfix(rpbuf, &rpbuflen, ctx->realtarget,
- ctx->realtarget_nchars);
- if (ret && !(ctx->extract_flags &
- WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS))
- {
- WARNING("Reparse point fixup of \"%"TS"\" "
- "failed", path);
- ret = 0;
- }
- if (ret)
- goto error;
- }
-#endif
-
- ret = ctx->ops->set_reparse_data(path, rpbuf, rpbuflen, ctx);
-
- /* On Windows, the SeCreateSymbolicLink privilege is required to create
- * symbolic links. To be more friendly towards non-Administrator users,
- * we merely warn the user if symbolic links cannot be created due to
- * insufficient permissions or privileges, unless
- * WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS was provided. */
-#ifdef __WIN32__
- if (ret && inode_is_symlink(inode) &&
- (errno == EACCES || errno == EPERM) &&
- !(ctx->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS))
- {
- WARNING("Can't set reparse data on \"%"TS"\": "
- "Access denied!\n"
- " You may be trying to "
- "extract a symbolic link without the\n"
- " SeCreateSymbolicLink privilege, "
- "which by default non-Administrator\n"
- " accounts do not have.",
- path);
- ret = 0;
- }
-#endif
- if (ret)
- goto error;
-
- /* Account for reparse data consumed. */
- update_extract_progress(ctx,
- (lte_override ? lte_override :
- inode_unnamed_lte_resolved(inode)));
- return 0;
-
-error:
- ERROR_WITH_ERRNO("Failed to set reparse data on \"%"TS"\"", path);
- return ret;
-}
-
-/*
- * Extract zero or more streams to a file.
- *
- * This function operates slightly differently depending on whether @lte_spec is
- * NULL or not. When @lte_spec is NULL, the behavior is to extract the default
- * file contents (unnamed stream), and, if named data streams are supported in
- * the extract mode and volume, any named data streams. When @lte_spec is not
- * NULL, the behavior is to extract only all copies of the stream @lte_spec, and
- * in addition use @lte_spec to set the reparse data or create the symbolic link
- * if appropriate.
- *
- * @path
- * Path to file to extract (as can be passed to apply_operations
- * functions).
- * @ctx
- * Apply context.
- * @dentry
- * WIM dentry that corresponds to the file being extracted.
- * @lte_spec
- * If non-NULL, specifies the lookup table entry for a stream to extract,
- * and only that stream will be extracted (although there may be more than
- * one instance of it).
- * @lte_override
- * Used only if @lte_spec != NULL; it is passed to the extraction functions
- * rather than @lte_spec, allowing the location of the stream to be
- * overridden. (This is used when the WIM is being read from a nonseekable
- * file, such as a pipe, when streams need to be used more than once; each
- * such stream is extracted to a temporary file.)
- */
-static int
-extract_streams(const tchar *path, struct apply_ctx *ctx,
- struct wim_dentry *dentry,
- struct wim_lookup_table_entry *lte_spec,
- struct wim_lookup_table_entry *lte_override)
-{
- struct wim_inode *inode = dentry->d_inode;
- struct wim_lookup_table_entry *lte;
- file_spec_t file_spec;
- int ret;
-
- if (dentry->was_linked)
- return 0;
-
-#ifdef ENABLE_DEBUG
- if (lte_spec) {
- char sha1_str[100];
- char *p = sha1_str;
- for (unsigned i = 0; i < SHA1_HASH_SIZE; i++)
- p += sprintf(p, "%02x", lte_override->hash[i]);
- DEBUG("Extracting stream SHA1=%s to \"%"TS"\"",
- sha1_str, path, inode->i_ino);
- } else {
- DEBUG("Extracting streams to \"%"TS"\"", path, inode->i_ino);
- }
-#endif
-
- if (ctx->ops->uses_cookies)
- file_spec.cookie = inode->extract_cookie;
- else
- file_spec.path = path;
-
- /* Unnamed data stream. */
- lte = inode_unnamed_lte_resolved(inode);
- if (lte && (!lte_spec || lte == lte_spec)) {
- if (lte_spec)
- lte = lte_override;
- if (!(inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY |
- FILE_ATTRIBUTE_REPARSE_POINT)))
- {
- if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED &&
- ctx->supported_features.encrypted_files) {
- if (!ctx->ops->extract_encrypted_stream_creates_file) {
- ret = ctx->ops->extract_encrypted_stream(
- path, lte, ctx);
- if (ret)
- goto error;
- }
- } else {
- ret = ctx->ops->extract_unnamed_stream(
- file_spec, lte, ctx,
- dentry);
- if (ret)
- goto error;
- }
- update_extract_progress(ctx, lte);
- }
- else if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)
- {
- ret = 0;
- if (ctx->supported_features.reparse_points)
- ret = extract_reparse_data(path, ctx, inode, lte);
- #ifndef __WIN32__
- else if ((inode_is_symlink(inode) &&
- ctx->supported_features.symlink_reparse_points))
- ret = extract_symlink(path, ctx, inode, lte);
- #endif
- if (ret)
- return ret;
- }
- }
-
- /* Named data streams. */
- if (can_extract_named_data_streams(ctx)) {
- for (u16 i = 0; i < inode->i_num_ads; i++) {
- struct wim_ads_entry *entry = &inode->i_ads_entries[i];
-
- if (!ads_entry_is_named_stream(entry))
- continue;
- lte = entry->lte;
- if (!lte)
- continue;
- if (lte_spec && lte_spec != lte)
- continue;
- if (lte_spec)
- lte = lte_override;
- ret = ctx->ops->extract_named_stream(file_spec, entry->stream_name,
- entry->stream_name_nbytes / 2,
- lte, ctx);
- if (ret)
- goto error;
- update_extract_progress(ctx, lte);
- }
- }
- return 0;
-
-error:
- ERROR_WITH_ERRNO("Failed to extract data of \"%"TS"\"", path);
- return ret;
-}
-
-/* Set attributes on an extracted file or directory if supported by the
- * extraction mode. */
-static int
-extract_file_attributes(const tchar *path, struct apply_ctx *ctx,
- struct wim_dentry *dentry, unsigned pass)
-{
- int ret;
-
- if (ctx->ops->set_file_attributes &&
- !(ctx->extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES) &&
- !(dentry == ctx->target_dentry && ctx->root_dentry_is_special)) {
- u32 attributes = dentry->d_inode->i_attributes;
-
- /* Clear unsupported attributes. */
- attributes &= ctx->supported_attributes_mask;
-
- if ((attributes & FILE_ATTRIBUTE_DIRECTORY &&
- !ctx->supported_features.encrypted_directories) ||
- (!(attributes & FILE_ATTRIBUTE_DIRECTORY) &&
- !ctx->supported_features.encrypted_files))
- {
- attributes &= ~FILE_ATTRIBUTE_ENCRYPTED;
- }
-
- if (attributes == 0)
- attributes = FILE_ATTRIBUTE_NORMAL;
-
- ret = ctx->ops->set_file_attributes(path, attributes, ctx, pass);
- if (ret) {
- ERROR_WITH_ERRNO("Failed to set attributes on "
- "\"%"TS"\"", path);
- return ret;
- }
- }
- return 0;
-}
-
-
-/* Set or remove the short (DOS) name on an extracted file or directory if
- * supported by the extraction mode. Since DOS names are unimportant and it's
- * easy to run into problems setting them on Windows (SetFileShortName()
- * requires SE_RESTORE privilege, which only the Administrator can request, and
- * also requires DELETE access to the file), failure is ignored unless
- * WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES is set. */
-static int
-extract_short_name(const tchar *path, struct apply_ctx *ctx,
- struct wim_dentry *dentry)
-{
- int ret;
-
- /* The root of the dentry tree being extracted may not be extracted to
- * its original name, so its short name should be ignored. */
- if (dentry == ctx->target_dentry)
- return 0;
-
- if (ctx->supported_features.short_names) {
- ret = ctx->ops->set_short_name(path,
- dentry->short_name,
- dentry->short_name_nbytes / 2,
- ctx);
- if (ret && (ctx->extract_flags &
- WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES))
- {
- ERROR_WITH_ERRNO("Failed to set short name of "
- "\"%"TS"\"", path);
- return ret;
- }
- }
- return 0;
-}
-
-/* Set security descriptor, UNIX data, or neither on an extracted file, taking
- * into account the current extraction mode and flags. */
-static int
-extract_security(const tchar *path, struct apply_ctx *ctx,
- struct wim_dentry *dentry)
-{
- int ret;
- struct wim_inode *inode = dentry->d_inode;
-
- if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS)
- return 0;
-
- if ((ctx->target_dentry == dentry) && ctx->root_dentry_is_special)
- return 0;
-
-#ifndef __WIN32__
- if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) {
- struct wimlib_unix_data data;
-
- ret = inode_get_unix_data(inode, &data, NULL);
- if (ret < 0)
- ret = 0;
- else if (ret == 0)
- ret = ctx->ops->set_unix_data(path, &data, ctx);
- if (ret) {
- if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS) {
- ERROR_WITH_ERRNO("Failed to set UNIX owner, "
- "group, and/or mode on "
- "\"%"TS"\"", path);
- return ret;
- } else {
- WARNING_WITH_ERRNO("Failed to set UNIX owner, "
- "group, and/or/mode on "
- "\"%"TS"\"", path);
- }
- }
- }
- else
-#endif /* __WIN32__ */
- if (ctx->supported_features.security_descriptors &&
- inode->i_security_id != -1)
- {
- const struct wim_security_data *sd;
- const u8 *desc;
- size_t desc_size;
-
- sd = wim_const_security_data(ctx->wim);
- desc = sd->descriptors[inode->i_security_id];
- desc_size = sd->sizes[inode->i_security_id];
-
- ret = ctx->ops->set_security_descriptor(path, desc,
- desc_size, ctx);
- if (ret) {
- if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS) {
- ERROR_WITH_ERRNO("Failed to set security "
- "descriptor on \"%"TS"\"", path);
- return ret;
- } else {
- #if 0
- if (errno != EACCES) {
- WARNING_WITH_ERRNO("Failed to set "
- "security descriptor "
- "on \"%"TS"\"", path);
- }
- #endif
- ctx->no_security_descriptors++;
- }
- }
- }
- return 0;
-}
-
-/* Set timestamps on an extracted file. Failure is warning-only unless
- * WIMLIB_EXTRACT_FLAG_STRICT_TIMESTAMPS is set. */
-static int
-extract_timestamps(const tchar *path, struct apply_ctx *ctx,
- struct wim_dentry *dentry)
-{
- struct wim_inode *inode = dentry->d_inode;
- int ret;
-
- if ((ctx->target_dentry == dentry) && ctx->root_dentry_is_special)
- return 0;
-
- if (ctx->ops->set_timestamps) {
- ret = ctx->ops->set_timestamps(path,
- inode->i_creation_time,
- inode->i_last_write_time,
- inode->i_last_access_time,
- ctx);
- if (ret) {
- if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_TIMESTAMPS) {
- ERROR_WITH_ERRNO("Failed to set timestamps "
- "on \"%"TS"\"", path);
- return ret;
- } else {
- WARNING_WITH_ERRNO("Failed to set timestamps "
- "on \"%"TS"\"", path);
- }
- }
- }
- return 0;
-}
-
-/* Check whether the extraction of a dentry should be skipped completely. */
-static bool
-dentry_is_supported(struct wim_dentry *dentry,
- const struct wim_features *supported_features)
-{
- struct wim_inode *inode = dentry->d_inode;
-
- if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
- return supported_features->reparse_points ||
- (inode_is_symlink(inode) &&
- supported_features->symlink_reparse_points);
- }
- if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) {
- if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
- return supported_features->encrypted_directories != 0;
- else
- return supported_features->encrypted_files != 0;
- }
- return true;
-}
-
-/* Given a WIM dentry to extract, build the path to which to extract it, in the
- * format understood by the callbacks in the apply_operations being used.
- *
- * Write the resulting path into @path, which must have room for at least
- * ctx->ops->path_max characters.
- *
- * Return %true if successful; %false if this WIM dentry doesn't actually need
- * to be extracted or if the calculated path exceeds ctx->ops->max_path
- * characters.
- *
- * 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,
- const 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;
-
- 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->target_dentry; d = d->parent) {
- if (!dentry_in_list(d))
- break;
-
- 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->target_dentry; 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->target_dentry) {
- 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_linked = 1;
- return 0;
-
-hardlink:
- ret = extract_hardlink(oldpath, path, ctx);
- if (ret)
- return ret;
- dentry->was_linked = 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, struct apply_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
- && dentry_in_list(other_dentry))
- {
- 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;
-}
-
-/* Create a file or directory, then immediately extract all streams. The WIM
- * may not be read sequentially by this function. */
-static int
-dentry_extract(struct wim_dentry *dentry, struct apply_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);
-}
-
-/* Finish extracting a file, directory, or symbolic link by setting file
- * security and timestamps. */
-static int
-dentry_extract_final(struct wim_dentry *dentry, struct apply_ctx *ctx)
-{
- int ret;
- tchar path[ctx->ops->path_max];
-
- if (!build_extraction_path(path, dentry, ctx))
- return 0;
-
- ret = extract_security(path, ctx, dentry);
- if (ret)
- return ret;
-
- if (ctx->ops->requires_final_set_attributes_pass) {
- /* Set file attributes (if supported). */
- ret = extract_file_attributes(path, ctx, dentry, 1);
- if (ret)
- return ret;
- }
-
- return extract_timestamps(path, ctx, dentry);
-}
-
-static int
-extract_structure(struct list_head *dentry_list, struct apply_ctx *ctx)
-{
- struct wim_dentry *dentry;
- int ret;
-
- list_for_each_entry(dentry, dentry_list, extraction_list) {
- ret = dentry_extract_skeleton(dentry, ctx);
- if (ret)
- return ret;
- }
- return 0;
-}
-
-static int
-extract_dir_structure(struct list_head *dentry_list, struct apply_ctx *ctx)
-{
- struct wim_dentry *dentry;
- int ret;
-
- list_for_each_entry(dentry, dentry_list, extraction_list) {
- if (dentry_is_directory(dentry)) {
- ret = dentry_extract_skeleton(dentry, ctx);
- if (ret)
- return ret;
- }
- }
- return 0;
-}
-
-static int
-extract_dentries(struct list_head *dentry_list, struct apply_ctx *ctx)
-{
- struct wim_dentry *dentry;
- int ret;
-
- list_for_each_entry(dentry, dentry_list, extraction_list) {
- ret = dentry_extract(dentry, ctx);
- if (ret)
- return ret;
- }
- return 0;
-}
-
-static int
-extract_final_metadata(struct list_head *dentry_list, struct apply_ctx *ctx)
-{
- struct wim_dentry *dentry;
- int ret;
-
- list_for_each_entry_reverse(dentry, dentry_list, extraction_list) {
- ret = dentry_extract_final(dentry, ctx);
- if (ret)
- return ret;
- }
- return 0;
-}
-
-/* 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) {
- if (errno == EEXIST) {
- FREE(name);
- goto retry;
- }
- ERROR_WITH_ERRNO("Failed to open temporary file \"%"TS"\"", name);
- FREE(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.
- */
-static int
-extract_stream_instances(struct wim_lookup_table_entry *lte,
- struct wim_lookup_table_entry *lte_override,
- struct apply_ctx *ctx)
-{
- struct wim_dentry **lte_dentries;
- tchar path[ctx->ops->path_max];
- size_t i;
- int ret;
-
- if (lte->out_refcnt <= ARRAY_LEN(lte->inline_lte_dentries))
- lte_dentries = lte->inline_lte_dentries;
- else
- lte_dentries = lte->lte_dentries;
-
- for (i = 0; i < lte->out_refcnt; i++) {
- struct wim_dentry *dentry = lte_dentries[i];
-
- if (dentry->tmp_flag)
- continue;
- if (!build_extraction_path(path, dentry, ctx))
- continue;
- ret = extract_streams(path, ctx, dentry, lte, lte_override);
- if (ret)
- goto out_clear_tmp_flags;
- dentry->tmp_flag = 1;
- }
- ret = 0;
-out_clear_tmp_flags:
- for (i = 0; i < lte->out_refcnt; i++)
- lte_dentries[i]->tmp_flag = 0;
- return ret;
-}
-
-/* Determine whether the specified stream needs to be extracted to a temporary
- * file or not.
- *
- * @lte->out_refcnt specifies the number of instances of this stream that must
- * be extracted.
- *
- * @is_partial_res is %true if this stream is just one of multiple in a single
- * WIM resource being extracted. */
-static bool
-need_tmpfile_to_extract(struct wim_lookup_table_entry *lte,
- bool is_partial_res)
-{
- /* Temporary file is always required when reading a partial resource,
- * since in that case we retrieve all the contained streams in one pass.
- * */
- if (is_partial_res)
- return true;
-
- /* Otherwise we don't need a temporary file if only a single instance of
- * the stream is needed. */
- if (lte->out_refcnt == 1)
- return false;
-
- wimlib_assert(lte->out_refcnt >= 2);
-
- /* We also don't need a temporary file if random access to the stream is
- * allowed. */
- if (lte->resource_location != RESOURCE_IN_WIM ||
- filedes_is_seekable(<e->rspec->wim->in_fd))
- return false;
-
- return true;
-}
-
-static int
-begin_extract_stream_to_tmpfile(struct wim_lookup_table_entry *lte,
- bool is_partial_res,
- void *_ctx)
-{
- struct apply_ctx *ctx = _ctx;
- int ret;
-
- if (!need_tmpfile_to_extract(lte, is_partial_res)) {
- DEBUG("Temporary file not needed "
- "for stream (size=%"PRIu64")", lte->size);
- ret = extract_stream_instances(lte, lte, ctx);
- if (ret)
- return ret;
-
- return BEGIN_STREAM_STATUS_SKIP_STREAM;
- }
-
- DEBUG("Temporary file needed for stream (size=%"PRIu64")", lte->size);
- return create_temporary_file(&ctx->tmpfile_fd, &ctx->tmpfile_name);
-}
-
-static int
-end_extract_stream_to_tmpfile(struct wim_lookup_table_entry *lte,
- int status, void *_ctx)