Skip to content

Commit

Permalink
Port PR [Kernel/IO] Implemented XFileRenameInformation xenia-project#…
Browse files Browse the repository at this point in the history
…1742 from xenia master
  • Loading branch information
aerisarn committed Jan 3, 2024
1 parent 434ae79 commit b32c7b7
Show file tree
Hide file tree
Showing 17 changed files with 136 additions and 60 deletions.
8 changes: 8 additions & 0 deletions src/xenia/base/filesystem.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,13 @@ bool CreateParentFolder(const std::filesystem::path& path) {
return true;
}

bool FileHandle::Rename(const std::string_view file_name) {
std::error_code error_code;
auto new_path = path().parent_path() / file_name;

std::filesystem::rename(path(), new_path, error_code);
return error_code.value();
}

} // namespace filesystem
} // namespace xe
2 changes: 2 additions & 0 deletions src/xenia/base/filesystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ class FileHandle {

const std::filesystem::path& path() const { return path_; }

bool Rename(const std::string_view file_name);

// Reads the requested number of bytes from the file starting at the given
// offset. The total number of bytes read is returned only if the complete
// read succeeds.
Expand Down
7 changes: 7 additions & 0 deletions src/xenia/kernel/info/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ struct X_FILE_NETWORK_OPEN_INFORMATION {
};
static_assert_size(X_FILE_NETWORK_OPEN_INFORMATION, 56);

struct X_FILE_RENAME_INFORMATION {
be<uint32_t> rename;
be<uint32_t> root_dir_handle;
X_ANSI_STRING ansi_string;
};
static_assert_size(X_FILE_RENAME_INFORMATION, 16);

#pragma pack(pop)

} // namespace kernel
Expand Down
64 changes: 4 additions & 60 deletions src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -122,7 +66,7 @@ dword_result_t NtCreateFile_entry(lpdword_t handle_out, dword_t desired_access,
auto target_path = util::TranslateAnsiString(kernel_memory(), object_name);

// Enforce that the path is ASCII.
if (!IsValidPath(target_path, false)) {
if (!xe::vfs::VirtualFileSystem::IsValidPath(target_path, false)) {
return X_STATUS_OBJECT_NAME_INVALID;
}

Expand Down Expand Up @@ -521,7 +465,7 @@ dword_result_t NtQueryFullAttributesFile_entry(
auto target_path = util::TranslateAnsiString(kernel_memory(), object_name);

// Enforce that the path is ASCII.
if (!IsValidPath(target_path, false)) {
if (!xe::vfs::VirtualFileSystem::IsValidPath(target_path, false)) {
return X_STATUS_OBJECT_NAME_INVALID;
}

Expand Down Expand Up @@ -560,7 +504,7 @@ dword_result_t NtQueryDirectoryFile_entry(
auto name = util::TranslateAnsiString(kernel_memory(), file_name);

// Enforce that the path is ASCII.
if (!IsValidPath(name, true)) {
if (!xe::vfs::VirtualFileSystem::IsValidPath(name, true)) {
return X_STATUS_INVALID_PARAMETER;
}

Expand Down Expand Up @@ -617,7 +561,7 @@ dword_result_t NtOpenSymbolicLinkObject_entry(
auto target_path = util::TranslateAnsiString(kernel_memory(), object_name);

// Enforce that the path is ASCII.
if (!IsValidPath(target_path, false)) {
if (!xe::vfs::VirtualFileSystem::IsValidPath(target_path, false)) {
return X_STATUS_OBJECT_NAME_INVALID;
}

Expand Down
23 changes: 23 additions & 0 deletions src/xenia/kernel/xboxkrnl/xboxkrnl_io_info.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
#include "xenia/vfs/device.h"
#include "xenia/xbox.h"

#include "xenia/kernel/util/shim_utils.h"

namespace xe {
namespace kernel {
namespace xboxkrnl {
Expand Down Expand Up @@ -248,6 +250,27 @@ dword_result_t NtSetInformationFile_entry(
}
break;
}
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 (!xe::vfs::VirtualFileSystem::IsValidPath(target_path.string(),
false)) {
return X_STATUS_OBJECT_NAME_INVALID;
}

if (target_path.has_filename()) {
file->SetName(target_path.filename().string());
} else {
result = X_STATUS_INVALID_PARAMETER;
}
out_length = sizeof(*info);
break;
}
default:
// Unsupported, for now.
assert_always();
Expand Down
4 changes: 4 additions & 0 deletions src/xenia/kernel/xfile.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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::SetName(const std::string_view file_name) {
entry()->set_name(file_name);
return file_->SetName(file_name);
}

void XFile::RegisterIOCompletionPort(uint32_t key,
object_ref<XIOCompletion> port) {
Expand Down
1 change: 1 addition & 0 deletions src/xenia/kernel/xfile.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ class XFile : public XObject {
uint32_t apc_context);

X_STATUS SetLength(size_t length);
X_STATUS SetName(const std::string_view file_name);

void RegisterIOCompletionPort(uint32_t key, object_ref<XIOCompletion> port);
void RemoveIOCompletionPort(uint32_t key);
Expand Down
1 change: 1 addition & 0 deletions src/xenia/vfs/devices/disc_image_entry.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class DiscImageEntry : public Entry {
size_t data_offset() const { return data_offset_; }
size_t data_size() const { return data_size_; }

void SetHostFileName(const std::string_view filename) override { return; }
X_STATUS Open(uint32_t desired_access, File** out_file) override;

bool can_map() const override { return true; }
Expand Down
4 changes: 4 additions & 0 deletions src/xenia/vfs/devices/host_path_entry.cc
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ bool HostPathEntry::DeleteEntryInternal(Entry* entry) {
}
}

void HostPathEntry::SetHostFileName(const std::string_view filename) {
host_path_ = host_path_.parent_path() / filename;
}

void HostPathEntry::update() {
xe::filesystem::FileInfo file_info;
if (!xe::filesystem::GetInfo(host_path_, &file_info)) {
Expand Down
1 change: 1 addition & 0 deletions src/xenia/vfs/devices/host_path_entry.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class HostPathEntry : public Entry {
xe::filesystem::FileInfo file_info);

const std::filesystem::path& host_path() const { return host_path_; }
void SetHostFileName(const std::string_view filename) override;

X_STATUS Open(uint32_t desired_access, File** out_file) override;

Expand Down
8 changes: 8 additions & 0 deletions src/xenia/vfs/devices/host_path_file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,13 @@ X_STATUS HostPathFile::SetLength(size_t length) {
}
}

X_STATUS HostPathFile::SetName(const std::string_view file_name) {
if (file_handle_->Rename(file_name)) {
return X_STATUS_SUCCESS;
} else {
return X_STATUS_NO_SUCH_FILE;
}
}

} // namespace vfs
} // namespace xe
1 change: 1 addition & 0 deletions src/xenia/vfs/devices/host_path_file.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class HostPathFile : public File {
X_STATUS WriteSync(const void* buffer, size_t buffer_length,
size_t byte_offset, size_t* out_bytes_written) override;
X_STATUS SetLength(size_t length) override;
X_STATUS SetName(const std::string_view file_name) override;

private:
std::unique_ptr<xe::filesystem::FileHandle> file_handle_;
Expand Down
8 changes: 8 additions & 0 deletions src/xenia/vfs/entry.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ void Entry::Dump(xe::StringBuffer* string_buffer, int indent) {
}
}

void Entry::set_name(const std::string_view path) {
absolute_path_ = xe::utf8::join_guest_paths(device_->mount_path(), path);
path_ = xe::utf8::find_name_from_guest_path(path);
name_ = xe::utf8::find_name_from_guest_path(path);

SetHostFileName(name_);
}

bool Entry::is_read_only() const { return device_->is_read_only(); }

Entry* Entry::GetChild(const std::string_view name) {
Expand Down
3 changes: 3 additions & 0 deletions src/xenia/vfs/entry.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ class Entry {
uint64_t access_timestamp() const { return access_timestamp_; }
uint64_t write_timestamp() const { return write_timestamp_; }

void set_name(const std::string_view name);

bool is_read_only() const;

Entry* GetChild(const std::string_view name);
Expand Down Expand Up @@ -131,6 +133,7 @@ class Entry {
return nullptr;
}
virtual bool DeleteEntryInternal(Entry* entry) { return false; }
virtual void SetHostFileName(const std::string_view filename){};

xe::global_critical_region global_critical_region_;
Device* device_;
Expand Down
4 changes: 4 additions & 0 deletions src/xenia/vfs/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ class File {

virtual X_STATUS SetLength(size_t length) { return X_STATUS_NOT_IMPLEMENTED; }

virtual X_STATUS SetName(const std::string_view file_name) {
return X_STATUS_NOT_IMPLEMENTED;
}

// xe::filesystem::FileAccess
uint32_t file_access() const { return file_access_; }
const Entry* entry() const { return entry_; }
Expand Down
56 changes: 56 additions & 0 deletions src/xenia/vfs/virtual_file_system.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,62 @@ namespace vfs {

using namespace xe::literals;

bool VirtualFileSystem::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;
}

VirtualFileSystem::VirtualFileSystem() {}

VirtualFileSystem::~VirtualFileSystem() {
Expand Down
1 change: 1 addition & 0 deletions src/xenia/vfs/virtual_file_system.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class VirtualFileSystem {
public:
VirtualFileSystem();
~VirtualFileSystem();
static bool IsValidPath(const std::string_view s, bool is_pattern);

bool RegisterDevice(std::unique_ptr<Device> device);
bool UnregisterDevice(const std::string_view path);
Expand Down

0 comments on commit b32c7b7

Please sign in to comment.