Implement file renaming and relative path handling in NtQueryAttributesFile (#557)

This PR introduces several enhancements, mainly to the file system
syscalls:

- Implements file renaming via `NtSetInformationFile` by adding a
deferred rename mechanism to `file_handle`. The rename operation occurs
when the file handle is closed.
- Adds support for the `RootDirectory` parameter in
`NtQueryAttributesFile` to handle relative file paths.
- Adds support for opening the console output device (`\\??\\CONOUT$`).
- Stubs out the `NtLockVirtualMemory` syscall.
- Fixes an incorrect assertion in `socket_wrapper` that could fire on
`recvfrom` errors.
This commit is contained in:
Maurice Heumann
2025-10-20 15:08:37 +02:00
committed by GitHub
6 changed files with 64 additions and 5 deletions

View File

@@ -7,6 +7,12 @@ namespace utils
{
class file_handle
{
struct rename_information
{
std::filesystem::path old_filepath;
std::filesystem::path new_filepath;
};
public:
file_handle() = default;
@@ -80,8 +86,14 @@ namespace utils
return _ftelli64(this->file_);
}
void defer_rename(std::filesystem::path oldname, std::filesystem::path newname)
{
deferred_rename_ = {.old_filepath = std::move(oldname), .new_filepath = std::move(newname)};
}
private:
FILE* file_{};
std::optional<rename_information> deferred_rename_;
void release()
{
@@ -90,6 +102,13 @@ namespace utils
(void)fclose(this->file_);
this->file_ = {};
}
if (this->deferred_rename_)
{
std::error_code ec{};
std::filesystem::rename(this->deferred_rename_->old_filepath, this->deferred_rename_->new_filepath, ec);
this->deferred_rename_ = {};
}
}
};
}

View File

@@ -111,7 +111,7 @@ namespace network
const auto res = ::recvfrom(this->socket_.get_socket(), reinterpret_cast<char*>(data.data()), static_cast<send_size>(data.size()),
0, &source.get_addr(), &source_length);
assert(source.get_size() == source_length);
assert(res < 0 || source.get_size() == source_length);
return res;
}

View File

@@ -132,6 +132,7 @@ namespace syscalls
emulator_pointer buffer, ULONG number_of_bytes_to_read,
emulator_object<ULONG> number_of_bytes_read);
NTSTATUS handle_NtSetInformationVirtualMemory();
BOOL handle_NtLockVirtualMemory();
// syscalls/mutant.cpp:
NTSTATUS handle_NtReleaseMutant(const syscall_context& c, handle mutant_handle, emulator_object<LONG> previous_count);
@@ -961,6 +962,7 @@ void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& ha
add_handler(NtQuerySystemInformation);
add_handler(NtCreateEvent);
add_handler(NtProtectVirtualMemory);
add_handler(NtLockVirtualMemory);
add_handler(NtOpenDirectoryObject);
add_handler(NtTraceEvent);
add_handler(NtAllocateVirtualMemoryEx);

View File

@@ -51,7 +51,7 @@ namespace syscalls
const emulator_object<IO_STATUS_BLOCK<EmulatorTraits<Emu64>>> io_status_block,
const uint64_t file_information, const ULONG length, const FILE_INFORMATION_CLASS info_class)
{
const auto* f = c.proc.files.get(file_handle);
auto* f = c.proc.files.get(file_handle);
if (!f)
{
if (c.proc.devices.get(file_handle))
@@ -88,7 +88,22 @@ namespace syscalls
c.win_emu.log.warn("--> File rename requested: %s --> %s\n", u16_to_u8(f->name).c_str(), u16_to_u8(new_name).c_str());
return STATUS_ACCESS_DENIED;
std::error_code ec{};
bool file_exists = std::filesystem::exists(new_name, ec);
if (ec)
{
return STATUS_ACCESS_DENIED;
}
if (!info.ReplaceIfExists && file_exists)
{
return STATUS_OBJECT_NAME_EXISTS;
}
f->handle.defer_rename(c.win_emu.file_sys.translate(f->name), c.win_emu.file_sys.translate(new_name));
return STATUS_SUCCESS;
}
if (info_class == FileBasicInformation)
@@ -886,6 +901,12 @@ namespace syscalls
return STATUS_SUCCESS;
}
if (filename == u"\\??\\CONOUT$")
{
file_handle.write(STDOUT_HANDLE);
return STATUS_SUCCESS;
}
file f{};
f.name = std::move(filename);
@@ -1022,8 +1043,19 @@ namespace syscalls
return STATUS_INVALID_PARAMETER;
}
const auto filename =
read_unicode_string(c.emu, emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>>{c.emu, attributes.ObjectName});
auto filename = read_unicode_string(c.emu, emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>>{c.emu, attributes.ObjectName});
if (attributes.RootDirectory)
{
const auto* root = c.proc.files.get(attributes.RootDirectory);
if (!root)
{
return STATUS_INVALID_HANDLE;
}
const auto has_separator = root->name.ends_with(u"\\") || root->name.ends_with(u"/");
filename = root->name + (has_separator ? u"" : u"\\") + filename;
}
c.win_emu.callbacks.on_generic_access("Querying file attributes", filename);

View File

@@ -295,4 +295,9 @@ namespace syscalls
{
return STATUS_NOT_SUPPORTED;
}
BOOL handle_NtLockVirtualMemory()
{
return TRUE;
}
}

View File

@@ -186,6 +186,7 @@ struct file : ref_counted_object
utils::file_handle handle{};
std::u16string name{};
std::optional<file_enumeration_state> enumeration_state{};
std::optional<std::u16string> deferred_rename;
bool is_file() const
{