/* Number of files for which we didn't have permission to set any part
* of the security descriptor. */
unsigned long no_security_descriptors;
+
+ /* Number of files for which we couldn't set the short name. */
+ unsigned long num_short_name_failures;
+
+ /* Have we tried to enable short name support on the target volume yet?
+ */
+ bool tried_to_enable_short_names;
};
/* Get the drive letter from a Windows path, or return the null character if the
/* Warning only. */
set_errno_from_win32_error(res);
WARNING_WITH_ERRNO("Failed to set \\Setup: dword \"WimBoot\"=1 value "
- "in registry hive \"%ls\" (err=0x%08"PRIu32")",
+ "in registry hive \"%ls\" (err=%"PRIu32")",
ctx->pathbuf.Buffer, (u32)res);
}
out:
return 0;
}
+/* Try to enable short name support on the target volume. If successful, return
+ * true. If unsuccessful, issue a warning and return false. */
+static bool
+try_to_enable_short_names(const wchar_t *volume)
+{
+ HANDLE h;
+ FILE_FS_PERSISTENT_VOLUME_INFORMATION info;
+ BOOL bret;
+ DWORD bytesReturned;
+
+ h = CreateFile(volume, GENERIC_WRITE,
+ FILE_SHARE_VALID_FLAGS, NULL, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ if (h == INVALID_HANDLE_VALUE)
+ goto fail;
+
+ info.VolumeFlags = 0;
+ info.FlagMask = PERSISTENT_VOLUME_STATE_SHORT_NAME_CREATION_DISABLED;
+ info.Version = 1;
+ info.Reserved = 0;
+
+ bret = DeviceIoControl(h, FSCTL_SET_PERSISTENT_VOLUME_STATE,
+ &info, sizeof(info), NULL, 0,
+ &bytesReturned, NULL);
+
+ CloseHandle(h);
+
+ if (!bret)
+ goto fail;
+ return true;
+
+fail:
+ WARNING("Failed to enable short name support on %ls "
+ "(err=%"PRIu32")", volume + 4, (u32)GetLastError());
+ return false;
+}
+
/* Set the short name on the open file @h which has been created at the location
* indicated by @dentry.
*
set_short_name(HANDLE h, const struct wim_dentry *dentry,
struct win32_apply_ctx *ctx)
{
+
+ if (!ctx->common.supported_features.short_names)
+ return 0;
+
+ /*
+ * Note: The size of the FILE_NAME_INFORMATION buffer must be such that
+ * FileName contains at least 2 wide characters (4 bytes). Otherwise,
+ * NtSetInformationFile() will return STATUS_INFO_LENGTH_MISMATCH. This
+ * is despite the fact that FileNameLength can validly be 0 or 2 bytes,
+ * with the former case being removing the existing short name if
+ * present, rather than setting one.
+ *
+ * FileName seemingly does not, however, need to be null-terminated in
+ * any case.
+ */
+
size_t bufsize = offsetof(FILE_NAME_INFORMATION, FileName) +
- dentry->short_name_nbytes;
+ max(dentry->short_name_nbytes, 2 * sizeof(wchar_t));
u8 buf[bufsize] _aligned_attribute(8);
FILE_NAME_INFORMATION *info = (FILE_NAME_INFORMATION *)buf;
NTSTATUS status;
info->FileNameLength = dentry->short_name_nbytes;
memcpy(info->FileName, dentry->short_name, dentry->short_name_nbytes);
+
+retry:
status = (*func_NtSetInformationFile)(h, &ctx->iosb, info, bufsize,
FileShortNameInformation);
if (NT_SUCCESS(status))
return 0;
+ if (status == STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME) {
+ if (dentry->short_name_nbytes == 0)
+ return 0;
+ if (!ctx->tried_to_enable_short_names) {
+ wchar_t volume[7];
+ int ret;
+
+ ctx->tried_to_enable_short_names = true;
+
+ ret = win32_get_drive_path(ctx->common.target,
+ volume);
+ if (ret)
+ return ret;
+ if (try_to_enable_short_names(volume))
+ goto retry;
+ }
+ }
+
/* By default, failure to set short names is not an error (since short
* names aren't too important anymore...). */
- if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES))
+ if (!(ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES)) {
+ ctx->num_short_name_failures++;
return 0;
+ }
if (status == STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME) {
- if (dentry->short_name_nbytes == 0)
- return 0;
- ERROR("Can't extract short name when short "
+ ERROR("Can't set short name when short "
"names are not enabled on the volume!");
} else {
ERROR("Can't set short name on \"%ls\" (status=0x%08"PRIx32")",
do_warnings(const struct win32_apply_ctx *ctx)
{
if (ctx->partial_security_descriptors == 0 &&
- ctx->no_security_descriptors == 0)
+ ctx->no_security_descriptors == 0 &&
+ ctx->num_short_name_failures == 0)
return;
WARNING("Extraction to \"%ls\" complete, but with one or more warnings:",
ctx->common.target);
- if (ctx->partial_security_descriptors != 0) {
+ if (ctx->num_short_name_failures) {
+ WARNING("- Could not set short names on %lu files or directories",
+ ctx->num_short_name_failures);
+ }
+ if (ctx->partial_security_descriptors) {
WARNING("- Could only partially set the security descriptor\n"
" on %lu files or directories.",
ctx->partial_security_descriptors);
}
- if (ctx->no_security_descriptors != 0) {
+ if (ctx->no_security_descriptors) {
WARNING("- Could not set security descriptor at all\n"
" on %lu files or directories.",
ctx->no_security_descriptors);
}
- WARNING("To fully restore all security descriptors, run the program\n"
- " with Administrator rights.");
+ if (ctx->partial_security_descriptors || ctx->no_security_descriptors) {
+ WARNING("To fully restore all security descriptors, run the program\n"
+ " with Administrator rights.");
+ }
}
/* Extract files from a WIM image to a directory on Windows */