fs: update LocalFileSystem

This commit is contained in:
Michael Scire 2022-03-28 01:33:40 -07:00
parent 0fbf007bcf
commit 9b47ddf01f
3 changed files with 76 additions and 42 deletions

View File

@ -151,22 +151,26 @@ namespace ams::fs {
R_SUCCEED();
}
const char *GetString() const {
constexpr const char *GetString() const {
/* Check pre-conditions. */
AMS_ASSERT(this->IsNormalized());
return m_str;
}
size_t GetLength() const {
constexpr size_t GetLength() const {
if (std::is_constant_evaluated()) {
return util::Strlen(this->GetString());
} else {
return std::strlen(this->GetString());
}
}
bool IsEmpty() const {
constexpr bool IsEmpty() const {
return *m_str == '\x00';
}
bool IsMatchHead(const char *p, size_t len) const {
constexpr bool IsMatchHead(const char *p, size_t len) const {
return util::Strncmp(this->GetString(), p, len) == 0;
}
@ -659,4 +663,9 @@ namespace ams::fs {
R_RETURN(SetUpFixedPath(out, buf));
}
constexpr inline bool IsWindowsDriveRootPath(const fs::Path &path) {
const char * const str = path.GetString();
return fs::IsWindowsDrive(str) && (str[2] == StringTraits::DirectorySeparator || str[2] == StringTraits::AlternateDirectorySeparator) && str[3] == StringTraits::NullTerminator;
}
}

View File

@ -128,6 +128,9 @@ namespace ams::fssystem {
/* If the error was access denied, we want to try again. */
R_UNLESS(err == ERROR_ACCESS_DENIED, ConvertLastErrorToResult());
/* Sleep before checking again. */
::Sleep(2);
}
/* We received access denied 25 times in a row. */
@ -160,8 +163,21 @@ namespace ams::fssystem {
Result SetFileSizeImpl(HANDLE handle, s64 size) {
/* Seek to the desired size. */
LONG high = size >> BITSIZEOF(LONG);
::SetFilePointer(handle, static_cast<LONG>(size), std::addressof(high), FILE_BEGIN);
LARGE_INTEGER seek;
seek.QuadPart = size;
R_UNLESS(::SetFilePointerEx(handle, seek, nullptr, FILE_BEGIN) != 0, ConvertLastErrorToResult());
/* Try to set the file size. */
if (::SetEndOfFile(handle) == 0) {
/* Check if the error resulted from too large size. */
R_UNLESS(::GetLastError() == ERROR_INVALID_PARAMETER, ConvertLastErrorToResult());
R_UNLESS(size <= INT64_C(0x00000FFFFFFF0000), ConvertLastErrorToResult());
/* The file size is too large. */
R_THROW(fs::ResultTooLargeSize());
}
R_SUCCEED();
/* Set the file size. */
R_UNLESS(::SetEndOfFile(handle), ConvertLastErrorToResult());
@ -313,6 +329,10 @@ namespace ams::fssystem {
}
};
bool IsDirectory(const WIN32_FIND_DATAW &fd) {
return fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
}
class LocalDirectory : public ::ams::fs::fsa::IDirectory, public ::ams::fs::impl::Newable {
private:
std::unique_ptr<wchar_t[], ::ams::fs::impl::Deleter> m_path;
@ -320,12 +340,14 @@ namespace ams::fssystem {
HANDLE m_search_handle;
fs::OpenDirectoryMode m_open_mode;
public:
LocalDirectory(HANDLE d, HANDLE s, fs::OpenDirectoryMode m, std::unique_ptr<wchar_t[], ::ams::fs::impl::Deleter> &&p) : m_path(std::move(p)), m_dir_handle(d), m_search_handle(s) {
LocalDirectory(HANDLE d, fs::OpenDirectoryMode m, std::unique_ptr<wchar_t[], ::ams::fs::impl::Deleter> &&p) : m_path(std::move(p)), m_dir_handle(d), m_search_handle(INVALID_HANDLE_VALUE) {
m_open_mode = static_cast<fs::OpenDirectoryMode>(util::ToUnderlying(m) & ~util::ToUnderlying(fs::OpenDirectoryMode_NotRequireFileSize));
}
virtual ~LocalDirectory() {
if (m_search_handle != INVALID_HANDLE_VALUE) {
::FindClose(m_search_handle);
}
::CloseHandle(m_dir_handle);
}
public:
@ -335,7 +357,14 @@ namespace ams::fssystem {
/* Read the next file. */
WIN32_FIND_DATAW fd;
std::memset(fd.cFileName, 0, sizeof(fd.cFileName));
if (!::FindNextFileW(m_search_handle, std::addressof(fd))) {
if (m_search_handle == INVALID_HANDLE_VALUE) {
/* Create our search handle. */
if (m_search_handle = ::FindFirstFileW(m_path.get(), std::addressof(fd)); m_search_handle == INVALID_HANDLE_VALUE) {
/* Check that we failed because there are no files. */
R_UNLESS(::GetLastError() == ERROR_FILE_NOT_FOUND, ConvertLastErrorToResult());
break;
}
} else if (!::FindNextFileW(m_search_handle, std::addressof(fd))) {
/* Check that we failed because we ran out of files. */
R_UNLESS(::GetLastError() == ERROR_NO_MORE_FILES, ConvertLastErrorToResult());
break;
@ -353,8 +382,7 @@ namespace ams::fssystem {
const auto wide_res = ::WideCharToMultiByte(CP_UTF8, 0, fd.cFileName, -1, entry.name, sizeof(entry.name), nullptr, nullptr);
R_UNLESS(wide_res != 0, fs::ResultInvalidPath());
entry.type = (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? fs::DirectoryEntryType_Directory : fs::DirectoryEntryType_File;
entry.type = IsDirectory(fd) ? fs::DirectoryEntryType_Directory : fs::DirectoryEntryType_File;
entry.file_size = static_cast<s64>(fd.nFileSizeLow) | static_cast<s64>(static_cast<u64>(fd.nFileSizeHigh) << BITSIZEOF(fd.nFileSizeLow));
}
@ -393,7 +421,7 @@ namespace ams::fssystem {
}
/* Return whether our open mode supports the target. */
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if (IsDirectory(fd)) {
return m_open_mode != fs::OpenDirectoryMode_File;
} else {
return m_open_mode != fs::OpenDirectoryMode_Directory;
@ -644,11 +672,9 @@ namespace ams::fssystem {
uintptr_t m_basep = 0;
#endif
public:
LocalDirectory(int d, int s, fs::OpenDirectoryMode m, std::unique_ptr<char[], ::ams::fs::impl::Deleter> &&p) : m_path(std::move(p)), m_dir_handle(d), m_temp_entries(nullptr), m_temp_entries_count(0), m_temp_entries_ofs(0) {
LocalDirectory(int d, fs::OpenDirectoryMode m, std::unique_ptr<char[], ::ams::fs::impl::Deleter> &&p) : m_path(std::move(p)), m_dir_handle(d), m_temp_entries(nullptr), m_temp_entries_count(0), m_temp_entries_ofs(0) {
m_open_mode = static_cast<fs::OpenDirectoryMode>(util::ToUnderlying(m) & ~util::ToUnderlying(fs::OpenDirectoryMode_NotRequireFileSize));
m_not_require_file_size = m & fs::OpenDirectoryMode_NotRequireFileSize;
AMS_ASSERT(s < 0);
AMS_UNUSED(s);
}
virtual ~LocalDirectory() {
@ -1269,6 +1295,7 @@ namespace ams::fssystem {
}
} R_END_TRY_CATCH;
}
ON_RESULT_FAILURE { ::DeleteFileW(native_path.get()); };
ON_SCOPE_EXIT { ::CloseHandle(handle); };
/* Set the file as sparse. */
@ -1289,6 +1316,7 @@ namespace ams::fssystem {
return ::open(native_path.get(), O_WRONLY | O_CREAT | O_EXCL, 0666);
});
R_UNLESS(handle >= 0, ConvertErrnoToResult(ErrnoSource_CreateFile));
ON_RESULT_FAILURE { ::unlink(native_path.get()); };
ON_SCOPE_EXIT { CloseFileDescriptor(handle); };
/* Set the file as sparse. */
@ -1381,6 +1409,13 @@ namespace ams::fssystem {
}
Result LocalFileSystem::DoDeleteDirectory(const fs::Path &path) {
/* Guard against deletion of raw drive. */
#if defined(ATMOSPHERE_OS_WINDOWS)
R_UNLESS(!fs::IsWindowsDriveRootPath(path), fs::ResultDirectoryNotDeletable());
#else
/* TODO: Linux/macOS? */
#endif
/* Resolve the path. */
NativePathBuffer native_path;
R_TRY(this->ResolveFullPath(std::addressof(native_path), path, MaxFilePathLength, 0, true));
@ -1404,6 +1439,13 @@ namespace ams::fssystem {
}
Result LocalFileSystem::DoDeleteDirectoryRecursively(const fs::Path &path) {
/* Guard against deletion of raw drive. */
#if defined(ATMOSPHERE_OS_WINDOWS)
R_UNLESS(!fs::IsWindowsDriveRootPath(path), fs::ResultDirectoryNotDeletable());
#else
/* TODO: Linux/macOS? */
#endif
/* Resolve the path. */
NativePathBuffer native_path;
R_TRY(this->ResolveFullPath(std::addressof(native_path), path, MaxFilePathLength, 0, true));
@ -1585,6 +1627,13 @@ namespace ams::fssystem {
R_UNLESS(dir_handle != INVALID_HANDLE_VALUE, ConvertLastErrorToResult());
ON_RESULT_FAILURE { ::CloseHandle(dir_handle); };
/* Check that we tried to open a directory. */
fs::DirectoryEntryType type;
R_TRY(GetEntryTypeImpl(std::addressof(type), native_path.get()));
/* If the type isn't directory, return path not found. */
R_UNLESS(type == fs::DirectoryEntryType_Directory, fs::ResultPathNotFound());
/* Fix up the path for us to perform a windows search. */
const auto native_len = ::wcslen(native_path.get());
const bool has_sep = native_len > 0 && native_path[native_len - 1] == '\\';
@ -1596,40 +1645,15 @@ namespace ams::fssystem {
native_path[native_len + 1] = '*';
native_path[native_len + 2] = 0;
}
/* Open windows search handle. */
WIN32_FIND_DATAW fd;
const auto search_handle = ::FindFirstFileW(native_path.get(), std::addressof(fd));
if (search_handle == INVALID_HANDLE_VALUE) {
/* Convert the last error to a result. */
const auto last_error_result = ConvertLastErrorToResult();
/* Fix the path, so that we can check if we tried to open a file. */
native_path[native_len] = 0;
/* Check if we tried to open a directory. */
fs::DirectoryEntryType type;
R_TRY(GetEntryTypeImpl(std::addressof(type), native_path.get()));
/* If the type isn't directory, return path not found. */
R_UNLESS(type == fs::DirectoryEntryType_Directory, fs::ResultPathNotFound());
/* Return the error we encountered earlier. */
R_THROW(last_error_result);
}
ON_RESULT_FAILURE_2 { ::CloseHandle(search_handle); };
#else
/* Open the directory. */
const auto dir_handle = RetryForEIntr([&] () ALWAYS_INLINE_LAMBDA { return ::open(native_path.get(), O_RDONLY | O_DIRECTORY); });
R_UNLESS(dir_handle >= 0, ConvertErrnoToResult(ErrnoSource_OpenDirectory));
ON_RESULT_FAILURE { CloseFileDescriptor(dir_handle); };
/* Non-windows doesn't have separate search handle. */
const auto search_handle = -1;
#endif
/* Create a new local directory. */
auto dir = std::make_unique<LocalDirectory>(dir_handle, search_handle, mode, std::move(native_path));
auto dir = std::make_unique<LocalDirectory>(dir_handle, mode, std::move(native_path));
R_UNLESS(dir != nullptr, fs::ResultAllocationMemoryFailedInLocalFileSystemB());
/* Set the output directory. */

View File

@ -452,6 +452,7 @@ namespace ams::fs {
R_DEFINE_ERROR_RESULT(ExtensionSizeInvalid, 6067);
R_DEFINE_ERROR_RESULT(InvalidOpenMode, 6072);
R_DEFINE_ERROR_RESULT(TooLargeSize, 6073);
R_DEFINE_ERROR_RANGE(InvalidEnumValue, 6080, 6099);
R_DEFINE_ERROR_RESULT(InvalidSaveDataState, 6081);