Skip to content

Commit

Permalink
Remove Windows executable path guessing logic
Browse files Browse the repository at this point in the history
Unlike CMD.EXE, CreateProcess() doesn't care if an executable name ends
with .COM or .EXE. We now have the unbourne shell and bash working well
on Windows, so we don't need DOS anymore. Making this change will grant
us better performance, particularly for builds, because commandv() will
need to make fewer system calls. Path mangling magic still happens with
WinMain() and ntspawn() in order to do things like turn \ into / so the
interop works well at the borders. But all the code in libraries, which
did that, has been removed. It's not possible for libraries to abstract
the differences between paths.
  • Loading branch information
jart committed Sep 21, 2023
1 parent 0c5dd7b commit c88f95a
Show file tree
Hide file tree
Showing 19 changed files with 123 additions and 608 deletions.
181 changes: 48 additions & 133 deletions libc/calls/commandv.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,121 +18,15 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/log/libfatal.internal.h"
#include "libc/paths.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/ok.h"
#include "libc/sysv/consts/s.h"
#include "libc/sysv/errfuns.h"

static bool IsExePath(const char *s, size_t n) {
return n >= 4 && (READ32LE(s + n - 4) == READ32LE(".exe") ||
READ32LE(s + n - 4) == READ32LE(".EXE"));
}

static bool IsComPath(const char *s, size_t n) {
return n >= 4 && (READ32LE(s + n - 4) == READ32LE(".com") ||
READ32LE(s + n - 4) == READ32LE(".COM"));
}

static bool IsComDbgPath(const char *s, size_t n) {
return n >= 8 && (READ64LE(s + n - 8) == READ64LE(".com.dbg") ||
READ64LE(s + n - 8) == READ64LE(".COM.DBG"));
}

static bool AccessCommand(const char *name, char *path, size_t pathsz,
size_t namelen, int *err, const char *suffix,
size_t pathlen) {
size_t suffixlen;
suffixlen = strlen(suffix);
if (IsWindows() && suffixlen == 0 && !IsExePath(name, namelen) &&
!IsComPath(name, namelen))
return false;
if (pathlen + 1 + namelen + suffixlen + 1 > pathsz) return false;
if (pathlen && (path[pathlen - 1] != '/' && path[pathlen - 1] != '\\')) {
path[pathlen] = !IsWindows() ? '/'
: memchr(path, '\\', pathlen) ? '\\'
: '/';
pathlen++;
}
memcpy(path + pathlen, name, namelen);
memcpy(path + pathlen + namelen, suffix, suffixlen + 1);
if (!access(path, X_OK)) {
struct stat st;
if (!stat(path, &st)) {
if (S_ISREG(st.st_mode)) {
return true;
} else {
errno = EACCES;
}
}
}
if (errno == EACCES || *err != EACCES) *err = errno;
return false;
}

static bool SearchPath(const char *name, char *path, size_t pathsz,
size_t namelen, int *err, const char *suffix) {
char sep;
size_t i;
const char *p;
if (!(p = getenv("PATH"))) p = "/bin:/usr/local/bin:/usr/bin";
sep = IsWindows() && strchr(p, ';') ? ';' : ':';
for (;;) {
for (i = 0; p[i] && p[i] != sep; ++i) {
if (i < pathsz) {
path[i] = p[i];
}
}
if (AccessCommand(name, path, pathsz, namelen, err, suffix, i)) {
return true;
}
if (p[i] == sep) {
p += i + 1;
} else {
break;
}
}
return false;
}

static bool FindCommand(const char *name, char *pb, size_t pbsz, size_t namelen,
bool pri, const char *suffix, int *err) {
if (pri && (memchr(name, '/', namelen) || memchr(name, '\\', namelen))) {
pb[0] = 0;
return AccessCommand(name, pb, pbsz, namelen, err, suffix, 0);
}
if (IsWindows() && pri &&
pbsz > max(strlen(kNtSystemDirectory), strlen(kNtWindowsDirectory))) {
return AccessCommand(name, pb, pbsz, namelen, err, suffix,
stpcpy(pb, kNtSystemDirectory) - pb) ||
AccessCommand(name, pb, pbsz, namelen, err, suffix,
stpcpy(pb, kNtWindowsDirectory) - pb);
}
return (IsWindows() &&
(pbsz > 1 && AccessCommand(name, pb, pbsz, namelen, err, suffix,
stpcpy(pb, ".") - pb))) ||
SearchPath(name, pb, pbsz, namelen, err, suffix);
}

static bool FindVerbatim(const char *name, char *pb, size_t pbsz,
size_t namelen, bool pri, int *err) {
return FindCommand(name, pb, pbsz, namelen, pri, "", err);
}

