mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-12 10:22:12 +02:00
Platform (Windows): adds implementation of POSIX readlink(2) and realpath(3)
This commit is contained in:
@@ -28,7 +28,7 @@ static void getExePath(FFPlatform* platform)
|
||||
NULL);
|
||||
if (hPath != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD len = GetFinalPathNameByHandleW(hPath, exePathW, MAX_PATH, FILE_NAME_OPENED);
|
||||
DWORD len = GetFinalPathNameByHandleW(hPath, exePathW, MAX_PATH, FILE_NAME_NORMALIZED);
|
||||
if (len > 0 && len < MAX_PATH)
|
||||
{
|
||||
ffStrbufSetNWS(&platform->exePath, len, exePathW);
|
||||
|
||||
+260
-1
@@ -1,6 +1,6 @@
|
||||
#include "common/path.h"
|
||||
#include "common/io.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "common/arrayUtils.h"
|
||||
|
||||
#if !_WIN32
|
||||
const char* ffFindExecutableInPath(const char* name, FFstrbuf* result)
|
||||
@@ -49,6 +49,9 @@ const char* ffFindExecutableInPath(const char* name, FFstrbuf* result)
|
||||
}
|
||||
#else
|
||||
#include <windows.h>
|
||||
#include <winioctl.h>
|
||||
#include <errno.h>
|
||||
#include <stdalign.h>
|
||||
|
||||
const char* ffFindExecutableInPath(const char* name, FFstrbuf* result)
|
||||
{
|
||||
@@ -62,4 +65,260 @@ const char* ffFindExecutableInPath(const char* name, FFstrbuf* result)
|
||||
ffStrbufSetS(result, buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int winerr2Errno(DWORD err)
|
||||
{
|
||||
switch (err)
|
||||
{
|
||||
case ERROR_FILE_NOT_FOUND:
|
||||
case ERROR_PATH_NOT_FOUND:
|
||||
case ERROR_INVALID_NAME:
|
||||
return ENOENT;
|
||||
case ERROR_ACCESS_DENIED:
|
||||
case ERROR_SHARING_VIOLATION:
|
||||
case ERROR_LOCK_VIOLATION:
|
||||
return EACCES;
|
||||
case ERROR_BUFFER_OVERFLOW:
|
||||
case ERROR_INSUFFICIENT_BUFFER:
|
||||
return ENAMETOOLONG;
|
||||
case ERROR_INVALID_PARAMETER:
|
||||
case ERROR_NOT_A_REPARSE_POINT:
|
||||
return EINVAL;
|
||||
default:
|
||||
return EIO;
|
||||
}
|
||||
}
|
||||
|
||||
char* frealpath(HANDLE hFile, char* resolved_name)
|
||||
{
|
||||
if (__builtin_expect(hFile == INVALID_HANDLE_VALUE || !hFile, false))
|
||||
{
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wchar_t resolvedNameW[MAX_PATH + 4]; /* +4 for "\\\\?\\" prefix */
|
||||
DWORD lenW = GetFinalPathNameByHandleW(hFile, resolvedNameW, (DWORD)ARRAY_SIZE(resolvedNameW), FILE_NAME_NORMALIZED);
|
||||
|
||||
if (lenW == 0)
|
||||
{
|
||||
errno = winerr2Errno(GetLastError());
|
||||
return NULL;
|
||||
}
|
||||
if (lenW >= ARRAY_SIZE(resolvedNameW))
|
||||
{
|
||||
errno = E2BIG;
|
||||
return NULL;
|
||||
}
|
||||
lenW++; // Include null terminator
|
||||
|
||||
wchar_t* srcW = resolvedNameW;
|
||||
DWORD srcLenW = lenW;
|
||||
|
||||
if (srcLenW >= 8 && wcsncmp(resolvedNameW, L"\\\\?\\UNC\\", 8) == 0)
|
||||
{
|
||||
/* Convert "\\?\UNC\server\share" to "\\server\share" */
|
||||
srcW += 6;
|
||||
srcLenW -= 6;
|
||||
*srcW = L'\\';
|
||||
}
|
||||
else if (srcLenW >= 4 && wcsncmp(resolvedNameW, L"\\\\?\\", 4) == 0)
|
||||
{
|
||||
srcW += 4;
|
||||
srcLenW -= 4;
|
||||
}
|
||||
|
||||
if (resolved_name)
|
||||
{
|
||||
ULONG outBytes = 0;
|
||||
if (!NT_SUCCESS(RtlUnicodeToUTF8N(resolved_name, MAX_PATH, &outBytes, srcW, (ULONG)(srcLenW * sizeof(wchar_t)))))
|
||||
{
|
||||
errno = E2BIG;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (outBytes > MAX_PATH)
|
||||
{
|
||||
errno = E2BIG;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return resolved_name;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* UTF-8 worst-case: up to 4 bytes per UTF-16 code unit */
|
||||
char tmp[(MAX_PATH + 4) * 4];
|
||||
ULONG outBytes = 0;
|
||||
|
||||
if (!NT_SUCCESS(RtlUnicodeToUTF8N(tmp, (ULONG)sizeof(tmp), &outBytes, srcW, (ULONG)(srcLenW * sizeof(wchar_t)))))
|
||||
{
|
||||
errno = E2BIG;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
resolved_name = (char*)malloc(outBytes);
|
||||
if (!resolved_name)
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(resolved_name, tmp, outBytes);
|
||||
return resolved_name;
|
||||
}
|
||||
|
||||
return resolved_name;
|
||||
}
|
||||
|
||||
char* realpath(const char* __restrict file_name, char* __restrict resolved_name)
|
||||
{
|
||||
if (!file_name)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wchar_t fileNameW[MAX_PATH];
|
||||
ULONG lenBytes = 0;
|
||||
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(fileNameW, (ULONG)sizeof(fileNameW), &lenBytes, file_name, (ULONG)strlen(file_name) + 1)))
|
||||
{
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FF_AUTO_CLOSE_FD HANDLE hFile = CreateFileW(
|
||||
fileNameW,
|
||||
0,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL);
|
||||
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
errno = winerr2Errno(GetLastError());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return frealpath(hFile, resolved_name);
|
||||
}
|
||||
|
||||
ssize_t freadlink(HANDLE hFile, char* buf, size_t bufsiz)
|
||||
{
|
||||
if (__builtin_expect(hFile == INVALID_HANDLE_VALUE || !buf || bufsiz == 0, false))
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
alignas(REPARSE_DATA_BUFFER) BYTE reparseBuf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
|
||||
DWORD bytesReturned = 0;
|
||||
if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, 0, reparseBuf, (DWORD) sizeof(reparseBuf), &bytesReturned, NULL))
|
||||
{
|
||||
errno = winerr2Errno(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
REPARSE_DATA_BUFFER* rp = (REPARSE_DATA_BUFFER*) reparseBuf;
|
||||
const wchar_t* targetW = NULL;
|
||||
USHORT targetBytes = 0;
|
||||
|
||||
if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
|
||||
{
|
||||
if (rp->SymbolicLinkReparseBuffer.PrintNameLength > 0)
|
||||
{
|
||||
targetW = rp->SymbolicLinkReparseBuffer.PathBuffer +
|
||||
(rp->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t));
|
||||
targetBytes = rp->SymbolicLinkReparseBuffer.PrintNameLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetW = rp->SymbolicLinkReparseBuffer.PathBuffer +
|
||||
(rp->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t));
|
||||
targetBytes = rp->SymbolicLinkReparseBuffer.SubstituteNameLength;
|
||||
|
||||
if (targetBytes >= 8 &&
|
||||
wcsncmp(targetW, L"\\??\\", 4) == 0)
|
||||
{
|
||||
targetW += 4;
|
||||
targetBytes -= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
|
||||
{
|
||||
if (rp->MountPointReparseBuffer.PrintNameLength > 0)
|
||||
{
|
||||
targetW = rp->MountPointReparseBuffer.PathBuffer +
|
||||
(rp->MountPointReparseBuffer.PrintNameOffset / sizeof(wchar_t));
|
||||
targetBytes = rp->MountPointReparseBuffer.PrintNameLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetW = rp->MountPointReparseBuffer.PathBuffer +
|
||||
(rp->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t));
|
||||
targetBytes = rp->MountPointReparseBuffer.SubstituteNameLength;
|
||||
|
||||
if (targetBytes >= 8 &&
|
||||
wcsncmp(targetW, L"\\??\\", 4) == 0)
|
||||
{
|
||||
targetW += 4;
|
||||
targetBytes -= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ULONG outBytes = 0;
|
||||
if (!NT_SUCCESS(RtlUnicodeToUTF8N(buf, (ULONG) bufsiz, &outBytes, targetW, targetBytes)))
|
||||
{
|
||||
errno = E2BIG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Not null-terminated
|
||||
return (ssize_t) outBytes;
|
||||
}
|
||||
|
||||
ssize_t readlink(const char* path, char* buf, size_t bufsiz)
|
||||
{
|
||||
if (!path || !buf || bufsiz == 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
wchar_t pathW[MAX_PATH];
|
||||
ULONG pathWBytes = 0;
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(pathW, (ULONG) sizeof(pathW), &pathWBytes, path, (ULONG) strlen(path) + 1)))
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
FF_AUTO_CLOSE_FD HANDLE hFile = CreateFileW(
|
||||
pathW,
|
||||
0,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
errno = winerr2Errno(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
return freadlink(hFile, buf, bufsiz);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -13,3 +13,10 @@ static inline bool ffIsAbsolutePath(const char* path)
|
||||
return path[0] == '/';
|
||||
#endif
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
char* frealpath(void* __restrict hFile, char* __restrict resolved_name /*MAX_PATH*/);
|
||||
char* realpath(const char* __restrict file_name, char* __restrict resolved_name /*MAX_PATH*/);
|
||||
ssize_t freadlink(void* hFile, char* buf, size_t bufsiz);
|
||||
ssize_t readlink(const char* path, char* buf, size_t bufsiz);
|
||||
#endif
|
||||
|
||||
@@ -7,14 +7,6 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
static inline char* realpath(const char* restrict file_name, char* restrict resolved_name)
|
||||
{
|
||||
assert(resolved_name != NULL);
|
||||
return _fullpath(resolved_name, file_name, _MAX_PATH);
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool extractNvimVersionFromBinary(const char* str, FF_MAYBE_UNUSED uint32_t len, void* userdata)
|
||||
{
|
||||
if (!ffStrStartsWith(str, "NVIM v")) return true;
|
||||
|
||||
+9
-12
@@ -407,17 +407,7 @@ static bool printImageKittyDirect(bool printError)
|
||||
#include <sys/ioctl.h>
|
||||
#else
|
||||
#include <wincon.h>
|
||||
|
||||
static inline char* realpath(const char* restrict file_name, char* restrict resolved_name)
|
||||
{
|
||||
char* result = _fullpath(resolved_name, file_name, _MAX_PATH);
|
||||
if(result)
|
||||
{
|
||||
resolved_name[1] = resolved_name[0]; // Drive Name
|
||||
resolved_name[0] = '/';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#include "common/path.h"
|
||||
#endif
|
||||
|
||||
#ifdef FF_HAVE_ZLIB
|
||||
@@ -1002,7 +992,8 @@ static bool printImageIfExistsSlowPath(FFLogoType type, bool printError)
|
||||
ffStrbufAppendS(&requestData.cacheDir, "fastfetch/images");
|
||||
|
||||
ffStrbufEnsureFree(&requestData.cacheDir, PATH_MAX);
|
||||
if(realpath(instance.config.logo.source.chars, requestData.cacheDir.chars + requestData.cacheDir.length) == NULL)
|
||||
char* filePath = requestData.cacheDir.chars + requestData.cacheDir.length;
|
||||
if(realpath(instance.config.logo.source.chars, filePath) == NULL)
|
||||
{
|
||||
//We can safely return here, because if realpath failed, we surely won't be able to read the file
|
||||
ffStrbufDestroy(&requestData.cacheDir);
|
||||
@@ -1010,6 +1001,12 @@ static bool printImageIfExistsSlowPath(FFLogoType type, bool printError)
|
||||
fputs("Logo: Querying realpath of the image source failed\n", stderr);
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
filePath[1] = filePath[0]; // Drive Name
|
||||
filePath[0] = '/';
|
||||
#endif
|
||||
|
||||
ffStrbufRecalculateLength(&requestData.cacheDir);
|
||||
ffStrbufEnsureEndsWithC(&requestData.cacheDir, '/');
|
||||
ffStrbufAppendF(&requestData.cacheDir, "%u*%u/", requestData.logoPixelWidth, requestData.logoPixelHeight);
|
||||
|
||||
Reference in New Issue
Block a user