Fix handling of paths with dirfd on Windows

This change fixes an issue with all system calls ending with *at(), when
the caller passes `dirfd != AT_FDCWD` and an absolute path. It's because
the old code was turning paths like C:\bin\ls into \\C:\bin\ls\C:\bin\ls
after being converted from paths like /C/bin/ls. I noticed this when the
Emacs dired mode stopped working. It's unclear if it's a regression with
Cosmopolitan Libc or if this was introduced by the Emacs v29 upgrade. It
also impacted posix_spawn() for which a newly minted example now exists.
This commit is contained in:
Justine Tunney 2024-09-01 16:35:48 -07:00
parent a089c07ddc
commit 39e7f24947
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
10 changed files with 373 additions and 46 deletions

View file

@ -18,6 +18,7 @@
*/
#include "libc/calls/internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/strace.h"
#include "libc/macros.h"
#include "libc/nt/enum/fileflagandattributes.h"
@ -27,6 +28,18 @@
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
static int IsAlpha(int c) {
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
}
static bool IsAbsolutePathWin32(char16_t *path) {
if (path[0] == '\\')
return true;
if (IsAlpha(path[0]) && path[1] == ':')
return true;
return false;
}
static textwindows int __mkntpathath_impl(int64_t dirhand, const char *path,
int flags,
char16_t file[hasatleast PATH_MAX]) {
@ -39,7 +52,7 @@ static textwindows int __mkntpathath_impl(int64_t dirhand, const char *path,
return -1;
if (!filelen)
return enoent();
if (file[0] != u'\\' && dirhand != AT_FDCWD) { // ProTip: \\?\C:\foo
if (dirhand != AT_FDCWD && !IsAbsolutePathWin32(file)) {
dirlen = GetFinalPathNameByHandle(dirhand, dir, ARRAYLEN(dir),
kNtFileNameNormalized | kNtVolumeNameDos);
if (!dirlen)
@ -49,7 +62,8 @@ static textwindows int __mkntpathath_impl(int64_t dirhand, const char *path,
dir[dirlen] = u'\\';
memcpy(dir + dirlen + 1, file, (filelen + 1) * sizeof(char16_t));
memcpy(file, dir, ((n = dirlen + 1 + filelen) + 1) * sizeof(char16_t));
return __normntpath(file, n);
int res = __normntpath(file, n);
return res;
} else {
return filelen;
}