static bool FindSuffixed(const char *name, char *pb, size_t pbsz,
size_t namelen, bool pri, int *err) {
return !IsExePath(name, namelen) && !IsComPath(name, namelen) &&
!IsComDbgPath(name, namelen) &&
(FindCommand(name, pb, pbsz, namelen, pri, ".com", err) ||
FindCommand(name, pb, pbsz, namelen, pri, ".exe", err));
}

/**
* Resolves full pathname of executable.
*
Expand All @@ -143,35 +37,56 @@ static bool FindSuffixed(const char *name, char *pb, size_t pbsz,
* @vforksafe
*/
char *commandv(const char *name, char *pathbuf, size_t pathbufsz) {
int e, f;
char *res;

// bounce empty names
size_t namelen;
res = 0;
if (!name) {
efault();
} else if (!(namelen = strlen(name))) {
if (!(namelen = strlen(name))) {
enoent();
} else if (namelen + 1 > pathbufsz) {
enametoolong();
} else {
e = errno;
f = ENOENT;
if ((IsWindows() &&
(FindSuffixed(name, pathbuf, pathbufsz, namelen, true, &f) ||
FindVerbatim(name, pathbuf, pathbufsz, namelen, true, &f) ||
FindSuffixed(name, pathbuf, pathbufsz, namelen, false, &f) ||
FindVerbatim(name, pathbuf, pathbufsz, namelen, false, &f))) ||
(!IsWindows() &&
(FindVerbatim(name, pathbuf, pathbufsz, namelen, true, &f) ||
FindSuffixed(name, pathbuf, pathbufsz, namelen, true, &f) ||
FindVerbatim(name, pathbuf, pathbufsz, namelen, false, &f) ||
FindSuffixed(name, pathbuf, pathbufsz, namelen, false, &f)))) {
errno = e;
res = pathbuf;
return 0;
}

// get system path
const char *syspath;
if (memchr(name, '/', namelen)) {
syspath = "";
} else if (!(syspath = getenv("PATH"))) {
syspath = _PATH_DEFPATH;
}

// iterate through directories
int old_errno = errno;
bool seen_eacces = false;
const char *b, *a = syspath;
errno = ENOENT;
do {
b = strchrnul(a, ':');
size_t dirlen = b - a;
if (dirlen + 1 + namelen < pathbufsz) {
if (dirlen) {
memcpy(pathbuf, a, dirlen);
pathbuf[dirlen] = '/';
memcpy(pathbuf + dirlen + 1, name, namelen + 1);
} else {
memcpy(pathbuf, name, namelen + 1);
}
if (!access(pathbuf, X_OK)) {
struct stat st;
if (!stat(pathbuf, &st) && S_ISREG(st.st_mode)) {
errno = old_errno;
return pathbuf;
}
} else if (errno == EACCES) {
seen_eacces = true;
}
} else {
errno = f;
enametoolong();
}
a = b + 1;
} while (*b);

// return error if not found
if (seen_eacces) {
errno = EACCES;
}
STRACE("commandv(%#s, %p, %'zu) → %#s% m", name, pathbuf, pathbufsz, res);
return res;
return 0;
}
9 changes: 4 additions & 5 deletions libc/calls/makedirs.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
#include "libc/calls/struct/stat.h"
#include "libc/errno.h"
#include "libc/limits.h"
#include "libc/str/path.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/s.h"
#include "libc/sysv/errfuns.h"
Expand Down Expand Up @@ -66,8 +65,8 @@ int makedirs(const char *path, unsigned mode) {
break;
}
if (errno != ENOENT) return -1;
while (i && _isdirsep(buf[i - 1])) buf[--i] = 0;
while (i && !_isdirsep(buf[i - 1])) buf[--i] = 0;
while (i && buf[i - 1] == '/') buf[--i] = 0;
while (i && buf[i - 1] != '/') buf[--i] = 0;
}

// ascend
Expand All @@ -77,8 +76,8 @@ int makedirs(const char *path, unsigned mode) {
if (i == n) goto CheckTop;
}
if (i == n) break;
while (i < n && !_isdirsep((c = path[i]))) buf[i++] = c;
while (i < n && _isdirsep((c = path[i]))) buf[i++] = c;
while (i < n && (c = path[i]) != '/') buf[i++] = c;
while (i < n && (c = path[i]) == '/') buf[i++] = c;
}

Finish:
Expand Down
1 change: 0 additions & 1 deletion libc/calls/unveil.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/str/path.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/audit.h"
Expand Down
7 changes: 3 additions & 4 deletions libc/fmt/basename.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/fmt/conv.h"
#include "libc/str/path.h"
#include "libc/fmt/libgen.h"
#include "libc/str/str.h"

