+/*
+ * win32_apply.c - Windows-specific code for applying files from a WIM image.
+ */
+
+/*
+ * Copyright (C) 2013 Eric Biggers
+ *
+ * This file is part of wimlib, a library for working with WIM files.
+ *
+ * wimlib is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with wimlib; if not, see http://www.gnu.org/licenses/.
+ */
+
#ifdef __WIN32__
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
#include <aclapi.h> /* for SetSecurityInfo() */
-#include "win32_common.h"
-#include "wimlib_internal.h"
-#include "dentry.h"
-#include "lookup_table.h"
-#include "endianness.h"
+#include "wimlib/win32_common.h"
+
+#include "wimlib/apply.h"
+#include "wimlib/dentry.h"
+#include "wimlib/endianness.h"
+#include "wimlib/error.h"
+#include "wimlib/lookup_table.h"
+#include "wimlib/metadata.h"
+#include "wimlib/reparse.h"
+#include "wimlib/security.h"
#define MAX_CREATE_HARD_LINK_WARNINGS 5
#define MAX_CREATE_SOFT_LINK_WARNINGS 5
static int
win32_extract_try_rpfix(u8 *rpbuf,
+ u16 *rpbuflen_p,
const wchar_t *extract_root_realpath,
unsigned extract_root_realpath_nchars)
{
size_t new_print_name_nchars;
utf16lechar *p;
- ret = parse_reparse_data(rpbuf, 8 + le16_to_cpu(*(u16*)(rpbuf + 4)),
- &rpdata);
+ ret = parse_reparse_data(rpbuf, *rpbuflen_p, &rpdata);
if (ret)
return ret;
*p++ = extract_root_realpath[1];
}
/* Copy the rest of the extract root */
- wmemcpy(p, extract_root_realpath + 2, extract_root_realpath_nchars - 2);
- p += extract_root_realpath_nchars - 2;
+ p = wmempcpy(p, extract_root_realpath + 2, extract_root_realpath_nchars - 2);
/* Append the stripped target */
- wmemcpy(p, stripped_target, stripped_target_nchars);
- p += stripped_target_nchars;
+ p = wmempcpy(p, stripped_target, stripped_target_nchars);
new_target_nchars = p - new_target;
new_print_name_nchars = p - new_print_name;
rpdata.substitute_name_nbytes = new_target_nchars * sizeof(utf16lechar);
rpdata.print_name = new_print_name;
rpdata.print_name_nbytes = new_print_name_nchars * sizeof(utf16lechar);
- return make_reparse_buffer(&rpdata, rpbuf);
+ return make_reparse_buffer(&rpdata, rpbuf, rpbuflen_p);
}
/* Wrapper around the FSCTL_SET_REPARSE_POINT ioctl to set the reparse data on
struct apply_args *args)
{
int ret;
- u8 rpbuf[REPARSE_POINT_MAX_SIZE];
+ u8 rpbuf[REPARSE_POINT_MAX_SIZE] _aligned_attribute(8);
DWORD bytesReturned;
+ u16 rpbuflen;
DEBUG("Setting reparse data on \"%ls\"", path);
- ret = wim_inode_get_reparse_data(inode, rpbuf);
+ ret = wim_inode_get_reparse_data(inode, rpbuf, &rpbuflen);
if (ret)
return ret;
!inode->i_not_rpfixed)
{
ret = win32_extract_try_rpfix(rpbuf,
+ &rpbuflen,
args->target_realpath,
args->target_realpath_len);
if (ret)
* "Not used with this operation; set to NULL."
*/
if (!DeviceIoControl(h, FSCTL_SET_REPARSE_POINT, rpbuf,
- 8 + le16_to_cpu(*(u16*)(rpbuf + 4)),
+ rpbuflen,
NULL, 0,
&bytesReturned /* lpBytesReturned */,
NULL /* lpOverlapped */))
{
int ret = 0;
const struct wim_inode *inode = dentry->d_inode;
- const wchar_t *short_name;
if (stream_name_utf16 == NULL) {
/* Unnamed stream. */
}
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
- }
+ SetFileShortNameW(h, dentry->short_name);
+ else if (running_on_windows_7_or_later())
+ SetFileShortNameW(h, L"");
} else {
/* Extract the data for a named data stream. */
if (lte != NULL) {
return ret;
}
+static int
+dentry_clear_inode_visited(struct wim_dentry *dentry, void *_ignore)
+{
+ dentry->d_inode->i_visited = 0;
+ return 0;
+}
+
+static int
+dentry_get_features(struct wim_dentry *dentry, void *_features_p)
+{
+ DWORD features = 0;
+ DWORD *features_p = _features_p;
+ struct wim_inode *inode = dentry->d_inode;
+
+ if (inode->i_visited) {
+ features |= FILE_SUPPORTS_HARD_LINKS;
+ } else {
+ inode->i_visited = 1;
+ if (inode->i_attributes & FILE_ATTRIBUTE_SPARSE_FILE)
+ features |= FILE_SUPPORTS_SPARSE_FILES;
+ if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)
+ features |= FILE_SUPPORTS_REPARSE_POINTS;
+ for (unsigned i = 0; i < inode->i_num_ads; i++)
+ if (inode->i_ads_entries[i].stream_name_nbytes)
+ features |= FILE_NAMED_STREAMS;
+ if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED)
+ features |= FILE_SUPPORTS_ENCRYPTION;
+ if (inode->i_attributes & FILE_ATTRIBUTE_COMPRESSED)
+ features |= FILE_FILE_COMPRESSION;
+ if (inode->i_security_id != -1)
+ features |= FILE_PERSISTENT_ACLS;
+ }
+ *features_p |= features;
+ return 0;
+}
+
/* If not done already, load the supported feature flags for the volume onto
* which the image is being extracted, and warn the user about any missing
* features that could be important. */
static int
-win32_check_vol_flags(const wchar_t *output_path, struct apply_args *args)
+win32_check_vol_flags(const wchar_t *output_path,
+ struct wim_dentry *root, struct apply_args *args)
{
+ DWORD dentry_features = 0;
+ DWORD missing_features;
+
if (args->have_vol_flags)
return 0;
+ for_dentry_in_tree(root, dentry_clear_inode_visited, NULL);
+ for_dentry_in_tree(root, dentry_get_features, &dentry_features);
+
win32_get_vol_flags(output_path, &args->vol_flags);
args->have_vol_flags = true;
+
+ missing_features = dentry_features & ~args->vol_flags;
+
/* Warn the user about data that may not be extracted. */
- if (!(args->vol_flags & FILE_SUPPORTS_SPARSE_FILES))
+ if (missing_features & FILE_SUPPORTS_SPARSE_FILES)
WARNING("Volume does not support sparse files!\n"
" Sparse files will be extracted as non-sparse.");
- if (!(args->vol_flags & FILE_SUPPORTS_REPARSE_POINTS))
+ if (missing_features & FILE_SUPPORTS_REPARSE_POINTS)
WARNING("Volume does not support reparse points!\n"
" Reparse point data will not be extracted.");
- if (!(args->vol_flags & FILE_NAMED_STREAMS)) {
+ if (missing_features & FILE_NAMED_STREAMS) {
WARNING("Volume does not support named data streams!\n"
" Named data streams will not be extracted.");
}
- if (!(args->vol_flags & FILE_SUPPORTS_ENCRYPTION)) {
+ if (missing_features & FILE_SUPPORTS_ENCRYPTION) {
WARNING("Volume does not support encryption!\n"
" Encrypted files will be extracted as raw data.");
}
- if (!(args->vol_flags & FILE_FILE_COMPRESSION)) {
+ if (missing_features & FILE_FILE_COMPRESSION) {
WARNING("Volume does not support transparent compression!\n"
" Compressed files will be extracted as non-compressed.");
}
- if (!(args->vol_flags & FILE_PERSISTENT_ACLS)) {
+ if (missing_features & FILE_PERSISTENT_ACLS) {
if (args->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS) {
ERROR("Strict ACLs requested, but the volume does not "
"support ACLs!");
" File permissions will not be extracted.");
}
}
+ if (running_on_windows_7_or_later() &&
+ (missing_features & FILE_SUPPORTS_HARD_LINKS))
+ {
+ WARNING("Volume does not support hard links!\n"
+ " Hard links will be extracted as duplicate files.");
+ }
return 0;
}
DWORD err;
/* There is a volume flag for this (FILE_SUPPORTS_HARD_LINKS),
- * but it's only available on Windows 7 and later. So no use
- * even checking it, really. Instead, CreateHardLinkW() will
- * apparently return ERROR_INVALID_FUNCTION if the volume does
- * not support hard links. */
+ * but it's only available on Windows 7 and later.
+ *
+ * Otherwise, CreateHardLinkW() will apparently return
+ * ERROR_INVALID_FUNCTION if the volume does not support hard links. */
+
DEBUG("Creating hard link \"%ls => %ls\"",
output_path, inode->i_extracted_file);
+
+ if (running_on_windows_7_or_later() &&
+ !(args->vol_flags & FILE_SUPPORTS_HARD_LINKS))
+ goto hard_links_unsupported;
+
if (CreateHardLinkW(output_path, inode->i_extracted_file, NULL))
return 0;
output_path, inode->i_extracted_file);
win32_error(err);
return WIMLIB_ERR_LINK;
- } else {
- args->num_hard_links_failed++;
- if (args->num_hard_links_failed <= MAX_CREATE_HARD_LINK_WARNINGS) {
- WARNING("Can't create hard link \"%ls => %ls\":\n"
+ }
+hard_links_unsupported:
+ args->num_hard_links_failed++;
+ if (args->num_hard_links_failed <= MAX_CREATE_HARD_LINK_WARNINGS) {
+ if (running_on_windows_7_or_later())
+ {
+ WARNING("Extracting duplicate copy of \"%ls\" "
+ "rather than hard link", output_path);
+ } else {
+ 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);
}
- if (args->num_hard_links_failed == MAX_CREATE_HARD_LINK_WARNINGS) {
- WARNING("Suppressing further hard linking warnings...");
- }
- return -1;
}
+ if (args->num_hard_links_failed == MAX_CREATE_HARD_LINK_WARNINGS)
+ WARNING("Suppressing further hard linking warnings...");
+ return -1;
}
/* Extract a file, directory, reparse point, or hard link to an
int ret;
struct wim_inode *inode = dentry->d_inode;
- ret = win32_check_vol_flags(output_path, args);
+ ret = win32_check_vol_flags(output_path, dentry, args);
if (ret)
return ret;
if (inode->i_nlink > 1 && inode->i_extracted_file != NULL) {
if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT &&
!(args->vol_flags & FILE_SUPPORTS_REPARSE_POINTS))
{
- WARNING("Skipping extraction of reparse point \"%ls\":\n"
- " Not supported by destination filesystem",
- output_path);
+ WARNING("Not extracting reparse point \"%ls\"", output_path);
} else {
/* Create the file, directory, or reparse point, and extract the
* data streams. */