[Kernel] Added Support For: XFileRenameInformation
This commit is contained in:
parent
8b14b4bbc4
commit
74b52711a3
|
@ -64,6 +64,14 @@ struct X_FILE_INTERNAL_INFORMATION {
|
||||||
};
|
};
|
||||||
static_assert_size(X_FILE_INTERNAL_INFORMATION, 8);
|
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
|
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-_file_disposition_information
|
||||||
struct X_FILE_DISPOSITION_INFORMATION {
|
struct X_FILE_DISPOSITION_INFORMATION {
|
||||||
uint8_t delete_file;
|
uint8_t delete_file;
|
||||||
|
|
|
@ -39,62 +39,6 @@ struct CreateOptions {
|
||||||
static const uint32_t FILE_RANDOM_ACCESS = 0x00000800;
|
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,
|
dword_result_t NtCreateFile_entry(lpdword_t handle_out, dword_t desired_access,
|
||||||
pointer_t<X_OBJECT_ATTRIBUTES> object_attrs,
|
pointer_t<X_OBJECT_ATTRIBUTES> object_attrs,
|
||||||
pointer_t<X_IO_STATUS_BLOCK> io_status_block,
|
pointer_t<X_IO_STATUS_BLOCK> io_status_block,
|
||||||
|
|
|
@ -91,6 +91,13 @@ dword_result_t NtQueryInformationFile_entry(
|
||||||
out_length = sizeof(*info);
|
out_length = sizeof(*info);
|
||||||
break;
|
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: {
|
case XFileSectorInformation: {
|
||||||
// SW that uses this seems to use the output as a way of uniquely
|
// 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
|
// 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);
|
out_length = sizeof(*info);
|
||||||
break;
|
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: {
|
default: {
|
||||||
// Unsupported, for now.
|
// Unsupported, for now.
|
||||||
assert_always();
|
assert_always();
|
||||||
|
@ -157,6 +157,8 @@ DECLARE_XBOXKRNL_EXPORT1(NtQueryInformationFile, kFileSystem, kImplemented);
|
||||||
|
|
||||||
uint32_t GetSetFileInfoMinimumLength(uint32_t info_class) {
|
uint32_t GetSetFileInfoMinimumLength(uint32_t info_class) {
|
||||||
switch (info_class) {
|
switch (info_class) {
|
||||||
|
case XFileRenameInformation:
|
||||||
|
return sizeof(X_FILE_RENAME_INFORMATION);
|
||||||
case XFileDispositionInformation:
|
case XFileDispositionInformation:
|
||||||
return sizeof(X_FILE_DISPOSITION_INFORMATION);
|
return sizeof(X_FILE_DISPOSITION_INFORMATION);
|
||||||
case XFilePositionInformation:
|
case XFilePositionInformation:
|
||||||
|
@ -171,7 +173,6 @@ uint32_t GetSetFileInfoMinimumLength(uint32_t info_class) {
|
||||||
case XFileEndOfFileInformation:
|
case XFileEndOfFileInformation:
|
||||||
case XFileMountPartitionInformation:
|
case XFileMountPartitionInformation:
|
||||||
return 8;
|
return 8;
|
||||||
case XFileRenameInformation:
|
|
||||||
case XFileLinkInformation:
|
case XFileLinkInformation:
|
||||||
return 16;
|
return 16;
|
||||||
case XFileBasicInformation:
|
case XFileBasicInformation:
|
||||||
|
@ -204,6 +205,25 @@ dword_result_t NtSetInformationFile_entry(
|
||||||
uint32_t out_length;
|
uint32_t out_length;
|
||||||
|
|
||||||
switch (info_class) {
|
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: {
|
case XFileDispositionInformation: {
|
||||||
// Used to set deletion flag. Which we don't support. Probably?
|
// Used to set deletion flag. Which we don't support. Probably?
|
||||||
auto info = info_ptr.as<X_FILE_DISPOSITION_INFORMATION*>();
|
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::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,
|
void XFile::RegisterIOCompletionPort(uint32_t key,
|
||||||
object_ref<XIOCompletion> port) {
|
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 {
|
class XFile : public XObject {
|
||||||
public:
|
public:
|
||||||
static const XObject::Type kObjectType = XObject::Type::File;
|
static const XObject::Type kObjectType = XObject::Type::File;
|
||||||
|
@ -110,6 +166,7 @@ class XFile : public XObject {
|
||||||
uint32_t apc_context);
|
uint32_t apc_context);
|
||||||
|
|
||||||
X_STATUS SetLength(size_t length);
|
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 RegisterIOCompletionPort(uint32_t key, object_ref<XIOCompletion> port);
|
||||||
void RemoveIOCompletionPort(uint32_t key);
|
void RemoveIOCompletionPort(uint32_t key);
|
||||||
|
|
|
@ -40,6 +40,10 @@ class HostPathDevice : public Device {
|
||||||
uint32_t sectors_per_allocation_unit() const override { return 1; }
|
uint32_t sectors_per_allocation_unit() const override { return 1; }
|
||||||
uint32_t bytes_per_sector() const override { return 0x200; }
|
uint32_t bytes_per_sector() const override { return 0x200; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend class HostPathEntry;
|
||||||
|
std::filesystem::path host_path() const { return host_path_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void PopulateEntry(HostPathEntry* parent_entry);
|
void PopulateEntry(HostPathEntry* parent_entry);
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "xenia/base/math.h"
|
#include "xenia/base/math.h"
|
||||||
#include "xenia/base/string.h"
|
#include "xenia/base/string.h"
|
||||||
#include "xenia/vfs/device.h"
|
#include "xenia/vfs/device.h"
|
||||||
|
#include "xenia/vfs/devices/host_path_device.h"
|
||||||
#include "xenia/vfs/devices/host_path_file.h"
|
#include "xenia/vfs/devices/host_path_file.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
@ -110,6 +111,15 @@ bool HostPathEntry::DeleteEntryInternal(Entry* entry) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HostPathEntry::RenameEntryInternal(const std::filesystem::path file_path) {
|
||||||
|
const std::string new_host_path_ = xe::utf8::join_paths(
|
||||||
|
xe::path_to_utf8(((HostPathDevice*)device_)->host_path()),
|
||||||
|
xe::path_to_utf8(file_path));
|
||||||
|
|
||||||
|
std::filesystem::rename(host_path_, new_host_path_);
|
||||||
|
host_path_ = new_host_path_;
|
||||||
|
}
|
||||||
|
|
||||||
void HostPathEntry::update() {
|
void HostPathEntry::update() {
|
||||||
xe::filesystem::FileInfo file_info;
|
xe::filesystem::FileInfo file_info;
|
||||||
if (!xe::filesystem::GetInfo(host_path_, &file_info)) {
|
if (!xe::filesystem::GetInfo(host_path_, &file_info)) {
|
||||||
|
|
|
@ -46,6 +46,7 @@ class HostPathEntry : public Entry {
|
||||||
std::unique_ptr<Entry> CreateEntryInternal(const std::string_view name,
|
std::unique_ptr<Entry> CreateEntryInternal(const std::string_view name,
|
||||||
uint32_t attributes) override;
|
uint32_t attributes) override;
|
||||||
bool DeleteEntryInternal(Entry* entry) override;
|
bool DeleteEntryInternal(Entry* entry) override;
|
||||||
|
void RenameEntryInternal(const std::filesystem::path file_path) override;
|
||||||
|
|
||||||
std::filesystem::path host_path_;
|
std::filesystem::path host_path_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -133,5 +133,22 @@ void Entry::Touch() {
|
||||||
// TODO(benvanik): update timestamps.
|
// TODO(benvanik): update timestamps.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Entry::Rename(const std::filesystem::path file_path) {
|
||||||
|
std::vector<std::string_view> splitted_path =
|
||||||
|
xe::utf8::split_path(xe::path_to_utf8(file_path));
|
||||||
|
|
||||||
|
splitted_path.erase(splitted_path.begin());
|
||||||
|
|
||||||
|
const std::string guest_path_without_root =
|
||||||
|
xe::utf8::join_guest_paths(splitted_path);
|
||||||
|
|
||||||
|
RenameEntryInternal(guest_path_without_root);
|
||||||
|
|
||||||
|
absolute_path_ = xe::utf8::join_guest_paths(device_->mount_path(),
|
||||||
|
guest_path_without_root);
|
||||||
|
path_ = guest_path_without_root;
|
||||||
|
name_ = xe::path_to_utf8(file_path.filename());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace vfs
|
} // namespace vfs
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -111,6 +111,7 @@ class Entry {
|
||||||
bool Delete();
|
bool Delete();
|
||||||
void Touch();
|
void Touch();
|
||||||
|
|
||||||
|
void Rename(const std::filesystem::path file_path);
|
||||||
// If successful, out_file points to a new file. When finished, call
|
// If successful, out_file points to a new file. When finished, call
|
||||||
// file->Destroy()
|
// file->Destroy()
|
||||||
virtual X_STATUS Open(uint32_t desired_access, File** out_file) = 0;
|
virtual X_STATUS Open(uint32_t desired_access, File** out_file) = 0;
|
||||||
|
@ -131,6 +132,7 @@ class Entry {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
virtual bool DeleteEntryInternal(Entry* entry) { return false; }
|
virtual bool DeleteEntryInternal(Entry* entry) { return false; }
|
||||||
|
virtual void RenameEntryInternal(const std::filesystem::path file_path) {}
|
||||||
|
|
||||||
xe::global_critical_region global_critical_region_;
|
xe::global_critical_region global_critical_region_;
|
||||||
Device* device_;
|
Device* device_;
|
||||||
|
|
|
@ -45,6 +45,9 @@ class File {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual X_STATUS SetLength(size_t length) { return X_STATUS_NOT_IMPLEMENTED; }
|
virtual X_STATUS SetLength(size_t length) { return X_STATUS_NOT_IMPLEMENTED; }
|
||||||
|
virtual X_STATUS Rename(const std::filesystem::path file_path) {
|
||||||
|
return X_STATUS_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
// xe::filesystem::FileAccess
|
// xe::filesystem::FileAccess
|
||||||
uint32_t file_access() const { return file_access_; }
|
uint32_t file_access() const { return file_access_; }
|
||||||
|
|
Loading…
Reference in New Issue