+ dentry->was_hardlinked = 1;
+ return 0;
+
+hardlink:
+ ret = extract_hardlink(oldpath, path, ctx);
+ if (ret)
+ return ret;
+ dentry->was_hardlinked = 1;
+ return 0;
+}
+
+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;
+
+ /* Here we may re-order the extraction of multiple names (hard links)
+ * for the same file in the same directory in order to ensure the short
+ * (DOS) name is set correctly. A short name is always associated with
+ * exactly one long name, and at least on NTFS, only one long name for a
+ * file can have a short name associated with it. (More specifically,
+ * there can be unlimited names in the POSIX namespace, but only one
+ * name can be in the Win32+DOS namespace, or one name in the Win32
+ * namespace with a corresponding name in the DOS namespace.) To ensure
+ * the short name of a file is associated with the correct long name in
+ * a directory, we extract the long name with a corresponding short name
+ * before any additional names. This can affect NTFS-3g extraction
+ * (which uses ntfs_set_ntfs_dos_name(), which doesn't allow specifying
+ * the long name to associate with a short name) and may affect Win32
+ * extraction as well (which uses SetFileShortName()). */
+
+ if (dentry->skeleton_extracted)
+ return 0;
+ orig_dentry = NULL;
+ if (ctx->supported_features.short_names
+ && !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->parent == dentry->parent)
+ {
+ 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. 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);
+}
+
+/* Extract all instances of the stream @lte that are being extracted in this
+ * call of extract_tree(). @can_seek specifies whether the WIM file descriptor
+ * is seekable or not (e.g. is a pipe). If not and the stream needs to be
+ * extracted multiple times, it is extracted to a temporary file first.
+ *
+ * This is intended for use with sequential extraction of a WIM image
+ * (WIMLIB_EXTRACT_FLAG_SEQUENTIAL specified). */
+static int
+extract_stream_instances(struct wim_lookup_table_entry *lte,
+ struct apply_ctx *ctx, bool can_seek)
+{
+ struct wim_dentry **lte_dentries;
+ struct wim_lookup_table_entry *lte_tmp = NULL;
+ struct wim_lookup_table_entry *lte_override;
+ tchar *stream_tmp_filename = NULL;
+ tchar path[ctx->ops->path_max];
+ unsigned 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;
+
+ if (likely(can_seek || lte->out_refcnt < 2)) {
+ lte_override = lte;
+ } else {
+ /* Need to extract stream to temporary file. */
+ struct filedes fd;
+ int raw_fd;
+
+ stream_tmp_filename = ttempnam(NULL, T("wimlib"));
+ if (!stream_tmp_filename) {
+ ERROR_WITH_ERRNO("Failed to create temporary filename");
+ ret = WIMLIB_ERR_OPEN;
+ goto out;
+ }
+
+ lte_tmp = memdup(lte, sizeof(struct wim_lookup_table_entry));
+ if (!lte_tmp) {
+ ret = WIMLIB_ERR_NOMEM;
+ goto out_free_stream_tmp_filename;
+ }
+ lte_tmp->resource_location = RESOURCE_IN_FILE_ON_DISK;
+ lte_tmp->file_on_disk = stream_tmp_filename;
+ lte_override = lte_tmp;
+
+ raw_fd = topen(stream_tmp_filename,
+ O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600);
+ if (raw_fd < 0) {
+ ERROR_WITH_ERRNO("Failed to open temporary file");
+ ret = WIMLIB_ERR_OPEN;
+ goto out_free_lte_tmp;
+ }
+ filedes_init(&fd, raw_fd);
+ ret = extract_wim_resource_to_fd(lte, &fd,
+ wim_resource_size(lte));
+ if (filedes_close(&fd) && !ret)
+ ret = WIMLIB_ERR_WRITE;
+ if (ret)
+ goto out_unlink_stream_tmp_file;
+ }
+
+ /* Extract all instances of the stream, reading either from the stream
+ * in the WIM file or from the temporary file containing the stream.
+ * dentry->tmp_flag is used to ensure that each dentry is processed only
+ * once regardless of how many times this stream appears in the streams
+ * of the corresponding inode. */
+ 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;
+out_unlink_stream_tmp_file:
+ if (stream_tmp_filename)
+ tunlink(stream_tmp_filename);
+out_free_lte_tmp:
+ FREE(lte_tmp);
+out_free_stream_tmp_filename:
+ FREE(stream_tmp_filename);
+out:
+ return ret;
+}
+
+/* Extracts a list of streams (ctx.stream_list), assuming that the directory
+ * structure and empty files were already created. This relies on the
+ * per-`struct wim_lookup_table_entry' list of dentries that reference each
+ * stream that was constructed earlier. Streams are extracted exactly in the
+ * order of the stream list; however, unless the WIM's file descriptor is
+ * detected to be non-seekable, streams may be read from the WIM file more than
+ * one time if multiple copies need to be extracted. */
+static int
+extract_stream_list(struct apply_ctx *ctx)
+{
+ struct wim_lookup_table_entry *lte;
+ bool can_seek;
+ int ret;
+
+ can_seek = (lseek(ctx->wim->in_fd.fd, 0, SEEK_CUR) != -1);
+ list_for_each_entry(lte, &ctx->stream_list, extraction_list) {
+ ret = extract_stream_instances(lte, ctx, can_seek);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+#define PWM_ALLOW_WIM_HDR 0x00001
+#define PWM_SILENT_EOF 0x00002
+
+/* Read the header from a stream in a pipable WIM. */
+static int
+read_pwm_stream_header(WIMStruct *pwm, struct wim_lookup_table_entry *lte,
+ int flags)
+{
+ struct pwm_stream_hdr stream_hdr;
+ int ret;
+
+ ret = full_read(&pwm->in_fd, &stream_hdr, sizeof(stream_hdr));
+ if (ret)
+ goto read_error;
+
+ if ((flags & PWM_ALLOW_WIM_HDR) && stream_hdr.magic == PWM_MAGIC) {
+ u8 buf[WIM_HEADER_DISK_SIZE - sizeof(stream_hdr)];
+ ret = full_read(&pwm->in_fd, buf, sizeof(buf));
+ if (ret)
+ goto read_error;
+ lte->resource_location = RESOURCE_NONEXISTENT;
+ return 0;
+ }
+
+ if (stream_hdr.magic != PWM_STREAM_MAGIC) {
+ ERROR("Data read on pipe is invalid (expected stream header).");
+ return WIMLIB_ERR_INVALID_PIPABLE_WIM;
+ }
+
+ lte->resource_entry.original_size = le64_to_cpu(stream_hdr.uncompressed_size);
+ copy_hash(lte->hash, stream_hdr.hash);
+ lte->resource_entry.flags = le32_to_cpu(stream_hdr.flags);
+ lte->resource_entry.offset = pwm->in_fd.offset;
+ lte->resource_location = RESOURCE_IN_WIM;
+ lte->wim = pwm;
+ if (lte->resource_entry.flags & WIM_RESHDR_FLAG_COMPRESSED) {
+ lte->compression_type = pwm->compression_type;
+ lte->resource_entry.size = 0;
+ } else {
+ lte->compression_type = WIMLIB_COMPRESSION_TYPE_NONE;
+ lte->resource_entry.size = lte->resource_entry.original_size;
+ }
+ lte->is_pipable = 1;
+ return 0;
+
+read_error:
+ if (ret != WIMLIB_ERR_UNEXPECTED_END_OF_FILE || !(flags & PWM_SILENT_EOF))
+ ERROR_WITH_ERRNO("Error reading pipable WIM from pipe");
+ return ret;
+}
+
+/* Skip over an unneeded stream in a pipable WIM being read from a pipe. */
+static int
+skip_pwm_stream(struct wim_lookup_table_entry *lte)
+{
+ return read_partial_wim_resource(lte, wim_resource_size(lte),
+ NULL, NULL,
+ WIMLIB_READ_RESOURCE_FLAG_SEEK_ONLY,
+ 0);
+}
+
+static int
+extract_streams_from_pipe(struct apply_ctx *ctx)
+{
+ struct wim_lookup_table_entry *found_lte;
+ struct wim_lookup_table_entry *needed_lte;
+ struct wim_lookup_table *lookup_table;
+ int ret;
+ int pwm_flags;
+
+ ret = WIMLIB_ERR_NOMEM;
+ found_lte = new_lookup_table_entry();
+ if (!found_lte)
+ goto out;
+
+ lookup_table = ctx->wim->lookup_table;
+ pwm_flags = PWM_ALLOW_WIM_HDR;
+ if ((ctx->extract_flags & WIMLIB_EXTRACT_FLAG_RESUME))
+ pwm_flags |= PWM_SILENT_EOF;
+ while (ctx->num_streams_remaining) {
+ ret = read_pwm_stream_header(ctx->wim, found_lte, pwm_flags);
+ if (ret) {
+ if (ret == WIMLIB_ERR_UNEXPECTED_END_OF_FILE &&
+ (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_RESUME))
+ {
+ goto resume_done;
+ }
+ goto out_free_found_lte;
+ }
+
+ if ((found_lte->resource_location != RESOURCE_NONEXISTENT)
+ && !(found_lte->resource_entry.flags & WIM_RESHDR_FLAG_METADATA)
+ && (needed_lte = __lookup_resource(lookup_table, found_lte->hash))
+ && (needed_lte->out_refcnt))
+ {
+ copy_resource_entry(&needed_lte->resource_entry,
+ &found_lte->resource_entry);
+ needed_lte->resource_location = found_lte->resource_location;
+ needed_lte->wim = found_lte->wim;
+ needed_lte->compression_type = found_lte->compression_type;
+ needed_lte->is_pipable = found_lte->is_pipable;
+
+ ret = extract_stream_instances(needed_lte, ctx, false);
+ if (ret)
+ goto out_free_found_lte;
+ ctx->num_streams_remaining--;
+ } else if (found_lte->resource_location != RESOURCE_NONEXISTENT) {
+ ret = skip_pwm_stream(found_lte);
+ if (ret)
+ goto out_free_found_lte;
+ }
+ }
+ ret = 0;
+out_free_found_lte:
+ free_lookup_table_entry(found_lte);
+out:
+ return ret;
+
+resume_done:
+ /* TODO */
+ return 0;
+}
+
+/* Finish extracting a file, directory, or symbolic link by setting file
+ * security and timestamps. */
+static int
+dentry_extract_final(struct wim_dentry *dentry, void *_ctx)
+{
+ struct apply_ctx *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;
+
+ return extract_timestamps(path, ctx, dentry);
+}
+
+/*
+ * Extract a WIM dentry to standard output.
+ *
+ * This obviously doesn't make sense in all cases. We return an error if the
+ * dentry does not correspond to a regular file. Otherwise we extract the
+ * unnamed data stream only.
+ */
+static int
+extract_dentry_to_stdout(struct wim_dentry *dentry)
+{
+ int ret = 0;
+ if (dentry->d_inode->i_attributes & (FILE_ATTRIBUTE_REPARSE_POINT |
+ FILE_ATTRIBUTE_DIRECTORY))
+ {
+ ERROR("\"%"TS"\" is not a regular file and therefore cannot be "
+ "extracted to standard output", dentry_full_path(dentry));
+ ret = WIMLIB_ERR_NOT_A_REGULAR_FILE;
+ } else {
+ struct wim_lookup_table_entry *lte;
+
+ lte = inode_unnamed_lte_resolved(dentry->d_inode);
+ if (lte) {
+ struct filedes _stdout;
+ filedes_init(&_stdout, STDOUT_FILENO);
+ ret = extract_wim_resource_to_fd(lte, &_stdout,
+ wim_resource_size(lte));
+ }
+ }
+ return ret;
+}
+
+#ifdef __WIN32__
+static const utf16lechar replacement_char = cpu_to_le16(0xfffd);
+#else
+static const utf16lechar replacement_char = cpu_to_le16('?');
+#endif
+
+static bool
+file_name_valid(utf16lechar *name, size_t num_chars, bool fix)
+{
+ size_t i;
+
+ if (num_chars == 0)
+ return true;
+ for (i = 0; i < num_chars; i++) {
+ switch (name[i]) {
+ #ifdef __WIN32__
+ case cpu_to_le16('\\'):
+ case cpu_to_le16(':'):
+ case cpu_to_le16('*'):
+ case cpu_to_le16('?'):
+ case cpu_to_le16('"'):
+ case cpu_to_le16('<'):
+ case cpu_to_le16('>'):
+ case cpu_to_le16('|'):
+ #endif
+ case cpu_to_le16('/'):
+ case cpu_to_le16('\0'):
+ if (fix)
+ name[i] = replacement_char;
+ else
+ return false;
+ }
+ }
+
+#ifdef __WIN32__
+ if (name[num_chars - 1] == cpu_to_le16(' ') ||
+ name[num_chars - 1] == cpu_to_le16('.'))
+ {
+ if (fix)
+ name[num_chars - 1] = replacement_char;
+ else
+ return false;
+ }
+#endif
+ return true;
+}
+
+static bool
+dentry_is_dot_or_dotdot(const struct wim_dentry *dentry)
+{
+ const utf16lechar *file_name = dentry->file_name;
+ return file_name != NULL &&
+ file_name[0] == cpu_to_le16('.') &&
+ (file_name[1] == cpu_to_le16('\0') ||
+ (file_name[1] == cpu_to_le16('.') &&
+ file_name[2] == cpu_to_le16('\0')));
+}