[rp-download] SetFileOriginInfo_win32.cpp: Use NTDLL to write the Alternate Data Stream with just the file handle.
Some checks are pending
Codecov / run (push) Waiting to run
CodeQL / Analyze (cpp) (push) Waiting to run

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:
David Korth 2025-06-12 23:33:56 -04:00
parent a19815f744
commit f157be4a97
4 changed files with 91 additions and 55 deletions

View File

@ -110,6 +110,11 @@ TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME}
IF(WIN32) IF(WIN32)
TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE win32common rptext-noi18n) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE win32common rptext-noi18n)
TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE wininet advapi32) 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. # 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/ # 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, # This is also needed when disabling direct Win32k syscalls,

View File

@ -2,7 +2,7 @@
* ROM Properties Page shell extension. (rp-download) * * ROM Properties Page shell extension. (rp-download) *
* SetFileOriginInfo.hpp: setFileOriginInfo() function. * * 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 * * SPDX-License-Identifier: GPL-2.0-or-later *
***************************************************************************/ ***************************************************************************/
@ -19,29 +19,14 @@
namespace RpDownload { namespace RpDownload {
#ifndef _WIN32
/** /**
* Set the file origin info. * Set the file origin info.
* This uses xattrs on Linux and ADS on Windows. * This uses xattrs on Linux and ADS on Windows.
* @param file Open file. (Must be writable.) * @param file Open file (Must be writable)
* @param url Origin URL. * @param url Origin URL
* @param mtime If >= 0, this value is set as the mtime. * @param mtime If >= 0, this value is set as the mtime.
* @return 0 on success; negative POSIX error code on error. * @return 0 on success; negative POSIX error code on error.
*/ */
int setFileOriginInfo(FILE *file, const TCHAR *url, time_t mtime); 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 } //namespace RpDownload

View File

@ -2,7 +2,7 @@
* ROM Properties Page shell extension. (rp-download) * * ROM Properties Page shell extension. (rp-download) *
* SetFileOriginInfo_win32.cpp: setFileOriginInfo() function. (Win32 version) * * 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 * * SPDX-License-Identifier: GPL-2.0-or-later *
******************************************************************************/ ******************************************************************************/
@ -29,6 +29,39 @@
using std::string; using std::string;
using std::tstring; 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 { namespace RpDownload {
/** /**
@ -75,13 +108,12 @@ static bool getStoreFileOriginInfo(void)
/** /**
* Set the file origin info. * Set the file origin info.
* This uses xattrs on Linux and ADS on Windows. * This uses xattrs on Linux and ADS on Windows.
* @param file Open file. (Must be writable.) * @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 url Origin URL.
* @param mtime If >= 0, this value is set as the mtime. * @param mtime If >= 0, this value is set as the mtime.
* @return 0 on success; negative POSIX error code on error. * @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 // NOTE: Even if one of the xattr functions fails, we'll
// continue with others and setting mtime. The first error // 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. # error 32-bit time_t is not supported. Get a newer compiler.
#endif #endif
#ifdef UNICODE
// Check if storeFileOriginInfo is enabled. // Check if storeFileOriginInfo is enabled.
const bool storeFileOriginInfo = getStoreFileOriginInfo(); const bool storeFileOriginInfo = getStoreFileOriginInfo();
if (storeFileOriginInfo) do { 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://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/questions/46141321/open-alternate-data-stream-ads-from-file-handle-or-file-id
// - https://stackoverflow.com/a/46141949 // - https://stackoverflow.com/a/46141949
// FIXME: NtCreateFile() seems to have issues, and we end up HANDLE hFile = reinterpret_cast<HANDLE>(_get_osfhandle(fileno(file)));
// getting STATUS_INVALID_PARAMETER (0xC000000D). IO_STATUS_BLOCK iosb;
// We'll use a regular CreateFile() call for now. UNICODE_STRING ObjectName = RTL_CONSTANT_STRING(L":Zone.Identifier");
tstring tfilename = filename;
tfilename += _T(":Zone.Identifier"); OBJECT_ATTRIBUTES oa;
HANDLE hAds = CreateFile( InitializeObjectAttributes(&oa, &ObjectName, 0, hFile, nullptr);
tfilename.c_str(), // lpFileName
GENERIC_WRITE, // dwDesiredAccess HANDLE hAds = nullptr;
FILE_SHARE_READ, // dwShareMode NTSTATUS status = NtCreateFile(
nullptr, // lpSecurityAttributes &hAds, // FileHandle
CREATE_ALWAYS, // dwCreationDisposition FILE_GENERIC_WRITE, // DesiredAccess
FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes &oa, // ObjectAttributes
nullptr); // hTemplateFile &iosb, // IoStatusBlock
if (!hAds || hAds == INVALID_HANDLE_VALUE) { 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. // Error opening the ADS.
err = w32err_to_posix(GetLastError()); // TODO: Convert NTSTATUS to POSIX?
/*err = w32err_to_posix(GetLastError());
if (err == 0) { if (err == 0) {
err = EIO; err = EIO;
} }*/
err = EIO;
break; break;
} }
@ -135,19 +177,28 @@ int setFileOriginInfo(FILE *file, const TCHAR *filename, const TCHAR *url, time_
s_zoneID += T2U8(url); s_zoneID += T2U8(url);
s_zoneID += "\r\n"; s_zoneID += "\r\n";
DWORD dwBytesWritten = 0; status = NtWriteFile(
BOOL bRet = WriteFile(hAds, s_zoneID.data(), static_cast<DWORD>(s_zoneID.size()), hAds, // FileHandle
&dwBytesWritten, nullptr); nullptr, // Event
if ((!bRet || dwBytesWritten != static_cast<DWORD>(s_zoneID.size())) && err == 0) { nullptr, // ApcRoutine
// Some error occurred... nullptr, // ApcContext
err = w32err_to_posix(GetLastError()); &iosb, // IoStatusBlock
if (err == 0) { s_zoneID.data(), // Buffer
err = EIO; 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); } while (0);
#endif /* UNICODE */
if (mtime >= 0) { if (mtime >= 0) {
// TODO: 100ns timestamp precision for access time? // TODO: 100ns timestamp precision for access time?

View File

@ -610,12 +610,7 @@ int RP_C_API _tmain(int argc, TCHAR *argv[])
fflush(f_out); fflush(f_out);
// Save the file origin information. // 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()); setFileOriginInfo(f_out, full_url.c_str(), downloader.mtime());
#endif /* _WIN32 */
fclose(f_out); fclose(f_out);
// Success. // Success.