diff options
author | David Robillard <d@drobilla.net> | 2022-10-23 13:41:27 -0400 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2022-10-23 14:57:45 -0400 |
commit | d8b960be46007f9c09356e526d3c2dcff4b186a5 (patch) | |
tree | cd40db8d5634e74f8795922b7ab19fc8c6507648 /src/win32 | |
parent | c886d489576cd0bc33d7d22d81981c794067946f (diff) | |
download | zix-d8b960be46007f9c09356e526d3c2dcff4b186a5.tar.gz zix-d8b960be46007f9c09356e526d3c2dcff4b186a5.tar.bz2 zix-d8b960be46007f9c09356e526d3c2dcff4b186a5.zip |
Add filesystem API
Diffstat (limited to 'src/win32')
-rw-r--r-- | src/win32/filesystem_win32.c | 325 | ||||
-rw-r--r-- | src/win32/system_win32.c | 20 |
2 files changed, 345 insertions, 0 deletions
diff --git a/src/win32/filesystem_win32.c b/src/win32/filesystem_win32.c new file mode 100644 index 0000000..3e9b7a6 --- /dev/null +++ b/src/win32/filesystem_win32.c @@ -0,0 +1,325 @@ +// Copyright 2007-2022 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#define _WIN32_WINNT 0x0600 // Vista + +#include "zix/bump_allocator.h" +#include "zix/filesystem.h" + +#include "../errno_status.h" +#include "../zix_config.h" + +#include "zix/allocator.h" +#include "zix/path.h" +#include "zix/status.h" + +#include <direct.h> +#include <fcntl.h> +#include <io.h> +#include <limits.h> +#include <sys/stat.h> +#include <windows.h> + +#include <errno.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static inline ZixStatus +zix_winerror_status(const DWORD e) +{ + switch (e) { + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_OUTOFMEMORY: + return ZIX_STATUS_NO_MEM; + case ERROR_SEM_TIMEOUT: + return ZIX_STATUS_TIMEOUT; + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + return ZIX_STATUS_NOT_FOUND; + case ERROR_BUFFER_OVERFLOW: + return ZIX_STATUS_OVERFLOW; + case ERROR_DISK_FULL: + return ZIX_STATUS_NO_SPACE; + case ERROR_ALREADY_EXISTS: + case ERROR_FILE_EXISTS: + return ZIX_STATUS_EXISTS; + case ERROR_PRIVILEGE_NOT_HELD: + return ZIX_STATUS_BAD_PERMS; + case ERROR_LOCK_VIOLATION: + return ZIX_STATUS_UNAVAILABLE; + } + + return ZIX_STATUS_ERROR; +} + +static inline ZixStatus +zix_windows_status(const bool success) +{ + return success ? ZIX_STATUS_SUCCESS : zix_winerror_status(GetLastError()); +} + +ZixStatus +zix_copy_file(ZixAllocator* const allocator, + const char* const src, + const char* const dst, + const ZixCopyOptions options) +{ + (void)allocator; + + return zix_windows_status( + CopyFile(src, dst, !(options & ZIX_COPY_OPTION_OVERWRITE_EXISTING))); +} + +char* +zix_create_temporary_directory(ZixAllocator* const allocator, + const char* const path_pattern) +{ + static const char chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + static const int n_chars = sizeof(chars) - 1; + + // Ensure that the pattern ends with "XXXXXX" + const size_t length = strlen(path_pattern); + if (length < 7 || strcmp(path_pattern + length - 6, "XXXXXX")) { + errno = EINVAL; + return NULL; + } + + // Allocate a result to manipulate as we search for paths + char* const result = (char*)zix_calloc(allocator, length + 1U, 1U); + if (!result) { + return NULL; + } + + // Repeatedly try creating a directory with random suffixes + memcpy(result, path_pattern, length + 1U); + char* const suffix = result + length - 6U; + for (unsigned attempt = 0U; attempt < 128U; ++attempt) { + for (unsigned i = 0U; i < 6U; ++i) { + suffix[i] = chars[rand() % n_chars]; + } + + if (!_mkdir(result)) { + return result; + } + } + + zix_free(allocator, result); + return NULL; +} + +ZixStatus +zix_remove(const char* const path) +{ + return ((zix_file_type(path) == ZIX_FILE_TYPE_DIRECTORY) + ? zix_windows_status(RemoveDirectory(path)) + : remove(path) ? zix_errno_status(errno) + : ZIX_STATUS_SUCCESS); +} + +void +zix_dir_for_each(const char* const path, + void* const data, + void (*const f)(const char* path, + const char* name, + void* data)) +{ + const size_t path_len = strlen(path); + char pat[MAX_PATH + 2U]; + memcpy(pat, path, path_len + 1U); + pat[path_len] = '\\'; + pat[path_len + 1U] = '*'; + pat[path_len + 2U] = '\0'; + + WIN32_FIND_DATA fd; + HANDLE fh = FindFirstFile(pat, &fd); + if (fh != INVALID_HANDLE_VALUE) { + do { + if (!!strcmp(fd.cFileName, ".") && !!strcmp(fd.cFileName, "..")) { + f(path, fd.cFileName, data); + } + } while (FindNextFile(fh, &fd)); + } + FindClose(fh); +} + +ZixStatus +zix_file_lock(FILE* const file, const ZixFileLockMode mode) +{ + HANDLE handle = (HANDLE)_get_osfhandle(fileno(file)); + OVERLAPPED overlapped = {0}; + + const DWORD flags = + (LOCKFILE_EXCLUSIVE_LOCK | + (mode == ZIX_FILE_LOCK_TRY ? LOCKFILE_FAIL_IMMEDIATELY : 0)); + + return zix_windows_status( + LockFileEx(handle, flags, 0, UINT32_MAX, UINT32_MAX, &overlapped)); +} + +ZixStatus +zix_file_unlock(FILE* const file, const ZixFileLockMode mode) +{ + (void)mode; + + HANDLE handle = (HANDLE)_get_osfhandle(fileno(file)); + OVERLAPPED overlapped = {0}; + + return zix_windows_status( + UnlockFileEx(handle, 0, UINT32_MAX, UINT32_MAX, &overlapped)); +} + +char* +zix_canonical_path(ZixAllocator* const allocator, const char* const path) +{ + char full[MAX_PATH] = {0}; + if (!path || !GetFullPathName(path, MAX_PATH, full, NULL)) { + return NULL; + } + + const HANDLE h = + CreateFile(full, + FILE_READ_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL); + + const DWORD flags = FILE_NAME_NORMALIZED | VOLUME_NAME_DOS; + const DWORD final_size = GetFinalPathNameByHandle(h, NULL, 0U, flags); + if (!final_size) { + CloseHandle(h); + return NULL; + } + + char* const final = (char*)zix_calloc(allocator, final_size + 1U, 1U); + if (final && !GetFinalPathNameByHandle(h, final, final_size + 1U, flags)) { + zix_free(allocator, final); + CloseHandle(h); + return NULL; + } + + CloseHandle(h); + return final; +} + +static ZixFileType +attrs_file_type(const DWORD attrs) +{ + if (attrs == INVALID_FILE_ATTRIBUTES) { + return ZIX_FILE_TYPE_NONE; + } + + if (attrs & FILE_ATTRIBUTE_DIRECTORY) { + return ZIX_FILE_TYPE_DIRECTORY; + } + + if (attrs & FILE_ATTRIBUTE_REPARSE_POINT) { + return ZIX_FILE_TYPE_SYMLINK; + } + + if (attrs & (FILE_ATTRIBUTE_DEVICE)) { + return ZIX_FILE_TYPE_UNKNOWN; + } + + return ZIX_FILE_TYPE_REGULAR; +} + +ZixFileType +zix_file_type(const char* const path) +{ + const ZixFileType type = attrs_file_type(GetFileAttributes(path)); + if (type != ZIX_FILE_TYPE_SYMLINK) { + return type; + } + + // Resolve symlink to find the canonical type + char buf[MAX_PATH]; + ZixBumpAllocator allocator = zix_bump_allocator(sizeof(buf), buf); + char* const canonical = zix_canonical_path(&allocator.base, path); + return zix_file_type(canonical); +} + +ZixFileType +zix_symlink_type(const char* const path) +{ + return attrs_file_type(GetFileAttributes(path)); +} + +ZixStatus +zix_create_directory(const char* const dir_path) +{ + return (!dir_path[0]) ? ZIX_STATUS_BAD_ARG + : _mkdir(dir_path) ? zix_errno_status(errno) + : ZIX_STATUS_SUCCESS; +} + +ZixStatus +zix_create_directory_like(const char* const dir_path, + const char* const existing_path) +{ + return (zix_file_type(existing_path) != ZIX_FILE_TYPE_DIRECTORY) + ? ZIX_STATUS_NOT_FOUND + : zix_create_directory(dir_path); +} + +ZixStatus +zix_create_symlink(const char* const target_path, const char* const link_path) +{ +#if USE_CREATESYMBOLICLINK + static const DWORD flags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; + + return zix_windows_status(CreateSymbolicLink(link_path, target_path, flags)); +#else + return ZIX_STATUS_NOT_SUPPORTED; +#endif +} + +ZixStatus +zix_create_directory_symlink(const char* const target_path, + const char* const link_path) +{ +#if USE_CREATESYMBOLICLINK + static const DWORD flags = + SYMBOLIC_LINK_FLAG_DIRECTORY | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; + + return zix_windows_status(CreateSymbolicLink(link_path, target_path, flags)); +#else + return ZIX_STATUS_NOT_SUPPORTED; +#endif +} + +ZixStatus +zix_create_hard_link(const char* const target_path, const char* const link_path) +{ + return zix_windows_status(CreateHardLink(link_path, target_path, NULL)); +} + +char* +zix_temp_directory_path(ZixAllocator* const allocator) +{ + const DWORD size = GetTempPath(0U, NULL); + char* const buf = (char*)zix_calloc(allocator, size, 1); + if (buf && (GetTempPath(size, buf) != size - 1U)) { + zix_free(allocator, buf); + return NULL; + } + + return buf; +} + +char* +zix_current_path(ZixAllocator* const allocator) +{ + const DWORD size = GetCurrentDirectory(0U, NULL); + char* const buf = (char*)zix_calloc(allocator, size, 1); + if (buf && (GetCurrentDirectory(size, buf) != size - 1U)) { + zix_free(allocator, buf); + return NULL; + } + + return buf; +} diff --git a/src/win32/system_win32.c b/src/win32/system_win32.c new file mode 100644 index 0000000..a2e68bf --- /dev/null +++ b/src/win32/system_win32.c @@ -0,0 +1,20 @@ +// Copyright 2007-2022 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#include "../system.h" + +#include <windows.h> + +#include <limits.h> +#include <stdint.h> + +uint32_t +zix_system_page_size(void) +{ + SYSTEM_INFO info; + GetSystemInfo(&info); + + return (info.dwPageSize > 0 && info.dwPageSize < UINT32_MAX) + ? (uint32_t)info.dwPageSize + : 512U; +} |