mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-11-16 17:11:18 +01:00
NOTE: This work is not yet fully complete; kernel is done, but it was taking an exceedingly long time to get through libstratosphere. Thus, I've temporarily added -Wno-error=unused-result for libstratosphere/stratosphere. All warnings should be fixed to do the same thing Nintendo does as relevant, but this is taking a phenomenally long time and is not actually the most important work to do, so it can be put off for some time to prioritize other tasks for 21.0.0 support.
315 lines
11 KiB
C++
315 lines
11 KiB
C++
/*
|
|
* Copyright (c) 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 "fs_file_accessor.hpp"
|
|
#include "fs_directory_accessor.hpp"
|
|
#include "fs_filesystem_accessor.hpp"
|
|
|
|
namespace ams::fs::impl {
|
|
|
|
namespace {
|
|
|
|
template<typename List, typename Iter>
|
|
void Remove(List &list, Iter *desired) {
|
|
for (auto it = list.cbegin(); it != list.cend(); ++it) {
|
|
if (it.operator->() == desired) {
|
|
list.erase(it);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* This should never happen. */
|
|
AMS_ABORT();
|
|
}
|
|
|
|
Result SetMountName(char *dst, const char *name) {
|
|
R_UNLESS(name[0] != 0, fs::ResultInvalidMountName());
|
|
|
|
const size_t n_len = util::Strlcpy<char>(dst, name, MountNameLengthMax + 1);
|
|
R_UNLESS(n_len <= MountNameLengthMax, fs::ResultInvalidMountName());
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
template<typename List>
|
|
Result ValidateNoOpenWriteModeFiles(List &list) {
|
|
for (auto it = list.cbegin(); it != list.cend(); it++) {
|
|
R_UNLESS((it->GetOpenMode() & OpenMode_Write) == 0, fs::ResultWriteModeFileNotClosed());
|
|
}
|
|
R_SUCCEED();
|
|
}
|
|
|
|
}
|
|
|
|
FileSystemAccessor::FileSystemAccessor(const char *n, std::unique_ptr<fsa::IFileSystem> &&fs, std::unique_ptr<fsa::ICommonMountNameGenerator> &&generator)
|
|
: m_impl(std::move(fs)), m_open_list_lock(), m_mount_name_generator(std::move(generator)),
|
|
m_access_log_enabled(false), m_data_cache_attachable(false), m_path_cache_attachable(false), m_path_cache_attached(false), m_multi_commit_supported(false),
|
|
m_path_flags()
|
|
{
|
|
R_ABORT_UNLESS(SetMountName(m_name.str, n));
|
|
|
|
if (std::strcmp(m_name.str, AMS_FS_IMPL_HOST_ROOT_FILE_SYSTEM_MOUNT_NAME) == 0) {
|
|
m_path_flags.AllowWindowsPath();
|
|
}
|
|
}
|
|
|
|
FileSystemAccessor::~FileSystemAccessor() {
|
|
std::scoped_lock lk(m_open_list_lock);
|
|
|
|
/* TODO: Iterate over list entries. */
|
|
|
|
if (!m_open_file_list.empty()) { R_ABORT_UNLESS(fs::ResultFileNotClosed()); }
|
|
if (!m_open_dir_list.empty()) { R_ABORT_UNLESS(fs::ResultDirectoryNotClosed()); }
|
|
|
|
if (m_path_cache_attached) {
|
|
/* TODO: Invalidate path cache */
|
|
}
|
|
}
|
|
|
|
Result FileSystemAccessor::GetCommonMountName(char *dst, size_t dst_size) const {
|
|
R_UNLESS(m_mount_name_generator != nullptr, fs::ResultPreconditionViolation());
|
|
R_RETURN(m_mount_name_generator->GenerateCommonMountName(dst, dst_size));
|
|
}
|
|
|
|
std::shared_ptr<fssrv::impl::FileSystemInterfaceAdapter> FileSystemAccessor::GetMultiCommitTarget() {
|
|
if (m_multi_commit_supported) {
|
|
AMS_ABORT("TODO: Support multi commit");
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void FileSystemAccessor::NotifyCloseFile(FileAccessor *f) {
|
|
std::scoped_lock lk(m_open_list_lock);
|
|
Remove(m_open_file_list, f);
|
|
}
|
|
|
|
void FileSystemAccessor::NotifyCloseDirectory(DirectoryAccessor *d) {
|
|
std::scoped_lock lk(m_open_list_lock);
|
|
Remove(m_open_dir_list, d);
|
|
}
|
|
|
|
Result FileSystemAccessor::SetUpPath(fs::Path *out, const char *p) {
|
|
/* Initialize the path appropriately. */
|
|
bool normalized;
|
|
size_t len;
|
|
if (R_SUCCEEDED(PathFormatter::IsNormalized(std::addressof(normalized), std::addressof(len), p, m_path_flags)) && normalized) {
|
|
/* We can use the input buffer directly. */
|
|
R_TRY(out->SetShallowBuffer(p));
|
|
} else {
|
|
/* Initialize with appropriate slash replacement. */
|
|
if (m_path_flags.IsWindowsPathAllowed()) {
|
|
R_TRY(out->InitializeWithReplaceForwardSlashes(p));
|
|
} else {
|
|
R_TRY(out->InitializeWithReplaceBackslash(p));
|
|
}
|
|
|
|
/* Ensure we're normalized. */
|
|
R_TRY(out->Normalize(m_path_flags));
|
|
}
|
|
|
|
/* Check the path isn't too long. */
|
|
R_UNLESS(out->GetLength() <= fs::EntryNameLengthMax, fs::ResultTooLongPath());
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result FileSystemAccessor::CreateFile(const char *path, s64 size, int option) {
|
|
/* Create path. */
|
|
fs::Path normalized_path;
|
|
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
|
|
|
if (m_path_cache_attached) {
|
|
/* TODO: Path cache */
|
|
R_TRY(m_impl->CreateFile(normalized_path, size, option));
|
|
} else {
|
|
R_TRY(m_impl->CreateFile(normalized_path, size, option));
|
|
}
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result FileSystemAccessor::DeleteFile(const char *path) {
|
|
/* Create path. */
|
|
fs::Path normalized_path;
|
|
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
|
|
|
R_RETURN(m_impl->DeleteFile(normalized_path));
|
|
}
|
|
|
|
Result FileSystemAccessor::CreateDirectory(const char *path) {
|
|
/* Create path. */
|
|
fs::Path normalized_path;
|
|
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
|
|
|
R_RETURN(m_impl->CreateDirectory(normalized_path));
|
|
}
|
|
|
|
Result FileSystemAccessor::DeleteDirectory(const char *path) {
|
|
/* Create path. */
|
|
fs::Path normalized_path;
|
|
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
|
|
|
R_RETURN(m_impl->DeleteDirectory(normalized_path));
|
|
}
|
|
|
|
Result FileSystemAccessor::DeleteDirectoryRecursively(const char *path) {
|
|
/* Create path. */
|
|
fs::Path normalized_path;
|
|
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
|
|
|
R_RETURN(m_impl->DeleteDirectoryRecursively(normalized_path));
|
|
}
|
|
|
|
Result FileSystemAccessor::RenameFile(const char *old_path, const char *new_path) {
|
|
/* Create path. */
|
|
fs::Path normalized_old_path;
|
|
fs::Path normalized_new_path;
|
|
R_TRY(this->SetUpPath(std::addressof(normalized_old_path), old_path));
|
|
R_TRY(this->SetUpPath(std::addressof(normalized_new_path), new_path));
|
|
|
|
if (m_path_cache_attached) {
|
|
/* TODO: Path cache */
|
|
R_TRY(m_impl->RenameFile(normalized_old_path, normalized_new_path));
|
|
} else {
|
|
R_TRY(m_impl->RenameFile(normalized_old_path, normalized_new_path));
|
|
}
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result FileSystemAccessor::RenameDirectory(const char *old_path, const char *new_path) {
|
|
/* Create path. */
|
|
fs::Path normalized_old_path;
|
|
fs::Path normalized_new_path;
|
|
R_TRY(this->SetUpPath(std::addressof(normalized_old_path), old_path));
|
|
R_TRY(this->SetUpPath(std::addressof(normalized_new_path), new_path));
|
|
|
|
if (m_path_cache_attached) {
|
|
/* TODO: Path cache */
|
|
R_TRY(m_impl->RenameDirectory(normalized_old_path, normalized_new_path));
|
|
} else {
|
|
R_TRY(m_impl->RenameDirectory(normalized_old_path, normalized_new_path));
|
|
}
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result FileSystemAccessor::GetEntryType(DirectoryEntryType *out, const char *path) {
|
|
/* Create path. */
|
|
fs::Path normalized_path;
|
|
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
|
|
|
R_RETURN(m_impl->GetEntryType(out, normalized_path));
|
|
}
|
|
|
|
Result FileSystemAccessor::OpenFile(std::unique_ptr<FileAccessor> *out_file, const char *path, OpenMode mode) {
|
|
/* Create path. */
|
|
fs::Path normalized_path;
|
|
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
|
|
|
std::unique_ptr<fsa::IFile> file;
|
|
R_TRY(m_impl->OpenFile(std::addressof(file), normalized_path, mode));
|
|
|
|
auto accessor = new FileAccessor(std::move(file), this, mode);
|
|
R_UNLESS(accessor != nullptr, fs::ResultAllocationMemoryFailedInFileSystemAccessorA());
|
|
|
|
{
|
|
std::scoped_lock lk(m_open_list_lock);
|
|
m_open_file_list.push_back(*accessor);
|
|
}
|
|
|
|
if (m_path_cache_attached) {
|
|
if (mode & OpenMode_AllowAppend) {
|
|
/* TODO: Append Path cache */
|
|
} else {
|
|
/* TODO: Non-append path cache */
|
|
}
|
|
}
|
|
|
|
out_file->reset(accessor);
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result FileSystemAccessor::OpenDirectory(std::unique_ptr<DirectoryAccessor> *out_dir, const char *path, OpenDirectoryMode mode) {
|
|
/* Create path. */
|
|
fs::Path normalized_path;
|
|
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
|
|
|
std::unique_ptr<fsa::IDirectory> dir;
|
|
R_TRY(m_impl->OpenDirectory(std::addressof(dir), normalized_path, mode));
|
|
|
|
auto accessor = new DirectoryAccessor(std::move(dir), *this);
|
|
R_UNLESS(accessor != nullptr, fs::ResultAllocationMemoryFailedInFileSystemAccessorB());
|
|
|
|
{
|
|
std::scoped_lock lk(m_open_list_lock);
|
|
m_open_dir_list.push_back(*accessor);
|
|
}
|
|
|
|
out_dir->reset(accessor);
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result FileSystemAccessor::Commit() {
|
|
{
|
|
std::scoped_lock lk(m_open_list_lock);
|
|
R_ABORT_UNLESS(ValidateNoOpenWriteModeFiles(m_open_file_list));
|
|
}
|
|
R_RETURN(m_impl->Commit());
|
|
}
|
|
|
|
Result FileSystemAccessor::GetFreeSpaceSize(s64 *out, const char *path) {
|
|
/* Create path. */
|
|
fs::Path normalized_path;
|
|
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
|
|
|
R_RETURN(m_impl->GetFreeSpaceSize(out, normalized_path));
|
|
}
|
|
|
|
Result FileSystemAccessor::GetTotalSpaceSize(s64 *out, const char *path) {
|
|
/* Create path. */
|
|
fs::Path normalized_path;
|
|
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
|
|
|
R_RETURN(m_impl->GetTotalSpaceSize(out, normalized_path));
|
|
}
|
|
|
|
Result FileSystemAccessor::CleanDirectoryRecursively(const char *path) {
|
|
/* Create path. */
|
|
fs::Path normalized_path;
|
|
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
|
|
|
R_RETURN(m_impl->CleanDirectoryRecursively(normalized_path));
|
|
}
|
|
|
|
Result FileSystemAccessor::GetFileTimeStampRaw(FileTimeStampRaw *out, const char *path) {
|
|
/* Create path. */
|
|
fs::Path normalized_path;
|
|
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
|
|
|
R_RETURN(m_impl->GetFileTimeStampRaw(out, normalized_path));
|
|
}
|
|
|
|
Result FileSystemAccessor::QueryEntry(char *dst, size_t dst_size, const char *src, size_t src_size, fsa::QueryId query, const char *path) {
|
|
/* Create path. */
|
|
fs::Path normalized_path;
|
|
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
|
|
|
R_RETURN(m_impl->QueryEntry(dst, dst_size, src, src_size, query, normalized_path));
|
|
}
|
|
|
|
|
|
}
|