]> wimlib.net Git - wimlib/blobdiff - src/extract.c
dentry.{c,h}: Cleanup and fixes
[wimlib] / src / extract.c
index 5c6c38a013ba8ee542aca292dd3a27d88bfd3cea..d6cc9c45d75ee854382a6c9c4d4fcffc7a02bcd0 100644 (file)
 #  include "config.h"
 #endif
 
+#ifdef __WIN32__
+#  include "wimlib/win32_common.h" /* For GetFullPathName() */
+#endif
+
 #include "wimlib/apply.h"
 #include "wimlib/dentry.h"
 #include "wimlib/encoding.h"
@@ -51,6 +55,8 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#define MAX_EXTRACT_LONG_PATH_WARNINGS 5
+
 static int
 do_apply_op(struct wim_dentry *dentry, struct apply_args *args,
            int (*apply_dentry_func)(const tchar *, size_t,
@@ -60,8 +66,21 @@ do_apply_op(struct wim_dentry *dentry, struct apply_args *args,
        size_t extraction_path_nchars;
        struct wim_dentry *d;
        LIST_HEAD(ancestor_list);
+       const tchar *target;
+       size_t target_nchars;
 
-       extraction_path_nchars = args->target_nchars;
+#ifdef __WIN32__
+       if (args->target_lowlevel_path) {
+               target = args->target_lowlevel_path;
+               target_nchars = args->target_lowlevel_path_nchars;
+       } else
+#endif
+       {
+               target = args->target;
+               target_nchars = args->target_nchars;
+       }
+
+       extraction_path_nchars = target_nchars;
 
        for (d = dentry; d != args->extract_root; d = d->parent) {
                if (d->not_extracted)
@@ -71,13 +90,30 @@ do_apply_op(struct wim_dentry *dentry, struct apply_args *args,
        }
 
        tchar extraction_path[extraction_path_nchars + 1];
-       p = tmempcpy(extraction_path, args->target, args->target_nchars);
+       p = tmempcpy(extraction_path, target, target_nchars);
+
 
        list_for_each_entry(d, &ancestor_list, tmp_list) {
                *p++ = OS_PREFERRED_PATH_SEPARATOR;
                p = tmempcpy(p, d->extraction_name, d->extraction_name_nchars);
        }
        *p = T('\0');
+
+#ifdef __WIN32__
+       /* Warn the user if the path exceeds MAX_PATH */
+
+       /* + 1 for '\0', -4 for \\?\.  */
+       if (extraction_path_nchars + 1 - 4 > MAX_PATH) {
+               if (dentry->needs_extraction &&
+                   args->num_long_paths < MAX_EXTRACT_LONG_PATH_WARNINGS)
+               {
+                       WARNING("Path \"%ls\" exceeds MAX_PATH and will not be accessible "
+                               "to most Windows software", extraction_path);
+                       if (++args->num_long_paths == MAX_EXTRACT_LONG_PATH_WARNINGS)
+                               WARNING("Suppressing further warnings about long paths");
+               }
+       }
+#endif
        return (*apply_dentry_func)(extraction_path, extraction_path_nchars,
                                    dentry, args);
 }
@@ -258,8 +294,11 @@ dentry_resolve_and_zero_lte_refcnt(struct wim_dentry *dentry, void *_lookup_tabl
        struct wim_inode *inode = dentry->d_inode;
        struct wim_lookup_table *lookup_table = _lookup_table;
        struct wim_lookup_table_entry *lte;
+       int ret;
 
-       inode_resolve_ltes(inode, lookup_table);
+       ret = inode_resolve_ltes(inode, lookup_table);
+       if (ret)
+               return ret;
        for (unsigned i = 0; i <= inode->i_num_ads; i++) {
                lte = inode_stream_lte_resolved(inode, i);
                if (lte)
@@ -268,19 +307,23 @@ dentry_resolve_and_zero_lte_refcnt(struct wim_dentry *dentry, void *_lookup_tabl
        return 0;
 }
 
-static void
+static int
 find_streams_for_extraction(struct wim_dentry *root,
                            struct list_head *stream_list,
                            struct wim_lookup_table *lookup_table,
                            int extract_flags)
 {
        struct find_streams_ctx ctx;
+       int ret;
 
        INIT_LIST_HEAD(&ctx.stream_list);
        ctx.extract_flags = extract_flags;
-       for_dentry_in_tree(root, dentry_resolve_and_zero_lte_refcnt, lookup_table);
+       ret = for_dentry_in_tree(root, dentry_resolve_and_zero_lte_refcnt, lookup_table);
+       if (ret)
+               return ret;
        for_dentry_in_tree(root, dentry_find_streams_to_extract, &ctx);
        list_transfer(&ctx.stream_list, stream_list);
+       return 0;
 }
 
 struct apply_operations {
@@ -396,9 +439,11 @@ static int
 extract_dentry_to_stdout(struct wim_dentry *dentry)
 {
        int ret = 0;
-       if (!dentry_is_regular_file(dentry)) {
+       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);
+                     "extracted to standard output", dentry_full_path(dentry));
                ret = WIMLIB_ERR_NOT_A_REGULAR_FILE;
        } else {
                struct wim_lookup_table_entry *lte;
@@ -446,6 +491,7 @@ file_name_valid(utf16lechar *name, size_t num_chars, bool fix)
                }
        }
 
+#ifdef __WIN32__
        if (name[num_chars - 1] == cpu_to_le16(' ') ||
            name[num_chars - 1] == cpu_to_le16('.'))
        {
@@ -454,6 +500,7 @@ file_name_valid(utf16lechar *name, size_t num_chars, bool fix)
                else
                        return false;
        }
+#endif
        return true;
 }
 
@@ -566,12 +613,14 @@ out_replace:
        #endif
                size_t fixed_name_num_chars = tchar_nchars;
                tchar fixed_name[tchar_nchars + 50];
-               size_t extraction_name_nbytes;
 
                tmemcpy(fixed_name, tchar_name, tchar_nchars);
                fixed_name_num_chars += tsprintf(fixed_name + tchar_nchars,
                                                 T(" (invalid filename #%lu)"),
                                                 ++args->invalid_sequence);
+       #ifndef __WIN32__
+               FREE(tchar_name);
+       #endif
                dentry->extraction_name = memdup(fixed_name, 2 * fixed_name_num_chars + 2);
                if (!dentry->extraction_name)
                        return WIMLIB_ERR_NOMEM;
@@ -587,19 +636,22 @@ skip_dentry:
 static int
 dentry_reset_needs_extraction(struct wim_dentry *dentry, void *_ignore)
 {
+       struct wim_inode *inode = dentry->d_inode;
+
        dentry->needs_extraction = 0;
        dentry->not_extracted = 0;
-       dentry->is_win32_name = 0;
-       dentry->d_inode->i_visited = 0;
-       dentry->d_inode->i_dos_name_extracted = 0;
-       FREE(dentry->d_inode->i_extracted_file);
-       dentry->d_inode->i_extracted_file = NULL;
+       inode->i_visited = 0;
+       inode->i_dos_name_extracted = 0;
+       FREE(inode->i_extracted_file);
+       inode->i_extracted_file = NULL;
        if ((void*)dentry->extraction_name != (void*)dentry->file_name)
                FREE(dentry->extraction_name);
        dentry->extraction_name = NULL;
        return 0;
 }
 
+#define WINDOWS_NT_MAX_PATH 32768
+
 /*
  * extract_tree - Extract a file or directory tree from the currently selected
  *               WIM image.
@@ -647,11 +699,38 @@ extract_tree(WIMStruct *wim, const tchar *wim_source_path, const tchar *target,
 
        memset(&args, 0, sizeof(args));
 
+
        args.w                      = wim;
        args.target                 = target;
+       args.target_nchars          = tstrlen(target);
        args.extract_flags          = extract_flags;
        args.progress_func          = progress_func;
-       args.target_nchars          = tstrlen(target);
+
+#ifdef __WIN32__
+       /* Work around defective behavior in Windows where paths longer than 260
+        * characters are not supported by default; instead they need to be
+        * turned into absolute paths and prefixed with "\\?\".  */
+       args.target_lowlevel_path = MALLOC(WINDOWS_NT_MAX_PATH * sizeof(wchar_t));
+       if (!args.target_lowlevel_path)
+       {
+               ret = WIMLIB_ERR_NOMEM;
+               goto out;
+       }
+       args.target_lowlevel_path_nchars =
+               GetFullPathName(args.target, WINDOWS_NT_MAX_PATH - 4,
+                               &args.target_lowlevel_path[4], NULL);
+
+       if (args.target_lowlevel_path_nchars == 0 ||
+           args.target_lowlevel_path_nchars >= WINDOWS_NT_MAX_PATH - 4)
+       {
+               WARNING("Can't get full path name for \"%ls\"", args.target);
+               FREE(args.target_lowlevel_path);
+               args.target_lowlevel_path = NULL;
+       } else {
+               wmemcpy(args.target_lowlevel_path, L"\\\\?\\", 4);
+               args.target_lowlevel_path_nchars += 4;
+       }
+#endif
 
        if (progress_func) {
                args.progress.extract.wimfile_name = wim->filename;
@@ -671,7 +750,7 @@ extract_tree(WIMStruct *wim, const tchar *wim_source_path, const tchar *target,
                        ERROR_WITH_ERRNO("Failed to mount NTFS volume `%"TS"'",
                                         target);
                        ret = WIMLIB_ERR_NTFS_3G;
-                       goto out;
+                       goto out_free_target_lowlevel_path;
                }
                ops = &ntfs_apply_operations;
        } else
@@ -695,9 +774,11 @@ extract_tree(WIMStruct *wim, const tchar *wim_source_path, const tchar *target,
                goto out_dentry_reset_needs_extraction;
 
        /* Build a list of the streams that need to be extracted */
-       find_streams_for_extraction(root,
-                                   &stream_list,
-                                   wim->lookup_table, extract_flags);
+       ret = find_streams_for_extraction(root,
+                                         &stream_list,
+                                         wim->lookup_table, extract_flags);
+       if (ret)
+               goto out_dentry_reset_needs_extraction;
 
        /* Calculate the number of bytes of data that will be extracted */
        calculate_bytes_to_extract(&stream_list, extract_flags,
@@ -789,6 +870,10 @@ out_ntfs_umount:
                }
        }
 #endif
+out_free_target_lowlevel_path:
+#ifdef __WIN32__
+       FREE(args.target_lowlevel_path);
+#endif
 out:
        return ret;
 }
@@ -1104,7 +1189,7 @@ extract_all_images(WIMStruct *wim,
        }
 
        tmemcpy(buf, target, output_path_len);
-       buf[output_path_len] = T('/');
+       buf[output_path_len] = OS_PREFERRED_PATH_SEPARATOR;
        for (image = 1; image <= wim->hdr.image_count; image++) {
                image_name = wimlib_get_image_name(wim, image);
                if (image_name_ok_as_dir(image_name)) {