]> wimlib.net Git - wimlib/blobdiff - src/win32.c
add_image => add
[wimlib] / src / win32.c
index 36f33f522bc2c9d930047ff1b398be5f2cafa087..f9f4276510c6b066bb0329592504dd359aa7eb5c 100644 (file)
@@ -48,6 +48,7 @@
 #define MAX_GET_SD_ACCESS_DENIED_WARNINGS 1
 #define MAX_GET_SACL_PRIV_NOTHELD_WARNINGS 1
 #define MAX_CREATE_HARD_LINK_WARNINGS 5
+#define MAX_CREATE_SOFT_LINK_WARNINGS 5
 struct win32_capture_state {
        unsigned long num_get_sd_access_denied;
        unsigned long num_get_sacl_priv_notheld;
@@ -78,6 +79,279 @@ win32_error(u32 err_code)
 #  define win32_error(err_code)
 #endif /* !ENABLE_ERROR_MESSAGES */
 
+static int
+win32_error_to_errno(DWORD err_code)
+{
+       /* This mapping is that used in Cygwin.
+        * Some of these choices are arbitrary. */
+       switch (err_code) {
+       case ERROR_ACCESS_DENIED:
+               return EACCES;
+       case ERROR_ACTIVE_CONNECTIONS:
+               return EAGAIN;
+       case ERROR_ALREADY_EXISTS:
+               return EEXIST;
+       case ERROR_BAD_DEVICE:
+               return ENODEV;
+       case ERROR_BAD_EXE_FORMAT:
+               return ENOEXEC;
+       case ERROR_BAD_NETPATH:
+               return ENOENT;
+       case ERROR_BAD_NET_NAME:
+               return ENOENT;
+       case ERROR_BAD_NET_RESP:
+               return ENOSYS;
+       case ERROR_BAD_PATHNAME:
+               return ENOENT;
+       case ERROR_BAD_PIPE:
+               return EINVAL;
+       case ERROR_BAD_UNIT:
+               return ENODEV;
+       case ERROR_BAD_USERNAME:
+               return EINVAL;
+       case ERROR_BEGINNING_OF_MEDIA:
+               return EIO;
+       case ERROR_BROKEN_PIPE:
+               return EPIPE;
+       case ERROR_BUSY:
+               return EBUSY;
+       case ERROR_BUS_RESET:
+               return EIO;
+       case ERROR_CALL_NOT_IMPLEMENTED:
+               return ENOSYS;
+       case ERROR_CANNOT_MAKE:
+               return EPERM;
+       case ERROR_CHILD_NOT_COMPLETE:
+               return EBUSY;
+       case ERROR_COMMITMENT_LIMIT:
+               return EAGAIN;
+       case ERROR_CRC:
+               return EIO;
+       case ERROR_DEVICE_DOOR_OPEN:
+               return EIO;
+       case ERROR_DEVICE_IN_USE:
+               return EAGAIN;
+       case ERROR_DEVICE_REQUIRES_CLEANING:
+               return EIO;
+       case ERROR_DIRECTORY:
+               return ENOTDIR;
+       case ERROR_DIR_NOT_EMPTY:
+               return ENOTEMPTY;
+       case ERROR_DISK_CORRUPT:
+               return EIO;
+       case ERROR_DISK_FULL:
+               return ENOSPC;
+#ifdef ENOTUNIQ
+       case ERROR_DUP_NAME:
+               return ENOTUNIQ;
+#endif
+       case ERROR_EAS_DIDNT_FIT:
+               return ENOSPC;
+       case ERROR_EAS_NOT_SUPPORTED:
+               return ENOTSUP;
+       case ERROR_EA_LIST_INCONSISTENT:
+               return EINVAL;
+       case ERROR_EA_TABLE_FULL:
+               return ENOSPC;
+       case ERROR_END_OF_MEDIA:
+               return ENOSPC;
+       case ERROR_EOM_OVERFLOW:
+               return EIO;
+       case ERROR_EXE_MACHINE_TYPE_MISMATCH:
+               return ENOEXEC;
+       case ERROR_EXE_MARKED_INVALID:
+               return ENOEXEC;
+       case ERROR_FILEMARK_DETECTED:
+               return EIO;
+       case ERROR_FILENAME_EXCED_RANGE:
+               return ENAMETOOLONG;
+       case ERROR_FILE_CORRUPT:
+               return EEXIST;
+       case ERROR_FILE_EXISTS:
+               return EEXIST;
+       case ERROR_FILE_INVALID:
+               return ENXIO;
+       case ERROR_FILE_NOT_FOUND:
+               return ENOENT;
+       case ERROR_HANDLE_DISK_FULL:
+               return ENOSPC;
+#ifdef ENODATA
+       case ERROR_HANDLE_EOF:
+               return ENODATA;
+#endif
+       case ERROR_INVALID_ADDRESS:
+               return EINVAL;
+       case ERROR_INVALID_AT_INTERRUPT_TIME:
+               return EINTR;
+       case ERROR_INVALID_BLOCK_LENGTH:
+               return EIO;
+       case ERROR_INVALID_DATA:
+               return EINVAL;
+       case ERROR_INVALID_DRIVE:
+               return ENODEV;
+       case ERROR_INVALID_EA_NAME:
+               return EINVAL;
+       case ERROR_INVALID_EXE_SIGNATURE:
+               return ENOEXEC;
+#ifdef EBADRQC
+       case ERROR_INVALID_FUNCTION:
+               return EBADRQC;
+#endif
+       case ERROR_INVALID_HANDLE:
+               return EBADF;
+       case ERROR_INVALID_NAME:
+               return ENOENT;
+       case ERROR_INVALID_PARAMETER:
+               return EINVAL;
+       case ERROR_INVALID_SIGNAL_NUMBER:
+               return EINVAL;
+       case ERROR_IOPL_NOT_ENABLED:
+               return ENOEXEC;
+       case ERROR_IO_DEVICE:
+               return EIO;
+       case ERROR_IO_INCOMPLETE:
+               return EAGAIN;
+       case ERROR_IO_PENDING:
+               return EAGAIN;
+       case ERROR_LOCK_VIOLATION:
+               return EBUSY;
+       case ERROR_MAX_THRDS_REACHED:
+               return EAGAIN;
+       case ERROR_META_EXPANSION_TOO_LONG:
+               return EINVAL;
+       case ERROR_MOD_NOT_FOUND:
+               return ENOENT;
+#ifdef EMSGSIZE
+       case ERROR_MORE_DATA:
+               return EMSGSIZE;
+#endif
+       case ERROR_NEGATIVE_SEEK:
+               return EINVAL;
+       case ERROR_NETNAME_DELETED:
+               return ENOENT;
+       case ERROR_NOACCESS:
+               return EFAULT;
+       case ERROR_NONE_MAPPED:
+               return EINVAL;
+       case ERROR_NONPAGED_SYSTEM_RESOURCES:
+               return EAGAIN;
+#ifdef ENOLINK
+       case ERROR_NOT_CONNECTED:
+               return ENOLINK;
+#endif
+       case ERROR_NOT_ENOUGH_MEMORY:
+               return ENOMEM;
+       case ERROR_NOT_OWNER:
+               return EPERM;
+#ifdef ENOMEDIUM
+       case ERROR_NOT_READY:
+               return ENOMEDIUM;
+#endif
+       case ERROR_NOT_SAME_DEVICE:
+               return EXDEV;
+       case ERROR_NOT_SUPPORTED:
+               return ENOSYS;
+       case ERROR_NO_DATA:
+               return EPIPE;
+       case ERROR_NO_DATA_DETECTED:
+               return EIO;
+#ifdef ENOMEDIUM
+       case ERROR_NO_MEDIA_IN_DRIVE:
+               return ENOMEDIUM;
+#endif
+#ifdef ENMFILE
+       case ERROR_NO_MORE_FILES:
+               return ENMFILE;
+#endif
+#ifdef ENMFILE
+       case ERROR_NO_MORE_ITEMS:
+               return ENMFILE;
+#endif
+       case ERROR_NO_MORE_SEARCH_HANDLES:
+               return ENFILE;
+       case ERROR_NO_PROC_SLOTS:
+               return EAGAIN;
+       case ERROR_NO_SIGNAL_SENT:
+               return EIO;
+       case ERROR_NO_SYSTEM_RESOURCES:
+               return EFBIG;
+       case ERROR_NO_TOKEN:
+               return EINVAL;
+       case ERROR_OPEN_FAILED:
+               return EIO;
+       case ERROR_OPEN_FILES:
+               return EAGAIN;
+       case ERROR_OUTOFMEMORY:
+               return ENOMEM;
+       case ERROR_PAGED_SYSTEM_RESOURCES:
+               return EAGAIN;
+       case ERROR_PAGEFILE_QUOTA:
+               return EAGAIN;
+       case ERROR_PATH_NOT_FOUND:
+               return ENOENT;
+       case ERROR_PIPE_BUSY:
+               return EBUSY;
+       case ERROR_PIPE_CONNECTED:
+               return EBUSY;
+#ifdef ECOMM
+       case ERROR_PIPE_LISTENING:
+               return ECOMM;
+       case ERROR_PIPE_NOT_CONNECTED:
+               return ECOMM;
+#endif
+       case ERROR_POSSIBLE_DEADLOCK:
+               return EDEADLOCK;
+       case ERROR_PRIVILEGE_NOT_HELD:
+               return EPERM;
+       case ERROR_PROCESS_ABORTED:
+               return EFAULT;
+       case ERROR_PROC_NOT_FOUND:
+               return ESRCH;
+#ifdef ENONET
+       case ERROR_REM_NOT_LIST:
+               return ENONET;
+#endif
+       case ERROR_SECTOR_NOT_FOUND:
+               return EINVAL;
+       case ERROR_SEEK:
+               return EINVAL;
+       case ERROR_SETMARK_DETECTED:
+               return EIO;
+       case ERROR_SHARING_BUFFER_EXCEEDED:
+               return ENOLCK;
+       case ERROR_SHARING_VIOLATION:
+               return EBUSY;
+       case ERROR_SIGNAL_PENDING:
+               return EBUSY;
+       case ERROR_SIGNAL_REFUSED:
+               return EIO;
+#ifdef ELIBBAD
+       case ERROR_SXS_CANT_GEN_ACTCTX:
+               return ELIBBAD;
+#endif
+       case ERROR_THREAD_1_INACTIVE:
+               return EINVAL;
+       case ERROR_TOO_MANY_LINKS:
+               return EMLINK;
+       case ERROR_TOO_MANY_OPEN_FILES:
+               return EMFILE;
+       case ERROR_WAIT_NO_CHILDREN:
+               return ECHILD;
+       case ERROR_WORKING_SET_QUOTA:
+               return EAGAIN;
+       case ERROR_WRITE_PROTECT:
+               return EROFS;
+       default:
+               return -1;
+       }
+}
+
+static void
+set_errno_from_GetLastError()
+{
+       errno = win32_error_to_errno(GetLastError());
+}
+
 /* Pointers to functions that are not available on all targetted versions of
  * Windows (XP and later).  NOTE: The WINAPI annotations seem to be important; I
  * assume it specifies a certain calling convention. */
@@ -428,7 +702,7 @@ win32_get_security_descriptor(struct wim_dentry *dentry,
                              struct sd_set *sd_set,
                              const wchar_t *path,
                              struct win32_capture_state *state,
-                             int add_image_flags)
+                             int add_flags)
 {
        SECURITY_INFORMATION requestedInformation;
        DWORD lenNeeded = 0;
@@ -463,7 +737,7 @@ again:
                }
        }
 
