# include "config.h"
#endif
-#include <aclapi.h> /* for SetSecurityInfo() */
-
#include "wimlib/win32_common.h"
#include "wimlib/apply.h"
unsigned long n;
DWORD err;
const struct wim_security_data *sd;
+ SECURITY_INFORMATION securityInformation;
- SECURITY_INFORMATION securityInformation = 0;
-
- void *owner = NULL;
- void *group = NULL;
- ACL *dacl = NULL;
- ACL *sacl = NULL;
+ securityInformation = OWNER_SECURITY_INFORMATION |
+ GROUP_SECURITY_INFORMATION |
+ DACL_SECURITY_INFORMATION |
+ SACL_SECURITY_INFORMATION;
- BOOL owner_defaulted;
- BOOL group_defaulted;
- BOOL dacl_present;
- BOOL dacl_defaulted;
- BOOL sacl_present;
- BOOL sacl_defaulted;
-
- sd = wim_const_security_data(args->w);
+ sd = wim_const_security_data(args->wim);
descriptor = sd->descriptors[inode->i_security_id];
- GetSecurityDescriptorOwner(descriptor, &owner, &owner_defaulted);
- if (owner)
- securityInformation |= OWNER_SECURITY_INFORMATION;
-
- GetSecurityDescriptorGroup(descriptor, &group, &group_defaulted);
- if (group)
- securityInformation |= GROUP_SECURITY_INFORMATION;
-
- GetSecurityDescriptorDacl(descriptor, &dacl_present,
- &dacl, &dacl_defaulted);
- if (dacl)
- securityInformation |= DACL_SECURITY_INFORMATION;
-
- GetSecurityDescriptorSacl(descriptor, &sacl_present,
- &sacl, &sacl_defaulted);
- if (sacl)
- securityInformation |= SACL_SECURITY_INFORMATION;
-
again:
- if (securityInformation == 0)
- return 0;
- if (SetSecurityInfo(hFile, SE_FILE_OBJECT,
- securityInformation, owner, group, dacl, sacl))
+ /* We could use SetSecurityInfo() here if it wasn't horribly broken. */
+ if (SetFileSecurity(path, securityInformation, descriptor))
return 0;
err = GetLastError();
if (args->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS)
if (securityInformation & SACL_SECURITY_INFORMATION) {
n = args->num_set_sacl_priv_notheld++;
securityInformation &= ~SACL_SECURITY_INFORMATION;
- sacl = NULL;
if (n < MAX_SET_SACL_PRIV_NOTHELD_WARNINGS) {
WARNING(
"We don't have enough privileges to set the full security\n"
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)))
if (stream_name_utf16 == NULL) {
/* Unnamed stream. */
- /* Set security descriptor, unless the extract_flags indicate
- * not to or the volume does not supported it. Note that this
- * is only done when the unnamed stream is being extracted, as
- * security descriptors are per-file and not per-stream. */
- if (inode->i_security_id >= 0 &&
- !(args->extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS)
- && (args->vol_flags & FILE_PERSISTENT_ACLS))
- {
- ret = win32_set_security_data(inode, h, stream_path, args);
- if (ret)
- return ret;
- }
-
/* Handle reparse points. The data for them needs to be set
* using a special ioctl. Note that the reparse point may have
* been created using CreateFileW() in the case of
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"");
+
+ /* Set security descriptor, unless the extract_flags indicate
+ * not to or the volume does not supported it. Note that this
+ * is only done when the unnamed stream is being extracted, as
+ * security descriptors are per-file and not per-stream. */
+ if (inode->i_security_id >= 0 &&
+ !(args->extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS)
+ && (args->vol_flags & FILE_PERSISTENT_ACLS))
+ {
+ ret = win32_set_security_data(inode, h, stream_path, args);
+ if (ret)
+ return ret;
+ }
} else {
/* Extract the data for a named data stream. */
if (lte != NULL) {
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 |
- ACCESS_SYSTEM_SECURITY;
+ requestedAccess = GENERIC_READ | GENERIC_WRITE;
+ /* 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;
+
+ /* SetFileSecurity() opens its own handle so doesn't need the following
+ * access rights. */
+#if 0
+ /* If file has a security descriptor, set extra permissions needed for
+ * SetSecurityInfo() not implied by GENERIC_WRITE. */
+ if (inode->i_security_id != -1)
+ requestedAccess |= WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY;
+#endif
+
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);