diff options
Diffstat (limited to 'src/win32/filesystem_win32.c')
-rw-r--r-- | src/win32/filesystem_win32.c | 298 |
1 files changed, 233 insertions, 65 deletions
diff --git a/src/win32/filesystem_win32.c b/src/win32/filesystem_win32.c index 5dc36a8..fa40cde 100644 --- a/src/win32/filesystem_win32.c +++ b/src/win32/filesystem_win32.c @@ -1,21 +1,22 @@ -// Copyright 2007-2022 David Robillard <d@drobilla.net> +// Copyright 2007-2024 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/bump_allocator.h" -#include "zix/filesystem.h" +#include <zix/filesystem.h> #include "../errno_status.h" #include "../zix_config.h" +#include "win32_util.h" -#include "zix/allocator.h" -#include "zix/path.h" -#include "zix/status.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 <tchar.h> #include <windows.h> #include <errno.h> @@ -25,6 +26,34 @@ #include <stdlib.h> #include <string.h> +#ifdef UNICODE + +static char* +path_result(ZixAllocator* const allocator, wchar_t* const path) +{ + if (!path) { + return NULL; + } + + static const wchar_t* const long_prefix = L"\\\\?\\"; + + const size_t p = !wcsncmp(path, long_prefix, 4U) ? 4U : 0U; + char* const result = zix_wchar_to_utf8(allocator, path + p); + zix_free(allocator, path); + return result; +} + +#else // !defined(UNICODE) + +static char* +path_result(ZixAllocator* const allocator, char* const path) +{ + (void)allocator; + return path; +} + +#endif + static inline ZixStatus zix_winerror_status(const DWORD e) { @@ -67,8 +96,15 @@ zix_copy_file(ZixAllocator* const allocator, { (void)allocator; - return zix_windows_status( - CopyFile(src, dst, !(options & ZIX_COPY_OPTION_OVERWRITE_EXISTING))); + ArgPathChar* const wsrc = arg_path_new(allocator, src); + ArgPathChar* const wdst = arg_path_new(allocator, dst); + + const BOOL ret = + CopyFile(wsrc, wdst, !(options & ZIX_COPY_OPTION_OVERWRITE_EXISTING)); + + arg_path_free(allocator, wdst); + arg_path_free(allocator, wsrc); + return zix_windows_status(ret); } /// Linear Congruential Generator for making random 32-bit integers @@ -111,7 +147,7 @@ zix_create_temporary_directory(ZixAllocator* const allocator, suffix[i] = chars[seed % n_chars]; } - if (!_mkdir(result)) { + if (!zix_create_directory(result)) { return result; } } @@ -123,22 +159,44 @@ zix_create_temporary_directory(ZixAllocator* const allocator, 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); + ArgPathChar* const wpath = arg_path_new(NULL, path); + const DWORD attrs = GetFileAttributes(wpath); + + const BOOL success = + ((attrs & FILE_ATTRIBUTE_DIRECTORY) ? RemoveDirectory(wpath) + : DeleteFile(wpath)); + + arg_path_free(NULL, wpath); + return zix_windows_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)) +zix_dir_for_each(const char* const path, + void* const data, + const ZixDirEntryVisitFunc f) { + static const TCHAR* const dot = TEXT("."); + static const TCHAR* const dotdot = TEXT(".."); + +#ifdef UNICODE + const int path_size = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0); + if (path_size < 1) { + return; + } + + const size_t path_len = (size_t)path_size - 1U; + TCHAR* const pat = (TCHAR*)zix_calloc(NULL, path_len + 4U, sizeof(TCHAR)); + if (!pat) { + return; + } + + MultiByteToWideChar(CP_UTF8, 0, path, -1, pat, path_size); +#else const size_t path_len = strlen(path); - char pat[MAX_PATH + 2U]; + TCHAR* const pat = (TCHAR*)zix_calloc(NULL, path_len + 2U, sizeof(TCHAR)); memcpy(pat, path, path_len + 1U); +#endif + pat[path_len] = '\\'; pat[path_len + 1U] = '*'; pat[path_len + 2U] = '\0'; @@ -147,8 +205,14 @@ zix_dir_for_each(const char* const path, HANDLE fh = FindFirstFile(pat, &fd); if (fh != INVALID_HANDLE_VALUE) { do { - if (!!strcmp(fd.cFileName, ".") && !!strcmp(fd.cFileName, "..")) { + if (!!_tcscmp(fd.cFileName, dot) && !!_tcscmp(fd.cFileName, dotdot)) { +#ifdef UNICODE + char* const name = zix_wchar_to_utf8(NULL, fd.cFileName); + f(path, name, data); + zix_free(NULL, name); +#else f(path, fd.cFileName, data); +#endif } } while (FindNextFile(fh, &fd)); } @@ -158,6 +222,12 @@ zix_dir_for_each(const char* const path, ZixStatus zix_file_lock(FILE* const file, const ZixFileLockMode mode) { +#ifdef __clang__ + (void)file; + (void)mode; + return ZIX_STATUS_NOT_SUPPORTED; +#else + HANDLE handle = (HANDLE)_get_osfhandle(fileno(file)); OVERLAPPED overlapped = {0}; @@ -167,58 +237,112 @@ zix_file_lock(FILE* const file, const ZixFileLockMode mode) return zix_windows_status( LockFileEx(handle, flags, 0, UINT32_MAX, UINT32_MAX, &overlapped)); +#endif } ZixStatus zix_file_unlock(FILE* const file, const ZixFileLockMode mode) { (void)mode; +#ifdef __clang__ + (void)file; + return ZIX_STATUS_NOT_SUPPORTED; +#else HANDLE handle = (HANDLE)_get_osfhandle(fileno(file)); OVERLAPPED overlapped = {0}; return zix_windows_status( UnlockFileEx(handle, 0, UINT32_MAX, UINT32_MAX, &overlapped)); +#endif } +#if USE_GETFINALPATHNAMEBYHANDLE && USE_CREATEFILE2 + +static HANDLE +open_attribute_handle(const char* const path) +{ + wchar_t* const wpath = zix_utf8_to_wchar(NULL, path); + + CREATEFILE2_EXTENDED_PARAMETERS params = { + sizeof(CREATEFILE2_EXTENDED_PARAMETERS), + 0U, + FILE_FLAG_BACKUP_SEMANTICS, + 0U, + NULL, + NULL}; + + const HANDLE handle = + CreateFile2(wpath, FILE_READ_ATTRIBUTES, 0U, OPEN_EXISTING, ¶ms); + + zix_free(NULL, wpath); + return handle; +} + +#elif USE_GETFINALPATHNAMEBYHANDLE + +static HANDLE +open_attribute_handle(const char* const path) +{ + ArgPathChar* const wpath = arg_path_new(NULL, path); + + const HANDLE handle = CreateFile(wpath, + FILE_READ_ATTRIBUTES, + 0U, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL); + + arg_path_free(NULL, wpath); + return handle; +} + +#endif + char* zix_canonical_path(ZixAllocator* const allocator, const char* const path) { - char full[MAX_PATH] = {0}; - if (!path || !GetFullPathName(path, MAX_PATH, full, NULL)) { + if (!path) { 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; +#if USE_GETFINALPATHNAMEBYHANDLE // Vista+ + + const HANDLE h = open_attribute_handle(path); + TCHAR* final = NULL; + if (h != INVALID_HANDLE_VALUE) { + const DWORD flags = FILE_NAME_NORMALIZED | VOLUME_NAME_DOS; + const DWORD length = GetFinalPathNameByHandle(h, NULL, 0U, flags); + if (length) { + final = (TCHAR*)zix_calloc(allocator, (size_t)length + 1U, sizeof(TCHAR)); + if (final) { + GetFinalPathNameByHandle(h, final, length + 1U, flags); + } + } } - 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 path_result(allocator, final); + +#else // Fall back to "full path iff it exists" for older Windows + + ArgPathChar* const wpath = arg_path_new(allocator, path); + TCHAR* full = NULL; + if (GetFileAttributes(wpath) != INVALID_FILE_ATTRIBUTES) { + const DWORD length = GetFullPathName(wpath, 0U, NULL, NULL); + if (length) { + full = (TCHAR*)zix_calloc(allocator, (size_t)length + 1U, sizeof(TCHAR)); + if (full) { + GetFullPathName(wpath, length + 1U, full, NULL); + } + } } - if (final_size > 4U && !strncmp(final, "\\\\?\\", 4)) { - memmove(final, final + 4U, final_size - 4U); - final[final_size - 4U] = '\0'; - } + arg_path_free(allocator, wpath); + return path_result(allocator, full); - CloseHandle(h); - return final; +#endif } static ZixFileType @@ -246,30 +370,44 @@ attrs_file_type(const DWORD attrs) ZixFileType zix_file_type(const char* const path) { - const ZixFileType type = attrs_file_type(GetFileAttributes(path)); + const ZixFileType type = zix_symlink_type(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); + char* const canonical = zix_canonical_path(NULL, path); + const ZixFileType real_type = + canonical ? zix_symlink_type(canonical) : ZIX_FILE_TYPE_NONE; + + zix_free(NULL, canonical); + return real_type; } ZixFileType zix_symlink_type(const char* const path) { - return attrs_file_type(GetFileAttributes(path)); + ArgPathChar* const wpath = arg_path_new(NULL, path); + if (!wpath) { + return ZIX_FILE_TYPE_NONE; + } + + const ZixFileType type = attrs_file_type(GetFileAttributes(wpath)); + arg_path_free(NULL, wpath); + return type; } 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; + if (!dir_path[0]) { + return ZIX_STATUS_BAD_ARG; + } + + ArgPathChar* const wpath = arg_path_new(NULL, dir_path); + const ZixStatus st = zix_windows_status(CreateDirectory(wpath, NULL)); + arg_path_free(NULL, wpath); + return st; } ZixStatus @@ -287,7 +425,15 @@ 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)); + ArgPathChar* const wtarget = arg_path_new(NULL, target_path); + ArgPathChar* const wlink = arg_path_new(NULL, link_path); + + const BOOL success = CreateSymbolicLink(wlink, wtarget, flags); + + arg_path_free(NULL, wlink); + arg_path_free(NULL, wtarget); + return zix_windows_status(success); + #else (void)target_path; (void)link_path; @@ -303,7 +449,15 @@ zix_create_directory_symlink(const char* const target_path, static const DWORD flags = SYMBOLIC_LINK_FLAG_DIRECTORY | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; - return zix_windows_status(CreateSymbolicLink(link_path, target_path, flags)); + ArgPathChar* const wtarget = arg_path_new(NULL, target_path); + ArgPathChar* const wlink = arg_path_new(NULL, link_path); + + const BOOL success = CreateSymbolicLink(wlink, wtarget, flags); + + arg_path_free(NULL, wlink); + arg_path_free(NULL, wtarget); + return zix_windows_status(success); + #else (void)target_path; (void)link_path; @@ -314,31 +468,45 @@ zix_create_directory_symlink(const char* const target_path, 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)); +#if USE_CREATEHARDLINK + ArgPathChar* const wtarget = arg_path_new(NULL, target_path); + ArgPathChar* const wlink = arg_path_new(NULL, link_path); + + const BOOL success = CreateHardLink(wlink, wtarget, NULL); + + arg_path_free(NULL, wlink); + arg_path_free(NULL, wtarget); + return zix_windows_status(success); + +#else + (void)target_path; + (void)link_path; + return ZIX_STATUS_NOT_SUPPORTED; +#endif } char* zix_temp_directory_path(ZixAllocator* const allocator) { - const DWORD size = GetTempPath(0U, NULL); - char* const buf = (char*)zix_calloc(allocator, size, 1); + const DWORD size = GetTempPath(0U, NULL); + TCHAR* const buf = (TCHAR*)zix_calloc(allocator, size, sizeof(TCHAR)); if (buf && (GetTempPath(size, buf) != size - 1U)) { zix_free(allocator, buf); return NULL; } - return buf; + return path_result(allocator, buf); } char* zix_current_path(ZixAllocator* const allocator) { - const DWORD size = GetCurrentDirectory(0U, NULL); - char* const buf = (char*)zix_calloc(allocator, size, 1); + const DWORD size = GetCurrentDirectory(0U, NULL); + TCHAR* const buf = (TCHAR*)zix_calloc(allocator, size, sizeof(TCHAR)); if (buf && (GetCurrentDirectory(size, buf) != size - 1U)) { zix_free(allocator, buf); return NULL; } - return buf; + return path_result(allocator, buf); } |