-       if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_STRICT_ACLS)
+       if (add_flags & WIMLIB_ADD_FLAG_STRICT_ACLS)
                goto fail;
 
        switch (err) {
@@ -653,7 +927,7 @@ enum rp_status {
  * fixup where we change it to be absolute relative to the root of the directory
  * tree being captured.
  *
- * Note that this is only executed when WIMLIB_ADD_IMAGE_FLAG_RPFIX has been
+ * Note that this is only executed when WIMLIB_ADD_FLAG_RPFIX has been
  * set.
  *
  * @capture_root_ino and @capture_root_dev indicate the inode number and device
@@ -678,6 +952,8 @@ win32_capture_maybe_rpfix_target(wchar_t *target, u16 *target_nbytes_p,
        if (ret < 0)
                return RP_NOT_FIXED;
        stripped_chars = ret;
+       if (stripped_chars)
+               stripped_chars -= 2;
        target[target_nchars] = L'\0';
        orig_target = target;
        target = capture_fixup_absolute_symlink(target + stripped_chars,
@@ -688,7 +964,7 @@ win32_capture_maybe_rpfix_target(wchar_t *target, u16 *target_nbytes_p,
        wmemmove(orig_target + stripped_chars, target, target_nchars + 1);
        *target_nbytes_p = (target_nchars + stripped_chars) * sizeof(wchar_t);
        DEBUG("Fixed reparse point (new target: \"%ls\")", orig_target);
-       if (stripped_chars == 6)
+       if (stripped_chars)
                return RP_FIXED_FULLPATH;
        else
                return RP_FIXED_ABSPATH;
@@ -698,7 +974,8 @@ win32_capture_maybe_rpfix_target(wchar_t *target, u16 *target_nbytes_p,
  * failure. */
 static int
 win32_capture_try_rpfix(u8 *rpbuf, u16 *rpbuflen_p,
-                       u64 capture_root_ino, u64 capture_root_dev)
+                       u64 capture_root_ino, u64 capture_root_dev,
+                       const wchar_t *path)
 {
        struct reparse_data rpdata;
        DWORD rpbuflen;
@@ -736,6 +1013,19 @@ win32_capture_try_rpfix(u8 *rpbuf, u16 *rpbuflen_p,
                else
                        ret = -ret;
        } else {
+               if (rp_status == RP_EXCLUDED) {
+                       size_t print_name_nchars = rpdata.print_name_nbytes / 2;
+                       wchar_t print_name0[print_name_nchars + 1];
+                       print_name0[print_name_nchars] = L'\0';
+                       wmemcpy(print_name0, rpdata.print_name, print_name_nchars);
+                       WARNING("Ignoring %ls pointing out of capture directory:\n"
+                               "          \"%ls\" -> \"%ls\"\n"
+                               "          (Use --norpfix to capture all symbolic links "
+                               "and junction points as-is)",
+                               (rpdata.rptag == WIM_IO_REPARSE_TAG_SYMLINK) ?
+                                       L"absolute symbolic link" : L"junction point",
+                               path, print_name0);
+               }
                ret = rp_status;
        }
        return ret;
@@ -795,7 +1085,7 @@ win32_get_reparse_data(HANDLE hFile, const wchar_t *path,
 
        rpbuflen = bytesReturned;
        reparse_tag = le32_to_cpu(*(u32*)rpbuf);
-       if (params->add_image_flags & WIMLIB_ADD_IMAGE_FLAG_RPFIX &&
+       if (params->add_flags & WIMLIB_ADD_FLAG_RPFIX &&
            (reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
             reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT))
        {
@@ -803,7 +1093,8 @@ win32_get_reparse_data(HANDLE hFile, const wchar_t *path,
                ret = win32_capture_try_rpfix(rpbuf,
                                              &rpbuflen,
                                              params->capture_root_ino,
-                                             params->capture_root_dev);
+                                             params->capture_root_dev,
+                                             path);
        } else {
                ret = RP_NOT_FIXED;
        }
@@ -1114,12 +1405,12 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret,
        u16 not_rpfixed;
 
        if (exclude_path(path, path_num_chars, params->config, true)) {
-               if (params->add_image_flags & WIMLIB_ADD_IMAGE_FLAG_ROOT) {
+               if (params->add_flags & WIMLIB_ADD_FLAG_ROOT) {
                        ERROR("Cannot exclude the root directory from capture");
                        ret = WIMLIB_ERR_INVALID_CAPTURE_CONFIG;
                        goto out;
                }
-               if ((params->add_image_flags & WIMLIB_ADD_IMAGE_FLAG_EXCLUDE_VERBOSE)
+               if ((params->add_flags & WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE)
                    && params->progress_func)
                {
                        union wimlib_progress_info info;
@@ -1131,7 +1422,7 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret,
                goto out;
        }
 
-       if ((params->add_image_flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE)
+       if ((params->add_flags & WIMLIB_ADD_FLAG_VERBOSE)
            && params->progress_func)
        {
                union wimlib_progress_info info;
@@ -1210,14 +1501,14 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret,
        inode->i_last_access_time = FILETIME_to_u64(&file_info.ftLastAccessTime);
        inode->i_resolved = 1;
 
-       params->add_image_flags &= ~(WIMLIB_ADD_IMAGE_FLAG_ROOT | WIMLIB_ADD_IMAGE_FLAG_SOURCE);
+       params->add_flags &= ~WIMLIB_ADD_FLAG_ROOT;
 
-       if (!(params->add_image_flags & WIMLIB_ADD_IMAGE_FLAG_NO_ACLS)
+       if (!(params->add_flags & WIMLIB_ADD_FLAG_NO_ACLS)
            && (vol_flags & FILE_PERSISTENT_ACLS))
        {
                ret = win32_get_security_descriptor(root, params->sd_set,
                                                    path, state,
-                                                   params->add_image_flags);
+                                                   params->add_flags);
                if (ret)
                        goto out_close_handle;
        }
@@ -1267,7 +1558,7 @@ out:
 
 static void
 win32_do_capture_warnings(const struct win32_capture_state *state,
-                         int add_image_flags)
+                         int add_flags)
 {
        if (state->num_get_sacl_priv_notheld == 0 &&
            state->num_get_sd_access_denied == 0)
@@ -1290,7 +1581,7 @@ win32_do_capture_warnings(const struct win32_capture_state *state,
 "          desired metadata has been captured exactly.  However, if you\n"
 "          do not care about capturing security descriptors correctly, then\n"
 "          nothing more needs to be done%ls\n",
-       (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_NO_ACLS) ? L"." :
+       (add_flags & WIMLIB_ADD_FLAG_NO_ACLS) ? L"." :
          L", although you might consider\n"
 "          passing the --no-acls flag to `wimlib-imagex capture' or\n"
 "          `wimlib-imagex append' to explicitly capture no security\n"
@@ -1314,6 +1605,14 @@ win32_build_dentry_tree(struct wim_dentry **root_ret,
        if (path_nchars > 32767)
                return WIMLIB_ERR_INVALID_PARAM;
 
+       if (GetFileAttributesW(root_disk_path) == INVALID_FILE_ATTRIBUTES &&
+           GetLastError() == ERROR_FILE_NOT_FOUND)
+       {
+               ERROR("Capture directory \"%ls\" does not exist!",
+                     root_disk_path);
+               return WIMLIB_ERR_OPENDIR;
+       }
+
        ret = win32_get_file_and_vol_ids(root_disk_path,
                                         &params->capture_root_ino,
                                         &params->capture_root_dev);
@@ -1338,7 +1637,7 @@ win32_build_dentry_tree(struct wim_dentry **root_ret,
                                                &state, vol_flags);
        FREE(path);
        if (ret == 0)
-               win32_do_capture_warnings(&state, params->add_image_flags);
+               win32_do_capture_warnings(&state, params->add_flags);
        return ret;
 }
 
@@ -1435,7 +1734,7 @@ win32_set_reparse_data(HANDLE h,
                       const struct wim_inode *inode,
                       const struct wim_lookup_table_entry *lte,
                       const wchar_t *path,
-                      const struct apply_args *args)
+                      struct apply_args *args)
 {
        int ret;
        u8 rpbuf[REPARSE_POINT_MAX_SIZE];
@@ -1487,15 +1786,30 @@ win32_set_reparse_data(HANDLE h,
                             NULL /* lpOverlapped */))
        {
                DWORD err = GetLastError();
-               ERROR("Failed to set reparse data on \"%ls\"", path);
-               win32_error(err);
                if (err == ERROR_ACCESS_DENIED || err == ERROR_PRIVILEGE_NOT_HELD)
-                       return WIMLIB_ERR_INSUFFICIENT_PRIVILEGES_TO_EXTRACT;
-               else if (inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
-                        inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT)
-                       return WIMLIB_ERR_LINK;
-               else
-                       return WIMLIB_ERR_WRITE;
+               {
+                       args->num_soft_links_failed++;
+                       if (args->num_soft_links_failed <= MAX_CREATE_SOFT_LINK_WARNINGS) {
+                               WARNING("Can't set reparse data on \"%ls\": 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);
+                       }
+                       if (args->num_hard_links_failed == MAX_CREATE_HARD_LINK_WARNINGS) {
+                               WARNING("Suppressing further warnings regarding failure to extract\n"
+                                       "          reparse points due to insufficient privileges...");
+                       }
+               } else {
+                       ERROR("Failed to set reparse data on \"%ls\"", path);
+                       win32_error(err);
+                       if (inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
+                           inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT)
+                               return WIMLIB_ERR_LINK;
+                       else
+                               return WIMLIB_ERR_WRITE;
+               }
        }
        return 0;
 }
@@ -1699,7 +2013,7 @@ win32_encrypted_import_cb(unsigned char *data, void *_ctx,
 
        len = min(len, wim_resource_size(lte) - ctx->offset);
 
-       if (read_partial_wim_resource_into_buf(lte, len, ctx->offset, data, false))
+       if (read_partial_wim_resource_into_buf(lte, len, ctx->offset, data))
                return ERROR_READ_FAULT;
 
        ctx->offset += len;
@@ -1941,13 +2255,15 @@ win32_begin_extract_unnamed_stream(const struct wim_inode *inode,
 /* Set security descriptor and extract stream data or reparse data (skip the
  * unnamed data stream of encrypted files, which was already extracted). */
 static int
-win32_finish_extract_stream(HANDLE h, const struct wim_inode *inode,
+win32_finish_extract_stream(HANDLE h, const struct wim_dentry *dentry,
                            const struct wim_lookup_table_entry *lte,
                            const wchar_t *stream_path,
                            const wchar_t *stream_name_utf16,
                            struct apply_args *args)
 {
        int ret = 0;
+       const struct wim_inode *inode = dentry->d_inode;
+       const wchar_t *short_name;
        if (stream_name_utf16 == NULL) {
                /* Unnamed stream. */
 
@@ -1998,6 +2314,21 @@ win32_finish_extract_stream(HANDLE h, const struct wim_inode *inode,
                         * the stream is encrypted and therefore was already
                         * extracted as a special case. */
                        ret = do_win32_extract_stream(h, lte);
+                       if (ret)
+                               return ret;
+               }
+
+               if (dentry_has_short_name(dentry))
+                       short_name = dentry->short_name;
+               else
+                       short_name = L"";
+               /* Set short name */
+               if (!SetFileShortNameW(h, short_name)) {
+               #if 0
+                       DWORD err = GetLastError();
+                       ERROR("Could not set short name on \"%ls\"", stream_path);
+                       win32_error(err);
+               #endif
                }
        } else {
                /* Extract the data for a named data stream. */
@@ -2038,7 +2369,7 @@ win32_decrypt_file(HANDLE open_handle, const wchar_t *path)
  * This handles reparse points, directories, alternate data streams, encrypted
  * files, compressed files, etc.
  *
- * @inode: WIM inode containing the stream.
+ * @dentry: WIM dentry for the file or directory being extracted.
  *
  * @path:  Path to extract the file to.
  *
@@ -2056,7 +2387,7 @@ win32_decrypt_file(HANDLE open_handle, const wchar_t *path)
  * Returns 0 on success; nonzero on failure.
  */
 static int
-win32_extract_stream(const struct wim_inode *inode,
+win32_extract_stream(const struct wim_dentry *dentry,
                     const wchar_t *path,
                     const wchar_t *stream_name_utf16,
                     struct wim_lookup_table_entry *lte,
@@ -2070,6 +2401,7 @@ win32_extract_stream(const struct wim_inode *inode,
        DWORD requestedAccess;
        BY_HANDLE_FILE_INFORMATION file_info;
        unsigned remaining_sharing_violations = 1000;
+       const struct wim_inode *inode = dentry->d_inode;
 
        if (stream_name_utf16) {
                /* Named stream.  Create a buffer that contains the UTF-16LE
@@ -2113,7 +2445,8 @@ win32_extract_stream(const struct wim_inode *inode,
        }
 
        DEBUG("Opening \"%ls\"", stream_path);
-       requestedAccess = GENERIC_READ | GENERIC_WRITE |
+       /* DELETE access is needed for SetFileShortNameW(), for some reason. */
+       requestedAccess = GENERIC_READ | GENERIC_WRITE | DELETE |
                          ACCESS_SYSTEM_SECURITY;
 try_open_again:
        /* Open the stream to be extracted.  Depending on what we have set
@@ -2221,7 +2554,7 @@ try_open_again:
         * descriptor and actually extract the stream data (other than for
         * extracted files, which were already extracted).
         * win32_finish_extract_stream() handles these additional steps. */
-       ret = win32_finish_extract_stream(h, inode, lte, stream_path,
+       ret = win32_finish_extract_stream(h, dentry, lte, stream_path,
                                          stream_name_utf16, args);
        if (ret)
                goto fail_close_handle;
@@ -2250,23 +2583,24 @@ out:
  * (unnamed data stream and/or reparse point stream, plus any alternate data
  * streams).  Handles sparse, compressed, and/or encrypted files.
  *
- * @inode:     WIM inode for this file or directory.
+ * @dentry:    WIM dentry for this file or directory.
  * @path:      UTF-16LE external path to extract the inode to.
  * @args:      Additional extraction context.
  *
  * Returns 0 on success; nonzero on failure.
  */
 static int
-win32_extract_streams(const struct wim_inode *inode,
+win32_extract_streams(const struct wim_dentry *dentry,
                      const wchar_t *path, struct apply_args *args)
 {
        struct wim_lookup_table_entry *unnamed_lte;
        int ret;
+       const struct wim_inode *inode = dentry->d_inode;
 
        /* First extract the unnamed stream. */
 
        unnamed_lte = inode_unnamed_lte_resolved(inode);
-       ret = win32_extract_stream(inode, path, NULL, unnamed_lte, args);
+       ret = win32_extract_stream(dentry, path, NULL, unnamed_lte, args);
        if (ret)
                goto out;
 
@@ -2283,7 +2617,7 @@ win32_extract_streams(const struct wim_inode *inode,
                        continue;
 
                /* Skip special UNIX data entries (see documentation for
-                * WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA) */
+                * WIMLIB_ADD_FLAG_UNIX_DATA) */
                if (ads_entry->stream_name_nbytes == WIMLIB_UNIX_DATA_TAG_UTF16LE_NBYTES
                    && !memcmp(ads_entry->stream_name,
                               WIMLIB_UNIX_DATA_TAG_UTF16LE,
@@ -2291,7 +2625,7 @@ win32_extract_streams(const struct wim_inode *inode,
                        continue;
 
                /* Extract the named stream */
-               ret = win32_extract_stream(inode,
+               ret = win32_extract_stream(dentry,
                                           path,
                                           ads_entry->stream_name,
                                           ads_entry->lte,
@@ -2390,12 +2724,13 @@ win32_try_hard_link(const wchar_t *output_path, const struct wim_inode *inode,
                return WIMLIB_ERR_LINK;
        } else {
                args->num_hard_links_failed++;
-               if (args->num_hard_links_failed < MAX_CREATE_HARD_LINK_WARNINGS) {
+               if (args->num_hard_links_failed <= MAX_CREATE_HARD_LINK_WARNINGS) {
                        WARNING("Can't create hard link \"%ls => %ls\":\n"
                                "          Volume does not support hard links!\n"
                                "          Falling back to extracting a copy of the file.",
                                output_path, inode->i_extracted_file);
-               } else if (args->num_hard_links_failed == MAX_CREATE_HARD_LINK_WARNINGS) {
+               }
+               if (args->num_hard_links_failed == MAX_CREATE_HARD_LINK_WARNINGS) {
                        WARNING("Suppressing further hard linking warnings...");
                }
                return -1;
@@ -2438,7 +2773,7 @@ win32_do_apply_dentry(const wchar_t *output_path,
        } else {
                /* Create the file, directory, or reparse point, and extract the
                 * data streams. */
-               ret = win32_extract_streams(inode, output_path, args);
+               ret = win32_extract_streams(dentry, output_path, args);
                if (ret)
                        return ret;
        }
@@ -2470,8 +2805,8 @@ win32_do_apply_dentry(const wchar_t *output_path,
 int
 win32_do_apply_dentry_timestamps(const wchar_t *path,
                                 size_t path_num_chars,
-                                const struct wim_dentry *dentry,
-                                const struct apply_args *args)
+                                struct wim_dentry *dentry,
+                                struct apply_args *args)
 {
        DWORD err;
        HANDLE h;
@@ -2528,25 +2863,18 @@ out:
 int
 fsync(int fd)
 {
-       DWORD err;
        HANDLE h;
 
        h = (HANDLE)_get_osfhandle(fd);
-       if (h == INVALID_HANDLE_VALUE) {
-               err = GetLastError();
-               ERROR("Could not get Windows handle for file descriptor");
-               win32_error(err);
-               errno = EBADF;
-               return -1;
-       }
-       if (!FlushFileBuffers(h)) {
-               err = GetLastError();
-               ERROR("Could not flush file buffers to disk");
-               win32_error(err);
-               errno = EIO;
-               return -1;
-       }
+       if (h == INVALID_HANDLE_VALUE)
+               goto err;
+       if (!FlushFileBuffers(h))
+               goto err_set_errno;
        return 0;
+err_set_errno:
+       set_errno_from_GetLastError();
+err:
+       return -1;
 }
 
 /* Use the Win32 API to get the number of processors */
@@ -2565,8 +2893,8 @@ wchar_t *
 realpath(const wchar_t *path, wchar_t *resolved_path)
 {
        DWORD ret;
-       wimlib_assert(resolved_path == NULL);
        DWORD err;
+       wimlib_assert(resolved_path == NULL);
 
        ret = GetFullPathNameW(path, 0, NULL, NULL);
        if (!ret) {
@@ -2586,8 +2914,7 @@ realpath(const wchar_t *path, wchar_t *resolved_path)
        }
        goto out;
 fail_win32:
-       win32_error(err);
-       errno = -1;
+       errno = win32_error_to_errno(err);
 out:
        return resolved_path;
 }
@@ -2600,12 +2927,7 @@ win32_rename_replacement(const wchar_t *oldpath, const wchar_t *newpath)
        if (MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING)) {
                return 0;
        } else {
-               /* As usual, the possible error values are not documented */
-               DWORD err = GetLastError();
-               ERROR("MoveFileEx(): Can't rename \"%ls\" to \"%ls\"",
-                     oldpath, newpath);
-               win32_error(err);
-               errno = -1;
+               set_errno_from_GetLastError();
                return -1;
        }
 }
@@ -2646,9 +2968,7 @@ fail_close_handle:
 fail:
        if (err == NO_ERROR)
                err = GetLastError();
-       ERROR("Can't truncate \"%ls\" to %"PRIu64" bytes", path, size);
-       win32_error(err);
-       errno = -1;
+       errno = win32_error_to_errno(err);
        return -1;
 }
 
@@ -2668,4 +2988,93 @@ win32_strerror_r_replacement(int errnum, wchar_t *buf, size_t buflen)
        return 0;
 }
 
+static int
+do_pread_or_pwrite(int fd, void *buf, size_t count, off_t offset,
+                  bool is_pwrite)
+{
+       HANDLE h;
+       LARGE_INTEGER orig_offset;
+       DWORD bytes_read_or_written;
+       LARGE_INTEGER relative_offset;
+       OVERLAPPED overlapped;
+       BOOL bret;
+
+       wimlib_assert(count <= 0xffffffff);
+
+       h = (HANDLE)_get_osfhandle(fd);
+       if (h == INVALID_HANDLE_VALUE)
+               goto err;
+
+       /* Get original position */
+       relative_offset.QuadPart = 0;
+       if (!SetFilePointerEx(h, relative_offset, &orig_offset, FILE_CURRENT))
+               goto err_set_errno;
+
+       memset(&overlapped, 0, sizeof(overlapped));
+       overlapped.Offset = offset;
+       overlapped.OffsetHigh = offset >> 32;
+
+       /* Do the read or write at the specified offset */
+       if (is_pwrite)
+               bret = WriteFile(h, buf, count, &bytes_read_or_written, &overlapped);
+       else
+               bret = ReadFile(h, buf, count, &bytes_read_or_written, &overlapped);
+       if (!bret)
+               goto err_set_errno;
+
+       /* Restore the original position */
+       if (!SetFilePointerEx(h, orig_offset, NULL, FILE_BEGIN))
+               goto err_set_errno;
+
+       return bytes_read_or_written;
+err_set_errno:
+       set_errno_from_GetLastError();
+err:
+       return -1;
+}
+
+/* Dumb Windows implementation of pread().  It temporarily changes the file
+ * offset, so it is not safe to use with readers/writers on the same file
+ * descriptor.  */
+extern ssize_t
+win32_pread(int fd, void *buf, size_t count, off_t offset)
+{
+       return do_pread_or_pwrite(fd, buf, count, offset, false);
+}
+
+/* Dumb Windows implementation of pwrite().  It temporarily changes the file
+ * offset, so it is not safe to use with readers/writers on the same file
+ * descriptor. */
+extern ssize_t
+win32_pwrite(int fd, const void *buf, size_t count, off_t offset)
+{
+       return do_pread_or_pwrite(fd, (void*)buf, count, offset, true);
+}
+
+/* Dumb Windows implementation of writev().  It writes the vectors one at a
+ * time. */
+extern ssize_t
+win32_writev(int fd, const struct iovec *iov, int iovcnt)
+{
+       ssize_t total_bytes_written = 0;
+
+       if (iovcnt <= 0) {
+               errno = EINVAL;
+               return -1;
+       }
+       for (int i = 0; i < iovcnt; i++) {
+               ssize_t bytes_written;
+
+               bytes_written = write(fd, iov[i].iov_base, iov[i].iov_len);
+               if (bytes_written >= 0)
+                       total_bytes_written += bytes_written;
+               if (bytes_written != iov[i].iov_len) {
+                       if (total_bytes_written == 0)
+                               total_bytes_written = -1;
+                       break;
+               }
+       }
+       return total_bytes_written;
+}
+
 #endif /* __WIN32__ */