# 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"
#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,
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)
}
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);
}
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)
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 {
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;
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.
memset(&args, 0, sizeof(args));
- args.w = wim;
+
+ args.wim = 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;
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
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,
}
}
#endif
+out_free_target_lowlevel_path:
+#ifdef __WIN32__
+ FREE(args.target_lowlevel_path);
+#endif
out:
return ret;
}
}
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)) {