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;
stripped_nchars = ret;
target = rpdata.substitute_name;
target_nchars = rpdata.substitute_name_nbytes / sizeof(utf16lechar);
- stripped_target = target + 6;
+ stripped_target = target + stripped_nchars;
stripped_target_nchars = target_nchars - stripped_nchars;
new_target = alloca((6 + extract_root_realpath_nchars +
p = new_target;
if (stripped_nchars == 6) {
/* Include \??\ prefix if it was present before */
- wmemcpy(p, L"\\??\\", 4);
- p += 4;
+ p = wmempcpy(p, L"\\??\\", 4);
}
/* Print name excludes the \??\ if present. */
*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;
!inode->i_not_rpfixed)
{
ret = win32_extract_try_rpfix(rpbuf,
+ &rpbuflen,
args->target_realpath,
args->target_realpath_len);
if (ret)
return ret;
}
-static bool
-path_is_root_of_drive(const wchar_t *path)
-{
- if (!*path)
- return false;
-
- if (*path != L'/' && *path != L'\\') {
- if (*(path + 1) == L':')
- path += 2;
- else
- return false;
- }
- while (*path == L'/' || *path == L'\\')
- path++;
- return (*path == L'\0');
-}
-
static inline DWORD
win32_mask_attributes(DWORD i_attributes)
{
/* Directories must be created with CreateDirectoryW(). Then the call
* to CreateFileW() will merely open the directory that was already
* created rather than creating a new file. */
- if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY &&
- !path_is_root_of_drive(path)) {
- if (!CreateDirectoryW(path, NULL)) {
- err = GetLastError();
- if (err != ERROR_ALREADY_EXISTS) {
- ERROR("Failed to create directory \"%ls\"",
- path);
- win32_error(err);
- return WIMLIB_ERR_MKDIR;
+ if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
+ if (!win32_path_is_root_of_drive(path)) {
+ if (!CreateDirectoryW(path, NULL)) {
+ err = GetLastError();
+ if (err != ERROR_ALREADY_EXISTS) {
+ ERROR("Failed to create directory \"%ls\"",
+ path);
+ win32_error(err);
+ return WIMLIB_ERR_MKDIR;
+ }
}
+ DEBUG("Created directory \"%ls\"", path);
}
- DEBUG("Created directory \"%ls\"", path);
*creationDisposition_ret = OPEN_EXISTING;
}
if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED &&
* directory, so treat that as a special case and do not set attributes.
* */
if (*creationDisposition_ret == OPEN_EXISTING &&
- !path_is_root_of_drive(path))
+ !win32_path_is_root_of_drive(path))
{
if (!SetFileAttributesW(path,
win32_mask_attributes(inode->i_attributes)))
return ret;
}
- if (dentry_has_short_name(dentry))
+ if (dentry_has_short_name(dentry) && !dentry->dos_name_invalid)
SetFileShortNameW(h, dentry->short_name);
else if (running_on_windows_7_or_later())
SetFileShortNameW(h, L"");
if (stream_name_utf16) {
/* Named stream. Create a buffer that contains the UTF-16LE
- * string [./]path:stream_name_utf16. This is needed to
+ * string [.\]path:stream_name_utf16. This is needed to
* create and open the stream using CreateFileW(). I'm not
* aware of any other APIs to do this. Note: the '$DATA' suffix
- * seems to be unneeded. Additional note: a "./" prefix needs
- * to be added when the path is not absolute to avoid ambiguity
- * with drive letters. */
+ * seems to be unneeded. Additional note: a ".\" prefix needs
+ * to be added when the path is a 1-character long relative path
+ * to avoid ambiguity with drive letters. */
size_t stream_path_nchars;
size_t path_nchars;
size_t stream_name_nchars;
path_nchars = wcslen(path);
stream_name_nchars = wcslen(stream_name_utf16);
stream_path_nchars = path_nchars + 1 + stream_name_nchars;
- if (path[0] != cpu_to_le16(L'\0') &&
- path[0] != cpu_to_le16(L'/') &&
- path[0] != cpu_to_le16(L'\\') &&
- path[1] != cpu_to_le16(L':'))
- {
- prefix = L"./";
+ if (path_nchars == 1 && !is_any_path_separator(path[0])) {
+ static const wchar_t _prefix[] =
+ {L'.', OS_PREFERRED_PATH_SEPARATOR, L'\0'};
+ prefix = _prefix;
stream_path_nchars += 2;
} else {
prefix = L"";
}
DEBUG("Opening \"%ls\"", stream_path);
- /* DELETE access is needed for SetFileShortNameW(), for some reason. */
- requestedAccess = GENERIC_READ | GENERIC_WRITE | DELETE |
+ requestedAccess = GENERIC_READ | GENERIC_WRITE |
ACCESS_SYSTEM_SECURITY;
+ /* DELETE access is needed for SetFileShortNameW(), for some reason.
+ * But don't request it for the extraction root, for the following
+ * reasons:
+ *
+ * - Requesting DELETE access on the extraction root will cause a
+ * sharing violation if the extraction root is the current working
+ * directory (".").
+ * - The extraction root may be extracted to a different name than given
+ * in the WIM file, in which case the DOS name, if given, would not be
+ * meaningful.
+ * - For full-image extractions, the root dentry is supposed to be
+ * unnamed anyway.
+ * - Microsoft's ImageX does not extract the root directory.
+ */
+ if (dentry != args->extract_root)
+ requestedAccess |= DELETE;
try_open_again:
/* Open the stream to be extracted. Depending on what we have set
* creationDisposition to, we may be creating this for the first time,
if (h == INVALID_HANDLE_VALUE) {
err = GetLastError();
if (err == ERROR_ACCESS_DENIED &&
- path_is_root_of_drive(stream_path))
+ win32_path_is_root_of_drive(stream_path))
{
ret = 0;
goto out;
requestedAccess &= ~ACCESS_SYSTEM_SECURITY;
goto try_open_again;
}
- if (err == ERROR_SHARING_VIOLATION) {
+ if (err == ERROR_SHARING_VIOLATION &&
+ (inode->i_attributes & (FILE_ATTRIBUTE_ENCRYPTED |
+ FILE_ATTRIBUTE_DIRECTORY)) ==
+ (FILE_ATTRIBUTE_ENCRYPTED | FILE_ATTRIBUTE_DIRECTORY))
+ {
if (remaining_sharing_violations) {
--remaining_sharing_violations;
/* This can happen when restoring encrypted directories
} else {
ERROR("Too many sharing violations; giving up...");
}
- } else {
- if (creationDisposition == OPEN_EXISTING)
- ERROR("Failed to open \"%ls\"", stream_path);
- else
- ERROR("Failed to create \"%ls\"", stream_path);
- win32_error(err);
}
+ if (creationDisposition == OPEN_EXISTING)
+ ERROR("Failed to open \"%ls\"", stream_path);
+ else
+ ERROR("Failed to create \"%ls\"", stream_path);
+ win32_error(err);
ret = WIMLIB_ERR_OPEN;
goto fail;
}
!(args->vol_flags & FILE_SUPPORTS_REPARSE_POINTS))
{
WARNING("Not extracting reparse point \"%ls\"", output_path);
+ dentry->not_extracted = 1;
} else {
/* Create the file, directory, or reparse point, and extract the
* data streams. */
/* Save extracted path for a later call to
* CreateHardLinkW() if this inode has multiple links.
* */
- inode->i_extracted_file = WSTRDUP(output_path);
+ inode->i_extracted_file = WCSDUP(output_path);
if (!inode->i_extracted_file)
return WIMLIB_ERR_NOMEM;
}
HANDLE h;
const struct wim_inode *inode = dentry->d_inode;
- if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT &&
- !(args->vol_flags & FILE_SUPPORTS_REPARSE_POINTS))
- {
- /* Skip reparse points not extracted */
- return 0;
- }
-
/* Windows doesn't let you change the timestamps of the root directory
* (at least on FAT, which is dumb but expected since FAT doesn't store
* any metadata about the root directory...) */
- if (path_is_root_of_drive(path))
+ if (win32_path_is_root_of_drive(path))
return 0;
DEBUG("Opening \"%ls\" to set timestamps", path);