Services/AM: Support using FS subfiles with the CIA-related service functions.

FS subfiles are created with File::OpenSubFile, they have a start offset that must be added to all read/write operations.

The implementation in this commit is done using a new FileBackend that wraps the FS::File along with the start offset.
This commit is contained in:
Subv 2018-10-02 08:52:00 -05:00
parent fb720c00b7
commit 5165b63512
3 changed files with 73 additions and 22 deletions

View file

@ -1049,7 +1049,39 @@ void Module::Interface::EndImportProgram(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
} }
ResultVal<std::shared_ptr<Service::FS::File>> GetFileFromSession( /// Wraps all File operations to allow adding an offset to them.
class AMFileWrapper : public FileSys::FileBackend {
public:
AMFileWrapper(std::shared_ptr<Service::FS::File> file, std::size_t offset, std::size_t size)
: file(std::move(file)), file_offset(offset), file_size(size) {}
ResultVal<std::size_t> Read(u64 offset, std::size_t length, u8* buffer) const override {
return file->backend->Read(offset + file_offset, length, buffer);
}
ResultVal<std::size_t> Write(u64 offset, std::size_t length, bool flush,
const u8* buffer) override {
return file->backend->Write(offset + file_offset, length, flush, buffer);
}
u64 GetSize() const override {
return file_size;
}
bool SetSize(u64 size) const override {
return false;
}
bool Close() const override {
return false;
}
void Flush() const override {}
private:
std::shared_ptr<Service::FS::File> file;
std::size_t file_offset;
std::size_t file_size;
};
ResultVal<std::unique_ptr<AMFileWrapper>> GetFileFromSession(
Kernel::SharedPtr<Kernel::ClientSession> file_session) { Kernel::SharedPtr<Kernel::ClientSession> file_session) {
// Step up the chain from ClientSession->ServerSession and then // Step up the chain from ClientSession->ServerSession and then
// cast to File. For AM on 3DS, invalid handles actually hang the system. // cast to File. For AM on 3DS, invalid handles actually hang the system.
@ -1069,8 +1101,13 @@ ResultVal<std::shared_ptr<Service::FS::File>> GetFileFromSession(
auto file = std::dynamic_pointer_cast<Service::FS::File>(server->hle_handler); auto file = std::dynamic_pointer_cast<Service::FS::File>(server->hle_handler);
// TODO(shinyquagsire23): This requires RTTI, use service calls directly instead? // TODO(shinyquagsire23): This requires RTTI, use service calls directly instead?
if (file != nullptr) if (file != nullptr) {
return MakeResult<std::shared_ptr<Service::FS::File>>(file); // Grab the session file offset in case we were given a subfile opened with
// File::OpenSubFile
std::size_t offset = file->GetSessionFileOffset(server);
std::size_t size = file->GetSessionFileSize(server);
return MakeResult(std::make_unique<AMFileWrapper>(file, offset, size));
}
LOG_ERROR(Service_AM, "Failed to cast handle to FSFile!"); LOG_ERROR(Service_AM, "Failed to cast handle to FSFile!");
return Kernel::ERR_INVALID_HANDLE; return Kernel::ERR_INVALID_HANDLE;
@ -1094,9 +1131,8 @@ void Module::Interface::GetProgramInfoFromCia(Kernel::HLERequestContext& ctx) {
return; return;
} }
auto file = file_res.Unwrap();
FileSys::CIAContainer container; FileSys::CIAContainer container;
if (container.Load(*file->backend) != Loader::ResultStatus::Success) { if (container.Load(*file_res.Unwrap()) != Loader::ResultStatus::Success) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultCode(ErrCodes::InvalidCIAHeader, ErrorModule::AM, rb.Push(ResultCode(ErrCodes::InvalidCIAHeader, ErrorModule::AM,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent)); ErrorSummary::InvalidArgument, ErrorLevel::Permanent));
@ -1135,9 +1171,9 @@ void Module::Interface::GetSystemMenuDataFromCia(Kernel::HLERequestContext& ctx)
std::size_t output_buffer_size = std::min(output_buffer.GetSize(), sizeof(Loader::SMDH)); std::size_t output_buffer_size = std::min(output_buffer.GetSize(), sizeof(Loader::SMDH));
auto file = file_res.Unwrap(); auto file = std::move(file_res.Unwrap());
FileSys::CIAContainer container; FileSys::CIAContainer container;
if (container.Load(*file->backend) != Loader::ResultStatus::Success) { if (container.Load(*file) != Loader::ResultStatus::Success) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(ResultCode(ErrCodes::InvalidCIAHeader, ErrorModule::AM, rb.Push(ResultCode(ErrCodes::InvalidCIAHeader, ErrorModule::AM,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent)); ErrorSummary::InvalidArgument, ErrorLevel::Permanent));
@ -1147,8 +1183,8 @@ void Module::Interface::GetSystemMenuDataFromCia(Kernel::HLERequestContext& ctx)
std::vector<u8> temp(output_buffer_size); std::vector<u8> temp(output_buffer_size);
// Read from the Meta offset + 0x400 for the 0x36C0-large SMDH // Read from the Meta offset + 0x400 for the 0x36C0-large SMDH
auto read_result = file->backend->Read( auto read_result = file->Read(container.GetMetadataOffset() + FileSys::CIA_METADATA_SIZE,
container.GetMetadataOffset() + FileSys::CIA_METADATA_SIZE, temp.size(), temp.data()); temp.size(), temp.data());
if (read_result.Failed() || *read_result != temp.size()) { if (read_result.Failed() || *read_result != temp.size()) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(ResultCode(ErrCodes::InvalidCIAHeader, ErrorModule::AM, rb.Push(ResultCode(ErrCodes::InvalidCIAHeader, ErrorModule::AM,
@ -1175,9 +1211,8 @@ void Module::Interface::GetDependencyListFromCia(Kernel::HLERequestContext& ctx)
return; return;
} }
auto file = file_res.Unwrap();
FileSys::CIAContainer container; FileSys::CIAContainer container;
if (container.Load(*file->backend) != Loader::ResultStatus::Success) { if (container.Load(*file_res.Unwrap()) != Loader::ResultStatus::Success) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultCode(ErrCodes::InvalidCIAHeader, ErrorModule::AM, rb.Push(ResultCode(ErrCodes::InvalidCIAHeader, ErrorModule::AM,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent)); ErrorSummary::InvalidArgument, ErrorLevel::Permanent));
@ -1203,9 +1238,8 @@ void Module::Interface::GetTransferSizeFromCia(Kernel::HLERequestContext& ctx) {
return; return;
} }
auto file = file_res.Unwrap();
FileSys::CIAContainer container; FileSys::CIAContainer container;
if (container.Load(*file->backend) != Loader::ResultStatus::Success) { if (container.Load(*file_res.Unwrap()) != Loader::ResultStatus::Success) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultCode(ErrCodes::InvalidCIAHeader, ErrorModule::AM, rb.Push(ResultCode(ErrCodes::InvalidCIAHeader, ErrorModule::AM,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent)); ErrorSummary::InvalidArgument, ErrorLevel::Permanent));
@ -1228,9 +1262,8 @@ void Module::Interface::GetCoreVersionFromCia(Kernel::HLERequestContext& ctx) {
return; return;
} }
auto file = file_res.Unwrap();
FileSys::CIAContainer container; FileSys::CIAContainer container;
if (container.Load(*file->backend) != Loader::ResultStatus::Success) { if (container.Load(*file_res.Unwrap()) != Loader::ResultStatus::Success) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultCode(ErrCodes::InvalidCIAHeader, ErrorModule::AM, rb.Push(ResultCode(ErrCodes::InvalidCIAHeader, ErrorModule::AM,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent)); ErrorSummary::InvalidArgument, ErrorLevel::Permanent));
@ -1254,9 +1287,8 @@ void Module::Interface::GetRequiredSizeFromCia(Kernel::HLERequestContext& ctx) {
return; return;
} }
auto file = file_res.Unwrap();
FileSys::CIAContainer container; FileSys::CIAContainer container;
if (container.Load(*file->backend) != Loader::ResultStatus::Success) { if (container.Load(*file_res.Unwrap()) != Loader::ResultStatus::Success) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultCode(ErrCodes::InvalidCIAHeader, ErrorModule::AM, rb.Push(ResultCode(ErrCodes::InvalidCIAHeader, ErrorModule::AM,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent)); ErrorSummary::InvalidArgument, ErrorLevel::Permanent));
@ -1302,9 +1334,8 @@ void Module::Interface::GetMetaSizeFromCia(Kernel::HLERequestContext& ctx) {
return; return;
} }
auto file = file_res.Unwrap();
FileSys::CIAContainer container; FileSys::CIAContainer container;
if (container.Load(*file->backend) != Loader::ResultStatus::Success) { if (container.Load(*file_res.Unwrap()) != Loader::ResultStatus::Success) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultCode(ErrCodes::InvalidCIAHeader, ErrorModule::AM, rb.Push(ResultCode(ErrCodes::InvalidCIAHeader, ErrorModule::AM,
@ -1334,9 +1365,9 @@ void Module::Interface::GetMetaDataFromCia(Kernel::HLERequestContext& ctx) {
// Don't write beyond the actual static buffer size. // Don't write beyond the actual static buffer size.
output_size = std::min(static_cast<u32>(output_buffer.GetSize()), output_size); output_size = std::min(static_cast<u32>(output_buffer.GetSize()), output_size);
auto file = file_res.Unwrap(); auto file = std::move(file_res.Unwrap());
FileSys::CIAContainer container; FileSys::CIAContainer container;
if (container.Load(*file->backend) != Loader::ResultStatus::Success) { if (container.Load(*file) != Loader::ResultStatus::Success) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(ResultCode(ErrCodes::InvalidCIAHeader, ErrorModule::AM, rb.Push(ResultCode(ErrCodes::InvalidCIAHeader, ErrorModule::AM,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent)); ErrorSummary::InvalidArgument, ErrorLevel::Permanent));
@ -1346,7 +1377,7 @@ void Module::Interface::GetMetaDataFromCia(Kernel::HLERequestContext& ctx) {
// Read from the Meta offset for the specified size // Read from the Meta offset for the specified size
std::vector<u8> temp(output_size); std::vector<u8> temp(output_size);
auto read_result = file->backend->Read(container.GetMetadataOffset(), output_size, temp.data()); auto read_result = file->Read(container.GetMetadataOffset(), output_size, temp.data());
if (read_result.Failed() || *read_result != output_size) { if (read_result.Failed() || *read_result != output_size) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultCode(ErrCodes::InvalidCIAHeader, ErrorModule::AM, rb.Push(ResultCode(ErrCodes::InvalidCIAHeader, ErrorModule::AM,

View file

@ -307,6 +307,18 @@ Kernel::SharedPtr<Kernel::ClientSession> File::Connect() {
return std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions); return std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions);
} }
std::size_t File::GetSessionFileOffset(Kernel::SharedPtr<Kernel::ServerSession> session) {
const FileSessionSlot* slot = GetSessionData(session);
ASSERT(slot);
return slot->offset;
}
std::size_t File::GetSessionFileSize(Kernel::SharedPtr<Kernel::ServerSession> session) {
const FileSessionSlot* slot = GetSessionData(session);
ASSERT(slot);
return slot->size;
}
Directory::Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, Directory::Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend,
const FileSys::Path& path) const FileSys::Path& path)
: ServiceFramework("", 1), path(path), backend(std::move(backend)) { : ServiceFramework("", 1), path(path), backend(std::move(backend)) {

View file

@ -73,6 +73,14 @@ public:
/// Creates a new session to this File and returns the ClientSession part of the connection. /// Creates a new session to this File and returns the ClientSession part of the connection.
Kernel::SharedPtr<Kernel::ClientSession> Connect(); Kernel::SharedPtr<Kernel::ClientSession> Connect();
// Returns the start offset of an open file represented by the input session, opened with
// OpenSubFile.
std::size_t GetSessionFileOffset(Kernel::SharedPtr<Kernel::ServerSession> session);
// Returns the size of an open file represented by the input session, opened with
// OpenSubFile.
std::size_t GetSessionFileSize(Kernel::SharedPtr<Kernel::ServerSession> session);
private: private:
void Read(Kernel::HLERequestContext& ctx); void Read(Kernel::HLERequestContext& ctx);
void Write(Kernel::HLERequestContext& ctx); void Write(Kernel::HLERequestContext& ctx);