[Kernel] Added Support For: XFileRenameInformation
This commit is contained in:
@@ -64,6 +64,14 @@ struct X_FILE_INTERNAL_INFORMATION {
|
||||
};
|
||||
static_assert_size(X_FILE_INTERNAL_INFORMATION, 8);
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_file_rename_information
|
||||
struct X_FILE_RENAME_INFORMATION {
|
||||
be<uint32_t> replace_existing;
|
||||
be<uint32_t> root_dir_handle;
|
||||
X_ANSI_STRING ansi_string;
|
||||
};
|
||||
static_assert_size(X_FILE_RENAME_INFORMATION, 16);
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-_file_disposition_information
|
||||
struct X_FILE_DISPOSITION_INFORMATION {
|
||||
uint8_t delete_file;
|
||||
|
||||
@@ -39,62 +39,6 @@ struct CreateOptions {
|
||||
static const uint32_t FILE_RANDOM_ACCESS = 0x00000800;
|
||||
};
|
||||
|
||||
static bool IsValidPath(const std::string_view s, bool is_pattern) {
|
||||
// TODO(gibbed): validate path components individually
|
||||
bool got_asterisk = false;
|
||||
for (const auto& c : s) {
|
||||
if (c <= 31 || c >= 127) {
|
||||
return false;
|
||||
}
|
||||
if (got_asterisk) {
|
||||
// * must be followed by a . (*.)
|
||||
//
|
||||
// 4D530819 has a bug in its game code where it attempts to
|
||||
// FindFirstFile() with filters of "Game:\\*_X3.rkv", "Game:\\m*_X3.rkv",
|
||||
// and "Game:\\w*_X3.rkv" and will infinite loop if the path filter is
|
||||
// allowed.
|
||||
if (c != '.') {
|
||||
return false;
|
||||
}
|
||||
got_asterisk = false;
|
||||
}
|
||||
switch (c) {
|
||||
case '"':
|
||||
// case '*':
|
||||
case '+':
|
||||
case ',':
|
||||
// case ':':
|
||||
case ';':
|
||||
case '<':
|
||||
case '=':
|
||||
case '>':
|
||||
// case '?':
|
||||
case '|': {
|
||||
return false;
|
||||
}
|
||||
case '*': {
|
||||
// Pattern-specific (for NtQueryDirectoryFile)
|
||||
if (!is_pattern) {
|
||||
return false;
|
||||
}
|
||||
got_asterisk = true;
|
||||
break;
|
||||
}
|
||||
case '?': {
|
||||
// Pattern-specific (for NtQueryDirectoryFile)
|
||||
if (!is_pattern) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
dword_result_t NtCreateFile_entry(lpdword_t handle_out, dword_t desired_access,
|
||||
pointer_t<X_OBJECT_ATTRIBUTES> object_attrs,
|
||||
pointer_t<X_IO_STATUS_BLOCK> io_status_block,
|
||||
|
||||
@@ -91,6 +91,13 @@ dword_result_t NtQueryInformationFile_entry(
|
||||
out_length = sizeof(*info);
|
||||
break;
|
||||
}
|
||||
case XFileAlignmentInformation: {
|
||||
// Requested by XMountUtilityDrive XAM-task
|
||||
auto info = info_ptr.as<uint32_t*>();
|
||||
*info = 0; // FILE_BYTE_ALIGNMENT?
|
||||
out_length = sizeof(*info);
|
||||
break;
|
||||
}
|
||||
case XFileSectorInformation: {
|
||||
// SW that uses this seems to use the output as a way of uniquely
|
||||
// identifying a file for sorting/lookup so we can just give it an
|
||||
@@ -130,13 +137,6 @@ dword_result_t NtQueryInformationFile_entry(
|
||||
out_length = sizeof(*info);
|
||||
break;
|
||||
}
|
||||
case XFileAlignmentInformation: {
|
||||
// Requested by XMountUtilityDrive XAM-task
|
||||
auto info = info_ptr.as<uint32_t*>();
|
||||
*info = 0; // FILE_BYTE_ALIGNMENT?
|
||||
out_length = sizeof(*info);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// Unsupported, for now.
|
||||
assert_always();
|
||||
@@ -157,6 +157,8 @@ DECLARE_XBOXKRNL_EXPORT1(NtQueryInformationFile, kFileSystem, kImplemented);
|
||||
|
||||
uint32_t GetSetFileInfoMinimumLength(uint32_t info_class) {
|
||||
switch (info_class) {
|
||||
case XFileRenameInformation:
|
||||
return sizeof(X_FILE_RENAME_INFORMATION);
|
||||
case XFileDispositionInformation:
|
||||
return sizeof(X_FILE_DISPOSITION_INFORMATION);
|
||||
case XFilePositionInformation:
|
||||
@@ -171,7 +173,6 @@ uint32_t GetSetFileInfoMinimumLength(uint32_t info_class) {
|
||||
case XFileEndOfFileInformation:
|
||||
case XFileMountPartitionInformation:
|
||||
return 8;
|
||||
case XFileRenameInformation:
|
||||
case XFileLinkInformation:
|
||||
return 16;
|
||||
case XFileBasicInformation:
|
||||
@@ -204,6 +205,25 @@ dword_result_t NtSetInformationFile_entry(
|
||||
uint32_t out_length;
|
||||
|
||||
switch (info_class) {
|
||||
case XFileRenameInformation: {
|
||||
auto info = info_ptr.as<X_FILE_RENAME_INFORMATION*>();
|
||||
// Compute path, possibly attrs relative.
|
||||
std::filesystem::path target_path =
|
||||
util::TranslateAnsiString(kernel_memory(), &info->ansi_string);
|
||||
|
||||
// Place IsValidPath in path from where it can be accessed everywhere
|
||||
if (!IsValidPath(target_path.string(), false)) {
|
||||
return X_STATUS_OBJECT_NAME_INVALID;
|
||||
}
|
||||
|
||||
if (!target_path.has_filename()) {
|
||||
return X_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
file->Rename(target_path);
|
||||
out_length = sizeof(*info);
|
||||
break;
|
||||
}
|
||||
case XFileDispositionInformation: {
|
||||
// Used to set deletion flag. Which we don't support. Probably?
|
||||
auto info = info_ptr.as<X_FILE_DISPOSITION_INFORMATION*>();
|
||||
|
||||
@@ -268,6 +268,10 @@ X_STATUS XFile::Write(uint32_t buffer_guest_address, uint32_t buffer_length,
|
||||
}
|
||||
|
||||
X_STATUS XFile::SetLength(size_t length) { return file_->SetLength(length); }
|
||||
X_STATUS XFile::Rename(const std::filesystem::path file_path) {
|
||||
entry()->Rename(file_path);
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void XFile::RegisterIOCompletionPort(uint32_t key,
|
||||
object_ref<XIOCompletion> port) {
|
||||
|
||||
@@ -73,6 +73,62 @@ class X_FILE_DIRECTORY_INFORMATION {
|
||||
}
|
||||
};
|
||||
|
||||
static bool IsValidPath(const std::string_view s, bool is_pattern) {
|
||||
// TODO(gibbed): validate path components individually
|
||||
bool got_asterisk = false;
|
||||
for (const auto& c : s) {
|
||||
if (c <= 31 || c >= 127) {
|
||||
return false;
|
||||
}
|
||||
if (got_asterisk) {
|
||||
// * must be followed by a . (*.)
|
||||
//
|
||||
// 4D530819 has a bug in its game code where it attempts to
|
||||
// FindFirstFile() with filters of "Game:\\*_X3.rkv", "Game:\\m*_X3.rkv",
|
||||
// and "Game:\\w*_X3.rkv" and will infinite loop if the path filter is
|
||||
// allowed.
|
||||
if (c != '.') {
|
||||
return false;
|
||||
}
|
||||
got_asterisk = false;
|
||||
}
|
||||
switch (c) {
|
||||
case '"':
|
||||
// case '*':
|
||||
case '+':
|
||||
case ',':
|
||||
// case ':':
|
||||
case ';':
|
||||
case '<':
|
||||
case '=':
|
||||
case '>':
|
||||
// case '?':
|
||||
case '|': {
|
||||
return false;
|
||||
}
|
||||
case '*': {
|
||||
// Pattern-specific (for NtQueryDirectoryFile)
|
||||
if (!is_pattern) {
|
||||
return false;
|
||||
}
|
||||
got_asterisk = true;
|
||||
break;
|
||||
}
|
||||
case '?': {
|
||||
// Pattern-specific (for NtQueryDirectoryFile)
|
||||
if (!is_pattern) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
class XFile : public XObject {
|
||||
public:
|
||||
static const XObject::Type kObjectType = XObject::Type::File;
|
||||
@@ -110,6 +166,7 @@ class XFile : public XObject {
|
||||
uint32_t apc_context);
|
||||
|
||||
X_STATUS SetLength(size_t length);
|
||||
X_STATUS Rename(const std::filesystem::path file_path);
|
||||
|
||||
void RegisterIOCompletionPort(uint32_t key, object_ref<XIOCompletion> port);
|
||||
void RemoveIOCompletionPort(uint32_t key);
|
||||
|
||||
Reference in New Issue
Block a user