/* * 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 . */ #pragma once #include #include #include #include #include namespace ams::fssystem { namespace impl { /* Iteration. */ template 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 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] = fs::StringTraits::NullTerminator; } return ResultSuccess(); } /* TODO: Cleanup. */ } /* Iteration API */ template 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) { AMS_ABORT_UNLESS(work_path_size >= fs::EntryNameLengthMax + 1); /* 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 (!fs::PathNormalizer::IsSeparator(work_path[root_path_len - 1])) { work_path[root_path_len++] = fs::StringTraits::DirectorySeparator; } /* Make sure the result path is still valid. */ R_UNLESS(root_path_len <= fs::EntryNameLengthMax, fs::ResultTooLongPath()); work_path[root_path_len] = fs::StringTraits::NullTerminator; return impl::IterateDirectoryRecursivelyImpl(fs, work_path, work_path_size, dir_ent_buf, on_enter_dir, on_exit_dir, on_file); } template 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 Result IterateDirectoryRecursively(fs::fsa::IFileSystem *fs, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) { return IterateDirectoryRecursively(fs, fs::PathNormalizer::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. */ Result HasFile(bool *out, fs::fsa::IFileSystem *fs, const char *path); Result HasDirectory(bool *out, fs::fsa::IFileSystem *fs, const char *path); Result EnsureDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *path); Result EnsureParentDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *path); template 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(); } } void AddCounter(void *counter, size_t counter_size, u64 value); }