Move NtCreateFile/NtOpenFile to VFS, implement (mostly) for real.
Progress on #305.
This commit is contained in:
@@ -26,9 +26,11 @@ DiscImageEntry::DiscImageEntry(Device* device, Entry* parent, std::string path,
|
||||
|
||||
DiscImageEntry::~DiscImageEntry() = default;
|
||||
|
||||
X_STATUS DiscImageEntry::Open(KernelState* kernel_state, Mode mode, bool async,
|
||||
XFile** out_file) {
|
||||
*out_file = new DiscImageFile(kernel_state, mode, this);
|
||||
X_STATUS DiscImageEntry::Open(KernelState* kernel_state,
|
||||
uint32_t desired_access,
|
||||
object_ref<XFile>* out_file) {
|
||||
*out_file =
|
||||
object_ref<XFile>(new DiscImageFile(kernel_state, desired_access, this));
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@ class DiscImageEntry : public Entry {
|
||||
size_t data_offset() const { return data_offset_; }
|
||||
size_t data_size() const { return data_size_; }
|
||||
|
||||
X_STATUS Open(KernelState* kernel_state, Mode mode, bool async,
|
||||
XFile** out_file) override;
|
||||
X_STATUS Open(KernelState* kernel_state, uint32_t desired_access,
|
||||
object_ref<XFile>* out_file) override;
|
||||
|
||||
bool can_map() const override { return true; }
|
||||
std::unique_ptr<MappedMemory> OpenMapped(MappedMemory::Mode mode,
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
namespace xe {
|
||||
namespace vfs {
|
||||
|
||||
DiscImageFile::DiscImageFile(KernelState* kernel_state, Mode mode,
|
||||
DiscImageFile::DiscImageFile(KernelState* kernel_state, uint32_t file_access,
|
||||
DiscImageEntry* entry)
|
||||
: XFile(kernel_state, mode, entry), entry_(entry) {}
|
||||
: XFile(kernel_state, file_access, entry), entry_(entry) {}
|
||||
|
||||
DiscImageFile::~DiscImageFile() = default;
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ class DiscImageEntry;
|
||||
|
||||
class DiscImageFile : public XFile {
|
||||
public:
|
||||
DiscImageFile(KernelState* kernel_state, Mode desired_access,
|
||||
DiscImageFile(KernelState* kernel_state, uint32_t file_access,
|
||||
DiscImageEntry* entry);
|
||||
~DiscImageFile() override;
|
||||
|
||||
|
||||
@@ -46,25 +46,10 @@ bool HostPathDevice::Initialize() {
|
||||
void HostPathDevice::PopulateEntry(HostPathEntry* parent_entry) {
|
||||
auto child_infos = xe::filesystem::ListFiles(parent_entry->local_path());
|
||||
for (auto& child_info : child_infos) {
|
||||
auto child = new HostPathEntry(
|
||||
this, parent_entry, xe::to_string(child_info.name),
|
||||
xe::join_paths(parent_entry->local_path(), child_info.name));
|
||||
child->create_timestamp_ = child_info.create_timestamp;
|
||||
child->access_timestamp_ = child_info.access_timestamp;
|
||||
child->write_timestamp_ = child_info.write_timestamp;
|
||||
|
||||
if (child_info.type == xe::filesystem::FileInfo::Type::kDirectory) {
|
||||
child->attributes_ = kFileAttributeDirectory;
|
||||
} else {
|
||||
child->attributes_ = kFileAttributeNormal;
|
||||
if (read_only_) {
|
||||
child->attributes_ |= kFileAttributeReadOnly;
|
||||
}
|
||||
child->size_ = child_info.total_size;
|
||||
child->allocation_size_ =
|
||||
xe::round_up(child_info.total_size, bytes_per_sector());
|
||||
}
|
||||
|
||||
auto child = HostPathEntry::Create(
|
||||
this, parent_entry,
|
||||
xe::join_paths(parent_entry->local_path(), child_info.name),
|
||||
child_info);
|
||||
parent_entry->children_.push_back(std::unique_ptr<Entry>(child));
|
||||
|
||||
if (child_info.type == xe::filesystem::FileInfo::Type::kDirectory) {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "xenia/vfs/devices/host_path_entry.h"
|
||||
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/mapped_memory.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/string.h"
|
||||
@@ -23,41 +24,70 @@ HostPathEntry::HostPathEntry(Device* device, Entry* parent, std::string path,
|
||||
|
||||
HostPathEntry::~HostPathEntry() = default;
|
||||
|
||||
X_STATUS HostPathEntry::Open(KernelState* kernel_state, Mode mode, bool async,
|
||||
XFile** out_file) {
|
||||
// TODO(benvanik): plumb through proper disposition/access mode.
|
||||
DWORD desired_access =
|
||||
is_read_only() ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE);
|
||||
if (mode == Mode::READ_APPEND) {
|
||||
desired_access |= FILE_APPEND_DATA;
|
||||
HostPathEntry* HostPathEntry::Create(Device* device, Entry* parent,
|
||||
const std::wstring& full_path,
|
||||
xe::filesystem::FileInfo file_info) {
|
||||
auto entry = new HostPathEntry(device, parent, xe::to_string(file_info.name),
|
||||
full_path);
|
||||
entry->create_timestamp_ = file_info.create_timestamp;
|
||||
entry->access_timestamp_ = file_info.access_timestamp;
|
||||
entry->write_timestamp_ = file_info.write_timestamp;
|
||||
if (file_info.type == xe::filesystem::FileInfo::Type::kDirectory) {
|
||||
entry->attributes_ = kFileAttributeDirectory;
|
||||
} else {
|
||||
entry->attributes_ = kFileAttributeNormal;
|
||||
if (device->is_read_only()) {
|
||||
entry->attributes_ |= kFileAttributeReadOnly;
|
||||
}
|
||||
entry->size_ = file_info.total_size;
|
||||
entry->allocation_size_ =
|
||||
xe::round_up(file_info.total_size, device->bytes_per_sector());
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
X_STATUS HostPathEntry::Open(KernelState* kernel_state, uint32_t desired_access,
|
||||
object_ref<XFile>* out_file) {
|
||||
if (is_read_only() && (desired_access & (FileAccess::kFileWriteData |
|
||||
FileAccess::kFileAppendData))) {
|
||||
XELOGE("Attempting to open file for write access on read-only device");
|
||||
return X_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
DWORD open_access = 0;
|
||||
if (desired_access & FileAccess::kGenericRead) {
|
||||
open_access |= GENERIC_READ;
|
||||
}
|
||||
if (desired_access & FileAccess::kGenericWrite) {
|
||||
open_access |= GENERIC_WRITE;
|
||||
}
|
||||
if (desired_access & FileAccess::kGenericExecute) {
|
||||
open_access |= GENERIC_EXECUTE;
|
||||
}
|
||||
if (desired_access & FileAccess::kGenericAll) {
|
||||
open_access |= GENERIC_ALL;
|
||||
}
|
||||
if (desired_access & FileAccess::kFileReadData) {
|
||||
open_access |= FILE_READ_DATA;
|
||||
}
|
||||
if (desired_access & FileAccess::kFileWriteData) {
|
||||
open_access |= FILE_WRITE_DATA;
|
||||
}
|
||||
if (desired_access & FileAccess::kFileAppendData) {
|
||||
open_access |= FILE_APPEND_DATA;
|
||||
}
|
||||
DWORD share_mode = FILE_SHARE_READ;
|
||||
DWORD creation_disposition;
|
||||
switch (mode) {
|
||||
case Mode::READ:
|
||||
creation_disposition = OPEN_EXISTING;
|
||||
break;
|
||||
case Mode::READ_WRITE:
|
||||
creation_disposition = OPEN_ALWAYS;
|
||||
break;
|
||||
case Mode::READ_APPEND:
|
||||
creation_disposition = OPEN_EXISTING;
|
||||
break;
|
||||
default:
|
||||
assert_unhandled_case(mode);
|
||||
break;
|
||||
}
|
||||
DWORD flags_and_attributes = async ? FILE_FLAG_OVERLAPPED : 0;
|
||||
// We assume we've already created the file in the caller.
|
||||
DWORD creation_disposition = OPEN_EXISTING;
|
||||
HANDLE file =
|
||||
CreateFileW(local_path_.c_str(), desired_access, share_mode, NULL,
|
||||
creation_disposition,
|
||||
flags_and_attributes | FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
creation_disposition, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
if (file == INVALID_HANDLE_VALUE) {
|
||||
// TODO(benvanik): pick correct response.
|
||||
return X_STATUS_NO_SUCH_FILE;
|
||||
}
|
||||
|
||||
*out_file = new HostPathFile(kernel_state, mode, this, file);
|
||||
*out_file = object_ref<XFile>(
|
||||
new HostPathFile(kernel_state, desired_access, this, file));
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -67,5 +97,38 @@ std::unique_ptr<MappedMemory> HostPathEntry::OpenMapped(MappedMemory::Mode mode,
|
||||
return MappedMemory::Open(local_path_, mode, offset, length);
|
||||
}
|
||||
|
||||
std::unique_ptr<Entry> HostPathEntry::CreateEntryInternal(std::string name,
|
||||
uint32_t attributes) {
|
||||
auto full_path = xe::join_paths(local_path_, xe::to_wstring(name));
|
||||
if (attributes & kFileAttributeDirectory) {
|
||||
if (!xe::filesystem::CreateFolder(full_path)) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
auto file = xe::filesystem::OpenFile(full_path, "wb");
|
||||
if (!file) {
|
||||
return nullptr;
|
||||
}
|
||||
fclose(file);
|
||||
}
|
||||
xe::filesystem::FileInfo file_info;
|
||||
if (!xe::filesystem::GetInfo(full_path, &file_info)) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::unique_ptr<Entry>(
|
||||
HostPathEntry::Create(device_, this, full_path, file_info));
|
||||
}
|
||||
|
||||
bool HostPathEntry::DeleteEntryInternal(Entry* entry) {
|
||||
auto full_path = xe::join_paths(local_path_, xe::to_wstring(entry->name()));
|
||||
if (entry->attributes() & kFileAttributeDirectory) {
|
||||
// Delete entire directory and contents.
|
||||
return xe::filesystem::DeleteFolder(full_path);
|
||||
} else {
|
||||
// Delete file.
|
||||
return xe::filesystem::DeleteFile(full_path);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace vfs
|
||||
} // namespace xe
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "xenia/base/filesystem.h"
|
||||
#include "xenia/vfs/entry.h"
|
||||
|
||||
namespace xe {
|
||||
@@ -25,10 +26,14 @@ class HostPathEntry : public Entry {
|
||||
const std::wstring& local_path);
|
||||
~HostPathEntry() override;
|
||||
|
||||
static HostPathEntry* Create(Device* device, Entry* parent,
|
||||
const std::wstring& full_path,
|
||||
xe::filesystem::FileInfo file_info);
|
||||
|
||||
const std::wstring& local_path() { return local_path_; }
|
||||
|
||||
X_STATUS Open(KernelState* kernel_state, Mode mode, bool async,
|
||||
XFile** out_file) override;
|
||||
X_STATUS Open(KernelState* kernel_state, uint32_t desired_access,
|
||||
object_ref<XFile>* out_file) override;
|
||||
|
||||
bool can_map() const override { return true; }
|
||||
std::unique_ptr<MappedMemory> OpenMapped(MappedMemory::Mode mode,
|
||||
@@ -38,6 +43,10 @@ class HostPathEntry : public Entry {
|
||||
private:
|
||||
friend class HostPathDevice;
|
||||
|
||||
std::unique_ptr<Entry> CreateEntryInternal(std::string name,
|
||||
uint32_t attributes) override;
|
||||
bool DeleteEntryInternal(Entry* entry) override;
|
||||
|
||||
std::wstring local_path_;
|
||||
};
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
namespace xe {
|
||||
namespace vfs {
|
||||
|
||||
HostPathFile::HostPathFile(KernelState* kernel_state, Mode mode,
|
||||
HostPathFile::HostPathFile(KernelState* kernel_state, uint32_t file_access,
|
||||
HostPathEntry* entry, HANDLE file_handle)
|
||||
: XFile(kernel_state, mode, entry),
|
||||
: XFile(kernel_state, file_access, entry),
|
||||
entry_(entry),
|
||||
file_handle_(file_handle) {}
|
||||
|
||||
@@ -24,6 +24,10 @@ HostPathFile::~HostPathFile() { CloseHandle(file_handle_); }
|
||||
|
||||
X_STATUS HostPathFile::ReadSync(void* buffer, size_t buffer_length,
|
||||
size_t byte_offset, size_t* out_bytes_read) {
|
||||
if (!(file_access() & FileAccess::kFileReadData)) {
|
||||
return X_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
OVERLAPPED overlapped;
|
||||
overlapped.Pointer = (PVOID)byte_offset;
|
||||
overlapped.hEvent = NULL;
|
||||
@@ -41,6 +45,11 @@ X_STATUS HostPathFile::ReadSync(void* buffer, size_t buffer_length,
|
||||
X_STATUS HostPathFile::WriteSync(const void* buffer, size_t buffer_length,
|
||||
size_t byte_offset,
|
||||
size_t* out_bytes_written) {
|
||||
if (!(file_access() & FileAccess::kFileWriteData |
|
||||
FileAccess::kFileAppendData)) {
|
||||
return X_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
OVERLAPPED overlapped;
|
||||
overlapped.Pointer = (PVOID)byte_offset;
|
||||
overlapped.hEvent = NULL;
|
||||
|
||||
@@ -21,8 +21,8 @@ class HostPathEntry;
|
||||
|
||||
class HostPathFile : public XFile {
|
||||
public:
|
||||
HostPathFile(KernelState* kernel_state, Mode mode, HostPathEntry* entry,
|
||||
HANDLE file_handle);
|
||||
HostPathFile(KernelState* kernel_state, uint32_t file_access,
|
||||
HostPathEntry* entry, HANDLE file_handle);
|
||||
~HostPathFile() override;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -24,9 +24,11 @@ STFSContainerEntry::STFSContainerEntry(Device* device, Entry* parent,
|
||||
|
||||
STFSContainerEntry::~STFSContainerEntry() = default;
|
||||
|
||||
X_STATUS STFSContainerEntry::Open(KernelState* kernel_state, Mode mode,
|
||||
bool async, XFile** out_file) {
|
||||
*out_file = new STFSContainerFile(kernel_state, mode, this);
|
||||
X_STATUS STFSContainerEntry::Open(KernelState* kernel_state,
|
||||
uint32_t desired_access,
|
||||
object_ref<XFile>* out_file) {
|
||||
*out_file = object_ref<XFile>(
|
||||
new STFSContainerFile(kernel_state, desired_access, this));
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@ class STFSContainerEntry : public Entry {
|
||||
size_t data_offset() const { return data_offset_; }
|
||||
size_t data_size() const { return data_size_; }
|
||||
|
||||
X_STATUS Open(KernelState* kernel_state, Mode desired_access, bool async,
|
||||
XFile** out_file) override;
|
||||
X_STATUS Open(KernelState* kernel_state, uint32_t desired_access,
|
||||
object_ref<XFile>* out_file) override;
|
||||
|
||||
struct BlockRecord {
|
||||
size_t offset;
|
||||
|
||||
@@ -16,9 +16,10 @@
|
||||
namespace xe {
|
||||
namespace vfs {
|
||||
|
||||
STFSContainerFile::STFSContainerFile(KernelState* kernel_state, Mode mode,
|
||||
STFSContainerFile::STFSContainerFile(KernelState* kernel_state,
|
||||
uint32_t file_access,
|
||||
STFSContainerEntry* entry)
|
||||
: XFile(kernel_state, mode, entry), entry_(entry) {}
|
||||
: XFile(kernel_state, file_access, entry), entry_(entry) {}
|
||||
|
||||
STFSContainerFile::~STFSContainerFile() = default;
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ class STFSContainerEntry;
|
||||
|
||||
class STFSContainerFile : public XFile {
|
||||
public:
|
||||
STFSContainerFile(KernelState* kernel_state, Mode mode,
|
||||
STFSContainerFile(KernelState* kernel_state, uint32_t file_access,
|
||||
STFSContainerEntry* entry);
|
||||
~STFSContainerFile() override;
|
||||
|
||||
|
||||
@@ -70,5 +70,54 @@ Entry* Entry::IterateChildren(const xe::filesystem::WildcardEngine& engine,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Entry* Entry::CreateEntry(std::string name, uint32_t attributes) {
|
||||
if (is_read_only()) {
|
||||
return nullptr;
|
||||
}
|
||||
std::lock_guard<xe::mutex> lock(device_->mutex());
|
||||
if (GetChild(name)) {
|
||||
// Already exists.
|
||||
return nullptr;
|
||||
}
|
||||
auto entry = CreateEntryInternal(name, attributes);
|
||||
if (!entry) {
|
||||
return nullptr;
|
||||
}
|
||||
children_.push_back(std::move(entry));
|
||||
// TODO(benvanik): resort? would break iteration?
|
||||
Touch();
|
||||
return children_.back().get();
|
||||
}
|
||||
|
||||
bool Entry::Delete(Entry* entry) {
|
||||
if (is_read_only()) {
|
||||
return false;
|
||||
}
|
||||
std::lock_guard<xe::mutex> lock(device_->mutex());
|
||||
if (entry->parent() != this) {
|
||||
return false;
|
||||
}
|
||||
if (!DeleteEntryInternal(entry)) {
|
||||
return false;
|
||||
}
|
||||
for (auto& it = children_.begin(); it != children_.end(); ++it) {
|
||||
if (it->get() == entry) {
|
||||
children_.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Touch();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Entry::Delete() {
|
||||
assert_not_null(parent_);
|
||||
return parent_->Delete(this);
|
||||
}
|
||||
|
||||
void Entry::Touch() {
|
||||
// TODO(benvanik): update timestamps.
|
||||
}
|
||||
|
||||
} // namespace vfs
|
||||
} // namespace xe
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "xenia/base/mapped_memory.h"
|
||||
#include "xenia/base/string_buffer.h"
|
||||
#include "xenia/kernel/xobject.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
namespace xe {
|
||||
@@ -35,10 +36,41 @@ using namespace xe::kernel;
|
||||
|
||||
class Device;
|
||||
|
||||
enum class Mode {
|
||||
READ,
|
||||
READ_WRITE,
|
||||
READ_APPEND,
|
||||
// Matches http://source.winehq.org/source/include/winternl.h#1591.
|
||||
enum class FileAction {
|
||||
kSuperseded = 0,
|
||||
kOpened = 1,
|
||||
kCreated = 2,
|
||||
kOverwritten = 3,
|
||||
kExists = 4,
|
||||
kDoesNotExist = 5,
|
||||
};
|
||||
|
||||
enum class FileDisposition {
|
||||
// If exist replace, else create.
|
||||
kSuperscede = 0,
|
||||
// If exist open, else error.
|
||||
kOpen = 1,
|
||||
// If exist error, else create.
|
||||
kCreate = 2,
|
||||
// If exist open, else create.
|
||||
kOpenIf = 3,
|
||||
// If exist open and overwrite, else error.
|
||||
kOverwrite = 4,
|
||||
// If exist open and overwrite, else create.
|
||||
kOverwriteIf = 5,
|
||||
};
|
||||
|
||||
struct FileAccess {
|
||||
// Implies kFileReadData.
|
||||
static const uint32_t kGenericRead = 0x80000000;
|
||||
// Implies kFileWriteData.
|
||||
static const uint32_t kGenericWrite = 0x40000000;
|
||||
static const uint32_t kGenericExecute = 0x20000000;
|
||||
static const uint32_t kGenericAll = 0x10000000;
|
||||
static const uint32_t kFileReadData = 0x00000001;
|
||||
static const uint32_t kFileWriteData = 0x00000002;
|
||||
static const uint32_t kFileAppendData = 0x00000004;
|
||||
};
|
||||
|
||||
enum FileAttributeFlags : uint32_t {
|
||||
@@ -81,8 +113,13 @@ class Entry {
|
||||
Entry* IterateChildren(const xe::filesystem::WildcardEngine& engine,
|
||||
size_t* current_index);
|
||||
|
||||
virtual X_STATUS Open(KernelState* kernel_state, Mode mode, bool async,
|
||||
XFile** out_file) = 0;
|
||||
Entry* CreateEntry(std::string name, uint32_t attributes);
|
||||
bool Delete(Entry* entry);
|
||||
bool Delete();
|
||||
void Touch();
|
||||
|
||||
virtual X_STATUS Open(KernelState* kernel_state, uint32_t desired_access,
|
||||
object_ref<XFile>* out_file) = 0;
|
||||
|
||||
virtual bool can_map() const { return false; }
|
||||
virtual std::unique_ptr<MappedMemory> OpenMapped(MappedMemory::Mode mode,
|
||||
@@ -94,6 +131,12 @@ class Entry {
|
||||
protected:
|
||||
Entry(Device* device, Entry* parent, const std::string& path);
|
||||
|
||||
virtual std::unique_ptr<Entry> CreateEntryInternal(std::string name,
|
||||
uint32_t attributes) {
|
||||
return nullptr;
|
||||
}
|
||||
virtual bool DeleteEntryInternal(Entry* entry) { return false; }
|
||||
|
||||
Device* device_;
|
||||
Entry* parent_;
|
||||
std::string path_;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "xenia/base/filesystem.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/string.h"
|
||||
#include "xenia/kernel/objects/xfile.h"
|
||||
|
||||
namespace xe {
|
||||
namespace vfs {
|
||||
@@ -93,5 +94,161 @@ Entry* VirtualFileSystem::ResolvePath(std::string path) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Entry* VirtualFileSystem::ResolveBasePath(std::string path) {
|
||||
auto base_path = xe::find_base_path(path);
|
||||
return ResolvePath(base_path);
|
||||
}
|
||||
|
||||
Entry* VirtualFileSystem::CreatePath(std::string path, uint32_t attributes) {
|
||||
// Create all required directories recursively.
|
||||
auto path_parts = xe::split_path(path);
|
||||
if (path_parts.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto partial_path = path_parts[0];
|
||||
auto partial_entry = ResolvePath(partial_path);
|
||||
if (!partial_entry) {
|
||||
return nullptr;
|
||||
}
|
||||
auto parent_entry = partial_entry;
|
||||
for (size_t i = 1; i < path_parts.size() - 1; ++i) {
|
||||
partial_path = xe::join_paths(partial_path, path_parts[i]);
|
||||
auto child_entry = ResolvePath(partial_path);
|
||||
if (!child_entry) {
|
||||
child_entry =
|
||||
parent_entry->CreateEntry(path_parts[i], kFileAttributeDirectory);
|
||||
}
|
||||
if (!child_entry) {
|
||||
return nullptr;
|
||||
}
|
||||
parent_entry = child_entry;
|
||||
}
|
||||
return parent_entry->CreateEntry(path_parts[path_parts.size() - 1],
|
||||
attributes);
|
||||
}
|
||||
|
||||
bool VirtualFileSystem::DeletePath(std::string path) {
|
||||
auto entry = ResolvePath(path);
|
||||
if (!entry) {
|
||||
return false;
|
||||
}
|
||||
auto parent = entry->parent();
|
||||
if (!parent) {
|
||||
// Can't delete root.
|
||||
return false;
|
||||
}
|
||||
return parent->Delete(entry);
|
||||
}
|
||||
|
||||
X_STATUS VirtualFileSystem::OpenFile(KernelState* kernel_state,
|
||||
std::string path,
|
||||
FileDisposition creation_disposition,
|
||||
uint32_t desired_access,
|
||||
object_ref<XFile>* out_file,
|
||||
FileAction* out_action) {
|
||||
// Cleanup access.
|
||||
if (desired_access & FileAccess::kGenericRead) {
|
||||
desired_access |= FileAccess::kFileReadData;
|
||||
}
|
||||
if (desired_access & FileAccess::kGenericWrite) {
|
||||
desired_access |= FileAccess::kFileWriteData;
|
||||
}
|
||||
if (desired_access & FileAccess::kGenericAll) {
|
||||
desired_access |= FileAccess::kFileReadData | FileAccess::kFileWriteData;
|
||||
}
|
||||
|
||||
// Lookup host device/parent path.
|
||||
// If no device or parent, fail.
|
||||
auto parent_entry = ResolveBasePath(path);
|
||||
if (!parent_entry) {
|
||||
*out_action = FileAction::kDoesNotExist;
|
||||
return X_STATUS_NO_SUCH_FILE;
|
||||
}
|
||||
|
||||
// Check if exists (if we need it to), or that it doesn't (if it shouldn't).
|
||||
auto file_name = xe::find_name_from_path(path);
|
||||
Entry* entry = parent_entry->GetChild(file_name);
|
||||
switch (creation_disposition) {
|
||||
case FileDisposition::kOpen:
|
||||
case FileDisposition::kOverwrite:
|
||||
// Must exist.
|
||||
if (!entry) {
|
||||
*out_action = FileAction::kDoesNotExist;
|
||||
return X_STATUS_NO_SUCH_FILE;
|
||||
}
|
||||
break;
|
||||
case FileDisposition::kCreate:
|
||||
// Must not exist.
|
||||
if (entry) {
|
||||
*out_action = FileAction::kExists;
|
||||
return X_STATUS_OBJECT_NAME_COLLISION;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Either way, ok.
|
||||
break;
|
||||
}
|
||||
|
||||
// Verify permissions.
|
||||
bool wants_write = desired_access & FileAccess::kFileWriteData ||
|
||||
desired_access & FileAccess::kFileAppendData;
|
||||
if (wants_write && parent_entry->is_read_only()) {
|
||||
// Fail if read only device and wants write.
|
||||
// return X_STATUS_ACCESS_DENIED;
|
||||
// TODO(benvanik): figure out why games are opening read-only files with
|
||||
// write modes.
|
||||
assert_always();
|
||||
XELOGW("Attempted to open the file/dir for create/write");
|
||||
desired_access = FileAccess::kGenericRead | FileAccess::kFileReadData;
|
||||
}
|
||||
|
||||
bool created = false;
|
||||
if (!entry) {
|
||||
// Remember that we are creating this new, instead of replacing.
|
||||
created = true;
|
||||
*out_action = FileAction::kCreated;
|
||||
} else {
|
||||
// May need to delete, if it exists.
|
||||
switch (creation_disposition) {
|
||||
case FileDisposition::kSuperscede:
|
||||
// Replace (by delete + recreate).
|
||||
if (!entry->Delete()) {
|
||||
return X_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
entry = nullptr;
|
||||
*out_action = FileAction::kSuperseded;
|
||||
break;
|
||||
case FileDisposition::kOpen:
|
||||
case FileDisposition::kOpenIf:
|
||||
// Normal open.
|
||||
*out_action = FileAction::kOpened;
|
||||
break;
|
||||
case FileDisposition::kOverwrite:
|
||||
case FileDisposition::kOverwriteIf:
|
||||
// Overwrite (we do by delete + recreate).
|
||||
if (!entry->Delete()) {
|
||||
return X_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
entry = nullptr;
|
||||
*out_action = FileAction::kOverwritten;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!entry) {
|
||||
// Create if needed (either new or as a replacement).
|
||||
entry = CreatePath(path, kFileAttributeNormal);
|
||||
if (!entry) {
|
||||
return X_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
|
||||
// Open.
|
||||
auto result = entry->Open(kernel_state, desired_access, out_file);
|
||||
if (XFAILED(result)) {
|
||||
*out_action = FileAction::kDoesNotExist;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace vfs
|
||||
} // namespace xe
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/mutex.h"
|
||||
#include "xenia/kernel/xobject.h"
|
||||
#include "xenia/vfs/device.h"
|
||||
#include "xenia/vfs/entry.h"
|
||||
|
||||
@@ -33,6 +34,15 @@ class VirtualFileSystem {
|
||||
bool UnregisterSymbolicLink(std::string path);
|
||||
|
||||
Entry* ResolvePath(std::string path);
|
||||
Entry* ResolveBasePath(std::string path);
|
||||
|
||||
Entry* CreatePath(std::string path, uint32_t attributes);
|
||||
bool DeletePath(std::string path);
|
||||
|
||||
X_STATUS OpenFile(KernelState* kernel_state, std::string path,
|
||||
FileDisposition creation_disposition,
|
||||
uint32_t desired_access, object_ref<XFile>* out_file,
|
||||
FileAction* out_action);
|
||||
|
||||
private:
|
||||
xe::mutex mutex_;
|
||||
|
||||
Reference in New Issue
Block a user