2019-12-09 11:57:37 +00:00
|
|
|
/*
|
2020-01-24 10:10:40 +00:00
|
|
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
2019-12-09 11:57:37 +00:00
|
|
|
*
|
|
|
|
* 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/fs_common.hpp"
|
|
|
|
#include "../fs/fs_file.hpp"
|
|
|
|
#include "../fs/fs_directory.hpp"
|
|
|
|
#include "../fs/fs_filesystem.hpp"
|
|
|
|
#include "fssystem_path_tool.hpp"
|
|
|
|
|
|
|
|
namespace ams::fssystem {
|
|
|
|
|
|
|
|
namespace impl {
|
|
|
|
|
|
|
|
/* Iteration. */
|
|
|
|
template<typename OnEnterDir, typename OnExitDir, typename OnFile>
|
|
|
|
Result IterateDirectoryRecursivelyImpl(fs::fsa::IFileSystem *fs, char *work_path, size_t work_path_size, fs::DirectoryEntry *dir_ent, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
|
|
|
|
/* Open the directory. */
|
|
|
|
std::unique_ptr<fs::fsa::IDirectory> dir;
|
|
|
|
R_TRY(fs->OpenDirectory(&dir, work_path, fs::OpenDirectoryMode_All));
|
|
|
|
|
|
|
|
const size_t parent_len = strnlen(work_path, work_path_size - 1);
|
|
|
|
|
|
|
|
/* Read and handle entries. */
|
|
|
|
while (true) {
|
|
|
|
/* Read a single entry. */
|
|
|
|
s64 read_count = 0;
|
|
|
|
R_TRY(dir->Read(&read_count, dir_ent, 1));
|
|
|
|
|
|
|
|
/* If we're out of entries, we're done. */
|
|
|
|
if (read_count == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Validate child path size. */
|
|
|
|
const size_t child_name_len = strnlen(dir_ent->name, sizeof(dir_ent->name) - 1);
|
|
|
|
const bool is_dir = dir_ent->type == fs::DirectoryEntryType_Directory;
|
|
|
|
const size_t separator_size = is_dir ? 1 : 0;
|
|
|
|
R_UNLESS(parent_len + child_name_len + separator_size < work_path_size, fs::ResultTooLongPath());
|
|
|
|
|
|
|
|
/* Set child path. */
|
|
|
|
std::strncat(work_path, dir_ent->name, work_path_size - parent_len - 1);
|
|
|
|
{
|
|
|
|
if (is_dir) {
|
|
|
|
/* Enter directory. */
|
|
|
|
R_TRY(on_enter_dir(work_path, *dir_ent));
|
|
|
|
|
|
|
|
/* Append separator, recurse. */
|
|
|
|
std::strncat(work_path, "/", work_path_size - (parent_len + child_name_len) - 1);
|
|
|
|
R_TRY(IterateDirectoryRecursivelyImpl(fs, work_path, work_path_size, dir_ent, on_enter_dir, on_exit_dir, on_file));
|
|
|
|
|
|
|
|
/* Exit directory. */
|
|
|
|
R_TRY(on_exit_dir(work_path, *dir_ent));
|
|
|
|
} else {
|
|
|
|
/* Call file handler. */
|
|
|
|
R_TRY(on_file(work_path, *dir_ent));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Restore parent path. */
|
|
|
|
work_path[parent_len] = StringTraits::NullTerminator;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ResultSuccess();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: Cleanup. */
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Iteration API */
|
|
|
|
template<typename OnEnterDir, typename OnExitDir, typename OnFile>
|
|
|
|
Result IterateDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *root_path, char *work_path, size_t work_path_size, fs::DirectoryEntry *dir_ent_buf, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
|
2020-02-23 07:05:14 +00:00
|
|
|
AMS_ABORT_UNLESS(work_path_size >= fs::EntryNameLengthMax + 1);
|
2019-12-09 11:57:37 +00:00
|
|
|
|
|
|
|
/* Get size of the root path. */
|
|
|
|
size_t root_path_len = strnlen(root_path, fs::EntryNameLengthMax + 1);
|
|
|
|
R_UNLESS(root_path_len <= fs::EntryNameLengthMax, fs::ResultTooLongPath());
|
|
|
|
|
|
|
|
/* Copy root path in, add a / if necessary. */
|
|
|
|
std::memcpy(work_path, root_path, root_path_len);
|
|
|
|
if (!PathTool::IsSeparator(work_path[root_path_len - 1])) {
|
|
|
|
work_path[root_path_len++] = StringTraits::DirectorySeparator;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Make sure the result path is still valid. */
|
|
|
|
R_UNLESS(root_path_len <= fs::EntryNameLengthMax, fs::ResultTooLongPath());
|
|
|
|
work_path[root_path_len] = StringTraits::NullTerminator;
|
|
|
|
|
|
|
|
return impl::IterateDirectoryRecursivelyImpl(fs, work_path, work_path_size, dir_ent_buf, on_enter_dir, on_exit_dir, on_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename OnEnterDir, typename OnExitDir, typename OnFile>
|
|
|
|
Result IterateDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *root_path, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
|
|
|
|
fs::DirectoryEntry dir_entry = {};
|
|
|
|
char work_path[fs::EntryNameLengthMax + 1] = {};
|
|
|
|
return IterateDirectoryRecursively(fs, root_path, work_path, sizeof(work_path), &dir_entry, on_enter_dir, on_exit_dir, on_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename OnEnterDir, typename OnExitDir, typename OnFile>
|
|
|
|
Result IterateDirectoryRecursively(fs::fsa::IFileSystem *fs, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
|
|
|
|
return IterateDirectoryRecursively(fs, PathTool::RootPath, on_enter_dir, on_exit_dir, on_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: Cleanup API */
|
|
|
|
|
|
|
|
/* Copy API. */
|
|
|
|
Result CopyFile(fs::fsa::IFileSystem *dst_fs, fs::fsa::IFileSystem *src_fs, const char *dst_parent_path, const char *src_path, const fs::DirectoryEntry *dir_ent, void *work_buf, size_t work_buf_size);
|
|
|
|
NX_INLINE Result CopyFile(fs::fsa::IFileSystem *fs, const char *dst_parent_path, const char *src_path, const fs::DirectoryEntry *dir_ent, void *work_buf, size_t work_buf_size) {
|
|
|
|
return CopyFile(fs, fs, dst_parent_path, src_path, dir_ent, work_buf, work_buf_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
Result CopyDirectoryRecursively(fs::fsa::IFileSystem *dst_fs, fs::fsa::IFileSystem *src_fs, const char *dst_path, const char *src_path, void *work_buf, size_t work_buf_size);
|
|
|
|
NX_INLINE Result CopyDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *dst_path, const char *src_path, void *work_buf, size_t work_buf_size) {
|
|
|
|
return CopyDirectoryRecursively(fs, fs, dst_path, src_path, work_buf, work_buf_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Semaphore adapter class. */
|
|
|
|
class SemaphoreAdapter : public os::Semaphore {
|
|
|
|
public:
|
|
|
|
SemaphoreAdapter(int c, int mc) : os::Semaphore(c, mc) { /* ... */ }
|
|
|
|
|
|
|
|
bool try_lock() {
|
|
|
|
return this->TryAcquire();
|
|
|
|
}
|
|
|
|
|
|
|
|
void unlock() {
|
|
|
|
this->Release();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Other utility. */
|
2020-03-08 09:45:12 +00:00
|
|
|
Result EnsureDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *path);
|
|
|
|
Result EnsureParentDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *path);
|
2019-12-09 11:57:37 +00:00
|
|
|
|
|
|
|
template<typename F>
|
|
|
|
NX_INLINE Result RetryFinitelyForTargetLocked(F f) {
|
|
|
|
/* Retry up to 10 times, 100ms between retries. */
|
|
|
|
constexpr s32 MaxRetryCount = 10;
|
|
|
|
constexpr u64 RetryWaitTime = 100'000'000ul;
|
|
|
|
|
|
|
|
s32 remaining_retries = MaxRetryCount;
|
|
|
|
while (true) {
|
|
|
|
R_TRY_CATCH(f()) {
|
|
|
|
R_CATCH(fs::ResultTargetLocked) {
|
|
|
|
R_UNLESS(remaining_retries > 0, fs::ResultTargetLocked());
|
|
|
|
|
|
|
|
remaining_retries--;
|
|
|
|
svcSleepThread(RetryWaitTime);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} R_END_TRY_CATCH;
|
|
|
|
|
|
|
|
return ResultSuccess();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|