mirror of
https://github.com/GerbilSoft/rom-properties.git
synced 2025-06-18 11:35:38 -04:00
[rp-download] SetFileOriginInfo_win32.cpp: Use NTDLL to write the Alternate Data Stream with just the file handle.
Note that we have to use NtWriteFile(). Using WriteFile() results in ERROR_BAD_COMMAND. If the ADS write fails, it will be deleted with NtDeleteFile(). NOTE: Unicode only! ANSI builds won't write the ADS anymore. ANSI builds (if they even work at all) should only be used on Win9x, which doesn't support ADS anyway. TODO: Convert NTSTATUS to POSIX error codes?
This commit is contained in:
parent
a19815f744
commit
f157be4a97
@ -110,6 +110,11 @@ TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME}
|
||||
IF(WIN32)
|
||||
TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE win32common rptext-noi18n)
|
||||
TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE wininet advapi32)
|
||||
# NTDLL is used to open Alternate Data Streams without requiring the original filename.
|
||||
# NOTE: Only works in Unicode builds. Non-Unicode builds would be for Win9x, which
|
||||
# doesn't support Alternate Data Streams, anyway.
|
||||
TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE ntdll)
|
||||
|
||||
# Delay-load shell32.dll and ole32.dll to prevent a performance penalty due to gdi32.dll.
|
||||
# Reference: https://randomascii.wordpress.com/2018/12/03/a-not-called-function-can-cause-a-5x-slowdown/
|
||||
# This is also needed when disabling direct Win32k syscalls,
|
||||
|
@ -2,7 +2,7 @@
|
||||
* ROM Properties Page shell extension. (rp-download) *
|
||||
* SetFileOriginInfo.hpp: setFileOriginInfo() function. *
|
||||
* *
|
||||
* Copyright (c) 2016-2023 by David Korth. *
|
||||
* Copyright (c) 2016-2025 by David Korth. *
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later *
|
||||
***************************************************************************/
|
||||
|
||||
@ -19,29 +19,14 @@
|
||||
|
||||
namespace RpDownload {
|
||||
|
||||
#ifndef _WIN32
|
||||
/**
|
||||
* Set the file origin info.
|
||||
* This uses xattrs on Linux and ADS on Windows.
|
||||
* @param file Open file. (Must be writable.)
|
||||
* @param url Origin URL.
|
||||
* @param file Open file (Must be writable)
|
||||
* @param url Origin URL
|
||||
* @param mtime If >= 0, this value is set as the mtime.
|
||||
* @return 0 on success; negative POSIX error code on error.
|
||||
*/
|
||||
int setFileOriginInfo(FILE *file, const TCHAR *url, time_t mtime);
|
||||
#endif /* !_WIN32 */
|
||||
|
||||
#ifdef _WIN32
|
||||
/**
|
||||
* Set the file origin info.
|
||||
* This uses xattrs on Linux and ADS on Windows.
|
||||
* @param file Open file. (Must be writable.)
|
||||
* @param filename Filename. [FIXME: Make it so we don't need this on Windows.]
|
||||
* @param url Origin URL.
|
||||
* @param mtime If >= 0, this value is set as the mtime.
|
||||
* @return 0 on success; negative POSIX error code on error.
|
||||
*/
|
||||
int setFileOriginInfo(FILE *file, const TCHAR *filename, const TCHAR *url, time_t mtime);
|
||||
#endif /* _WIN32 */
|
||||
|
||||
} //namespace RpDownload
|
||||
|
@ -2,7 +2,7 @@
|
||||
* ROM Properties Page shell extension. (rp-download) *
|
||||
* SetFileOriginInfo_win32.cpp: setFileOriginInfo() function. (Win32 version) *
|
||||
* *
|
||||
* Copyright (c) 2016-2024 by David Korth. *
|
||||
* Copyright (c) 2016-2025 by David Korth. *
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later *
|
||||
******************************************************************************/
|
||||
|
||||
@ -29,6 +29,39 @@
|
||||
using std::string;
|
||||
using std::tstring;
|
||||
|
||||
#ifdef UNICODE
|
||||
// NTDLL function calls
|
||||
#include <io.h>
|
||||
#include <winternl.h>
|
||||
|
||||
// FIXME: RTL_CONSTANT_STRING macro is in ntdef.h,
|
||||
// but #include'ing it breaks things.
|
||||
// This version is from MinGW-w64 12.0.0.
|
||||
#define RTL_CONSTANT_STRING(s) { sizeof(s)-sizeof((s)[0]), sizeof(s), const_cast<PWSTR>(s) }
|
||||
|
||||
// NTDLL functions not declared in winternl.h.
|
||||
extern "C" {
|
||||
|
||||
__kernel_entry NTSYSCALLAPI NTSTATUS NtWriteFile(
|
||||
HANDLE FileHandle,
|
||||
HANDLE Event,
|
||||
PIO_APC_ROUTINE ApcRoutine,
|
||||
PVOID ApcContext,
|
||||
PIO_STATUS_BLOCK IoStatusBlock,
|
||||
PVOID Buffer,
|
||||
ULONG Length,
|
||||
PLARGE_INTEGER ByteOffset,
|
||||
PULONG Key
|
||||
);
|
||||
|
||||
typedef const OBJECT_ATTRIBUTES *PCOBJECT_ATTRIBUTES;
|
||||
__kernel_entry NTSYSCALLAPI NTSTATUS NtDeleteFile(
|
||||
PCOBJECT_ATTRIBUTES ObjectAttributes
|
||||
);
|
||||
|
||||
}
|
||||
#endif /* UNICODE */
|
||||
|
||||
namespace RpDownload {
|
||||
|
||||
/**
|
||||
@ -75,13 +108,12 @@ static bool getStoreFileOriginInfo(void)
|
||||
/**
|
||||
* Set the file origin info.
|
||||
* This uses xattrs on Linux and ADS on Windows.
|
||||
* @param file Open file. (Must be writable.)
|
||||
* @param filename Filename. [FIXME: Make it so we don't need this on Windows.]
|
||||
* @param url Origin URL.
|
||||
* @param file Open file (Must be writable)
|
||||
* @param url Origin URL
|
||||
* @param mtime If >= 0, this value is set as the mtime.
|
||||
* @return 0 on success; negative POSIX error code on error.
|
||||
*/
|
||||
int setFileOriginInfo(FILE *file, const TCHAR *filename, const TCHAR *url, time_t mtime)
|
||||
int setFileOriginInfo(FILE *file, const TCHAR *url, time_t mtime)
|
||||
{
|
||||
// NOTE: Even if one of the xattr functions fails, we'll
|
||||
// continue with others and setting mtime. The first error
|
||||
@ -94,6 +126,7 @@ int setFileOriginInfo(FILE *file, const TCHAR *filename, const TCHAR *url, time_
|
||||
# error 32-bit time_t is not supported. Get a newer compiler.
|
||||
#endif
|
||||
|
||||
#ifdef UNICODE
|
||||
// Check if storeFileOriginInfo is enabled.
|
||||
const bool storeFileOriginInfo = getStoreFileOriginInfo();
|
||||
if (storeFileOriginInfo) do {
|
||||
@ -102,25 +135,34 @@ int setFileOriginInfo(FILE *file, const TCHAR *filename, const TCHAR *url, time_
|
||||
// - https://cqureacademy.com/blog/alternate-data-streams
|
||||
// - https://stackoverflow.com/questions/46141321/open-alternate-data-stream-ads-from-file-handle-or-file-id
|
||||
// - https://stackoverflow.com/a/46141949
|
||||
// FIXME: NtCreateFile() seems to have issues, and we end up
|
||||
// getting STATUS_INVALID_PARAMETER (0xC000000D).
|
||||
// We'll use a regular CreateFile() call for now.
|
||||
tstring tfilename = filename;
|
||||
tfilename += _T(":Zone.Identifier");
|
||||
HANDLE hAds = CreateFile(
|
||||
tfilename.c_str(), // lpFileName
|
||||
GENERIC_WRITE, // dwDesiredAccess
|
||||
FILE_SHARE_READ, // dwShareMode
|
||||
nullptr, // lpSecurityAttributes
|
||||
CREATE_ALWAYS, // dwCreationDisposition
|
||||
FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes
|
||||
nullptr); // hTemplateFile
|
||||
if (!hAds || hAds == INVALID_HANDLE_VALUE) {
|
||||
HANDLE hFile = reinterpret_cast<HANDLE>(_get_osfhandle(fileno(file)));
|
||||
IO_STATUS_BLOCK iosb;
|
||||
UNICODE_STRING ObjectName = RTL_CONSTANT_STRING(L":Zone.Identifier");
|
||||
|
||||
OBJECT_ATTRIBUTES oa;
|
||||
InitializeObjectAttributes(&oa, &ObjectName, 0, hFile, nullptr);
|
||||
|
||||
HANDLE hAds = nullptr;
|
||||
NTSTATUS status = NtCreateFile(
|
||||
&hAds, // FileHandle
|
||||
FILE_GENERIC_WRITE, // DesiredAccess
|
||||
&oa, // ObjectAttributes
|
||||
&iosb, // IoStatusBlock
|
||||
nullptr, // AllocationSize
|
||||
FILE_ATTRIBUTE_NORMAL, // FileAttributes
|
||||
FILE_SHARE_READ, // ShareAccess
|
||||
FILE_OVERWRITE_IF, // CreateDisposition
|
||||
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, // CreateOptions
|
||||
nullptr, // EaBuffer
|
||||
0); // EaLength
|
||||
if (status < 0) {
|
||||
// Error opening the ADS.
|
||||
err = w32err_to_posix(GetLastError());
|
||||
// TODO: Convert NTSTATUS to POSIX?
|
||||
/*err = w32err_to_posix(GetLastError());
|
||||
if (err == 0) {
|
||||
err = EIO;
|
||||
}
|
||||
}*/
|
||||
err = EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -135,19 +177,28 @@ int setFileOriginInfo(FILE *file, const TCHAR *filename, const TCHAR *url, time_
|
||||
s_zoneID += T2U8(url);
|
||||
s_zoneID += "\r\n";
|
||||
|
||||
DWORD dwBytesWritten = 0;
|
||||
BOOL bRet = WriteFile(hAds, s_zoneID.data(), static_cast<DWORD>(s_zoneID.size()),
|
||||
&dwBytesWritten, nullptr);
|
||||
if ((!bRet || dwBytesWritten != static_cast<DWORD>(s_zoneID.size())) && err == 0) {
|
||||
// Some error occurred...
|
||||
err = w32err_to_posix(GetLastError());
|
||||
if (err == 0) {
|
||||
err = EIO;
|
||||
}
|
||||
}
|
||||
status = NtWriteFile(
|
||||
hAds, // FileHandle
|
||||
nullptr, // Event
|
||||
nullptr, // ApcRoutine
|
||||
nullptr, // ApcContext
|
||||
&iosb, // IoStatusBlock
|
||||
s_zoneID.data(), // Buffer
|
||||
static_cast<DWORD>(s_zoneID.size()), // Length
|
||||
nullptr, // ByteOffset
|
||||
nullptr); // Key
|
||||
NtClose(hAds);
|
||||
|
||||
CloseHandle(hAds);
|
||||
if (status < 0) {
|
||||
// Error writing the stream data.
|
||||
// Delete the stream.
|
||||
NtDeleteFile(&oa);
|
||||
|
||||
// TODO: Convert NTSTATUS to Win32?
|
||||
err = -EIO;
|
||||
}
|
||||
} while (0);
|
||||
#endif /* UNICODE */
|
||||
|
||||
if (mtime >= 0) {
|
||||
// TODO: 100ns timestamp precision for access time?
|
||||
|
@ -610,12 +610,7 @@ int RP_C_API _tmain(int argc, TCHAR *argv[])
|
||||
fflush(f_out);
|
||||
|
||||
// Save the file origin information.
|
||||
#ifdef _WIN32
|
||||
// TODO: Figure out how to setFileOriginInfo() on Windows using an open file handle.
|
||||
setFileOriginInfo(f_out, cache_filename.c_str(), full_url.c_str(), downloader.mtime());
|
||||
#else /* !_WIN32 */
|
||||
setFileOriginInfo(f_out, full_url.c_str(), downloader.mtime());
|
||||
#endif /* _WIN32 */
|
||||
fclose(f_out);
|
||||
|
||||
// Success.
|
||||
|
Loading…
Reference in New Issue
Block a user