Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/core/StarFile_windows.cpp
diff options
context:
space:
mode:
authorKae <80987908+Novaenia@users.noreply.github.com>2023-06-20 14:33:09 +1000
committerKae <80987908+Novaenia@users.noreply.github.com>2023-06-20 14:33:09 +1000
commit6352e8e3196f78388b6c771073f9e03eaa612673 (patch)
treee23772f79a7fbc41bc9108951e9e136857484bf4 /source/core/StarFile_windows.cpp
parent6741a057e5639280d85d0f88ba26f000baa58f61 (diff)
everything everywhere
all at once
Diffstat (limited to 'source/core/StarFile_windows.cpp')
-rw-r--r--source/core/StarFile_windows.cpp412
1 files changed, 412 insertions, 0 deletions
diff --git a/source/core/StarFile_windows.cpp b/source/core/StarFile_windows.cpp
new file mode 100644
index 0000000..bb13ffc
--- /dev/null
+++ b/source/core/StarFile_windows.cpp
@@ -0,0 +1,412 @@
+#include "StarFile.hpp"
+#include "StarFormat.hpp"
+#include "StarRandom.hpp"
+#include "StarEncode.hpp"
+#include "StarMathCommon.hpp"
+#include "StarThread.hpp"
+
+#include "StarString_windows.hpp"
+
+#include <errno.h>
+#include <io.h>
+#include <stdio.h>
+#include <windows.h>
+
+#ifndef MAX_PATH
+#define MAX_PATH 1024
+#endif
+
+namespace Star {
+
+namespace {
+ OVERLAPPED makeOverlapped(StreamOffset offset) {
+ OVERLAPPED overlapped = {};
+ overlapped.Offset = offset;
+ overlapped.OffsetHigh = offset >> 32;
+ return overlapped;
+ }
+}
+
+String File::convertDirSeparators(String const& path) {
+ return path.replace("/", "\\");
+}
+
+String File::currentDirectory() {
+ WCHAR buffer[MAX_PATH];
+ size_t len = GetCurrentDirectoryW(MAX_PATH, buffer);
+ if (len == 0)
+ throw IOException("GetCurrentDirectory failed");
+
+ return utf16ToString(buffer);
+}
+
+void File::changeDirectory(const String& dirName) {
+ if (!SetCurrentDirectoryW(stringToUtf16(dirName).get()))
+ throw IOException(strf("could not change directory to %s", dirName));
+}
+
+void File::makeDirectory(String const& dirName) {
+ if (CreateDirectoryW(stringToUtf16(dirName).get(), NULL) == 0) {
+ auto error = GetLastError();
+ throw IOException(strf("could not create directory '%s', %s", dirName, error));
+ }
+}
+
+bool File::exists(String const& path) {
+ WIN32_FIND_DATAW findFileData;
+ const HANDLE handle = FindFirstFileW(stringToUtf16(path).get(), &findFileData);
+ if (handle == INVALID_HANDLE_VALUE)
+ return false;
+ FindClose(handle);
+ return true;
+}
+
+bool File::isFile(String const& path) {
+ WIN32_FIND_DATAW findFileData;
+ const HANDLE handle = FindFirstFileW(stringToUtf16(path).get(), &findFileData);
+ if (handle == INVALID_HANDLE_VALUE)
+ return false;
+ FindClose(handle);
+ return (FILE_ATTRIBUTE_DIRECTORY & findFileData.dwFileAttributes) == 0;
+}
+
+bool File::isDirectory(String const& path) {
+ DWORD attribs = GetFileAttributesW(stringToUtf16(path.trimEnd("\\/")).get());
+ if (attribs == INVALID_FILE_ATTRIBUTES)
+ return false;
+ return attribs & FILE_ATTRIBUTE_DIRECTORY;
+}
+
+String File::fullPath(const String& path) {
+ WCHAR buffer[MAX_PATH];
+
+ size_t fullpath_size;
+ WCHAR* lpszLastNamePart;
+
+ fullpath_size = GetFullPathNameW(stringToUtf16(path).get(), (DWORD)MAX_PATH, buffer, (WCHAR**)&lpszLastNamePart);
+ if (0 == fullpath_size)
+ throw IOException::format("GetFullPathName failed on path: '%s'", path);
+ if (fullpath_size >= MAX_PATH)
+ throw IOException::format("GetFullPathName failed on path: '%s'", path);
+
+ return utf16ToString(buffer);
+}
+
+List<std::pair<String, bool>> File::dirList(const String& dirName, bool skipDots) {
+ List<std::pair<String, bool>> fileList;
+ WIN32_FIND_DATAW findFileData;
+ HANDLE hFind;
+
+ hFind = FindFirstFileW(stringToUtf16(File::relativeTo(dirName, "*")).get(), &findFileData);
+ if (hFind == INVALID_HANDLE_VALUE)
+ throw IOException(strf("Invalid file handle in dirList of '%s', error is %u", dirName, GetLastError()));
+
+ while (true) {
+ String entry = utf16ToString(findFileData.cFileName);
+ if (!skipDots || (entry != "." && entry != ".."))
+ fileList.append({entry, (FILE_ATTRIBUTE_DIRECTORY & findFileData.dwFileAttributes) != 0});
+ if (!FindNextFileW(hFind, &findFileData))
+ break;
+ }
+
+ DWORD dwError = GetLastError();
+ FindClose(hFind);
+
+ if ((dwError != ERROR_NO_MORE_FILES) && (dwError != NO_ERROR))
+ throw IOException(strf("FindNextFile error in dirList of '%s'. Error is %u", dirName, dwError));
+
+ return fileList;
+}
+
+String File::baseName(const String& fileName) {
+ return String(fileName).rextract("\\/");
+}
+
+String File::dirName(const String& fileName) {
+ if (fileName == "\\" || fileName == "/")
+ return "\\";
+
+ String directory = fileName;
+ directory.rextract("\\/");
+ if (directory.empty())
+ return ".";
+ else
+ return directory;
+}
+
+String File::relativeTo(String const& relativeTo, String const& path) {
+ if (path.beginsWith('/') || path.beginsWith('\\') || path.regexMatch("^[a-z]:", false, false))
+ return path;
+
+ String finalPath;
+ if (relativeTo.endsWith('\\') || relativeTo.endsWith('/'))
+ finalPath = relativeTo.substr(0, relativeTo.size() - 1);
+ else if (relativeTo.endsWith("\\.") || relativeTo.endsWith("/."))
+ finalPath = relativeTo.substr(0, relativeTo.size() - 2);
+ else
+ finalPath = relativeTo;
+
+ if (path.beginsWith(".\\") || path.beginsWith("./"))
+ finalPath += '\\' + path.substr(2);
+ else
+ finalPath += '\\' + path;
+
+ return finalPath;
+}
+
+String File::temporaryFileName() {
+ WCHAR tempPath[MAX_PATH];
+ if (!GetTempPathW(MAX_PATH, tempPath)) {
+ auto error = GetLastError();
+ throw IOException(strf("Could not call GetTempPath %s", error));
+ }
+
+ return relativeTo(utf16ToString(tempPath), strf("starbound.tmpfile.%s", hexEncode(Random::randBytes(16))));
+}
+
+FilePtr File::temporaryFile() {
+ return open(temporaryFileName(), IOMode::ReadWrite);
+}
+
+FilePtr File::ephemeralFile() {
+ auto file = temporaryFile();
+ DeleteFileW(stringToUtf16(file->fileName()).get());
+ file->m_filename = "";
+ return file;
+}
+
+String File::temporaryDirectory() {
+ WCHAR tempPath[MAX_PATH];
+ if (!GetTempPathW(MAX_PATH, tempPath)) {
+ auto error = GetLastError();
+ throw IOException(strf("Could not call GetTempPath %s", error));
+ }
+
+ String dirname = relativeTo(utf16ToString(tempPath), strf("starbound.tmpdir.%s", hexEncode(Random::randBytes(16))));
+ makeDirectory(dirname);
+ return dirname;
+}
+
+void File::remove(String const& filename) {
+ if (isDirectory(filename)) {
+ if (!RemoveDirectoryW(stringToUtf16(filename).get())) {
+ auto error = GetLastError();
+ throw IOException(strf("Rename error: %s", error));
+ }
+ } else if (::_wremove(stringToUtf16(filename).get()) < 0) {
+ auto error = errno;
+ throw IOException::format("remove error: %s", strerror(error));
+ }
+}
+
+void File::rename(String const& source, String const& target) {
+ bool replace = File::exists(target);
+ auto temp = target + ".tmp";
+
+ if (replace) {
+ if (!DeleteFileW(stringToUtf16(temp).get())) {
+ auto error = GetLastError();
+ if (error != ERROR_FILE_NOT_FOUND)
+ throw IOException(strf("error deleting existing temp file: %s", error));
+ }
+ if (!MoveFileExW(stringToUtf16(target).get(), stringToUtf16(temp).get(), MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH)) {
+ auto error = GetLastError();
+ throw IOException(strf("error temporary file '%s': %s", temp, GetLastError()));
+ }
+ }
+
+ if (!MoveFileExW(stringToUtf16(source).get(), stringToUtf16(target).get(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH)) {
+ auto error = GetLastError();
+ throw IOException(strf("Rename error: %s", error));
+ }
+
+ if (replace && !DeleteFileW(stringToUtf16(temp).get())) {
+ auto error = GetLastError();
+ throw IOException(strf("error deleting temp file '%s': %s", temp, GetLastError()));
+ }
+}
+
+void File::overwriteFileWithRename(char const* data, size_t len, String const& filename, String const& newSuffix) {
+ String newFile = filename + newSuffix;
+
+ try {
+ auto file = File::open(newFile, IOMode::Write | IOMode::Truncate);
+ file->writeFull(data, len);
+ file->sync();
+ file->close();
+
+ File::rename(newFile, filename);
+ } catch (IOException const&) {
+ // HACK: Been having trouble on windows with the write / flush / move
+ // sequence, try super hard to just write the file non-atomically in case
+ // of weird file locking problems instead of erroring.
+
+ // Ignore any error on removal of the maybe existing newFile
+ ::_wremove(stringToUtf16(newFile).get());
+
+ writeFile(data, len, filename);
+ }
+}
+
+void* File::fopen(char const* filename, IOMode mode) {
+ DWORD desiredAccess = 0;
+ if (mode & IOMode::Read)
+ desiredAccess |= GENERIC_READ;
+ if (mode & IOMode::Write)
+ desiredAccess |= GENERIC_WRITE;
+
+ DWORD creationDisposition = 0;
+ if (mode & IOMode::Write)
+ creationDisposition = OPEN_ALWAYS;
+ else
+ creationDisposition = OPEN_EXISTING;
+
+ HANDLE file = CreateFileW(stringToUtf16(String(filename)).get(),
+ desiredAccess, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ creationDisposition, 0, NULL);
+
+ if (file == INVALID_HANDLE_VALUE)
+ throw IOException::format("could not open file '%s' %s", filename, GetLastError());
+
+ LARGE_INTEGER szero;
+ szero.QuadPart = 0;
+ if (!SetFilePointerEx(file, szero, NULL, 0)) {
+ CloseHandle(file);
+ throw IOException::format("could not set file pointer in fopen '%s' %s", filename, GetLastError());
+ }
+
+ if (mode & IOMode::Truncate) {
+ if (!SetEndOfFile(file)) {
+ CloseHandle(file);
+ throw IOException::format("could not set end of file in fopen '%s' %s", filename, GetLastError());
+ }
+ }
+
+ if (mode & IOMode::Append) {
+ LARGE_INTEGER size;
+ if (GetFileSizeEx(file, &size) == 0) {
+ CloseHandle(file);
+ throw IOException::format("could not get file size in fopen '%s' %s", filename, GetLastError());
+ }
+ if (!SetFilePointerEx(file, size, NULL, 0)) {
+ CloseHandle(file);
+ throw IOException::format("could not set file pointer in fopen '%s' %s", filename, GetLastError());
+ }
+ }
+
+ return (void*)file;
+}
+
+void File::fseek(void* f, StreamOffset offset, IOSeek seekMode) {
+ HANDLE file = (HANDLE)f;
+
+ LARGE_INTEGER loffset;
+ loffset.QuadPart = offset;
+
+ if (seekMode == IOSeek::Relative)
+ SetFilePointerEx(file, loffset, nullptr, FILE_CURRENT);
+ else if (seekMode == IOSeek::Absolute)
+ SetFilePointerEx(file, loffset, nullptr, FILE_BEGIN);
+ else
+ SetFilePointerEx(file, loffset, nullptr, FILE_END);
+}
+
+StreamOffset File::ftell(void* f) {
+ HANDLE file = (HANDLE)f;
+ LARGE_INTEGER pos;
+ LARGE_INTEGER szero;
+ szero.QuadPart = 0;
+ SetFilePointerEx(file, szero, &pos, FILE_CURRENT);
+ return pos.QuadPart;
+}
+
+size_t File::fread(void* f, char* data, size_t len) {
+ if (len == 0)
+ return 0;
+
+ HANDLE file = (HANDLE)f;
+
+ DWORD numRead = 0;
+ int ret = ReadFile(file, data, len, &numRead, nullptr);
+ if (ret == 0) {
+ auto err = GetLastError();
+ if (err != ERROR_IO_PENDING)
+ throw IOException::format("read error %s", err);
+ }
+
+ return numRead;
+}
+
+size_t File::fwrite(void* f, char const* data, size_t len) {
+ if (len == 0)
+ return 0;
+
+ HANDLE file = (HANDLE)f;
+
+ DWORD numWritten = 0;
+ int ret = WriteFile(file, data, len, &numWritten, nullptr);
+ if (ret == 0) {
+ auto err = GetLastError();
+ if (err != ERROR_IO_PENDING)
+ throw IOException::format("write error %s", err);
+ }
+
+ return numWritten;
+}
+
+void File::fsync(void* f) {
+ HANDLE file = (HANDLE)f;
+ if (FlushFileBuffers(file) == 0)
+ throw IOException::format("fsync error %s", GetLastError());
+}
+
+void File::fclose(void* f) {
+ HANDLE file = (HANDLE)f;
+ CloseHandle(file);
+}
+
+StreamOffset File::fsize(void* f) {
+ HANDLE file = (HANDLE)f;
+ LARGE_INTEGER size;
+ if (GetFileSizeEx(file, &size) == 0)
+ throw IOException::format("could not get file size in fsize %s", GetLastError());
+ return size.QuadPart;
+}
+
+size_t File::pread(void* f, char* data, size_t len, StreamOffset position) {
+ HANDLE file = (HANDLE)f;
+ DWORD numRead = 0;
+ OVERLAPPED overlapped = makeOverlapped(position);
+ int ret = ReadFile(file, data, len, &numRead, &overlapped);
+ if (ret == 0) {
+ auto err = GetLastError();
+ if (err != ERROR_IO_PENDING)
+ throw IOException::format("pread error %s", err);
+ }
+
+ return numRead;
+}
+
+size_t File::pwrite(void* f, char const* data, size_t len, StreamOffset position) {
+ HANDLE file = (HANDLE)f;
+ DWORD numWritten = 0;
+ OVERLAPPED overlapped = makeOverlapped(position);
+ int ret = WriteFile(file, data, len, &numWritten, &overlapped);
+ if (ret == 0) {
+ auto err = GetLastError();
+ if (err != ERROR_IO_PENDING)
+ throw IOException::format("pwrite error %s", err);
+ }
+
+ return numWritten;
+}
+
+void File::resize(void* f, StreamOffset size) {
+ HANDLE file = (HANDLE)f;
+ LARGE_INTEGER s;
+ s.QuadPart = size;
+ SetFilePointerEx(file, s, NULL, 0);
+ SetEndOfFile(file);
+}
+
+}