Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8696bd5d4 | ||
|
|
af7baa6b34 | ||
|
|
6de1361c8b | ||
|
|
2b930d21fd | ||
|
|
0b52596087 | ||
|
|
e9134d8044 | ||
|
|
33d6dfb6b3 | ||
|
|
6096fa0e45 | ||
|
|
058f265bd6 | ||
|
|
bd4c608b08 | ||
|
|
7fc1e86bf5 | ||
|
|
87ec045a98 | ||
|
|
7502e2174f | ||
|
|
0545eb18c0 | ||
|
|
0c161a4c1b | ||
|
|
3d518759da | ||
|
|
0af2758fde |
@@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||
branch = master
|
||||
commit = 30f3e4c33d2767126c36a51b9259051c6c42d6b5
|
||||
parent = 82eab9c8d0093e4d8907a668e9335981f6f4949b
|
||||
commit = a40d4593d7e46ea296802e5c50fd0da2d9f92a76
|
||||
parent = 6de1361c8be886eda16faf87c73b9e4d01187bca
|
||||
method = merge
|
||||
cmdver = 0.4.1
|
||||
|
||||
@@ -239,7 +239,7 @@ namespace ams::kern {
|
||||
|
||||
/* Align up the address. */
|
||||
KVirtualAddress end = addr + size;
|
||||
const size_t align = (this->next_block_shift != 0) ? (u64(1) << this->next_block_shift) : (this->block_shift);
|
||||
const size_t align = (this->next_block_shift != 0) ? (u64(1) << this->next_block_shift) : (u64(1) << this->block_shift);
|
||||
addr = util::AlignDown(GetInteger(addr), align);
|
||||
end = util::AlignUp(GetInteger(end), align);
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#include "stratosphere/lr.hpp"
|
||||
#include "stratosphere/map.hpp"
|
||||
#include "stratosphere/ncm.hpp"
|
||||
#include "stratosphere/nim.hpp"
|
||||
#include "stratosphere/patcher.hpp"
|
||||
#include "stratosphere/pm.hpp"
|
||||
#include "stratosphere/reg.hpp"
|
||||
@@ -58,7 +59,6 @@
|
||||
#include "stratosphere/spl.hpp"
|
||||
#include "stratosphere/updater.hpp"
|
||||
|
||||
|
||||
/* Include FS last. */
|
||||
#include "stratosphere/fs.hpp"
|
||||
#include "stratosphere/fssrv.hpp"
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <stratosphere/fs/fs_code.hpp>
|
||||
#include <stratosphere/fs/fs_content.hpp>
|
||||
#include <stratosphere/fs/fs_content_storage.hpp>
|
||||
#include <stratosphere/fs/fs_image_directory.hpp>
|
||||
#include <stratosphere/fs/fs_game_card.hpp>
|
||||
#include <stratosphere/fs/fs_save_data_types.hpp>
|
||||
#include <stratosphere/fs/fs_save_data_management.hpp>
|
||||
|
||||
@@ -14,13 +14,14 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "fs_common.hpp"
|
||||
#include "fs_istorage.hpp"
|
||||
#include "fsa/fs_ifile.hpp"
|
||||
#include <stratosphere/fs/fs_common.hpp>
|
||||
#include <stratosphere/fs/fs_istorage.hpp>
|
||||
#include <stratosphere/fs/fsa/fs_ifile.hpp>
|
||||
#include <stratosphere/fs/impl/fs_newable.hpp>
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
class FileStorage : public IStorage {
|
||||
class FileStorage : public IStorage, public impl::Newable {
|
||||
private:
|
||||
static constexpr s64 InvalidSize = -1;
|
||||
private:
|
||||
@@ -53,4 +54,32 @@ namespace ams::fs {
|
||||
virtual Result OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override;
|
||||
};
|
||||
|
||||
class FileHandleStorage : public IStorage, public impl::Newable {
|
||||
private:
|
||||
static constexpr s64 InvalidSize = -1;
|
||||
private:
|
||||
FileHandle handle;
|
||||
bool close_file;
|
||||
s64 size;
|
||||
os::Mutex mutex;
|
||||
public:
|
||||
constexpr explicit FileHandleStorage(FileHandle handle, bool close_file) : handle(handle), close_file(close_file), size(InvalidSize), mutex() { /* ... */ }
|
||||
constexpr explicit FileHandleStorage(FileHandle handle) : FileHandleStorage(handle, false) { /* ... */ }
|
||||
|
||||
virtual ~FileHandleStorage() override {
|
||||
if (this->close_file) {
|
||||
CloseFile(this->handle);
|
||||
}
|
||||
}
|
||||
protected:
|
||||
Result UpdateSize();
|
||||
public:
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override;
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override;
|
||||
virtual Result Flush() override;
|
||||
virtual Result GetSize(s64 *out_size) override;
|
||||
virtual Result SetSize(s64 size) override;
|
||||
virtual Result OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "fs_common.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
enum class ImageDirectoryId {
|
||||
Nand = 0,
|
||||
SdCard = 1,
|
||||
};
|
||||
|
||||
Result MountImageDirectory(const char *name, ImageDirectoryId id);
|
||||
|
||||
}
|
||||
@@ -85,7 +85,7 @@ namespace ams::fs::fsa {
|
||||
protected:
|
||||
Result DryRead(size_t *out, s64 offset, size_t size, const fs::ReadOption &option, OpenMode open_mode) {
|
||||
/* Check that we can read. */
|
||||
R_UNLESS((open_mode & OpenMode_Read) != 0, fs::ResultInvalidOperationForOpenMode());
|
||||
R_UNLESS((open_mode & OpenMode_Read) != 0, fs::ResultReadNotPermitted());
|
||||
|
||||
/* Get the file size, and validate our offset. */
|
||||
s64 file_size = 0;
|
||||
@@ -98,12 +98,31 @@ namespace ams::fs::fsa {
|
||||
|
||||
Result DrySetSize(s64 size, fs::OpenMode open_mode) {
|
||||
/* Check that we can write. */
|
||||
R_UNLESS((open_mode & OpenMode_Write) != 0, fs::ResultInvalidOperationForOpenMode());
|
||||
R_UNLESS((open_mode & OpenMode_Write) != 0, fs::ResultWriteNotPermitted());
|
||||
|
||||
AMS_ASSERT(size >= 0);
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result DryWrite(bool *out_append, s64 offset, size_t size, const fs::WriteOption &option, fs::OpenMode open_mode) {
|
||||
/* Check that we can write. */
|
||||
R_UNLESS((open_mode & OpenMode_Write) != 0, fs::ResultWriteNotPermitted());
|
||||
|
||||
/* Get the file size. */
|
||||
s64 file_size = 0;
|
||||
R_TRY(this->GetSize(&file_size));
|
||||
|
||||
/* Determine if we need to append. */
|
||||
if (file_size < offset + static_cast<s64>(size)) {
|
||||
R_UNLESS((open_mode & OpenMode_AllowAppend) != 0, fs::ResultFileExtensionWithoutOpenModeAllowAppend());
|
||||
*out_append = true;
|
||||
} else {
|
||||
*out_append = false;
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
private:
|
||||
virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) = 0;
|
||||
virtual Result GetSizeImpl(s64 *out) = 0;
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#pragma once
|
||||
#include "fssystem/fssystem_utility.hpp"
|
||||
#include "fssystem/fssystem_external_code.hpp"
|
||||
#include "fssystem/fssystem_partition_file_system.hpp"
|
||||
#include "fssystem/fssystem_partition_file_system_meta.hpp"
|
||||
#include "fssystem/fssystem_path_tool.hpp"
|
||||
#include "fssystem/fssystem_subdirectory_filesystem.hpp"
|
||||
#include "fssystem/fssystem_directory_redirection_filesystem.hpp"
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Adubbz, Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "fssystem_partition_file_system_meta.hpp"
|
||||
#include "../fs/fsa/fs_ifile.hpp"
|
||||
#include "../fs/fsa/fs_idirectory.hpp"
|
||||
#include "../fs/fsa/fs_ifilesystem.hpp"
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
template<typename MetaType>
|
||||
class PartitionFileSystemCore : public fs::impl::Newable, public fs::fsa::IFileSystem {
|
||||
NON_COPYABLE(PartitionFileSystemCore);
|
||||
NON_MOVEABLE(PartitionFileSystemCore);
|
||||
private:
|
||||
class PartitionFile;
|
||||
class PartitionDirectory;
|
||||
private:
|
||||
fs::IStorage *base_storage;
|
||||
MetaType *meta_data;
|
||||
bool initialized;
|
||||
size_t meta_data_size;
|
||||
std::unique_ptr<MetaType> unique_meta_data;
|
||||
std::shared_ptr<fs::IStorage> shared_storage;
|
||||
private:
|
||||
Result Initialize(fs::IStorage *base_storage, MemoryResource *allocator);
|
||||
public:
|
||||
PartitionFileSystemCore();
|
||||
virtual ~PartitionFileSystemCore() override;
|
||||
|
||||
Result Initialize(std::unique_ptr<MetaType> &&meta_data, std::shared_ptr<fs::IStorage> base_storage);
|
||||
Result Initialize(MetaType *meta_data, std::shared_ptr<fs::IStorage> base_storage);
|
||||
Result Initialize(fs::IStorage *base_storage);
|
||||
Result Initialize(std::shared_ptr<fs::IStorage> base_storage);
|
||||
Result Initialize(std::shared_ptr<fs::IStorage> base_storage, MemoryResource *allocator);
|
||||
|
||||
Result GetFileBaseOffset(s64 *out_offset, const char *path);
|
||||
|
||||
virtual Result CreateFileImpl(const char *path, s64 size, int option) override;
|
||||
virtual Result DeleteFileImpl(const char *path) override;
|
||||
virtual Result CreateDirectoryImpl(const char *path) override;
|
||||
virtual Result DeleteDirectoryImpl(const char *path) override;
|
||||
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override;
|
||||
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override;
|
||||
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override;
|
||||
virtual Result GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) override;
|
||||
virtual Result OpenFileImpl(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) override;
|
||||
virtual Result OpenDirectoryImpl(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) override;
|
||||
virtual Result CommitImpl() override;
|
||||
virtual Result CleanDirectoryRecursivelyImpl(const char *path) override;
|
||||
|
||||
/* These aren't accessible as commands. */
|
||||
virtual Result CommitProvisionallyImpl(s64 counter) override;
|
||||
};
|
||||
|
||||
using PartitionFileSystem = PartitionFileSystemCore<PartitionFileSystemMeta>;
|
||||
using Sha256PartitionFileSystem = PartitionFileSystemCore<Sha256PartitionFileSystemMeta>;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Adubbz, Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere/fs/fs_common.hpp>
|
||||
#include <stratosphere/fs/impl/fs_newable.hpp>
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
namespace impl {
|
||||
|
||||
struct PartitionFileSystemFormat {
|
||||
#pragma pack(push, 1)
|
||||
struct PartitionEntry {
|
||||
u64 offset;
|
||||
u64 size;
|
||||
u32 name_offset;
|
||||
u32 reserved;
|
||||
};
|
||||
static_assert(std::is_pod<PartitionEntry>::value);
|
||||
#pragma pack(pop)
|
||||
|
||||
static constexpr const char VersionSignature[] = { 'P', 'F', 'S', '0' };
|
||||
|
||||
static constexpr size_t EntryNameLengthMax = ::ams::fs::EntryNameLengthMax;
|
||||
static constexpr size_t FileDataAlignmentSize = 0x20;
|
||||
|
||||
using ResultSignatureVerificationFailed = fs::ResultPartitionSignatureVerificationFailed;
|
||||
};
|
||||
|
||||
struct Sha256PartitionFileSystemFormat {
|
||||
static constexpr size_t HashSize = ::ams::crypto::Sha256Generator::HashSize;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct PartitionEntry {
|
||||
u64 offset;
|
||||
u64 size;
|
||||
u32 name_offset;
|
||||
u32 hash_target_size;
|
||||
u64 hash_target_offset;
|
||||
char hash[HashSize];
|
||||
};
|
||||
static_assert(std::is_pod<PartitionEntry>::value);
|
||||
#pragma pack(pop)
|
||||
|
||||
static constexpr const char VersionSignature[] = { 'H', 'F', 'S', '0' };
|
||||
|
||||
static constexpr size_t EntryNameLengthMax = ::ams::fs::EntryNameLengthMax;
|
||||
static constexpr size_t FileDataAlignmentSize = 0x200;
|
||||
|
||||
using ResultSignatureVerificationFailed = fs::ResultSha256PartitionSignatureVerificationFailed;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<typename Format>
|
||||
class PartitionFileSystemMetaCore : public fs::impl::Newable {
|
||||
public:
|
||||
static constexpr size_t EntryNameLengthMax = Format::EntryNameLengthMax;
|
||||
static constexpr size_t FileDataAlignmentSize = Format::FileDataAlignmentSize;
|
||||
|
||||
/* Forward declare header. */
|
||||
struct PartitionFileSystemHeader;
|
||||
|
||||
using PartitionEntry = typename Format::PartitionEntry;
|
||||
protected:
|
||||
bool initialized;
|
||||
PartitionFileSystemHeader *header;
|
||||
PartitionEntry *entries;
|
||||
char *name_table;
|
||||
size_t meta_data_size;
|
||||
MemoryResource *allocator;
|
||||
char *buffer;
|
||||
public:
|
||||
PartitionFileSystemMetaCore() : initialized(false), allocator(nullptr), buffer(nullptr) { /* ... */ }
|
||||
~PartitionFileSystemMetaCore();
|
||||
|
||||
Result Initialize(fs::IStorage *storage, MemoryResource *allocator);
|
||||
Result Initialize(fs::IStorage *storage, void *header, size_t header_size);
|
||||
|
||||
const PartitionEntry *GetEntry(s32 index) const;
|
||||
s32 GetEntryCount() const;
|
||||
s32 GetEntryIndex(const char *name) const;
|
||||
const char *GetEntryName(s32 index) const;
|
||||
size_t GetHeaderSize() const;
|
||||
size_t GetMetaDataSize() const;
|
||||
public:
|
||||
static Result QueryMetaDataSize(size_t *out_size, fs::IStorage *storage);
|
||||
protected:
|
||||
void DeallocateBuffer();
|
||||
};
|
||||
|
||||
using PartitionFileSystemMeta = PartitionFileSystemMetaCore<impl::PartitionFileSystemFormat>;
|
||||
|
||||
class Sha256PartitionFileSystemMeta : public PartitionFileSystemMetaCore<impl::Sha256PartitionFileSystemFormat> {
|
||||
public:
|
||||
using PartitionFileSystemMetaCore<impl::Sha256PartitionFileSystemFormat>::Initialize;
|
||||
Result Initialize(fs::IStorage *base_storage, MemoryResource *allocator, const void *hash, size_t hash_size, std::optional<u8> suffix = std::nullopt);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -49,7 +49,7 @@ namespace ams::kvdb {
|
||||
|
||||
void Reset() {
|
||||
if (this->buffer != nullptr) {
|
||||
std::free(this->buffer);
|
||||
delete[] this->buffer;
|
||||
this->buffer = nullptr;
|
||||
this->size = 0;
|
||||
}
|
||||
@@ -68,7 +68,7 @@ namespace ams::kvdb {
|
||||
AMS_ABORT_UNLESS(this->buffer == nullptr);
|
||||
|
||||
/* Allocate a buffer. */
|
||||
this->buffer = static_cast<u8 *>(std::malloc(size));
|
||||
this->buffer = new (std::nothrow) u8[size];
|
||||
R_UNLESS(this->buffer != nullptr, ResultAllocationFailed());
|
||||
|
||||
this->size = size;
|
||||
|
||||
@@ -93,13 +93,14 @@ namespace ams::kvdb {
|
||||
size_t count;
|
||||
size_t capacity;
|
||||
Entry *entries;
|
||||
MemoryResource *memory_resource;
|
||||
public:
|
||||
Index() : count(0), capacity(0), entries(nullptr) { /* ... */ }
|
||||
Index() : count(0), capacity(0), entries(nullptr), memory_resource(nullptr) { /* ... */ }
|
||||
|
||||
~Index() {
|
||||
if (this->entries != nullptr) {
|
||||
this->ResetEntries();
|
||||
std::free(this->entries);
|
||||
this->memory_resource->Deallocate(this->entries, sizeof(Entry) * this->capacity);
|
||||
this->entries = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -114,30 +115,25 @@ namespace ams::kvdb {
|
||||
|
||||
void ResetEntries() {
|
||||
for (size_t i = 0; i < this->count; i++) {
|
||||
std::free(this->entries[i].GetValuePointer());
|
||||
this->memory_resource->Deallocate(this->entries[i].GetValuePointer(), this->entries[i].GetValueSize());
|
||||
}
|
||||
this->count = 0;
|
||||
}
|
||||
|
||||
Result Initialize(size_t capacity) {
|
||||
this->entries = reinterpret_cast<Entry *>(std::malloc(sizeof(Entry) * capacity));
|
||||
Result Initialize(size_t capacity, MemoryResource *mr) {
|
||||
this->entries = reinterpret_cast<Entry *>(mr->Allocate(sizeof(Entry) * capacity));
|
||||
R_UNLESS(this->entries != nullptr, ResultAllocationFailed());
|
||||
this->capacity = capacity;
|
||||
this->memory_resource = mr;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result Set(const Key &key, const void *value, size_t value_size) {
|
||||
/* Allocate new value. */
|
||||
void *new_value = std::malloc(value_size);
|
||||
R_UNLESS(new_value != nullptr, ResultAllocationFailed());
|
||||
auto value_guard = SCOPE_GUARD { std::free(new_value); };
|
||||
std::memcpy(new_value, value, value_size);
|
||||
|
||||
/* Find entry for key. */
|
||||
Entry *it = this->lower_bound(key);
|
||||
if (it != this->end() && it->GetKey() == key) {
|
||||
/* Entry already exists. Free old value. */
|
||||
std::free(it->GetValuePointer());
|
||||
this->memory_resource->Deallocate(it->GetValuePointer(), it->GetValueSize());
|
||||
} else {
|
||||
/* We need to add a new entry. Check we have room, move future keys forward. */
|
||||
R_UNLESS(this->count < this->capacity, ResultOutOfKeyResource());
|
||||
@@ -145,8 +141,12 @@ namespace ams::kvdb {
|
||||
this->count++;
|
||||
}
|
||||
|
||||
/* Allocate new value. */
|
||||
void *new_value = this->memory_resource->Allocate(value_size);
|
||||
R_UNLESS(new_value != nullptr, ResultAllocationFailed());
|
||||
std::memcpy(new_value, value, value_size);
|
||||
|
||||
/* Save the new Entry in the map. */
|
||||
value_guard.Cancel();
|
||||
*it = Entry(key, new_value, value_size);
|
||||
return ResultSuccess();
|
||||
}
|
||||
@@ -164,7 +164,7 @@ namespace ams::kvdb {
|
||||
R_UNLESS(it != this->end(), ResultKeyNotFound());
|
||||
|
||||
/* Free the value, move entries back. */
|
||||
std::free(it->GetValuePointer());
|
||||
this->memory_resource->Deallocate(it->GetValuePointer(), it->GetValueSize());
|
||||
std::memmove(it, it + 1, sizeof(*it) * (this->end() - (it + 1)));
|
||||
this->count--;
|
||||
return ResultSuccess();
|
||||
@@ -258,10 +258,11 @@ namespace ams::kvdb {
|
||||
Index index;
|
||||
Path path;
|
||||
Path temp_path;
|
||||
MemoryResource *memory_resource;
|
||||
public:
|
||||
MemoryKeyValueStore() { /* ... */ }
|
||||
|
||||
Result Initialize(const char *dir, size_t capacity) {
|
||||
Result Initialize(const char *dir, size_t capacity, MemoryResource *mr) {
|
||||
/* Ensure that the passed path is a directory. */
|
||||
fs::DirectoryEntryType entry_type;
|
||||
R_TRY(fs::GetEntryType(std::addressof(entry_type), dir));
|
||||
@@ -272,18 +273,21 @@ namespace ams::kvdb {
|
||||
this->temp_path.SetFormat("%s%s", dir, "/imkvdb.tmp");
|
||||
|
||||
/* Initialize our index. */
|
||||
R_TRY(this->index.Initialize(capacity));
|
||||
R_TRY(this->index.Initialize(capacity, mr));
|
||||
this->memory_resource = mr;
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result Initialize(size_t capacity) {
|
||||
Result Initialize(size_t capacity, MemoryResource *mr) {
|
||||
/* This initializes without an archive file. */
|
||||
/* A store initialized this way cannot have its contents loaded from or flushed to disk. */
|
||||
this->path.Set("");
|
||||
this->temp_path.Set("");
|
||||
|
||||
/* Initialize our index. */
|
||||
R_TRY(this->index.Initialize(capacity));
|
||||
R_TRY(this->index.Initialize(capacity, mr));
|
||||
this->memory_resource = mr;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
@@ -319,9 +323,9 @@ namespace ams::kvdb {
|
||||
R_TRY(reader.GetEntrySize(&key_size, &value_size));
|
||||
|
||||
/* Allocate memory for value. */
|
||||
void *new_value = std::malloc(value_size);
|
||||
void *new_value = this->memory_resource->Allocate(value_size);
|
||||
R_UNLESS(new_value != nullptr, ResultAllocationFailed());
|
||||
auto value_guard = SCOPE_GUARD { std::free(new_value); };
|
||||
auto value_guard = SCOPE_GUARD { this->memory_resource->Deallocate(new_value, value_size); };
|
||||
|
||||
/* Read key and value. */
|
||||
Key key;
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace ams::ncm {
|
||||
|
||||
void Reset() {
|
||||
if (this->buffer != nullptr) {
|
||||
std::free(this->buffer);
|
||||
delete[] this->buffer;
|
||||
this->buffer = nullptr;
|
||||
this->size = 0;
|
||||
}
|
||||
@@ -68,7 +68,7 @@ namespace ams::ncm {
|
||||
AMS_ABORT_UNLESS(this->buffer == nullptr);
|
||||
|
||||
/* Allocate a buffer. */
|
||||
this->buffer = static_cast<u8 *>(std::malloc(size));
|
||||
this->buffer = new (std::nothrow) u8[size];
|
||||
R_UNLESS(this->buffer != nullptr, ResultAllocationFailed());
|
||||
|
||||
this->size = size;
|
||||
@@ -85,4 +85,5 @@ namespace ams::ncm {
|
||||
return ResultSuccess();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -30,6 +30,16 @@
|
||||
|
||||
namespace ams::ncm {
|
||||
|
||||
class ContentMetaMemoryResource {
|
||||
private:
|
||||
mem::StandardAllocator allocator;
|
||||
sf::StandardAllocatorMemoryResource memory_resource;
|
||||
public:
|
||||
ContentMetaMemoryResource(void *heap, size_t heap_size) : allocator(heap, heap_size), memory_resource(std::addressof(allocator)) { /* ... */ }
|
||||
|
||||
MemoryResource *Get() { return std::addressof(this->memory_resource); }
|
||||
};
|
||||
|
||||
struct SystemSaveDataInfo {
|
||||
u64 id;
|
||||
u64 size;
|
||||
@@ -67,6 +77,7 @@ namespace ams::ncm {
|
||||
SystemSaveDataInfo info;
|
||||
std::shared_ptr<IContentMetaDatabase> content_meta_database;
|
||||
std::optional<kvdb::MemoryKeyValueStore<ContentMetaKey>> kvs;
|
||||
ContentMetaMemoryResource *memory_resource;
|
||||
u32 max_content_metas;
|
||||
|
||||
ContentMetaDatabaseRoot() { /* ... */ }
|
||||
@@ -92,8 +103,8 @@ namespace ams::ncm {
|
||||
Result InitializeContentStorageRoot(ContentStorageRoot *out, StorageId storage_id, fs::ContentStorageId content_storage_id);
|
||||
Result InitializeGameCardContentStorageRoot(ContentStorageRoot *out);
|
||||
|
||||
Result InitializeContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, StorageId storage_id, const SystemSaveDataInfo &info, size_t max_content_metas);
|
||||
Result InitializeGameCardContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, size_t max_content_metas);
|
||||
Result InitializeContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, StorageId storage_id, const SystemSaveDataInfo &info, size_t max_content_metas, ContentMetaMemoryResource *mr);
|
||||
Result InitializeGameCardContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, size_t max_content_metas, ContentMetaMemoryResource *mr);
|
||||
|
||||
Result BuildContentMetaDatabase(StorageId storage_id);
|
||||
Result ImportContentMetaDatabase(StorageId storage_id, bool from_signed_partition);
|
||||
|
||||
19
libraries/libstratosphere/include/stratosphere/nim.hpp
Normal file
19
libraries/libstratosphere/include/stratosphere/nim.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stratosphere/nim/nim_network_install_manager_api.hpp>
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere/nim/nim_system_update_task_id.hpp>
|
||||
|
||||
namespace ams::nim {
|
||||
|
||||
/* Management. */
|
||||
void InitializeForNetworkInstallManager();
|
||||
void FinalizeForNetworkInstallManager();
|
||||
|
||||
/* Service API. */
|
||||
Result DestroySystemUpdateTask(const SystemUpdateTaskId& id);
|
||||
s32 ListSystemUpdateTask(SystemUpdateTaskId *out_list, size_t out_list_size);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 Adubbz, Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/nim/nim_task_id_common.hpp>
|
||||
|
||||
namespace ams::nim {
|
||||
|
||||
AMS_NIM_DEFINE_TASK_ID(SystemUpdateTaskId, 8);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 Adubbz, Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/nim/nim_task_id_common.hpp>
|
||||
|
||||
namespace ams::nim {
|
||||
|
||||
#define AMS_NIM_DEFINE_TASK_ID(ID, ALIGN) \
|
||||
struct alignas(ALIGN) ID { \
|
||||
util::Uuid uuid; \
|
||||
\
|
||||
ALWAYS_INLINE bool IsValid() const { \
|
||||
return this->uuid != util::InvalidUuid; \
|
||||
} \
|
||||
}; \
|
||||
\
|
||||
static_assert(alignof(ID) == ALIGN); \
|
||||
\
|
||||
ALWAYS_INLINE bool operator==(const ID &lhs, const ID &rhs) { \
|
||||
return lhs.uuid == rhs.uuid; \
|
||||
} \
|
||||
\
|
||||
ALWAYS_INLINE bool operator!=(const ID &lhs, const ID &rhs) { \
|
||||
return lhs.uuid != rhs.uuid; \
|
||||
} \
|
||||
\
|
||||
constexpr inline ID Invalid##ID = { util::InvalidUuid }
|
||||
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "sf/sf_common.hpp"
|
||||
#include "sf/sf_mem_utility.hpp"
|
||||
#include "sf/sf_service_object.hpp"
|
||||
#include "sf/hipc/sf_hipc_server_session_manager.hpp"
|
||||
|
||||
|
||||
@@ -16,6 +16,6 @@
|
||||
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include "../ams.hpp"
|
||||
#include "../os.hpp"
|
||||
#include "../sm/sm_types.hpp"
|
||||
#include <stratosphere/ams.hpp>
|
||||
#include <stratosphere/os.hpp>
|
||||
#include <stratosphere/sm/sm_types.hpp>
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere/sf/sf_common.hpp>
|
||||
#include <stratosphere/mem.hpp>
|
||||
|
||||
namespace ams::sf {
|
||||
|
||||
class StandardAllocatorMemoryResource : public MemoryResource {
|
||||
private:
|
||||
mem::StandardAllocator *standard_allocator;
|
||||
public:
|
||||
explicit StandardAllocatorMemoryResource(mem::StandardAllocator *sa) : standard_allocator(sa) { /* ... */ }
|
||||
|
||||
mem::StandardAllocator *GetAllocator() const { return this->standard_allocator; }
|
||||
private:
|
||||
virtual void *AllocateImpl(size_t size, size_t alignment) override {
|
||||
return this->standard_allocator->Allocate(size, alignment);
|
||||
}
|
||||
|
||||
virtual void DeallocateImpl(void *buffer, size_t size, size_t alignment) override {
|
||||
return this->standard_allocator->Free(buffer);
|
||||
}
|
||||
|
||||
virtual bool IsEqualImpl(const MemoryResource &resource) const {
|
||||
return this == std::addressof(resource);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -21,7 +21,16 @@
|
||||
namespace ams::updater {
|
||||
|
||||
/* Public API. */
|
||||
BootImageUpdateType GetBootImageUpdateType(int boot_image_update_type);
|
||||
BootImageUpdateType GetBootImageUpdateType(spl::HardwareType hw_type);
|
||||
|
||||
Result GetBootImagePackageId(ncm::SystemDataId *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size);
|
||||
|
||||
Result MarkVerifyingRequired(BootModeType mode, void *work_buffer, size_t work_buffer_size);
|
||||
Result MarkVerified(BootModeType mode, void *work_buffer, size_t work_buffer_size);
|
||||
|
||||
Result UpdateBootImagesFromPackage(ncm::SystemDataId boot_image_package_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
|
||||
|
||||
Result VerifyBootImagesAndRepairIfNeeded(bool *out_repaired_normal, bool *out_repaired_safe, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
|
||||
|
||||
}
|
||||
|
||||
@@ -90,4 +90,75 @@ namespace ams::fs {
|
||||
}
|
||||
}
|
||||
|
||||
Result FileHandleStorage::UpdateSize() {
|
||||
R_SUCCEED_IF(this->size != InvalidSize);
|
||||
return GetFileSize(std::addressof(this->size), this->handle);
|
||||
}
|
||||
|
||||
Result FileHandleStorage::Read(s64 offset, void *buffer, size_t size) {
|
||||
/* Lock the mutex. */
|
||||
std::scoped_lock lk(this->mutex);
|
||||
|
||||
/* Immediately succeed if there's nothing to read. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Validate buffer. */
|
||||
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
/* Ensure our size is valid. */
|
||||
R_TRY(this->UpdateSize());
|
||||
|
||||
/* Ensure our access is valid. */
|
||||
R_UNLESS(IStorage::IsRangeValid(offset, size, this->size), fs::ResultOutOfRange());
|
||||
|
||||
return ReadFile(this->handle, offset, buffer, size, fs::ReadOption());
|
||||
}
|
||||
|
||||
Result FileHandleStorage::Write(s64 offset, const void *buffer, size_t size) {
|
||||
/* Lock the mutex. */
|
||||
std::scoped_lock lk(this->mutex);
|
||||
|
||||
/* Immediately succeed if there's nothing to write. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Validate buffer. */
|
||||
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
/* Ensure our size is valid. */
|
||||
R_TRY(this->UpdateSize());
|
||||
|
||||
/* Ensure our access is valid. */
|
||||
R_UNLESS(IStorage::IsRangeValid(offset, size, this->size), fs::ResultOutOfRange());
|
||||
|
||||
return WriteFile(this->handle, offset, buffer, size, fs::WriteOption());
|
||||
}
|
||||
|
||||
Result FileHandleStorage::Flush() {
|
||||
return FlushFile(this->handle);
|
||||
}
|
||||
|
||||
Result FileHandleStorage::GetSize(s64 *out_size) {
|
||||
R_TRY(this->UpdateSize());
|
||||
*out_size = this->size;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result FileHandleStorage::SetSize(s64 size) {
|
||||
this->size = InvalidSize;
|
||||
return SetFileSize(this->handle, size);
|
||||
}
|
||||
|
||||
Result FileHandleStorage::OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
|
||||
switch (op_id) {
|
||||
case OperationId::QueryRange:
|
||||
/* Validate buffer and size. */
|
||||
R_UNLESS(dst != nullptr, fs::ResultNullptrArgument());
|
||||
R_UNLESS(dst_size == sizeof(QueryRangeInfo), fs::ResultInvalidSize());
|
||||
|
||||
return QueryRange(static_cast<QueryRangeInfo *>(dst), this->handle, offset, size);
|
||||
default:
|
||||
return fs::ResultUnsupportedOperationInFileStorageB();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
37
libraries/libstratosphere/source/fs/fs_image_directory.cpp
Normal file
37
libraries/libstratosphere/source/fs/fs_image_directory.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsa/fs_mount_utils.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
Result MountImageDirectory(const char *name, ImageDirectoryId id) {
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountName(name));
|
||||
|
||||
/* Open the image directory. This uses libnx bindings. */
|
||||
FsFileSystem fs;
|
||||
R_TRY(fsOpenImageDirectoryFileSystem(std::addressof(fs), static_cast<::FsImageDirectoryId>(id)));
|
||||
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
auto fsa = std::make_unique<RemoteFileSystem>(fs);
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInImageDirectoryA());
|
||||
|
||||
/* Register. */
|
||||
return fsa::Register(name, std::move(fsa));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,6 +18,30 @@
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
namespace {
|
||||
|
||||
/* NOTE: Nintendo does not attach a generator to a mounted SD card filesystem. */
|
||||
/* However, it is desirable for homebrew to be able to access SD via common path. */
|
||||
class SdCardCommonMountNameGenerator : public fsa::ICommonMountNameGenerator, public impl::Newable {
|
||||
public:
|
||||
explicit SdCardCommonMountNameGenerator() { /* ... */ }
|
||||
|
||||
virtual Result GenerateCommonMountName(char *dst, size_t dst_size) override {
|
||||
/* Determine how much space we need. */
|
||||
const size_t needed_size = strnlen(impl::SdCardFileSystemMountName, MountNameLengthMax) + 2;
|
||||
AMS_ABORT_UNLESS(dst_size >= needed_size);
|
||||
|
||||
/* Generate the name. */
|
||||
auto size = std::snprintf(dst, dst_size, "%s:", impl::SdCardFileSystemMountName);
|
||||
AMS_ASSERT(static_cast<size_t>(size) == needed_size - 1);
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
Result MountSdCard(const char *name) {
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountName(name));
|
||||
@@ -30,8 +54,13 @@ namespace ams::fs {
|
||||
auto fsa = std::make_unique<RemoteFileSystem>(fs);
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInSdCardA());
|
||||
|
||||
/* Allocate a new mountname generator. */
|
||||
/* NOTE: Nintendo does not attach a generator. */
|
||||
auto generator = std::make_unique<SdCardCommonMountNameGenerator>();
|
||||
R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInSdCardA());
|
||||
|
||||
/* Register. */
|
||||
return fsa::Register(name, std::move(fsa));
|
||||
return fsa::Register(name, std::move(fsa), std::move(generator));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Adubbz, Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
namespace {
|
||||
|
||||
class PartitionFileSystemDefaultAllocator : public MemoryResource {
|
||||
private:
|
||||
virtual void *AllocateImpl(size_t size, size_t alignment) override {
|
||||
return ::ams::fs::impl::Allocate(size);
|
||||
}
|
||||
|
||||
virtual void DeallocateImpl(void *buffer, size_t size, size_t alignment) override {
|
||||
::ams::fs::impl::Deallocate(buffer, size);
|
||||
}
|
||||
|
||||
virtual bool IsEqualImpl(const MemoryResource &rhs) const override {
|
||||
return this == std::addressof(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
PartitionFileSystemDefaultAllocator g_partition_filesystem_default_allocator;
|
||||
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
class PartitionFileSystemCore<MetaType>::PartitionFile : public fs::fsa::IFile, public fs::impl::Newable {
|
||||
private:
|
||||
const typename MetaType::PartitionEntry *partition_entry;
|
||||
const PartitionFileSystemCore<MetaType> *parent;
|
||||
const fs::OpenMode mode;
|
||||
public:
|
||||
PartitionFile(PartitionFileSystemCore<MetaType> *parent, const typename MetaType::PartitionEntry *partition_entry, fs::OpenMode mode) : partition_entry(partition_entry), parent(parent), mode(mode) { /* ... */ }
|
||||
private:
|
||||
virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override final;
|
||||
|
||||
virtual Result GetSizeImpl(s64 *out) override final {
|
||||
*out = this->partition_entry->size;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual Result FlushImpl() override final {
|
||||
/* Nothing to do if writing disallowed. */
|
||||
R_SUCCEED_IF((this->mode & fs::OpenMode_Write) == 0);
|
||||
|
||||
/* Flush base storage. */
|
||||
return this->parent->base_storage->Flush();
|
||||
}
|
||||
|
||||
virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final {
|
||||
/* Ensure appending is not required. */
|
||||
bool needs_append;
|
||||
R_TRY(this->DryWrite(std::addressof(needs_append), offset, size, option, this->mode));
|
||||
R_UNLESS(!needs_append, fs::ResultUnsupportedOperationInPartitionFileA());
|
||||
|
||||
/* Appending is prohibited. */
|
||||
AMS_ASSERT((this->mode & fs::OpenMode_AllowAppend) == 0);
|
||||
|
||||
/* Validate offset and size. */
|
||||
R_UNLESS(offset <= static_cast<s64>(this->partition_entry->size), fs::ResultOutOfRange());
|
||||
R_UNLESS(static_cast<s64>(offset + size) <= static_cast<s64>(this->partition_entry->size), fs::ResultInvalidSize());
|
||||
|
||||
/* Write to the base storage. */
|
||||
return this->parent->base_storage->Write(this->parent->meta_data_size + this->partition_entry->offset + offset, buffer, size);
|
||||
}
|
||||
|
||||
virtual Result SetSizeImpl(s64 size) override final {
|
||||
R_TRY(this->DrySetSize(size, this->mode));
|
||||
return fs::ResultUnsupportedOperationInPartitionFileA();
|
||||
}
|
||||
|
||||
virtual Result OperateRangeImpl(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override final {
|
||||
/* Validate preconditions for operation. */
|
||||
switch (op_id) {
|
||||
case fs::OperationId::InvalidateCache:
|
||||
R_UNLESS((this->mode & fs::OpenMode_Read) != 0, fs::ResultReadNotPermitted());
|
||||
R_UNLESS((this->mode & fs::OpenMode_Write) == 0, fs::ResultUnsupportedOperationInPartitionFileB());
|
||||
break;
|
||||
case fs::OperationId::QueryRange:
|
||||
break;
|
||||
default:
|
||||
return fs::ResultUnsupportedOperationInPartitionFileB();
|
||||
}
|
||||
|
||||
/* Validate offset and size. */
|
||||
R_UNLESS(offset >= 0, fs::ResultOutOfRange());
|
||||
R_UNLESS(offset <= static_cast<s64>(this->partition_entry->size), fs::ResultOutOfRange());
|
||||
R_UNLESS(static_cast<s64>(offset + size) <= static_cast<s64>(this->partition_entry->size), fs::ResultInvalidSize());
|
||||
R_UNLESS(static_cast<s64>(offset + size) >= offset, fs::ResultInvalidSize());
|
||||
|
||||
return this->parent->base_storage->OperateRange(dst, dst_size, op_id, this->parent->meta_data_size + this->partition_entry->offset + offset, size, src, src_size);
|
||||
}
|
||||
public:
|
||||
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
|
||||
/* TODO: How should this be handled? */
|
||||
return sf::cmif::InvalidDomainObjectId;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
Result PartitionFileSystemCore<PartitionFileSystemMeta>::PartitionFile::ReadImpl(size_t *out, s64 offset, void *dst, size_t dst_size, const fs::ReadOption &option) {
|
||||
/* Perform a dry read. */
|
||||
size_t read_size = 0;
|
||||
R_TRY(this->DryRead(std::addressof(read_size), offset, dst_size, option, this->mode));
|
||||
|
||||
/* Read from the base storage. */
|
||||
R_TRY(this->parent->base_storage->Read(this->parent->meta_data_size + this->partition_entry->offset + offset, dst, read_size));
|
||||
|
||||
/* Set output size. */
|
||||
*out = read_size;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template<>
|
||||
Result PartitionFileSystemCore<Sha256PartitionFileSystemMeta>::PartitionFile::ReadImpl(size_t *out, s64 offset, void *dst, size_t dst_size, const fs::ReadOption &option) {
|
||||
/* Perform a dry read. */
|
||||
size_t read_size = 0;
|
||||
R_TRY(this->DryRead(std::addressof(read_size), offset, dst_size, option, this->mode));
|
||||
|
||||
const s64 entry_start = this->parent->meta_data_size + this->partition_entry->offset;
|
||||
const s64 read_end = static_cast<s64>(offset + read_size);
|
||||
const s64 hash_start = static_cast<s64>(this->partition_entry->hash_target_offset);
|
||||
const s64 hash_end = hash_start + this->partition_entry->hash_target_size;
|
||||
|
||||
if (read_end <= hash_start || hash_end <= offset) {
|
||||
/* We aren't reading hashed data, so we can just read from the base storage. */
|
||||
R_TRY(this->parent->base_storage->Read(entry_start + offset, dst, read_size));
|
||||
} else {
|
||||
/* Only hash target offset == 0 is supported. */
|
||||
R_UNLESS(hash_start == 0, fs::ResultInvalidSha256PartitionHashTarget());
|
||||
|
||||
/* Ensure that the hash region is valid. */
|
||||
R_UNLESS(this->partition_entry->hash_target_offset + this->partition_entry->hash_target_size <= this->partition_entry->size, fs::ResultInvalidSha256PartitionHashTarget());
|
||||
|
||||
/* Validate our read offset. */
|
||||
const s64 read_offset = entry_start + offset;
|
||||
R_UNLESS(read_offset >= offset, fs::ResultOutOfRange());
|
||||
|
||||
/* Prepare a buffer for our calculated hash. */
|
||||
char hash[crypto::Sha256Generator::HashSize];
|
||||
crypto::Sha256Generator generator;
|
||||
|
||||
/* Ensure we can perform our read. */
|
||||
const bool hash_in_read = offset <= hash_start && hash_end <= read_end;
|
||||
const bool read_in_hash = hash_start <= offset && read_end <= hash_end;
|
||||
R_UNLESS(hash_in_read || read_in_hash, fs::ResultInvalidSha256PartitionHashTarget());
|
||||
|
||||
/* Initialize the generator. */
|
||||
generator.Initialize();
|
||||
|
||||
if (hash_in_read) {
|
||||
/* Easy case: hash region is contained within the bounds. */
|
||||
R_TRY(this->parent->base_storage->Read(entry_start + offset, dst, read_size));
|
||||
generator.Update(static_cast<u8 *>(dst) + hash_start - offset, this->partition_entry->hash_target_size);
|
||||
} else /* if (read_in_hash) */ {
|
||||
/* We're reading a portion of what's hashed. */
|
||||
s64 remaining_hash_size = this->partition_entry->hash_target_size;
|
||||
s64 hash_offset = entry_start + hash_start;
|
||||
s64 remaining_size = read_size;
|
||||
s64 copy_offset = 0;
|
||||
while (remaining_hash_size > 0) {
|
||||
/* Read some portion of data into the buffer. */
|
||||
constexpr size_t HashBufferSize = 0x200;
|
||||
char hash_buffer[HashBufferSize];
|
||||
size_t cur_size = static_cast<size_t>(std::min(static_cast<s64>(HashBufferSize), remaining_hash_size));
|
||||
R_TRY(this->parent->base_storage->Read(hash_offset, hash_buffer, cur_size));
|
||||
|
||||
/* Update the hash. */
|
||||
generator.Update(hash_buffer, cur_size);
|
||||
|
||||
/* If we need to copy, do so. */
|
||||
if (read_offset <= (hash_offset + static_cast<s64>(cur_size)) && remaining_size > 0) {
|
||||
const s64 hash_buffer_offset = std::max<s64>(read_offset - hash_offset, 0);
|
||||
const size_t copy_size = static_cast<size_t>(std::min<s64>(cur_size - hash_buffer_offset, remaining_size));
|
||||
std::memcpy(static_cast<u8 *>(dst) + copy_offset, hash_buffer + hash_buffer_offset, copy_size);
|
||||
remaining_size -= copy_size;
|
||||
copy_offset += copy_size;
|
||||
}
|
||||
|
||||
/* Update offsets. */
|
||||
remaining_hash_size -= cur_size;
|
||||
hash_offset += cur_size;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the hash. */
|
||||
generator.GetHash(hash, sizeof(hash));
|
||||
|
||||
/* Validate the hash. */
|
||||
auto hash_guard = SCOPE_GUARD { std::memset(dst, 0, read_size); };
|
||||
R_UNLESS(crypto::IsSameBytes(this->partition_entry->hash, hash, sizeof(hash)), fs::ResultSha256PartitionHashVerificationFailed());
|
||||
|
||||
/* We successfully completed our read. */
|
||||
hash_guard.Cancel();
|
||||
}
|
||||
|
||||
/* Set output size. */
|
||||
*out = read_size;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
class PartitionFileSystemCore<MetaType>::PartitionDirectory : public fs::fsa::IDirectory, public fs::impl::Newable {
|
||||
private:
|
||||
u32 cur_index;
|
||||
const PartitionFileSystemCore<MetaType> *parent;
|
||||
const fs::OpenDirectoryMode mode;
|
||||
public:
|
||||
PartitionDirectory(PartitionFileSystemCore<MetaType> *parent, fs::OpenDirectoryMode mode) : cur_index(0), parent(parent), mode(mode) { /* ... */ }
|
||||
public:
|
||||
virtual Result ReadImpl(s64 *out_count, fs::DirectoryEntry *out_entries, s64 max_entries) override final {
|
||||
/* There are no subdirectories. */
|
||||
if ((this->mode & fs::OpenDirectoryMode_File) == 0) {
|
||||
*out_count = 0;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
/* Calculate number of entries. */
|
||||
const s64 entry_count = std::min(max_entries, static_cast<s64>(this->parent->meta_data->GetEntryCount() - this->cur_index));
|
||||
|
||||
/* Populate output directory entries. */
|
||||
for (auto i = 0; i < entry_count; i++, this->cur_index++) {
|
||||
fs::DirectoryEntry &dir_entry = out_entries[i];
|
||||
|
||||
/* Setup the output directory entry. */
|
||||
dir_entry.type = fs::DirectoryEntryType_File;
|
||||
dir_entry.file_size = this->parent->meta_data->GetEntry(this->cur_index)->size;
|
||||
std::strncpy(dir_entry.name, this->parent->meta_data->GetEntryName(this->cur_index), sizeof(dir_entry.name) - 1);
|
||||
dir_entry.name[sizeof(dir_entry.name) - 1] = StringTraits::NullTerminator;
|
||||
}
|
||||
|
||||
*out_count = entry_count;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual Result GetEntryCountImpl(s64 *out) override final {
|
||||
/* Output the parent meta data entry count for files, otherwise 0. */
|
||||
if (this->mode & fs::OpenDirectoryMode_File) {
|
||||
*out = this->parent->meta_data->GetEntryCount();
|
||||
} else {
|
||||
*out = 0;
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
|
||||
/* TODO: How should this be handled? */
|
||||
return sf::cmif::InvalidDomainObjectId;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename MetaType>
|
||||
PartitionFileSystemCore<MetaType>::PartitionFileSystemCore() : initialized(false) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
PartitionFileSystemCore<MetaType>::~PartitionFileSystemCore() {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::Initialize(fs::IStorage *base_storage, MemoryResource *allocator) {
|
||||
/* Validate preconditions. */
|
||||
R_UNLESS(!this->initialized, fs::ResultPreconditionViolation());
|
||||
|
||||
/* Allocate meta data. */
|
||||
this->unique_meta_data = std::make_unique<MetaType>();
|
||||
R_UNLESS(this->unique_meta_data != nullptr, fs::ResultAllocationFailureInPartitionFileSystemA());
|
||||
|
||||
/* Initialize meta data. */
|
||||
R_TRY(this->unique_meta_data->Initialize(base_storage, allocator));
|
||||
|
||||
/* Initialize members. */
|
||||
this->meta_data = this->unique_meta_data.get();
|
||||
this->base_storage = base_storage;
|
||||
this->meta_data_size = this->meta_data->GetMetaDataSize();
|
||||
this->initialized = true;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::Initialize(std::unique_ptr<MetaType> &&meta_data, std::shared_ptr<fs::IStorage> base_storage) {
|
||||
this->unique_meta_data = std::move(meta_data);
|
||||
return this->Initialize(this->unique_meta_data.get(), base_storage);
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::Initialize(MetaType *meta_data, std::shared_ptr<fs::IStorage> base_storage) {
|
||||
/* Validate preconditions. */
|
||||
R_UNLESS(!this->initialized, fs::ResultPreconditionViolation());
|
||||
|
||||
/* Initialize members. */
|
||||
this->shared_storage = std::move(base_storage);
|
||||
this->base_storage = this->shared_storage.get();
|
||||
this->meta_data = meta_data;
|
||||
this->meta_data_size = this->meta_data->GetMetaDataSize();
|
||||
this->initialized = true;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::Initialize(fs::IStorage *base_storage) {
|
||||
return this->Initialize(base_storage, std::addressof(g_partition_filesystem_default_allocator));
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::Initialize(std::shared_ptr<fs::IStorage> base_storage) {
|
||||
this->shared_storage = std::move(base_storage);
|
||||
return this->Initialize(this->shared_storage.get());
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::Initialize(std::shared_ptr<fs::IStorage> base_storage, MemoryResource *allocator) {
|
||||
this->shared_storage = std::move(base_storage);
|
||||
return this->Initialize(this->shared_storage.get(), allocator);
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::GetFileBaseOffset(s64 *out_offset, const char *path) {
|
||||
/* Validate preconditions. */
|
||||
R_UNLESS(this->initialized, fs::ResultPreconditionViolation());
|
||||
|
||||
/* Obtain and validate the entry index. */
|
||||
const s32 entry_index = this->meta_data->GetEntryIndex(path + 1);
|
||||
R_UNLESS(entry_index >= 0, fs::ResultPathNotFound());
|
||||
|
||||
/* Output offset. */
|
||||
*out_offset = this->meta_data_size + this->meta_data->GetEntry(entry_index)->offset;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) {
|
||||
/* Validate preconditions. */
|
||||
R_UNLESS(this->initialized, fs::ResultPreconditionViolation());
|
||||
R_UNLESS(PathTool::IsSeparator(path[0]), fs::ResultInvalidPathFormat());
|
||||
|
||||
/* Check if the path is for a directory. */
|
||||
if (std::strncmp(path, PathTool::RootPath, sizeof(PathTool::RootPath)) == 0) {
|
||||
*out = fs::DirectoryEntryType_Directory;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
/* Ensure that path is for a file. */
|
||||
R_UNLESS(this->meta_data->GetEntryIndex(path + 1) >= 0, fs::ResultPathNotFound());
|
||||
|
||||
*out = fs::DirectoryEntryType_File;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::OpenFileImpl(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) {
|
||||
/* Validate preconditions. */
|
||||
R_UNLESS(this->initialized, fs::ResultPreconditionViolation());
|
||||
|
||||
/* Obtain and validate the entry index. */
|
||||
const s32 entry_index = this->meta_data->GetEntryIndex(path + 1);
|
||||
R_UNLESS(entry_index >= 0, fs::ResultPathNotFound());
|
||||
|
||||
/* Create and output the file directory. */
|
||||
std::unique_ptr file = std::make_unique<PartitionFile>(this, this->meta_data->GetEntry(entry_index), mode);
|
||||
R_UNLESS(file != nullptr, fs::ResultAllocationFailureInPartitionFileSystemB());
|
||||
*out_file = std::move(file);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::OpenDirectoryImpl(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) {
|
||||
/* Validate preconditions. */
|
||||
R_UNLESS(this->initialized, fs::ResultPreconditionViolation());
|
||||
R_UNLESS(std::strncmp(path, PathTool::RootPath, sizeof(PathTool::RootPath)) == 0, fs::ResultPathNotFound());
|
||||
|
||||
/* Create and output the partition directory. */
|
||||
std::unique_ptr directory = std::make_unique<PartitionDirectory>(this, mode);
|
||||
R_UNLESS(directory != nullptr, fs::ResultAllocationFailureInPartitionFileSystemC());
|
||||
*out_dir = std::move(directory);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::CommitImpl() {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::CleanDirectoryRecursivelyImpl(const char *path) {
|
||||
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::CreateDirectoryImpl(const char *path) {
|
||||
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::CreateFileImpl(const char *path, s64 size, int option) {
|
||||
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::DeleteDirectoryImpl(const char *path) {
|
||||
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::DeleteDirectoryRecursivelyImpl(const char *path) {
|
||||
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::DeleteFileImpl(const char *path) {
|
||||
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::RenameDirectoryImpl(const char *old_path, const char *new_path) {
|
||||
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::RenameFileImpl(const char *old_path, const char *new_path) {
|
||||
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::CommitProvisionallyImpl(s64 counter) {
|
||||
return fs::ResultUnsupportedOperationInPartitionFileSystemB();
|
||||
}
|
||||
|
||||
template class PartitionFileSystemCore<PartitionFileSystemMeta>;
|
||||
template class PartitionFileSystemCore<Sha256PartitionFileSystemMeta>;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Adubbz, Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
template <typename Format>
|
||||
struct PartitionFileSystemMetaCore<Format>::PartitionFileSystemHeader {
|
||||
char signature[sizeof(Format::VersionSignature)];
|
||||
s32 entry_count;
|
||||
u32 name_table_size;
|
||||
u32 reserved;
|
||||
};
|
||||
static_assert(std::is_pod<PartitionFileSystemMeta::PartitionFileSystemHeader>::value);
|
||||
static_assert(sizeof(PartitionFileSystemMeta::PartitionFileSystemHeader) == 0x10);
|
||||
|
||||
template <typename Format>
|
||||
PartitionFileSystemMetaCore<Format>::~PartitionFileSystemMetaCore() {
|
||||
this->DeallocateBuffer();
|
||||
}
|
||||
|
||||
template <typename Format>
|
||||
Result PartitionFileSystemMetaCore<Format>::Initialize(fs::IStorage *storage, MemoryResource *allocator) {
|
||||
/* Validate preconditions. */
|
||||
AMS_ASSERT(allocator != nullptr);
|
||||
|
||||
/* Determine the meta data size. */
|
||||
R_TRY(this->QueryMetaDataSize(std::addressof(this->meta_data_size), storage));
|
||||
|
||||
/* Deallocate any old meta buffer and allocate a new one. */
|
||||
this->DeallocateBuffer();
|
||||
this->allocator = allocator;
|
||||
this->buffer = static_cast<char *>(this->allocator->Allocate(this->meta_data_size));
|
||||
R_UNLESS(this->buffer != nullptr, fs::ResultAllocationFailureInPartitionFileSystemMetaA());
|
||||
|
||||
/* Perform regular initialization. */
|
||||
return this->Initialize(storage, this->buffer, this->meta_data_size);
|
||||
}
|
||||
|
||||
template <typename Format>
|
||||
Result PartitionFileSystemMetaCore<Format>::Initialize(fs::IStorage *storage, void *meta, size_t meta_size) {
|
||||
/* Validate size for header. */
|
||||
R_UNLESS(meta_size >= sizeof(PartitionFileSystemHeader), fs::ResultInvalidSize());
|
||||
|
||||
/* Read the header. */
|
||||
R_TRY(storage->Read(0, meta, sizeof(PartitionFileSystemHeader)));
|
||||
|
||||
/* Set and validate the header. */
|
||||
this->header = reinterpret_cast<PartitionFileSystemHeader *>(meta);
|
||||
R_UNLESS(crypto::IsSameBytes(this->header->signature, Format::VersionSignature, sizeof(Format::VersionSignature)), typename Format::ResultSignatureVerificationFailed());
|
||||
|
||||
/* Setup entries and name table. */
|
||||
const size_t entries_size = this->header->entry_count * sizeof(typename Format::PartitionEntry);
|
||||
this->entries = reinterpret_cast<PartitionEntry *>(static_cast<u8 *>(meta) + sizeof(PartitionFileSystemHeader));
|
||||
this->name_table = static_cast<char *>(meta) + sizeof(PartitionFileSystemHeader) + entries_size;
|
||||
|
||||
/* Validate size for header + entries + name table. */
|
||||
R_UNLESS(meta_size >= sizeof(PartitionFileSystemHeader) + entries_size + this->header->name_table_size, fs::ResultInvalidSize());
|
||||
|
||||
/* Read entries and name table. */
|
||||
R_TRY(storage->Read(sizeof(PartitionFileSystemHeader), this->entries, entries_size + this->header->name_table_size));
|
||||
|
||||
/* Mark as initialized. */
|
||||
this->initialized = true;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template <typename Format>
|
||||
void PartitionFileSystemMetaCore<Format>::DeallocateBuffer() {
|
||||
if (this->buffer != nullptr) {
|
||||
AMS_ABORT_UNLESS(this->allocator != nullptr);
|
||||
this->allocator->Deallocate(this->buffer, this->meta_data_size);
|
||||
this->buffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Format>
|
||||
const typename Format::PartitionEntry *PartitionFileSystemMetaCore<Format>::GetEntry(s32 index) const {
|
||||
if (this->initialized && 0 <= index && index < static_cast<s32>(this->header->entry_count)) {
|
||||
return std::addressof(this->entries[index]);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Format>
|
||||
s32 PartitionFileSystemMetaCore<Format>::GetEntryCount() const {
|
||||
if (this->initialized) {
|
||||
return this->header->entry_count;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename Format>
|
||||
s32 PartitionFileSystemMetaCore<Format>::GetEntryIndex(const char *name) const {
|
||||
/* Fail if not initialized. */
|
||||
if (!this->initialized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (s32 i = 0; i < static_cast<s32>(this->header->entry_count); i++) {
|
||||
const auto &entry = this->entries[i];
|
||||
|
||||
/* Name offset is invalid. */
|
||||
if (entry.name_offset >= this->header->name_table_size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Compare to input name. */
|
||||
const s32 max_name_len = this->header->name_table_size - entry.name_offset;
|
||||
if (std::strncmp(std::addressof(this->name_table[entry.name_offset]), name, max_name_len) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not found. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
template <typename Format>
|
||||
const char *PartitionFileSystemMetaCore<Format>::GetEntryName(s32 index) const {
|
||||
if (this->initialized && index < static_cast<s32>(this->header->entry_count)) {
|
||||
return std::addressof(this->name_table[this->GetEntry(index)->name_offset]);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Format>
|
||||
size_t PartitionFileSystemMetaCore<Format>::GetHeaderSize() const {
|
||||
return sizeof(PartitionFileSystemHeader);
|
||||
}
|
||||
|
||||
template <typename Format>
|
||||
size_t PartitionFileSystemMetaCore<Format>::GetMetaDataSize() const {
|
||||
return this->meta_data_size;
|
||||
}
|
||||
|
||||
template <typename Format>
|
||||
Result PartitionFileSystemMetaCore<Format>::QueryMetaDataSize(size_t *out_size, fs::IStorage *storage) {
|
||||
/* Read and validate the header. */
|
||||
PartitionFileSystemHeader header;
|
||||
R_TRY(storage->Read(0, std::addressof(header), sizeof(PartitionFileSystemHeader)));
|
||||
R_UNLESS(crypto::IsSameBytes(std::addressof(header), Format::VersionSignature, sizeof(Format::VersionSignature)), typename Format::ResultSignatureVerificationFailed());
|
||||
|
||||
/* Output size. */
|
||||
*out_size = sizeof(PartitionFileSystemHeader) + header.entry_count * sizeof(typename Format::PartitionEntry) + header.name_table_size;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template class PartitionFileSystemMetaCore<impl::PartitionFileSystemFormat>;
|
||||
template class PartitionFileSystemMetaCore<impl::Sha256PartitionFileSystemFormat>;
|
||||
|
||||
Result Sha256PartitionFileSystemMeta::Initialize(fs::IStorage *base_storage, MemoryResource *allocator, const void *hash, size_t hash_size, std::optional<u8> suffix) {
|
||||
/* Ensure preconditions. */
|
||||
R_UNLESS(hash_size == crypto::Sha256Generator::HashSize, fs::ResultPreconditionViolation());
|
||||
|
||||
/* Get metadata size. */
|
||||
R_TRY(QueryMetaDataSize(std::addressof(this->meta_data_size), base_storage));
|
||||
|
||||
/* Ensure we have no buffer. */
|
||||
this->DeallocateBuffer();
|
||||
|
||||
/* Set allocator and allocate buffer. */
|
||||
this->allocator = allocator;
|
||||
this->buffer = static_cast<char *>(this->allocator->Allocate(this->meta_data_size));
|
||||
R_UNLESS(this->buffer != nullptr, fs::ResultAllocationFailureInPartitionFileSystemMetaB());
|
||||
|
||||
/* Read metadata. */
|
||||
R_TRY(base_storage->Read(0, this->buffer, this->meta_data_size));
|
||||
|
||||
/* Calculate hash. */
|
||||
char calc_hash[crypto::Sha256Generator::HashSize];
|
||||
{
|
||||
crypto::Sha256Generator generator;
|
||||
generator.Initialize();
|
||||
generator.Update(this->buffer, this->meta_data_size);
|
||||
if (suffix) {
|
||||
u8 suffix_val = *suffix;
|
||||
generator.Update(std::addressof(suffix_val), 1);
|
||||
}
|
||||
generator.GetHash(calc_hash, sizeof(calc_hash));
|
||||
}
|
||||
|
||||
/* Ensure hash is valid. */
|
||||
R_UNLESS(crypto::IsSameBytes(hash, calc_hash, sizeof(calc_hash)), fs::ResultSha256PartitionHashVerificationFailed());
|
||||
|
||||
/* Give access to Format */
|
||||
using Format = impl::Sha256PartitionFileSystemFormat;
|
||||
|
||||
/* Set header. */
|
||||
this->header = reinterpret_cast<PartitionFileSystemHeader *>(this->buffer);
|
||||
R_UNLESS(crypto::IsSameBytes(this->header->signature, Format::VersionSignature, sizeof(Format::VersionSignature)), typename Format::ResultSignatureVerificationFailed());
|
||||
|
||||
/* Validate size for entries and name table. */
|
||||
const size_t entries_size = this->header->entry_count * sizeof(typename Format::PartitionEntry);
|
||||
R_UNLESS(this->meta_data_size >= sizeof(PartitionFileSystemHeader) + entries_size + this->header->name_table_size, fs::ResultInvalidSha256PartitionMetaDataSize());
|
||||
|
||||
/* Set entries and name table. */
|
||||
this->entries = reinterpret_cast<PartitionEntry *>(this->buffer + sizeof(PartitionFileSystemHeader));
|
||||
this->name_table = this->buffer + sizeof(PartitionFileSystemHeader) + entries_size;
|
||||
|
||||
/* We initialized. */
|
||||
this->initialized = true;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -284,7 +284,7 @@ namespace ams::lmem::impl {
|
||||
|
||||
{
|
||||
/* Create the used block */
|
||||
MemoryRegion used_region{ .start = free_region_front.end, .end = free_region_back.start };
|
||||
MemoryRegion used_region{ .start = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(block) - sizeof(ExpHeapMemoryBlockHead)), .end = free_region_back.start };
|
||||
|
||||
ExpHeapMemoryBlockHead *used_block = InitializeUsedMemoryBlock(used_region);
|
||||
|
||||
|
||||
@@ -22,8 +22,6 @@ namespace ams::mem::impl::heap {
|
||||
|
||||
namespace {
|
||||
|
||||
char g_log_buf[4_KB];
|
||||
|
||||
void InitializeSpanPage(SpanPage *sp) {
|
||||
static_assert(SpanPage::MaxSpanCount <= BITSIZEOF(u64));
|
||||
constexpr size_t NumUnusedBits = BITSIZEOF(u64) - SpanPage::MaxSpanCount;
|
||||
|
||||
@@ -73,12 +73,11 @@ namespace ams::ncm {
|
||||
|
||||
/* Allocate space to hold the converted meta. */
|
||||
const size_t meta_size = package_meta_reader.CalculateConvertContentMetaSize();
|
||||
void *meta = std::malloc(meta_size);
|
||||
ON_SCOPE_EXIT { std::free(meta); };
|
||||
std::unique_ptr<char[]> meta(new (std::nothrow) char[meta_size]);
|
||||
|
||||
/* Convert the meta from packaged form to normal form. */
|
||||
package_meta_reader.ConvertToContentMeta(meta, meta_size, meta_info);
|
||||
ncm::ContentMetaReader meta_reader(meta, meta_size);
|
||||
package_meta_reader.ConvertToContentMeta(meta.get(), meta_size, meta_info);
|
||||
ncm::ContentMetaReader meta_reader(meta.get(), meta_size);
|
||||
|
||||
/* Insert the new metas into the database. */
|
||||
R_TRY(this->db->Set(package_meta_reader.GetKey(), meta_reader.GetData(), meta_reader.GetSize()));
|
||||
|
||||
@@ -24,6 +24,14 @@ namespace ams::ncm {
|
||||
|
||||
namespace {
|
||||
|
||||
alignas(os::MemoryPageSize) u8 g_system_content_meta_database_heap[512_KB];
|
||||
alignas(os::MemoryPageSize) u8 g_gamecard_content_meta_database_heap[512_KB];
|
||||
alignas(os::MemoryPageSize) u8 g_sd_and_user_content_meta_database_heap[2_MB + 512_KB];
|
||||
|
||||
ContentMetaMemoryResource g_system_content_meta_memory_resource(g_system_content_meta_database_heap, sizeof(g_system_content_meta_database_heap));
|
||||
ContentMetaMemoryResource g_gamecard_content_meta_memory_resource(g_gamecard_content_meta_database_heap, sizeof(g_gamecard_content_meta_database_heap));
|
||||
ContentMetaMemoryResource g_sd_and_user_content_meta_memory_resource(g_sd_and_user_content_meta_database_heap, sizeof(g_sd_and_user_content_meta_database_heap));
|
||||
|
||||
constexpr fs::SystemSaveDataId BuiltInSystemSaveDataId = 0x8000000000000120;
|
||||
constexpr u64 BuiltInSystemSaveDataSize = 0x6c000;
|
||||
constexpr u64 BuiltInSystemSaveDataJournalSize = 0x6c000;
|
||||
@@ -216,10 +224,11 @@ namespace ams::ncm {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ContentManagerImpl::InitializeContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, StorageId storage_id, const SystemSaveDataInfo &info, size_t max_content_metas) {
|
||||
Result ContentManagerImpl::InitializeContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, StorageId storage_id, const SystemSaveDataInfo &info, size_t max_content_metas, ContentMetaMemoryResource *memory_resource) {
|
||||
out->storage_id = storage_id;
|
||||
out->info = info;
|
||||
out->max_content_metas = max_content_metas;
|
||||
out->memory_resource = memory_resource;
|
||||
out->content_meta_database = nullptr;
|
||||
out->kvs = std::nullopt;
|
||||
|
||||
@@ -231,9 +240,10 @@ namespace ams::ncm {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ContentManagerImpl::InitializeGameCardContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, size_t max_content_metas) {
|
||||
Result ContentManagerImpl::InitializeGameCardContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, size_t max_content_metas, ContentMetaMemoryResource *memory_resource) {
|
||||
out->storage_id = StorageId::GameCard;
|
||||
out->max_content_metas = max_content_metas;
|
||||
out->memory_resource = memory_resource;
|
||||
out->content_meta_database = nullptr;
|
||||
out->kvs = std::nullopt;
|
||||
|
||||
@@ -332,7 +342,7 @@ namespace ams::ncm {
|
||||
R_TRY(this->ActivateContentStorage(StorageId::BuiltInSystem));
|
||||
|
||||
/* Next, the BuiltInSystem content meta entry. */
|
||||
R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[this->num_content_meta_entries++], StorageId::BuiltInSystem, BuiltInSystemSystemSaveDataInfo, MaxBuiltInSystemContentMetaCount));
|
||||
R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[this->num_content_meta_entries++], StorageId::BuiltInSystem, BuiltInSystemSystemSaveDataInfo, MaxBuiltInSystemContentMetaCount, std::addressof(g_system_content_meta_memory_resource)));
|
||||
|
||||
if (R_FAILED(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem))) {
|
||||
R_TRY(this->CreateContentMetaDatabase(StorageId::BuiltInSystem));
|
||||
@@ -360,18 +370,18 @@ namespace ams::ncm {
|
||||
|
||||
/* Now for BuiltInUser's content storage and content meta entries. */
|
||||
R_TRY(this->InitializeContentStorageRoot(&this->content_storage_roots[this->num_content_storage_entries++], StorageId::BuiltInUser, fs::ContentStorageId::User));
|
||||
R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[this->num_content_meta_entries++], StorageId::BuiltInUser, BuiltInUserSystemSaveDataInfo, MaxBuiltInUserContentMetaCount));
|
||||
R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[this->num_content_meta_entries++], StorageId::BuiltInUser, BuiltInUserSystemSaveDataInfo, MaxBuiltInUserContentMetaCount, std::addressof(g_sd_and_user_content_meta_memory_resource)));
|
||||
|
||||
/* Beyond this point, N uses hardcoded indices. */
|
||||
|
||||
/* Next SdCard's content storage and content meta entries. */
|
||||
R_TRY(this->InitializeContentStorageRoot(&this->content_storage_roots[2], StorageId::SdCard, fs::ContentStorageId::SdCard));
|
||||
R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[2], StorageId::SdCard, SdCardSystemSaveDataInfo, MaxSdCardContentMetaCount));
|
||||
R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[2], StorageId::SdCard, SdCardSystemSaveDataInfo, MaxSdCardContentMetaCount, std::addressof(g_sd_and_user_content_meta_memory_resource)));
|
||||
|
||||
/* GameCard's content storage and content meta entries. */
|
||||
/* N doesn't set a content storage id for game cards, so we'll just use 0 (System). */
|
||||
R_TRY(this->InitializeGameCardContentStorageRoot(&this->content_storage_roots[3]));
|
||||
R_TRY(this->InitializeGameCardContentMetaDatabaseRoot(&this->content_meta_database_roots[3], MaxGameCardContentMetaCount));
|
||||
R_TRY(this->InitializeGameCardContentMetaDatabaseRoot(&this->content_meta_database_roots[3], MaxGameCardContentMetaCount, std::addressof(g_gamecard_content_meta_memory_resource)));
|
||||
|
||||
this->initialized = true;
|
||||
return ResultSuccess();
|
||||
@@ -609,7 +619,7 @@ namespace ams::ncm {
|
||||
|
||||
if (storage_id == StorageId::GameCard) {
|
||||
/* Initialize the key value store. */
|
||||
R_TRY(root->kvs->Initialize(root->max_content_metas));
|
||||
R_TRY(root->kvs->Initialize(root->max_content_metas, root->memory_resource->Get()));
|
||||
|
||||
/* Create an on memory content meta database for game cards. */
|
||||
root->content_meta_database = std::make_shared<OnMemoryContentMetaDatabaseImpl>(std::addressof(*root->kvs));
|
||||
@@ -621,7 +631,7 @@ namespace ams::ncm {
|
||||
auto mount_guard = SCOPE_GUARD { fs::Unmount(root->mount_name); };
|
||||
|
||||
/* Initialize and load the key value store from the filesystem. */
|
||||
R_TRY(root->kvs->Initialize(root->path, root->max_content_metas));
|
||||
R_TRY(root->kvs->Initialize(root->path, root->max_content_metas, root->memory_resource->Get()));
|
||||
R_TRY(root->kvs->Load());
|
||||
|
||||
/* Create the content meta database. */
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 Adubbz, Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::nim {
|
||||
|
||||
namespace {
|
||||
|
||||
bool g_initialized;
|
||||
|
||||
}
|
||||
/* Management. */
|
||||
void InitializeForNetworkInstallManager() {
|
||||
AMS_ASSERT(!g_initialized);
|
||||
R_ABORT_UNLESS(nimInitialize());
|
||||
g_initialized = true;
|
||||
}
|
||||
|
||||
void FinalizeForNetworkInstallManager() {
|
||||
AMS_ASSERT(g_initialized);
|
||||
nimExit();
|
||||
g_initialized = false;
|
||||
}
|
||||
|
||||
/* Service API. */
|
||||
Result DestroySystemUpdateTask(const SystemUpdateTaskId& id) {
|
||||
static_assert(sizeof(SystemUpdateTaskId) == sizeof(::NimSystemUpdateTaskId));
|
||||
return nimDestroySystemUpdateTask(reinterpret_cast<const ::NimSystemUpdateTaskId *>(std::addressof(id)));
|
||||
}
|
||||
|
||||
s32 ListSystemUpdateTask(SystemUpdateTaskId *out_list, size_t out_list_size) {
|
||||
static_assert(sizeof(SystemUpdateTaskId) == sizeof(::NimSystemUpdateTaskId));
|
||||
|
||||
s32 count;
|
||||
R_ABORT_UNLESS(nimListSystemUpdateTask(std::addressof(count), reinterpret_cast<::NimSystemUpdateTaskId *>(out_list), out_list_size));
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -33,7 +33,6 @@ namespace ams::updater {
|
||||
bool HasEks(BootImageUpdateType boot_image_update_type);
|
||||
bool HasAutoRcmPreserve(BootImageUpdateType boot_image_update_type);
|
||||
ncm::ContentMetaType GetContentMetaType(BootModeType mode);
|
||||
Result GetBootImagePackageId(ncm::SystemDataId *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size);
|
||||
|
||||
/* Verification Prototypes. */
|
||||
Result GetVerificationState(VerificationState *out, void *work_buffer, size_t work_buffer_size);
|
||||
@@ -42,8 +41,7 @@ namespace ams::updater {
|
||||
Result VerifyBootImagesSafe(ncm::SystemDataId data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
|
||||
|
||||
/* Update Prototypes. */
|
||||
Result SetVerificationNeeded(BootModeType mode, bool needed, void *work_buffer, size_t work_buffer_size);
|
||||
Result UpdateBootImages(ncm::SystemDataId data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
|
||||
Result SetVerificationNeeded(BootModeType mode, void *work_buffer, size_t work_buffer_size, bool needed);
|
||||
Result UpdateBootImagesNormal(ncm::SystemDataId data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
|
||||
Result UpdateBootImagesSafe(ncm::SystemDataId data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
|
||||
|
||||
@@ -123,45 +121,12 @@ namespace ams::updater {
|
||||
R_CATCH(ResultNeedsRepairBootImages) {
|
||||
/* Perform repair. */
|
||||
*out_repaired = true;
|
||||
R_TRY(UpdateBootImages(bip_data_id, mode, work_buffer, work_buffer_size, boot_image_update_type));
|
||||
R_TRY(UpdateBootImagesFromPackage(bip_data_id, mode, work_buffer, work_buffer_size, boot_image_update_type));
|
||||
}
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
/* We've either just verified or just repaired. Either way, we don't need to verify any more. */
|
||||
return SetVerificationNeeded(mode, false, work_buffer, work_buffer_size);
|
||||
}
|
||||
|
||||
Result GetBootImagePackageId(ncm::SystemDataId *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size) {
|
||||
/* Ensure we can read content metas. */
|
||||
constexpr size_t MaxContentMetas = 0x40;
|
||||
AMS_ABORT_UNLESS(work_buffer_size >= sizeof(ncm::ContentMetaKey) * MaxContentMetas);
|
||||
|
||||
/* Open NAND System meta database, list contents. */
|
||||
ncm::ContentMetaDatabase db;
|
||||
R_TRY(ncm::OpenContentMetaDatabase(std::addressof(db), ncm::StorageId::BuiltInSystem));
|
||||
|
||||
ncm::ContentMetaKey *keys = reinterpret_cast<ncm::ContentMetaKey *>(work_buffer);
|
||||
const auto content_meta_type = GetContentMetaType(mode);
|
||||
|
||||
auto count = db.ListContentMeta(keys, MaxContentMetas, content_meta_type);
|
||||
R_UNLESS(count.total > 0, ResultBootImagePackageNotFound());
|
||||
|
||||
/* Output is sorted, return the lowest valid exfat entry. */
|
||||
if (count.total > 1) {
|
||||
for (auto i = 0; i < count.total; i++) {
|
||||
u8 attr;
|
||||
R_TRY(db.GetAttributes(std::addressof(attr), keys[i]));
|
||||
|
||||
if (attr & ncm::ContentMetaAttribute_IncludesExFatDriver) {
|
||||
out_data_id->value = keys[i].id;
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If there's only one entry or no exfat entries, return that entry. */
|
||||
out_data_id->value = keys[0].id;
|
||||
return ResultSuccess();
|
||||
return SetVerificationNeeded(mode, work_buffer, work_buffer_size, false);
|
||||
}
|
||||
|
||||
Result VerifyBootImages(ncm::SystemDataId data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) {
|
||||
@@ -281,16 +246,6 @@ namespace ams::updater {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result UpdateBootImages(ncm::SystemDataId data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) {
|
||||
switch (mode) {
|
||||
case BootModeType::Normal:
|
||||
return UpdateBootImagesNormal(data_id, work_buffer, work_buffer_size, boot_image_update_type);
|
||||
case BootModeType::Safe:
|
||||
return UpdateBootImagesSafe(data_id, work_buffer, work_buffer_size, boot_image_update_type);
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
Result UpdateBootImagesNormal(ncm::SystemDataId data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) {
|
||||
/* Ensure work buffer is big enough for us to do what we want to do. */
|
||||
R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size));
|
||||
@@ -409,7 +364,7 @@ namespace ams::updater {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result SetVerificationNeeded(BootModeType mode, bool needed, void *work_buffer, size_t work_buffer_size) {
|
||||
Result SetVerificationNeeded(BootModeType mode, void *work_buffer, size_t work_buffer_size, bool needed) {
|
||||
/* Ensure work buffer is big enough for us to do what we want to do. */
|
||||
R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size));
|
||||
|
||||
@@ -485,6 +440,67 @@ namespace ams::updater {
|
||||
}
|
||||
}
|
||||
|
||||
BootImageUpdateType GetBootImageUpdateType(int boot_image_update_type) {
|
||||
switch (boot_image_update_type) {
|
||||
case 0:
|
||||
return BootImageUpdateType::Erista;
|
||||
case 1:
|
||||
return BootImageUpdateType::Mariko;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
Result GetBootImagePackageId(ncm::SystemDataId *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size) {
|
||||
/* Ensure we can read content metas. */
|
||||
constexpr size_t MaxContentMetas = 0x40;
|
||||
AMS_ABORT_UNLESS(work_buffer_size >= sizeof(ncm::ContentMetaKey) * MaxContentMetas);
|
||||
|
||||
/* Open NAND System meta database, list contents. */
|
||||
ncm::ContentMetaDatabase db;
|
||||
R_TRY(ncm::OpenContentMetaDatabase(std::addressof(db), ncm::StorageId::BuiltInSystem));
|
||||
|
||||
ncm::ContentMetaKey *keys = reinterpret_cast<ncm::ContentMetaKey *>(work_buffer);
|
||||
const auto content_meta_type = GetContentMetaType(mode);
|
||||
|
||||
auto count = db.ListContentMeta(keys, MaxContentMetas, content_meta_type);
|
||||
R_UNLESS(count.total > 0, ResultBootImagePackageNotFound());
|
||||
|
||||
/* Output is sorted, return the lowest valid exfat entry. */
|
||||
if (count.total > 1) {
|
||||
for (auto i = 0; i < count.total; i++) {
|
||||
u8 attr;
|
||||
R_TRY(db.GetAttributes(std::addressof(attr), keys[i]));
|
||||
|
||||
if (attr & ncm::ContentMetaAttribute_IncludesExFatDriver) {
|
||||
out_data_id->value = keys[i].id;
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If there's only one entry or no exfat entries, return that entry. */
|
||||
out_data_id->value = keys[0].id;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result MarkVerifyingRequired(BootModeType mode, void *work_buffer, size_t work_buffer_size) {
|
||||
return SetVerificationNeeded(mode, work_buffer, work_buffer_size, true);
|
||||
}
|
||||
|
||||
Result MarkVerified(BootModeType mode, void *work_buffer, size_t work_buffer_size) {
|
||||
return SetVerificationNeeded(mode, work_buffer, work_buffer_size, false);
|
||||
}
|
||||
|
||||
Result UpdateBootImagesFromPackage(ncm::SystemDataId data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) {
|
||||
switch (mode) {
|
||||
case BootModeType::Normal:
|
||||
return UpdateBootImagesNormal(data_id, work_buffer, work_buffer_size, boot_image_update_type);
|
||||
case BootModeType::Safe:
|
||||
return UpdateBootImagesSafe(data_id, work_buffer, work_buffer_size, boot_image_update_type);
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
Result VerifyBootImagesAndRepairIfNeeded(bool *out_repaired_normal, bool *out_repaired_safe, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) {
|
||||
/* Always set output to false before doing anything else. */
|
||||
*out_repaired_normal = false;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/literals.hpp>
|
||||
|
||||
#include <vapours/allocator.hpp>
|
||||
#include <vapours/timespan.hpp>
|
||||
#include <vapours/span.hpp>
|
||||
|
||||
|
||||
58
libraries/libvapours/include/vapours/allocator.hpp
Normal file
58
libraries/libvapours/include/vapours/allocator.hpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams {
|
||||
|
||||
constexpr inline size_t DefaultAlignment = alignof(max_align_t);
|
||||
|
||||
using AllocateFunction = void *(*)(size_t);
|
||||
using AllocateFunctionWithUserData = void *(*)(size_t, void *);
|
||||
using AlignedAllocateFunction = void *(*)(size_t, size_t);
|
||||
using AlignedAllocateFunctionWithUserData = void *(*)(size_t, size_t, void *);
|
||||
using DeallocateFunction = void (*)(void *, size_t);
|
||||
using FreeFunction = void (*)(void *);
|
||||
using FreeFunctionWithUserData = void (*)(void *, void *);
|
||||
|
||||
class MemoryResource {
|
||||
public:
|
||||
ALWAYS_INLINE void *allocate(size_t size, size_t alignment = DefaultAlignment) {
|
||||
return this->AllocateImpl(size, alignment);
|
||||
}
|
||||
ALWAYS_INLINE void deallocate(void *buffer, size_t size, size_t alignment = DefaultAlignment) {
|
||||
this->DeallocateImpl(buffer, size, alignment);
|
||||
}
|
||||
ALWAYS_INLINE bool is_equal(const MemoryResource &resource) const {
|
||||
return this->IsEqualImpl(resource);
|
||||
}
|
||||
ALWAYS_INLINE void *Allocate(size_t size, size_t alignment = DefaultAlignment) {
|
||||
return this->AllocateImpl(size, alignment);
|
||||
}
|
||||
ALWAYS_INLINE void Deallocate(void *buffer, size_t size, size_t alignment = DefaultAlignment) {
|
||||
this->DeallocateImpl(buffer, size, alignment);
|
||||
}
|
||||
ALWAYS_INLINE bool IsEqual(const MemoryResource &resource) const {
|
||||
return this->IsEqualImpl(resource);
|
||||
}
|
||||
protected:
|
||||
virtual void *AllocateImpl(size_t size, size_t alignment) = 0;
|
||||
virtual void DeallocateImpl(void *buffer, size_t size, size_t alignment) = 0;
|
||||
virtual bool IsEqualImpl(const MemoryResource &resource) const = 0;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -38,7 +38,7 @@
|
||||
#define WRAP_TEMPLATE_CONSTANT(...) ([] { using U = union { static constexpr auto GetValue() { return __VA_ARGS__; } }; return U{}; }())
|
||||
#define UNWRAP_TEMPLATE_CONSTANT(tpnm) (tpnm::GetValue())
|
||||
|
||||
#define CONCATENATE_IMPL(S1, s2) s1##s2
|
||||
#define CONCATENATE_IMPL(s1, s2) s1##s2
|
||||
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
|
||||
|
||||
#define BITSIZEOF(x) (sizeof(x) * CHAR_BIT)
|
||||
|
||||
@@ -68,13 +68,21 @@ namespace ams::fs {
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardB, 3226);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardC, 3227);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardD, 3228);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInImageDirectoryA, 3232);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInSdCardA, 3244);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInSdCardB, 3245);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInSystemSaveDataA, 3246);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemA, 3247);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemB, 3248);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemC, 3249);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemCreatorA, 3280);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInDirectorySaveDataFileSystem, 3321);
|
||||
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemA, 3347);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemB, 3348);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemC, 3349);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemMetaA, 3350);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemMetaB, 3351);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemD, 3352);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInSubDirectoryFileSystem, 3355);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInRegisterA, 3365);
|
||||
@@ -238,6 +246,8 @@ namespace ams::fs {
|
||||
|
||||
R_DEFINE_ERROR_RANGE(InvalidOperationForOpenMode, 6200, 6299);
|
||||
R_DEFINE_ERROR_RESULT(FileExtensionWithoutOpenModeAllowAppend, 6201);
|
||||
R_DEFINE_ERROR_RESULT(ReadNotPermitted, 6202);
|
||||
R_DEFINE_ERROR_RESULT(WriteNotPermitted, 6203);
|
||||
|
||||
R_DEFINE_ERROR_RANGE(UnsupportedOperation, 6300, 6399);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageA, 6302);
|
||||
@@ -245,6 +255,7 @@ namespace ams::fs {
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageA, 6304);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageB, 6305);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileStorageA, 6306);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileStorageB, 6307);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileServiceObjectAdapterA, 6362);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemA, 6364);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemB, 6365);
|
||||
@@ -256,6 +267,10 @@ namespace ams::fs {
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileSystemTemplateC, 6371);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileA, 6372);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileB, 6373);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileSystemA, 6374);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileSystemB, 6375);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileA, 6376);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileB, 6377);
|
||||
|
||||
R_DEFINE_ERROR_RANGE(PermissionDenied, 6400, 6449);
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace ams::pm {
|
||||
|
||||
R_DEFINE_ERROR_RESULT(ProcessNotFound, 1);
|
||||
R_DEFINE_ERROR_RESULT(AlreadyStarted, 2);
|
||||
R_DEFINE_ERROR_RESULT(NotExited, 3);
|
||||
R_DEFINE_ERROR_RESULT(NotTerminated, 3);
|
||||
R_DEFINE_ERROR_RESULT(DebugHookInUse, 4);
|
||||
R_DEFINE_ERROR_RESULT(ApplicationRunning, 5);
|
||||
R_DEFINE_ERROR_RESULT(InvalidSize, 6);
|
||||
|
||||
@@ -176,7 +176,7 @@ namespace ams::mitm::fs {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result FsMitmService::OpenSaveDataFileSystem(sf::Out<std::shared_ptr<IFileSystemInterface>> out, u8 _space_id, const FsSaveDataAttribute &attribute) {
|
||||
Result FsMitmService::OpenSaveDataFileSystem(sf::Out<std::shared_ptr<IFileSystemInterface>> out, u8 _space_id, const fs::SaveDataAttribute &attribute) {
|
||||
/* We only want to intercept saves for games, right now. */
|
||||
const bool is_game_or_hbl = this->client_info.override_status.IsHbl() || ncm::IsApplicationId(this->client_info.program_id);
|
||||
R_UNLESS(is_game_or_hbl, sm::mitm::ResultShouldForwardToSession());
|
||||
@@ -188,14 +188,15 @@ namespace ams::mitm::fs {
|
||||
R_UNLESS(cfg::HasContentSpecificFlag(this->client_info.program_id, "redirect_save"), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Only redirect account savedata. */
|
||||
R_UNLESS(attribute.save_data_type == FsSaveDataType_Account, sm::mitm::ResultShouldForwardToSession());
|
||||
R_UNLESS(attribute.type == fs::SaveDataType::Account, sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Get enum type for space id. */
|
||||
auto space_id = static_cast<FsSaveDataSpaceId>(_space_id);
|
||||
|
||||
/* Verify we can open the save. */
|
||||
static_assert(sizeof(fs::SaveDataAttribute) == sizeof(::FsSaveDataAttribute));
|
||||
FsFileSystem save_fs;
|
||||
R_UNLESS(R_SUCCEEDED(fsOpenSaveDataFileSystemFwd(this->forward_service.get(), &save_fs, space_id, &attribute)), sm::mitm::ResultShouldForwardToSession());
|
||||
R_UNLESS(R_SUCCEEDED(fsOpenSaveDataFileSystemFwd(this->forward_service.get(), &save_fs, space_id, reinterpret_cast<const FsSaveDataAttribute *>(&attribute))), sm::mitm::ResultShouldForwardToSession());
|
||||
std::unique_ptr<fs::fsa::IFileSystem> save_ifs = std::make_unique<fs::RemoteFileSystem>(save_fs);
|
||||
|
||||
/* Mount the SD card using fs.mitm's session. */
|
||||
@@ -205,7 +206,7 @@ namespace ams::mitm::fs {
|
||||
std::shared_ptr<fs::fsa::IFileSystem> sd_ifs = std::make_shared<fs::RemoteFileSystem>(sd_fs);
|
||||
|
||||
/* Verify that we can open the save directory, and that it exists. */
|
||||
const ncm::ProgramId application_id = attribute.application_id == 0 ? this->client_info.program_id : ncm::ProgramId{attribute.application_id};
|
||||
const ncm::ProgramId application_id = attribute.program_id == ncm::InvalidProgramId ? this->client_info.program_id : attribute.program_id;
|
||||
char save_dir_path[fs::EntryNameLengthMax + 1];
|
||||
R_TRY(mitm::fs::SaveUtil::GetDirectorySaveDataPath(save_dir_path, sizeof(save_dir_path), application_id, space_id, attribute));
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace ams::mitm::fs {
|
||||
Result OpenFileSystemWithPatch(sf::Out<std::shared_ptr<IFileSystemInterface>> out, ncm::ProgramId program_id, u32 _filesystem_type);
|
||||
Result OpenFileSystemWithId(sf::Out<std::shared_ptr<IFileSystemInterface>> out, const fssrv::sf::Path &path, ncm::ProgramId program_id, u32 _filesystem_type);
|
||||
Result OpenSdCardFileSystem(sf::Out<std::shared_ptr<IFileSystemInterface>> out);
|
||||
Result OpenSaveDataFileSystem(sf::Out<std::shared_ptr<IFileSystemInterface>> out, u8 space_id, const FsSaveDataAttribute &attribute);
|
||||
Result OpenSaveDataFileSystem(sf::Out<std::shared_ptr<IFileSystemInterface>> out, u8 space_id, const ams::fs::SaveDataAttribute &attribute);
|
||||
Result OpenBisStorage(sf::Out<std::shared_ptr<IStorageInterface>> out, u32 bis_partition_id);
|
||||
Result OpenDataStorageByCurrentProcess(sf::Out<std::shared_ptr<IStorageInterface>> out);
|
||||
Result OpenDataStorageByDataId(sf::Out<std::shared_ptr<IStorageInterface>> out, ncm::DataId data_id, u8 storage_id);
|
||||
|
||||
@@ -22,24 +22,24 @@ namespace ams::mitm::fs {
|
||||
namespace {
|
||||
|
||||
Result GetSaveDataSpaceIdString(const char **out_str, u8 space_id) {
|
||||
switch (space_id) {
|
||||
case FsSaveDataSpaceId_System:
|
||||
case FsSaveDataSpaceId_ProperSystem:
|
||||
switch (static_cast<SaveDataSpaceId>(space_id)) {
|
||||
case SaveDataSpaceId::System:
|
||||
case SaveDataSpaceId::ProperSystem:
|
||||
*out_str = "sys";
|
||||
break;
|
||||
case FsSaveDataSpaceId_User:
|
||||
case SaveDataSpaceId::User:
|
||||
*out_str = "user";
|
||||
break;
|
||||
case FsSaveDataSpaceId_SdSystem:
|
||||
case SaveDataSpaceId::SdSystem:
|
||||
*out_str = "sd_sys";
|
||||
break;
|
||||
case FsSaveDataSpaceId_Temporary:
|
||||
case SaveDataSpaceId::Temporary:
|
||||
*out_str = "temp";
|
||||
break;
|
||||
case FsSaveDataSpaceId_SdUser:
|
||||
case SaveDataSpaceId::SdUser:
|
||||
*out_str = "sd_user";
|
||||
break;
|
||||
case FsSaveDataSpaceId_SafeMode:
|
||||
case SaveDataSpaceId::SafeMode:
|
||||
*out_str = "safe";
|
||||
break;
|
||||
default:
|
||||
@@ -49,27 +49,27 @@ namespace ams::mitm::fs {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GetSaveDataTypeString(const char **out_str, u8 save_data_type) {
|
||||
Result GetSaveDataTypeString(const char **out_str, SaveDataType save_data_type) {
|
||||
switch (save_data_type) {
|
||||
case FsSaveDataType_System:
|
||||
case SaveDataType::System:
|
||||
*out_str = "system";
|
||||
break;
|
||||
case FsSaveDataType_Account:
|
||||
case SaveDataType::Account:
|
||||
*out_str = "account";
|
||||
break;
|
||||
case FsSaveDataType_Bcat:
|
||||
case SaveDataType::Bcat:
|
||||
*out_str = "bcat";
|
||||
break;
|
||||
case FsSaveDataType_Device:
|
||||
case SaveDataType::Device:
|
||||
*out_str = "device";
|
||||
break;
|
||||
case FsSaveDataType_Temporary:
|
||||
case SaveDataType::Temporary:
|
||||
*out_str = "temp";
|
||||
break;
|
||||
case FsSaveDataType_Cache:
|
||||
case SaveDataType::Cache:
|
||||
*out_str = "cache";
|
||||
break;
|
||||
case FsSaveDataType_SystemBcat:
|
||||
case SaveDataType::SystemBcat:
|
||||
*out_str = "system_bcat";
|
||||
break;
|
||||
default:
|
||||
@@ -80,30 +80,29 @@ namespace ams::mitm::fs {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
constexpr inline bool IsEmptyAccountId(const AccountUid &uid) {
|
||||
constexpr AccountUid empty_uid = {};
|
||||
return std::memcmp(&uid, &empty_uid, sizeof(uid)) == 0;
|
||||
constexpr inline bool IsEmptyAccountId(const UserId &uid) {
|
||||
return uid == InvalidUserId;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Result SaveUtil::GetDirectorySaveDataPath(char *dst, size_t dst_size, ncm::ProgramId program_id, u8 space_id, const FsSaveDataAttribute &attribute) {
|
||||
Result SaveUtil::GetDirectorySaveDataPath(char *dst, size_t dst_size, ncm::ProgramId program_id, u8 space_id, const fs::SaveDataAttribute &attribute) {
|
||||
/* Saves should be separate for emunand vs sysnand. */
|
||||
const char *emummc_str = emummc::IsActive() ? "emummc" : "sysmmc";
|
||||
|
||||
/* Get space_id, save_data_type strings. */
|
||||
const char *space_id_str, *save_type_str;
|
||||
R_TRY(GetSaveDataSpaceIdString(&space_id_str, space_id));
|
||||
R_TRY(GetSaveDataTypeString(&save_type_str, attribute.save_data_type));
|
||||
R_TRY(GetSaveDataTypeString(&save_type_str, attribute.type));
|
||||
|
||||
/* Initialize the path. */
|
||||
const bool is_system = attribute.system_save_data_id != 0 && IsEmptyAccountId(attribute.uid);
|
||||
const bool is_system = attribute.system_save_data_id != InvalidSystemSaveDataId && IsEmptyAccountId(attribute.user_id);
|
||||
size_t out_path_len;
|
||||
if (is_system) {
|
||||
out_path_len = static_cast<size_t>(std::snprintf(dst, dst_size, "/atmosphere/saves/%s/%s/%s/%016lx", emummc_str, space_id_str, save_type_str, attribute.system_save_data_id));
|
||||
} else {
|
||||
out_path_len = static_cast<size_t>(std::snprintf(dst, dst_size, "/atmosphere/saves/%s/%s/%s/%016lx/%016lx%016lx", emummc_str, space_id_str, save_type_str, static_cast<u64>(program_id), attribute.uid.uid[1], attribute.uid.uid[0]));
|
||||
out_path_len = static_cast<size_t>(std::snprintf(dst, dst_size, "/atmosphere/saves/%s/%s/%s/%016lx/%016lx%016lx", emummc_str, space_id_str, save_type_str, static_cast<u64>(program_id), attribute.user_id.data[1], attribute.user_id.data[0]));
|
||||
}
|
||||
|
||||
R_UNLESS(out_path_len < dst_size, fs::ResultTooLongPath());
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace ams::mitm::fs {
|
||||
|
||||
class SaveUtil {
|
||||
public:
|
||||
static Result GetDirectorySaveDataPath(char *dst, size_t dst_size, ncm::ProgramId program_id, u8 space_id, const FsSaveDataAttribute &attribute);
|
||||
static Result GetDirectorySaveDataPath(char *dst, size_t dst_size, ncm::ProgramId program_id, u8 space_id, const ams::fs::SaveDataAttribute &attribute);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -246,17 +246,17 @@ namespace ams::creport {
|
||||
this->thread_count = 0;
|
||||
|
||||
/* Get thread list. */
|
||||
u32 num_threads;
|
||||
s32 num_threads;
|
||||
u64 thread_ids[ThreadCountMax];
|
||||
{
|
||||
if (R_FAILED(svcGetThreadList(&num_threads, thread_ids, ThreadCountMax, debug_handle))) {
|
||||
if (R_FAILED(svc::GetThreadList(&num_threads, thread_ids, ThreadCountMax, debug_handle))) {
|
||||
return;
|
||||
}
|
||||
num_threads = std::min(size_t(num_threads), ThreadCountMax);
|
||||
}
|
||||
|
||||
/* Parse thread infos. */
|
||||
for (size_t i = 0; i < num_threads; i++) {
|
||||
for (s32 i = 0; i < num_threads; i++) {
|
||||
if (this->threads[this->thread_count].ReadFromProcess(debug_handle, tls_map, thread_ids[i], is_64_bit)) {
|
||||
this->thread_count++;
|
||||
}
|
||||
|
||||
@@ -213,14 +213,14 @@ namespace ams::fatal::srv {
|
||||
ThreadContext thread_ctx;
|
||||
{
|
||||
/* We start by trying to get a list of threads. */
|
||||
u32 thread_count;
|
||||
s32 thread_count;
|
||||
u64 thread_ids[0x60];
|
||||
if (R_FAILED(svcGetThreadList(&thread_count, thread_ids, 0x60, debug_handle.Get()))) {
|
||||
if (R_FAILED(svc::GetThreadList(&thread_count, thread_ids, 0x60, debug_handle.Get()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* We need to locate the thread that's called fatal. */
|
||||
for (u32 i = 0; i < thread_count; i++) {
|
||||
for (s32 i = 0; i < thread_count; i++) {
|
||||
const u64 cur_thread_id = thread_ids[i];
|
||||
if (thread_id_to_tls.find(cur_thread_id) == thread_id_to_tls.end()) {
|
||||
continue;
|
||||
|
||||
@@ -21,7 +21,7 @@ extern "C" {
|
||||
|
||||
u32 __nx_applet_type = AppletType_None;
|
||||
|
||||
#define INNER_HEAP_SIZE 0x400000
|
||||
#define INNER_HEAP_SIZE 0x8000
|
||||
size_t nx_inner_heap_size = INNER_HEAP_SIZE;
|
||||
char nx_inner_heap[INNER_HEAP_SIZE];
|
||||
|
||||
@@ -49,6 +49,25 @@ namespace ams {
|
||||
|
||||
using namespace ams;
|
||||
|
||||
namespace {
|
||||
|
||||
u8 g_heap_memory[1_MB];
|
||||
lmem::HeapHandle g_heap_handle;
|
||||
|
||||
void *Allocate(size_t size) {
|
||||
return lmem::AllocateFromExpHeap(g_heap_handle, size);
|
||||
}
|
||||
|
||||
void Deallocate(void *p, size_t size) {
|
||||
lmem::FreeToExpHeap(g_heap_handle, p);
|
||||
}
|
||||
|
||||
void InitializeHeap() {
|
||||
g_heap_handle = lmem::CreateExpHeap(g_heap_memory, sizeof(g_heap_memory), lmem::CreateOption_None);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void __libnx_exception_handler(ThreadExceptionDump *ctx) {
|
||||
ams::CrashHandler(ctx);
|
||||
}
|
||||
@@ -63,11 +82,15 @@ void __libnx_initheap(void) {
|
||||
|
||||
fake_heap_start = (char*)addr;
|
||||
fake_heap_end = (char*)addr + size;
|
||||
|
||||
InitializeHeap();
|
||||
}
|
||||
|
||||
void __appInit(void) {
|
||||
hos::SetVersionForLibnx();
|
||||
|
||||
fs::SetAllocator(Allocate, Deallocate);
|
||||
|
||||
sm::DoWithSession([&]() {
|
||||
R_ABORT_UNLESS(fsInitialize());
|
||||
R_ABORT_UNLESS(splInitialize());
|
||||
@@ -82,6 +105,30 @@ void __appExit(void) {
|
||||
fsExit();
|
||||
}
|
||||
|
||||
void *operator new(size_t size) {
|
||||
return Allocate(size);
|
||||
}
|
||||
|
||||
void *operator new(size_t size, const std::nothrow_t &) {
|
||||
return Allocate(size);
|
||||
}
|
||||
|
||||
void operator delete(void *p) {
|
||||
return Deallocate(p, 0);
|
||||
}
|
||||
|
||||
void *operator new[](size_t size) {
|
||||
return Allocate(size);
|
||||
}
|
||||
|
||||
void *operator new[](size_t size, const std::nothrow_t &) {
|
||||
return Allocate(size);
|
||||
}
|
||||
|
||||
void operator delete[](void *p) {
|
||||
return Deallocate(p, 0);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
struct ContentManagerServerOptions {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
namespace ams::pm::impl {
|
||||
|
||||
ProcessInfo::ProcessInfo(Handle h, os::ProcessId pid, ldr::PinId pin, const ncm::ProgramLocation &l, const cfg::OverrideStatus &s) : process_id(pid), pin_id(pin), loc(l), status(s), handle(h), state(ProcessState_Created), flags(0), waitable_holder(h) {
|
||||
ProcessInfo::ProcessInfo(Handle h, os::ProcessId pid, ldr::PinId pin, const ncm::ProgramLocation &l, const cfg::OverrideStatus &s) : process_id(pid), pin_id(pin), loc(l), status(s), handle(h), state(svc::ProcessState_Created), flags(0), waitable_holder(h) {
|
||||
this->waitable_holder.SetUserData(reinterpret_cast<uintptr_t>(this));
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace ams::pm::impl {
|
||||
const ncm::ProgramLocation loc;
|
||||
const cfg::OverrideStatus status;
|
||||
Handle handle;
|
||||
ProcessState state;
|
||||
svc::ProcessState state;
|
||||
u32 flags;
|
||||
os::WaitableHolder waitable_holder;
|
||||
private:
|
||||
@@ -87,20 +87,20 @@ namespace ams::pm::impl {
|
||||
return this->status;
|
||||
}
|
||||
|
||||
ProcessState GetState() const {
|
||||
svc::ProcessState GetState() const {
|
||||
return this->state;
|
||||
}
|
||||
|
||||
void SetState(ProcessState state) {
|
||||
void SetState(svc::ProcessState state) {
|
||||
this->state = state;
|
||||
}
|
||||
|
||||
bool HasStarted() const {
|
||||
return this->state != ProcessState_Created && this->state != ProcessState_CreatedAttached;
|
||||
return this->state != svc::ProcessState_Created && this->state != svc::ProcessState_CreatedAttached;
|
||||
}
|
||||
|
||||
bool HasExited() const {
|
||||
return this->state == ProcessState_Exited;
|
||||
bool HasTerminated() const {
|
||||
return this->state == svc::ProcessState_Terminated;
|
||||
}
|
||||
|
||||
#define DEFINE_FLAG_SET(flag) \
|
||||
|
||||
@@ -97,7 +97,7 @@ namespace ams::pm::impl {
|
||||
Started = 2,
|
||||
Exception = 3,
|
||||
DebugRunning = 4,
|
||||
DebugSuspended = 5,
|
||||
DebugBreak = 5,
|
||||
};
|
||||
|
||||
enum class ProcessEventDeprecated {
|
||||
@@ -105,7 +105,7 @@ namespace ams::pm::impl {
|
||||
Exception = 1,
|
||||
Exited = 2,
|
||||
DebugRunning = 3,
|
||||
DebugSuspended = 4,
|
||||
DebugBreak = 4,
|
||||
Started = 5,
|
||||
};
|
||||
|
||||
@@ -124,8 +124,8 @@ namespace ams::pm::impl {
|
||||
return static_cast<u32>(ProcessEventDeprecated::Exception);
|
||||
case ProcessEvent::DebugRunning:
|
||||
return static_cast<u32>(ProcessEventDeprecated::DebugRunning);
|
||||
case ProcessEvent::DebugSuspended:
|
||||
return static_cast<u32>(ProcessEventDeprecated::DebugSuspended);
|
||||
case ProcessEvent::DebugBreak:
|
||||
return static_cast<u32>(ProcessEventDeprecated::DebugBreak);
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
@@ -263,7 +263,7 @@ namespace ams::pm::impl {
|
||||
|
||||
Result StartProcess(ProcessInfo *process_info, const ldr::ProgramInfo *program_info) {
|
||||
R_TRY(svcStartProcess(process_info->GetHandle(), program_info->main_thread_priority, program_info->default_cpu_id, program_info->main_thread_stack_size));
|
||||
process_info->SetState(ProcessState_Running);
|
||||
process_info->SetState(svc::ProcessState_Running);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
@@ -372,25 +372,25 @@ namespace ams::pm::impl {
|
||||
svcResetSignal(process_info->GetHandle());
|
||||
|
||||
/* Update the process's state. */
|
||||
const ProcessState old_state = process_info->GetState();
|
||||
const svc::ProcessState old_state = process_info->GetState();
|
||||
{
|
||||
u64 tmp = 0;
|
||||
R_ABORT_UNLESS(svcGetProcessInfo(&tmp, process_info->GetHandle(), ProcessInfoType_ProcessState));
|
||||
process_info->SetState(static_cast<ProcessState>(tmp));
|
||||
s64 tmp = 0;
|
||||
R_ABORT_UNLESS(svc::GetProcessInfo(&tmp, process_info->GetHandle(), svc::ProcessInfoType_ProcessState));
|
||||
process_info->SetState(static_cast<svc::ProcessState>(tmp));
|
||||
}
|
||||
const ProcessState new_state = process_info->GetState();
|
||||
const svc::ProcessState new_state = process_info->GetState();
|
||||
|
||||
/* If we're transitioning away from crashed, clear waiting attached. */
|
||||
if (old_state == ProcessState_Crashed && new_state != ProcessState_Crashed) {
|
||||
if (old_state == svc::ProcessState_Crashed && new_state != svc::ProcessState_Crashed) {
|
||||
process_info->ClearExceptionWaitingAttach();
|
||||
}
|
||||
|
||||
switch (new_state) {
|
||||
case ProcessState_Created:
|
||||
case ProcessState_CreatedAttached:
|
||||
case ProcessState_Exiting:
|
||||
case svc::ProcessState_Created:
|
||||
case svc::ProcessState_CreatedAttached:
|
||||
case svc::ProcessState_Terminating:
|
||||
break;
|
||||
case ProcessState_Running:
|
||||
case svc::ProcessState_Running:
|
||||
if (process_info->ShouldSignalOnDebugEvent()) {
|
||||
process_info->ClearSuspended();
|
||||
process_info->SetSuspendedStateChanged();
|
||||
@@ -401,18 +401,18 @@ namespace ams::pm::impl {
|
||||
g_process_event.Signal();
|
||||
}
|
||||
break;
|
||||
case ProcessState_Crashed:
|
||||
case svc::ProcessState_Crashed:
|
||||
process_info->SetExceptionOccurred();
|
||||
g_process_event.Signal();
|
||||
break;
|
||||
case ProcessState_RunningAttached:
|
||||
case svc::ProcessState_RunningAttached:
|
||||
if (process_info->ShouldSignalOnDebugEvent()) {
|
||||
process_info->ClearSuspended();
|
||||
process_info->SetSuspendedStateChanged();
|
||||
g_process_event.Signal();
|
||||
}
|
||||
break;
|
||||
case ProcessState_Exited:
|
||||
case svc::ProcessState_Terminated:
|
||||
/* Free process resources, unlink from waitable manager. */
|
||||
process_info->Cleanup();
|
||||
|
||||
@@ -438,7 +438,7 @@ namespace ams::pm::impl {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ProcessState_DebugSuspended:
|
||||
case svc::ProcessState_DebugBreak:
|
||||
if (process_info->ShouldSignalOnDebugEvent()) {
|
||||
process_info->SetSuspended();
|
||||
process_info->SetSuspendedStateChanged();
|
||||
@@ -535,7 +535,7 @@ namespace ams::pm::impl {
|
||||
if (process.HasSuspendedStateChanged()) {
|
||||
process.ClearSuspendedStateChanged();
|
||||
if (process.IsSuspended()) {
|
||||
out->event = GetProcessEventValue(ProcessEvent::DebugSuspended);
|
||||
out->event = GetProcessEventValue(ProcessEvent::DebugBreak);
|
||||
} else {
|
||||
out->event = GetProcessEventValue(ProcessEvent::DebugRunning);
|
||||
}
|
||||
@@ -548,7 +548,7 @@ namespace ams::pm::impl {
|
||||
out->process_id = process.GetProcessId();
|
||||
return ResultSuccess();
|
||||
}
|
||||
if (hos::GetVersion() < hos::Version_500 && process.ShouldSignalOnExit() && process.HasExited()) {
|
||||
if (hos::GetVersion() < hos::Version_500 && process.ShouldSignalOnExit() && process.HasTerminated()) {
|
||||
out->event = GetProcessEventValue(ProcessEvent::Exited);
|
||||
out->process_id = process.GetProcessId();
|
||||
return ResultSuccess();
|
||||
@@ -579,8 +579,8 @@ namespace ams::pm::impl {
|
||||
ProcessListAccessor list(g_process_list);
|
||||
|
||||
auto process_info = list->Find(process_id);
|
||||
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
||||
R_UNLESS(process_info->HasExited(), pm::ResultNotExited());
|
||||
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
||||
R_UNLESS(process_info->HasTerminated(), pm::ResultNotTerminated());
|
||||
|
||||
CleanupProcessInfo(list, process_info);
|
||||
return ResultSuccess();
|
||||
@@ -725,8 +725,8 @@ namespace ams::pm::impl {
|
||||
return resource::BoostApplicationThreadResourceLimit();
|
||||
}
|
||||
|
||||
Result AtmosphereGetCurrentLimitInfo(u64 *out_cur_val, u64 *out_lim_val, u32 group, u32 resource) {
|
||||
return resource::GetResourceLimitValues(out_cur_val, out_lim_val, static_cast<ResourceLimitGroup>(group), static_cast<LimitableResource>(resource));
|
||||
Result AtmosphereGetCurrentLimitInfo(s64 *out_cur_val, s64 *out_lim_val, u32 group, u32 resource) {
|
||||
return resource::GetResourceLimitValues(out_cur_val, out_lim_val, static_cast<ResourceLimitGroup>(group), static_cast<svc::LimitableResource>(resource));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -51,6 +51,6 @@ namespace ams::pm::impl {
|
||||
/* Resource Limit API. */
|
||||
Result BoostSystemMemoryResourceLimit(u64 boost_size);
|
||||
Result BoostApplicationThreadResourceLimit();
|
||||
Result AtmosphereGetCurrentLimitInfo(u64 *out_cur_val, u64 *out_lim_val, u32 group, u32 resource);
|
||||
Result AtmosphereGetCurrentLimitInfo(s64 *out_cur_val, s64 *out_lim_val, u32 group, u32 resource);
|
||||
|
||||
}
|
||||
|
||||
@@ -19,14 +19,13 @@ namespace ams::pm::resource {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr LimitableResource LimitableResources[] = {
|
||||
LimitableResource_Memory,
|
||||
LimitableResource_Threads,
|
||||
LimitableResource_Events,
|
||||
LimitableResource_TransferMemories,
|
||||
LimitableResource_Sessions,
|
||||
constexpr svc::LimitableResource LimitableResources[] = {
|
||||
svc::LimitableResource_PhysicalMemoryMax,
|
||||
svc::LimitableResource_ThreadCountMax,
|
||||
svc::LimitableResource_EventCountMax,
|
||||
svc::LimitableResource_TransferMemoryCountMax,
|
||||
svc::LimitableResource_SessionCountMax,
|
||||
};
|
||||
constexpr size_t LimitableResource_Count = util::size(LimitableResources);
|
||||
|
||||
/* Definitions for limit differences over time. */
|
||||
constexpr size_t ExtraSystemThreadCount400 = 100;
|
||||
@@ -48,27 +47,27 @@ namespace ams::pm::resource {
|
||||
u64 g_system_memory_boost_size = 0;
|
||||
u64 g_extra_application_threads_available = 0;
|
||||
|
||||
u64 g_resource_limits[ResourceLimitGroup_Count][LimitableResource_Count] = {
|
||||
u64 g_resource_limits[ResourceLimitGroup_Count][svc::LimitableResource_Count] = {
|
||||
[ResourceLimitGroup_System] = {
|
||||
[LimitableResource_Memory] = 0, /* Initialized by more complicated logic later. */
|
||||
[LimitableResource_Threads] = 508,
|
||||
[LimitableResource_Events] = 600,
|
||||
[LimitableResource_TransferMemories] = 128,
|
||||
[LimitableResource_Sessions] = 794,
|
||||
[svc::LimitableResource_PhysicalMemoryMax] = 0, /* Initialized by more complicated logic later. */
|
||||
[svc::LimitableResource_ThreadCountMax] = 508,
|
||||
[svc::LimitableResource_EventCountMax] = 600,
|
||||
[svc::LimitableResource_TransferMemoryCountMax] = 128,
|
||||
[svc::LimitableResource_SessionCountMax] = 794,
|
||||
},
|
||||
[ResourceLimitGroup_Application] = {
|
||||
[LimitableResource_Memory] = 0, /* Initialized by more complicated logic later. */
|
||||
[LimitableResource_Threads] = 96,
|
||||
[LimitableResource_Events] = 0,
|
||||
[LimitableResource_TransferMemories] = 32,
|
||||
[LimitableResource_Sessions] = 1,
|
||||
[svc::LimitableResource_PhysicalMemoryMax] = 0, /* Initialized by more complicated logic later. */
|
||||
[svc::LimitableResource_ThreadCountMax] = 96,
|
||||
[svc::LimitableResource_EventCountMax] = 0,
|
||||
[svc::LimitableResource_TransferMemoryCountMax] = 32,
|
||||
[svc::LimitableResource_SessionCountMax] = 1,
|
||||
},
|
||||
[ResourceLimitGroup_Applet] = {
|
||||
[LimitableResource_Memory] = 0, /* Initialized by more complicated logic later. */
|
||||
[LimitableResource_Threads] = 96,
|
||||
[LimitableResource_Events] = 0,
|
||||
[LimitableResource_TransferMemories] = 32,
|
||||
[LimitableResource_Sessions] = 5,
|
||||
[svc::LimitableResource_PhysicalMemoryMax] = 0, /* Initialized by more complicated logic later. */
|
||||
[svc::LimitableResource_ThreadCountMax] = 96,
|
||||
[svc::LimitableResource_EventCountMax] = 0,
|
||||
[svc::LimitableResource_TransferMemoryCountMax] = 32,
|
||||
[svc::LimitableResource_SessionCountMax] = 5,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -102,13 +101,13 @@ namespace ams::pm::resource {
|
||||
|
||||
/* Helpers. */
|
||||
Result SetMemoryResourceLimitLimitValue(ResourceLimitGroup group, u64 new_memory_limit) {
|
||||
const u64 old_memory_limit = g_resource_limits[group][LimitableResource_Memory];
|
||||
g_resource_limits[group][LimitableResource_Memory] = new_memory_limit;
|
||||
const u64 old_memory_limit = g_resource_limits[group][svc::LimitableResource_PhysicalMemoryMax];
|
||||
g_resource_limits[group][svc::LimitableResource_PhysicalMemoryMax] = new_memory_limit;
|
||||
|
||||
{
|
||||
/* If we fail, restore the old memory limit. */
|
||||
auto limit_guard = SCOPE_GUARD { g_resource_limits[group][LimitableResource_Memory] = old_memory_limit; };
|
||||
R_TRY(svcSetResourceLimitLimitValue(GetResourceLimitHandle(group), LimitableResource_Memory, g_resource_limits[group][LimitableResource_Memory]));
|
||||
auto limit_guard = SCOPE_GUARD { g_resource_limits[group][svc::LimitableResource_PhysicalMemoryMax] = old_memory_limit; };
|
||||
R_TRY(svc::SetResourceLimitLimitValue(GetResourceLimitHandle(group), svc::LimitableResource_PhysicalMemoryMax, g_resource_limits[group][svc::LimitableResource_PhysicalMemoryMax]));
|
||||
limit_guard.Cancel();
|
||||
}
|
||||
|
||||
@@ -120,12 +119,12 @@ namespace ams::pm::resource {
|
||||
R_TRY(SetMemoryResourceLimitLimitValue(group, new_memory_limit));
|
||||
|
||||
/* Set other limit values. */
|
||||
for (size_t i = 0; i < LimitableResource_Count; i++) {
|
||||
for (size_t i = 0; i < svc::LimitableResource_Count; i++) {
|
||||
const auto resource = LimitableResources[i];
|
||||
if (resource == LimitableResource_Memory) {
|
||||
if (resource == svc::LimitableResource_PhysicalMemoryMax) {
|
||||
continue;
|
||||
}
|
||||
R_TRY(svcSetResourceLimitLimitValue(GetResourceLimitHandle(group), resource, g_resource_limits[group][resource]));
|
||||
R_TRY(svc::SetResourceLimitLimitValue(GetResourceLimitHandle(group), resource, g_resource_limits[group][resource]));
|
||||
}
|
||||
return ResultSuccess();
|
||||
}
|
||||
@@ -143,16 +142,16 @@ namespace ams::pm::resource {
|
||||
|
||||
void WaitResourceAvailable(ResourceLimitGroup group) {
|
||||
const Handle reslimit_hnd = GetResourceLimitHandle(group);
|
||||
for (size_t i = 0; i < LimitableResource_Count; i++) {
|
||||
for (size_t i = 0; i < svc::LimitableResource_Count; i++) {
|
||||
const auto resource = LimitableResources[i];
|
||||
|
||||
u64 value = 0;
|
||||
s64 value = 0;
|
||||
while (true) {
|
||||
R_ABORT_UNLESS(svcGetResourceLimitCurrentValue(&value, reslimit_hnd, resource));
|
||||
R_ABORT_UNLESS(svc::GetResourceLimitCurrentValue(&value, reslimit_hnd, resource));
|
||||
if (value == 0) {
|
||||
break;
|
||||
}
|
||||
svcSleepThread(1'000'000ul);
|
||||
svc::SleepThread(1'000'000ul);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,11 +159,11 @@ namespace ams::pm::resource {
|
||||
void WaitApplicationMemoryAvailable() {
|
||||
u64 value = 0;
|
||||
while (true) {
|
||||
R_ABORT_UNLESS(svcGetSystemInfo(&value, SystemInfoType_UsedPhysicalMemorySize, INVALID_HANDLE, PhysicalMemoryInfo_Application));
|
||||
R_ABORT_UNLESS(svc::GetSystemInfo(&value, svc::SystemInfoType_UsedPhysicalMemorySize, INVALID_HANDLE, svc::PhysicalMemorySystemInfo_Application));
|
||||
if (value == 0) {
|
||||
break;
|
||||
}
|
||||
svcSleepThread(1'000'000ul);
|
||||
svc::SleepThread(1'000'000ul);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,10 +175,10 @@ namespace ams::pm::resource {
|
||||
for (size_t i = 0; i < ResourceLimitGroup_Count; i++) {
|
||||
if (i == ResourceLimitGroup_System) {
|
||||
u64 value = 0;
|
||||
R_ABORT_UNLESS(svcGetInfo(&value, InfoType_ResourceLimit, INVALID_HANDLE, 0));
|
||||
g_resource_limit_handles[i] = static_cast<Handle>(value);
|
||||
R_ABORT_UNLESS(svc::GetInfo(&value, svc::InfoType_ResourceLimit, svc::InvalidHandle, 0));
|
||||
g_resource_limit_handles[i] = static_cast<svc::Handle>(value);
|
||||
} else {
|
||||
R_ABORT_UNLESS(svcCreateResourceLimit(&g_resource_limit_handles[i]));
|
||||
R_ABORT_UNLESS(svc::CreateResourceLimit(&g_resource_limit_handles[i]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +186,7 @@ namespace ams::pm::resource {
|
||||
const auto hos_version = hos::GetVersion();
|
||||
if (hos_version >= hos::Version_400) {
|
||||
/* 4.0.0 increased the system thread limit. */
|
||||
g_resource_limits[ResourceLimitGroup_System][LimitableResource_Threads] += ExtraSystemThreadCount400;
|
||||
g_resource_limits[ResourceLimitGroup_System][svc::LimitableResource_ThreadCountMax] += ExtraSystemThreadCount400;
|
||||
/* 4.0.0 also took memory away from applet and gave it to system, for the Standard and StandardForSystemDev profiles. */
|
||||
g_memory_resource_limits[spl::MemoryArrangement_Standard][ResourceLimitGroup_System] += ExtraSystemMemorySize400;
|
||||
g_memory_resource_limits[spl::MemoryArrangement_Standard][ResourceLimitGroup_Applet] -= ExtraSystemMemorySize400;
|
||||
@@ -203,26 +202,26 @@ namespace ams::pm::resource {
|
||||
}
|
||||
if (hos_version >= hos::Version_600) {
|
||||
/* 6.0.0 increased the system event and session limits. */
|
||||
g_resource_limits[ResourceLimitGroup_System][LimitableResource_Events] += ExtraSystemEventCount600;
|
||||
g_resource_limits[ResourceLimitGroup_System][LimitableResource_Sessions] += ExtraSystemSessionCount600;
|
||||
g_resource_limits[ResourceLimitGroup_System][svc::LimitableResource_EventCountMax] += ExtraSystemEventCount600;
|
||||
g_resource_limits[ResourceLimitGroup_System][svc::LimitableResource_SessionCountMax] += ExtraSystemSessionCount600;
|
||||
}
|
||||
if (hos_version >= hos::Version_900) {
|
||||
/* 9.2.0 increased the system session limit. */
|
||||
/* NOTE: We don't currently support detection of minor version, so we will provide this increase on 9.0.0+. */
|
||||
/* This shouldn't impact any existing behavior in undesirable ways. */
|
||||
g_resource_limits[ResourceLimitGroup_System][LimitableResource_Sessions] += ExtraSystemSessionCount920;
|
||||
g_resource_limits[ResourceLimitGroup_System][svc::LimitableResource_SessionCountMax] += ExtraSystemSessionCount920;
|
||||
}
|
||||
|
||||
/* 7.0.0+: Calculate the number of extra application threads available. */
|
||||
if (hos::GetVersion() >= hos::Version_700) {
|
||||
/* See how many threads we have available. */
|
||||
u64 total_threads_available = 0;
|
||||
R_ABORT_UNLESS(svcGetResourceLimitLimitValue(&total_threads_available, GetResourceLimitHandle(ResourceLimitGroup_System), LimitableResource_Threads));
|
||||
s64 total_threads_available = 0;
|
||||
R_ABORT_UNLESS(svc::GetResourceLimitLimitValue(&total_threads_available, GetResourceLimitHandle(ResourceLimitGroup_System), svc::LimitableResource_ThreadCountMax));
|
||||
|
||||
/* See how many threads we're expecting. */
|
||||
const size_t total_threads_allocated = g_resource_limits[ResourceLimitGroup_System][LimitableResource_Threads] -
|
||||
g_resource_limits[ResourceLimitGroup_Application][LimitableResource_Threads] -
|
||||
g_resource_limits[ResourceLimitGroup_Applet][LimitableResource_Threads];
|
||||
const s64 total_threads_allocated = g_resource_limits[ResourceLimitGroup_System][svc::LimitableResource_ThreadCountMax] -
|
||||
g_resource_limits[ResourceLimitGroup_Application][svc::LimitableResource_ThreadCountMax] -
|
||||
g_resource_limits[ResourceLimitGroup_Applet][svc::LimitableResource_ThreadCountMax];
|
||||
|
||||
/* Ensure we don't over-commit threads. */
|
||||
AMS_ABORT_UNLESS(total_threads_allocated <= total_threads_available);
|
||||
@@ -237,16 +236,16 @@ namespace ams::pm::resource {
|
||||
g_memory_arrangement = spl::MemoryArrangement_Dynamic;
|
||||
|
||||
/* Get total memory available. */
|
||||
u64 total_memory = 0;
|
||||
R_ABORT_UNLESS(svcGetResourceLimitLimitValue(&total_memory, GetResourceLimitHandle(ResourceLimitGroup_System), LimitableResource_Memory));
|
||||
s64 total_memory = 0;
|
||||
R_ABORT_UNLESS(svc::GetResourceLimitLimitValue(&total_memory, GetResourceLimitHandle(ResourceLimitGroup_System), svc::LimitableResource_PhysicalMemoryMax));
|
||||
|
||||
/* Get and save application + applet memory. */
|
||||
R_ABORT_UNLESS(svcGetSystemInfo(&g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Application], SystemInfoType_TotalPhysicalMemorySize, INVALID_HANDLE, PhysicalMemoryInfo_Application));
|
||||
R_ABORT_UNLESS(svcGetSystemInfo(&g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Applet], SystemInfoType_TotalPhysicalMemorySize, INVALID_HANDLE, PhysicalMemoryInfo_Applet));
|
||||
R_ABORT_UNLESS(svc::GetSystemInfo(&g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Application], svc::SystemInfoType_TotalPhysicalMemorySize, svc::InvalidHandle, svc::PhysicalMemorySystemInfo_Application));
|
||||
R_ABORT_UNLESS(svc::GetSystemInfo(&g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Applet], svc::SystemInfoType_TotalPhysicalMemorySize, svc::InvalidHandle, svc::PhysicalMemorySystemInfo_Applet));
|
||||
|
||||
const u64 application_size = g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Application];
|
||||
const u64 applet_size = g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Applet];
|
||||
const u64 reserved_non_system_size = (application_size + applet_size + ReservedMemorySize600);
|
||||
const s64 application_size = g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Application];
|
||||
const s64 applet_size = g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Applet];
|
||||
const s64 reserved_non_system_size = (application_size + applet_size + ReservedMemorySize600);
|
||||
|
||||
/* Ensure there's enough memory for the system region. */
|
||||
AMS_ABORT_UNLESS(reserved_non_system_size < total_memory);
|
||||
@@ -292,11 +291,11 @@ namespace ams::pm::resource {
|
||||
if (hos::GetVersion() >= hos::Version_500) {
|
||||
/* Starting in 5.0.0, PM does not allow for only one of the sets to fail. */
|
||||
if (boost_size < g_system_memory_boost_size) {
|
||||
R_TRY(svcSetUnsafeLimit(boost_size));
|
||||
R_TRY(svc::SetUnsafeLimit(boost_size));
|
||||
R_ABORT_UNLESS(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
|
||||
} else {
|
||||
R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
|
||||
R_ABORT_UNLESS(svcSetUnsafeLimit(boost_size));
|
||||
R_ABORT_UNLESS(svc::SetUnsafeLimit(boost_size));
|
||||
}
|
||||
} else {
|
||||
const u64 new_sys_size = g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_System] + boost_size;
|
||||
@@ -318,11 +317,11 @@ namespace ams::pm::resource {
|
||||
Result BoostApplicationThreadResourceLimit() {
|
||||
std::scoped_lock lk(g_resource_limit_lock);
|
||||
/* Set new limit. */
|
||||
const u64 new_thread_count = g_resource_limits[ResourceLimitGroup_Application][LimitableResource_Threads] + g_extra_application_threads_available;
|
||||
R_TRY(svcSetResourceLimitLimitValue(GetResourceLimitHandle(ResourceLimitGroup_Application), LimitableResource_Threads, new_thread_count));
|
||||
const s64 new_thread_count = g_resource_limits[ResourceLimitGroup_Application][svc::LimitableResource_ThreadCountMax] + g_extra_application_threads_available;
|
||||
R_TRY(svc::SetResourceLimitLimitValue(GetResourceLimitHandle(ResourceLimitGroup_Application), svc::LimitableResource_ThreadCountMax, new_thread_count));
|
||||
|
||||
/* Record that we did so. */
|
||||
g_resource_limits[ResourceLimitGroup_Application][LimitableResource_Threads] = new_thread_count;
|
||||
g_resource_limits[ResourceLimitGroup_Application][svc::LimitableResource_ThreadCountMax] = new_thread_count;
|
||||
g_extra_application_threads_available = 0;
|
||||
|
||||
return ResultSuccess();
|
||||
@@ -345,14 +344,14 @@ namespace ams::pm::resource {
|
||||
}
|
||||
}
|
||||
|
||||
Result GetResourceLimitValues(u64 *out_cur, u64 *out_lim, ResourceLimitGroup group, LimitableResource resource) {
|
||||
Result GetResourceLimitValues(s64 *out_cur, s64 *out_lim, ResourceLimitGroup group, svc::LimitableResource resource) {
|
||||
/* Do not allow out of bounds access. */
|
||||
AMS_ABORT_UNLESS(group < ResourceLimitGroup_Count);
|
||||
AMS_ABORT_UNLESS(resource < LimitableResource_Count);
|
||||
AMS_ABORT_UNLESS(resource < svc::LimitableResource_Count);
|
||||
|
||||
const Handle reslimit_hnd = GetResourceLimitHandle(group);
|
||||
R_TRY(svcGetResourceLimitCurrentValue(out_cur, reslimit_hnd, resource));
|
||||
R_TRY(svcGetResourceLimitLimitValue(out_lim, reslimit_hnd, resource));
|
||||
R_TRY(svc::GetResourceLimitCurrentValue(out_cur, reslimit_hnd, resource));
|
||||
R_TRY(svc::GetResourceLimitLimitValue(out_lim, reslimit_hnd, resource));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
@@ -26,6 +26,6 @@ namespace ams::pm::resource {
|
||||
Handle GetResourceLimitHandle(const ldr::ProgramInfo *info);
|
||||
void WaitResourceAvailable(const ldr::ProgramInfo *info);
|
||||
|
||||
Result GetResourceLimitValues(u64 *out_cur, u64 *out_lim, ResourceLimitGroup group, LimitableResource resource);
|
||||
Result GetResourceLimitValues(s64 *out_cur, s64 *out_lim, ResourceLimitGroup group, svc::LimitableResource resource);
|
||||
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace ams::pm::dmnt {
|
||||
return impl::AtmosphereGetProcessInfo(out_process_handle.GetHandlePointer(), out_loc.GetPointer(), out_status.GetPointer(), process_id);
|
||||
}
|
||||
|
||||
Result DebugMonitorServiceBase::AtmosphereGetCurrentLimitInfo(sf::Out<u64> out_cur_val, sf::Out<u64> out_lim_val, u32 group, u32 resource) {
|
||||
Result DebugMonitorServiceBase::AtmosphereGetCurrentLimitInfo(sf::Out<s64> out_cur_val, sf::Out<s64> out_lim_val, u32 group, u32 resource) {
|
||||
return impl::AtmosphereGetCurrentLimitInfo(out_cur_val.GetPointer(), out_lim_val.GetPointer(), group, resource);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace ams::pm::dmnt {
|
||||
|
||||
/* Atmosphere extension commands. */
|
||||
virtual Result AtmosphereGetProcessInfo(sf::OutCopyHandle out_process_handle, sf::Out<ncm::ProgramLocation> out_loc, sf::Out<cfg::OverrideStatus> out_status, os::ProcessId process_id);
|
||||
virtual Result AtmosphereGetCurrentLimitInfo(sf::Out<u64> out_cur_val, sf::Out<u64> out_lim_val, u32 group, u32 resource);
|
||||
virtual Result AtmosphereGetCurrentLimitInfo(sf::Out<s64> out_cur_val, sf::Out<s64> out_lim_val, u32 group, u32 resource);
|
||||
};
|
||||
|
||||
/* This represents modern DebugMonitorService (5.0.0+). */
|
||||
|
||||
@@ -115,10 +115,10 @@ namespace {
|
||||
cfg::GetInitialProcessRange(&min_priv_process_id, &max_priv_process_id);
|
||||
|
||||
/* Get list of processes, register all privileged ones. */
|
||||
u32 num_pids;
|
||||
s32 num_pids;
|
||||
os::ProcessId pids[ProcessCountMax];
|
||||
R_ABORT_UNLESS(svcGetProcessList(&num_pids, reinterpret_cast<u64 *>(pids), ProcessCountMax));
|
||||
for (size_t i = 0; i < num_pids; i++) {
|
||||
R_ABORT_UNLESS(svc::GetProcessList(&num_pids, reinterpret_cast<u64 *>(pids), ProcessCountMax));
|
||||
for (s32 i = 0; i < num_pids; i++) {
|
||||
if (min_priv_process_id <= pids[i] && pids[i] <= max_priv_process_id) {
|
||||
RegisterPrivilegedProcess(pids[i]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user