/* * Copyright 2022 Hans-Kristian Arntzen for Valve Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #define VKD3D_DBG_CHANNEL VKD3D_DBG_CHANNEL_API #include "vkd3d_file_utils.h" #include "vkd3d_debug.h" /* For disk cache. */ #ifdef _WIN32 #include #include #else #include #include #include #endif #include #include #include bool vkd3d_file_rename_overwrite(const char *from_path, const char *to_path) { #ifdef _WIN32 DWORD code = ERROR_SUCCESS; if (!MoveFileA(from_path, to_path)) { code = GetLastError(); if (code == ERROR_ALREADY_EXISTS) { code = ERROR_SUCCESS; if (!ReplaceFileA(to_path, from_path, NULL, 0, NULL, NULL)) code = GetLastError(); } } return code == ERROR_SUCCESS; #else return rename(from_path, to_path) == 0; #endif } bool vkd3d_file_rename_no_replace(const char *from_path, const char *to_path) { #ifdef _WIN32 DWORD code = ERROR_SUCCESS; if (!MoveFileA(from_path, to_path)) code = GetLastError(); return code == ERROR_SUCCESS; #else return renameat2(AT_FDCWD, from_path, AT_FDCWD, to_path, RENAME_NOREPLACE) == 0; #endif } bool vkd3d_file_delete(const char *path) { #ifdef _WIN32 DWORD code = ERROR_SUCCESS; if (!DeleteFileA(path)) code = GetLastError(); return code == ERROR_SUCCESS; #else return unlink(path) == 0; #endif } FILE *vkd3d_file_open_exclusive_write(const char *path) { #ifdef _WIN32 /* From Fossilize. AFAIK, there is no direct way to make this work with FILE interface, so have to roundtrip * through jank POSIX layer. * wbx kinda works, but Wine warns about it, despite it working anyways. * Older MSVC runtimes do not support wbx. */ FILE *file = NULL; int fd; fd = _open(path, _O_BINARY | _O_WRONLY | _O_CREAT | _O_EXCL | _O_TRUNC | _O_SEQUENTIAL, _S_IWRITE | _S_IREAD); if (fd >= 0) { file = _fdopen(fd, "wb"); /* _fdopen takes ownership. */ if (!file) _close(fd); } return file; #else return fopen(path, "wbx"); #endif } void vkd3d_file_unmap(struct vkd3d_memory_mapped_file *file) { if (file->mapped) { #ifdef _WIN32 UnmapViewOfFile(file->mapped); #else munmap(file->mapped, file->mapped_size); #endif } memset(file, 0, sizeof(*file)); } bool vkd3d_file_map_read_only(const char *path, struct vkd3d_memory_mapped_file *file) { #ifdef _WIN32 DWORD size_hi, size_lo; HANDLE file_mapping; HANDLE handle; #else struct stat stat_buf; int fd; #endif file->mapped = NULL; file->mapped_size = 0; #ifdef _WIN32 handle = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, INVALID_HANDLE_VALUE); if (handle == INVALID_HANDLE_VALUE) goto out; size_lo = GetFileSize(handle, &size_hi); file->mapped_size = size_lo | (((uint64_t)size_hi) << 32); file_mapping = CreateFileMappingA(handle, NULL, PAGE_READONLY, 0, 0, NULL); if (file_mapping == INVALID_HANDLE_VALUE) goto out; file->mapped = MapViewOfFile(file_mapping, FILE_MAP_READ, 0, 0, file->mapped_size); CloseHandle(file_mapping); file_mapping = INVALID_HANDLE_VALUE; if (!file->mapped) { ERR("Failed to MapViewOfFile for %s.\n", path); goto out; } out: if (handle != INVALID_HANDLE_VALUE) CloseHandle(handle); #else fd = open(path, O_RDONLY); if (fd < 0) goto out; if (fstat(fd, &stat_buf) < 0) { ERR("Failed to fstat pipeline cache.\n"); goto out; } /* Map private to make sure we get CoW behavior in case someone clobbers * the cache while in flight. We need to read data directly out of the cache. */ file->mapped = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (file->mapped != MAP_FAILED) file->mapped_size = stat_buf.st_size; else goto out; out: if (fd >= 0) close(fd); #endif if (!file->mapped) file->mapped_size = 0; return file->mapped != NULL; }