diff --git a/src/xenia/kernel/info/file.h b/src/xenia/kernel/info/file.h new file mode 100644 index 000000000..9305927bc --- /dev/null +++ b/src/xenia/kernel/info/file.h @@ -0,0 +1,115 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2020 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_KERNEL_INFO_FILE_H_ +#define XENIA_KERNEL_INFO_FILE_H_ + +#include "xenia/xbox.h" + +namespace xe { +namespace kernel { + +// https://github.com/oukiar/vdash/blob/master/vdash/include/kernel.h +enum X_FILE_INFORMATION_CLASS { + XFileDirectoryInformation = 1, + XFileFullDirectoryInformation, + XFileBothDirectoryInformation, + XFileBasicInformation, + XFileStandardInformation, + XFileInternalInformation, + XFileEaInformation, + XFileAccessInformation, + XFileNameInformation, + XFileRenameInformation, + XFileLinkInformation, + XFileNamesInformation, + XFileDispositionInformation, + XFilePositionInformation, + XFileFullEaInformation, + XFileModeInformation, + XFileAlignmentInformation, + XFileAllInformation, + XFileAllocationInformation, + XFileEndOfFileInformation, + XFileAlternateNameInformation, + XFileStreamInformation, + XFileMountPartitionInformation, + XFileMountPartitionsInformation, + XFilePipeRemoteInformation, + XFileSectorInformation, + XFileXctdCompressionInformation, + XFileCompressionInformation, + XFileObjectIdInformation, + XFileCompletionInformation, + XFileMoveClusterInformation, + XFileIoPriorityInformation, + XFileReparsePointInformation, + XFileNetworkOpenInformation, + XFileAttributeTagInformation, + XFileTrackingInformation, + XFileMaximumInformation +}; + +#pragma pack(push, 1) + +// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_file_internal_information +struct X_FILE_INTERNAL_INFORMATION { + be index_number; +}; +static_assert_size(X_FILE_INTERNAL_INFORMATION, 8); + +// 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; +}; +static_assert_size(X_FILE_DISPOSITION_INFORMATION, 1); + +// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_file_position_information +struct X_FILE_POSITION_INFORMATION { + be current_byte_offset; +}; +static_assert_size(X_FILE_POSITION_INFORMATION, 8); + +// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-_file_end_of_file_information +struct X_FILE_END_OF_FILE_INFORMATION { + be end_of_file; +}; +static_assert_size(X_FILE_END_OF_FILE_INFORMATION, 8); + +struct X_FILE_XCTD_COMPRESSION_INFORMATION { + be unknown; +}; +static_assert_size(X_FILE_XCTD_COMPRESSION_INFORMATION, 4); + +// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_file_completion_information +struct X_FILE_COMPLETION_INFORMATION { + be handle; + be key; +}; +static_assert_size(X_FILE_COMPLETION_INFORMATION, 8); + +// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_file_network_open_information +struct X_FILE_NETWORK_OPEN_INFORMATION { + be creation_time; + be last_access_time; + be last_write_time; + be change_time; + be allocation_size; + be end_of_file; // size in bytes + be attributes; + be pad; +}; +static_assert_size(X_FILE_NETWORK_OPEN_INFORMATION, 56); + +#pragma pack(pop) + +} // namespace kernel +} // namespace xe + +#endif // XENIA_KERNEL_INFO_FILE_H_ diff --git a/src/xenia/kernel/info/volume.h b/src/xenia/kernel/info/volume.h new file mode 100644 index 000000000..bbb6ec3ac --- /dev/null +++ b/src/xenia/kernel/info/volume.h @@ -0,0 +1,62 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2020 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_KERNEL_INFO_VOLUME_H_ +#define XENIA_KERNEL_INFO_VOLUME_H_ + +#include "xenia/xbox.h" + +namespace xe { +namespace kernel { + +enum X_FILE_FS_INFORMATION_CLASS { + XFileFsVolumeInformation = 1, + XFileFsSizeInformation = 3, + XFileFsDeviceInformation, + XFileFsAttributeInformation = 5, +}; + +#pragma pack(push, 1) + +// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-_file_fs_volume_information +struct X_FILE_FS_VOLUME_INFORMATION { + be creation_time; + be serial_number; + be label_length; + uint8_t supports_objects; + char label[1]; + uint8_t pad[6]; +}; +static_assert_size(X_FILE_FS_VOLUME_INFORMATION, 24); + +// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-_file_fs_size_information +struct X_FILE_FS_SIZE_INFORMATION { + be total_allocation_units; + be available_allocation_units; + be sectors_per_allocation_unit; + be bytes_per_sector; +}; +static_assert_size(X_FILE_FS_SIZE_INFORMATION, 24); + +// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_file_fs_attribute_information +struct X_FILE_FS_ATTRIBUTE_INFORMATION { + be attributes; + be component_name_max_length; + be name_length; + char name[1]; + uint8_t pad[3]; +}; +static_assert_size(X_FILE_FS_ATTRIBUTE_INFORMATION, 16); + +#pragma pack(pop) + +} // namespace kernel +} // namespace xe + +#endif // XENIA_KERNEL_INFO_VOLUME_H_ diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc index b0c5fdb56..5b4e86807 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc @@ -11,6 +11,7 @@ #include "xenia/base/memory.h" #include "xenia/base/mutex.h" #include "xenia/cpu/processor.h" +#include "xenia/kernel/info/file.h" #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/util/shim_utils.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_private.h" @@ -26,37 +27,6 @@ namespace xe { namespace kernel { namespace xboxkrnl { -// https://msdn.microsoft.com/en-us/library/windows/hardware/ff540287.aspx -struct X_FILE_FS_VOLUME_INFORMATION { - // FILE_FS_VOLUME_INFORMATION - xe::be creation_time; - xe::be serial_number; - xe::be label_length; - xe::be supports_objects; - char label[1]; -}; -static_assert_size(X_FILE_FS_VOLUME_INFORMATION, 24); - -// https://msdn.microsoft.com/en-us/library/windows/hardware/ff540282.aspx -struct X_FILE_FS_SIZE_INFORMATION { - // FILE_FS_SIZE_INFORMATION - xe::be total_allocation_units; - xe::be available_allocation_units; - xe::be sectors_per_allocation_unit; - xe::be bytes_per_sector; -}; -static_assert_size(X_FILE_FS_SIZE_INFORMATION, 24); - -// https://msdn.microsoft.com/en-us/library/windows/hardware/ff540251(v=vs.85).aspx -struct X_FILE_FS_ATTRIBUTE_INFORMATION { - // FILE_FS_ATTRIBUTE_INFORMATION - xe::be attributes; - xe::be maximum_component_name_length; - xe::be fs_name_length; - char fs_name[1]; -}; -static_assert_size(X_FILE_FS_ATTRIBUTE_INFORMATION, 16); - struct CreateOptions { // https://processhacker.sourceforge.io/doc/ntioapi_8h.html static const uint32_t FILE_DIRECTORY_FILE = 0x00000001; @@ -355,207 +325,6 @@ dword_result_t NtRemoveIoCompletion( } DECLARE_XBOXKRNL_EXPORT1(NtRemoveIoCompletion, kFileSystem, kImplemented); -dword_result_t NtSetInformationFile( - dword_t file_handle, pointer_t io_status_block, - lpvoid_t file_info, dword_t length, dword_t file_info_class) { - X_STATUS result = X_STATUS_SUCCESS; - uint32_t info = 0; - - // Grab file. - auto file = kernel_state()->object_table()->LookupObject(file_handle); - if (!file) { - result = X_STATUS_INVALID_HANDLE; - } - - if (XSUCCEEDED(result)) { - switch (file_info_class) { - case XFileDispositionInformation: { - // Used to set deletion flag. Which we don't support. Probably? - info = 0; - bool delete_on_close = - (xe::load_and_swap(file_info)) ? true : false; - XELOGW("NtSetInformationFile ignoring delete on close: {}", - delete_on_close); - break; - } - case XFilePositionInformation: - // struct FILE_POSITION_INFORMATION { - // LARGE_INTEGER CurrentByteOffset; - // }; - assert_true(length == 8); - info = 8; - file->set_position(xe::load_and_swap(file_info)); - break; - case XFileAllocationInformation: - assert_true(length == 8); - info = 8; - XELOGW("NtSetInformationFile ignoring alloc"); - break; - case XFileEndOfFileInformation: { - assert_true(length == 8); - auto eof = xe::load_and_swap(file_info); - result = file->SetLength(eof); - - // Update the files vfs::Entry information - file->entry()->update(); - break; - } - case XFileCompletionInformation: { - // Info contains IO Completion handle and completion key - assert_true(length == 8); - - auto handle = xe::load_and_swap(file_info + 0x0); - auto key = xe::load_and_swap(file_info + 0x4); - auto port = - kernel_state()->object_table()->LookupObject(handle); - if (!port) { - result = X_STATUS_INVALID_HANDLE; - break; - } - - file->RegisterIOCompletionPort(key, port); - break; - } - default: - // Unsupported, for now. - assert_always(); - info = 0; - break; - } - } - - if (io_status_block) { - io_status_block->status = result; - io_status_block->information = info; - } - - return result; -} -DECLARE_XBOXKRNL_EXPORT2(NtSetInformationFile, kFileSystem, kImplemented, - kHighFrequency); - -struct X_IO_STATUS_BLOCK { - union { - xe::be status; - xe::be pointer; - }; - xe::be information; -}; - -dword_result_t NtQueryInformationFile( - dword_t file_handle, pointer_t io_status_block_ptr, - lpvoid_t file_info_ptr, dword_t length, dword_t file_info_class) { - X_STATUS result = X_STATUS_SUCCESS; - uint32_t info = 0; - - // Grab file. - auto file = kernel_state()->object_table()->LookupObject(file_handle); - if (file) { - switch (file_info_class) { - case XFileInternalInformation: - // Internal unique file pointer. Not sure why anyone would want this. - assert_true(length == 8); - info = 8; - // TODO(benvanik): use pointer to fs:: entry? - xe::store_and_swap(file_info_ptr, - xe::memory::hash_combine(0, file->path())); - break; - case XFilePositionInformation: - // struct FILE_POSITION_INFORMATION { - // LARGE_INTEGER CurrentByteOffset; - // }; - assert_true(length == 8); - info = 8; - xe::store_and_swap(file_info_ptr, file->position()); - break; - case XFileNetworkOpenInformation: { - // struct FILE_NETWORK_OPEN_INFORMATION { - // LARGE_INTEGER CreationTime; - // LARGE_INTEGER LastAccessTime; - // LARGE_INTEGER LastWriteTime; - // LARGE_INTEGER ChangeTime; - // LARGE_INTEGER AllocationSize; - // LARGE_INTEGER EndOfFile; - // ULONG FileAttributes; - // ULONG Unknown; - // }; - assert_true(length == 56); - - // Make sure we're working with up-to-date information, just in case the - // file size has changed via something other than NtSetInfoFile - // (eg. seems NtWriteFile might extend the file in some cases) - file->entry()->update(); - - auto file_info = file_info_ptr.as(); - file_info->creation_time = file->entry()->create_timestamp(); - file_info->last_access_time = file->entry()->access_timestamp(); - file_info->last_write_time = file->entry()->write_timestamp(); - file_info->change_time = file->entry()->write_timestamp(); - file_info->allocation_size = file->entry()->allocation_size(); - file_info->end_of_file = file->entry()->size(); - file_info->attributes = file->entry()->attributes(); - info = 56; - break; - } - case XFileXctdCompressionInformation: { - assert_true(length == 4); - XELOGE( - "NtQueryInformationFile(XFileXctdCompressionInformation) " - "unimplemented"); - // This is wrong and puts files into wrong states for games that use - // XctdDecompression. - /* - uint32_t magic; - uint32_t bytes_read; - uint64_t cur_pos = file->position(); - - file->set_position(0); - // FIXME(Triang3l): For now, XFile can be read only to guest buffers - - // this line won't work, implement reading to host buffers if needed. - result = file->Read(&magic, sizeof(magic), 0, &bytes_read); - if (XSUCCEEDED(result)) { - if (bytes_read == sizeof(magic)) { - info = 4; - *file_info_ptr.as*>() = - magic == xe::byte_swap(0x0FF512ED) ? 1 : 0; - } else { - result = X_STATUS_UNSUCCESSFUL; - } - } - file->set_position(cur_pos); - info = 4; - */ - xe::store_and_swap(file_info_ptr, 0); - result = X_STATUS_UNSUCCESSFUL; - info = 0; - } break; - case XFileSectorInformation: - // TODO(benvanik): return sector this file's on. - assert_true(length == 4); - XELOGE("NtQueryInformationFile(XFileSectorInformation) unimplemented"); - result = X_STATUS_UNSUCCESSFUL; - info = 0; - break; - default: - // Unsupported, for now. - assert_always(); - info = 0; - result = X_STATUS_UNSUCCESSFUL; - break; - } - } else { - result = X_STATUS_INVALID_HANDLE; - } - - if (io_status_block_ptr) { - io_status_block_ptr->status = result; - io_status_block_ptr->information = info; // # bytes written - } - - return result; -} -DECLARE_XBOXKRNL_EXPORT1(NtQueryInformationFile, kFileSystem, kImplemented); - dword_result_t NtQueryFullAttributesFile( pointer_t obj_attribs, pointer_t file_info) { @@ -592,82 +361,6 @@ dword_result_t NtQueryFullAttributesFile( } DECLARE_XBOXKRNL_EXPORT1(NtQueryFullAttributesFile, kFileSystem, kImplemented); -dword_result_t NtQueryVolumeInformationFile( - dword_t file_handle, pointer_t io_status_block_ptr, - lpvoid_t fs_info_ptr, dword_t length, dword_t fs_info_class) { - X_STATUS result = X_STATUS_SUCCESS; - uint32_t info = 0; - - // Grab file. - auto file = kernel_state()->object_table()->LookupObject(file_handle); - if (file) { - switch (fs_info_class) { - case 1: { // FileFsVolumeInformation - // TODO(gibbed): actual value - std::string name = "test"; - X_FILE_FS_VOLUME_INFORMATION* volume_info = - fs_info_ptr.as(); - volume_info->creation_time = 0; - volume_info->serial_number = 12345678; - volume_info->supports_objects = 0; - volume_info->label_length = uint32_t(name.size()); - std::memcpy(volume_info->label, name.data(), name.size()); - info = length; - break; - } - case 3: { // FileFsSizeInformation - X_FILE_FS_SIZE_INFORMATION* fs_size_info = - fs_info_ptr.as(); - fs_size_info->total_allocation_units = - file->device()->total_allocation_units(); - fs_size_info->available_allocation_units = - file->device()->available_allocation_units(); - fs_size_info->sectors_per_allocation_unit = - file->device()->sectors_per_allocation_unit(); - fs_size_info->bytes_per_sector = file->device()->bytes_per_sector(); - info = length; - break; - } - case 5: { // FileFsAttributeInformation - // TODO(gibbed): actual value - std::string name = "test"; - X_FILE_FS_ATTRIBUTE_INFORMATION* fs_attribute_info = - fs_info_ptr.as(); - fs_attribute_info->attributes = 0; - fs_attribute_info->maximum_component_name_length = 255; - fs_attribute_info->fs_name_length = uint32_t(name.size()); - std::memcpy(fs_attribute_info->fs_name, name.data(), name.size()); - info = length; - break; - } - case 2: // FileFsLabelInformation - case 4: // FileFsDeviceInformation - case 6: // FileFsControlInformation - case 7: // FileFsFullSizeInformation - case 8: // FileFsObjectIdInformation - default: - // Unsupported, for now. - assert_always(); - info = 0; - break; - } - } else { - result = X_STATUS_NO_SUCH_FILE; - } - - if (XFAILED(result)) { - info = 0; - } - if (io_status_block_ptr) { - io_status_block_ptr->status = result; - io_status_block_ptr->information = info; - } - - return result; -} -DECLARE_XBOXKRNL_EXPORT1(NtQueryVolumeInformationFile, kFileSystem, - kImplemented); - dword_result_t NtQueryDirectoryFile( dword_t file_handle, dword_t event_handle, function_t apc_routine, lpvoid_t apc_context, pointer_t io_status_block, diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_io_info.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_io_info.cc new file mode 100644 index 000000000..0add7390a --- /dev/null +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_io_info.cc @@ -0,0 +1,358 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2020 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/base/logging.h" +#include "xenia/base/memory.h" +#include "xenia/base/mutex.h" +#include "xenia/cpu/processor.h" +#include "xenia/kernel/info/file.h" +#include "xenia/kernel/info/volume.h" +#include "xenia/kernel/kernel_state.h" +#include "xenia/kernel/util/shim_utils.h" +#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h" +#include "xenia/kernel/xevent.h" +#include "xenia/kernel/xfile.h" +#include "xenia/kernel/xiocompletion.h" +#include "xenia/kernel/xsymboliclink.h" +#include "xenia/kernel/xthread.h" +#include "xenia/vfs/device.h" +#include "xenia/xbox.h" + +namespace xe { +namespace kernel { +namespace xboxkrnl { + +uint32_t GetQueryFileInfoMinimumLength(uint32_t info_class) { + switch (info_class) { + case XFileInternalInformation: + return sizeof(X_FILE_INTERNAL_INFORMATION); + case XFilePositionInformation: + return sizeof(X_FILE_POSITION_INFORMATION); + case XFileXctdCompressionInformation: + return sizeof(X_FILE_XCTD_COMPRESSION_INFORMATION); + case XFileNetworkOpenInformation: + return sizeof(X_FILE_NETWORK_OPEN_INFORMATION); + // TODO(gibbed): structures to get the size of. + case XFileModeInformation: + case XFileAlignmentInformation: + case XFileSectorInformation: + case XFileIoPriorityInformation: + return 4; + case XFileNameInformation: + case XFileAllocationInformation: + return 8; + case XFileBasicInformation: + return 40; + default: + return 0; + } +} + +dword_result_t NtQueryInformationFile( + dword_t file_handle, pointer_t io_status_block_ptr, + lpvoid_t info_ptr, dword_t info_length, dword_t info_class) { + uint32_t minimum_length = GetQueryFileInfoMinimumLength(info_class); + if (!minimum_length) { + return X_STATUS_INVALID_INFO_CLASS; + } + + if (info_length < minimum_length) { + return X_STATUS_INFO_LENGTH_MISMATCH; + } + + auto file = kernel_state()->object_table()->LookupObject(file_handle); + if (!file) { + return X_STATUS_INVALID_HANDLE; + } + + info_ptr.Zero(info_length); + + X_STATUS status = X_STATUS_SUCCESS; + uint32_t out_length; + + switch (info_class) { + case XFileInternalInformation: { + // Internal unique file pointer. Not sure why anyone would want this. + // TODO(benvanik): use pointer to fs::entry? + auto info = info_ptr.as(); + info->index_number = xe::memory::hash_combine(0, file->path()); + out_length = sizeof(*info); + break; + } + case XFilePositionInformation: { + auto info = info_ptr.as(); + info->current_byte_offset = file->position(); + out_length = sizeof(*info); + break; + } + case XFileSectorInformation: { + // TODO(benvanik): return sector this file's on. + XELOGE("NtQueryInformationFile(XFileSectorInformation) unimplemented"); + status = X_STATUS_INVALID_PARAMETER; + out_length = 0; + break; + } + case XFileXctdCompressionInformation: { + XELOGE( + "NtQueryInformationFile(XFileXctdCompressionInformation) " + "unimplemented"); + // Files that are XCTD compressed begin with the magic 0x0FF512ED but we + // shouldn't detect this that way. There's probably a flag somewhere + // (attributes?) that defines if it's compressed or not. + status = X_STATUS_INVALID_PARAMETER; + out_length = 0; + break; + }; + case XFileNetworkOpenInformation: { + // Make sure we're working with up-to-date information, just in case the + // file size has changed via something other than NtSetInfoFile + // (eg. seems NtWriteFile might extend the file in some cases) + file->entry()->update(); + + auto info = info_ptr.as(); + info->creation_time = file->entry()->create_timestamp(); + info->last_access_time = file->entry()->access_timestamp(); + info->last_write_time = file->entry()->write_timestamp(); + info->change_time = file->entry()->write_timestamp(); + info->allocation_size = file->entry()->allocation_size(); + info->end_of_file = file->entry()->size(); + info->attributes = file->entry()->attributes(); + out_length = sizeof(*info); + break; + } + default: { + // Unsupported, for now. + assert_always(); + status = X_STATUS_INVALID_PARAMETER; + out_length = 0; + break; + } + } + + if (io_status_block_ptr) { + io_status_block_ptr->status = status; + io_status_block_ptr->information = out_length; + } + + return status; +} +DECLARE_XBOXKRNL_EXPORT1(NtQueryInformationFile, kFileSystem, kImplemented); + +uint32_t GetSetFileInfoMinimumLength(uint32_t info_class) { + switch (info_class) { + case XFileDispositionInformation: + return sizeof(X_FILE_DISPOSITION_INFORMATION); + case XFilePositionInformation: + return sizeof(X_FILE_POSITION_INFORMATION); + case XFileCompletionInformation: + return sizeof(X_FILE_COMPLETION_INFORMATION); + // TODO(gibbed): structures to get the size of. + case XFileModeInformation: + case XFileIoPriorityInformation: + return 4; + case XFileAllocationInformation: + case XFileEndOfFileInformation: + case XFileMountPartitionInformation: + return 8; + case XFileRenameInformation: + case XFileLinkInformation: + return 16; + case XFileBasicInformation: + return 40; + case XFileMountPartitionsInformation: + return 152; + default: + return 0; + } +} + +dword_result_t NtSetInformationFile( + dword_t file_handle, pointer_t io_status_block, + lpvoid_t info_ptr, dword_t info_length, dword_t info_class) { + uint32_t minimum_length = GetSetFileInfoMinimumLength(info_class); + if (!minimum_length) { + return X_STATUS_INVALID_INFO_CLASS; + } + + if (info_length < minimum_length) { + return X_STATUS_INFO_LENGTH_MISMATCH; + } + + auto file = kernel_state()->object_table()->LookupObject(file_handle); + if (!file) { + return X_STATUS_INVALID_HANDLE; + } + + X_STATUS result = X_STATUS_SUCCESS; + uint32_t out_length; + + switch (info_class) { + case XFileDispositionInformation: { + // Used to set deletion flag. Which we don't support. Probably? + auto info = info_ptr.as(); + bool delete_on_close = info->delete_file ? true : false; + out_length = 0; + XELOGW("NtSetInformationFile ignoring delete on close: {}", + delete_on_close); + break; + } + case XFilePositionInformation: { + auto info = info_ptr.as(); + file->set_position(info->current_byte_offset); + out_length = sizeof(*info); + break; + } + case XFileAllocationInformation: { + XELOGW("NtSetInformationFile ignoring alloc"); + out_length = 8; + break; + } + case XFileEndOfFileInformation: { + auto info = info_ptr.as(); + result = file->SetLength(info->end_of_file); + out_length = sizeof(*info); + + // Update the files vfs::Entry information + file->entry()->update(); + break; + } + case XFileCompletionInformation: { + // Info contains IO Completion handle and completion key + auto info = info_ptr.as(); + auto handle = uint32_t(info->handle); + auto key = uint32_t(info->key); + out_length = sizeof(*info); + auto port = + kernel_state()->object_table()->LookupObject(handle); + if (!port) { + result = X_STATUS_INVALID_HANDLE; + } else { + file->RegisterIOCompletionPort(key, port); + } + break; + } + default: + // Unsupported, for now. + assert_always(); + out_length = 0; + break; + } + + if (io_status_block) { + io_status_block->status = result; + io_status_block->information = out_length; + } + + return result; +} +DECLARE_XBOXKRNL_EXPORT2(NtSetInformationFile, kFileSystem, kImplemented, + kHighFrequency); + +uint32_t GetQueryVolumeInfoMinimumLength(uint32_t info_class) { + switch (info_class) { + case XFileFsVolumeInformation: + return sizeof(X_FILE_FS_VOLUME_INFORMATION); + case XFileFsSizeInformation: + return sizeof(X_FILE_FS_SIZE_INFORMATION); + case XFileFsAttributeInformation: + return sizeof(X_FILE_FS_ATTRIBUTE_INFORMATION); + // TODO(gibbed): structures to get the size of. + case XFileFsDeviceInformation: + return 8; + default: + return 0; + } +} + +dword_result_t NtQueryVolumeInformationFile( + dword_t file_handle, pointer_t io_status_block_ptr, + lpvoid_t info_ptr, dword_t info_length, dword_t info_class) { + uint32_t minimum_length = GetQueryVolumeInfoMinimumLength(info_class); + if (!minimum_length) { + return X_STATUS_INVALID_INFO_CLASS; + } + + if (info_length < minimum_length) { + return X_STATUS_INFO_LENGTH_MISMATCH; + } + + auto file = kernel_state()->object_table()->LookupObject(file_handle); + if (!file) { + return X_STATUS_INVALID_HANDLE; + } + + info_ptr.Zero(info_length); + + X_STATUS status = X_STATUS_SUCCESS; + uint32_t out_length; + + switch (info_class) { + case XFileFsVolumeInformation: { + auto info = info_ptr.as(); + info->creation_time = 0; + info->serial_number = 0; // set for FATX, but we don't do that currently + info->supports_objects = 0; + info->label_length = 0; + out_length = offsetof(X_FILE_FS_VOLUME_INFORMATION, label); + break; + } + case XFileFsSizeInformation: { + auto device = file->device(); + auto info = info_ptr.as(); + info->total_allocation_units = device->total_allocation_units(); + info->available_allocation_units = device->available_allocation_units(); + info->sectors_per_allocation_unit = device->sectors_per_allocation_unit(); + info->bytes_per_sector = device->bytes_per_sector(); + // TODO(gibbed): sanity check, XCTD userland code seems to require this. + assert_true(info->bytes_per_sector == 0x200); + out_length = sizeof(*info); + break; + } + case XFileFsAttributeInformation: { + auto device = file->device(); + const auto& name = device->name(); + auto info = info_ptr.as(); + info->attributes = device->attributes(); + info->component_name_max_length = device->component_name_max_length(); + info->name_length = uint32_t(name.size()); + if (info_length >= 12 + name.size()) { + std::memcpy(info->name, name.data(), name.size()); + out_length = + offsetof(X_FILE_FS_ATTRIBUTE_INFORMATION, name) + info->name_length; + } else { + status = X_STATUS_BUFFER_OVERFLOW; + out_length = offsetof(X_FILE_FS_ATTRIBUTE_INFORMATION, name); + } + break; + } + case XFileFsDeviceInformation: + default: { + // Unsupported, for now. + assert_always(); + out_length = 0; + break; + } + } + + if (io_status_block_ptr) { + io_status_block_ptr->status = status; + io_status_block_ptr->information = out_length; + } + + return status; +} +DECLARE_XBOXKRNL_EXPORT1(NtQueryVolumeInformationFile, kFileSystem, + kImplemented); + +void RegisterIoInfoExports(xe::cpu::ExportResolver* export_resolver, + KernelState* kernel_state) {} + +} // namespace xboxkrnl +} // namespace kernel +} // namespace xe diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_module.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_module.cc index 1eb89a816..20aef4564 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_module.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_module.cc @@ -91,6 +91,7 @@ XboxkrnlModule::XboxkrnlModule(Emulator* emulator, KernelState* kernel_state) RegisterHalExports(export_resolver_, kernel_state_); RegisterHidExports(export_resolver_, kernel_state_); RegisterIoExports(export_resolver_, kernel_state_); + RegisterIoInfoExports(export_resolver_, kernel_state_); RegisterMemoryExports(export_resolver_, kernel_state_); RegisterMiscExports(export_resolver_, kernel_state_); RegisterModuleExports(export_resolver_, kernel_state_); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_private.h b/src/xenia/kernel/xboxkrnl/xboxkrnl_private.h index 71fb12223..d1f081518 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_private.h +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_private.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * + * Copyright 2020 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -32,6 +32,7 @@ DECLARE_REGISTER_EXPORTS(Error); DECLARE_REGISTER_EXPORTS(Hal); DECLARE_REGISTER_EXPORTS(Hid); DECLARE_REGISTER_EXPORTS(Io); +DECLARE_REGISTER_EXPORTS(IoInfo); DECLARE_REGISTER_EXPORTS(Memory); DECLARE_REGISTER_EXPORTS(Misc); DECLARE_REGISTER_EXPORTS(Module); diff --git a/src/xenia/kernel/xfile.h b/src/xenia/kernel/xfile.h index 5da2d587b..d7e668ce7 100644 --- a/src/xenia/kernel/xfile.h +++ b/src/xenia/kernel/xfile.h @@ -23,58 +23,55 @@ namespace xe { namespace kernel { -// https://msdn.microsoft.com/en-us/library/windows/hardware/ff545822.aspx -struct X_FILE_NETWORK_OPEN_INFORMATION { - xe::be creation_time; - xe::be last_access_time; - xe::be last_write_time; - xe::be change_time; - xe::be allocation_size; - xe::be end_of_file; // size in bytes - xe::be attributes; - xe::be pad; +// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_io_status_block +struct X_IO_STATUS_BLOCK { + union { + be status; + be pointer; + }; + be information; }; -// https://msdn.microsoft.com/en-us/library/windows/hardware/ff540248.aspx +// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_file_directory_information class X_FILE_DIRECTORY_INFORMATION { public: // FILE_DIRECTORY_INFORMATION - xe::be next_entry_offset; // 0x0 - xe::be file_index; // 0x4 - xe::be creation_time; // 0x8 - xe::be last_access_time; // 0x10 - xe::be last_write_time; // 0x18 - xe::be change_time; // 0x20 - xe::be end_of_file; // 0x28 size in bytes - xe::be allocation_size; // 0x30 - xe::be attributes; // 0x38 X_FILE_ATTRIBUTES - xe::be file_name_length; // 0x3C - char file_name[1]; // 0x40 + be next_entry_offset; // 0x0 + be file_index; // 0x4 + be creation_time; // 0x8 + be last_access_time; // 0x10 + be last_write_time; // 0x18 + be change_time; // 0x20 + be end_of_file; // 0x28 size in bytes + be allocation_size; // 0x30 + be attributes; // 0x38 X_FILE_ATTRIBUTES + be file_name_length; // 0x3C + char file_name[1]; // 0x40 void Write(uint8_t* base, uint32_t p) { uint8_t* dst = base + p; uint8_t* src = reinterpret_cast(this); - X_FILE_DIRECTORY_INFORMATION* info; + X_FILE_DIRECTORY_INFORMATION* right; do { - info = reinterpret_cast(src); - xe::store_and_swap(dst, info->next_entry_offset); - xe::store_and_swap(dst + 4, info->file_index); - xe::store_and_swap(dst + 8, info->creation_time); - xe::store_and_swap(dst + 16, info->last_access_time); - xe::store_and_swap(dst + 24, info->last_write_time); - xe::store_and_swap(dst + 32, info->change_time); - xe::store_and_swap(dst + 40, info->end_of_file); - xe::store_and_swap(dst + 48, info->allocation_size); - xe::store_and_swap(dst + 56, info->attributes); - xe::store_and_swap(dst + 60, info->file_name_length); - memcpy(dst + 64, info->file_name, info->file_name_length); + auto left = reinterpret_cast(dst); + right = reinterpret_cast(src); + left->next_entry_offset = right->next_entry_offset; + left->file_index = right->file_index; + left->creation_time = right->creation_time; + left->last_access_time = right->last_access_time; + left->last_write_time = right->last_write_time; + left->change_time = right->change_time; + left->end_of_file = right->end_of_file; + left->allocation_size = right->allocation_size; + left->attributes = right->attributes; + left->file_name_length = right->file_name_length; + std::memcpy(left->file_name, right->file_name, right->file_name_length); - dst += info->next_entry_offset; - src += info->next_entry_offset; - } while (info->next_entry_offset != 0); + dst += right->next_entry_offset; + src += right->next_entry_offset; + } while (right->next_entry_offset != 0); } }; -static_assert_size(X_FILE_DIRECTORY_INFORMATION, 72); class XFile : public XObject { public: diff --git a/src/xenia/vfs/device.h b/src/xenia/vfs/device.h index de4ca9879..6201bd09e 100644 --- a/src/xenia/vfs/device.h +++ b/src/xenia/vfs/device.h @@ -33,6 +33,10 @@ class Device { virtual void Dump(StringBuffer* string_buffer) = 0; virtual Entry* ResolvePath(const std::string_view path) = 0; + virtual const std::string& name() const = 0; + virtual uint32_t attributes() const = 0; + virtual uint32_t component_name_max_length() const = 0; + virtual uint32_t total_allocation_units() const = 0; virtual uint32_t available_allocation_units() const = 0; virtual uint32_t sectors_per_allocation_unit() const = 0; diff --git a/src/xenia/vfs/devices/disc_image_device.cc b/src/xenia/vfs/devices/disc_image_device.cc index 0675e5990..d5ae0c15f 100644 --- a/src/xenia/vfs/devices/disc_image_device.cc +++ b/src/xenia/vfs/devices/disc_image_device.cc @@ -20,7 +20,7 @@ const size_t kXESectorSize = 2048; DiscImageDevice::DiscImageDevice(const std::string_view mount_path, const std::filesystem::path& host_path) - : Device(mount_path), host_path_(host_path) {} + : Device(mount_path), name_("GDFX"), host_path_(host_path) {} DiscImageDevice::~DiscImageDevice() = default; diff --git a/src/xenia/vfs/devices/disc_image_device.h b/src/xenia/vfs/devices/disc_image_device.h index 6b756b19a..cbbfa5024 100644 --- a/src/xenia/vfs/devices/disc_image_device.h +++ b/src/xenia/vfs/devices/disc_image_device.h @@ -31,13 +31,17 @@ class DiscImageDevice : public Device { void Dump(StringBuffer* string_buffer) override; Entry* ResolvePath(const std::string_view path) override; + const std::string& name() const override { return name_; } + uint32_t attributes() const override { return 0; } + uint32_t component_name_max_length() const override { return 255; } + uint32_t total_allocation_units() const override { return uint32_t(mmap_->size() / sectors_per_allocation_unit() / bytes_per_sector()); } uint32_t available_allocation_units() const override { return 0; } uint32_t sectors_per_allocation_unit() const override { return 1; } - uint32_t bytes_per_sector() const override { return 2 * 1024; } + uint32_t bytes_per_sector() const override { return 0x200; } private: enum class Error { @@ -48,6 +52,7 @@ class DiscImageDevice : public Device { kErrorDamagedFile = -31, }; + std::string name_; std::filesystem::path host_path_; std::unique_ptr root_entry_; std::unique_ptr mmap_; diff --git a/src/xenia/vfs/devices/host_path_device.cc b/src/xenia/vfs/devices/host_path_device.cc index 5dcf3d21f..33aee1b7a 100644 --- a/src/xenia/vfs/devices/host_path_device.cc +++ b/src/xenia/vfs/devices/host_path_device.cc @@ -21,7 +21,10 @@ namespace vfs { HostPathDevice::HostPathDevice(const std::string_view mount_path, const std::filesystem::path& host_path, bool read_only) - : Device(mount_path), host_path_(host_path), read_only_(read_only) {} + : Device(mount_path), + name_("STFS"), + host_path_(host_path), + read_only_(read_only) {} HostPathDevice::~HostPathDevice() = default; diff --git a/src/xenia/vfs/devices/host_path_device.h b/src/xenia/vfs/devices/host_path_device.h index 88a5578f1..fa8482ce2 100644 --- a/src/xenia/vfs/devices/host_path_device.h +++ b/src/xenia/vfs/devices/host_path_device.h @@ -31,14 +31,19 @@ class HostPathDevice : public Device { bool is_read_only() const override { return read_only_; } + const std::string& name() const override { return name_; } + uint32_t attributes() const override { return 0; } + uint32_t component_name_max_length() const override { return 40; } + uint32_t total_allocation_units() const override { return 128 * 1024; } uint32_t available_allocation_units() const override { return 128 * 1024; } uint32_t sectors_per_allocation_unit() const override { return 1; } - uint32_t bytes_per_sector() const override { return 2 * 1024; } + uint32_t bytes_per_sector() const override { return 0x200; } private: void PopulateEntry(HostPathEntry* parent_entry); + std::string name_; std::filesystem::path host_path_; std::unique_ptr root_entry_; bool read_only_; diff --git a/src/xenia/vfs/devices/stfs_container_device.cc b/src/xenia/vfs/devices/stfs_container_device.cc index 8da4b303f..22499e057 100644 --- a/src/xenia/vfs/devices/stfs_container_device.cc +++ b/src/xenia/vfs/devices/stfs_container_device.cc @@ -55,7 +55,15 @@ uint64_t decode_fat_timestamp(uint32_t date, uint32_t time) { StfsContainerDevice::StfsContainerDevice(const std::string_view mount_path, const std::filesystem::path& host_path) - : Device(mount_path), host_path_(host_path) {} + : Device(mount_path), + name_("STFS"), + host_path_(host_path), + mmap_total_size_(), + base_offset_(), + magic_offset_(), + package_type_(), + header_(), + table_size_shift_() {} StfsContainerDevice::~StfsContainerDevice() = default; @@ -370,7 +378,7 @@ StfsContainerDevice::Error StfsContainerDevice::ReadEntrySVOD( // Entry is a file entry->attributes_ = kFileAttributeNormal | kFileAttributeReadOnly; entry->size_ = length; - entry->allocation_size_ = xe::round_up(length, bytes_per_sector()); + entry->allocation_size_ = xe::round_up(length, kSectorSize); entry->data_offset_ = data_address; entry->data_size_ = length; entry->block_ = data_block; @@ -536,7 +544,7 @@ StfsContainerDevice::Error StfsContainerDevice::ReadSTFS() { entry->data_size_ = file_size; } entry->size_ = file_size; - entry->allocation_size_ = xe::round_up(file_size, bytes_per_sector()); + entry->allocation_size_ = xe::round_up(file_size, kSectorSize); entry->create_timestamp_ = decode_fat_timestamp(update_date, update_time); entry->access_timestamp_ = decode_fat_timestamp(access_date, access_time); @@ -618,7 +626,7 @@ StfsContainerDevice::BlockHash StfsContainerDevice::GetBlockHash( // table and then subtract one sector to land on the table itself. size_t hash_offset = BlockToOffsetSTFS( xe::round_up(block_index + 1, kSTFSHashSpacing) - kSTFSHashSpacing); - hash_offset -= bytes_per_sector(); + hash_offset -= kSectorSize; const uint8_t* hash_data = map_ptr + hash_offset; // table_index += table_offset - (1 << table_size_shift_); diff --git a/src/xenia/vfs/devices/stfs_container_device.h b/src/xenia/vfs/devices/stfs_container_device.h index 9d0bba0d8..f70c78b08 100644 --- a/src/xenia/vfs/devices/stfs_container_device.h +++ b/src/xenia/vfs/devices/stfs_container_device.h @@ -173,15 +173,21 @@ class StfsContainerDevice : public Device { void Dump(StringBuffer* string_buffer) override; Entry* ResolvePath(const std::string_view path) override; + const std::string& name() const override { return name_; } + uint32_t attributes() const override { return 0; } + uint32_t component_name_max_length() const override { return 40; } + uint32_t total_allocation_units() const override { return uint32_t(mmap_total_size_ / sectors_per_allocation_unit() / bytes_per_sector()); } uint32_t available_allocation_units() const override { return 0; } - uint32_t sectors_per_allocation_unit() const override { return 1; } - uint32_t bytes_per_sector() const override { return 4 * 1024; } + uint32_t sectors_per_allocation_unit() const override { return 8; } + uint32_t bytes_per_sector() const override { return 0x200; } private: + const uint32_t kSectorSize = 0x1000; + enum class Error { kSuccess = 0, kErrorOutOfMemory = -1, @@ -215,6 +221,7 @@ class StfsContainerDevice : public Device { BlockHash GetBlockHash(const uint8_t* map_ptr, uint32_t block_index, uint32_t table_offset); + std::string name_; std::filesystem::path host_path_; std::map> mmap_; size_t mmap_total_size_; diff --git a/src/xenia/vfs/devices/stfs_container_entry.cc b/src/xenia/vfs/devices/stfs_container_entry.cc index 023af8fec..c35f8b336 100644 --- a/src/xenia/vfs/devices/stfs_container_entry.cc +++ b/src/xenia/vfs/devices/stfs_container_entry.cc @@ -22,7 +22,8 @@ StfsContainerEntry::StfsContainerEntry(Device* device, Entry* parent, : Entry(device, parent, path), mmap_(mmap), data_offset_(0), - data_size_(0) {} + data_size_(0), + block_(0) {} StfsContainerEntry::~StfsContainerEntry() = default; diff --git a/src/xenia/xbox.h b/src/xenia/xbox.h index 90bc1a328..4556a2854 100644 --- a/src/xenia/xbox.h +++ b/src/xenia/xbox.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * + * Copyright 2020 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -163,47 +163,6 @@ enum X_FILE_ATTRIBUTES : uint32_t { X_FILE_ATTRIBUTE_ENCRYPTED = 0x4000, }; -// https://github.com/oukiar/vdash/blob/master/vdash/include/kernel.h -enum X_FILE_INFORMATION_CLASS { - XFileDirectoryInformation = 1, - XFileFullDirectoryInformation, - XFileBothDirectoryInformation, - XFileBasicInformation, - XFileStandardInformation, - XFileInternalInformation, - XFileEaInformation, - XFileAccessInformation, - XFileNameInformation, - XFileRenameInformation, - XFileLinkInformation, - XFileNamesInformation, - XFileDispositionInformation, - XFilePositionInformation, - XFileFullEaInformation, - XFileModeInformation, - XFileAlignmentInformation, - XFileAllInformation, - XFileAllocationInformation, - XFileEndOfFileInformation, - XFileAlternateNameInformation, - XFileStreamInformation, - XFileMountPartitionInformation, - XFileMountPartitionsInformation, - XFilePipeRemoteInformation, - XFileSectorInformation, - XFileXctdCompressionInformation, - XFileCompressionInformation, - XFileObjectIdInformation, - XFileCompletionInformation, - XFileMoveClusterInformation, - XFileIoPriorityInformation, - XFileReparsePointInformation, - XFileNetworkOpenInformation, - XFileAttributeTagInformation, - XFileTrackingInformation, - XFileMaximumInformation -}; - // Known as XOVERLAPPED to 360 code. struct XAM_OVERLAPPED { xe::be result; // 0x0