+ inode = dentry->d_inode;
+
+ /* Extract all aliases only when the "first" comes up. */
+ if (dentry != inode_first_extraction_dentry(inode))
+ return 0;
+
+ /* Is this a directory, a symbolic link, or any type of nonempty file?
+ */
+ if (should_extract_as_directory(inode) || inode_is_symlink(inode) ||
+ inode_get_blob_for_unnamed_data_stream_resolved(inode))
+ return 0;
+
+ /* Recognize special files in UNIX_DATA mode */
+ if ((ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) &&
+ inode_get_unix_data(inode, &unix_data) &&
+ !S_ISREG(unix_data.mode))
+ {
+ path = unix_build_extraction_path(dentry, ctx);
+ retry_mknod:
+ if (mknod(path, unix_data.mode, unix_data.rdev)) {
+ if (errno == EPERM) {
+ WARNING_WITH_ERRNO("Can't create special "
+ "file \"%s\"", path);
+ ctx->num_special_files_ignored++;
+ return 0;
+ }
+ if (errno == EEXIST && !unlink(path))
+ goto retry_mknod;
+ ERROR_WITH_ERRNO("Can't create special file \"%s\"",
+ path);
+ return WIMLIB_ERR_MKNOD;
+ }
+ /* On special files, we can set timestamps immediately because
+ * we don't need to write any data to them. */
+ ret = unix_set_metadata(-1, inode, path, ctx);
+ } else {
+ int fd;
+
+ path = unix_build_extraction_path(dentry, ctx);
+ retry_create:
+ fd = open(path, O_EXCL | O_CREAT | O_WRONLY | O_NOFOLLOW, 0644);
+ if (fd < 0) {
+ if (errno == EEXIST && !unlink(path))
+ goto retry_create;
+ ERROR_WITH_ERRNO("Can't create regular file \"%s\"", path);
+ return WIMLIB_ERR_OPEN;
+ }
+ /* On empty files, we can set timestamps immediately because we
+ * don't need to write any data to them. */
+ ret = unix_set_metadata(fd, inode, path, ctx);
+ if (close(fd) && !ret) {
+ ERROR_WITH_ERRNO("Error closing \"%s\"", path);
+ ret = WIMLIB_ERR_WRITE;
+ }
+ }
+ if (ret)
+ return ret;
+
+ ret = unix_create_hardlinks(inode, dentry, path, ctx);
+ if (ret)
+ return ret;
+
+ return report_file_created(&ctx->common);
+}
+
+static int
+unix_create_dirs_and_empty_files(const struct list_head *dentry_list,
+ struct unix_apply_ctx *ctx)
+{
+ const struct wim_dentry *dentry;
+ int ret;
+
+ list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
+ ret = unix_create_if_directory(dentry, ctx);
+ if (ret)
+ return ret;
+ }
+ list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
+ ret = unix_extract_if_empty_file(dentry, ctx);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static void
+unix_count_dentries(const struct list_head *dentry_list,
+ u64 *dir_count_ret, u64 *empty_file_count_ret)
+{
+ const struct wim_dentry *dentry;
+ u64 dir_count = 0;
+ u64 empty_file_count = 0;
+
+ list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
+
+ const struct wim_inode *inode = dentry->d_inode;
+
+ if (should_extract_as_directory(inode))
+ dir_count++;
+ else if ((dentry == inode_first_extraction_dentry(inode)) &&
+ !inode_is_symlink(inode) &&
+ !inode_get_blob_for_unnamed_data_stream_resolved(inode))
+ empty_file_count++;
+ }
+
+ *dir_count_ret = dir_count;
+ *empty_file_count_ret = empty_file_count;
+}
+
+static int
+unix_create_symlink(const struct wim_inode *inode, const char *path,
+ size_t rpdatalen, struct unix_apply_ctx *ctx)
+{
+ char target[REPARSE_POINT_MAX_SIZE];
+ struct blob_descriptor blob_override;
+ int ret;
+
+ blob_set_is_located_in_attached_buffer(&blob_override,
+ ctx->reparse_data, rpdatalen);
+
+ ret = wim_inode_readlink(inode, target, sizeof(target) - 1,
+ &blob_override,
+ ctx->target_abspath,
+ ctx->target_abspath_nchars);
+ if (unlikely(ret < 0)) {
+ errno = -ret;
+ return WIMLIB_ERR_READLINK;
+ }
+ target[ret] = '\0';
+
+retry_symlink:
+ if (symlink(target, path)) {
+ if (errno == EEXIST && !unlink(path))
+ goto retry_symlink;
+ return WIMLIB_ERR_LINK;
+ }
+ return 0;
+}
+
+static void
+unix_cleanup_open_fds(struct unix_apply_ctx *ctx, unsigned offset)
+{
+ for (unsigned i = offset; i < ctx->num_open_fds; i++)
+ filedes_close(&ctx->open_fds[i]);
+ ctx->num_open_fds = 0;
+ ctx->any_sparse_files = false;
+}
+
+static int
+unix_begin_extract_blob_instance(const struct blob_descriptor *blob,
+ const struct wim_inode *inode,
+ const struct wim_inode_stream *strm,
+ struct unix_apply_ctx *ctx)
+{
+ const struct wim_dentry *first_dentry;
+ const char *first_path;
+ int fd;
+
+ if (unlikely(strm->stream_type == STREAM_TYPE_REPARSE_POINT)) {
+ /* On UNIX, symbolic links must be created with symlink(), which
+ * requires that the full link target be available. */
+ if (blob->size > REPARSE_DATA_MAX_SIZE) {
+ ERROR_WITH_ERRNO("Reparse data of \"%s\" has size "
+ "%"PRIu64" bytes (exceeds %u bytes)",
+ inode_any_full_path(inode),
+ blob->size, REPARSE_DATA_MAX_SIZE);
+ return WIMLIB_ERR_INVALID_REPARSE_DATA;
+ }
+ ctx->reparse_ptr = ctx->reparse_data;
+ return 0;
+ }
+
+ wimlib_assert(stream_is_unnamed_data_stream(strm));
+
+ /* Unnamed data stream of "regular" file */
+
+ /* This should be ensured by extract_blob_list() */
+ wimlib_assert(ctx->num_open_fds < MAX_OPEN_FILES);
+
+ first_dentry = inode_first_extraction_dentry(inode);
+ first_path = unix_build_extraction_path(first_dentry, ctx);
+retry_create:
+ fd = open(first_path, O_EXCL | O_CREAT | O_WRONLY | O_NOFOLLOW, 0644);
+ if (fd < 0) {
+ if (errno == EEXIST && !unlink(first_path))
+ goto retry_create;
+ ERROR_WITH_ERRNO("Can't create regular file \"%s\"", first_path);