/**
Expand Down Expand Up @@ -46,10 +45,10 @@ char *basename(char *path) {
size_t i;
if (path && *path) {
i = strlen(path) - 1;
for (; i && _isdirsep(path[i]); i--) {
for (; i && path[i] == '/'; i--) {
path[i] = 0;
}
while (i && !_isdirsep(path[i - 1])) {
while (i && path[i - 1] != '/') {
i--;
}
return path + i;
Expand Down
9 changes: 4 additions & 5 deletions libc/fmt/dirname.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/fmt/conv.h"
#include "libc/str/path.h"
#include "libc/fmt/libgen.h"
#include "libc/str/str.h"

/**
Expand All @@ -41,13 +40,13 @@ char *dirname(char *path) {
size_t i;
if (path && *path) {
i = strlen(path) - 1;
for (; _isdirsep(path[i]); i--) {
for (; path[i] == '/'; i--) {
if (!i) return "/";
}
for (; !_isdirsep(path[i]); i--) {
for (; path[i] != '/'; i--) {
if (!i) return ".";
}
for (; _isdirsep(path[i]); i--) {
for (; path[i] == '/'; i--) {
if (!i) return "/";
}
path[i + 1] = 0;
Expand Down
3 changes: 1 addition & 2 deletions libc/mem/get_current_dir_name.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
#include "libc/calls/calls.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/str/path.h"

/**
* Returns current working directory.
Expand All @@ -32,7 +31,7 @@
*/
char *get_current_dir_name(void) {
const char *p;
if ((p = getenv("PWD")) && _isabspath(p)) {
if ((p = getenv("PWD")) && *p == '/') {
return strdup(p);
} else {
return getcwd(0, 0);
Expand Down
35 changes: 9 additions & 26 deletions libc/proc/ntspawn.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,41 +81,24 @@ textwindows int ntspawn(
const char16_t *opt_lpCurrentDirectory,
const struct NtStartupInfo *lpStartupInfo,
struct NtProcessInformation *opt_out_lpProcessInformation) {
int rc = -1;
int64_t handle;
int i, e, rc = -1;
struct SpawnBlock *block = 0;
char16_t prog16[PATH_MAX + 5], *p;
char16_t suffixes[][5] = {u"", u".com", u".exe"};

char16_t prog16[PATH_MAX + 5];
if (__mkntpath(prog, prog16) == -1) return -1;
if ((handle = CreateFileMapping(-1, 0, pushpop(kNtPageReadwrite), 0,
sizeof(*block), 0)) &&
(block = MapViewOfFileEx(handle, kNtFileMapRead | kNtFileMapWrite, 0, 0,
sizeof(*block), 0)) &&
mkntcmdline(block->cmdline, argv) != -1 &&
mkntenvblock(block->envvars, envp, extravar, block->buf) != -1) {
p = prog16 + strlen16(prog16);
for (i = 0; i < ARRAYLEN(suffixes); ++i) {
if (suffixes[i][0] && endswith16(prog16, suffixes[i])) {
p -= strlen16(suffixes[i]);
*p = 0;
} else {
strcpy16(p, suffixes[i]);
}
e = errno;
if (CreateProcess(prog16, block->cmdline, opt_lpProcessAttributes,
opt_lpThreadAttributes, bInheritHandles,
dwCreationFlags | kNtCreateUnicodeEnvironment |
kNtInheritParentAffinity,
block->envvars, opt_lpCurrentDirectory, lpStartupInfo,
opt_out_lpProcessInformation)) {
rc = 0;
break;
} else if (errno == ENOENT) {
errno = e;
} else {
break;
}
if (CreateProcess(prog16, block->cmdline, opt_lpProcessAttributes,
opt_lpThreadAttributes, bInheritHandles,
dwCreationFlags | kNtCreateUnicodeEnvironment |
kNtInheritParentAffinity,
block->envvars, opt_lpCurrentDirectory, lpStartupInfo,
opt_out_lpProcessInformation)) {
rc = 0;
}
} else if (GetLastError() == kNtErrorSharingViolation) {
etxtbsy();
Expand Down
4 changes: 2 additions & 2 deletions libc/sock/pselect.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
const sigset_t *s;
size_t n;
} ss;
BEGIN_CANCELLATION_POINT;

#ifdef SYSDEBUG
fd_set old_readfds;
Expand All @@ -78,6 +77,7 @@ int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
fd_set *old_exceptfds_ptr = 0;
#endif

BEGIN_CANCELLATION_POINT;
if (nfds < 0) {
rc = einval();
} else if (IsAsan() &&
Expand Down Expand Up @@ -126,8 +126,8 @@ int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
rc = sys_select_nt(nfds, readfds, writefds, exceptfds, tvp, sigmask);
}
}

END_CANCELLATION_POINT;

STRACE("pselect(%d, %s → [%s], %s → [%s], %s → [%s], %s, %s) → %d% m", nfds,
DescribeFdSet(rc, nfds, old_readfds_ptr),
DescribeFdSet(rc, nfds, readfds),
Expand Down
Loading

0 comments on commit c88f95a

Please sign in to comment.