X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fwin32_capture.c;h=afb38e30a7ce497a49d2656e770cdaa5c2217613;hp=e3072494b96718488cdc1f38f2cb3cee4ad0bb63;hb=2200ddb2ab85b390daa140de5338ac9f023d3683;hpb=5218b1d7c83cf9e98ed6276e099844ae0d80abc2 diff --git a/src/win32_capture.c b/src/win32_capture.c index e3072494..afb38e30 100644 --- a/src/win32_capture.c +++ b/src/win32_capture.c @@ -1,16 +1,49 @@ +/* + * win32_capture.c - Windows-specific code for capturing files into 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__ -#include "win32_common.h" -#include "wimlib_internal.h" -#include "lookup_table.h" -#include "security.h" -#include "endianness.h" +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "wimlib/win32_common.h" + +#include "wimlib/capture.h" +#include "wimlib/endianness.h" +#include "wimlib/error.h" +#include "wimlib/lookup_table.h" +#include "wimlib/paths.h" +#include "wimlib/reparse.h" #define MAX_GET_SD_ACCESS_DENIED_WARNINGS 1 #define MAX_GET_SACL_PRIV_NOTHELD_WARNINGS 1 +#define MAX_CAPTURE_LONG_PATH_WARNINGS 5 + struct win32_capture_state { unsigned long num_get_sd_access_denied; unsigned long num_get_sacl_priv_notheld; + unsigned long num_long_path_warnings; }; @@ -124,9 +157,10 @@ win32_encrypted_export_cb(unsigned char *_data, void *_ctx, unsigned long len) } } else { size_t len_to_copy = min(len, ctx->bytes_remaining); - memcpy(ctx->read_prefix_ctx_or_buf, data, len_to_copy); + ctx->read_prefix_ctx_or_buf = mempcpy(ctx->read_prefix_ctx_or_buf, + data, + len_to_copy); ctx->bytes_remaining -= len_to_copy; - ctx->read_prefix_ctx_or_buf += len_to_copy; } return ERROR_SUCCESS; } @@ -231,7 +265,7 @@ win32_get_short_name(struct wim_dentry *dentry, const wchar_t *path) static int win32_get_security_descriptor(struct wim_dentry *dentry, - struct sd_set *sd_set, + struct wim_sd_set *sd_set, const wchar_t *path, struct win32_capture_state *state, int add_flags) @@ -337,7 +371,7 @@ win32_recurse_directory(struct wim_dentry *root, * opendir(), FindFirstFileW has file globbing built into it. But this * isn't what we actually want, so just add a dummy glob to get all * entries. */ - dir_path[dir_path_num_chars] = L'/'; + dir_path[dir_path_num_chars] = OS_PREFERRED_PATH_SEPARATOR; dir_path[dir_path_num_chars + 1] = L'*'; dir_path[dir_path_num_chars + 2] = L'\0'; hFind = FindFirstFileW(dir_path, &dat); @@ -363,7 +397,7 @@ win32_recurse_directory(struct wim_dentry *root, continue; size_t filename_len = wcslen(dat.cFileName); - dir_path[dir_path_num_chars] = L'/'; + dir_path[dir_path_num_chars] = OS_PREFERRED_PATH_SEPARATOR; wmemcpy(dir_path + dir_path_num_chars + 1, dat.cFileName, filename_len + 1); @@ -394,40 +428,6 @@ out_find_close: return ret; } -int -win32_get_file_and_vol_ids(const wchar_t *path, u64 *ino_ret, u64 *dev_ret) -{ - HANDLE hFile; - DWORD err; - BY_HANDLE_FILE_INFORMATION file_info; - int ret; - - hFile = win32_open_existing_file(path, FILE_READ_ATTRIBUTES); - if (hFile == INVALID_HANDLE_VALUE) { - err = GetLastError(); - if (err != ERROR_FILE_NOT_FOUND) { - WARNING("Failed to open \"%ls\" to get file " - "and volume IDs", path); - win32_error(err); - } - return WIMLIB_ERR_OPEN; - } - - if (!GetFileInformationByHandle(hFile, &file_info)) { - err = GetLastError(); - ERROR("Failed to get file information for \"%ls\"", path); - win32_error(err); - ret = WIMLIB_ERR_STAT; - } else { - *ino_ret = ((u64)file_info.nFileIndexHigh << 32) | - (u64)file_info.nFileIndexLow; - *dev_ret = file_info.dwVolumeSerialNumber; - ret = 0; - } - CloseHandle(hFile); - return ret; -} - /* Reparse point fixup status code */ enum rp_status { /* Reparse point corresponded to an absolute symbolic link or junction @@ -510,12 +510,10 @@ win32_capture_try_rpfix(u8 *rpbuf, u16 *rpbuflen_p, const wchar_t *path) { struct reparse_data rpdata; - DWORD rpbuflen; int ret; enum rp_status rp_status; - rpbuflen = *rpbuflen_p; - ret = parse_reparse_data(rpbuf, rpbuflen, &rpdata); + ret = parse_reparse_data(rpbuf, *rpbuflen_p, &rpdata); if (ret) return -ret; @@ -523,7 +521,7 @@ win32_capture_try_rpfix(u8 *rpbuf, u16 *rpbuflen_p, &rpdata.substitute_name_nbytes, capture_root_ino, capture_root_dev, - le32_to_cpu(*(u32*)rpbuf)); + le32_to_cpu(*(le32*)rpbuf)); if (rp_status & RP_FIXED) { wimlib_assert(rpdata.substitute_name_nbytes % 2 == 0); utf16lechar substitute_name_copy[rpdata.substitute_name_nbytes / 2]; @@ -539,7 +537,7 @@ win32_capture_try_rpfix(u8 *rpbuf, u16 *rpbuflen_p, rpdata.print_name += 4; rpdata.print_name_nbytes -= 8; } - ret = make_reparse_buffer(&rpdata, rpbuf); + ret = make_reparse_buffer(&rpdata, rpbuf, rpbuflen_p); if (ret == 0) ret = rp_status; else @@ -616,7 +614,7 @@ win32_get_reparse_data(HANDLE hFile, const wchar_t *path, } rpbuflen = bytesReturned; - reparse_tag = le32_to_cpu(*(u32*)rpbuf); + reparse_tag = le32_to_cpu(*(le32*)rpbuf); if (params->add_flags & WIMLIB_ADD_FLAG_RPFIX && (reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK || reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT)) @@ -759,12 +757,11 @@ win32_capture_stream(const wchar_t *path, if (is_named_stream) { spath_nchars += 1 + stream_name_nchars; colonchar = L":"; - if (path_num_chars == 1 && - path[0] != L'/' && - path[0] != L'\\') - { + if (path_num_chars == 1 && !is_any_path_separator(path[0])) { spath_nchars += 2; - relpath_prefix = L"./"; + static const wchar_t _relpath_prefix[] = + {L'.', OS_PREFERRED_PATH_SEPARATOR, L'\0'}; + relpath_prefix = _relpath_prefix; } } @@ -954,6 +951,18 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret, goto out; } +#if 0 + if (path_num_chars >= 4 && + !wmemcmp(path, L"\\\\?\\", 4) && + path_num_chars + 1 - 4 > MAX_PATH && + state->num_long_path_warnings < MAX_CAPTURE_LONG_PATH_WARNINGS) + { + WARNING("Path \"%ls\" exceeds MAX_PATH", path); + if (++state->num_long_path_warnings == MAX_CAPTURE_LONG_PATH_WARNINGS) + WARNING("Suppressing further warnings about long paths."); + } +#endif + if ((params->add_flags & WIMLIB_ADD_FLAG_VERBOSE) && params->progress_func) { @@ -1007,7 +1016,7 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret, * only 1 link and refuse to hard link them. This is because Windows * has a bug where it can return duplicate File IDs for files and * directories on the FAT filesystem. */ - ret = inode_table_new_dentry(params->inode_table, + ret = inode_table_new_dentry(¶ms->inode_table, path_basename_with_len(path, path_num_chars), ((u64)file_info.nFileIndexHigh << 32) | (u64)file_info.nFileIndexLow, @@ -1038,7 +1047,7 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret, if (!(params->add_flags & WIMLIB_ADD_FLAG_NO_ACLS) && (vol_flags & FILE_PERSISTENT_ACLS)) { - ret = win32_get_security_descriptor(root, params->sd_set, + ret = win32_get_security_descriptor(root, ¶ms->sd_set, path, state, params->add_flags); if (ret) @@ -1065,7 +1074,7 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret, /* Reparse point: set the reparse data (which we read already) * */ inode->i_not_rpfixed = not_rpfixed; - inode->i_reparse_tag = le32_to_cpu(*(u32*)rpbuf); + inode->i_reparse_tag = le32_to_cpu(*(le32*)rpbuf); ret = inode_set_unnamed_stream(inode, rpbuf + 8, rpbuflen - 8, params->lookup_table); } else if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) { @@ -1115,11 +1124,12 @@ win32_do_capture_warnings(const struct win32_capture_state *state, " nothing more needs to be done%ls\n", (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" +" using the --no-acls option to explicitly capture no security\n" " descriptors.\n"); } +#define WINDOWS_NT_MAX_PATH 32768 + /* Win32 version of capturing a directory tree */ int win32_build_dentry_tree(struct wim_dentry **root_ret, @@ -1131,10 +1141,15 @@ win32_build_dentry_tree(struct wim_dentry **root_ret, int ret; struct win32_capture_state state; unsigned vol_flags; + DWORD dret; + if (!win32func_FindFirstStreamW) { + WARNING("Running on Windows XP or earlier; " + "alternate data streams will not be captured."); + } path_nchars = wcslen(root_disk_path); - if (path_nchars > 32767) + if (path_nchars > WINDOWS_NT_MAX_PATH) return WIMLIB_ERR_INVALID_PARAM; if (GetFileAttributesW(root_disk_path) == INVALID_FILE_ATTRIBUTES && @@ -1153,15 +1168,31 @@ win32_build_dentry_tree(struct wim_dentry **root_ret, win32_get_vol_flags(root_disk_path, &vol_flags); - /* There is no check for overflow later when this buffer is being used! - * But the max path length on NTFS is 32767 characters, and paths need - * to be written specially to even go past 260 characters, so we should - * be okay with 32770 characters. */ - path = MALLOC(32770 * sizeof(wchar_t)); + /* WARNING: There is no check for overflow later when this buffer is + * being used! But it's as long as the maximum path length understood + * by Windows NT (which is NOT the same as MAX_PATH). */ + path = MALLOC(WINDOWS_NT_MAX_PATH * sizeof(wchar_t)); if (!path) return WIMLIB_ERR_NOMEM; - wmemcpy(path, root_disk_path, path_nchars + 1); + /* 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 "\\?\". */ + + if (wcsncmp(root_disk_path, L"\\\\?\\", 4)) { + dret = GetFullPathName(root_disk_path, WINDOWS_NT_MAX_PATH - 4, + &path[4], NULL); + + if (dret == 0 || dret >= WINDOWS_NT_MAX_PATH - 4) { + WARNING("Can't get full path name for \"%ls\"", root_disk_path); + wmemcpy(path, root_disk_path, path_nchars + 1); + } else { + wmemcpy(path, L"\\\\?\\", 4); + path_nchars = 4 + dret; + } + } else { + wmemcpy(path, root_disk_path, path_nchars + 1); + } memset(&state, 0, sizeof(state)); ret = win32_build_dentry_tree_recursive(root_ret, path,